Coverage Report

Created: 2025-11-16 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/systemd/src/shared/parse-helpers.c
Line
Count
Source
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3
#include <sys/socket.h>
4
5
#include "af-list.h"
6
#include "alloc-util.h"
7
#include "extract-word.h"
8
#include "ip-protocol-list.h"
9
#include "log.h"
10
#include "mountpoint-util.h"
11
#include "parse-helpers.h"
12
#include "parse-util.h"
13
#include "path-util.h"
14
#include "string-util.h"
15
#include "utf8.h"
16
17
46.8k
static bool validate_api_vfs(const char *path, PathSimplifyWarnFlags flags) {
18
19
46.8k
        assert(path);
20
21
46.8k
        if ((flags & (PATH_CHECK_NON_API_VFS|PATH_CHECK_NON_API_VFS_DEV_OK)) == 0)
22
29.7k
                return true;
23
24
17.1k
        if (!path_below_api_vfs(path))
25
15.6k
                return true;
26
27
1.46k
        if (FLAGS_SET(flags, PATH_CHECK_NON_API_VFS_DEV_OK) && path_startswith(path, "/dev"))
28
358
                return true;
29
30
1.10k
        return false;
31
1.46k
}
32
33
int path_simplify_and_warn(
34
                char *path,
35
                PathSimplifyWarnFlags flags,
36
                const char *unit,
37
                const char *filename,
38
                unsigned line,
39
61.4k
                const char *lvalue) {
40
41
61.4k
        bool fatal = flags & PATH_CHECK_FATAL;
42
61.4k
        int level = fatal ? LOG_ERR : LOG_WARNING;
43
44
61.4k
        assert(path);
45
61.4k
        assert(!FLAGS_SET(flags, PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE));
46
61.4k
        assert(!FLAGS_SET(flags, PATH_CHECK_NON_API_VFS | PATH_CHECK_NON_API_VFS_DEV_OK));
47
61.4k
        assert(lvalue);
48
49
61.4k
        if (!utf8_is_valid(path))
50
1.68k
                return log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, path);
51
52
59.7k
        if (flags & (PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)) {
53
54.2k
                bool absolute;
54
55
54.2k
                absolute = path_is_absolute(path);
56
57
54.2k
                if (!absolute && (flags & PATH_CHECK_ABSOLUTE))
58
9.12k
                        return log_syntax(unit, level, filename, line, SYNTHETIC_ERRNO(EINVAL),
59
54.2k
                                          "%s= path is not absolute%s: %s",
60
54.2k
                                          lvalue, fatal ? "" : ", ignoring", path);
61
62
45.1k
                if (absolute && (flags & PATH_CHECK_RELATIVE))
63
644
                        return log_syntax(unit, level, filename, line, SYNTHETIC_ERRNO(EINVAL),
64
45.1k
                                          "%s= path is absolute%s: %s",
65
45.1k
                                          lvalue, fatal ? "" : ", ignoring", path);
66
45.1k
        }
67
68
49.9k
        path_simplify_full(path, flags & PATH_KEEP_TRAILING_SLASH ? PATH_SIMPLIFY_KEEP_TRAILING_SLASH : 0);
69
70
49.9k
        if (!path_is_valid(path))
71
1.42k
                return log_syntax(unit, level, filename, line, SYNTHETIC_ERRNO(EINVAL),
72
49.9k
                                  "%s= path has invalid length (%zu bytes)%s.",
73
49.9k
                                  lvalue, strlen(path), fatal ? "" : ", ignoring");
74
75
48.5k
        if (!path_is_normalized(path))
76
1.66k
                return log_syntax(unit, level, filename, line, SYNTHETIC_ERRNO(EINVAL),
77
48.5k
                                  "%s= path is not normalized%s: %s",
78
48.5k
                                  lvalue, fatal ? "" : ", ignoring", path);
79
80
46.8k
        if (!validate_api_vfs(path, flags))
81
1.10k
                return log_syntax(unit, level, filename, line, SYNTHETIC_ERRNO(EINVAL),
82
46.8k
                                  "%s= path is below API VFS%s: %s",
83
46.8k
                                  lvalue, fatal ? ", refusing" : ", ignoring",
84
46.8k
                                  path);
85
86
45.7k
        return 0;
