Skip to content

Commit 8bd095a

Browse files
author
Julia Kartseva
committed
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.
1 parent 159d68c commit 8bd095a

File tree

5 files changed

+253
-0
lines changed

5 files changed

+253
-0
lines changed

src/shared/meson.build

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ shared_sources = files('''
225225
pager.h
226226
parse-argument.c
227227
parse-argument.h
228+
parse-socket-bind-item.c
229+
parse-socket-bind-item.h
228230
pe-header.h
229231
pkcs11-util.c
230232
pkcs11-util.h
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2+
3+
#include "af-list.h"
4+
#include "extract-word.h"
5+
#include "ip-protocol-list.h"
6+
#include "parse-socket-bind-item.h"
7+
#include "parse-util.h"
8+
9+
static int parse_af_token(
10+
const char *token,
11+
int *family,
12+
int *ip_protocol,
13+
uint16_t *nr_ports,
14+
uint16_t *port_min) {
15+
int af;
16+
17+
assert(token);
18+
assert(family);
19+
20+
af = af_from_ipv4_ipv6(token);
21+
if (af == AF_UNSPEC)
22+
return -EINVAL;
23+
24+
*family = af;
25+
return 0;
26+
}
27+
28+
static int parse_ip_protocol_token(
29+
const char *token,
30+
int *family,
31+
int *ip_protocol,
32+
uint16_t *nr_ports,
33+
uint16_t *port_min) {
34+
int proto;
35+
36+
assert(token);
37+
assert(ip_protocol);
38+
39+
proto = ip_protocol_from_tcp_udp(token);
40+
if (proto < 0)
41+
return -EINVAL;
42+
43+
*ip_protocol = proto;
44+
return 0;
45+
}
46+
47+
static int parse_ip_ports_token(
48+
const char *token,
49+
int *family,
50+
int *ip_protocol,
51+
uint16_t *nr_ports,
52+
uint16_t *port_min) {
53+
assert(token);
54+
assert(nr_ports);
55+
assert(port_min);
56+
57+
if (streq(token, "any"))
58+
*nr_ports = *port_min = 0;
59+
else {
60+
uint16_t mn = 0, mx = 0;
61+
int r = parse_ip_port_range(token, &mn, &mx);
62+
if (r < 0)
63+
return r;
64+
65+
*nr_ports = mx - mn + 1;
66+
*port_min = mn;
67+
}
68+
69+
return 0;
70+
}
71+
72+
typedef int (*parse_token_f)(
73+
const char *,
74+
int *,
75+
int *,
76+
uint16_t *,
77+
uint16_t *);
78+
79+
int parse_socket_bind_item(
80+
const char *str,
81+
int *address_family,
82+
int *ip_protocol,
83+
uint16_t *nr_ports,
84+
uint16_t *port_min) {
85+
/* Order of token parsers is important. */
86+
const parse_token_f parsers[] = {
87+
&parse_af_token,
88+
&parse_ip_protocol_token,
89+
&parse_ip_ports_token,
90+
};
91+
parse_token_f const *parser_ptr = parsers;
92+
int af = AF_UNSPEC, proto = 0, r;
93+
uint16_t nr = 0, mn = 0;
94+
const char *p = str;
95+
96+
assert(str);
97+
assert(address_family);
98+
assert(ip_protocol);
99+
assert(nr_ports);
100+
assert(port_min);
101+
102+
if (isempty(p))
103+
return -EINVAL;
104+
105+
for (;;) {
106+
_cleanup_free_ char *token = NULL;
107+
108+
r = extract_first_word(&p, &token, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
109+
if (r == 0)
110+
break;
111+
if (r < 0)
112+
return r;
113+
114+
if (isempty(token))
115+
return -EINVAL;
116+
117+
while (parser_ptr != parsers + ELEMENTSOF(parsers)) {
118+
r = (*parser_ptr)(token, &af, &proto, &nr, &mn);
119+
if (r == -ENOMEM)
120+
return r;
121+
122+
++parser_ptr;
123+
/* Continue to next token if parsing succeeded,
124+
* otherwise apply next parser to the same token.
125+
*/
126+
if (r >= 0)
127+
break;
128+
}
129+
if (parser_ptr == parsers + ELEMENTSOF(parsers))
130+
break;
131+
}
132+
133+
/* Failed to parse a token. */
134+
if (r < 0)
135+
return r;
136+
137+
/* Parsers applied succesfully, but end of the string not reached. */
138+
if (p)
139+
return -EINVAL;
140+
141+
*address_family = af;
142+
*ip_protocol = proto;
143+
*nr_ports = nr;
144+
*port_min = mn;
145+
return 0;
146+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2+
3+
#pragma once
4+
5+
#include <stdint.h>
6+
7+
int parse_socket_bind_item(
8+
const char *str,
9+
int *address_family,
10+
int *ip_protocol,
11+
uint16_t *nr_ports,
12+
uint16_t *port_min);

src/test/meson.build

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,8 @@ tests += [
242242

243243
[['src/test/test-parse-argument.c']],
244244

245+
[['src/test/test-parse-socket-bind-item.c']],
246+
245247
[['src/test/test-parse-util.c']],
246248

247249
[['src/test/test-sysctl-util.c']],
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2+
3+
#include <linux/in.h>
4+
#include <sys/socket.h>
5+
#include <stdio.h>
6+
7+
#include "macro.h"
8+
#include "parse-socket-bind-item.h"
9+
10+
static void test_valid_item(
11+
const char *str,
12+
int expected_af,
13+
int expected_ip_protocol,
14+
uint16_t expected_nr_ports,
15+
uint16_t expected_port_min) {
16+
uint16_t nr_ports, port_min;
17+
int af, ip_protocol;
18+
19+
assert_se(parse_socket_bind_item(str, &af, &ip_protocol, &nr_ports, &port_min) >= 0);
20+
assert_se(af == expected_af);
21+
assert_se(ip_protocol == expected_ip_protocol);
22+
assert_se(nr_ports == expected_nr_ports);
23+
assert_se(port_min == expected_port_min);
24+
25+
log_info("%s: \"%s\" ok", __func__, str);
26+
}
27+
28+
static void test_invalid_item(const char *str) {
29+
uint16_t nr_ports, port_min;
30+
int af, ip_protocol;
31+
32+
assert_se(parse_socket_bind_item(str, &af, &ip_protocol, &nr_ports, &port_min) == -EINVAL);
33+
34+
log_info("%s: \"%s\" ok", __func__, str);
35+
}
36+
37+
int main(int argc, char *argv[]) {
38+
test_valid_item("any", AF_UNSPEC, 0, 0, 0);
39+
test_valid_item("ipv4", AF_INET, 0, 0, 0);
40+
test_valid_item("ipv6", AF_INET6, 0, 0, 0);
41+
test_valid_item("ipv4:any", AF_INET, 0, 0, 0);
42+
test_valid_item("ipv6:any", AF_INET6, 0, 0, 0);
43+
test_valid_item("tcp", AF_UNSPEC, IPPROTO_TCP, 0, 0);
44+
test_valid_item("udp", AF_UNSPEC, IPPROTO_UDP, 0, 0);
45+
test_valid_item("tcp:any", AF_UNSPEC, IPPROTO_TCP, 0, 0);
46+
test_valid_item("udp:any", AF_UNSPEC, IPPROTO_UDP, 0, 0);
47+
test_valid_item("6666", AF_UNSPEC, 0, 1, 6666);
48+
test_valid_item("6666-6667", AF_UNSPEC, 0, 2, 6666);
49+
test_valid_item("65535", AF_UNSPEC, 0, 1, 65535);
50+
test_valid_item("1-65535", AF_UNSPEC, 0, 65535, 1);
51+
test_valid_item("ipv4:tcp", AF_INET, IPPROTO_TCP, 0, 0);
52+
test_valid_item("ipv4:udp", AF_INET, IPPROTO_UDP, 0, 0);
53+
test_valid_item("ipv6:tcp", AF_INET6, IPPROTO_TCP, 0, 0);
54+
test_valid_item("ipv6:udp", AF_INET6, IPPROTO_UDP, 0, 0);
55+
test_valid_item("ipv4:6666", AF_INET, 0, 1, 6666);
56+
test_valid_item("ipv6:6666", AF_INET6, 0, 1, 6666);
57+
test_valid_item("tcp:6666", AF_UNSPEC, IPPROTO_TCP, 1, 6666);
58+
test_valid_item("udp:6666", AF_UNSPEC, IPPROTO_UDP, 1, 6666);
59+
test_valid_item("ipv4:tcp:6666", AF_INET, IPPROTO_TCP, 1, 6666);
60+
test_valid_item("ipv6:tcp:6666", AF_INET6, IPPROTO_TCP, 1, 6666);
61+
test_valid_item("ipv6:udp:6666-6667", AF_INET6, IPPROTO_UDP, 2, 6666);
62+
test_valid_item("ipv6:tcp:any", AF_INET6, IPPROTO_TCP, 0, 0);
63+
64+
test_invalid_item("");
65+
test_invalid_item(":");
66+
test_invalid_item("::");
67+
test_invalid_item("any:");
68+
test_invalid_item("meh");
69+
test_invalid_item("zupa:meh");
70+
test_invalid_item("zupa:meh:eh");
71+
test_invalid_item("ip");
72+
test_invalid_item("dccp");
73+
test_invalid_item("ipv6meh");
74+
test_invalid_item("ipv6::");
75+
test_invalid_item("ipv6:ipv6");
76+
test_invalid_item("ipv6:icmp");
77+
test_invalid_item("ipv6:tcp:0");
78+
test_invalid_item("65536");
79+
test_invalid_item("0-65535");
80+
test_invalid_item("ipv6:tcp:6666-6665");
81+
test_invalid_item("ipv6:tcp:6666-100000");
82+
test_invalid_item("ipv6::6666");
83+
test_invalid_item("ipv6:tcp:any:");
84+
test_invalid_item("ipv6:tcp:any:ipv6");
85+
test_invalid_item("ipv6:tcp:6666:zupa");
86+
test_invalid_item("ipv6:tcp:6666:any");
87+
test_invalid_item("ipv6:tcp:6666 zupa");
88+
test_invalid_item("ipv6:tcp:6666: zupa");
89+
test_invalid_item("ipv6:tcp:6666\n zupa");
90+
return 0;
91+
}

0 commit comments

Comments
 (0)