1 /* SPDX-License-Identifier: LGPL-2.1+ */
6 #include "alloc-util.h"
8 #include "dns-domain.h"
9 #include "extract-word.h"
11 #include "parse-util.h"
12 #include "resolvconf-compat.h"
13 #include "resolve-tool.h"
14 #include "resolved-def.h"
15 #include "string-util.h"
18 static void resolvconf_help(void) {
19 printf("%1$s -a INTERFACE < FILE\n"
22 "Register DNS server and domain configuration with systemd-resolved.\n\n"
23 " -h --help Show this help\n"
24 " --version Show package version\n"
25 " -a Register per-interface DNS server and domain data\n"
26 " -d Unregister per-interface DNS server and domain data\n"
27 " -f Ignore if specified interface does not exist\n"
28 " -x Send DNS traffic preferably over this interface\n"
30 "This is a compatibility alias for the systemd-resolve(1) tool, providing native\n"
31 "command line compatibility with the resolvconf(8) tool of various Linux\n"
32 "distributions and BSD systems. Some options supported by other implementations\n"
33 "are not supported and are ignored: -m, -p. Various options supported by other\n"
34 "implementations are not supported and will cause the invocation to fail: -u,\n"
35 "-I, -i, -l, -R, -r, -v, -V, --enable-updates, --disable-updates,\n"
36 "--updates-are-enabled.\n"
37 , program_invocation_short_name
);
40 static int parse_nameserver(const char *string
) {
46 _cleanup_free_
char *word
= NULL
;
47 struct in_addr_data data
, *n
;
50 r
= extract_first_word(&string
, &word
, NULL
, 0);
56 r
= in_addr_ifindex_from_string_auto(word
, &data
.family
, &data
.address
, &ifindex
);
58 return log_error_errno(r
, "Failed to parse name server '%s': %m", word
);
60 if (ifindex
> 0 && ifindex
!= arg_ifindex
) {
61 log_error("Name server interface '%s' does not match selected interface: %m", word
);
65 /* Some superficial filtering */
66 if (in_addr_is_null(data
.family
, &data
.address
))
68 if (data
.family
== AF_INET
&& data
.address
.in
.s_addr
== htobe32(INADDR_DNS_STUB
)) /* resolved's own stub? */
71 n
= reallocarray(arg_set_dns
, arg_n_set_dns
+ 1, sizeof(struct in_addr_data
));
76 arg_set_dns
[arg_n_set_dns
++] = data
;
82 static int parse_search_domain(const char *string
) {
88 _cleanup_free_
char *word
= NULL
;
90 r
= extract_first_word(&string
, &word
, NULL
, EXTRACT_QUOTES
);
96 r
= dns_name_is_valid(word
);
98 return log_error_errno(r
, "Failed to validate specified domain '%s': %m", word
);
100 log_error("Domain not valid: %s", word
);
104 if (strv_push(&arg_set_domain
, word
) < 0)
113 int resolvconf_parse_argv(int argc
, char *argv
[]) {
119 ARG_UPDATES_ARE_ENABLED
,
122 static const struct option options
[] = {
123 { "help", no_argument
, NULL
, 'h' },
124 { "version", no_argument
, NULL
, ARG_VERSION
},
126 /* The following are specific to Debian's original resolvconf */
127 { "enable-updates", no_argument
, NULL
, ARG_ENABLE_UPDATES
},
128 { "disable-updates", no_argument
, NULL
, ARG_DISABLE_UPDATES
},
129 { "updates-are-enabled", no_argument
, NULL
, ARG_UPDATES_ARE_ENABLED
},
135 TYPE_PRIVATE
, /* -p: Not supported, treated identically to TYPE_REGULAR */
136 TYPE_EXCLUSIVE
, /* -x */
137 } type
= TYPE_REGULAR
;
139 const char *dot
, *iface
;
145 /* openresolv checks these environment variables */
146 if (getenv("IF_EXCLUSIVE"))
147 type
= TYPE_EXCLUSIVE
;
148 if (getenv("IF_PRIVATE"))
149 type
= TYPE_PRIVATE
; /* not actually supported */
151 arg_mode
= _MODE_INVALID
;
153 while ((c
= getopt_long(argc
, argv
, "hadxpfm:uIi:l:Rr:vV", options
, NULL
)) >= 0)
158 return 0; /* done */;
163 /* -a and -d is what everybody can agree on */
165 arg_mode
= MODE_SET_LINK
;
169 arg_mode
= MODE_REVERT_LINK
;
172 /* The exclusive/private/force stuff is an openresolv invention, we support in some skewed way */
174 type
= TYPE_EXCLUSIVE
;
178 type
= TYPE_PRIVATE
; /* not actually supported */
182 arg_ifindex_permissive
= true;
185 /* The metrics stuff is an openresolv invention we ignore (and don't really need) */
187 log_debug("Switch -%c ignored.", c
);
190 /* Everybody else can agree on the existance of -u but we don't support it. */
193 /* The following options are openresolv inventions we don't support. */
201 log_error("Switch -%c not supported.", c
);
204 /* The Debian resolvconf commands we don't support. */
205 case ARG_ENABLE_UPDATES
:
206 log_error("Switch --enable-updates not supported.");
208 case ARG_DISABLE_UPDATES
:
209 log_error("Switch --disable-updates not supported.");
211 case ARG_UPDATES_ARE_ENABLED
:
212 log_error("Switch --updates-are-enabled not supported.");
219 assert_not_reached("Unhandled option");
222 if (arg_mode
== _MODE_INVALID
) {
223 log_error("Expected either -a or -d on the command line.");
227 if (optind
+1 != argc
) {
228 log_error("Expected interface name as argument.");
232 dot
= strchr(argv
[optind
], '.');
234 iface
= strndupa(argv
[optind
], dot
- argv
[optind
]);
235 log_debug("Ignoring protocol specifier '%s'.", dot
+ 1);
237 iface
= argv
[optind
];
240 if (parse_ifindex(iface
, &arg_ifindex
) < 0) {
243 ifi
= if_nametoindex(iface
);
245 if (errno
== ENODEV
&& arg_ifindex_permissive
) {
246 log_debug("Interface '%s' not found, but -f specified, ignoring.", iface
);
250 return log_error_errno(errno
, "Unknown interface '%s': %m", iface
);
256 if (arg_mode
== MODE_SET_LINK
) {
260 _cleanup_free_
char *line
= NULL
;
263 r
= read_line(stdin
, LONG_LINE_MAX
, &line
);
265 return log_error_errno(r
, "Failed to read from stdin: %m");
272 if (IN_SET(*l
, '#', ';', 0))
275 a
= first_word(l
, "nameserver");
277 (void) parse_nameserver(a
);
281 a
= first_word(l
, "domain");
283 a
= first_word(l
, "search");
285 (void) parse_search_domain(a
);
289 log_syntax(NULL
, LOG_DEBUG
, "stdin", n
, 0, "Ignoring resolv.conf line: %s", l
);
292 if (type
== TYPE_EXCLUSIVE
) {
294 /* If -x mode is selected, let's preferably route non-suffixed lookups to this interface. This
295 * somewhat matches the original -x behaviour */
297 r
= strv_extend(&arg_set_domain
, "~.");
301 } else if (type
== TYPE_PRIVATE
)
302 log_debug("Private DNS server data not supported, ignoring.");
304 if (arg_n_set_dns
== 0) {
305 log_error("No DNS servers specified, refusing operation.");
310 return 1; /* work to do */