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