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
manager_add_dns_server_by_string(Manager * m,DnsServerType type,const char * word)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, 127.0.0.53, 127.0.0.54 (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
manager_parse_dns_server_string_and_warn(Manager * m,DnsServerType type,const char * string)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
72 r = manager_add_dns_server_by_string(m, type, word);
73 if (r < 0)
74 log_warning_errno(r, "Failed to add DNS server address '%s', ignoring: %m", word);
75 }
76 }
77
manager_add_search_domain_by_string(Manager * m,const char * domain)78 static int manager_add_search_domain_by_string(Manager *m, const char *domain) {
79 DnsSearchDomain *d;
80 bool route_only;
81 int r;
82
83 assert(m);
84 assert(domain);
85
86 route_only = *domain == '~';
87 if (route_only)
88 domain++;
89
90 if (dns_name_is_root(domain) || streq(domain, "*")) {
91 route_only = true;
92 domain = ".";
93 }
94
95 r = dns_search_domain_find(m->search_domains, domain, &d);
96 if (r < 0)
97 return r;
98 if (r > 0)
99 dns_search_domain_move_back_and_unmark(d);
100 else {
101 r = dns_search_domain_new(m, &d, DNS_SEARCH_DOMAIN_SYSTEM, NULL, domain);
102 if (r < 0)
103 return r;
104 }
105
106 d->route_only = route_only;
107 return 0;
108 }
109
manager_parse_search_domains_and_warn(Manager * m,const char * string)110 int manager_parse_search_domains_and_warn(Manager *m, const char *string) {
111 int r;
112
113 assert(m);
114 assert(string);
115
116 for (;;) {
117 _cleanup_free_ char *word = NULL;
118
119 r = extract_first_word(&string, &word, NULL, EXTRACT_UNQUOTE);
120 if (r <= 0)
121 return r;
122
123 r = manager_add_search_domain_by_string(m, word);
124 if (r < 0)
125 log_warning_errno(r, "Failed to add search domain '%s', ignoring: %m", word);
126 }
127 }
128
config_parse_dns_servers(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)129 int config_parse_dns_servers(
130 const char *unit,
131 const char *filename,
132 unsigned line,
133 const char *section,
134 unsigned section_line,
135 const char *lvalue,
136 int ltype,
137 const char *rvalue,
138 void *data,
139 void *userdata) {
140
141 Manager *m = userdata;
142 int r;
143
144 assert(filename);
145 assert(lvalue);
146 assert(rvalue);
147 assert(m);
148
149 if (isempty(rvalue))
150 /* Empty assignment means clear the list */
151 dns_server_unlink_all(manager_get_first_dns_server(m, ltype));
152 else {
153 /* Otherwise, add to the list */
154 r = manager_parse_dns_server_string_and_warn(m, ltype, rvalue);
155 if (r < 0) {
156 log_syntax(unit, LOG_WARNING, filename, line, r,
157 "Failed to parse DNS server string '%s', ignoring.", rvalue);
158 return 0;
159 }
160 }
161
162 /* If we have a manual setting, then we stop reading
163 * /etc/resolv.conf */
164 if (ltype == DNS_SERVER_SYSTEM)
165 m->read_resolv_conf = false;
166 if (ltype == DNS_SERVER_FALLBACK)
167 m->need_builtin_fallbacks = false;
168
169 return 0;
170 }
171
config_parse_search_domains(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)172 int config_parse_search_domains(
173 const char *unit,
174 const char *filename,
175 unsigned line,
176 const char *section,
177 unsigned section_line,
178 const char *lvalue,
179 int ltype,
180 const char *rvalue,
181 void *data,
182 void *userdata) {
183
184 Manager *m = userdata;
185 int r;
186
187 assert(filename);
188 assert(lvalue);
189 assert(rvalue);
190 assert(m);
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) {
199 log_syntax(unit, LOG_WARNING, filename, line, r,
200 "Failed to parse search domains string '%s', ignoring.", rvalue);
201 return 0;
202 }
203 }
204
205 /* If we have a manual setting, then we stop reading
206 * /etc/resolv.conf */
207 m->read_resolv_conf = false;
208
209 return 0;
210 }
211
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)212 int 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
224 static const Specifier specifier_table[] = {
225 { 'a', specifier_architecture, NULL },
226 { 'b', specifier_boot_id, NULL },
227 { 'B', specifier_os_build_id, NULL },
228 { 'H', specifier_hostname, NULL }, /* We will use specifier_dnssd_hostname(). */
229 { 'm', specifier_machine_id, NULL },
230 { 'o', specifier_os_id, NULL },
231 { 'v', specifier_kernel_release, NULL },
232 { 'w', specifier_os_version_id, NULL },
233 { 'W', specifier_os_variant_id, NULL },
234 {}
235 };
236 DnssdService *s = userdata;
237 _cleanup_free_ char *name = NULL;
238 int r;
239
240 assert(filename);
241 assert(lvalue);
242 assert(rvalue);
243 assert(s);
244
245 if (isempty(rvalue)) {
246 s->name_template = mfree(s->name_template);
247 return 0;
248 }
249
250 r = specifier_printf(rvalue, DNS_LABEL_MAX, specifier_table, NULL, NULL, &name);
251 if (r < 0) {
252 log_syntax(unit, LOG_WARNING, filename, line, r,
253 "Invalid service instance name template '%s', ignoring assignment: %m", rvalue);
254 return 0;
255 }
256
257 if (!dns_service_name_is_valid(name)) {
258 log_syntax(unit, LOG_WARNING, filename, line, 0,
259 "Service instance name template '%s' renders to invalid name '%s'. Ignoring assignment.",
260 rvalue, name);
261 return 0;
262 }
263
264 return free_and_strdup_warn(&s->name_template, rvalue);
265 }
266
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)267 int config_parse_dnssd_service_type(
268 const char *unit,
269 const char *filename,
270 unsigned line,
271 const char *section,
272 unsigned section_line,
273 const char *lvalue,
274 int ltype,
275 const char *rvalue,
276 void *data,
277 void *userdata) {
278
279 DnssdService *s = userdata;
280 int r;
281
282 assert(filename);
283 assert(lvalue);
284 assert(rvalue);
285 assert(s);
286
287 if (isempty(rvalue)) {
288 s->type = mfree(s->type);
289 return 0;
290 }
291
292 if (!dnssd_srv_type_is_valid(rvalue)) {
293 log_syntax(unit, LOG_WARNING, filename, line, 0, "Service type is invalid. Ignoring.");
294 return 0;
295 }
296
297 r = free_and_strdup(&s->type, rvalue);
298 if (r < 0)
299 return log_oom();
300
301 return 0;
302 }
303
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)304 int config_parse_dnssd_txt(
305 const char *unit,
306 const char *filename,
307 unsigned line,
308 const char *section,
309 unsigned section_line,
310 const char *lvalue,
311 int ltype,
312 const char *rvalue,
313 void *data,
314 void *userdata) {
315
316 _cleanup_(dnssd_txtdata_freep) DnssdTxtData *txt_data = NULL;
317 DnssdService *s = userdata;
318 DnsTxtItem *last = NULL;
319
320 assert(filename);
321 assert(lvalue);
322 assert(rvalue);
323 assert(s);
324
325 if (isempty(rvalue)) {
326 /* Flush out collected items */
327 s->txt_data_items = dnssd_txtdata_free_all(s->txt_data_items);
328 return 0;
329 }
330
331 txt_data = new0(DnssdTxtData, 1);
332 if (!txt_data)
333 return log_oom();
334
335 for (;;) {
336 _cleanup_free_ char *word = NULL, *key = NULL, *value = NULL;
337 _cleanup_free_ void *decoded = NULL;
338 size_t length = 0;
339 DnsTxtItem *i;
340 int r;
341
342 r = extract_first_word(&rvalue, &word, NULL,
343 EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX);
344 if (r == 0)
345 break;
346 if (r == -ENOMEM)
347 return log_oom();
348 if (r < 0) {
349 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
350 return 0;
351 }
352
353 r = split_pair(word, "=", &key, &value);
354 if (r == -ENOMEM)
355 return log_oom();
356 if (r == -EINVAL)
357 key = TAKE_PTR(word);
358
359 if (!ascii_is_valid(key)) {
360 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid key, ignoring: %s", key);
361 continue;
362 }
363
364 switch (ltype) {
365
366 case DNS_TXT_ITEM_DATA:
367 if (value) {
368 r = unbase64mem(value, strlen(value), &decoded, &length);
369 if (r == -ENOMEM)
370 return log_oom();
371 if (r < 0) {
372 log_syntax(unit, LOG_WARNING, filename, line, r,
373 "Invalid base64 encoding, ignoring: %s", value);
374 continue;
375 }
376 }
377
378 r = dnssd_txt_item_new_from_data(key, decoded, length, &i);
379 if (r < 0)
380 return log_oom();
381 break;
382
383 case DNS_TXT_ITEM_TEXT:
384 r = dnssd_txt_item_new_from_string(key, value, &i);
385 if (r < 0)
386 return log_oom();
387 break;
388
389 default:
390 assert_not_reached();
391 }
392
393 LIST_INSERT_AFTER(items, txt_data->txt, last, i);
394 last = i;
395 }
396
397 if (!LIST_IS_EMPTY(txt_data->txt)) {
398 LIST_PREPEND(items, s->txt_data_items, txt_data);
399 TAKE_PTR(txt_data);
400 }
401
402 return 0;
403 }
404
config_parse_dns_stub_listener_extra(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)405 int config_parse_dns_stub_listener_extra(
406 const char *unit,
407 const char *filename,
408 unsigned line,
409 const char *section,
410 unsigned section_line,
411 const char *lvalue,
412 int ltype,
413 const char *rvalue,
414 void *data,
415 void *userdata) {
416
417 _cleanup_free_ DnsStubListenerExtra *stub = NULL;
418 Manager *m = userdata;
419 const char *p;
420 int r;
421
422 assert(filename);
423 assert(lvalue);
424 assert(rvalue);
425 assert(data);
426
427 if (isempty(rvalue)) {
428 m->dns_extra_stub_listeners = ordered_set_free(m->dns_extra_stub_listeners);
429 return 0;
430 }
431
432 r = dns_stub_listener_extra_new(m, &stub);
433 if (r < 0)
434 return log_oom();
435
436 p = startswith(rvalue, "udp:");
437 if (p)
438 stub->mode = DNS_STUB_LISTENER_UDP;
439 else {
440 p = startswith(rvalue, "tcp:");
441 if (p)
442 stub->mode = DNS_STUB_LISTENER_TCP;
443 else {
444 stub->mode = DNS_STUB_LISTENER_YES;
445 p = rvalue;
446 }
447 }
448
449 r = in_addr_port_ifindex_name_from_string_auto(p, &stub->family, &stub->address, &stub->port, NULL, NULL);
450 if (r < 0) {
451 log_syntax(unit, LOG_WARNING, filename, line, r,
452 "Failed to parse address in %s=%s, ignoring assignment: %m",
453 lvalue, rvalue);
454 return 0;
455 }
456
457 r = ordered_set_ensure_put(&m->dns_extra_stub_listeners, &dns_stub_listener_extra_hash_ops, stub);
458 if (r == -ENOMEM)
459 return log_oom();
460 if (r < 0) {
461 log_syntax(unit, LOG_WARNING, filename, line, r,
462 "Failed to store %s=%s, ignoring assignment: %m", lvalue, rvalue);
463 return 0;
464 }
465
466 TAKE_PTR(stub);
467
468 return 0;
469 }
470
manager_parse_config_file(Manager * m)471 int manager_parse_config_file(Manager *m) {
472 int r;
473
474 assert(m);
475
476 r = config_parse_many_nulstr(
477 PKGSYSCONFDIR "/resolved.conf",
478 CONF_PATHS_NULSTR("systemd/resolved.conf.d"),
479 "Resolve\0",
480 config_item_perf_lookup, resolved_gperf_lookup,
481 CONFIG_PARSE_WARN,
482 m,
483 NULL);
484 if (r < 0)
485 return r;
486
487 if (m->need_builtin_fallbacks) {
488 r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_FALLBACK, DNS_SERVERS);
489 if (r < 0)
490 return r;
491 }
492
493 #if !HAVE_OPENSSL_OR_GCRYPT
494 if (m->dnssec_mode != DNSSEC_NO) {
495 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.");
496 m->dnssec_mode = DNSSEC_NO;
497 }
498 #endif
499
500 #if !ENABLE_DNS_OVER_TLS
501 if (m->dns_over_tls_mode != DNS_OVER_TLS_NO) {
502 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.");
503 m->dns_over_tls_mode = DNS_OVER_TLS_NO;
504 }
505 #endif
506 return 0;
507
508 }
509