]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-conf.c
Merge pull request #16145 from poettering/qrcode-dlopen
[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 "dns-domain.h"
14 #include "socket-netlink.h"
15 #include "specifier.h"
16 #include "string-table.h"
17 #include "string-util.h"
18 #include "utf8.h"
19
20 DEFINE_CONFIG_PARSE_ENUM(config_parse_dns_stub_listener_mode, dns_stub_listener_mode, DnsStubListenerMode, "Failed to parse DNS stub listener mode setting");
21
22 static const char* const dns_stub_listener_mode_table[_DNS_STUB_LISTENER_MODE_MAX] = {
23 [DNS_STUB_LISTENER_NO] = "no",
24 [DNS_STUB_LISTENER_UDP] = "udp",
25 [DNS_STUB_LISTENER_TCP] = "tcp",
26 [DNS_STUB_LISTENER_YES] = "yes",
27 };
28 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_stub_listener_mode, DnsStubListenerMode, DNS_STUB_LISTENER_YES);
29
30 static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) {
31 _cleanup_free_ char *server_name = NULL;
32 union in_addr_union address;
33 int family, r, ifindex = 0;
34 uint16_t port;
35 DnsServer *s;
36
37 assert(m);
38 assert(word);
39
40 r = in_addr_port_ifindex_name_from_string_auto(word, &family, &address, &port, &ifindex, &server_name);
41 if (r < 0)
42 return r;
43
44 if (IN_SET(port, 53, 853))
45 port = 0;
46
47 /* Silently filter out 0.0.0.0 and 127.0.0.53 (our own stub DNS listener) */
48 if (!dns_server_address_valid(family, &address))
49 return 0;
50
51 /* By default, the port number is determined with the transaction feature level.
52 * See dns_transaction_port() and dns_server_port(). */
53 if (IN_SET(port, 53, 853))
54 port = 0;
55
56 /* Filter out duplicates */
57 s = dns_server_find(manager_get_first_dns_server(m, type), family, &address, port, ifindex, server_name);
58 if (s) {
59 /*
60 * Drop the marker. This is used to find the servers
61 * that ceased to exist, see
62 * manager_mark_dns_servers() and
63 * manager_flush_marked_dns_servers().
64 */
65 dns_server_move_back_and_unmark(s);
66 return 0;
67 }
68
69 return dns_server_new(m, NULL, type, NULL, family, &address, port, ifindex, server_name);
70 }
71
72 int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) {
73 int r;
74
75 assert(m);
76 assert(string);
77
78 for (;;) {
79 _cleanup_free_ char *word = NULL;
80
81 r = extract_first_word(&string, &word, NULL, 0);
82 if (r < 0)
83 return r;
84 if (r == 0)
85 break;
86
87 r = manager_add_dns_server_by_string(m, type, word);
88 if (r < 0)
89 log_warning_errno(r, "Failed to add DNS server address '%s', ignoring: %m", word);
90 }
91
92 return 0;
93 }
94
95 static int manager_add_search_domain_by_string(Manager *m, const char *domain) {
96 DnsSearchDomain *d;
97 bool route_only;
98 int r;
99
100 assert(m);
101 assert(domain);
102
103 route_only = *domain == '~';
104 if (route_only)
105 domain++;
106
107 if (dns_name_is_root(domain) || streq(domain, "*")) {
108 route_only = true;
109 domain = ".";
110 }
111
112 r = dns_search_domain_find(m->search_domains, domain, &d);
113 if (r < 0)
114 return r;
115 if (r > 0)
116 dns_search_domain_move_back_and_unmark(d);
117 else {
118 r = dns_search_domain_new(m, &d, DNS_SEARCH_DOMAIN_SYSTEM, NULL, domain);
119 if (r < 0)
120 return r;
121 }
122
123 d->route_only = route_only;
124 return 0;
125 }
126
127 int manager_parse_search_domains_and_warn(Manager *m, const char *string) {
128 int r;
129
130 assert(m);
131 assert(string);
132
133 for (;;) {
134 _cleanup_free_ char *word = NULL;
135
136 r = extract_first_word(&string, &word, NULL, EXTRACT_UNQUOTE);
137 if (r < 0)
138 return r;
139 if (r == 0)
140 break;
141
142 r = manager_add_search_domain_by_string(m, word);
143 if (r < 0)
144 log_warning_errno(r, "Failed to add search domain '%s', ignoring: %m", word);
145 }
146
147 return 0;
148 }
149
150 int config_parse_dns_servers(
151 const char *unit,
152 const char *filename,
153 unsigned line,
154 const char *section,
155 unsigned section_line,
156 const char *lvalue,
157 int ltype,
158 const char *rvalue,
159 void *data,
160 void *userdata) {
161
162 Manager *m = userdata;
163 int r;
164
165 assert(filename);
166 assert(lvalue);
167 assert(rvalue);
168 assert(m);
169
170 if (isempty(rvalue))
171 /* Empty assignment means clear the list */
172 dns_server_unlink_all(manager_get_first_dns_server(m, ltype));
173 else {
174 /* Otherwise, add to the list */
175 r = manager_parse_dns_server_string_and_warn(m, ltype, rvalue);
176 if (r < 0) {
177 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse DNS server string '%s'. Ignoring.", rvalue);
178 return 0;
179 }
180 }
181
182 /* If we have a manual setting, then we stop reading
183 * /etc/resolv.conf */
184 if (ltype == DNS_SERVER_SYSTEM)
185 m->read_resolv_conf = false;
186 if (ltype == DNS_SERVER_FALLBACK)
187 m->need_builtin_fallbacks = false;
188
189 return 0;
190 }
191
192 int config_parse_search_domains(
193 const char *unit,
194 const char *filename,
195 unsigned line,
196 const char *section,
197 unsigned section_line,
198 const char *lvalue,
199 int ltype,
200 const char *rvalue,
201 void *data,
202 void *userdata) {
203
204 Manager *m = userdata;
205 int r;
206
207 assert(filename);
208 assert(lvalue);
209 assert(rvalue);
210 assert(m);
211
212 if (isempty(rvalue))
213 /* Empty assignment means clear the list */
214 dns_search_domain_unlink_all(m->search_domains);
215 else {
216 /* Otherwise, add to the list */
217 r = manager_parse_search_domains_and_warn(m, rvalue);
218 if (r < 0) {
219 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse search domains string '%s'. Ignoring.", rvalue);
220 return 0;
221 }
222 }
223
224 /* If we have a manual setting, then we stop reading
225 * /etc/resolv.conf */
226 m->read_resolv_conf = false;
227
228 return 0;
229 }
230
231 int 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) {
232 static const Specifier specifier_table[] = {
233 { 'm', specifier_machine_id, NULL },
234 { 'b', specifier_boot_id, NULL },
235 { 'H', specifier_host_name, NULL },
236 { 'v', specifier_kernel_release, NULL },
237 { 'a', specifier_architecture, NULL },
238 { 'o', specifier_os_id, NULL },
239 { 'w', specifier_os_version_id, NULL },
240 { 'B', specifier_os_build_id, NULL },
241 { 'W', specifier_os_variant_id, NULL },
242 {}
243 };
244 DnssdService *s = userdata;
245 _cleanup_free_ char *name = NULL;
246 int r;
247
248 assert(filename);
249 assert(lvalue);
250 assert(rvalue);
251 assert(s);
252
253 if (isempty(rvalue)) {
254 log_syntax(unit, LOG_ERR, filename, line, 0, "Service instance name can't be empty. Ignoring.");
255 return -EINVAL;
256 }
257
258 r = free_and_strdup(&s->name_template, rvalue);
259 if (r < 0)
260 return log_oom();
261
262 r = specifier_printf(s->name_template, specifier_table, NULL, &name);
263 if (r < 0)
264 return log_debug_errno(r, "Failed to replace specifiers: %m");
265
266 if (!dns_service_name_is_valid(name)) {
267 log_syntax(unit, LOG_ERR, filename, line, 0, "Service instance name template renders to invalid name '%s'. Ignoring.", name);
268 return -EINVAL;
269 }
270
271 return 0;
272 }
273
274 int 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) {
275 DnssdService *s = userdata;
276 int r;
277
278 assert(filename);
279 assert(lvalue);
280 assert(rvalue);
281 assert(s);
282
283 if (isempty(rvalue)) {
284 log_syntax(unit, LOG_ERR, filename, line, 0, "Service type can't be empty. Ignoring.");
285 return -EINVAL;
286 }
287
288 if (!dnssd_srv_type_is_valid(rvalue)) {
289 log_syntax(unit, LOG_ERR, filename, line, 0, "Service type is invalid. Ignoring.");
290 return -EINVAL;
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(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) {
301 _cleanup_(dnssd_txtdata_freep) DnssdTxtData *txt_data = NULL;
302 DnssdService *s = userdata;
303 DnsTxtItem *last = NULL;
304
305 assert(filename);
306 assert(lvalue);
307 assert(rvalue);
308 assert(s);
309
310 if (isempty(rvalue)) {
311 /* Flush out collected items */
312 s->txt_data_items = dnssd_txtdata_free_all(s->txt_data_items);
313 return 0;
314 }
315
316 txt_data = new0(DnssdTxtData, 1);
317 if (!txt_data)
318 return log_oom();
319
320 for (;;) {
321 _cleanup_free_ char *word = NULL;
322 _cleanup_free_ char *key = NULL;
323 _cleanup_free_ char *value = NULL;
324 _cleanup_free_ void *decoded = NULL;
325 size_t length = 0;
326 DnsTxtItem *i;
327 int r;
328
329 r = extract_first_word(&rvalue, &word, NULL,
330 EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX);
331 if (r == 0)
332 break;
333 if (r == -ENOMEM)
334 return log_oom();
335 if (r < 0)
336 return log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
337
338 r = split_pair(word, "=", &key, &value);
339 if (r == -ENOMEM)
340 return log_oom();
341 if (r == -EINVAL)
342 key = TAKE_PTR(word);
343
344 if (!ascii_is_valid(key)) {
345 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid syntax, ignoring: %s", key);
346 return -EINVAL;
347 }
348
349 switch (ltype) {
350
351 case DNS_TXT_ITEM_DATA:
352 if (value) {
353 r = unbase64mem(value, strlen(value), &decoded, &length);
354 if (r == -ENOMEM)
355 return log_oom();
356 if (r < 0)
357 return log_syntax(unit, LOG_ERR, filename, line, r,
358 "Invalid base64 encoding, ignoring: %s", value);
359 }
360
361 r = dnssd_txt_item_new_from_data(key, decoded, length, &i);
362 if (r < 0)
363 return log_oom();
364 break;
365
366 case DNS_TXT_ITEM_TEXT:
367 r = dnssd_txt_item_new_from_string(key, value, &i);
368 if (r < 0)
369 return log_oom();
370 break;
371
372 default:
373 assert_not_reached("Unknown type of Txt config");
374 }
375
376 LIST_INSERT_AFTER(items, txt_data->txt, last, i);
377 last = i;
378 }
379
380 if (!LIST_IS_EMPTY(txt_data->txt)) {
381 LIST_PREPEND(items, s->txt_data_items, txt_data);
382 txt_data = NULL;
383 }
384
385 return 0;
386 }
387
388 int manager_parse_config_file(Manager *m) {
389 int r;
390
391 assert(m);
392
393 r = config_parse_many_nulstr(
394 PKGSYSCONFDIR "/resolved.conf",
395 CONF_PATHS_NULSTR("systemd/resolved.conf.d"),
396 "Resolve\0",
397 config_item_perf_lookup, resolved_gperf_lookup,
398 CONFIG_PARSE_WARN,
399 m,
400 NULL);
401 if (r < 0)
402 return r;
403
404 if (m->need_builtin_fallbacks) {
405 r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_FALLBACK, DNS_SERVERS);
406 if (r < 0)
407 return r;
408 }
409
410 #if ! HAVE_GCRYPT
411 if (m->dnssec_mode != DNSSEC_NO) {
412 log_warning("DNSSEC option cannot be enabled or set to allow-downgrade when systemd-resolved is built without gcrypt support. Turning off DNSSEC support.");
413 m->dnssec_mode = DNSSEC_NO;
414 }
415 #endif
416
417 #if ! ENABLE_DNS_OVER_TLS
418 if (m->dns_over_tls_mode != DNS_OVER_TLS_NO) {
419 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.");
420 m->dns_over_tls_mode = DNS_OVER_TLS_NO;
421 }
422 #endif
423 return 0;
424
425 }