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