File: | build-scan/../src/resolve/resolvconf-compat.c |
Warning: | line 230, column 13 Potential leak of memory pointed to by 'iface' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 "resolvectl.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 resolvectl(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)do { if ((__builtin_expect(!!(!(string)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("string"), "../src/resolve/resolvconf-compat.c" , 43, __PRETTY_FUNCTION__); } while (0); | |||
44 | ||||
45 | for (;;) { | |||
46 | _cleanup_free___attribute__((cleanup(freep))) char *word = NULL((void*)0); | |||
47 | ||||
48 | r = extract_first_word(&string, &word, NULL((void*)0), 0); | |||
49 | if (r < 0) | |||
50 | return r; | |||
51 | if (r == 0) | |||
52 | break; | |||
53 | ||||
54 | if (strv_push(&arg_set_dns, word) < 0) | |||
55 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/resolve/resolvconf-compat.c" , 55, __func__); | |||
56 | ||||
57 | word = NULL((void*)0); | |||
58 | } | |||
59 | ||||
60 | return 0; | |||
61 | } | |||
62 | ||||
63 | static int parse_search_domain(const char *string) { | |||
64 | int r; | |||
65 | ||||
66 | assert(string)do { if ((__builtin_expect(!!(!(string)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("string"), "../src/resolve/resolvconf-compat.c" , 66, __PRETTY_FUNCTION__); } while (0); | |||
67 | ||||
68 | for (;;) { | |||
69 | _cleanup_free___attribute__((cleanup(freep))) char *word = NULL((void*)0); | |||
70 | ||||
71 | r = extract_first_word(&string, &word, NULL((void*)0), EXTRACT_QUOTES); | |||
72 | if (r < 0) | |||
73 | return r; | |||
74 | if (r == 0) | |||
75 | break; | |||
76 | ||||
77 | if (strv_push(&arg_set_domain, word) < 0) | |||
78 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/resolve/resolvconf-compat.c" , 78, __func__); | |||
79 | ||||
80 | word = NULL((void*)0); | |||
81 | } | |||
82 | ||||
83 | return 0; | |||
84 | } | |||
85 | ||||
86 | int resolvconf_parse_argv(int argc, char *argv[]) { | |||
87 | ||||
88 | enum { | |||
89 | ARG_VERSION = 0x100, | |||
90 | ARG_ENABLE_UPDATES, | |||
91 | ARG_DISABLE_UPDATES, | |||
92 | ARG_UPDATES_ARE_ENABLED, | |||
93 | }; | |||
94 | ||||
95 | static const struct option options[] = { | |||
96 | { "help", no_argument0, NULL((void*)0), 'h' }, | |||
97 | { "version", no_argument0, NULL((void*)0), ARG_VERSION }, | |||
98 | ||||
99 | /* The following are specific to Debian's original resolvconf */ | |||
100 | { "enable-updates", no_argument0, NULL((void*)0), ARG_ENABLE_UPDATES }, | |||
101 | { "disable-updates", no_argument0, NULL((void*)0), ARG_DISABLE_UPDATES }, | |||
102 | { "updates-are-enabled", no_argument0, NULL((void*)0), ARG_UPDATES_ARE_ENABLED }, | |||
103 | {} | |||
104 | }; | |||
105 | ||||
106 | enum { | |||
107 | TYPE_REGULAR, | |||
108 | TYPE_PRIVATE, /* -p: Not supported, treated identically to TYPE_REGULAR */ | |||
109 | TYPE_EXCLUSIVE, /* -x */ | |||
110 | } type = TYPE_REGULAR; | |||
111 | ||||
112 | const char *dot, *iface; | |||
113 | int c, r; | |||
114 | ||||
115 | assert(argc >= 0)do { if ((__builtin_expect(!!(!(argc >= 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("argc >= 0"), "../src/resolve/resolvconf-compat.c" , 115, __PRETTY_FUNCTION__); } while (0); | |||
| ||||
116 | assert(argv)do { if ((__builtin_expect(!!(!(argv)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("argv"), "../src/resolve/resolvconf-compat.c" , 116, __PRETTY_FUNCTION__); } while (0); | |||
117 | ||||
118 | /* openresolv checks these environment variables */ | |||
119 | if (getenv("IF_EXCLUSIVE")) | |||
120 | type = TYPE_EXCLUSIVE; | |||
121 | if (getenv("IF_PRIVATE")) | |||
122 | type = TYPE_PRIVATE; /* not actually supported */ | |||
123 | ||||
124 | arg_mode = _MODE_INVALID; | |||
125 | ||||
126 | while ((c = getopt_long(argc, argv, "hadxpfm:uIi:l:Rr:vV", options, NULL((void*)0))) >= 0) | |||
127 | switch(c) { | |||
128 | ||||
129 | case 'h': | |||
130 | resolvconf_help(); | |||
131 | return 0; /* done */; | |||
132 | ||||
133 | case ARG_VERSION: | |||
134 | return version(); | |||
135 | ||||
136 | /* -a and -d is what everybody can agree on */ | |||
137 | case 'a': | |||
138 | arg_mode = MODE_SET_LINK; | |||
139 | break; | |||
140 | ||||
141 | case 'd': | |||
142 | arg_mode = MODE_REVERT_LINK; | |||
143 | break; | |||
144 | ||||
145 | /* The exclusive/private/force stuff is an openresolv invention, we support in some skewed way */ | |||
146 | case 'x': | |||
147 | type = TYPE_EXCLUSIVE; | |||
148 | break; | |||
149 | ||||
150 | case 'p': | |||
151 | type = TYPE_PRIVATE; /* not actually supported */ | |||
152 | break; | |||
153 | ||||
154 | case 'f': | |||
155 | arg_ifindex_permissive = true1; | |||
156 | break; | |||
157 | ||||
158 | /* The metrics stuff is an openresolv invention we ignore (and don't really need) */ | |||
159 | case 'm': | |||
160 | log_debug("Switch -%c ignored.", c)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/resolve/resolvconf-compat.c", 160, __func__, "Switch -%c ignored." , c) : -abs(_e); }); | |||
161 | break; | |||
162 | ||||
163 | /* Everybody else can agree on the existance of -u but we don't support it. */ | |||
164 | case 'u': | |||
165 | ||||
166 | /* The following options are openresolv inventions we don't support. */ | |||
167 | case 'I': | |||
168 | case 'i': | |||
169 | case 'l': | |||
170 | case 'R': | |||
171 | case 'r': | |||
172 | case 'v': | |||
173 | case 'V': | |||
174 | log_error("Switch -%c not supported.", c)({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/resolve/resolvconf-compat.c", 174, __func__, "Switch -%c not supported." , c) : -abs(_e); }); | |||
175 | return -EINVAL22; | |||
176 | ||||
177 | /* The Debian resolvconf commands we don't support. */ | |||
178 | case ARG_ENABLE_UPDATES: | |||
179 | log_error("Switch --enable-updates not supported.")({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/resolve/resolvconf-compat.c", 179, __func__, "Switch --enable-updates not supported." ) : -abs(_e); }); | |||
180 | return -EINVAL22; | |||
181 | case ARG_DISABLE_UPDATES: | |||
182 | log_error("Switch --disable-updates not supported.")({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/resolve/resolvconf-compat.c", 182, __func__, "Switch --disable-updates not supported." ) : -abs(_e); }); | |||
183 | return -EINVAL22; | |||
184 | case ARG_UPDATES_ARE_ENABLED: | |||
185 | log_error("Switch --updates-are-enabled not supported.")({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/resolve/resolvconf-compat.c", 185, __func__, "Switch --updates-are-enabled not supported." ) : -abs(_e); }); | |||
186 | return -EINVAL22; | |||
187 | ||||
188 | case '?': | |||
189 | return -EINVAL22; | |||
190 | ||||
191 | default: | |||
192 | assert_not_reached("Unhandled option")do { log_assert_failed_unreachable_realm(LOG_REALM_SYSTEMD, ( "Unhandled option"), "../src/resolve/resolvconf-compat.c", 192 , __PRETTY_FUNCTION__); } while (0); | |||
193 | } | |||
194 | ||||
195 | if (arg_mode
| |||
196 | log_error("Expected either -a or -d on the command line.")({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/resolve/resolvconf-compat.c", 196, __func__, "Expected either -a or -d on the command line." ) : -abs(_e); }); | |||
197 | return -EINVAL22; | |||
198 | } | |||
199 | ||||
200 | if (optind+1 != argc) { | |||
201 | log_error("Expected interface name as argument.")({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/resolve/resolvconf-compat.c", 201, __func__, "Expected interface name as argument." ) : -abs(_e); }); | |||
202 | return -EINVAL22; | |||
203 | } | |||
204 | ||||
205 | dot = strchr(argv[optind], '.'); | |||
206 | if (dot) { | |||
207 | iface = strndup(argv[optind], dot - argv[optind]); | |||
208 | log_debug("Ignoring protocol specifier '%s'.", dot + 1)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/resolve/resolvconf-compat.c", 208, __func__, "Ignoring protocol specifier '%s'." , dot + 1) : -abs(_e); }); | |||
209 | } else | |||
210 | iface = argv[optind]; | |||
211 | optind++; | |||
212 | ||||
213 | if (parse_ifindex(iface, &arg_ifindex) < 0) { | |||
214 | int ifi; | |||
215 | ||||
216 | ifi = if_nametoindex(iface); | |||
217 | if (ifi <= 0) { | |||
218 | if (errno(*__errno_location ()) == ENODEV19 && arg_ifindex_permissive) { | |||
219 | log_debug("Interface '%s' not found, but -f specified, ignoring.", iface)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/resolve/resolvconf-compat.c", 219, __func__, "Interface '%s' not found, but -f specified, ignoring." , iface) : -abs(_e); }); | |||
220 | return 0; /* done */ | |||
221 | } | |||
222 | ||||
223 | return log_error_errno(errno, "Unknown interface '%s': %m", iface)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/resolve/resolvconf-compat.c", 223 , __func__, "Unknown interface '%s': %m", iface) : -abs(_e); } ); | |||
224 | } | |||
225 | ||||
226 | arg_ifindex = ifi; | |||
227 | arg_ifname = iface; | |||
228 | } | |||
229 | ||||
230 | if (arg_mode == MODE_SET_LINK) { | |||
| ||||
231 | unsigned n = 0; | |||
232 | ||||
233 | for (;;) { | |||
234 | _cleanup_free___attribute__((cleanup(freep))) char *line = NULL((void*)0); | |||
235 | const char *a, *l; | |||
236 | ||||
237 | r = read_line(stdinstdin, LONG_LINE_MAX(1U*1024U*1024U), &line); | |||
238 | if (r < 0) | |||
239 | return log_error_errno(r, "Failed to read from stdin: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/resolve/resolvconf-compat.c", 239, __func__, "Failed to read from stdin: %m" ) : -abs(_e); }); | |||
240 | if (r == 0) | |||
241 | break; | |||
242 | ||||
243 | n++; | |||
244 | ||||
245 | l = strstrip(line); | |||
246 | if (IN_SET(*l, '#', ';', 0)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){'#', ';', 0})/sizeof(int)]; switch(*l) { case '#': case ';': case 0: _found = 1; break; default: break ; } _found; })) | |||
247 | continue; | |||
248 | ||||
249 | a = first_word(l, "nameserver"); | |||
250 | if (a) { | |||
251 | (void) parse_nameserver(a); | |||
252 | continue; | |||
253 | } | |||
254 | ||||
255 | a = first_word(l, "domain"); | |||
256 | if (!a) | |||
257 | a = first_word(l, "search"); | |||
258 | if (a) { | |||
259 | (void) parse_search_domain(a); | |||
260 | continue; | |||
261 | } | |||
262 | ||||
263 | log_syntax(NULL, LOG_DEBUG, "stdin", n, 0, "Ignoring resolv.conf line: %s", l)({ int _level = (7), _e = (0); (log_get_max_level_realm(LOG_REALM_SYSTEMD ) >= ((_level) & 0x07)) ? log_syntax_internal(((void*) 0), _level, "stdin", n, _e, "../src/resolve/resolvconf-compat.c" , 263, __func__, "Ignoring resolv.conf line: %s", l) : -abs(_e ); }); | |||
264 | } | |||
265 | ||||
266 | if (type == TYPE_EXCLUSIVE) { | |||
267 | ||||
268 | /* If -x mode is selected, let's preferably route non-suffixed lookups to this interface. This | |||
269 | * somewhat matches the original -x behaviour */ | |||
270 | ||||
271 | r = strv_extend(&arg_set_domain, "~."); | |||
272 | if (r < 0) | |||
273 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/resolve/resolvconf-compat.c" , 273, __func__); | |||
274 | ||||
275 | } else if (type == TYPE_PRIVATE) | |||
276 | log_debug("Private DNS server data not supported, ignoring.")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/resolve/resolvconf-compat.c", 276, __func__, "Private DNS server data not supported, ignoring." ) : -abs(_e); }); | |||
277 | ||||
278 | if (!arg_set_dns) { | |||
279 | log_error("No DNS servers specified, refusing operation.")({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/resolve/resolvconf-compat.c", 279, __func__, "No DNS servers specified, refusing operation." ) : -abs(_e); }); | |||
280 | return -EINVAL22; | |||
281 | } | |||
282 | } | |||
283 | ||||
284 | return 1; /* work to do */ | |||
285 | } |