]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/resolve/resolved-conf.c
man: improve Description= documentation (#38101)
[thirdparty/systemd.git] / src / resolve / resolved-conf.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include "alloc-util.h"
4#include "conf-parser.h"
5#include "creds-util.h"
6#include "dns-type.h"
7#include "extract-word.h"
8#include "ordered-set.h"
9#include "proc-cmdline.h"
10#include "resolved-conf.h"
11#include "resolved-dns-search-domain.h"
12#include "resolved-dns-server.h"
13#include "resolved-dns-stub.h"
14#include "resolved-manager.h"
15#include "set.h"
16#include "socket-netlink.h"
17#include "string-util.h"
18
19DEFINE_CONFIG_PARSE_ENUM(config_parse_dns_stub_listener_mode, dns_stub_listener_mode, DnsStubListenerMode);
20
21int config_parse_dns_servers(
22 const char *unit,
23 const char *filename,
24 unsigned line,
25 const char *section,
26 unsigned section_line,
27 const char *lvalue,
28 int ltype,
29 const char *rvalue,
30 void *data,
31 void *userdata) {
32
33 Manager *m = ASSERT_PTR(userdata);
34 int r;
35
36 assert(filename);
37 assert(lvalue);
38 assert(rvalue);
39
40 if (isempty(rvalue))
41 /* Empty assignment means clear the list */
42 dns_server_unlink_all(manager_get_first_dns_server(m, ltype));
43 else {
44 /* Otherwise, add to the list */
45 r = manager_parse_dns_server_string_and_warn(m, ltype, rvalue);
46 if (r < 0) {
47 log_syntax(unit, LOG_WARNING, filename, line, r,
48 "Failed to parse DNS server string '%s', ignoring.", rvalue);
49 return 0;
50 }
51 }
52
53 /* If we have a manual setting, then we stop reading
54 * /etc/resolv.conf */
55 if (ltype == DNS_SERVER_SYSTEM)
56 m->read_resolv_conf = false;
57 if (ltype == DNS_SERVER_FALLBACK)
58 m->need_builtin_fallbacks = false;
59
60 return 0;
61}
62
63int config_parse_search_domains(
64 const char *unit,
65 const char *filename,
66 unsigned line,
67 const char *section,
68 unsigned section_line,
69 const char *lvalue,
70 int ltype,
71 const char *rvalue,
72 void *data,
73 void *userdata) {
74
75 Manager *m = ASSERT_PTR(userdata);
76 int r;
77
78 assert(filename);
79 assert(lvalue);
80 assert(rvalue);
81
82 if (isempty(rvalue))
83 /* Empty assignment means clear the list */
84 dns_search_domain_unlink_all(m->search_domains);
85 else {
86 /* Otherwise, add to the list */
87 r = manager_parse_search_domains_and_warn(m, rvalue);
88 if (r < 0) {
89 log_syntax(unit, LOG_WARNING, filename, line, r,
90 "Failed to parse search domains string '%s', ignoring.", rvalue);
91 return 0;
92 }
93 }
94
95 /* If we have a manual setting, then we stop reading
96 * /etc/resolv.conf */
97 m->read_resolv_conf = false;
98
99 return 0;
100}
101
102int config_parse_dns_stub_listener_extra(
103 const char *unit,
104 const char *filename,
105 unsigned line,
106 const char *section,
107 unsigned section_line,
108 const char *lvalue,
109 int ltype,
110 const char *rvalue,
111 void *data,
112 void *userdata) {
113
114 _cleanup_free_ DnsStubListenerExtra *stub = NULL;
115 Manager *m = userdata;
116 const char *p;
117 int r;
118
119 assert(filename);
120 assert(lvalue);
121 assert(rvalue);
122 assert(data);
123
124 if (isempty(rvalue)) {
125 m->dns_extra_stub_listeners = ordered_set_free(m->dns_extra_stub_listeners);
126 return 0;
127 }
128
129 r = dns_stub_listener_extra_new(m, &stub);
130 if (r < 0)
131 return log_oom();
132
133 p = startswith(rvalue, "udp:");
134 if (p)
135 stub->mode = DNS_STUB_LISTENER_UDP;
136 else {
137 p = startswith(rvalue, "tcp:");
138 if (p)
139 stub->mode = DNS_STUB_LISTENER_TCP;
140 else {
141 stub->mode = DNS_STUB_LISTENER_YES;
142 p = rvalue;
143 }
144 }
145
146 r = in_addr_port_ifindex_name_from_string_auto(p, &stub->family, &stub->address, &stub->port, NULL, NULL);
147 if (r < 0) {
148 log_syntax(unit, LOG_WARNING, filename, line, r,
149 "Failed to parse address in %s=%s, ignoring assignment: %m",
150 lvalue, rvalue);
151 return 0;
152 }
153
154 r = ordered_set_ensure_put(&m->dns_extra_stub_listeners, &dns_stub_listener_extra_hash_ops, stub);
155 if (r == -ENOMEM)
156 return log_oom();
157 if (r < 0) {
158 log_syntax(unit, LOG_WARNING, filename, line, r,
159 "Failed to store %s=%s, ignoring assignment: %m", lvalue, rvalue);
160 return 0;
161 }
162
163 TAKE_PTR(stub);
164
165 return 0;
166}
167
168static void read_credentials(Manager *m) {
169 _cleanup_free_ char *dns = NULL, *domains = NULL;
170 int r;
171
172 assert(m);
173
174 /* Hmm, if we aren't supposed to read /etc/resolv.conf because the DNS settings were already
175 * configured explicitly in our config file, we don't want to honour credentials either */
176 if (!m->read_resolv_conf)
177 return;
178
179 r = read_credential_strings_many("network.dns", &dns,
180 "network.search_domains", &domains);
181 if (r < 0)
182 log_warning_errno(r, "Failed to read credentials, ignoring: %m");
183
184 if (dns) {
185 r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_SYSTEM, dns);
186 if (r < 0)
187 log_warning_errno(r, "Failed to parse credential network.dns '%s', ignoring.", dns);
188
189 m->read_resolv_conf = false;
190 }
191
192 if (domains) {
193 r = manager_parse_search_domains_and_warn(m, domains);
194 if (r < 0)
195 log_warning_errno(r, "Failed to parse credential network.search_domains '%s', ignoring.", domains);
196
197 m->read_resolv_conf = false;
198 }
199}
200
201struct ProcCmdlineInfo {
202 Manager *manager;
203
204 /* If there's a setting configured via /proc/cmdline we want to reset the configured lists, but only
205 * once, so that multiple nameserver= or domain= settings can be specified on the kernel command line
206 * and will be combined. These booleans will be set once we erase the list once. */
207 bool dns_server_unlinked;
208 bool search_domain_unlinked;
209};
210
211static int proc_cmdline_callback(const char *key, const char *value, void *data) {
212 struct ProcCmdlineInfo *info = ASSERT_PTR(data);
213 int r;
214
215 assert(key);
216 assert(info->manager);
217
218 /* The kernel command line option names are chosen to be compatible with what various tools already
219 * interpret, for example dracut and SUSE Linux. */
220
221 if (streq(key, "nameserver")) {
222
223 if (proc_cmdline_value_missing(key, value))
224 return 0;
225
226 if (!info->dns_server_unlinked) {
227 /* The kernel command line overrides any prior configuration */
228 dns_server_unlink_all(manager_get_first_dns_server(info->manager, DNS_SERVER_SYSTEM));
229 info->dns_server_unlinked = true;
230 }
231
232 r = manager_parse_dns_server_string_and_warn(info->manager, DNS_SERVER_SYSTEM, value);
233 if (r < 0)
234 log_warning_errno(r, "Failed to parse DNS server string '%s', ignoring.", value);
235
236 info->manager->read_resolv_conf = false;
237
238 } else if (streq(key, "domain")) {
239
240 if (proc_cmdline_value_missing(key, value))
241 return 0;
242
243 if (!info->search_domain_unlinked) {
244 dns_search_domain_unlink_all(info->manager->search_domains);
245 info->search_domain_unlinked = true;
246 }
247
248 r = manager_parse_search_domains_and_warn(info->manager, value);
249 if (r < 0)
250 log_warning_errno(r, "Failed to parse credential provided search domain string '%s', ignoring.", value);
251
252 info->manager->read_resolv_conf = false;
253 }
254
255 return 0;
256}
257
258static void read_proc_cmdline(Manager *m) {
259 int r;
260
261 assert(m);
262
263 r = proc_cmdline_parse(proc_cmdline_callback, &(struct ProcCmdlineInfo) { .manager = m }, 0);
264 if (r < 0)
265 log_warning_errno(r, "Failed to read kernel command line, ignoring: %m");
266}
267
268int manager_parse_config_file(Manager *m) {
269 int r;
270
271 assert(m);
272
273 r = config_parse_standard_file_with_dropins(
274 "systemd/resolved.conf",
275 "Resolve\0",
276 config_item_perf_lookup, resolved_gperf_lookup,
277 CONFIG_PARSE_WARN,
278 /* userdata= */ m);
279 if (r < 0)
280 return r;
281
282 read_credentials(m); /* credentials are only used when nothing is explicitly configured … */
283 read_proc_cmdline(m); /* … but kernel command line overrides local configuration. */
284
285 if (m->need_builtin_fallbacks) {
286 r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_FALLBACK, DNS_SERVERS);
287 if (r < 0)
288 return r;
289 }
290
291#if !HAVE_OPENSSL
292 if (m->dnssec_mode != DNSSEC_NO) {
293 log_warning("DNSSEC option cannot be enabled or set to allow-downgrade when systemd-resolved is built without openssl. Turning off DNSSEC support.");
294 m->dnssec_mode = DNSSEC_NO;
295 }
296#endif
297
298#if !ENABLE_DNS_OVER_TLS
299 if (m->dns_over_tls_mode != DNS_OVER_TLS_NO) {
300 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.");
301 m->dns_over_tls_mode = DNS_OVER_TLS_NO;
302 }
303#endif
304 return 0;
305}
306
307int config_parse_record_types(
308 const char *unit,
309 const char *filename,
310 unsigned line,
311 const char *section,
312 unsigned section_line,
313 const char *lvalue,
314 int ltype,
315 const char *rvalue,
316 void *data,
317 void *userdata) {
318
319 Set **types = ASSERT_PTR(data);
320 int r;
321
322 if (isempty(rvalue)) {
323 *types = set_free(*types);
324 return 1;
325 }
326
327 for (const char *p = rvalue;;) {
328 _cleanup_free_ char *word = NULL;
329 r = extract_first_word(&p, &word, NULL, 0);
330 if (r < 0)
331 return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
332 if (r == 0)
333 return 1;
334
335 r = dns_type_from_string(word);
336 if (r < 0) {
337 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid DNS record type, ignoring: %s", word);
338 continue;
339 }
340
341 r = set_ensure_put(types, NULL, INT_TO_PTR(r));
342 if (r < 0)
343 return log_oom();
344 }
345}