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