| 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 | } |