]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-conf.c
Implement SNI when using DNS-over-TLS
[thirdparty/systemd.git] / src / resolve / resolved-conf.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
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"
2e22a54f 11#include "resolved-util.h"
6501dd31 12#include "specifier.h"
1ae43295 13#include "string-table.h"
6bedfcbb 14#include "string-util.h"
6501dd31 15#include "utf8.h"
4e945a6f 16
1ae43295
DM
17DEFINE_CONFIG_PARSE_ENUM(config_parse_dns_stub_listener_mode, dns_stub_listener_mode, DnsStubListenerMode, "Failed to parse DNS stub listener mode setting");
18
19static const char* const dns_stub_listener_mode_table[_DNS_STUB_LISTENER_MODE_MAX] = {
20 [DNS_STUB_LISTENER_NO] = "no",
21 [DNS_STUB_LISTENER_UDP] = "udp",
22 [DNS_STUB_LISTENER_TCP] = "tcp",
23 [DNS_STUB_LISTENER_YES] = "yes",
24};
25DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_stub_listener_mode, DnsStubListenerMode, DNS_STUB_LISTENER_YES);
26
8e97dc67 27static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) {
636e813d 28 union in_addr_union address;
2817157b 29 int family, r, ifindex = 0;
0eac4623 30 DnsServer *s;
2e22a54f 31 _cleanup_free_ char *server_name = NULL;
4e945a6f
LP
32
33 assert(m);
636e813d
LP
34 assert(word);
35
2e22a54f 36 r = in_addr_ifindex_name_from_string_auto(word, &family, &address, &ifindex, &server_name);
636e813d
LP
37 if (r < 0)
38 return r;
4e945a6f 39
b30bf55d
LP
40 /* Silently filter out 0.0.0.0 and 127.0.0.53 (our own stub DNS listener) */
41 if (!dns_server_address_valid(family, &address))
42 return 0;
43
636e813d 44 /* Filter out duplicates */
2817157b 45 s = dns_server_find(manager_get_first_dns_server(m, type), family, &address, ifindex);
636e813d
LP
46 if (s) {
47 /*
48 * Drop the marker. This is used to find the servers
49 * that ceased to exist, see
50 * manager_mark_dns_servers() and
51 * manager_flush_marked_dns_servers().
52 */
0b58db65 53 dns_server_move_back_and_unmark(s);
636e813d
LP
54 return 0;
55 }
56
2e22a54f 57 return dns_server_new(m, NULL, type, NULL, family, &address, ifindex, server_name);
636e813d
LP
58}
59
60int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) {
61 int r;
62
63 assert(m);
64 assert(string);
65
9ed794a3 66 for (;;) {
b5efdb8a 67 _cleanup_free_ char *word = NULL;
880603a1
SS
68
69 r = extract_first_word(&string, &word, NULL, 0);
70 if (r < 0)
636e813d 71 return r;
880603a1
SS
72 if (r == 0)
73 break;
4e945a6f 74
636e813d 75 r = manager_add_dns_server_by_string(m, type, word);
a51c1048 76 if (r < 0)
2817157b 77 log_warning_errno(r, "Failed to add DNS server address '%s', ignoring: %m", word);
a51c1048
LP
78 }
79
80 return 0;
81}
82
8e97dc67 83static int manager_add_search_domain_by_string(Manager *m, const char *domain) {
a51c1048 84 DnsSearchDomain *d;
adc800a6 85 bool route_only;
a51c1048
LP
86 int r;
87
88 assert(m);
89 assert(domain);
90
adc800a6
LP
91 route_only = *domain == '~';
92 if (route_only)
93 domain++;
94
95 if (dns_name_is_root(domain) || streq(domain, "*")) {
96 route_only = true;
97 domain = ".";
98 }
99
a51c1048
LP
100 r = dns_search_domain_find(m->search_domains, domain, &d);
101 if (r < 0)
102 return r;
adc800a6 103 if (r > 0)
a51c1048 104 dns_search_domain_move_back_and_unmark(d);
adc800a6
LP
105 else {
106 r = dns_search_domain_new(m, &d, DNS_SEARCH_DOMAIN_SYSTEM, NULL, domain);
107 if (r < 0)
108 return r;
a51c1048
LP
109 }
110
adc800a6
LP
111 d->route_only = route_only;
112 return 0;
a51c1048
LP
113}
114
115int manager_parse_search_domains_and_warn(Manager *m, const char *string) {
116 int r;
117
118 assert(m);
119 assert(string);
120
9ed794a3 121 for (;;) {
a51c1048
LP
122 _cleanup_free_ char *word = NULL;
123
4ec85141 124 r = extract_first_word(&string, &word, NULL, EXTRACT_UNQUOTE);
a51c1048
LP
125 if (r < 0)
126 return r;
127 if (r == 0)
128 break;
129
130 r = manager_add_search_domain_by_string(m, word);
131 if (r < 0)
2817157b 132 log_warning_errno(r, "Failed to add search domain '%s', ignoring: %m", word);
4e945a6f
LP
133 }
134
135 return 0;
136}
137
636e813d 138int config_parse_dns_servers(
4e945a6f
LP
139 const char *unit,
140 const char *filename,
141 unsigned line,
142 const char *section,
143 unsigned section_line,
144 const char *lvalue,
145 int ltype,
146 const char *rvalue,
147 void *data,
148 void *userdata) {
149
150 Manager *m = userdata;
4e945a6f
LP
151 int r;
152
153 assert(filename);
154 assert(lvalue);
155 assert(rvalue);
156 assert(m);
157
3e684349 158 if (isempty(rvalue))
5cb36f41 159 /* Empty assignment means clear the list */
4b95f179 160 dns_server_unlink_all(manager_get_first_dns_server(m, ltype));
3e684349 161 else {
b938cb90 162 /* Otherwise, add to the list */
636e813d 163 r = manager_parse_dns_server_string_and_warn(m, ltype, rvalue);
5cb36f41 164 if (r < 0) {
12ca818f 165 log_syntax(unit, LOG_ERR, filename, line, r, "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) {
207 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse search domains string '%s'. Ignoring.", rvalue);
208 return 0;
209 }
210 }
211
212 /* If we have a manual setting, then we stop reading
213 * /etc/resolv.conf */
214 m->read_resolv_conf = false;
6501dd31
DR
215
216 return 0;
217}
218
219int 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) {
220 static const Specifier specifier_table[] = {
221 { 'b', specifier_boot_id, NULL },
24eb8621
LP
222 { 'H', specifier_host_name, NULL },
223 { 'm', specifier_machine_id, NULL },
6501dd31
DR
224 { 'v', specifier_kernel_release, NULL },
225 {}
226 };
227 DnssdService *s = userdata;
228 _cleanup_free_ char *name = NULL;
229 int r;
230
231 assert(filename);
232 assert(lvalue);
233 assert(rvalue);
234 assert(s);
235
236 if (isempty(rvalue)) {
237 log_syntax(unit, LOG_ERR, filename, line, 0, "Service instance name can't be empty. Ignoring.");
238 return -EINVAL;
239 }
240
241 r = free_and_strdup(&s->name_template, rvalue);
242 if (r < 0)
243 return log_oom();
244
245 r = specifier_printf(s->name_template, specifier_table, NULL, &name);
246 if (r < 0)
247 return log_debug_errno(r, "Failed to replace specifiers: %m");
248
249 if (!dns_service_name_is_valid(name)) {
250 log_syntax(unit, LOG_ERR, filename, line, 0, "Service instance name template renders to invalid name '%s'. Ignoring.", name);
251 return -EINVAL;
252 }
253
254 return 0;
255}
256
257int 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) {
258 DnssdService *s = userdata;
259 int r;
260
261 assert(filename);
262 assert(lvalue);
263 assert(rvalue);
264 assert(s);
265
266 if (isempty(rvalue)) {
267 log_syntax(unit, LOG_ERR, filename, line, 0, "Service type can't be empty. Ignoring.");
268 return -EINVAL;
269 }
270
271 if (!dnssd_srv_type_is_valid(rvalue)) {
272 log_syntax(unit, LOG_ERR, filename, line, 0, "Service type is invalid. Ignoring.");
273 return -EINVAL;
274 }
275
276 r = free_and_strdup(&s->type, rvalue);
277 if (r < 0)
278 return log_oom();
279
280 return 0;
281}
282
283int 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) {
400f54fb 284 _cleanup_(dnssd_txtdata_freep) DnssdTxtData *txt_data = NULL;
6501dd31
DR
285 DnssdService *s = userdata;
286 DnsTxtItem *last = NULL;
287
288 assert(filename);
289 assert(lvalue);
290 assert(rvalue);
291 assert(s);
292
400f54fb
DR
293 if (isempty(rvalue)) {
294 /* Flush out collected items */
295 s->txt_data_items = dnssd_txtdata_free_all(s->txt_data_items);
6501dd31 296 return 0;
400f54fb
DR
297 }
298
299 txt_data = new0(DnssdTxtData, 1);
300 if (!txt_data)
301 return log_oom();
6501dd31
DR
302
303 for (;;) {
304 _cleanup_free_ char *word = NULL;
305 _cleanup_free_ char *key = NULL;
306 _cleanup_free_ char *value = NULL;
307 _cleanup_free_ void *decoded = NULL;
308 size_t length = 0;
309 DnsTxtItem *i;
310 int r;
311
312 r = extract_first_word(&rvalue, &word, NULL,
4ec85141 313 EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX);
6501dd31
DR
314 if (r == 0)
315 break;
316 if (r == -ENOMEM)
317 return log_oom();
318 if (r < 0)
319 return log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
320
321 r = split_pair(word, "=", &key, &value);
322 if (r == -ENOMEM)
323 return log_oom();
1cc6c93a
YW
324 if (r == -EINVAL)
325 key = TAKE_PTR(word);
6501dd31
DR
326
327 if (!ascii_is_valid(key)) {
328 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid syntax, ignoring: %s", key);
329 return -EINVAL;
330 }
331
332 switch (ltype) {
333
334 case DNS_TXT_ITEM_DATA:
335 if (value) {
336 r = unbase64mem(value, strlen(value), &decoded, &length);
337 if (r == -ENOMEM)
338 return log_oom();
339 if (r < 0)
340 return log_syntax(unit, LOG_ERR, filename, line, r,
341 "Invalid base64 encoding, ignoring: %s", value);
342 }
343
344 r = dnssd_txt_item_new_from_data(key, decoded, length, &i);
345 if (r < 0)
346 return log_oom();
347 break;
348
349 case DNS_TXT_ITEM_TEXT:
350 r = dnssd_txt_item_new_from_string(key, value, &i);
351 if (r < 0)
352 return log_oom();
353 break;
354
355 default:
356 assert_not_reached("Unknown type of Txt config");
357 }
358
400f54fb 359 LIST_INSERT_AFTER(items, txt_data->txt, last, i);
6501dd31
DR
360 last = i;
361 }
400f54fb
DR
362
363 if (!LIST_IS_EMPTY(txt_data->txt)) {
364 LIST_PREPEND(items, s->txt_data_items, txt_data);
365 txt_data = NULL;
366 }
a51c1048
LP
367
368 return 0;
369}
370
4e945a6f 371int manager_parse_config_file(Manager *m) {
00fa60ae
LP
372 int r;
373
4e945a6f
LP
374 assert(m);
375
43688c49 376 r = config_parse_many_nulstr(PKGSYSCONFDIR "/resolved.conf",
da412854
YW
377 CONF_PATHS_NULSTR("systemd/resolved.conf.d"),
378 "Resolve\0",
379 config_item_perf_lookup, resolved_gperf_lookup,
bcde742e 380 CONFIG_PARSE_WARN, m);
00fa60ae
LP
381 if (r < 0)
382 return r;
383
384 if (m->need_builtin_fallbacks) {
385 r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_FALLBACK, DNS_SERVERS);
386 if (r < 0)
387 return r;
388 }
389
349cc4a5 390#if ! HAVE_GCRYPT
42303dcb
YW
391 if (m->dnssec_mode != DNSSEC_NO) {
392 log_warning("DNSSEC option cannot be enabled or set to allow-downgrade when systemd-resolved is built without gcrypt support. Turning off DNSSEC support.");
393 m->dnssec_mode = DNSSEC_NO;
394 }
395#endif
5d67a7ae 396
56ddbf10 397#if ! ENABLE_DNS_OVER_TLS
c9299be2 398 if (m->dns_over_tls_mode != DNS_OVER_TLS_NO) {
4310bfc2 399 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 400 m->dns_over_tls_mode = DNS_OVER_TLS_NO;
5d67a7ae
IT
401 }
402#endif
00fa60ae
LP
403 return 0;
404
4e945a6f 405}