]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-conf.c
journal: explicitly sync namespaced journals before stopping socket units
[thirdparty/systemd.git] / src / resolve / resolved-conf.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
4e945a6f 2
b5efdb8a 3#include "alloc-util.h"
4e945a6f 4#include "conf-parser.h"
28db6fbf 5#include "constants.h"
116687f2
LP
6#include "creds-util.h"
7#include "dns-domain.h"
b5efdb8a 8#include "extract-word.h"
6501dd31 9#include "hexdecoct.h"
6bedfcbb 10#include "parse-util.h"
116687f2 11#include "proc-cmdline.h"
4e945a6f 12#include "resolved-conf.h"
be28f72d 13#include "resolved-dns-search-domain.h"
1f05101f 14#include "resolved-dns-stub.h"
116687f2
LP
15#include "resolved-dnssd.h"
16#include "resolved-manager.h"
2d95d81f 17#include "socket-netlink.h"
6501dd31 18#include "specifier.h"
1ae43295 19#include "string-table.h"
6bedfcbb 20#include "string-util.h"
1f05101f 21#include "strv.h"
6501dd31 22#include "utf8.h"
4e945a6f 23
1ae43295
DM
24DEFINE_CONFIG_PARSE_ENUM(config_parse_dns_stub_listener_mode, dns_stub_listener_mode, DnsStubListenerMode, "Failed to parse DNS stub listener mode setting");
25
8e97dc67 26static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) {
da9de738 27 _cleanup_free_ char *server_name = NULL;
636e813d 28 union in_addr_union address;
2817157b 29 int family, r, ifindex = 0;
da9de738 30 uint16_t port;
0eac4623 31 DnsServer *s;
4e945a6f
LP
32
33 assert(m);
636e813d
LP
34 assert(word);
35
da9de738 36 r = in_addr_port_ifindex_name_from_string_auto(word, &family, &address, &port, &ifindex, &server_name);
636e813d
LP
37 if (r < 0)
38 return r;
4e945a6f 39
281df579
LP
40 /* Silently filter out 0.0.0.0, 127.0.0.53, 127.0.0.54 (our own stub DNS listener) */
41 if (!dns_server_address_valid(family, &address))
42 return 0;
43
44 /* By default, the port number is determined with the transaction feature level.
da9de738
YW
45 * See dns_transaction_port() and dns_server_port(). */
46 if (IN_SET(port, 53, 853))
47 port = 0;
48
636e813d 49 /* Filter out duplicates */
1b860092 50 s = dns_server_find(manager_get_first_dns_server(m, type), family, &address, port, ifindex, server_name);
636e813d 51 if (s) {
c4c6ee3a
ZJS
52 /* Drop the marker. This is used to find the servers that ceased to exist, see
53 * manager_mark_dns_servers() and manager_flush_marked_dns_servers(). */
0b58db65 54 dns_server_move_back_and_unmark(s);
636e813d
LP
55 return 0;
56 }
57
14a52176 58 return dns_server_new(m, NULL, type, NULL, family, &address, port, ifindex, server_name, RESOLVE_CONFIG_SOURCE_FILE);
636e813d
LP
59}
60
281df579 61int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) {
636e813d
LP
62 int r;
63
64 assert(m);
65 assert(string);
66
9ed794a3 67 for (;;) {
b5efdb8a 68 _cleanup_free_ char *word = NULL;
880603a1
SS
69
70 r = extract_first_word(&string, &word, NULL, 0);
9d84fdec 71 if (r <= 0)
636e813d 72 return r;
4e945a6f 73
636e813d 74 r = manager_add_dns_server_by_string(m, type, word);
281df579 75 if (r < 0)
2817157b 76 log_warning_errno(r, "Failed to add DNS server address '%s', ignoring: %m", word);
a51c1048 77 }
a51c1048
LP
78}
79
8e97dc67 80static int manager_add_search_domain_by_string(Manager *m, const char *domain) {
a51c1048 81 DnsSearchDomain *d;
adc800a6 82 bool route_only;
a51c1048
LP
83 int r;
84
85 assert(m);
86 assert(domain);
87
adc800a6
LP
88 route_only = *domain == '~';
89 if (route_only)
90 domain++;
91
92 if (dns_name_is_root(domain) || streq(domain, "*")) {
93 route_only = true;
94 domain = ".";
95 }
96
a51c1048
LP
97 r = dns_search_domain_find(m->search_domains, domain, &d);
98 if (r < 0)
99 return r;
adc800a6 100 if (r > 0)
a51c1048 101 dns_search_domain_move_back_and_unmark(d);
adc800a6
LP
102 else {
103 r = dns_search_domain_new(m, &d, DNS_SEARCH_DOMAIN_SYSTEM, NULL, domain);
104 if (r < 0)
105 return r;
a51c1048
LP
106 }
107
adc800a6
LP
108 d->route_only = route_only;
109 return 0;
a51c1048
LP
110}
111
112int manager_parse_search_domains_and_warn(Manager *m, const char *string) {
113 int r;
114
115 assert(m);
116 assert(string);
117
9ed794a3 118 for (;;) {
a51c1048
LP
119 _cleanup_free_ char *word = NULL;
120
4ec85141 121 r = extract_first_word(&string, &word, NULL, EXTRACT_UNQUOTE);
9d84fdec 122 if (r <= 0)
a51c1048 123 return r;
a51c1048
LP
124
125 r = manager_add_search_domain_by_string(m, word);
126 if (r < 0)
2817157b 127 log_warning_errno(r, "Failed to add search domain '%s', ignoring: %m", word);
4e945a6f 128 }
4e945a6f
LP
129}
130
636e813d 131int config_parse_dns_servers(
4e945a6f
LP
132 const char *unit,
133 const char *filename,
134 unsigned line,
135 const char *section,
136 unsigned section_line,
137 const char *lvalue,
138 int ltype,
139 const char *rvalue,
140 void *data,
141 void *userdata) {
142
99534007 143 Manager *m = ASSERT_PTR(userdata);
4e945a6f
LP
144 int r;
145
146 assert(filename);
147 assert(lvalue);
148 assert(rvalue);
4e945a6f 149
3e684349 150 if (isempty(rvalue))
5cb36f41 151 /* Empty assignment means clear the list */
4b95f179 152 dns_server_unlink_all(manager_get_first_dns_server(m, ltype));
3e684349 153 else {
b938cb90 154 /* Otherwise, add to the list */
281df579 155 r = manager_parse_dns_server_string_and_warn(m, ltype, rvalue);
5cb36f41 156 if (r < 0) {
94069bef
YW
157 log_syntax(unit, LOG_WARNING, filename, line, r,
158 "Failed to parse DNS server string '%s', ignoring.", rvalue);
5cb36f41
LP
159 return 0;
160 }
4e945a6f
LP
161 }
162
281df579
LP
163 /* If we have a manual setting, then we stop reading
164 * /etc/resolv.conf */
5cb36f41
LP
165 if (ltype == DNS_SERVER_SYSTEM)
166 m->read_resolv_conf = false;
00fa60ae
LP
167 if (ltype == DNS_SERVER_FALLBACK)
168 m->need_builtin_fallbacks = false;
5cb36f41 169
4e945a6f
LP
170 return 0;
171}
172
a51c1048
LP
173int config_parse_search_domains(
174 const char *unit,
175 const char *filename,
176 unsigned line,
177 const char *section,
178 unsigned section_line,
179 const char *lvalue,
180 int ltype,
181 const char *rvalue,
182 void *data,
183 void *userdata) {
184
99534007 185 Manager *m = ASSERT_PTR(userdata);
a51c1048
LP
186 int r;
187
188 assert(filename);
189 assert(lvalue);
190 assert(rvalue);
a51c1048
LP
191
192 if (isempty(rvalue))
193 /* Empty assignment means clear the list */
194 dns_search_domain_unlink_all(m->search_domains);
195 else {
196 /* Otherwise, add to the list */
197 r = manager_parse_search_domains_and_warn(m, rvalue);
198 if (r < 0) {
94069bef
YW
199 log_syntax(unit, LOG_WARNING, filename, line, r,
200 "Failed to parse search domains string '%s', ignoring.", rvalue);
a51c1048
LP
201 return 0;
202 }
203 }
204
281df579
LP
205 /* If we have a manual setting, then we stop reading
206 * /etc/resolv.conf */
a51c1048 207 m->read_resolv_conf = false;
6501dd31
DR
208
209 return 0;
210}
211
d63542bc
YW
212int config_parse_dnssd_service_name(
213 const char *unit,
214 const char *filename,
215 unsigned line,
216 const char *section,
217 unsigned section_line,
218 const char *lvalue,
219 int ltype,
220 const char *rvalue,
221 void *data,
222 void *userdata) {
223
07e4a8dc 224 static const Specifier specifier_table[] = {
e93387f3 225 { 'a', specifier_architecture, NULL },
07e4a8dc 226 { 'b', specifier_boot_id, NULL },
e93387f3 227 { 'B', specifier_os_build_id, NULL },
9a5893e9 228 { 'H', specifier_hostname, NULL }, /* We will use specifier_dnssd_hostname(). */
e93387f3 229 { 'm', specifier_machine_id, NULL },
07e4a8dc 230 { 'o', specifier_os_id, NULL },
e93387f3 231 { 'v', specifier_kernel_release, NULL },
07e4a8dc 232 { 'w', specifier_os_version_id, NULL },
07e4a8dc
RB
233 { 'W', specifier_os_variant_id, NULL },
234 {}
235 };
99534007 236 DnssdService *s = ASSERT_PTR(userdata);
07e4a8dc 237 _cleanup_free_ char *name = NULL;
6501dd31
DR
238 int r;
239
240 assert(filename);
241 assert(lvalue);
242 assert(rvalue);
6501dd31
DR
243
244 if (isempty(rvalue)) {
0c949643
YW
245 s->name_template = mfree(s->name_template);
246 return 0;
34136e15 247 }
6501dd31 248
de61a04b 249 r = specifier_printf(rvalue, DNS_LABEL_MAX, specifier_table, NULL, NULL, &name);
dd2e9e1d
YW
250 if (r < 0) {
251 log_syntax(unit, LOG_WARNING, filename, line, r,
252 "Invalid service instance name template '%s', ignoring assignment: %m", rvalue);
253 return 0;
254 }
07e4a8dc
RB
255
256 if (!dns_service_name_is_valid(name)) {
dd2e9e1d
YW
257 log_syntax(unit, LOG_WARNING, filename, line, 0,
258 "Service instance name template '%s' renders to invalid name '%s'. Ignoring assignment.",
259 rvalue, name);
260 return 0;
07e4a8dc
RB
261 }
262
dd2e9e1d 263 return free_and_strdup_warn(&s->name_template, rvalue);
6501dd31
DR
264}
265
94069bef
YW
266int config_parse_dnssd_service_type(
267 const char *unit,
268 const char *filename,
269 unsigned line,
270 const char *section,
271 unsigned section_line,
272 const char *lvalue,
273 int ltype,
274 const char *rvalue,
275 void *data,
276 void *userdata) {
277
99534007 278 DnssdService *s = ASSERT_PTR(userdata);
6501dd31
DR
279 int r;
280
281 assert(filename);
282 assert(lvalue);
283 assert(rvalue);
6501dd31
DR
284
285 if (isempty(rvalue)) {
94069bef
YW
286 s->type = mfree(s->type);
287 return 0;
6501dd31
DR
288 }
289
290 if (!dnssd_srv_type_is_valid(rvalue)) {
94069bef
YW
291 log_syntax(unit, LOG_WARNING, filename, line, 0, "Service type is invalid. Ignoring.");
292 return 0;
6501dd31
DR
293 }
294
295 r = free_and_strdup(&s->type, rvalue);
296 if (r < 0)
297 return log_oom();
298
299 return 0;
300}
301
88123aa2
RP
302int config_parse_dnssd_service_subtype(
303 const char *unit,
304 const char *filename,
305 unsigned line,
306 const char *section,
307 unsigned section_line,
308 const char *lvalue,
309 int ltype,
310 const char *rvalue,
311 void *data,
312 void *userdata) {
313
314 DnssdService *s = ASSERT_PTR(userdata);
315
316 assert(filename);
317 assert(lvalue);
318 assert(rvalue);
319
320 if (isempty(rvalue)) {
321 s->subtype = mfree(s->subtype);
322 return 0;
323 }
324
325 if (!dns_subtype_name_is_valid(rvalue)) {
326 log_syntax(unit, LOG_WARNING, filename, line, 0, "Service subtype is invalid. Ignoring.");
327 return 0;
328 }
329
330 return free_and_strdup_warn(&s->subtype, rvalue);
331}
332
94069bef
YW
333int config_parse_dnssd_txt(
334 const char *unit,
335 const char *filename,
336 unsigned line,
337 const char *section,
338 unsigned section_line,
339 const char *lvalue,
340 int ltype,
341 const char *rvalue,
342 void *data,
343 void *userdata) {
344
400f54fb 345 _cleanup_(dnssd_txtdata_freep) DnssdTxtData *txt_data = NULL;
99534007 346 DnssdService *s = ASSERT_PTR(userdata);
6501dd31
DR
347 DnsTxtItem *last = NULL;
348
349 assert(filename);
350 assert(lvalue);
351 assert(rvalue);
6501dd31 352
400f54fb
DR
353 if (isempty(rvalue)) {
354 /* Flush out collected items */
355 s->txt_data_items = dnssd_txtdata_free_all(s->txt_data_items);
6501dd31 356 return 0;
400f54fb
DR
357 }
358
359 txt_data = new0(DnssdTxtData, 1);
360 if (!txt_data)
361 return log_oom();
6501dd31
DR
362
363 for (;;) {
94069bef 364 _cleanup_free_ char *word = NULL, *key = NULL, *value = NULL;
6501dd31
DR
365 _cleanup_free_ void *decoded = NULL;
366 size_t length = 0;
367 DnsTxtItem *i;
368 int r;
369
370 r = extract_first_word(&rvalue, &word, NULL,
3141089f 371 EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX);
6501dd31
DR
372 if (r == 0)
373 break;
374 if (r == -ENOMEM)
375 return log_oom();
94069bef
YW
376 if (r < 0) {
377 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
378 return 0;
379 }
6501dd31
DR
380
381 r = split_pair(word, "=", &key, &value);
382 if (r == -ENOMEM)
383 return log_oom();
1cc6c93a
YW
384 if (r == -EINVAL)
385 key = TAKE_PTR(word);
6501dd31
DR
386
387 if (!ascii_is_valid(key)) {
94069bef
YW
388 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid key, ignoring: %s", key);
389 continue;
6501dd31
DR
390 }
391
392 switch (ltype) {
393
394 case DNS_TXT_ITEM_DATA:
395 if (value) {
bdd2036e 396 r = unbase64mem(value, &decoded, &length);
6501dd31
DR
397 if (r == -ENOMEM)
398 return log_oom();
94069bef
YW
399 if (r < 0) {
400 log_syntax(unit, LOG_WARNING, filename, line, r,
401 "Invalid base64 encoding, ignoring: %s", value);
402 continue;
403 }
6501dd31
DR
404 }
405
406 r = dnssd_txt_item_new_from_data(key, decoded, length, &i);
407 if (r < 0)
408 return log_oom();
409 break;
410
411 case DNS_TXT_ITEM_TEXT:
412 r = dnssd_txt_item_new_from_string(key, value, &i);
413 if (r < 0)
414 return log_oom();
415 break;
416
417 default:
04499a70 418 assert_not_reached();
6501dd31
DR
419 }
420
d70f15f5 421 LIST_INSERT_AFTER(items, txt_data->txts, last, i);
6501dd31
DR
422 last = i;
423 }
400f54fb 424
d70f15f5 425 if (txt_data->txts) {
400f54fb 426 LIST_PREPEND(items, s->txt_data_items, txt_data);
94069bef 427 TAKE_PTR(txt_data);
400f54fb 428 }
a51c1048
LP
429
430 return 0;
431}
432
1f05101f
SS
433int config_parse_dns_stub_listener_extra(
434 const char *unit,
435 const char *filename,
436 unsigned line,
437 const char *section,
438 unsigned section_line,
439 const char *lvalue,
440 int ltype,
441 const char *rvalue,
442 void *data,
443 void *userdata) {
444
36aaabc3 445 _cleanup_free_ DnsStubListenerExtra *stub = NULL;
1f05101f 446 Manager *m = userdata;
1f05101f
SS
447 const char *p;
448 int r;
449
450 assert(filename);
451 assert(lvalue);
452 assert(rvalue);
453 assert(data);
454
455 if (isempty(rvalue)) {
456 m->dns_extra_stub_listeners = ordered_set_free(m->dns_extra_stub_listeners);
457 return 0;
458 }
459
0354029b 460 r = dns_stub_listener_extra_new(m, &stub);
7314b397 461 if (r < 0)
1f05101f 462 return log_oom();
1f05101f 463
7314b397
YW
464 p = startswith(rvalue, "udp:");
465 if (p)
466 stub->mode = DNS_STUB_LISTENER_UDP;
467 else {
468 p = startswith(rvalue, "tcp:");
469 if (p)
470 stub->mode = DNS_STUB_LISTENER_TCP;
471 else {
472 stub->mode = DNS_STUB_LISTENER_YES;
473 p = rvalue;
1f05101f
SS
474 }
475 }
476
222eaaf9 477 r = in_addr_port_ifindex_name_from_string_auto(p, &stub->family, &stub->address, &stub->port, NULL, NULL);
7314b397
YW
478 if (r < 0) {
479 log_syntax(unit, LOG_WARNING, filename, line, r,
480 "Failed to parse address in %s=%s, ignoring assignment: %m",
481 lvalue, rvalue);
482 return 0;
1f05101f
SS
483 }
484
7314b397
YW
485 r = ordered_set_ensure_put(&m->dns_extra_stub_listeners, &dns_stub_listener_extra_hash_ops, stub);
486 if (r == -ENOMEM)
487 return log_oom();
488 if (r < 0) {
489 log_syntax(unit, LOG_WARNING, filename, line, r,
490 "Failed to store %s=%s, ignoring assignment: %m", lvalue, rvalue);
491 return 0;
1f05101f
SS
492 }
493
7314b397 494 TAKE_PTR(stub);
1f05101f
SS
495
496 return 0;
497}
498
116687f2
LP
499static void read_credentials(Manager *m) {
500 _cleanup_free_ char *dns = NULL, *domains = NULL;
501 int r;
502
503 assert(m);
504
505 /* Hmm, if we aren't supposed to read /etc/resolv.conf because the DNS settings were already
506 * configured explicitly in our config file, we don't want to honour credentials either */
507 if (!m->read_resolv_conf)
508 return;
509
55ace8e5
ZJS
510 r = read_credential_strings_many("network.dns", &dns,
511 "network.search_domains", &domains);
512 if (r < 0)
116687f2
LP
513 log_warning_errno(r, "Failed to read credentials, ignoring: %m");
514
515 if (dns) {
516 r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_SYSTEM, dns);
517 if (r < 0)
1c7ed990 518 log_warning_errno(r, "Failed to parse credential network.dns '%s', ignoring.", dns);
116687f2
LP
519
520 m->read_resolv_conf = false;
521 }
522
523 if (domains) {
524 r = manager_parse_search_domains_and_warn(m, domains);
525 if (r < 0)
1c7ed990 526 log_warning_errno(r, "Failed to parse credential network.search_domains '%s', ignoring.", domains);
116687f2
LP
527
528 m->read_resolv_conf = false;
529 }
530}
531
532struct ProcCmdlineInfo {
533 Manager *manager;
534
535 /* If there's a setting configured via /proc/cmdline we want to reset the configured lists, but only
536 * once, so that multiple nameserver= or domain= settings can be specified on the kernel command line
537 * and will be combined. These booleans will be set once we erase the list once. */
538 bool dns_server_unlinked;
539 bool search_domain_unlinked;
540};
541
542static int proc_cmdline_callback(const char *key, const char *value, void *data) {
543 struct ProcCmdlineInfo *info = ASSERT_PTR(data);
544 int r;
545
a8d3315b 546 assert(key);
116687f2
LP
547 assert(info->manager);
548
549 /* The kernel command line option names are chosen to be compatible with what various tools already
550 * interpret, for example dracut and SUSE Linux. */
551
a8d3315b 552 if (streq(key, "nameserver")) {
91acee99
YW
553
554 if (proc_cmdline_value_missing(key, value))
555 return 0;
556
116687f2
LP
557 if (!info->dns_server_unlinked) {
558 /* The kernel command line overrides any prior configuration */
559 dns_server_unlink_all(manager_get_first_dns_server(info->manager, DNS_SERVER_SYSTEM));
560 info->dns_server_unlinked = true;
561 }
562
563 r = manager_parse_dns_server_string_and_warn(info->manager, DNS_SERVER_SYSTEM, value);
564 if (r < 0)
565 log_warning_errno(r, "Failed to parse DNS server string '%s', ignoring.", value);
566
567 info->manager->read_resolv_conf = false;
568
a8d3315b 569 } else if (streq(key, "domain")) {
116687f2 570
91acee99
YW
571 if (proc_cmdline_value_missing(key, value))
572 return 0;
573
116687f2
LP
574 if (!info->search_domain_unlinked) {
575 dns_search_domain_unlink_all(info->manager->search_domains);
576 info->search_domain_unlinked = true;
577 }
578
579 r = manager_parse_search_domains_and_warn(info->manager, value);
580 if (r < 0)
581 log_warning_errno(r, "Failed to parse credential provided search domain string '%s', ignoring.", value);
582
583 info->manager->read_resolv_conf = false;
584 }
585
586 return 0;
587}
588
589static void read_proc_cmdline(Manager *m) {
590 int r;
591
592 assert(m);
593
594 r = proc_cmdline_parse(proc_cmdline_callback, &(struct ProcCmdlineInfo) { .manager = m }, 0);
595 if (r < 0)
596 log_warning_errno(r, "Failed to read kernel command line, ignoring: %m");
597}
598
4e945a6f 599int manager_parse_config_file(Manager *m) {
00fa60ae
LP
600 int r;
601
4e945a6f
LP
602 assert(m);
603
6378f257
ZJS
604 r = config_parse_standard_file_with_dropins(
605 "systemd/resolved.conf",
606 "Resolve\0",
607 config_item_perf_lookup, resolved_gperf_lookup,
608 CONFIG_PARSE_WARN,
609 /* userdata= */ m);
00fa60ae
LP
610 if (r < 0)
611 return r;
612
116687f2
LP
613 read_credentials(m); /* credentials are only used when nothing is explicitly configured … */
614 read_proc_cmdline(m); /* … but kernel command line overrides local configuration. */
615
00fa60ae 616 if (m->need_builtin_fallbacks) {
281df579 617 r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_FALLBACK, DNS_SERVERS);
00fa60ae
LP
618 if (r < 0)
619 return r;
620 }
621
7e8facb3 622#if !HAVE_OPENSSL_OR_GCRYPT
42303dcb 623 if (m->dnssec_mode != DNSSEC_NO) {
7e8facb3 624 log_warning("DNSSEC option cannot be enabled or set to allow-downgrade when systemd-resolved is built without a cryptographic library. Turning off DNSSEC support.");
42303dcb
YW
625 m->dnssec_mode = DNSSEC_NO;
626 }
627#endif
5d67a7ae 628
7e8facb3 629#if !ENABLE_DNS_OVER_TLS
c9299be2 630 if (m->dns_over_tls_mode != DNS_OVER_TLS_NO) {
4310bfc2 631 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 632 m->dns_over_tls_mode = DNS_OVER_TLS_NO;
5d67a7ae
IT
633 }
634#endif
00fa60ae
LP
635 return 0;
636
4e945a6f 637}