]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-conf.c
hwdb/60-keyboard: untabify and move comments to the same column
[thirdparty/systemd.git] / src / resolve / resolved-conf.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
4e945a6f 2
b5efdb8a 3#include "alloc-util.h"
4e945a6f 4#include "conf-parser.h"
a0f29c76 5#include "def.h"
b5efdb8a 6#include "extract-word.h"
6501dd31 7#include "hexdecoct.h"
6bedfcbb 8#include "parse-util.h"
4e945a6f 9#include "resolved-conf.h"
6501dd31 10#include "resolved-dnssd.h"
be28f72d
DDM
11#include "resolved-manager.h"
12#include "resolved-dns-search-domain.h"
1f05101f 13#include "resolved-dns-stub.h"
be28f72d 14#include "dns-domain.h"
2d95d81f 15#include "socket-netlink.h"
6501dd31 16#include "specifier.h"
1ae43295 17#include "string-table.h"
6bedfcbb 18#include "string-util.h"
1f05101f 19#include "strv.h"
6501dd31 20#include "utf8.h"
4e945a6f 21
1ae43295
DM
22DEFINE_CONFIG_PARSE_ENUM(config_parse_dns_stub_listener_mode, dns_stub_listener_mode, DnsStubListenerMode, "Failed to parse DNS stub listener mode setting");
23
8e97dc67 24static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) {
da9de738 25 _cleanup_free_ char *server_name = NULL;
636e813d 26 union in_addr_union address;
2817157b 27 int family, r, ifindex = 0;
da9de738 28 uint16_t port;
0eac4623 29 DnsServer *s;
4e945a6f
LP
30
31 assert(m);
636e813d
LP
32 assert(word);
33
da9de738 34 r = in_addr_port_ifindex_name_from_string_auto(word, &family, &address, &port, &ifindex, &server_name);
636e813d
LP
35 if (r < 0)
36 return r;
4e945a6f 37
b30bf55d
LP
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
da9de738
YW
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
636e813d 47 /* Filter out duplicates */
1b860092 48 s = dns_server_find(manager_get_first_dns_server(m, type), family, &address, port, ifindex, server_name);
636e813d 49 if (s) {
c4c6ee3a
ZJS
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(). */
0b58db65 52 dns_server_move_back_and_unmark(s);
636e813d
LP
53 return 0;
54 }
55
da9de738 56 return dns_server_new(m, NULL, type, NULL, family, &address, port, ifindex, server_name);
636e813d
LP
57}
58
59int 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
9ed794a3 65 for (;;) {
b5efdb8a 66 _cleanup_free_ char *word = NULL;
880603a1
SS
67
68 r = extract_first_word(&string, &word, NULL, 0);
69 if (r < 0)
636e813d 70 return r;
880603a1
SS
71 if (r == 0)
72 break;
4e945a6f 73
636e813d 74 r = manager_add_dns_server_by_string(m, type, word);
a51c1048 75 if (r < 0)
2817157b 76 log_warning_errno(r, "Failed to add DNS server address '%s', ignoring: %m", word);
a51c1048
LP
77 }
78
79 return 0;
80}
81
8e97dc67 82static int manager_add_search_domain_by_string(Manager *m, const char *domain) {
a51c1048 83 DnsSearchDomain *d;
adc800a6 84 bool route_only;
a51c1048
LP
85 int r;
86
87 assert(m);
88 assert(domain);
89
adc800a6
LP
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
a51c1048
LP
99 r = dns_search_domain_find(m->search_domains, domain, &d);
100 if (r < 0)
101 return r;
adc800a6 102 if (r > 0)
a51c1048 103 dns_search_domain_move_back_and_unmark(d);
adc800a6
LP
104 else {
105 r = dns_search_domain_new(m, &d, DNS_SEARCH_DOMAIN_SYSTEM, NULL, domain);
106 if (r < 0)
107 return r;
a51c1048
LP
108 }
109
adc800a6
LP
110 d->route_only = route_only;
111 return 0;
a51c1048
LP
112}
113
114int manager_parse_search_domains_and_warn(Manager *m, const char *string) {
115 int r;
116
117 assert(m);
118 assert(string);
119
9ed794a3 120 for (;;) {
a51c1048
LP
121 _cleanup_free_ char *word = NULL;
122
4ec85141 123 r = extract_first_word(&string, &word, NULL, EXTRACT_UNQUOTE);
a51c1048
LP
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)
2817157b 131 log_warning_errno(r, "Failed to add search domain '%s', ignoring: %m", word);
4e945a6f
LP
132 }
133
134 return 0;
135}
136
636e813d 137int config_parse_dns_servers(
4e945a6f
LP
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;
4e945a6f
LP
150 int r;
151
152 assert(filename);
153 assert(lvalue);
154 assert(rvalue);
155 assert(m);
156
3e684349 157 if (isempty(rvalue))
5cb36f41 158 /* Empty assignment means clear the list */
4b95f179 159 dns_server_unlink_all(manager_get_first_dns_server(m, ltype));
3e684349 160 else {
b938cb90 161 /* Otherwise, add to the list */
636e813d 162 r = manager_parse_dns_server_string_and_warn(m, ltype, rvalue);
5cb36f41 163 if (r < 0) {
94069bef
YW
164 log_syntax(unit, LOG_WARNING, filename, line, r,
165 "Failed to parse DNS server string '%s', ignoring.", rvalue);
5cb36f41
LP
166 return 0;
167 }
4e945a6f
LP
168 }
169
5cb36f41
LP
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;
00fa60ae
LP
174 if (ltype == DNS_SERVER_FALLBACK)
175 m->need_builtin_fallbacks = false;
5cb36f41 176
4e945a6f
LP
177 return 0;
178}
179
a51c1048
LP
180int 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) {
94069bef
YW
207 log_syntax(unit, LOG_WARNING, filename, line, r,
208 "Failed to parse search domains string '%s', ignoring.", rvalue);
a51c1048
LP
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;
6501dd31
DR
216
217 return 0;
218}
219
34136e15
YW
220int 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
6501dd31 232 DnssdService *s = userdata;
6501dd31
DR
233 int r;
234
235 assert(filename);
236 assert(lvalue);
237 assert(rvalue);
238 assert(s);
239
240 if (isempty(rvalue)) {
34136e15
YW
241 s->name_template = mfree(s->name_template);
242 return 0;
6501dd31
DR
243 }
244
34136e15
YW
245 r = dnssd_render_instance_name(rvalue, NULL);
246 if (r == -ENOMEM)
6501dd31 247 return log_oom();
34136e15
YW
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 }
6501dd31 253
34136e15 254 r = free_and_strdup(&s->name_template, rvalue);
6501dd31 255 if (r < 0)
34136e15 256 return log_oom();
6501dd31
DR
257
258 return 0;
259}
260
94069bef
YW
261int 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
6501dd31
DR
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)) {
94069bef
YW
282 s->type = mfree(s->type);
283 return 0;
6501dd31
DR
284 }
285
286 if (!dnssd_srv_type_is_valid(rvalue)) {
94069bef
YW
287 log_syntax(unit, LOG_WARNING, filename, line, 0, "Service type is invalid. Ignoring.");
288 return 0;
6501dd31
DR
289 }
290
291 r = free_and_strdup(&s->type, rvalue);
292 if (r < 0)
293 return log_oom();
294
295 return 0;
296}
297
94069bef
YW
298int 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
400f54fb 310 _cleanup_(dnssd_txtdata_freep) DnssdTxtData *txt_data = NULL;
6501dd31
DR
311 DnssdService *s = userdata;
312 DnsTxtItem *last = NULL;
313
314 assert(filename);
315 assert(lvalue);
316 assert(rvalue);
317 assert(s);
318
400f54fb
DR
319 if (isempty(rvalue)) {
320 /* Flush out collected items */
321 s->txt_data_items = dnssd_txtdata_free_all(s->txt_data_items);
6501dd31 322 return 0;
400f54fb
DR
323 }
324
325 txt_data = new0(DnssdTxtData, 1);
326 if (!txt_data)
327 return log_oom();
6501dd31
DR
328
329 for (;;) {
94069bef 330 _cleanup_free_ char *word = NULL, *key = NULL, *value = NULL;
6501dd31
DR
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,
4ec85141 337 EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX);
6501dd31
DR
338 if (r == 0)
339 break;
340 if (r == -ENOMEM)
341 return log_oom();
94069bef
YW
342 if (r < 0) {
343 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
344 return 0;
345 }
6501dd31
DR
346
347 r = split_pair(word, "=", &key, &value);
348 if (r == -ENOMEM)
349 return log_oom();
1cc6c93a
YW
350 if (r == -EINVAL)
351 key = TAKE_PTR(word);
6501dd31
DR
352
353 if (!ascii_is_valid(key)) {
94069bef
YW
354 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid key, ignoring: %s", key);
355 continue;
6501dd31
DR
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();
94069bef
YW
365 if (r < 0) {
366 log_syntax(unit, LOG_WARNING, filename, line, r,
367 "Invalid base64 encoding, ignoring: %s", value);
368 continue;
369 }
6501dd31
DR
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
400f54fb 387 LIST_INSERT_AFTER(items, txt_data->txt, last, i);
6501dd31
DR
388 last = i;
389 }
400f54fb
DR
390
391 if (!LIST_IS_EMPTY(txt_data->txt)) {
392 LIST_PREPEND(items, s->txt_data_items, txt_data);
94069bef 393 TAKE_PTR(txt_data);
400f54fb 394 }
a51c1048
LP
395
396 return 0;
397}
398
1f05101f
SS
399int 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
36aaabc3 411 _cleanup_free_ DnsStubListenerExtra *stub = NULL;
1f05101f 412 Manager *m = userdata;
1f05101f
SS
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
0354029b 426 r = dns_stub_listener_extra_new(m, &stub);
7314b397 427 if (r < 0)
1f05101f 428 return log_oom();
1f05101f 429
7314b397
YW
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;
1f05101f
SS
440 }
441 }
442
222eaaf9 443 r = in_addr_port_ifindex_name_from_string_auto(p, &stub->family, &stub->address, &stub->port, NULL, NULL);
7314b397
YW
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;
1f05101f
SS
449 }
450
7314b397
YW
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;
1f05101f
SS
458 }
459
7314b397 460 TAKE_PTR(stub);
1f05101f
SS
461
462 return 0;
463}
464
4e945a6f 465int manager_parse_config_file(Manager *m) {
00fa60ae
LP
466 int r;
467
4e945a6f
LP
468 assert(m);
469
4f9ff96a
LP
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);
00fa60ae
LP
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
349cc4a5 487#if ! HAVE_GCRYPT
42303dcb
YW
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
5d67a7ae 493
56ddbf10 494#if ! ENABLE_DNS_OVER_TLS
c9299be2 495 if (m->dns_over_tls_mode != DNS_OVER_TLS_NO) {
4310bfc2 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.");
c9299be2 497 m->dns_over_tls_mode = DNS_OVER_TLS_NO;
5d67a7ae
IT
498 }
499#endif
00fa60ae
LP
500 return 0;
501
4e945a6f 502}