]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolvconf-compat.c
man: don't place nginx socket in /tmp (#8757)
[thirdparty/systemd.git] / src / resolve / resolvconf-compat.c
CommitLineData
088c1363
LP
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
18static 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
40static 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
82static 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
113int 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
134 enum {
135 TYPE_REGULAR,
136 TYPE_PRIVATE, /* -p: Not supported, treated identically to TYPE_REGULAR */
137 TYPE_EXCLUSIVE, /* -x */
138 } type = TYPE_REGULAR;
139
140 const char *dot, *iface;
141 int c, r;
142
143 assert(argc >= 0);
144 assert(argv);
145
146 /* openresolv checks these environment variables */
147 if (getenv("IF_EXCLUSIVE"))
148 type = TYPE_EXCLUSIVE;
149 if (getenv("IF_PRIVATE"))
150 type = TYPE_PRIVATE; /* not actually supported */
151
152 arg_mode = _MODE_INVALID;
153
154 while ((c = getopt_long(argc, argv, "hadxpfm:uIi:l:Rr:vV", options, NULL)) >= 0)
155 switch(c) {
156
157 case 'h':
158 resolvconf_help();
159 return 0; /* done */;
160
161 case ARG_VERSION:
162 return version();
163
164 /* -a and -d is what everybody can agree on */
165 case 'a':
166 arg_mode = MODE_SET_LINK;
167 break;
168
169 case 'd':
170 arg_mode = MODE_REVERT_LINK;
171 break;
172
173 /* The exclusive/private/force stuff is an openresolv invention, we support in some skewed way */
174 case 'x':
175 type = TYPE_EXCLUSIVE;
176 break;
177
178 case 'p':
179 type = TYPE_PRIVATE; /* not actually supported */
180 break;
181
182 case 'f':
183 arg_ifindex_permissive = true;
184 break;
185
186 /* The metrics stuff is an openresolv invention we ignore (and don't really need) */
187 case 'm':
188 log_debug("Switch -%c ignored.", c);
189 break;
190
191 /* Everybody else can agree on the existance of -u but we don't support it. */
192 case 'u':
193
194 /* The following options are openresolv inventions we don't support. */
195 case 'I':
196 case 'i':
197 case 'l':
198 case 'R':
199 case 'r':
200 case 'v':
201 case 'V':
202 log_error("Switch -%c not supported.", c);
203 return -EINVAL;
204
205 /* The Debian resolvconf commands we don't support. */
206 case ARG_ENABLE_UPDATES:
207 log_error("Switch --enable-updates not supported.");
208 return -EINVAL;
209 case ARG_DISABLE_UPDATES:
210 log_error("Switch --disable-updates not supported.");
211 return -EINVAL;
212 case ARG_UPDATES_ARE_ENABLED:
213 log_error("Switch --updates-are-enabled not supported.");
214 return -EINVAL;
215
216 case '?':
217 return -EINVAL;
218
219 default:
220 assert_not_reached("Unhandled option");
221 }
222
223 if (arg_mode == _MODE_INVALID) {
224 log_error("Expected either -a or -d on the command line.");
225 return -EINVAL;
226 }
227
228 if (optind+1 != argc) {
229 log_error("Expected interface name as argument.");
230 return -EINVAL;
231 }
232
233 dot = strchr(argv[optind], '.');
234 if (dot) {
235 iface = strndupa(argv[optind], dot - argv[optind]);
236 log_debug("Ignoring protocol specifier '%s'.", dot + 1);
237 } else
238 iface = argv[optind];
239 optind++;
240
241 if (parse_ifindex(iface, &arg_ifindex) < 0) {
242 int ifi;
243
244 ifi = if_nametoindex(iface);
245 if (ifi <= 0) {
246 if (errno == ENODEV && arg_ifindex_permissive) {
247 log_debug("Interface '%s' not found, but -f specified, ignoring.", iface);
248 return 0; /* done */
249 }
250
251 return log_error_errno(errno, "Unknown interface '%s': %m", iface);
252 }
253
254 arg_ifindex = ifi;
255 }
256
257 if (arg_mode == MODE_SET_LINK) {
258 unsigned n = 0;
259
260 for (;;) {
261 _cleanup_free_ char *line = NULL;
262 const char *a, *l;
263
264 r = read_line(stdin, LONG_LINE_MAX, &line);
265 if (r < 0)
266 return log_error_errno(r, "Failed to read from stdin: %m");
267 if (r == 0)
268 break;
269
270 n++;
271
272 l = strstrip(line);
273 if (IN_SET(*l, '#', ';', 0))
274 continue;
275
276 a = first_word(l, "nameserver");
277 if (a) {
278 (void) parse_nameserver(a);
279 continue;
280 }
281
282 a = first_word(l, "domain");
283 if (!a)
284 a = first_word(l, "search");
285 if (a) {
286 (void) parse_search_domain(a);
287 continue;
288 }
289
290 log_syntax(NULL, LOG_DEBUG, "stdin", n, 0, "Ignoring resolv.conf line: %s", l);
291 }
292
293 if (type == TYPE_EXCLUSIVE) {
294
295 /* If -x mode is selected, let's preferably route non-suffixed lookups to this interface. This
296 * somewhat matches the original -x behaviour */
297
298 r = strv_extend(&arg_set_domain, "~.");
299 if (r < 0)
300 return log_oom();
301
302 } else if (type == TYPE_PRIVATE)
303 log_debug("Private DNS server data not supported, ignoring.");
304
305 if (arg_n_set_dns == 0) {
306 log_error("No DNS servers specified, refusing operation.");
307 return -EINVAL;
308 }
309 }
310
311 return 1; /* work to do */
312}