1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2014 Tom Gundersen <teg@jklm.no>
8 #include "alloc-util.h"
9 #include "conf-parser.h"
11 #include "extract-word.h"
12 #include "hexdecoct.h"
13 #include "parse-util.h"
14 #include "resolved-conf.h"
15 #include "resolved-dnssd.h"
16 #include "specifier.h"
17 #include "string-table.h"
18 #include "string-util.h"
21 DEFINE_CONFIG_PARSE_ENUM(config_parse_dns_stub_listener_mode
, dns_stub_listener_mode
, DnsStubListenerMode
, "Failed to parse DNS stub listener mode setting");
23 static const char* const dns_stub_listener_mode_table
[_DNS_STUB_LISTENER_MODE_MAX
] = {
24 [DNS_STUB_LISTENER_NO
] = "no",
25 [DNS_STUB_LISTENER_UDP
] = "udp",
26 [DNS_STUB_LISTENER_TCP
] = "tcp",
27 [DNS_STUB_LISTENER_YES
] = "yes",
29 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_stub_listener_mode
, DnsStubListenerMode
, DNS_STUB_LISTENER_YES
);
31 int manager_add_dns_server_by_string(Manager
*m
, DnsServerType type
, const char *word
) {
32 union in_addr_union address
;
33 int family
, r
, ifindex
= 0;
39 r
= in_addr_ifindex_from_string_auto(word
, &family
, &address
, &ifindex
);
43 /* Silently filter out 0.0.0.0 and 127.0.0.53 (our own stub DNS listener) */
44 if (!dns_server_address_valid(family
, &address
))
47 /* Filter out duplicates */
48 s
= dns_server_find(manager_get_first_dns_server(m
, type
), family
, &address
, ifindex
);
51 * Drop the marker. This is used to find the servers
52 * that ceased to exist, see
53 * manager_mark_dns_servers() and
54 * manager_flush_marked_dns_servers().
56 dns_server_move_back_and_unmark(s
);
60 return dns_server_new(m
, NULL
, type
, NULL
, family
, &address
, ifindex
);
63 int manager_parse_dns_server_string_and_warn(Manager
*m
, DnsServerType type
, const char *string
) {
70 _cleanup_free_
char *word
= NULL
;
72 r
= extract_first_word(&string
, &word
, NULL
, 0);
78 r
= manager_add_dns_server_by_string(m
, type
, word
);
80 log_warning_errno(r
, "Failed to add DNS server address '%s', ignoring: %m", word
);
86 int manager_add_search_domain_by_string(Manager
*m
, const char *domain
) {
94 route_only
= *domain
== '~';
98 if (dns_name_is_root(domain
) || streq(domain
, "*")) {
103 r
= dns_search_domain_find(m
->search_domains
, domain
, &d
);
107 dns_search_domain_move_back_and_unmark(d
);
109 r
= dns_search_domain_new(m
, &d
, DNS_SEARCH_DOMAIN_SYSTEM
, NULL
, domain
);
114 d
->route_only
= route_only
;
118 int manager_parse_search_domains_and_warn(Manager
*m
, const char *string
) {
125 _cleanup_free_
char *word
= NULL
;
127 r
= extract_first_word(&string
, &word
, NULL
, EXTRACT_QUOTES
);
133 r
= manager_add_search_domain_by_string(m
, word
);
135 log_warning_errno(r
, "Failed to add search domain '%s', ignoring: %m", word
);
141 int config_parse_dns_servers(
143 const char *filename
,
146 unsigned section_line
,
153 Manager
*m
= userdata
;
162 /* Empty assignment means clear the list */
163 dns_server_unlink_all(manager_get_first_dns_server(m
, ltype
));
165 /* Otherwise, add to the list */
166 r
= manager_parse_dns_server_string_and_warn(m
, ltype
, rvalue
);
168 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse DNS server string '%s'. Ignoring.", rvalue
);
173 /* If we have a manual setting, then we stop reading
174 * /etc/resolv.conf */
175 if (ltype
== DNS_SERVER_SYSTEM
)
176 m
->read_resolv_conf
= false;
177 if (ltype
== DNS_SERVER_FALLBACK
)
178 m
->need_builtin_fallbacks
= false;
183 int config_parse_search_domains(
185 const char *filename
,
188 unsigned section_line
,
195 Manager
*m
= userdata
;
204 /* Empty assignment means clear the list */
205 dns_search_domain_unlink_all(m
->search_domains
);
207 /* Otherwise, add to the list */
208 r
= manager_parse_search_domains_and_warn(m
, rvalue
);
210 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse search domains string '%s'. Ignoring.", rvalue
);
215 /* If we have a manual setting, then we stop reading
216 * /etc/resolv.conf */
217 m
->read_resolv_conf
= false;
222 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
) {
223 static const Specifier specifier_table
[] = {
224 { 'b', specifier_boot_id
, NULL
},
225 { 'H', specifier_host_name
, NULL
},
226 { 'm', specifier_machine_id
, NULL
},
227 { 'v', specifier_kernel_release
, NULL
},
230 DnssdService
*s
= userdata
;
231 _cleanup_free_
char *name
= NULL
;
239 if (isempty(rvalue
)) {
240 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Service instance name can't be empty. Ignoring.");
244 r
= free_and_strdup(&s
->name_template
, rvalue
);
248 r
= specifier_printf(s
->name_template
, specifier_table
, NULL
, &name
);
250 return log_debug_errno(r
, "Failed to replace specifiers: %m");
252 if (!dns_service_name_is_valid(name
)) {
253 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Service instance name template renders to invalid name '%s'. Ignoring.", name
);
260 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
) {
261 DnssdService
*s
= userdata
;
269 if (isempty(rvalue
)) {
270 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Service type can't be empty. Ignoring.");
274 if (!dnssd_srv_type_is_valid(rvalue
)) {
275 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Service type is invalid. Ignoring.");
279 r
= free_and_strdup(&s
->type
, rvalue
);
286 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
) {
287 _cleanup_(dnssd_txtdata_freep
) DnssdTxtData
*txt_data
= NULL
;
288 DnssdService
*s
= userdata
;
289 DnsTxtItem
*last
= NULL
;
296 if (isempty(rvalue
)) {
297 /* Flush out collected items */
298 s
->txt_data_items
= dnssd_txtdata_free_all(s
->txt_data_items
);
302 txt_data
= new0(DnssdTxtData
, 1);
307 _cleanup_free_
char *word
= NULL
;
308 _cleanup_free_
char *key
= NULL
;
309 _cleanup_free_
char *value
= NULL
;
310 _cleanup_free_
void *decoded
= NULL
;
315 r
= extract_first_word(&rvalue
, &word
, NULL
,
316 EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
|EXTRACT_CUNESCAPE_RELAX
);
322 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
324 r
= split_pair(word
, "=", &key
, &value
);
328 key
= TAKE_PTR(word
);
330 if (!ascii_is_valid(key
)) {
331 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid syntax, ignoring: %s", key
);
337 case DNS_TXT_ITEM_DATA
:
339 r
= unbase64mem(value
, strlen(value
), &decoded
, &length
);
343 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
344 "Invalid base64 encoding, ignoring: %s", value
);
347 r
= dnssd_txt_item_new_from_data(key
, decoded
, length
, &i
);
352 case DNS_TXT_ITEM_TEXT
:
353 r
= dnssd_txt_item_new_from_string(key
, value
, &i
);
359 assert_not_reached("Unknown type of Txt config");
362 LIST_INSERT_AFTER(items
, txt_data
->txt
, last
, i
);
366 if (!LIST_IS_EMPTY(txt_data
->txt
)) {
367 LIST_PREPEND(items
, s
->txt_data_items
, txt_data
);
374 int manager_parse_config_file(Manager
*m
) {
379 r
= config_parse_many_nulstr(PKGSYSCONFDIR
"/resolved.conf",
380 CONF_PATHS_NULSTR("systemd/resolved.conf.d"),
382 config_item_perf_lookup
, resolved_gperf_lookup
,
383 CONFIG_PARSE_WARN
, m
);
387 if (m
->need_builtin_fallbacks
) {
388 r
= manager_parse_dns_server_string_and_warn(m
, DNS_SERVER_FALLBACK
, DNS_SERVERS
);
394 if (m
->dnssec_mode
!= DNSSEC_NO
) {
395 log_warning("DNSSEC option cannot be enabled or set to allow-downgrade when systemd-resolved is built without gcrypt support. Turning off DNSSEC support.");
396 m
->dnssec_mode
= DNSSEC_NO
;
401 if (m
->dns_over_tls_mode
!= DNS_OVER_TLS_NO
) {
402 log_warning("DNS-over-TLS option cannot be set to opportunistic when systemd-resolved is built without gnutls support. Turning off DNS-over-TLS support.");
403 m
->dns_over_tls_mode
= DNS_OVER_TLS_NO
;