1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "alloc-util.h"
4 #include "conf-parser.h"
5 #include "creds-util.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"
16 #include "socket-netlink.h"
17 #include "string-util.h"
19 DEFINE_CONFIG_PARSE_ENUM(config_parse_dns_stub_listener_mode
, dns_stub_listener_mode
, DnsStubListenerMode
);
21 int config_parse_dns_servers(
26 unsigned section_line
,
33 Manager
*m
= ASSERT_PTR(userdata
);
41 /* Empty assignment means clear the list */
42 dns_server_unlink_all(manager_get_first_dns_server(m
, ltype
));
44 /* Otherwise, add to the list */
45 r
= manager_parse_dns_server_string_and_warn(m
, ltype
, rvalue
);
47 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
48 "Failed to parse DNS server string '%s', ignoring.", rvalue
);
53 /* If we have a manual setting, then we stop reading
55 if (ltype
== DNS_SERVER_SYSTEM
)
56 m
->read_resolv_conf
= false;
57 if (ltype
== DNS_SERVER_FALLBACK
)
58 m
->need_builtin_fallbacks
= false;
63 int config_parse_search_domains(
68 unsigned section_line
,
75 Manager
*m
= ASSERT_PTR(userdata
);
83 /* Empty assignment means clear the list */
84 dns_search_domain_unlink_all(m
->search_domains
);
86 /* Otherwise, add to the list */
87 r
= manager_parse_search_domains_and_warn(m
, rvalue
);
89 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
90 "Failed to parse search domains string '%s', ignoring.", rvalue
);
95 /* If we have a manual setting, then we stop reading
97 m
->read_resolv_conf
= false;
102 int config_parse_dns_stub_listener_extra(
104 const char *filename
,
107 unsigned section_line
,
114 _cleanup_free_ DnsStubListenerExtra
*stub
= NULL
;
115 Manager
*m
= userdata
;
124 if (isempty(rvalue
)) {
125 m
->dns_extra_stub_listeners
= ordered_set_free(m
->dns_extra_stub_listeners
);
129 r
= dns_stub_listener_extra_new(m
, &stub
);
133 p
= startswith(rvalue
, "udp:");
135 stub
->mode
= DNS_STUB_LISTENER_UDP
;
137 p
= startswith(rvalue
, "tcp:");
139 stub
->mode
= DNS_STUB_LISTENER_TCP
;
141 stub
->mode
= DNS_STUB_LISTENER_YES
;
146 r
= in_addr_port_ifindex_name_from_string_auto(p
, &stub
->family
, &stub
->address
, &stub
->port
, NULL
, NULL
);
148 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
149 "Failed to parse address in %s=%s, ignoring assignment: %m",
154 r
= ordered_set_ensure_put(&m
->dns_extra_stub_listeners
, &dns_stub_listener_extra_hash_ops
, stub
);
158 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
159 "Failed to store %s=%s, ignoring assignment: %m", lvalue
, rvalue
);
168 static void read_credentials(Manager
*m
) {
169 _cleanup_free_
char *dns
= NULL
, *domains
= NULL
;
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
)
179 r
= read_credential_strings_many("network.dns", &dns
,
180 "network.search_domains", &domains
);
182 log_warning_errno(r
, "Failed to read credentials, ignoring: %m");
185 r
= manager_parse_dns_server_string_and_warn(m
, DNS_SERVER_SYSTEM
, dns
);
187 log_warning_errno(r
, "Failed to parse credential network.dns '%s', ignoring.", dns
);
189 m
->read_resolv_conf
= false;
193 r
= manager_parse_search_domains_and_warn(m
, domains
);
195 log_warning_errno(r
, "Failed to parse credential network.search_domains '%s', ignoring.", domains
);
197 m
->read_resolv_conf
= false;
201 struct ProcCmdlineInfo
{
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
;
211 static int proc_cmdline_callback(const char *key
, const char *value
, void *data
) {
212 struct ProcCmdlineInfo
*info
= ASSERT_PTR(data
);
216 assert(info
->manager
);
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. */
221 if (streq(key
, "nameserver")) {
223 if (proc_cmdline_value_missing(key
, value
))
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;
232 r
= manager_parse_dns_server_string_and_warn(info
->manager
, DNS_SERVER_SYSTEM
, value
);
234 log_warning_errno(r
, "Failed to parse DNS server string '%s', ignoring.", value
);
236 info
->manager
->read_resolv_conf
= false;
238 } else if (streq(key
, "domain")) {
240 if (proc_cmdline_value_missing(key
, value
))
243 if (!info
->search_domain_unlinked
) {
244 dns_search_domain_unlink_all(info
->manager
->search_domains
);
245 info
->search_domain_unlinked
= true;
248 r
= manager_parse_search_domains_and_warn(info
->manager
, value
);
250 log_warning_errno(r
, "Failed to parse credential provided search domain string '%s', ignoring.", value
);
252 info
->manager
->read_resolv_conf
= false;
258 static void read_proc_cmdline(Manager
*m
) {
263 r
= proc_cmdline_parse(proc_cmdline_callback
, &(struct ProcCmdlineInfo
) { .manager
= m
}, 0);
265 log_warning_errno(r
, "Failed to read kernel command line, ignoring: %m");
268 int manager_parse_config_file(Manager
*m
) {
273 r
= config_parse_standard_file_with_dropins(
274 "systemd/resolved.conf",
276 config_item_perf_lookup
, resolved_gperf_lookup
,
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. */
285 if (m
->need_builtin_fallbacks
) {
286 r
= manager_parse_dns_server_string_and_warn(m
, DNS_SERVER_FALLBACK
, DNS_SERVERS
);
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
;
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
;
307 int config_parse_record_types(
309 const char *filename
,
312 unsigned section_line
,
319 Set
**types
= ASSERT_PTR(data
);
322 if (isempty(rvalue
)) {
323 *types
= set_free(*types
);
327 for (const char *p
= rvalue
;;) {
328 _cleanup_free_
char *word
= NULL
;
329 r
= extract_first_word(&p
, &word
, NULL
, 0);
331 return log_syntax_parse_error(unit
, filename
, line
, r
, lvalue
, rvalue
);
335 r
= dns_type_from_string(word
);
337 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid DNS record type, ignoring: %s", word
);
341 r
= set_ensure_put(types
, NULL
, INT_TO_PTR(r
));