87
46.8k
}
88
89
static int parse_af_token(
90
                const char *token,
91
                int *family,
92
                int *ip_protocol,
93
                uint16_t *nr_ports,
94
5.35k
                uint16_t *port_min) {
95
96
5.35k
        int af;
97
98
5.35k
        assert(token);
99
5.35k
        assert(family);
100
101
5.35k
        af = af_from_ipv4_ipv6(token);
102
5.35k
        if (af == AF_UNSPEC)
103
4.82k
                return -EINVAL;
104
105
533
        *family = af;
106
533
        return 0;
107
5.35k
}
108
109
static int parse_ip_protocol_token(
110
                const char *token,
111
                int *family,
112
                int *ip_protocol,
113
                uint16_t *nr_ports,
114
5.09k
                uint16_t *port_min) {
115
116
5.09k
        int proto;
117
118
5.09k
        assert(token);
119
5.09k
        assert(ip_protocol);
120
121
5.09k
        proto = ip_protocol_from_tcp_udp(token);
122
5.09k
        if (proto < 0)
123
4.57k
                return -EINVAL;
124
125
519
        *ip_protocol = proto;
126
519
        return 0;
127
5.09k
}
128
129
static int parse_ip_ports_token(
130
                const char *token,
131
                int *family,
132
                int *ip_protocol,
133
                uint16_t *nr_ports,
134
4.57k
                uint16_t *port_min) {
135
136
4.57k
        int r;
137
138
4.57k
        assert(token);
139
4.57k
        assert(nr_ports);
140
4.57k
        assert(port_min);
141
142
4.57k
        if (streq(token, "any"))
143
588
                *nr_ports = *port_min = 0;
144
3.98k
        else {
145
3.98k
                uint16_t mn = 0, mx = 0;
146
3.98k
                r = parse_ip_port_range(token, &mn, &mx, /* allow_zero = */ true);
147
3.98k
                if (r < 0)
148
1.39k
                        return r;
149
150
2.59k
                *nr_ports = mx - mn + 1;
151
2.59k
                *port_min = mn;
152
2.59k
        }
153
154
3.18k
        return 0;
155
4.57k
}
156
157
typedef int (*parse_token_f)(
158
                const char *,
159
                int *,
160
                int *,
161
                uint16_t *,
162
                uint16_t *);
163
164
int parse_socket_bind_item(
165
                const char *str,
166
                int *address_family,
167
                int *ip_protocol,
168
                uint16_t *nr_ports,
169
5.62k
                uint16_t *port_min) {
170
171
        /* Order of token parsers is important. */
172
5.62k
        const parse_token_f parsers[] = {
173
5.62k
                &parse_af_token,
174
5.62k
                &parse_ip_protocol_token,
175
5.62k
                &parse_ip_ports_token,
176
5.62k
        };
177
5.62k
        parse_token_f const *parser_ptr = parsers;
178
5.62k
        int af = AF_UNSPEC, proto = 0, r;
179
5.62k
        uint16_t nr = 0, mn = 0;
180
5.62k
        const char *p = ASSERT_PTR(str);
181
182
5.62k
        assert(address_family);
183
5.62k
        assert(ip_protocol);
184
5.62k
        assert(nr_ports);
185
5.62k
        assert(port_min);
186
187
5.62k
        if (isempty(p))
188
8
                return -EINVAL;
189
190
6.67k
        for (;;) {
191
6.67k
                _cleanup_free_ char *token = NULL;
192
193
6.67k
                r = extract_first_word(&p, &token, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
194
6.67k
                if (r == 0)
195
513
                        break;
196
6.16k
                if (r < 0)
197
197
                        return r;
198
199
5.96k
                if (isempty(token))
200
334
                        return -EINVAL;
201
202
16.4k
                while (parser_ptr != parsers + ELEMENTSOF(parsers)) {
203
15.0k
                        r = (*parser_ptr)(token, &af, &proto, &nr, &mn);
204
15.0k
                        if (r == -ENOMEM)
205
0
                                return r;
206
207
15.0k
                        ++parser_ptr;
208
                        /* Continue to next token if parsing succeeded,
209
                         * otherwise apply next parser to the same token.
210
                         */
211
15.0k
                        if (r >= 0)
212
4.23k
                                break;
213
15.0k
                }
214
5.62k
                if (parser_ptr == parsers + ELEMENTSOF(parsers))
215
4.57k
                                break;
216
5.62k
        }
217
218
        /* Failed to parse a token. */
219
5.09k
        if (r < 0)
220
1.39k
                return r;
221
222
        /* Parsers applied successfully, but end of the string not reached. */
223
3.69k
        if (p)
224
212
                return -EINVAL;
225
226
3.48k
        *address_family = af;
227
3.48k
        *ip_protocol = proto;
228
3.48k
        *nr_ports = nr;
229
3.48k
        *port_min = mn;
230
231
3.48k
        return 0;
232
3.69k
}
233
234
int config_parse_path_or_ignore(
235
                const char *unit,
236
                const char *filename,
237
                unsigned line,
238
                const char *section,
239
                unsigned section_line,
240
                const char *lvalue,
241
                int ltype,
242
                const char *rvalue,
243
                void *data,
244
0
                void *userdata) {
245
246
0
        _cleanup_free_ char *n = NULL;
247
0
        bool fatal = ltype;
248
0
        char **s = ASSERT_PTR(data);
249
0
        int r;
250
251
0
        assert(filename);
252
0
        assert(lvalue);
253
0
        assert(rvalue);
254
255
0
        if (isempty(rvalue))
256
0
                goto finalize;
257
258
0
        n = strdup(rvalue);
259
0
        if (!n)
260
0
                return log_oom();
261
262
0
        if (streq(n, "-"))
263
0
                goto finalize;
264
265
0
        r = path_simplify_and_warn(n, PATH_CHECK_ABSOLUTE | (fatal ? PATH_CHECK_FATAL : 0), unit, filename, line, lvalue);
266
0
        if (r < 0)
267
0
                return fatal ? -ENOEXEC : 0;
268
269
0
finalize:
270
        return free_and_replace(*s, n);
271
0
}