]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolvconf-compat.c
resolved: add missing error code check when initializing DNS-over-TLS
[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 "pretty-print.h"
13 #include "resolvconf-compat.h"
14 #include "resolvectl.h"
15 #include "resolved-def.h"
16 #include "string-util.h"
17 #include "strv.h"
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();
27
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"
39 "This is a compatibility alias for the resolvectl(1) tool, providing native\n"
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"
46 "\nSee the %2$s for details.\n"
47 , program_invocation_short_name
48 , link
49 );
50
51 return 0;
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;
61
62 r = extract_first_word(&string, &word, NULL, 0);
63 if (r < 0)
64 return r;
65 if (r == 0)
66 break;
67
68 if (strv_push(&arg_set_dns, word) < 0)
69 return log_oom();
70
71 word = NULL;
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
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
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
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':
143 return resolvconf_help();
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 existence 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':
186 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
187 "Switch -%c not supported.", c);
188
189 /* The Debian resolvconf commands we don't support. */
190 case ARG_ENABLE_UPDATES:
191 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
192 "Switch --enable-updates not supported.");
193 case ARG_DISABLE_UPDATES:
194 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
195 "Switch --disable-updates not supported.");
196 case ARG_UPDATES_ARE_ENABLED:
197 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
198 "Switch --updates-are-enabled not supported.");
199
200 case '?':
201 return -EINVAL;
202
203 default:
204 assert_not_reached("Unhandled option");
205 }
206
207 if (arg_mode == _MODE_INVALID)
208 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
209 "Expected either -a or -d on the command line.");
210
211 if (optind+1 != argc)
212 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
213 "Expected interface name as argument.");
214
215 r = ifname_mangle(argv[optind]);
216 if (r <= 0)
217 return r;
218
219 optind++;
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
269 if (!arg_set_dns)
270 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
271 "No DNS servers specified, refusing operation.");
272 }
273
274 return 1; /* work to do */
275 }