]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolvconf-compat.c
tree-wide: drop spurious newlines (#8764)
[thirdparty/systemd.git] / src / resolve / resolvconf-compat.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <getopt.h>
4 #include <net/if.h>
5
6 #include "alloc-util.h"
7 #include "def.h"
8 #include "dns-domain.h"
9 #include "extract-word.h"
10 #include "fileio.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"
16 #include "strv.h"
17
18 static void resolvconf_help(void) {
19 printf("%1$s -a INTERFACE < FILE\n"
20 "%1$s -d INTERFACE\n"
21 "\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"
29 "\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);
38 }
39
40 static int parse_nameserver(const char *string) {
41 int r;
42
43 assert(string);
44
45 for (;;) {
46 _cleanup_free_ char *word = NULL;
47 struct in_addr_data data, *n;
48 int ifindex = 0;
49
50 r = extract_first_word(&string, &word, NULL, 0);
51 if (r < 0)
52 return r;
53 if (r == 0)
54 break;
55
56 r = in_addr_ifindex_from_string_auto(word, &data.family, &data.address, &ifindex);
57 if (r < 0)
58 return log_error_errno(r, "Failed to parse name server '%s': %m", word);
59
60 if (ifindex > 0 && ifindex != arg_ifindex) {
61 log_error("Name server interface '%s' does not match selected interface: %m", word);
62 return -EINVAL;
63 }
64
65 /* Some superficial filtering */
66 if (in_addr_is_null(data.family, &data.address))
67 continue;
68 if (data.family == AF_INET && data.address.in.s_addr == htobe32(INADDR_DNS_STUB)) /* resolved's own stub? */
69 continue;
70
71 n = reallocarray(arg_set_dns, arg_n_set_dns + 1, sizeof(struct in_addr_data));
72 if (!n)
73 return log_oom();
74 arg_set_dns = n;
75
76 arg_set_dns[arg_n_set_dns++] = data;
77 }
78
79 return 0;
80 }
81
82 static int parse_search_domain(const char *string) {
83 int r;
84
85 assert(string);
86
87 for (;;) {
88 _cleanup_free_ char *word = NULL;
89
90 r = extract_first_word(&string, &word, NULL, EXTRACT_QUOTES);
91 if (r < 0)
92 return r;
93 if (r == 0)
94 break;
95
96 r = dns_name_is_valid(word);
97 if (r < 0)
98 return log_error_errno(r, "Failed to validate specified domain '%s': %m", word);
99 if (r == 0) {
100 log_error("Domain not valid: %s", word);
101 return -EINVAL;
102 }
103
104 if (strv_push(&arg_set_domain, word) < 0)
105 return log_oom();
106
107 word = NULL;
108 }
109
110 return 0;
111 }
112
113 int resolvconf_parse_argv(int argc, char *argv[]) {
114
115 enum {
116 ARG_VERSION = 0x100,
117 ARG_ENABLE_UPDATES,
118 ARG_DISABLE_UPDATES,
119 ARG_UPDATES_ARE_ENABLED,
120 };
121
122 static const struct option options[] = {
123 { "help", no_argument, NULL, 'h' },
124 { "version", no_argument, NULL, ARG_VERSION },
125
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 },
130 {}
131 };
132
133 enum {
134 TYPE_REGULAR,
135 TYPE_PRIVATE, /* -p: Not supported, treated identically to TYPE_REGULAR */
136 TYPE_EXCLUSIVE, /* -x */
137 } type = TYPE_REGULAR;
138
139 const char *dot, *iface;
140 int c, r;
141
142 assert(argc >= 0);
143 assert(argv);
144
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 */
150
151 arg_mode = _MODE_INVALID;
152
153 while ((c = getopt_long(argc, argv, "hadxpfm:uIi:l:Rr:vV", options, NULL)) >= 0)
154 switch(c) {
155
156 case 'h':
157 resolvconf_help();
158 return 0; /* done */;
159
160 case ARG_VERSION:
161 return version();
162
163 /* -a and -d is what everybody can agree on */
164 case 'a':
165 arg_mode = MODE_SET_LINK;
166 break;
167
168 case 'd':
169 arg_mode = MODE_REVERT_LINK;
170 break;
171
172 /* The exclusive/private/force stuff is an openresolv invention, we support in some skewed way */
173 case 'x':
174 type = TYPE_EXCLUSIVE;
175 break;
176
177 case 'p':
178 type = TYPE_PRIVATE; /* not actually supported */
179 break;
180
181 case 'f':
182 arg_ifindex_permissive = true;
183 break;
184
185 /* The metrics stuff is an openresolv invention we ignore (and don't really need) */
186 case 'm':
187 log_debug("Switch -%c ignored.", c);
188 break;
189
190 /* Everybody else can agree on the existance of -u but we don't support it. */
191 case 'u':
192
193 /* The following options are openresolv inventions we don't support. */
194 case 'I':
195 case 'i':
196 case 'l':
197 case 'R':
198 case 'r':
199 case 'v':
200 case 'V':
201 log_error("Switch -%c not supported.", c);
202 return -EINVAL;
203
204 /* The Debian resolvconf commands we don't support. */
205 case ARG_ENABLE_UPDATES:
206 log_error("Switch --enable-updates not supported.");
207 return -EINVAL;
208 case ARG_DISABLE_UPDATES:
209 log_error("Switch --disable-updates not supported.");
210 return -EINVAL;
211 case ARG_UPDATES_ARE_ENABLED:
212 log_error("Switch --updates-are-enabled not supported.");
213 return -EINVAL;
214
215 case '?':
216 return -EINVAL;
217
218 default:
219 assert_not_reached("Unhandled option");
220 }
221
222 if (arg_mode == _MODE_INVALID) {
223 log_error("Expected either -a or -d on the command line.");
224 return -EINVAL;
225 }
226
227 if (optind+1 != argc) {
228 log_error("Expected interface name as argument.");
229 return -EINVAL;
230 }
231
232 dot = strchr(argv[optind], '.');
233 if (dot) {
234 iface = strndupa(argv[optind], dot - argv[optind]);
235 log_debug("Ignoring protocol specifier '%s'.", dot + 1);
236 } else
237 iface = argv[optind];
238 optind++;
239
240 if (parse_ifindex(iface, &arg_ifindex) < 0) {
241 int ifi;
242
243 ifi = if_nametoindex(iface);
244 if (ifi <= 0) {
245 if (errno == ENODEV && arg_ifindex_permissive) {
246 log_debug("Interface '%s' not found, but -f specified, ignoring.", iface);
247 return 0; /* done */
248 }
249
250 return log_error_errno(errno, "Unknown interface '%s': %m", iface);
251 }
252
253 arg_ifindex = ifi;
254 }
255
256 if (arg_mode == MODE_SET_LINK) {
257 unsigned n = 0;
258
259 for (;;) {
260 _cleanup_free_ char *line = NULL;
261 const char *a, *l;
262
263 r = read_line(stdin, LONG_LINE_MAX, &line);
264 if (r < 0)
265 return log_error_errno(r, "Failed to read from stdin: %m");
266 if (r == 0)
267 break;
268
269 n++;
270
271 l = strstrip(line);
272 if (IN_SET(*l, '#', ';', 0))
273 continue;
274
275 a = first_word(l, "nameserver");
276 if (a) {
277 (void) parse_nameserver(a);
278 continue;
279 }
280
281 a = first_word(l, "domain");
282 if (!a)
283 a = first_word(l, "search");
284 if (a) {
285 (void) parse_search_domain(a);
286 continue;
287 }
288
289 log_syntax(NULL, LOG_DEBUG, "stdin", n, 0, "Ignoring resolv.conf line: %s", l);
290 }
291
292 if (type == TYPE_EXCLUSIVE) {
293
294 /* If -x mode is selected, let's preferably route non-suffixed lookups to this interface. This
295 * somewhat matches the original -x behaviour */
296
297 r = strv_extend(&arg_set_domain, "~.");
298 if (r < 0)
299 return log_oom();
300
301 } else if (type == TYPE_PRIVATE)
302 log_debug("Private DNS server data not supported, ignoring.");
303
304 if (arg_n_set_dns == 0) {
305 log_error("No DNS servers specified, refusing operation.");
306 return -EINVAL;
307 }
308 }
309
310 return 1; /* work to do */
311 }