diff options
Diffstat (limited to '')
-rw-r--r-- | src/resolve/resolvconf-compat.c | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/src/resolve/resolvconf-compat.c b/src/resolve/resolvconf-compat.c new file mode 100644 index 0000000..bef95c0 --- /dev/null +++ b/src/resolve/resolvconf-compat.c @@ -0,0 +1,277 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <getopt.h> +#include <net/if.h> + +#include "alloc-util.h" +#include "build.h" +#include "constants.h" +#include "dns-domain.h" +#include "extract-word.h" +#include "fileio.h" +#include "parse-util.h" +#include "pretty-print.h" +#include "resolvconf-compat.h" +#include "resolvectl.h" +#include "resolved-def.h" +#include "string-util.h" +#include "strv.h" +#include "terminal-util.h" + +static int resolvconf_help(void) { + _cleanup_free_ char *link = NULL; + int r; + + r = terminal_urlify_man("resolvectl", "1", &link); + if (r < 0) + return log_oom(); + + printf("%1$s -a INTERFACE < FILE\n" + "%1$s -d INTERFACE\n" + "\n" + "Register DNS server and domain configuration with systemd-resolved.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " -a Register per-interface DNS server and domain data\n" + " -d Unregister per-interface DNS server and domain data\n" + " -f Ignore if specified interface does not exist\n" + " -x Send DNS traffic preferably over this interface\n" + "\n" + "This is a compatibility alias for the resolvectl(1) tool, providing native\n" + "command line compatibility with the resolvconf(8) tool of various Linux\n" + "distributions and BSD systems. Some options supported by other implementations\n" + "are not supported and are ignored: -m, -p, -u. Various options supported by other\n" + "implementations are not supported and will cause the invocation to fail:\n" + "-I, -i, -l, -R, -r, -v, -V, --enable-updates, --disable-updates,\n" + "--updates-are-enabled.\n" + "\nSee the %2$s for details.\n", + program_invocation_short_name, + link); + + return 0; +} + +static int parse_nameserver(const char *string) { + int r; + + assert(string); + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&string, &word, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; + + if (strv_push(&arg_set_dns, word) < 0) + return log_oom(); + + word = NULL; + } + + return 0; +} + +static int parse_search_domain(const char *string) { + int r; + + assert(string); + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&string, &word, NULL, EXTRACT_UNQUOTE); + if (r < 0) + return r; + if (r == 0) + break; + + if (strv_push(&arg_set_domain, word) < 0) + return log_oom(); + + word = NULL; + } + + return 0; +} + +int resolvconf_parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_ENABLE_UPDATES, + ARG_DISABLE_UPDATES, + ARG_UPDATES_ARE_ENABLED, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + + /* The following are specific to Debian's original resolvconf */ + { "enable-updates", no_argument, NULL, ARG_ENABLE_UPDATES }, + { "disable-updates", no_argument, NULL, ARG_DISABLE_UPDATES }, + { "updates-are-enabled", no_argument, NULL, ARG_UPDATES_ARE_ENABLED }, + {} + }; + + enum { + TYPE_REGULAR, + TYPE_PRIVATE, /* -p: Not supported, treated identically to TYPE_REGULAR */ + TYPE_EXCLUSIVE, /* -x */ + } type = TYPE_REGULAR; + + int c, r; + + assert(argc >= 0); + assert(argv); + + /* openresolv checks these environment variables */ + if (getenv("IF_EXCLUSIVE")) + type = TYPE_EXCLUSIVE; + if (getenv("IF_PRIVATE")) + type = TYPE_PRIVATE; /* not actually supported */ + + arg_mode = _MODE_INVALID; + + while ((c = getopt_long(argc, argv, "hadxpfm:uIi:l:Rr:vV", options, NULL)) >= 0) + switch (c) { + + case 'h': + return resolvconf_help(); + + case ARG_VERSION: + return version(); + + /* -a and -d is what everybody can agree on */ + case 'a': + arg_mode = MODE_SET_LINK; + break; + + case 'd': + arg_mode = MODE_REVERT_LINK; + break; + + /* The exclusive/private/force stuff is an openresolv invention, we support in some skewed way */ + case 'x': + type = TYPE_EXCLUSIVE; + break; + + case 'p': + type = TYPE_PRIVATE; /* not actually supported */ + break; + + case 'f': + arg_ifindex_permissive = true; + break; + + /* The metrics stuff is an openresolv invention we ignore (and don't really need) */ + case 'm': + log_debug("Switch -%c ignored.", c); + break; + + /* -u supposedly should "update all subscribers". We have no subscribers, hence let's make + this a NOP, and exit immediately, cleanly. */ + case 'u': + log_info("Switch -%c ignored.", c); + return 0; + + /* The following options are openresolv inventions we don't support. */ + case 'I': + case 'i': + case 'l': + case 'R': + case 'r': + case 'v': + case 'V': + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Switch -%c not supported.", c); + + /* The Debian resolvconf commands we don't support. */ + case ARG_ENABLE_UPDATES: + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Switch --enable-updates not supported."); + case ARG_DISABLE_UPDATES: + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Switch --disable-updates not supported."); + case ARG_UPDATES_ARE_ENABLED: + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Switch --updates-are-enabled not supported."); + + case '?': + return -EINVAL; + + default: + assert_not_reached(); + } + + if (arg_mode == _MODE_INVALID) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Expected either -a or -d on the command line."); + + if (optind+1 != argc) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Expected interface name as argument."); + + r = ifname_resolvconf_mangle(argv[optind]); + if (r <= 0) + return r; + + optind++; + + if (arg_mode == MODE_SET_LINK) { + unsigned n = 0; + + for (;;) { + _cleanup_free_ char *line = NULL; + const char *a; + + r = read_stripped_line(stdin, LONG_LINE_MAX, &line); + if (r < 0) + return log_error_errno(r, "Failed to read from stdin: %m"); + if (r == 0) + break; + + n++; + + if (IN_SET(*line, '#', ';', 0)) + continue; + + a = first_word(line, "nameserver"); + if (a) { + (void) parse_nameserver(a); + continue; + } + + a = first_word(line, "domain"); + if (!a) + a = first_word(line, "search"); + if (a) { + (void) parse_search_domain(a); + continue; + } + + log_syntax(NULL, LOG_DEBUG, "stdin", n, 0, "Ignoring resolv.conf line: %s", line); + } + + if (type == TYPE_EXCLUSIVE) { + + /* If -x mode is selected, let's preferably route non-suffixed lookups to this interface. This + * somewhat matches the original -x behaviour */ + + r = strv_extend(&arg_set_domain, "~."); + if (r < 0) + return log_oom(); + + } else if (type == TYPE_PRIVATE) + log_debug("Private DNS server data not supported, ignoring."); + + if (!arg_set_dns) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "No DNS servers specified, refusing operation."); + } + + return 1; /* work to do */ +} |