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