2018-02-27 17:48:54 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
|
|
|
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <net/if.h>
|
|
|
|
|
|
|
|
#include "alloc-util.h"
|
|
|
|
#include "def.h"
|
|
|
|
#include "dns-domain.h"
|
|
|
|
#include "extract-word.h"
|
|
|
|
#include "fileio.h"
|
|
|
|
#include "parse-util.h"
|
|
|
|
#include "resolvconf-compat.h"
|
2018-04-18 20:24:23 +02:00
|
|
|
#include "resolvectl.h"
|
2018-02-27 17:48:54 +01:00
|
|
|
#include "resolved-def.h"
|
|
|
|
#include "string-util.h"
|
|
|
|
#include "strv.h"
|
2018-08-09 10:32:31 +02:00
|
|
|
#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();
|
2018-02-27 17:48:54 +01:00
|
|
|
|
|
|
|
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"
|
2018-05-01 09:39:36 +02:00
|
|
|
"This is a compatibility alias for the resolvectl(1) tool, providing native\n"
|
2018-02-27 17:48:54 +01:00
|
|
|
"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. Various options supported by other\n"
|
|
|
|
"implementations are not supported and will cause the invocation to fail: -u,\n"
|
|
|
|
"-I, -i, -l, -R, -r, -v, -V, --enable-updates, --disable-updates,\n"
|
|
|
|
"--updates-are-enabled.\n"
|
2018-08-09 10:32:31 +02:00
|
|
|
"\nSee the %2$s for details.\n"
|
|
|
|
, program_invocation_short_name
|
|
|
|
, link
|
|
|
|
);
|
|
|
|
|
|
|
|
return 0;
|
2018-02-27 17:48:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2018-04-11 03:44:51 +02:00
|
|
|
if (strv_push(&arg_set_dns, word) < 0)
|
2018-02-27 17:48:54 +01:00
|
|
|
return log_oom();
|
2018-06-26 03:07:48 +02:00
|
|
|
|
|
|
|
word = NULL;
|
2018-02-27 17:48:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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_QUOTES);
|
|
|
|
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':
|
2018-08-09 10:32:31 +02:00
|
|
|
return resolvconf_help();
|
2018-02-27 17:48:54 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
/* Everybody else can agree on the existance of -u but we don't support it. */
|
|
|
|
case 'u':
|
|
|
|
|
|
|
|
/* The following options are openresolv inventions we don't support. */
|
|
|
|
case 'I':
|
|
|
|
case 'i':
|
|
|
|
case 'l':
|
|
|
|
case 'R':
|
|
|
|
case 'r':
|
|
|
|
case 'v':
|
|
|
|
case 'V':
|
|
|
|
log_error("Switch -%c not supported.", c);
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* The Debian resolvconf commands we don't support. */
|
|
|
|
case ARG_ENABLE_UPDATES:
|
|
|
|
log_error("Switch --enable-updates not supported.");
|
|
|
|
return -EINVAL;
|
|
|
|
case ARG_DISABLE_UPDATES:
|
|
|
|
log_error("Switch --disable-updates not supported.");
|
|
|
|
return -EINVAL;
|
|
|
|
case ARG_UPDATES_ARE_ENABLED:
|
|
|
|
log_error("Switch --updates-are-enabled not supported.");
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
case '?':
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
default:
|
|
|
|
assert_not_reached("Unhandled option");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (arg_mode == _MODE_INVALID) {
|
|
|
|
log_error("Expected either -a or -d on the command line.");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (optind+1 != argc) {
|
|
|
|
log_error("Expected interface name as argument.");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-06-26 09:41:22 +02:00
|
|
|
r = ifname_mangle(argv[optind], false);
|
|
|
|
if (r <= 0)
|
|
|
|
return r;
|
2018-02-27 17:48:54 +01:00
|
|
|
|
2018-06-26 09:41:22 +02:00
|
|
|
optind++;
|
2018-02-27 17:48:54 +01:00
|
|
|
|
|
|
|
if (arg_mode == MODE_SET_LINK) {
|
|
|
|
unsigned n = 0;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
_cleanup_free_ char *line = NULL;
|
|
|
|
const char *a, *l;
|
|
|
|
|
|
|
|
r = read_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++;
|
|
|
|
|
|
|
|
l = strstrip(line);
|
|
|
|
if (IN_SET(*l, '#', ';', 0))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
a = first_word(l, "nameserver");
|
|
|
|
if (a) {
|
|
|
|
(void) parse_nameserver(a);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
a = first_word(l, "domain");
|
|
|
|
if (!a)
|
|
|
|
a = first_word(l, "search");
|
|
|
|
if (a) {
|
|
|
|
(void) parse_search_domain(a);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
log_syntax(NULL, LOG_DEBUG, "stdin", n, 0, "Ignoring resolv.conf line: %s", l);
|
|
|
|
}
|
|
|
|
|
|
|
|
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.");
|
|
|
|
|
2018-04-11 03:44:51 +02:00
|
|
|
if (!arg_set_dns) {
|
2018-02-27 17:48:54 +01:00
|
|
|
log_error("No DNS servers specified, refusing operation.");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1; /* work to do */
|
|
|
|
}
|