]>
Commit | Line | Data |
---|---|---|
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" | |
c2e84cab | 13 | #include "resolvectl.h" |
088c1363 LP |
14 | #include "resolved-def.h" |
15 | #include "string-util.h" | |
16 | #include "strv.h" | |
37ec0fdd LP |
17 | #include "terminal-util.h" |
18 | ||
19 | static int resolvconf_help(void) { | |
20 | _cleanup_free_ char *link = NULL; | |
21 | int r; | |
22 | ||
23 | r = terminal_urlify_man("resolvectl", "1", &link); | |
24 | if (r < 0) | |
25 | return log_oom(); | |
088c1363 | 26 | |
088c1363 LP |
27 | printf("%1$s -a INTERFACE < FILE\n" |
28 | "%1$s -d INTERFACE\n" | |
29 | "\n" | |
30 | "Register DNS server and domain configuration with systemd-resolved.\n\n" | |
31 | " -h --help Show this help\n" | |
32 | " --version Show package version\n" | |
33 | " -a Register per-interface DNS server and domain data\n" | |
34 | " -d Unregister per-interface DNS server and domain data\n" | |
35 | " -f Ignore if specified interface does not exist\n" | |
36 | " -x Send DNS traffic preferably over this interface\n" | |
37 | "\n" | |
5bc53fee | 38 | "This is a compatibility alias for the resolvectl(1) tool, providing native\n" |
088c1363 LP |
39 | "command line compatibility with the resolvconf(8) tool of various Linux\n" |
40 | "distributions and BSD systems. Some options supported by other implementations\n" | |
41 | "are not supported and are ignored: -m, -p. Various options supported by other\n" | |
42 | "implementations are not supported and will cause the invocation to fail: -u,\n" | |
43 | "-I, -i, -l, -R, -r, -v, -V, --enable-updates, --disable-updates,\n" | |
44 | "--updates-are-enabled.\n" | |
37ec0fdd LP |
45 | "\nSee the %2$s for details.\n" |
46 | , program_invocation_short_name | |
47 | , link | |
48 | ); | |
49 | ||
50 | return 0; | |
088c1363 LP |
51 | } |
52 | ||
53 | static int parse_nameserver(const char *string) { | |
54 | int r; | |
55 | ||
56 | assert(string); | |
57 | ||
58 | for (;;) { | |
59 | _cleanup_free_ char *word = NULL; | |
088c1363 LP |
60 | |
61 | r = extract_first_word(&string, &word, NULL, 0); | |
62 | if (r < 0) | |
63 | return r; | |
64 | if (r == 0) | |
65 | break; | |
66 | ||
a7a4c60a | 67 | if (strv_push(&arg_set_dns, word) < 0) |
088c1363 | 68 | return log_oom(); |
5a01b3f3 FB |
69 | |
70 | word = NULL; | |
088c1363 LP |
71 | } |
72 | ||
73 | return 0; | |
74 | } | |
75 | ||
76 | static int parse_search_domain(const char *string) { | |
77 | int r; | |
78 | ||
79 | assert(string); | |
80 | ||
81 | for (;;) { | |
82 | _cleanup_free_ char *word = NULL; | |
83 | ||
84 | r = extract_first_word(&string, &word, NULL, EXTRACT_QUOTES); | |
85 | if (r < 0) | |
86 | return r; | |
87 | if (r == 0) | |
88 | break; | |
89 | ||
088c1363 LP |
90 | if (strv_push(&arg_set_domain, word) < 0) |
91 | return log_oom(); | |
92 | ||
93 | word = NULL; | |
94 | } | |
95 | ||
96 | return 0; | |
97 | } | |
98 | ||
99 | int resolvconf_parse_argv(int argc, char *argv[]) { | |
100 | ||
101 | enum { | |
102 | ARG_VERSION = 0x100, | |
103 | ARG_ENABLE_UPDATES, | |
104 | ARG_DISABLE_UPDATES, | |
105 | ARG_UPDATES_ARE_ENABLED, | |
106 | }; | |
107 | ||
108 | static const struct option options[] = { | |
109 | { "help", no_argument, NULL, 'h' }, | |
110 | { "version", no_argument, NULL, ARG_VERSION }, | |
111 | ||
112 | /* The following are specific to Debian's original resolvconf */ | |
113 | { "enable-updates", no_argument, NULL, ARG_ENABLE_UPDATES }, | |
114 | { "disable-updates", no_argument, NULL, ARG_DISABLE_UPDATES }, | |
115 | { "updates-are-enabled", no_argument, NULL, ARG_UPDATES_ARE_ENABLED }, | |
116 | {} | |
117 | }; | |
118 | ||
088c1363 LP |
119 | enum { |
120 | TYPE_REGULAR, | |
121 | TYPE_PRIVATE, /* -p: Not supported, treated identically to TYPE_REGULAR */ | |
122 | TYPE_EXCLUSIVE, /* -x */ | |
123 | } type = TYPE_REGULAR; | |
124 | ||
088c1363 LP |
125 | int c, r; |
126 | ||
127 | assert(argc >= 0); | |
128 | assert(argv); | |
129 | ||
130 | /* openresolv checks these environment variables */ | |
131 | if (getenv("IF_EXCLUSIVE")) | |
132 | type = TYPE_EXCLUSIVE; | |
133 | if (getenv("IF_PRIVATE")) | |
134 | type = TYPE_PRIVATE; /* not actually supported */ | |
135 | ||
136 | arg_mode = _MODE_INVALID; | |
137 | ||
138 | while ((c = getopt_long(argc, argv, "hadxpfm:uIi:l:Rr:vV", options, NULL)) >= 0) | |
139 | switch(c) { | |
140 | ||
141 | case 'h': | |
37ec0fdd | 142 | return resolvconf_help(); |
088c1363 LP |
143 | |
144 | case ARG_VERSION: | |
145 | return version(); | |
146 | ||
147 | /* -a and -d is what everybody can agree on */ | |
148 | case 'a': | |
149 | arg_mode = MODE_SET_LINK; | |
150 | break; | |
151 | ||
152 | case 'd': | |
153 | arg_mode = MODE_REVERT_LINK; | |
154 | break; | |
155 | ||
156 | /* The exclusive/private/force stuff is an openresolv invention, we support in some skewed way */ | |
157 | case 'x': | |
158 | type = TYPE_EXCLUSIVE; | |
159 | break; | |
160 | ||
161 | case 'p': | |
162 | type = TYPE_PRIVATE; /* not actually supported */ | |
163 | break; | |
164 | ||
165 | case 'f': | |
166 | arg_ifindex_permissive = true; | |
167 | break; | |
168 | ||
169 | /* The metrics stuff is an openresolv invention we ignore (and don't really need) */ | |
170 | case 'm': | |
171 | log_debug("Switch -%c ignored.", c); | |
172 | break; | |
173 | ||
174 | /* Everybody else can agree on the existance of -u but we don't support it. */ | |
175 | case 'u': | |
176 | ||
177 | /* The following options are openresolv inventions we don't support. */ | |
178 | case 'I': | |
179 | case 'i': | |
180 | case 'l': | |
181 | case 'R': | |
182 | case 'r': | |
183 | case 'v': | |
184 | case 'V': | |
185 | log_error("Switch -%c not supported.", c); | |
186 | return -EINVAL; | |
187 | ||
188 | /* The Debian resolvconf commands we don't support. */ | |
189 | case ARG_ENABLE_UPDATES: | |
190 | log_error("Switch --enable-updates not supported."); | |
191 | return -EINVAL; | |
192 | case ARG_DISABLE_UPDATES: | |
193 | log_error("Switch --disable-updates not supported."); | |
194 | return -EINVAL; | |
195 | case ARG_UPDATES_ARE_ENABLED: | |
196 | log_error("Switch --updates-are-enabled not supported."); | |
197 | return -EINVAL; | |
198 | ||
199 | case '?': | |
200 | return -EINVAL; | |
201 | ||
202 | default: | |
203 | assert_not_reached("Unhandled option"); | |
204 | } | |
205 | ||
206 | if (arg_mode == _MODE_INVALID) { | |
207 | log_error("Expected either -a or -d on the command line."); | |
208 | return -EINVAL; | |
209 | } | |
210 | ||
211 | if (optind+1 != argc) { | |
212 | log_error("Expected interface name as argument."); | |
213 | return -EINVAL; | |
214 | } | |
215 | ||
a661dc36 YW |
216 | r = ifname_mangle(argv[optind], false); |
217 | if (r <= 0) | |
218 | return r; | |
088c1363 | 219 | |
a661dc36 | 220 | optind++; |
088c1363 LP |
221 | |
222 | if (arg_mode == MODE_SET_LINK) { | |
223 | unsigned n = 0; | |
224 | ||
225 | for (;;) { | |
226 | _cleanup_free_ char *line = NULL; | |
227 | const char *a, *l; | |
228 | ||
229 | r = read_line(stdin, LONG_LINE_MAX, &line); | |
230 | if (r < 0) | |
231 | return log_error_errno(r, "Failed to read from stdin: %m"); | |
232 | if (r == 0) | |
233 | break; | |
234 | ||
235 | n++; | |
236 | ||
237 | l = strstrip(line); | |
238 | if (IN_SET(*l, '#', ';', 0)) | |
239 | continue; | |
240 | ||
241 | a = first_word(l, "nameserver"); | |
242 | if (a) { | |
243 | (void) parse_nameserver(a); | |
244 | continue; | |
245 | } | |
246 | ||
247 | a = first_word(l, "domain"); | |
248 | if (!a) | |
249 | a = first_word(l, "search"); | |
250 | if (a) { | |
251 | (void) parse_search_domain(a); | |
252 | continue; | |
253 | } | |
254 | ||
255 | log_syntax(NULL, LOG_DEBUG, "stdin", n, 0, "Ignoring resolv.conf line: %s", l); | |
256 | } | |
257 | ||
258 | if (type == TYPE_EXCLUSIVE) { | |
259 | ||
260 | /* If -x mode is selected, let's preferably route non-suffixed lookups to this interface. This | |
261 | * somewhat matches the original -x behaviour */ | |
262 | ||
263 | r = strv_extend(&arg_set_domain, "~."); | |
264 | if (r < 0) | |
265 | return log_oom(); | |
266 | ||
267 | } else if (type == TYPE_PRIVATE) | |
268 | log_debug("Private DNS server data not supported, ignoring."); | |
269 | ||
a7a4c60a | 270 | if (!arg_set_dns) { |
088c1363 LP |
271 | log_error("No DNS servers specified, refusing operation."); |
272 | return -EINVAL; | |
273 | } | |
274 | } | |
275 | ||
276 | return 1; /* work to do */ | |
277 | } |