1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2014 Tom Gundersen <teg@jklm.no>
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.
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.
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/>.
21 #include "alloc-util.h"
22 #include "conf-parser.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"
34 DEFINE_CONFIG_PARSE_ENUM(config_parse_dns_stub_listener_mode
, dns_stub_listener_mode
, DnsStubListenerMode
, "Failed to parse DNS stub listener mode setting");
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",
42 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_stub_listener_mode
, DnsStubListenerMode
, DNS_STUB_LISTENER_YES
);
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;
52 r
= in_addr_ifindex_from_string_auto(word
, &family
, &address
, &ifindex
);
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
))
60 /* Filter out duplicates */
61 s
= dns_server_find(manager_get_first_dns_server(m
, type
), family
, &address
, ifindex
);
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().
69 dns_server_move_back_and_unmark(s
);
73 return dns_server_new(m
, NULL
, type
, NULL
, family
, &address
, ifindex
);
76 int manager_parse_dns_server_string_and_warn(Manager
*m
, DnsServerType type
, const char *string
) {
83 _cleanup_free_
char *word
= NULL
;
85 r
= extract_first_word(&string
, &word
, NULL
, 0);
91 r
= manager_add_dns_server_by_string(m
, type
, word
);
93 log_warning_errno(r
, "Failed to add DNS server address '%s', ignoring: %m", word
);
99 int manager_add_search_domain_by_string(Manager
*m
, const char *domain
) {
107 route_only
= *domain
== '~';
111 if (dns_name_is_root(domain
) || streq(domain
, "*")) {
116 r
= dns_search_domain_find(m
->search_domains
, domain
, &d
);
120 dns_search_domain_move_back_and_unmark(d
);
122 r
= dns_search_domain_new(m
, &d
, DNS_SEARCH_DOMAIN_SYSTEM
, NULL
, domain
);
127 d
->route_only
= route_only
;
131 int manager_parse_search_domains_and_warn(Manager
*m
, const char *string
) {
138 _cleanup_free_
char *word
= NULL
;
140 r
= extract_first_word(&string
, &word
, NULL
, EXTRACT_QUOTES
);
146 r
= manager_add_search_domain_by_string(m
, word
);
148 log_warning_errno(r
, "Failed to add search domain '%s', ignoring: %m", word
);
154 int config_parse_dns_servers(
156 const char *filename
,
159 unsigned section_line
,
166 Manager
*m
= userdata
;
175 /* Empty assignment means clear the list */
176 dns_server_unlink_all(manager_get_first_dns_server(m
, ltype
));
178 /* Otherwise, add to the list */
179 r
= manager_parse_dns_server_string_and_warn(m
, ltype
, rvalue
);
181 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse DNS server string '%s'. Ignoring.", rvalue
);
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;
196 int config_parse_search_domains(
198 const char *filename
,
201 unsigned section_line
,
208 Manager
*m
= userdata
;
217 /* Empty assignment means clear the list */
218 dns_search_domain_unlink_all(m
->search_domains
);
220 /* Otherwise, add to the list */
221 r
= manager_parse_search_domains_and_warn(m
, rvalue
);
223 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse search domains string '%s'. Ignoring.", rvalue
);
228 /* If we have a manual setting, then we stop reading
229 * /etc/resolv.conf */
230 m
->read_resolv_conf
= false;
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
},
243 DnssdService
*s
= userdata
;
244 _cleanup_free_
char *name
= NULL
;
252 if (isempty(rvalue
)) {
253 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Service instance name can't be empty. Ignoring.");
257 r
= free_and_strdup(&s
->name_template
, rvalue
);
261 r
= specifier_printf(s
->name_template
, specifier_table
, NULL
, &name
);
263 return log_debug_errno(r
, "Failed to replace specifiers: %m");
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
);
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
;
282 if (isempty(rvalue
)) {
283 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Service type can't be empty. Ignoring.");
287 if (!dnssd_srv_type_is_valid(rvalue
)) {
288 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Service type is invalid. Ignoring.");
292 r
= free_and_strdup(&s
->type
, rvalue
);
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
;
309 if (isempty(rvalue
)) {
310 /* Flush out collected items */
311 s
->txt_data_items
= dnssd_txtdata_free_all(s
->txt_data_items
);
315 txt_data
= new0(DnssdTxtData
, 1);
320 _cleanup_free_
char *word
= NULL
;
321 _cleanup_free_
char *key
= NULL
;
322 _cleanup_free_
char *value
= NULL
;
323 _cleanup_free_
void *decoded
= NULL
;
328 r
= extract_first_word(&rvalue
, &word
, NULL
,
329 EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
|EXTRACT_CUNESCAPE_RELAX
);
335 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
337 r
= split_pair(word
, "=", &key
, &value
);
345 if (!ascii_is_valid(key
)) {
346 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid syntax, ignoring: %s", key
);
352 case DNS_TXT_ITEM_DATA
:
354 r
= unbase64mem(value
, strlen(value
), &decoded
, &length
);
358 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
359 "Invalid base64 encoding, ignoring: %s", value
);
362 r
= dnssd_txt_item_new_from_data(key
, decoded
, length
, &i
);
367 case DNS_TXT_ITEM_TEXT
:
368 r
= dnssd_txt_item_new_from_string(key
, value
, &i
);
374 assert_not_reached("Unknown type of Txt config");
377 LIST_INSERT_AFTER(items
, txt_data
->txt
, last
, i
);
381 if (!LIST_IS_EMPTY(txt_data
->txt
)) {
382 LIST_PREPEND(items
, s
->txt_data_items
, txt_data
);
389 int manager_parse_config_file(Manager
*m
) {
394 r
= config_parse_many_nulstr(PKGSYSCONFDIR
"/resolved.conf",
395 CONF_PATHS_NULSTR("systemd/resolved.conf.d"),
397 config_item_perf_lookup
, resolved_gperf_lookup
,
398 CONFIG_PARSE_WARN
, m
);
402 if (m
->need_builtin_fallbacks
) {
403 r
= manager_parse_dns_server_string_and_warn(m
, DNS_SERVER_FALLBACK
, DNS_SERVERS
);
409 if (m
->dnssec_mode
!= DNSSEC_NO
) {
410 log_warning("DNSSEC option cannot be enabled or set to allow-downgrade when systemd-resolved is built without gcrypt support. Turning off DNSSEC support.");
411 m
->dnssec_mode
= DNSSEC_NO
;