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