1
0
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:
Julia Kartseva 2021-06-29 16:04:32 -07:00
parent 159d68c3e0
commit 8bd095aa9a
5 changed files with 253 additions and 0 deletions

View File

@ -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

View 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;
}

View 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);

View File

@ -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']],

View 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;
}