]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-conf.c
Merge pull request #16976 from keszybz/systemctl-service-log-levels
[thirdparty/systemd.git] / src / resolve / resolved-conf.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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 DnssdService *s = userdata;
233 int r;
234
235 assert(filename);
236 assert(lvalue);
237 assert(rvalue);
238 assert(s);
239
240 if (isempty(rvalue)) {
241 s->name_template = mfree(s->name_template);
242 return 0;
243 }
244
245 r = dnssd_render_instance_name(rvalue, NULL);
246 if (r == -ENOMEM)
247 return log_oom();
248 if (r < 0) {
249 log_syntax(unit, LOG_WARNING, filename, line, r,
250 "Invalid service instance name template '%s', ignoring: %m", rvalue);
251 return 0;
252 }
253
254 r = free_and_strdup(&s->name_template, rvalue);
255 if (r < 0)
256 return log_oom();
257
258 return 0;
259 }
260
261 int config_parse_dnssd_service_type(
262 const char *unit,
263 const char *filename,
264 unsigned line,
265 const char *section,
266 unsigned section_line,
267 const char *lvalue,
268 int ltype,
269 const char *rvalue,
270 void *data,
271 void *userdata) {
272
273 DnssdService *s = userdata;
274 int r;
275
276 assert(filename);
277 assert(lvalue);
278 assert(rvalue);
279 assert(s);
280
281 if (isempty(rvalue)) {
282 s->type = mfree(s->type);
283 return 0;
284 }
285
286 if (!dnssd_srv_type_is_valid(rvalue)) {
287 log_syntax(unit, LOG_WARNING, filename, line, 0, "Service type is invalid. Ignoring.");
288 return 0;
289 }
290
291 r = free_and_strdup(&s->type, rvalue);
292 if (r < 0)
293 return log_oom();
294
295 return 0;
296 }
297
298 int config_parse_dnssd_txt(
299 const char *unit,
300 const char *filename,
301 unsigned line,
302 const char *section,
303 unsigned section_line,
304 const char *lvalue,
305 int ltype,
306 const char *rvalue,
307 void *data,
308 void *userdata) {
309
310 _cleanup_(dnssd_txtdata_freep) DnssdTxtData *txt_data = NULL;
311 DnssdService *s = userdata;
312 DnsTxtItem *last = NULL;
313
314 assert(filename);
315 assert(lvalue);
316 assert(rvalue);
317 assert(s);
318
319 if (isempty(rvalue)) {
320 /* Flush out collected items */
321 s->txt_data_items = dnssd_txtdata_free_all(s->txt_data_items);
322 return 0;
323 }
324
325 txt_data = new0(DnssdTxtData, 1);
326 if (!txt_data)
327 return log_oom();
328
329 for (;;) {
330 _cleanup_free_ char *word = NULL, *key = NULL, *value = NULL;
331 _cleanup_free_ void *decoded = NULL;
332 size_t length = 0;
333 DnsTxtItem *i;
334 int r;
335
336 r = extract_first_word(&rvalue, &word, NULL,
337 EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX);
338 if (r == 0)
339 break;
340 if (r == -ENOMEM)
341 return log_oom();
342 if (r < 0) {
343 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
344 return 0;
345 }
346
347 r = split_pair(word, "=", &key, &value);
348 if (r == -ENOMEM)
349 return log_oom();
350 if (r == -EINVAL)
351 key = TAKE_PTR(word);
352
353 if (!ascii_is_valid(key)) {
354 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid key, ignoring: %s", key);
355 continue;
356 }
357
358 switch (ltype) {
359
360 case DNS_TXT_ITEM_DATA:
361 if (value) {
362 r = unbase64mem(value, strlen(value), &decoded, &length);
363 if (r == -ENOMEM)
364 return log_oom();
365 if (r < 0) {
366 log_syntax(unit, LOG_WARNING, filename, line, r,
367 "Invalid base64 encoding, ignoring: %s", value);
368 continue;
369 }
370 }
371
372 r = dnssd_txt_item_new_from_data(key, decoded, length, &i);
373 if (r < 0)
374 return log_oom();
375 break;
376
377 case DNS_TXT_ITEM_TEXT:
378 r = dnssd_txt_item_new_from_string(key, value, &i);
379 if (r < 0)
380 return log_oom();
381 break;
382
383 default:
384 assert_not_reached("Unknown type of Txt config");
385 }
386
387 LIST_INSERT_AFTER(items, txt_data->txt, last, i);
388 last = i;
389 }
390
391 if (!LIST_IS_EMPTY(txt_data->txt)) {
392 LIST_PREPEND(items, s->txt_data_items, txt_data);
393 TAKE_PTR(txt_data);
394 }
395
396 return 0;
397 }
398
399 int config_parse_dns_stub_listener_extra(
400 const char *unit,
401 const char *filename,
402 unsigned line,
403 const char *section,
404 unsigned section_line,
405 const char *lvalue,
406 int ltype,
407 const char *rvalue,
408 void *data,
409 void *userdata) {
410
411 _cleanup_free_ DnsStubListenerExtra *stub = NULL;
412 Manager *m = userdata;
413 const char *p;
414 int r;
415
416 assert(filename);
417 assert(lvalue);
418 assert(rvalue);
419 assert(data);
420
421 if (isempty(rvalue)) {
422 m->dns_extra_stub_listeners = ordered_set_free(m->dns_extra_stub_listeners);
423 return 0;
424 }
425
426 r = dns_stub_listener_extra_new(m, &stub);
427 if (r < 0)
428 return log_oom();
429
430 p = startswith(rvalue, "udp:");
431 if (p)
432 stub->mode = DNS_STUB_LISTENER_UDP;
433 else {
434 p = startswith(rvalue, "tcp:");
435 if (p)
436 stub->mode = DNS_STUB_LISTENER_TCP;
437 else {
438 stub->mode = DNS_STUB_LISTENER_YES;
439 p = rvalue;
440 }
441 }
442
443 r = in_addr_port_ifindex_name_from_string_auto(p, &stub->family, &stub->address, &stub->port, NULL, NULL);
444 if (r < 0) {
445 log_syntax(unit, LOG_WARNING, filename, line, r,
446 "Failed to parse address in %s=%s, ignoring assignment: %m",
447 lvalue, rvalue);
448 return 0;
449 }
450
451 r = ordered_set_ensure_put(&m->dns_extra_stub_listeners, &dns_stub_listener_extra_hash_ops, stub);
452 if (r == -ENOMEM)
453 return log_oom();
454 if (r < 0) {
455 log_syntax(unit, LOG_WARNING, filename, line, r,
456 "Failed to store %s=%s, ignoring assignment: %m", lvalue, rvalue);
457 return 0;
458 }
459
460 TAKE_PTR(stub);
461
462 return 0;
463 }
464
465 int manager_parse_config_file(Manager *m) {
466 int r;
467
468 assert(m);
469
470 r = config_parse_many_nulstr(
471 PKGSYSCONFDIR "/resolved.conf",
472 CONF_PATHS_NULSTR("systemd/resolved.conf.d"),
473 "Resolve\0",
474 config_item_perf_lookup, resolved_gperf_lookup,
475 CONFIG_PARSE_WARN,
476 m,
477 NULL);
478 if (r < 0)
479 return r;
480
481 if (m->need_builtin_fallbacks) {
482 r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_FALLBACK, DNS_SERVERS);
483 if (r < 0)
484 return r;
485 }
486
487 #if ! HAVE_GCRYPT
488 if (m->dnssec_mode != DNSSEC_NO) {
489 log_warning("DNSSEC option cannot be enabled or set to allow-downgrade when systemd-resolved is built without gcrypt support. Turning off DNSSEC support.");
490 m->dnssec_mode = DNSSEC_NO;
491 }
492 #endif
493
494 #if ! ENABLE_DNS_OVER_TLS
495 if (m->dns_over_tls_mode != DNS_OVER_TLS_NO) {
496 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.");
497 m->dns_over_tls_mode = DNS_OVER_TLS_NO;
498 }
499 #endif
500 return 0;
501
502 }