]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-conf.c
tree-wide: make specifier expansion --root= aware
[thirdparty/systemd.git] / src / resolve / resolved-conf.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "alloc-util.h"
4 #include "conf-parser.h"
5 #include "def.h"
6 #include "extract-word.h"
7 #include "hexdecoct.h"
8 #include "parse-util.h"
9 #include "resolved-conf.h"
10 #include "resolved-dnssd.h"
11 #include "resolved-manager.h"
12 #include "resolved-dns-search-domain.h"
13 #include "resolved-dns-stub.h"
14 #include "dns-domain.h"
15 #include "socket-netlink.h"
16 #include "specifier.h"
17 #include "string-table.h"
18 #include "string-util.h"
19 #include "strv.h"
20 #include "utf8.h"
21
22 DEFINE_CONFIG_PARSE_ENUM(config_parse_dns_stub_listener_mode, dns_stub_listener_mode, DnsStubListenerMode, "Failed to parse DNS stub listener mode setting");
23
24 static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) {
25 _cleanup_free_ char *server_name = NULL;
26 union in_addr_union address;
27 int family, r, ifindex = 0;
28 uint16_t port;
29 DnsServer *s;
30
31 assert(m);
32 assert(word);
33
34 r = in_addr_port_ifindex_name_from_string_auto(word, &family, &address, &port, &ifindex, &server_name);
35 if (r < 0)
36 return r;
37
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
42 /* By default, the port number is determined with the transaction feature level.
43 * See dns_transaction_port() and dns_server_port(). */
44 if (IN_SET(port, 53, 853))
45 port = 0;
46
47 /* Filter out duplicates */
48 s = dns_server_find(manager_get_first_dns_server(m, type), family, &address, port, ifindex, server_name);
49 if (s) {
50 /* Drop the marker. This is used to find the servers that ceased to exist, see
51 * manager_mark_dns_servers() and manager_flush_marked_dns_servers(). */
52 dns_server_move_back_and_unmark(s);
53 return 0;
54 }
55
56 return dns_server_new(m, NULL, type, NULL, family, &address, port, ifindex, server_name);
57 }
58
59 int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) {
60 int r;
61
62 assert(m);
63 assert(string);
64
65 for (;;) {
66 _cleanup_free_ char *word = NULL;
67
68 r = extract_first_word(&string, &word, NULL, 0);
69 if (r < 0)
70 return r;
71 if (r == 0)
72 break;
73
74 r = manager_add_dns_server_by_string(m, type, word);
75 if (r < 0)
76 log_warning_errno(r, "Failed to add DNS server address '%s', ignoring: %m", word);
77 }
78
79 return 0;
80 }
81
82 static int manager_add_search_domain_by_string(Manager *m, const char *domain) {
83 DnsSearchDomain *d;
84 bool route_only;
85 int r;
86
87 assert(m);
88 assert(domain);
89
90 route_only = *domain == '~';
91 if (route_only)
92 domain++;
93
94 if (dns_name_is_root(domain) || streq(domain, "*")) {
95 route_only = true;
96 domain = ".";
97 }
98
99 r = dns_search_domain_find(m->search_domains, domain, &d);
100 if (r < 0)
101 return r;
102 if (r > 0)
103 dns_search_domain_move_back_and_unmark(d);
104 else {
105 r = dns_search_domain_new(m, &d, DNS_SEARCH_DOMAIN_SYSTEM, NULL, domain);
106 if (r < 0)
107 return r;
108 }
109
110 d->route_only = route_only;
111 return 0;
112 }
113
114 int manager_parse_search_domains_and_warn(Manager *m, const char *string) {
115 int r;
116
117 assert(m);
118 assert(string);
119
120 for (;;) {
121 _cleanup_free_ char *word = NULL;
122
123 r = extract_first_word(&string, &word, NULL, EXTRACT_UNQUOTE);
124 if (r < 0)
125 return r;
126 if (r == 0)
127 break;
128
129 r = manager_add_search_domain_by_string(m, word);
130 if (r < 0)
131 log_warning_errno(r, "Failed to add search domain '%s', ignoring: %m", word);
132 }
133
134 return 0;
135 }
136
137 int config_parse_dns_servers(
138 const char *unit,
139 const char *filename,
140 unsigned line,
141 const char *section,
142 unsigned section_line,
143 const char *lvalue,
144 int ltype,
145 const char *rvalue,
146 void *data,
147 void *userdata) {
148
149 Manager *m = userdata;
150 int r;
151
152 assert(filename);
153 assert(lvalue);
154 assert(rvalue);
155 assert(m);
156
157 if (isempty(rvalue))
158 /* Empty assignment means clear the list */
159 dns_server_unlink_all(manager_get_first_dns_server(m, ltype));
160 else {
161 /* Otherwise, add to the list */
162 r = manager_parse_dns_server_string_and_warn(m, ltype, rvalue);
163 if (r < 0) {
164 log_syntax(unit, LOG_WARNING, filename, line, r,
165 "Failed to parse DNS server string '%s', ignoring.", rvalue);
166 return 0;
167 }
168 }
169
170 /* If we have a manual setting, then we stop reading
171 * /etc/resolv.conf */
172 if (ltype == DNS_SERVER_SYSTEM)
173 m->read_resolv_conf = false;
174 if (ltype == DNS_SERVER_FALLBACK)
175 m->need_builtin_fallbacks = false;
176
177 return 0;
178 }
179
180 int config_parse_search_domains(
181 const char *unit,
182 const char *filename,
183 unsigned line,
184 const char *section,
185 unsigned section_line,
186 const char *lvalue,
187 int ltype,
188 const char *rvalue,
189 void *data,
190 void *userdata) {
191
192 Manager *m = userdata;
193 int r;
194
195 assert(filename);
196 assert(lvalue);
197 assert(rvalue);
198 assert(m);
199
200 if (isempty(rvalue))
201 /* Empty assignment means clear the list */
202 dns_search_domain_unlink_all(m->search_domains);
203 else {
204 /* Otherwise, add to the list */
205 r = manager_parse_search_domains_and_warn(m, rvalue);
206 if (r < 0) {
207 log_syntax(unit, LOG_WARNING, filename, line, r,
208 "Failed to parse search domains string '%s', ignoring.", rvalue);
209 return 0;
210 }
211 }
212
213 /* If we have a manual setting, then we stop reading
214 * /etc/resolv.conf */
215 m->read_resolv_conf = false;
216
217 return 0;
218 }
219
220 int config_parse_dnssd_service_name(
221 const char *unit,
222 const char *filename,
223 unsigned line,
224 const char *section,
225 unsigned section_line,
226 const char *lvalue,
227 int ltype,
228 const char *rvalue,
229 void *data,
230 void *userdata) {
231
232 static const Specifier specifier_table[] = {
233 { 'a', specifier_architecture, NULL },
234 { 'b', specifier_boot_id, NULL },
235 { 'B', specifier_os_build_id, NULL },
236 { 'H', specifier_host_name, NULL }, /* We will use specifier_dnssd_host_name(). */
237 { 'm', specifier_machine_id, NULL },
238 { 'o', specifier_os_id, NULL },
239 { 'v', specifier_kernel_release, NULL },
240 { 'w', specifier_os_version_id, NULL },
241 { 'W', specifier_os_variant_id, NULL },
242 {}
243 };
244 DnssdService *s = userdata;
245 _cleanup_free_ char *name = NULL;
246 int r;
247
248 assert(filename);
249 assert(lvalue);
250 assert(rvalue);
251 assert(s);
252
253 if (isempty(rvalue)) {
254 s->name_template = mfree(s->name_template);
255 return 0;
256 }
257
258 r = specifier_printf(rvalue, DNS_LABEL_MAX, specifier_table, NULL, NULL, &name);
259 if (r < 0) {
260 log_syntax(unit, LOG_WARNING, filename, line, r,
261 "Invalid service instance name template '%s', ignoring assignment: %m", rvalue);
262 return 0;
263 }
264
265 if (!dns_service_name_is_valid(name)) {
266 log_syntax(unit, LOG_WARNING, filename, line, 0,
267 "Service instance name template '%s' renders to invalid name '%s'. Ignoring assignment.",
268 rvalue, name);
269 return 0;
270 }
271
272 return free_and_strdup_warn(&s->name_template, rvalue);
273 }
274
275 int config_parse_dnssd_service_type(
276 const char *unit,
277 const char *filename,
278 unsigned line,
279 const char *section,
280 unsigned section_line,
281 const char *lvalue,
282 int ltype,
283 const char *rvalue,
284 void *data,
285 void *userdata) {
286
287 DnssdService *s = userdata;
288 int r;
289
290 assert(filename);
291 assert(lvalue);
292 assert(rvalue);
293 assert(s);
294
295 if (isempty(rvalue)) {
296 s->type = mfree(s->type);
297 return 0;
298 }
299
300 if (!dnssd_srv_type_is_valid(rvalue)) {
301 log_syntax(unit, LOG_WARNING, filename, line, 0, "Service type is invalid. Ignoring.");
302 return 0;
303 }
304
305 r = free_and_strdup(&s->type, rvalue);
306 if (r < 0)
307 return log_oom();
308
309 return 0;
310 }
311
312 int config_parse_dnssd_txt(
313 const char *unit,
314 const char *filename,
315 unsigned line,
316 const char *section,
317 unsigned section_line,
318 const char *lvalue,
319 int ltype,
320 const char *rvalue,
321 void *data,
322 void *userdata) {
323
324 _cleanup_(dnssd_txtdata_freep) DnssdTxtData *txt_data = NULL;
325 DnssdService *s = userdata;
326 DnsTxtItem *last = NULL;
327
328 assert(filename);
329 assert(lvalue);
330 assert(rvalue);
331 assert(s);
332
333 if (isempty(rvalue)) {
334 /* Flush out collected items */
335 s->txt_data_items = dnssd_txtdata_free_all(s->txt_data_items);
336 return 0;
337 }
338
339 txt_data = new0(DnssdTxtData, 1);
340 if (!txt_data)
341 return log_oom();
342
343 for (;;) {
344 _cleanup_free_ char *word = NULL, *key = NULL, *value = NULL;
345 _cleanup_free_ void *decoded = NULL;
346 size_t length = 0;
347 DnsTxtItem *i;
348 int r;
349
350 r = extract_first_word(&rvalue, &word, NULL,
351 EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX);
352 if (r == 0)
353 break;
354 if (r == -ENOMEM)
355 return log_oom();
356 if (r < 0) {
357 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
358 return 0;
359 }
360
361 r = split_pair(word, "=", &key, &value);
362 if (r == -ENOMEM)
363 return log_oom();
364 if (r == -EINVAL)
365 key = TAKE_PTR(word);
366
367 if (!ascii_is_valid(key)) {
368 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid key, ignoring: %s", key);
369 continue;
370 }
371
372 switch (ltype) {
373
374 case DNS_TXT_ITEM_DATA:
375 if (value) {
376 r = unbase64mem(value, strlen(value), &decoded, &length);
377 if (r == -ENOMEM)
378 return log_oom();
379 if (r < 0) {
380 log_syntax(unit, LOG_WARNING, filename, line, r,
381 "Invalid base64 encoding, ignoring: %s", value);
382 continue;
383 }
384 }
385
386 r = dnssd_txt_item_new_from_data(key, decoded, length, &i);
387 if (r < 0)
388 return log_oom();
389 break;
390
391 case DNS_TXT_ITEM_TEXT:
392 r = dnssd_txt_item_new_from_string(key, value, &i);
393 if (r < 0)
394 return log_oom();
395 break;
396
397 default:
398 assert_not_reached("Unknown type of Txt config");
399 }
400
401 LIST_INSERT_AFTER(items, txt_data->txt, last, i);
402 last = i;
403 }
404
405 if (!LIST_IS_EMPTY(txt_data->txt)) {
406 LIST_PREPEND(items, s->txt_data_items, txt_data);
407 TAKE_PTR(txt_data);
408 }
409
410 return 0;
411 }
412
413 int config_parse_dns_stub_listener_extra(
414 const char *unit,
415 const char *filename,
416 unsigned line,
417 const char *section,
418 unsigned section_line,
419 const char *lvalue,
420 int ltype,
421 const char *rvalue,
422 void *data,
423 void *userdata) {
424
425 _cleanup_free_ DnsStubListenerExtra *stub = NULL;
426 Manager *m = userdata;
427 const char *p;
428 int r;
429
430 assert(filename);
431 assert(lvalue);
432 assert(rvalue);
433 assert(data);
434
435 if (isempty(rvalue)) {
436 m->dns_extra_stub_listeners = ordered_set_free(m->dns_extra_stub_listeners);
437 return 0;
438 }
439
440 r = dns_stub_listener_extra_new(m, &stub);
441 if (r < 0)
442 return log_oom();
443
444 p = startswith(rvalue, "udp:");
445 if (p)
446 stub->mode = DNS_STUB_LISTENER_UDP;
447 else {
448 p = startswith(rvalue, "tcp:");
449 if (p)
450 stub->mode = DNS_STUB_LISTENER_TCP;
451 else {
452 stub->mode = DNS_STUB_LISTENER_YES;
453 p = rvalue;
454 }
455 }
456
457 r = in_addr_port_ifindex_name_from_string_auto(p, &stub->family, &stub->address, &stub->port, NULL, NULL);
458 if (r < 0) {
459 log_syntax(unit, LOG_WARNING, filename, line, r,
460 "Failed to parse address in %s=%s, ignoring assignment: %m",
461 lvalue, rvalue);
462 return 0;
463 }
464
465 r = ordered_set_ensure_put(&m->dns_extra_stub_listeners, &dns_stub_listener_extra_hash_ops, stub);
466 if (r == -ENOMEM)
467 return log_oom();
468 if (r < 0) {
469 log_syntax(unit, LOG_WARNING, filename, line, r,
470 "Failed to store %s=%s, ignoring assignment: %m", lvalue, rvalue);
471 return 0;
472 }
473
474 TAKE_PTR(stub);
475
476 return 0;
477 }
478
479 int manager_parse_config_file(Manager *m) {
480 int r;
481
482 assert(m);
483
484 r = config_parse_many_nulstr(
485 PKGSYSCONFDIR "/resolved.conf",
486 CONF_PATHS_NULSTR("systemd/resolved.conf.d"),
487 "Resolve\0",
488 config_item_perf_lookup, resolved_gperf_lookup,
489 CONFIG_PARSE_WARN,
490 m,
491 NULL);
492 if (r < 0)
493 return r;
494
495 if (m->need_builtin_fallbacks) {
496 r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_FALLBACK, DNS_SERVERS);
497 if (r < 0)
498 return r;
499 }
500
501 #if ! HAVE_GCRYPT
502 if (m->dnssec_mode != DNSSEC_NO) {
503 log_warning("DNSSEC option cannot be enabled or set to allow-downgrade when systemd-resolved is built without gcrypt support. Turning off DNSSEC support.");
504 m->dnssec_mode = DNSSEC_NO;
505 }
506 #endif
507
508 #if ! ENABLE_DNS_OVER_TLS
509 if (m->dns_over_tls_mode != DNS_OVER_TLS_NO) {
510 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.");
511 m->dns_over_tls_mode = DNS_OVER_TLS_NO;
512 }
513 #endif
514 return 0;
515
516 }