]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
4e945a6f | 2 | |
b5efdb8a | 3 | #include "alloc-util.h" |
4e945a6f | 4 | #include "conf-parser.h" |
a0f29c76 | 5 | #include "def.h" |
b5efdb8a | 6 | #include "extract-word.h" |
6501dd31 | 7 | #include "hexdecoct.h" |
6bedfcbb | 8 | #include "parse-util.h" |
4e945a6f | 9 | #include "resolved-conf.h" |
6501dd31 DR |
10 | #include "resolved-dnssd.h" |
11 | #include "specifier.h" | |
1ae43295 | 12 | #include "string-table.h" |
6bedfcbb | 13 | #include "string-util.h" |
6501dd31 | 14 | #include "utf8.h" |
4e945a6f | 15 | |
1ae43295 DM |
16 | DEFINE_CONFIG_PARSE_ENUM(config_parse_dns_stub_listener_mode, dns_stub_listener_mode, DnsStubListenerMode, "Failed to parse DNS stub listener mode setting"); |
17 | ||
18 | static const char* const dns_stub_listener_mode_table[_DNS_STUB_LISTENER_MODE_MAX] = { | |
19 | [DNS_STUB_LISTENER_NO] = "no", | |
20 | [DNS_STUB_LISTENER_UDP] = "udp", | |
21 | [DNS_STUB_LISTENER_TCP] = "tcp", | |
22 | [DNS_STUB_LISTENER_YES] = "yes", | |
23 | }; | |
24 | DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_stub_listener_mode, DnsStubListenerMode, DNS_STUB_LISTENER_YES); | |
25 | ||
8e97dc67 | 26 | static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) { |
636e813d | 27 | union in_addr_union address; |
2817157b | 28 | int family, r, ifindex = 0; |
0eac4623 | 29 | DnsServer *s; |
4e945a6f LP |
30 | |
31 | assert(m); | |
636e813d LP |
32 | assert(word); |
33 | ||
2817157b | 34 | r = in_addr_ifindex_from_string_auto(word, &family, &address, &ifindex); |
636e813d LP |
35 | if (r < 0) |
36 | return r; | |
4e945a6f | 37 | |
b30bf55d LP |
38 | /* Silently filter out 0.0.0.0 and 127.0.0.53 (our own stub DNS listener) */ |
39 | if (!dns_server_address_valid(family, &address)) | |
40 | return 0; | |
41 | ||
636e813d | 42 | /* Filter out duplicates */ |
2817157b | 43 | s = dns_server_find(manager_get_first_dns_server(m, type), family, &address, ifindex); |
636e813d LP |
44 | if (s) { |
45 | /* | |
46 | * Drop the marker. This is used to find the servers | |
47 | * that ceased to exist, see | |
48 | * manager_mark_dns_servers() and | |
49 | * manager_flush_marked_dns_servers(). | |
50 | */ | |
0b58db65 | 51 | dns_server_move_back_and_unmark(s); |
636e813d LP |
52 | return 0; |
53 | } | |
54 | ||
2817157b | 55 | return dns_server_new(m, NULL, type, NULL, family, &address, ifindex); |
636e813d LP |
56 | } |
57 | ||
58 | int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) { | |
59 | int r; | |
60 | ||
61 | assert(m); | |
62 | assert(string); | |
63 | ||
9ed794a3 | 64 | for (;;) { |
b5efdb8a | 65 | _cleanup_free_ char *word = NULL; |
880603a1 SS |
66 | |
67 | r = extract_first_word(&string, &word, NULL, 0); | |
68 | if (r < 0) | |
636e813d | 69 | return r; |
880603a1 SS |
70 | if (r == 0) |
71 | break; | |
4e945a6f | 72 | |
636e813d | 73 | r = manager_add_dns_server_by_string(m, type, word); |
a51c1048 | 74 | if (r < 0) |
2817157b | 75 | log_warning_errno(r, "Failed to add DNS server address '%s', ignoring: %m", word); |
a51c1048 LP |
76 | } |
77 | ||
78 | return 0; | |
79 | } | |
80 | ||
8e97dc67 | 81 | static int manager_add_search_domain_by_string(Manager *m, const char *domain) { |
a51c1048 | 82 | DnsSearchDomain *d; |
adc800a6 | 83 | bool route_only; |
a51c1048 LP |
84 | int r; |
85 | ||
86 | assert(m); | |
87 | assert(domain); | |
88 | ||
adc800a6 LP |
89 | route_only = *domain == '~'; |
90 | if (route_only) | |
91 | domain++; | |
92 | ||
93 | if (dns_name_is_root(domain) || streq(domain, "*")) { | |
94 | route_only = true; | |
95 | domain = "."; | |
96 | } | |
97 | ||
a51c1048 LP |
98 | r = dns_search_domain_find(m->search_domains, domain, &d); |
99 | if (r < 0) | |
100 | return r; | |
adc800a6 | 101 | if (r > 0) |
a51c1048 | 102 | dns_search_domain_move_back_and_unmark(d); |
adc800a6 LP |
103 | else { |
104 | r = dns_search_domain_new(m, &d, DNS_SEARCH_DOMAIN_SYSTEM, NULL, domain); | |
105 | if (r < 0) | |
106 | return r; | |
a51c1048 LP |
107 | } |
108 | ||
adc800a6 LP |
109 | d->route_only = route_only; |
110 | return 0; | |
a51c1048 LP |
111 | } |
112 | ||
113 | int manager_parse_search_domains_and_warn(Manager *m, const char *string) { | |
114 | int r; | |
115 | ||
116 | assert(m); | |
117 | assert(string); | |
118 | ||
9ed794a3 | 119 | for (;;) { |
a51c1048 LP |
120 | _cleanup_free_ char *word = NULL; |
121 | ||
4ec85141 | 122 | r = extract_first_word(&string, &word, NULL, EXTRACT_UNQUOTE); |
a51c1048 LP |
123 | if (r < 0) |
124 | return r; | |
125 | if (r == 0) | |
126 | break; | |
127 | ||
128 | r = manager_add_search_domain_by_string(m, word); | |
129 | if (r < 0) | |
2817157b | 130 | log_warning_errno(r, "Failed to add search domain '%s', ignoring: %m", word); |
4e945a6f LP |
131 | } |
132 | ||
133 | return 0; | |
134 | } | |
135 | ||
636e813d | 136 | int config_parse_dns_servers( |
4e945a6f LP |
137 | const char *unit, |
138 | const char *filename, | |
139 | unsigned line, | |
140 | const char *section, | |
141 | unsigned section_line, | |
142 | const char *lvalue, | |
143 | int ltype, | |
144 | const char *rvalue, | |
145 | void *data, | |
146 | void *userdata) { | |
147 | ||
148 | Manager *m = userdata; | |
4e945a6f LP |
149 | int r; |
150 | ||
151 | assert(filename); | |
152 | assert(lvalue); | |
153 | assert(rvalue); | |
154 | assert(m); | |
155 | ||
3e684349 | 156 | if (isempty(rvalue)) |
5cb36f41 | 157 | /* Empty assignment means clear the list */ |
4b95f179 | 158 | dns_server_unlink_all(manager_get_first_dns_server(m, ltype)); |
3e684349 | 159 | else { |
b938cb90 | 160 | /* Otherwise, add to the list */ |
636e813d | 161 | r = manager_parse_dns_server_string_and_warn(m, ltype, rvalue); |
5cb36f41 | 162 | if (r < 0) { |
12ca818f | 163 | log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse DNS server string '%s'. Ignoring.", rvalue); |
5cb36f41 LP |
164 | return 0; |
165 | } | |
4e945a6f LP |
166 | } |
167 | ||
5cb36f41 LP |
168 | /* If we have a manual setting, then we stop reading |
169 | * /etc/resolv.conf */ | |
170 | if (ltype == DNS_SERVER_SYSTEM) | |
171 | m->read_resolv_conf = false; | |
00fa60ae LP |
172 | if (ltype == DNS_SERVER_FALLBACK) |
173 | m->need_builtin_fallbacks = false; | |
5cb36f41 | 174 | |
4e945a6f LP |
175 | return 0; |
176 | } | |
177 | ||
a51c1048 LP |
178 | int config_parse_search_domains( |
179 | const char *unit, | |
180 | const char *filename, | |
181 | unsigned line, | |
182 | const char *section, | |
183 | unsigned section_line, | |
184 | const char *lvalue, | |
185 | int ltype, | |
186 | const char *rvalue, | |
187 | void *data, | |
188 | void *userdata) { | |
189 | ||
190 | Manager *m = userdata; | |
191 | int r; | |
192 | ||
193 | assert(filename); | |
194 | assert(lvalue); | |
195 | assert(rvalue); | |
196 | assert(m); | |
197 | ||
198 | if (isempty(rvalue)) | |
199 | /* Empty assignment means clear the list */ | |
200 | dns_search_domain_unlink_all(m->search_domains); | |
201 | else { | |
202 | /* Otherwise, add to the list */ | |
203 | r = manager_parse_search_domains_and_warn(m, rvalue); | |
204 | if (r < 0) { | |
205 | log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse search domains string '%s'. Ignoring.", rvalue); | |
206 | return 0; | |
207 | } | |
208 | } | |
209 | ||
210 | /* If we have a manual setting, then we stop reading | |
211 | * /etc/resolv.conf */ | |
212 | m->read_resolv_conf = false; | |
6501dd31 DR |
213 | |
214 | return 0; | |
215 | } | |
216 | ||
217 | int config_parse_dnssd_service_name(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { | |
218 | static const Specifier specifier_table[] = { | |
219 | { 'b', specifier_boot_id, NULL }, | |
24eb8621 LP |
220 | { 'H', specifier_host_name, NULL }, |
221 | { 'm', specifier_machine_id, NULL }, | |
6501dd31 DR |
222 | { 'v', specifier_kernel_release, NULL }, |
223 | {} | |
224 | }; | |
225 | DnssdService *s = userdata; | |
226 | _cleanup_free_ char *name = NULL; | |
227 | int r; | |
228 | ||
229 | assert(filename); | |
230 | assert(lvalue); | |
231 | assert(rvalue); | |
232 | assert(s); | |
233 | ||
234 | if (isempty(rvalue)) { | |
235 | log_syntax(unit, LOG_ERR, filename, line, 0, "Service instance name can't be empty. Ignoring."); | |
236 | return -EINVAL; | |
237 | } | |
238 | ||
239 | r = free_and_strdup(&s->name_template, rvalue); | |
240 | if (r < 0) | |
241 | return log_oom(); | |
242 | ||
243 | r = specifier_printf(s->name_template, specifier_table, NULL, &name); | |
244 | if (r < 0) | |
245 | return log_debug_errno(r, "Failed to replace specifiers: %m"); | |
246 | ||
247 | if (!dns_service_name_is_valid(name)) { | |
248 | log_syntax(unit, LOG_ERR, filename, line, 0, "Service instance name template renders to invalid name '%s'. Ignoring.", name); | |
249 | return -EINVAL; | |
250 | } | |
251 | ||
252 | return 0; | |
253 | } | |
254 | ||
255 | int config_parse_dnssd_service_type(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { | |
256 | DnssdService *s = userdata; | |
257 | int r; | |
258 | ||
259 | assert(filename); | |
260 | assert(lvalue); | |
261 | assert(rvalue); | |
262 | assert(s); | |
263 | ||
264 | if (isempty(rvalue)) { | |
265 | log_syntax(unit, LOG_ERR, filename, line, 0, "Service type can't be empty. Ignoring."); | |
266 | return -EINVAL; | |
267 | } | |
268 | ||
269 | if (!dnssd_srv_type_is_valid(rvalue)) { | |
270 | log_syntax(unit, LOG_ERR, filename, line, 0, "Service type is invalid. Ignoring."); | |
271 | return -EINVAL; | |
272 | } | |
273 | ||
274 | r = free_and_strdup(&s->type, rvalue); | |
275 | if (r < 0) | |
276 | return log_oom(); | |
277 | ||
278 | return 0; | |
279 | } | |
280 | ||
281 | int config_parse_dnssd_txt(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { | |
400f54fb | 282 | _cleanup_(dnssd_txtdata_freep) DnssdTxtData *txt_data = NULL; |
6501dd31 DR |
283 | DnssdService *s = userdata; |
284 | DnsTxtItem *last = NULL; | |
285 | ||
286 | assert(filename); | |
287 | assert(lvalue); | |
288 | assert(rvalue); | |
289 | assert(s); | |
290 | ||
400f54fb DR |
291 | if (isempty(rvalue)) { |
292 | /* Flush out collected items */ | |
293 | s->txt_data_items = dnssd_txtdata_free_all(s->txt_data_items); | |
6501dd31 | 294 | return 0; |
400f54fb DR |
295 | } |
296 | ||
297 | txt_data = new0(DnssdTxtData, 1); | |
298 | if (!txt_data) | |
299 | return log_oom(); | |
6501dd31 DR |
300 | |
301 | for (;;) { | |
302 | _cleanup_free_ char *word = NULL; | |
303 | _cleanup_free_ char *key = NULL; | |
304 | _cleanup_free_ char *value = NULL; | |
305 | _cleanup_free_ void *decoded = NULL; | |
306 | size_t length = 0; | |
307 | DnsTxtItem *i; | |
308 | int r; | |
309 | ||
310 | r = extract_first_word(&rvalue, &word, NULL, | |
4ec85141 | 311 | EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX); |
6501dd31 DR |
312 | if (r == 0) |
313 | break; | |
314 | if (r == -ENOMEM) | |
315 | return log_oom(); | |
316 | if (r < 0) | |
317 | return log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); | |
318 | ||
319 | r = split_pair(word, "=", &key, &value); | |
320 | if (r == -ENOMEM) | |
321 | return log_oom(); | |
1cc6c93a YW |
322 | if (r == -EINVAL) |
323 | key = TAKE_PTR(word); | |
6501dd31 DR |
324 | |
325 | if (!ascii_is_valid(key)) { | |
326 | log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid syntax, ignoring: %s", key); | |
327 | return -EINVAL; | |
328 | } | |
329 | ||
330 | switch (ltype) { | |
331 | ||
332 | case DNS_TXT_ITEM_DATA: | |
333 | if (value) { | |
334 | r = unbase64mem(value, strlen(value), &decoded, &length); | |
335 | if (r == -ENOMEM) | |
336 | return log_oom(); | |
337 | if (r < 0) | |
338 | return log_syntax(unit, LOG_ERR, filename, line, r, | |
339 | "Invalid base64 encoding, ignoring: %s", value); | |
340 | } | |
341 | ||
342 | r = dnssd_txt_item_new_from_data(key, decoded, length, &i); | |
343 | if (r < 0) | |
344 | return log_oom(); | |
345 | break; | |
346 | ||
347 | case DNS_TXT_ITEM_TEXT: | |
348 | r = dnssd_txt_item_new_from_string(key, value, &i); | |
349 | if (r < 0) | |
350 | return log_oom(); | |
351 | break; | |
352 | ||
353 | default: | |
354 | assert_not_reached("Unknown type of Txt config"); | |
355 | } | |
356 | ||
400f54fb | 357 | LIST_INSERT_AFTER(items, txt_data->txt, last, i); |
6501dd31 DR |
358 | last = i; |
359 | } | |
400f54fb DR |
360 | |
361 | if (!LIST_IS_EMPTY(txt_data->txt)) { | |
362 | LIST_PREPEND(items, s->txt_data_items, txt_data); | |
363 | txt_data = NULL; | |
364 | } | |
a51c1048 LP |
365 | |
366 | return 0; | |
367 | } | |
368 | ||
4e945a6f | 369 | int manager_parse_config_file(Manager *m) { |
00fa60ae LP |
370 | int r; |
371 | ||
4e945a6f LP |
372 | assert(m); |
373 | ||
43688c49 | 374 | r = config_parse_many_nulstr(PKGSYSCONFDIR "/resolved.conf", |
da412854 YW |
375 | CONF_PATHS_NULSTR("systemd/resolved.conf.d"), |
376 | "Resolve\0", | |
377 | config_item_perf_lookup, resolved_gperf_lookup, | |
bcde742e | 378 | CONFIG_PARSE_WARN, m); |
00fa60ae LP |
379 | if (r < 0) |
380 | return r; | |
381 | ||
382 | if (m->need_builtin_fallbacks) { | |
383 | r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_FALLBACK, DNS_SERVERS); | |
384 | if (r < 0) | |
385 | return r; | |
386 | } | |
387 | ||
349cc4a5 | 388 | #if ! HAVE_GCRYPT |
42303dcb YW |
389 | if (m->dnssec_mode != DNSSEC_NO) { |
390 | log_warning("DNSSEC option cannot be enabled or set to allow-downgrade when systemd-resolved is built without gcrypt support. Turning off DNSSEC support."); | |
391 | m->dnssec_mode = DNSSEC_NO; | |
392 | } | |
393 | #endif | |
5d67a7ae | 394 | |
56ddbf10 | 395 | #if ! ENABLE_DNS_OVER_TLS |
c9299be2 | 396 | if (m->dns_over_tls_mode != DNS_OVER_TLS_NO) { |
4310bfc2 | 397 | log_warning("DNS-over-TLS option cannot be enabled or set to opportunistic when systemd-resolved is built without DNS-over-TLS support. Turning off DNS-over-TLS support."); |
c9299be2 | 398 | m->dns_over_tls_mode = DNS_OVER_TLS_NO; |
5d67a7ae IT |
399 | } |
400 | #endif | |
00fa60ae LP |
401 | return 0; |
402 | ||
4e945a6f | 403 | } |