mirror of
https://github.com/systemd/systemd.git
synced 2024-10-29 21:55:36 +03:00
shared: add parser for SocketBind{Allow|Deny}=
Parse address family, ip protocol and ports, any of them can be optional. If neither is specified, a special value 'any' is expected. Helper is placed in shared to be reused in both fragment and dbus. Add unit tests with valid and invalid examples.
This commit is contained in:
parent
159d68c3e0
commit
8bd095aa9a
@ -225,6 +225,8 @@ shared_sources = files('''
|
||||
pager.h
|
||||
parse-argument.c
|
||||
parse-argument.h
|
||||
parse-socket-bind-item.c
|
||||
parse-socket-bind-item.h
|
||||
pe-header.h
|
||||
pkcs11-util.c
|
||||
pkcs11-util.h
|
||||
|
146
src/shared/parse-socket-bind-item.c
Normal file
146
src/shared/parse-socket-bind-item.c
Normal file
@ -0,0 +1,146 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "af-list.h"
|
||||
#include "extract-word.h"
|
||||
#include "ip-protocol-list.h"
|
||||
#include "parse-socket-bind-item.h"
|
||||
#include "parse-util.h"
|
||||
|
||||
static int parse_af_token(
|
||||
const char *token,
|
||||
int *family,
|
||||
int *ip_protocol,
|
||||
uint16_t *nr_ports,
|
||||
uint16_t *port_min) {
|
||||
int af;
|
||||
|
||||
assert(token);
|
||||
assert(family);
|
||||
|
||||
af = af_from_ipv4_ipv6(token);
|
||||
if (af == AF_UNSPEC)
|
||||
return -EINVAL;
|
||||
|
||||
*family = af;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_ip_protocol_token(
|
||||
const char *token,
|
||||
int *family,
|
||||
int *ip_protocol,
|
||||
uint16_t *nr_ports,
|
||||
uint16_t *port_min) {
|
||||
int proto;
|
||||
|
||||
assert(token);
|
||||
assert(ip_protocol);
|
||||
|
||||
proto = ip_protocol_from_tcp_udp(token);
|
||||
if (proto < 0)
|
||||
return -EINVAL;
|
||||
|
||||
*ip_protocol = proto;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_ip_ports_token(
|
||||
const char *token,
|
||||
int *family,
|
||||
int *ip_protocol,
|
||||
uint16_t *nr_ports,
|
||||
uint16_t *port_min) {
|
||||
assert(token);
|
||||
assert(nr_ports);
|
||||
assert(port_min);
|
||||
|
||||
if (streq(token, "any"))
|
||||
*nr_ports = *port_min = 0;
|
||||
else {
|
||||
uint16_t mn = 0, mx = 0;
|
||||
int r = parse_ip_port_range(token, &mn, &mx);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*nr_ports = mx - mn + 1;
|
||||
*port_min = mn;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef int (*parse_token_f)(
|
||||
const char *,
|
||||
int *,
|
||||
int *,
|
||||
uint16_t *,
|
||||
uint16_t *);
|
||||
|
||||
int parse_socket_bind_item(
|
||||
const char *str,
|
||||
int *address_family,
|
||||
int *ip_protocol,
|
||||
uint16_t *nr_ports,
|
||||
uint16_t *port_min) {
|
||||
/* Order of token parsers is important. */
|
||||
const parse_token_f parsers[] = {
|
||||
&parse_af_token,
|
||||
&parse_ip_protocol_token,
|
||||
&parse_ip_ports_token,
|
||||
};
|
||||
parse_token_f const *parser_ptr = parsers;
|
||||
int af = AF_UNSPEC, proto = 0, r;
|
||||
uint16_t nr = 0, mn = 0;
|
||||
const char *p = str;
|
||||
|
||||
assert(str);
|
||||
assert(address_family);
|
||||
assert(ip_protocol);
|
||||
assert(nr_ports);
|
||||
assert(port_min);
|
||||
|
||||
if (isempty(p))
|
||||
return -EINVAL;
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *token = NULL;
|
||||
|
||||
r = extract_first_word(&p, &token, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
|
||||
if (r == 0)
|
||||
break;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (isempty(token))
|
||||
return -EINVAL;
|
||||
|
||||
while (parser_ptr != parsers + ELEMENTSOF(parsers)) {
|
||||
r = (*parser_ptr)(token, &af, &proto, &nr, &mn);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
|
||||
++parser_ptr;
|
||||
/* Continue to next token if parsing succeeded,
|
||||
* otherwise apply next parser to the same token.
|
||||
*/
|
||||
if (r >= 0)
|
||||
break;
|
||||
}
|
||||
if (parser_ptr == parsers + ELEMENTSOF(parsers))
|
||||
break;
|
||||
}
|
||||
|
||||
/* Failed to parse a token. */
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Parsers applied succesfully, but end of the string not reached. */
|
||||
if (p)
|
||||
return -EINVAL;
|
||||
|
||||
*address_family = af;
|
||||
*ip_protocol = proto;
|
||||
*nr_ports = nr;
|
||||
*port_min = mn;
|
||||
return 0;
|
||||
}
|
12
src/shared/parse-socket-bind-item.h
Normal file
12
src/shared/parse-socket-bind-item.h
Normal file
@ -0,0 +1,12 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
int parse_socket_bind_item(
|
||||
const char *str,
|
||||
int *address_family,
|
||||
int *ip_protocol,
|
||||
uint16_t *nr_ports,
|
||||
uint16_t *port_min);
|
@ -242,6 +242,8 @@ tests += [
|
||||
|
||||
[['src/test/test-parse-argument.c']],
|
||||
|
||||
[['src/test/test-parse-socket-bind-item.c']],
|
||||
|
||||
[['src/test/test-parse-util.c']],
|
||||
|
||||
[['src/test/test-sysctl-util.c']],
|
||||
|
91
src/test/test-parse-socket-bind-item.c
Normal file
91
src/test/test-parse-socket-bind-item.c
Normal file
@ -0,0 +1,91 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <linux/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "macro.h"
|
||||
#include "parse-socket-bind-item.h"
|
||||
|
||||
static void test_valid_item(
|
||||
const char *str,
|
||||
int expected_af,
|
||||
int expected_ip_protocol,
|
||||
uint16_t expected_nr_ports,
|
||||
uint16_t expected_port_min) {
|
||||
uint16_t nr_ports, port_min;
|
||||
int af, ip_protocol;
|
||||
|
||||
assert_se(parse_socket_bind_item(str, &af, &ip_protocol, &nr_ports, &port_min) >= 0);
|
||||
assert_se(af == expected_af);
|
||||
assert_se(ip_protocol == expected_ip_protocol);
|
||||
assert_se(nr_ports == expected_nr_ports);
|
||||
assert_se(port_min == expected_port_min);
|
||||
|
||||
log_info("%s: \"%s\" ok", __func__, str);
|
||||
}
|
||||
|
||||
static void test_invalid_item(const char *str) {
|
||||
uint16_t nr_ports, port_min;
|
||||
int af, ip_protocol;
|
||||
|
||||
assert_se(parse_socket_bind_item(str, &af, &ip_protocol, &nr_ports, &port_min) == -EINVAL);
|
||||
|
||||
log_info("%s: \"%s\" ok", __func__, str);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
test_valid_item("any", AF_UNSPEC, 0, 0, 0);
|
||||
test_valid_item("ipv4", AF_INET, 0, 0, 0);
|
||||
test_valid_item("ipv6", AF_INET6, 0, 0, 0);
|
||||
test_valid_item("ipv4:any", AF_INET, 0, 0, 0);
|
||||
test_valid_item("ipv6:any", AF_INET6, 0, 0, 0);
|
||||
test_valid_item("tcp", AF_UNSPEC, IPPROTO_TCP, 0, 0);
|
||||
test_valid_item("udp", AF_UNSPEC, IPPROTO_UDP, 0, 0);
|
||||
test_valid_item("tcp:any", AF_UNSPEC, IPPROTO_TCP, 0, 0);
|
||||
test_valid_item("udp:any", AF_UNSPEC, IPPROTO_UDP, 0, 0);
|
||||
test_valid_item("6666", AF_UNSPEC, 0, 1, 6666);
|
||||
test_valid_item("6666-6667", AF_UNSPEC, 0, 2, 6666);
|
||||
test_valid_item("65535", AF_UNSPEC, 0, 1, 65535);
|
||||
test_valid_item("1-65535", AF_UNSPEC, 0, 65535, 1);
|
||||
test_valid_item("ipv4:tcp", AF_INET, IPPROTO_TCP, 0, 0);
|
||||
test_valid_item("ipv4:udp", AF_INET, IPPROTO_UDP, 0, 0);
|
||||
test_valid_item("ipv6:tcp", AF_INET6, IPPROTO_TCP, 0, 0);
|
||||
test_valid_item("ipv6:udp", AF_INET6, IPPROTO_UDP, 0, 0);
|
||||
test_valid_item("ipv4:6666", AF_INET, 0, 1, 6666);
|
||||
test_valid_item("ipv6:6666", AF_INET6, 0, 1, 6666);
|
||||
test_valid_item("tcp:6666", AF_UNSPEC, IPPROTO_TCP, 1, 6666);
|
||||
test_valid_item("udp:6666", AF_UNSPEC, IPPROTO_UDP, 1, 6666);
|
||||
test_valid_item("ipv4:tcp:6666", AF_INET, IPPROTO_TCP, 1, 6666);
|
||||
test_valid_item("ipv6:tcp:6666", AF_INET6, IPPROTO_TCP, 1, 6666);
|
||||
test_valid_item("ipv6:udp:6666-6667", AF_INET6, IPPROTO_UDP, 2, 6666);
|
||||
test_valid_item("ipv6:tcp:any", AF_INET6, IPPROTO_TCP, 0, 0);
|
||||
|
||||
test_invalid_item("");
|
||||
test_invalid_item(":");
|
||||
test_invalid_item("::");
|
||||
test_invalid_item("any:");
|
||||
test_invalid_item("meh");
|
||||
test_invalid_item("zupa:meh");
|
||||
test_invalid_item("zupa:meh:eh");
|
||||
test_invalid_item("ip");
|
||||
test_invalid_item("dccp");
|
||||
test_invalid_item("ipv6meh");
|
||||
test_invalid_item("ipv6::");
|
||||
test_invalid_item("ipv6:ipv6");
|
||||
test_invalid_item("ipv6:icmp");
|
||||
test_invalid_item("ipv6:tcp:0");
|
||||
test_invalid_item("65536");
|
||||
test_invalid_item("0-65535");
|
||||
test_invalid_item("ipv6:tcp:6666-6665");
|
||||
test_invalid_item("ipv6:tcp:6666-100000");
|
||||
test_invalid_item("ipv6::6666");
|
||||
test_invalid_item("ipv6:tcp:any:");
|
||||
test_invalid_item("ipv6:tcp:any:ipv6");
|
||||
test_invalid_item("ipv6:tcp:6666:zupa");
|
||||
test_invalid_item("ipv6:tcp:6666:any");
|
||||
test_invalid_item("ipv6:tcp:6666 zupa");
|
||||
test_invalid_item("ipv6:tcp:6666: zupa");
|
||||
test_invalid_item("ipv6:tcp:6666\n zupa");
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user