]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/parse-helpers.c
Merge pull request #30716 from yuwata/network-cancel-request
[thirdparty/systemd.git] / src / shared / parse-helpers.c
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 "log.h"
7 #include "parse-helpers.h"
8 #include "parse-util.h"
9 #include "path-util.h"
10 #include "utf8.h"
11
12 int path_simplify_and_warn(
13 char *path,
14 unsigned flag,
15 const char *unit,
16 const char *filename,
17 unsigned line,
18 const char *lvalue) {
19
20 bool fatal = flag & PATH_CHECK_FATAL;
21
22 assert(!FLAGS_SET(flag, PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE));
23
24 if (!utf8_is_valid(path))
25 return log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, path);
26
27 if (flag & (PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)) {
28 bool absolute;
29
30 absolute = path_is_absolute(path);
31
32 if (!absolute && (flag & PATH_CHECK_ABSOLUTE))
33 return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
34 "%s= path is not absolute%s: %s",
35 lvalue, fatal ? "" : ", ignoring", path);
36
37 if (absolute && (flag & PATH_CHECK_RELATIVE))
38 return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
39 "%s= path is absolute%s: %s",
40 lvalue, fatal ? "" : ", ignoring", path);
41 }
42
43 path_simplify_full(path, flag & PATH_KEEP_TRAILING_SLASH ? PATH_SIMPLIFY_KEEP_TRAILING_SLASH : 0);
44
45 if (!path_is_valid(path))
46 return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
47 "%s= path has invalid length (%zu bytes)%s.",
48 lvalue, strlen(path), fatal ? "" : ", ignoring");
49
50 if (!path_is_normalized(path))
51 return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
52 "%s= path is not normalized%s: %s",
53 lvalue, fatal ? "" : ", ignoring", path);
54
55 return 0;
56 }
57
58 static int parse_af_token(
59 const char *token,
60 int *family,
61 int *ip_protocol,
62 uint16_t *nr_ports,
63 uint16_t *port_min) {
64
65 int af;
66
67 assert(token);
68 assert(family);
69
70 af = af_from_ipv4_ipv6(token);
71 if (af == AF_UNSPEC)
72 return -EINVAL;
73
74 *family = af;
75 return 0;
76 }
77
78 static int parse_ip_protocol_token(
79 const char *token,
80 int *family,
81 int *ip_protocol,
82 uint16_t *nr_ports,
83 uint16_t *port_min) {
84
85 int proto;
86
87 assert(token);
88 assert(ip_protocol);
89
90 proto = ip_protocol_from_tcp_udp(token);
91 if (proto < 0)
92 return -EINVAL;
93
94 *ip_protocol = proto;
95 return 0;
96 }
97
98 static int parse_ip_ports_token(
99 const char *token,
100 int *family,
101 int *ip_protocol,
102 uint16_t *nr_ports,
103 uint16_t *port_min) {
104
105 int r;
106
107 assert(token);
108 assert(nr_ports);
109 assert(port_min);
110
111 if (streq(token, "any"))
112 *nr_ports = *port_min = 0;
113 else {
114 uint16_t mn = 0, mx = 0;
115 r = parse_ip_port_range(token, &mn, &mx, /* allow_zero = */ true);
116 if (r < 0)
117 return r;
118
119 *nr_ports = mx - mn + 1;
120 *port_min = mn;
121 }
122
123 return 0;
124 }
125
126 typedef int (*parse_token_f)(
127 const char *,
128 int *,
129 int *,
130 uint16_t *,
131 uint16_t *);
132
133 int parse_socket_bind_item(
134 const char *str,
135 int *address_family,
136 int *ip_protocol,
137 uint16_t *nr_ports,
138 uint16_t *port_min) {
139
140 /* Order of token parsers is important. */
141 const parse_token_f parsers[] = {
142 &parse_af_token,
143 &parse_ip_protocol_token,
144 &parse_ip_ports_token,
145 };
146 parse_token_f const *parser_ptr = parsers;
147 int af = AF_UNSPEC, proto = 0, r;
148 uint16_t nr = 0, mn = 0;
149 const char *p = ASSERT_PTR(str);
150
151 assert(address_family);
152 assert(ip_protocol);
153 assert(nr_ports);
154 assert(port_min);
155
156 if (isempty(p))
157 return -EINVAL;
158
159 for (;;) {
160 _cleanup_free_ char *token = NULL;
161
162 r = extract_first_word(&p, &token, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
163 if (r == 0)
164 break;
165 if (r < 0)
166 return r;
167
168 if (isempty(token))
169 return -EINVAL;
170
171 while (parser_ptr != parsers + ELEMENTSOF(parsers)) {
172 r = (*parser_ptr)(token, &af, &proto, &nr, &mn);
173 if (r == -ENOMEM)
174 return r;
175
176 ++parser_ptr;
177 /* Continue to next token if parsing succeeded,
178 * otherwise apply next parser to the same token.
179 */
180 if (r >= 0)
181 break;
182 }
183 if (parser_ptr == parsers + ELEMENTSOF(parsers))
184 break;
185 }
186
187 /* Failed to parse a token. */
188 if (r < 0)
189 return r;
190
191 /* Parsers applied successfully, but end of the string not reached. */
192 if (p)
193 return -EINVAL;
194
195 *address_family = af;
196 *ip_protocol = proto;
197 *nr_ports = nr;
198 *port_min = mn;
199
200 return 0;
201 }
202
203 int config_parse_path_or_ignore(
204 const char *unit,
205 const char *filename,
206 unsigned line,
207 const char *section,
208 unsigned section_line,
209 const char *lvalue,
210 int ltype,
211 const char *rvalue,
212 void *data,
213 void *userdata) {
214
215 _cleanup_free_ char *n = NULL;
216 bool fatal = ltype;
217 char **s = ASSERT_PTR(data);
218 int r;
219
220 assert(filename);
221 assert(lvalue);
222 assert(rvalue);
223
224 if (isempty(rvalue))
225 goto finalize;
226
227 n = strdup(rvalue);
228 if (!n)
229 return log_oom();
230
231 if (streq(n, "-"))
232 goto finalize;
233
234 r = path_simplify_and_warn(n, PATH_CHECK_ABSOLUTE | (fatal ? PATH_CHECK_FATAL : 0), unit, filename, line, lvalue);
235 if (r < 0)
236 return fatal ? -ENOEXEC : 0;
237
238 finalize:
239 return free_and_replace(*s, n);
240 }