/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- This file is part of systemd.
-
- Copyright 2014 Zbigniew Jędrzejewski-Szmek
-***/
#include <getopt.h>
#include <net/if.h>
#include "escape.h"
#include "gcrypt-util.h"
#include "in-addr-util.h"
+#include "main-func.h"
#include "netlink-util.h"
#include "pager.h"
#include "parse-util.h"
+#include "pretty-print.h"
#include "resolvconf-compat.h"
#include "resolvectl.h"
#include "resolved-def.h"
#include "resolved-dns-packet.h"
+#include "string-table.h"
#include "strv.h"
#include "terminal-util.h"
#include "verbs.h"
static int arg_family = AF_UNSPEC;
-int arg_ifindex = 0;
-const char *arg_ifname = NULL;
+static int arg_ifindex = 0;
+static char *arg_ifname = NULL;
static uint16_t arg_type = 0;
static uint16_t arg_class = 0;
static bool arg_legend = true;
static uint64_t arg_flags = 0;
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
bool arg_ifindex_permissive = false; /* If true, don't generate an error if the specified interface index doesn't exist */
-
-typedef enum ServiceFamily {
- SERVICE_FAMILY_TCP,
- SERVICE_FAMILY_UDP,
- SERVICE_FAMILY_SCTP,
- _SERVICE_FAMILY_INVALID = -1,
-} ServiceFamily;
static const char *arg_service_family = NULL;
typedef enum RawType {
char **arg_set_domain = NULL;
static const char *arg_set_llmnr = NULL;
static const char *arg_set_mdns = NULL;
+static const char *arg_set_dns_over_tls = NULL;
static const char *arg_set_dnssec = NULL;
static char **arg_set_nta = NULL;
+STATIC_DESTRUCTOR_REGISTER(arg_ifname, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_set_dns, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_set_domain, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_set_nta, strv_freep);
+
typedef enum StatusMode {
STATUS_ALL,
STATUS_DNS,
STATUS_DOMAIN,
STATUS_LLMNR,
STATUS_MDNS,
+ STATUS_PRIVATE,
STATUS_DNSSEC,
STATUS_NTA,
} StatusMode;
-static int parse_ifindex_with_warn(const char *s) {
+static int parse_ifindex_and_warn(const char *s) {
int ifi;
assert(s);
if (parse_ifindex(s, &ifi) < 0) {
ifi = if_nametoindex(s);
if (ifi <= 0)
- return log_error_errno(errno, "Unknown interface %s: %m", s);
+ return log_error_errno(errno, "Unknown interface '%s': %m", s);
}
return ifi;
}
-static ServiceFamily service_family_from_string(const char *s) {
- if (!s || streq(s, "tcp"))
- return SERVICE_FAMILY_TCP;
- if (streq(s, "udp"))
- return SERVICE_FAMILY_UDP;
- if (streq(s, "sctp"))
- return SERVICE_FAMILY_SCTP;
- return _SERVICE_FAMILY_INVALID;
-}
+int ifname_mangle(const char *s, bool allow_loopback) {
+ _cleanup_free_ char *iface = NULL;
+ const char *dot;
+ int r;
+
+ assert(s);
+
+ if (arg_ifname) {
+ assert(arg_ifindex >= 0);
+
+ if (!allow_loopback && arg_ifindex == LOOPBACK_IFINDEX)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Interface can't be the loopback interface (lo). Sorry.");
-static const char* service_family_to_string(ServiceFamily service) {
- switch(service) {
- case SERVICE_FAMILY_TCP:
- return "_tcp";
- case SERVICE_FAMILY_UDP:
- return "_udp";
- case SERVICE_FAMILY_SCTP:
- return "_sctp";
- default:
- assert_not_reached("invalid service");
+ return 1;
}
+
+ dot = strchr(s, '.');
+ if (dot) {
+ iface = strndup(s, dot - s);
+ if (!iface)
+ return log_oom();
+
+ log_debug("Ignoring protocol specifier '%s'.", dot + 1);
+ } else {
+ iface = strdup(s);
+ if (!iface)
+ return log_oom();
+ }
+
+ if (parse_ifindex(iface, &r) < 0) {
+ r = if_nametoindex(iface);
+ if (r <= 0) {
+ if (errno == ENODEV && arg_ifindex_permissive) {
+ log_debug("Interface '%s' not found, but -f specified, ignoring.", iface);
+ return 0; /* done */
+ }
+
+ return log_error_errno(errno, "Unknown interface '%s': %m", iface);
+ }
+ }
+
+ if (!allow_loopback && r == LOOPBACK_IFINDEX)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Interface can't be the loopback interface (lo). Sorry.");
+
+ arg_ifindex = r;
+ arg_ifname = TAKE_PTR(iface);
+
+ return 1;
}
static void print_source(uint64_t flags, usec_t rtt) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
const char *canonical = NULL;
- char ifname[IF_NAMESIZE] = "";
unsigned c = 0;
- int r;
uint64_t flags;
usec_t ts;
+ int r;
assert(name);
- if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname))
- return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex);
-
- log_debug("Resolving %s (family %s, interface %s).", name, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
+ log_debug("Resolving %s (family %s, interface %s).", name, af_to_name(arg_family) ?: "*", isempty(arg_ifname) ? "*" : arg_ifname);
r = sd_bus_message_new_method_call(
bus,
while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
_cleanup_free_ char *pretty = NULL;
+ char ifname[IF_NAMESIZE] = "";
int ifindex, family;
const void *a;
size_t sz;
return -EINVAL;
}
- ifname[0] = 0;
if (ifindex > 0 && !if_indextoname(ifindex, ifname))
log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
ts = now(CLOCK_MONOTONIC);
r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
- if (r < 0) {
- log_error("%s: resolve call failed: %s", pretty, bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "%s: resolve call failed: %s", pretty, bus_error_message(&error, r));
ts = now(CLOCK_MONOTONIC) - ts;
static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_t type, bool warn_missing) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- char ifname[IF_NAMESIZE] = "";
unsigned n = 0;
uint64_t flags;
int r;
assert(name);
- if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname))
- return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex);
-
- log_debug("Resolving %s %s %s (interface %s).", name, dns_class_to_string(class), dns_type_to_string(type), isempty(ifname) ? "*" : ifname);
+ log_debug("Resolving %s %s %s (interface %s).", name, dns_class_to_string(class), dns_type_to_string(type), isempty(arg_ifname) ? "*" : arg_ifname);
r = sd_bus_message_new_method_call(
bus,
_cleanup_free_ char *t = NULL;
const char *e;
- if (class != 0) {
- log_error("DNS class specified twice.");
- return -EINVAL;
- }
+ if (class != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "DNS class specified twice.");
e = strchrnul(f, ';');
t = strndup(f, e - f);
return log_oom();
r = dns_class_from_string(t);
- if (r < 0) {
- log_error("Unknown DNS class %s.", t);
- return -EINVAL;
- }
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown DNS class %s.", t);
class = r;
_cleanup_free_ char *t = NULL;
const char *e;
- if (type != 0) {
- log_error("DNS type specified twice.");
- return -EINVAL;
- }
+ if (type != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "DNS type specified twice.");
e = strchrnul(f, ';');
t = strndup(f, e - f);
return log_oom();
r = dns_type_from_string(t);
- if (r < 0) {
- log_error("Unknown DNS type %s.", t);
- return -EINVAL;
- }
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown DNS type %s.", t);
type = r;
return resolve_record(bus, n, class, type, true);
invalid:
- log_error("Invalid DNS URI: %s", name);
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid DNS URI: %s", name);
}
static int verb_query(int argc, char **argv, void *userdata) {
const char *canonical_name, *canonical_type, *canonical_domain;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- char ifname[IF_NAMESIZE] = "";
size_t indent, sz;
uint64_t flags;
const char *p;
name = empty_to_null(name);
type = empty_to_null(type);
- if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname))
- return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex);
-
if (name)
- log_debug("Resolving service \"%s\" of type %s in %s (family %s, interface %s).", name, type, domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
+ log_debug("Resolving service \"%s\" of type %s in %s (family %s, interface %s).", name, type, domain, af_to_name(arg_family) ?: "*", isempty(arg_ifname) ? "*" : arg_ifname);
else if (type)
- log_debug("Resolving service type %s of %s (family %s, interface %s).", type, domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
+ log_debug("Resolving service type %s of %s (family %s, interface %s).", type, domain, af_to_name(arg_family) ?: "*", isempty(arg_ifname) ? "*" : arg_ifname);
else
- log_debug("Resolving service type %s (family %s, interface %s).", domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
+ log_debug("Resolving service type %s (family %s, interface %s).", domain, af_to_name(arg_family) ?: "*", isempty(arg_ifname) ? "*" : arg_ifname);
r = sd_bus_message_new_method_call(
bus,
while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
_cleanup_free_ char *pretty = NULL;
+ char ifname[IF_NAMESIZE] = "";
int ifindex, family;
const void *a;
return -EINVAL;
}
- ifname[0] = 0;
if (ifindex > 0 && !if_indextoname(ifindex, ifname))
log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
assert(address);
domain = strrchr(address, '@');
- if (!domain) {
- log_error("Address does not contain '@': \"%s\"", address);
- return -EINVAL;
- } else if (domain == address || domain[1] == '\0') {
- log_error("Address starts or ends with '@': \"%s\"", address);
- return -EINVAL;
- }
+ if (!domain)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Address does not contain '@': \"%s\"", address);
+ if (domain == address || domain[1] == '\0')
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Address starts or ends with '@': \"%s\"", address);
domain++;
r = string_hashsum_sha256(address, domain - 1 - address, &hashed);
arg_type ?: DNS_TYPE_OPENPGPKEY, false);
if (IN_SET(r, -ENXIO, -ESRCH)) { /* NXDOMAIN or NODATA? */
- hashed = NULL;
+ hashed = mfree(hashed);
r = string_hashsum_sha224(address, domain - 1 - address, &hashed);
if (r < 0)
return log_error_errno(r, "Hashing failed: %m");
return r;
}
-static int resolve_tlsa(sd_bus *bus, ServiceFamily family, const char *address) {
+static int resolve_tlsa(sd_bus *bus, const char *family, const char *address) {
const char *port;
uint16_t port_num = 443;
_cleanup_free_ char *full = NULL;
address = strndupa(address, port - address);
}
- r = asprintf(&full, "_%u.%s.%s",
+ r = asprintf(&full, "_%u._%s.%s",
port_num,
- service_family_to_string(family),
+ family,
address);
if (r < 0)
return log_oom();
arg_type ?: DNS_TYPE_TLSA, true);
}
+static bool service_family_is_valid(const char *s) {
+ return STR_IN_SET(s, "tcp", "udp", "sctp");
+}
+
static int verb_tlsa(int argc, char **argv, void *userdata) {
sd_bus *bus = userdata;
- ServiceFamily family;
char **p, **args = argv + 1;
+ const char *family = "tcp";
int q, r = 0;
- family = service_family_from_string(argv[1]);
- if (family < 0)
- family = SERVICE_FAMILY_TCP;
- else
+ if (service_family_is_valid(argv[1])) {
+ family = argv[1];
args++;
+ }
STRV_FOREACH(p, args) {
q = resolve_tlsa(bus, family, *p);
return 0;
}
+static int read_dns_server_one(sd_bus_message *m, bool with_ifindex, char **ret) {
+ _cleanup_free_ char *pretty = NULL;
+ int ifindex, family, r;
+ const void *a;
+ size_t sz;
+
+ assert(m);
+ assert(ret);
+
+ r = sd_bus_message_enter_container(m, 'r', with_ifindex ? "iiay" : "iay");
+ if (r <= 0)
+ return r;
+
+ if (with_ifindex) {
+ r = sd_bus_message_read(m, "i", &ifindex);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_read(m, "i", &family);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_array(m, 'y', &a, &sz);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ if (with_ifindex && ifindex != 0) {
+ /* only show the global ones here */
+ *ret = NULL;
+ return 1;
+ }
+
+ if (!IN_SET(family, AF_INET, AF_INET6)) {
+ log_debug("Unexpected family, ignoring: %i", family);
+
+ *ret = NULL;
+ return 1;
+ }
+
+ if (sz != FAMILY_ADDRESS_SIZE(family)) {
+ log_debug("Address size mismatch, ignoring.");
+
+ *ret = NULL;
+ return 1;
+ }
+
+ r = in_addr_to_string(family, a, &pretty);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(pretty);
+
+ return 1;
+}
+
static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
char ***l = userdata;
int r;
return r;
for (;;) {
- const void *a;
- char *pretty;
- int family;
- size_t sz;
+ char *pretty = NULL;
- r = sd_bus_message_enter_container(m, 'r', "iay");
+ r = read_dns_server_one(m, false, &pretty);
if (r < 0)
return r;
if (r == 0)
break;
- r = sd_bus_message_read(m, "i", &family);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read_array(m, 'y', &a, &sz);
- if (r < 0)
- return r;
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
-
- if (!IN_SET(family, AF_INET, AF_INET6)) {
- log_debug("Unexpected family, ignoring.");
+ if (isempty(pretty))
continue;
- }
-
- if (sz != FAMILY_ADDRESS_SIZE(family)) {
- log_debug("Address size mismatch, ignoring.");
- continue;
- }
-
- r = in_addr_to_string(family, a, &pretty);
- if (r < 0)
- return r;
r = strv_consume(l, pretty);
if (r < 0)
return 0;
}
+static int map_link_current_dns_server(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ assert(m);
+ assert(userdata);
+
+ return read_dns_server_one(m, false, userdata);
+}
+
+static int read_domain_one(sd_bus_message *m, bool with_ifindex, char **ret) {
+ _cleanup_free_ char *str = NULL;
+ int ifindex, route_only, r;
+ const char *domain;
+
+ assert(m);
+ assert(ret);
+
+ if (with_ifindex)
+ r = sd_bus_message_read(m, "(isb)", &ifindex, &domain, &route_only);
+ else
+ r = sd_bus_message_read(m, "(sb)", &domain, &route_only);
+ if (r <= 0)
+ return r;
+
+ if (with_ifindex && ifindex != 0) {
+ /* only show the global ones here */
+ *ret = NULL;
+ return 1;
+ }
+
+ if (route_only)
+ str = strappend("~", domain);
+ else
+ str = strdup(domain);
+ if (!str)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(str);
+
+ return 1;
+}
+
static int map_link_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
char ***l = userdata;
int r;
return r;
for (;;) {
- const char *domain;
- int route_only;
- char *pretty;
+ char *pretty = NULL;
- r = sd_bus_message_read(m, "(sb)", &domain, &route_only);
+ r = read_domain_one(m, false, &pretty);
if (r < 0)
return r;
if (r == 0)
break;
- if (route_only)
- pretty = strappend("~", domain);
- else
- pretty = strdup(domain);
- if (!pretty)
- return -ENOMEM;
+ if (isempty(pretty))
+ continue;
r = strv_consume(l, pretty);
if (r < 0)
return 0;
}
-static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode mode, bool *empty_line) {
-
- struct link_info {
- uint64_t scopes_mask;
- const char *llmnr;
- const char *mdns;
- const char *dnssec;
- char **dns;
- char **domains;
- char **ntas;
- bool dnssec_supported;
- } link_info = {};
+struct link_info {
+ uint64_t scopes_mask;
+ const char *llmnr;
+ const char *mdns;
+ const char *dns_over_tls;
+ const char *dnssec;
+ char *current_dns;
+ char **dns;
+ char **domains;
+ char **ntas;
+ bool dnssec_supported;
+};
+
+static void link_info_clear(struct link_info *p) {
+ free(p->current_dns);
+ strv_free(p->dns);
+ strv_free(p->domains);
+ strv_free(p->ntas);
+}
+static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode mode, bool *empty_line) {
static const struct bus_properties_map property_map[] = {
- { "ScopesMask", "t", NULL, offsetof(struct link_info, scopes_mask) },
- { "DNS", "a(iay)", map_link_dns_servers, offsetof(struct link_info, dns) },
- { "Domains", "a(sb)", map_link_domains, offsetof(struct link_info, domains) },
- { "LLMNR", "s", NULL, offsetof(struct link_info, llmnr) },
- { "MulticastDNS", "s", NULL, offsetof(struct link_info, mdns) },
- { "DNSSEC", "s", NULL, offsetof(struct link_info, dnssec) },
- { "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct link_info, ntas) },
- { "DNSSECSupported", "b", NULL, offsetof(struct link_info, dnssec_supported) },
+ { "ScopesMask", "t", NULL, offsetof(struct link_info, scopes_mask) },
+ { "DNS", "a(iay)", map_link_dns_servers, offsetof(struct link_info, dns) },
+ { "CurrentDNSServer", "(iay)", map_link_current_dns_server, offsetof(struct link_info, current_dns) },
+ { "Domains", "a(sb)", map_link_domains, offsetof(struct link_info, domains) },
+ { "LLMNR", "s", NULL, offsetof(struct link_info, llmnr) },
+ { "MulticastDNS", "s", NULL, offsetof(struct link_info, mdns) },
+ { "DNSOverTLS", "s", NULL, offsetof(struct link_info, dns_over_tls) },
+ { "DNSSEC", "s", NULL, offsetof(struct link_info, dnssec) },
+ { "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct link_info, ntas) },
+ { "DNSSECSupported", "b", NULL, offsetof(struct link_info, dnssec_supported) },
{}
};
-
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ _cleanup_(link_info_clear) struct link_info link_info = {};
_cleanup_free_ char *ifi = NULL, *p = NULL;
char ifname[IF_NAMESIZE] = "";
char **i;
&error,
&m,
&link_info);
- if (r < 0) {
- log_error_errno(r, "Failed to get link data for %i: %s", ifindex, bus_error_message(&error, r));
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get link data for %i: %s", ifindex, bus_error_message(&error, r));
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
- if (mode == STATUS_DNS) {
- r = status_print_strv_ifindex(ifindex, name, link_info.dns);
- goto finish;
- }
+ if (mode == STATUS_DNS)
+ return status_print_strv_ifindex(ifindex, name, link_info.dns);
- if (mode == STATUS_DOMAIN) {
- r = status_print_strv_ifindex(ifindex, name, link_info.domains);
- goto finish;
- }
+ if (mode == STATUS_DOMAIN)
+ return status_print_strv_ifindex(ifindex, name, link_info.domains);
- if (mode == STATUS_NTA) {
- r = status_print_strv_ifindex(ifindex, name, link_info.ntas);
- goto finish;
- }
+ if (mode == STATUS_NTA)
+ return status_print_strv_ifindex(ifindex, name, link_info.ntas);
if (mode == STATUS_LLMNR) {
printf("%sLink %i (%s)%s: %s\n",
ansi_highlight(), ifindex, name, ansi_normal(),
strna(link_info.llmnr));
- r = 0;
- goto finish;
+ return 0;
}
if (mode == STATUS_MDNS) {
ansi_highlight(), ifindex, name, ansi_normal(),
strna(link_info.mdns));
- r = 0;
- goto finish;
+ return 0;
+ }
+
+ if (mode == STATUS_PRIVATE) {
+ printf("%sLink %i (%s)%s: %s\n",
+ ansi_highlight(), ifindex, name, ansi_normal(),
+ strna(link_info.dns_over_tls));
+
+ return 0;
}
if (mode == STATUS_DNSSEC) {
ansi_highlight(), ifindex, name, ansi_normal(),
strna(link_info.dnssec));
- r = 0;
- goto finish;
+ return 0;
}
if (empty_line && *empty_line)
printf(" LLMNR setting: %s\n"
"MulticastDNS setting: %s\n"
+ " DNSOverTLS setting: %s\n"
" DNSSEC setting: %s\n"
" DNSSEC supported: %s\n",
strna(link_info.llmnr),
strna(link_info.mdns),
+ strna(link_info.dns_over_tls),
strna(link_info.dnssec),
yes_no(link_info.dnssec_supported));
+ if (link_info.current_dns)
+ printf(" Current DNS Server: %s\n", link_info.current_dns);
+
STRV_FOREACH(i, link_info.dns) {
printf(" %s %s\n",
i == link_info.dns ? "DNS Servers:" : " ",
if (empty_line)
*empty_line = true;
- r = 0;
-
-finish:
- strv_free(link_info.dns);
- strv_free(link_info.domains);
- strv_free(link_info.ntas);
- return r;
+ return 0;
}
static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
return r;
for (;;) {
- const void *a;
- char *pretty;
- int family, ifindex;
- size_t sz;
+ char *pretty = NULL;
- r = sd_bus_message_enter_container(m, 'r', "iiay");
+ r = read_dns_server_one(m, true, &pretty);
if (r < 0)
return r;
if (r == 0)
break;
- r = sd_bus_message_read(m, "ii", &ifindex, &family);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read_array(m, 'y', &a, &sz);
- if (r < 0)
- return r;
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
-
- if (ifindex != 0) /* only show the global ones here */
- continue;
-
- if (!IN_SET(family, AF_INET, AF_INET6)) {
- log_debug("Unexpected family, ignoring.");
+ if (isempty(pretty))
continue;
- }
-
- if (sz != FAMILY_ADDRESS_SIZE(family)) {
- log_debug("Address size mismatch, ignoring.");
- continue;
- }
-
- r = in_addr_to_string(family, a, &pretty);
- if (r < 0)
- return r;
r = strv_consume(l, pretty);
if (r < 0)
return 0;
}
+static int map_global_current_dns_server(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ assert(m);
+ assert(userdata);
+
+ return read_dns_server_one(m, true, userdata);
+}
+
static int map_global_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
char ***l = userdata;
int r;
return r;
for (;;) {
- const char *domain;
- int route_only, ifindex;
- char *pretty;
+ char *pretty = NULL;
- r = sd_bus_message_read(m, "(isb)", &ifindex, &domain, &route_only);
+ r = read_domain_one(m, true, &pretty);
if (r < 0)
return r;
if (r == 0)
break;
- if (ifindex != 0) /* only show the global ones here */
+ if (isempty(pretty))
continue;
- if (route_only)
- pretty = strappend("~", domain);
- else
- pretty = strdup(domain);
- if (!pretty)
- return -ENOMEM;
-
r = strv_consume(l, pretty);
if (r < 0)
return r;
return 0;
}
-static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
-
- struct global_info {
- char **dns;
- char **domains;
- char **ntas;
- const char *llmnr;
- const char *mdns;
- const char *dnssec;
- bool dnssec_supported;
- } global_info = {};
+struct global_info {
+ char *current_dns;
+ char **dns;
+ char **fallback_dns;
+ char **domains;
+ char **ntas;
+ const char *llmnr;
+ const char *mdns;
+ const char *dns_over_tls;
+ const char *dnssec;
+ bool dnssec_supported;
+};
+
+static void global_info_clear(struct global_info *p) {
+ free(p->current_dns);
+ strv_free(p->dns);
+ strv_free(p->fallback_dns);
+ strv_free(p->domains);
+ strv_free(p->ntas);
+}
+static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
static const struct bus_properties_map property_map[] = {
- { "DNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, dns) },
- { "Domains", "a(isb)", map_global_domains, offsetof(struct global_info, domains) },
- { "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct global_info, ntas) },
- { "LLMNR", "s", NULL, offsetof(struct global_info, llmnr) },
- { "MulticastDNS", "s", NULL, offsetof(struct global_info, mdns) },
- { "DNSSEC", "s", NULL, offsetof(struct global_info, dnssec) },
- { "DNSSECSupported", "b", NULL, offsetof(struct global_info, dnssec_supported) },
+ { "DNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, dns) },
+ { "FallbackDNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, fallback_dns) },
+ { "CurrentDNSServer", "(iiay)", map_global_current_dns_server, offsetof(struct global_info, current_dns) },
+ { "Domains", "a(isb)", map_global_domains, offsetof(struct global_info, domains) },
+ { "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct global_info, ntas) },
+ { "LLMNR", "s", NULL, offsetof(struct global_info, llmnr) },
+ { "MulticastDNS", "s", NULL, offsetof(struct global_info, mdns) },
+ { "DNSOverTLS", "s", NULL, offsetof(struct global_info, dns_over_tls) },
+ { "DNSSEC", "s", NULL, offsetof(struct global_info, dnssec) },
+ { "DNSSECSupported", "b", NULL, offsetof(struct global_info, dnssec_supported) },
{}
};
-
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ _cleanup_(global_info_clear) struct global_info global_info = {};
char **i;
int r;
&error,
&m,
&global_info);
- if (r < 0) {
- log_error_errno(r, "Failed to get global data: %s", bus_error_message(&error, r));
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get global data: %s", bus_error_message(&error, r));
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
- if (mode == STATUS_DNS) {
- r = status_print_strv_global(global_info.dns);
- goto finish;
- }
+ if (mode == STATUS_DNS)
+ return status_print_strv_global(global_info.dns);
- if (mode == STATUS_DOMAIN) {
- r = status_print_strv_global(global_info.domains);
- goto finish;
- }
+ if (mode == STATUS_DOMAIN)
+ return status_print_strv_global(global_info.domains);
- if (mode == STATUS_NTA) {
- r = status_print_strv_global(global_info.ntas);
- goto finish;
- }
+ if (mode == STATUS_NTA)
+ return status_print_strv_global(global_info.ntas);
if (mode == STATUS_LLMNR) {
printf("%sGlobal%s: %s\n", ansi_highlight(), ansi_normal(),
strna(global_info.llmnr));
- r = 0;
- goto finish;
+ return 0;
}
if (mode == STATUS_MDNS) {
printf("%sGlobal%s: %s\n", ansi_highlight(), ansi_normal(),
strna(global_info.mdns));
- r = 0;
- goto finish;
+ return 0;
+ }
+
+ if (mode == STATUS_PRIVATE) {
+ printf("%sGlobal%s: %s\n", ansi_highlight(), ansi_normal(),
+ strna(global_info.dns_over_tls));
+
+ return 0;
}
if (mode == STATUS_DNSSEC) {
printf("%sGlobal%s: %s\n", ansi_highlight(), ansi_normal(),
strna(global_info.dnssec));
- r = 0;
- goto finish;
+ return 0;
}
printf("%sGlobal%s\n", ansi_highlight(), ansi_normal());
printf(" LLMNR setting: %s\n"
"MulticastDNS setting: %s\n"
+ " DNSOverTLS setting: %s\n"
" DNSSEC setting: %s\n"
" DNSSEC supported: %s\n",
strna(global_info.llmnr),
strna(global_info.mdns),
+ strna(global_info.dns_over_tls),
strna(global_info.dnssec),
yes_no(global_info.dnssec_supported));
+ if (global_info.current_dns)
+ printf(" Current DNS Server: %s\n", global_info.current_dns);
+
STRV_FOREACH(i, global_info.dns) {
printf(" %s %s\n",
i == global_info.dns ? "DNS Servers:" : " ",
*i);
}
+ STRV_FOREACH(i, global_info.fallback_dns) {
+ printf("%s %s\n",
+ i == global_info.fallback_dns ? "Fallback DNS Servers:" : " ",
+ *i);
+ }
+
STRV_FOREACH(i, global_info.domains) {
printf(" %s %s\n",
i == global_info.domains ? "DNS Domain:" : " ",
*empty_line = true;
- r = 0;
-
-finish:
- strv_free(global_info.dns);
- strv_free(global_info.domains);
- strv_free(global_info.ntas);
-
- return r;
+ return 0;
}
static int status_all(sd_bus *bus, StatusMode mode) {
STRV_FOREACH(ifname, argv + 1) {
int ifindex;
- ifindex = parse_ifindex_with_warn(*ifname);
+ ifindex = parse_ifindex_and_warn(*ifname);
if (ifindex < 0)
continue;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
sd_bus *bus = userdata;
- int ifindex, r;
char **p;
+ int r;
assert(bus);
if (argc <= 1)
return status_all(bus, STATUS_DNS);
- ifindex = parse_ifindex_with_warn(argv[1]);
- if (ifindex < 0)
- return ifindex;
-
- if (ifindex == LOOPBACK_IFINDEX) {
- log_error("Interface can't be the loopback interface (lo). Sorry.");
- return -EINVAL;
- }
+ r = ifname_mangle(argv[1], false);
+ if (r < 0)
+ return r;
if (argc == 2)
- return status_ifindex(bus, ifindex, NULL, STATUS_DNS, NULL);
+ return status_ifindex(bus, arg_ifindex, NULL, STATUS_DNS, NULL);
r = sd_bus_message_new_method_call(
bus,
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append(req, "i", ifindex);
+ r = sd_bus_message_append(req, "i", arg_ifindex);
if (r < 0)
return bus_log_create_error(r);
if (r < 0)
return bus_log_create_error(r);
- STRV_FOREACH(p, argv + 2) {
- struct in_addr_data data;
+ /* If only argument is the empty string, then call SetLinkDNS() with an
+ * empty list, which will clear the list of domains for an interface. */
+ if (!strv_equal(argv + 2, STRV_MAKE(""))) {
+ STRV_FOREACH(p, argv + 2) {
+ struct in_addr_data data;
- r = in_addr_from_string_auto(*p, &data.family, &data.address);
- if (r < 0)
- return log_error_errno(r, "Failed to parse DNS server address: %s", *p);
+ r = in_addr_from_string_auto(*p, &data.family, &data.address);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse DNS server address: %s", *p);
- r = sd_bus_message_open_container(req, 'r', "iay");
- if (r < 0)
- return bus_log_create_error(r);
+ r = sd_bus_message_open_container(req, 'r', "iay");
+ if (r < 0)
+ return bus_log_create_error(r);
- r = sd_bus_message_append(req, "i", data.family);
- if (r < 0)
- return bus_log_create_error(r);
+ r = sd_bus_message_append(req, "i", data.family);
+ if (r < 0)
+ return bus_log_create_error(r);
- r = sd_bus_message_append_array(req, 'y', &data.address, FAMILY_ADDRESS_SIZE(data.family));
- if (r < 0)
- return bus_log_create_error(r);
+ r = sd_bus_message_append_array(req, 'y', &data.address, FAMILY_ADDRESS_SIZE(data.family));
+ if (r < 0)
+ return bus_log_create_error(r);
- r = sd_bus_message_close_container(req);
- if (r < 0)
- return bus_log_create_error(r);
+ r = sd_bus_message_close_container(req);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
}
r = sd_bus_message_close_container(req);
r = sd_bus_call(bus, req, 0, &error, NULL);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
- return log_interface_is_managed(r, ifindex);
+ return log_interface_is_managed(r, arg_ifindex);
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
sd_bus *bus = userdata;
- int ifindex, r;
char **p;
+ int r;
assert(bus);
if (argc <= 1)
return status_all(bus, STATUS_DOMAIN);
- ifindex = parse_ifindex_with_warn(argv[1]);
- if (ifindex < 0)
- return ifindex;
-
- if (ifindex == LOOPBACK_IFINDEX) {
- log_error("Interface can't be the loopback interface (lo). Sorry.");
- return -EINVAL;
- }
+ r = ifname_mangle(argv[1], false);
+ if (r < 0)
+ return r;
if (argc == 2)
- return status_ifindex(bus, ifindex, NULL, STATUS_DOMAIN, NULL);
+ return status_ifindex(bus, arg_ifindex, NULL, STATUS_DOMAIN, NULL);
r = sd_bus_message_new_method_call(
bus,
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append(req, "i", ifindex);
+ r = sd_bus_message_append(req, "i", arg_ifindex);
if (r < 0)
return bus_log_create_error(r);
if (r < 0)
return bus_log_create_error(r);
- STRV_FOREACH(p, argv + 2) {
- const char *n;
+ /* If only argument is the empty string, then call SetLinkDomains() with an
+ * empty list, which will clear the list of domains for an interface. */
+ if (!strv_equal(argv + 2, STRV_MAKE(""))) {
+ STRV_FOREACH(p, argv + 2) {
+ const char *n;
- n = **p == '~' ? *p + 1 : *p;
+ n = **p == '~' ? *p + 1 : *p;
- r = dns_name_is_valid(n);
- if (r < 0)
- return log_error_errno(r, "Failed to validate specified domain %s: %m", n);
- if (r == 0) {
- log_error("Domain not valid: %s", n);
- return -EINVAL;
- }
+ r = dns_name_is_valid(n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to validate specified domain %s: %m", n);
+ if (r == 0) {
+ log_error("Domain not valid: %s", n);
+ return -EINVAL;
+ }
- r = sd_bus_message_append(req, "(sb)", n, **p == '~');
- if (r < 0)
- return bus_log_create_error(r);
+ r = sd_bus_message_append(req, "(sb)", n, **p == '~');
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
}
r = sd_bus_message_close_container(req);
r = sd_bus_call(bus, req, 0, &error, NULL);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
- return log_interface_is_managed(r, ifindex);
+ return log_interface_is_managed(r, arg_ifindex);
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
static int verb_llmnr(int argc, char **argv, void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
- int ifindex, r;
+ int r;
assert(bus);
if (argc <= 1)
return status_all(bus, STATUS_LLMNR);
- ifindex = parse_ifindex_with_warn(argv[1]);
- if (ifindex < 0)
- return ifindex;
-
- if (ifindex == LOOPBACK_IFINDEX) {
- log_error("Interface can't be the loopback interface (lo). Sorry.");
- return -EINVAL;
- }
+ r = ifname_mangle(argv[1], false);
+ if (r < 0)
+ return r;
if (argc == 2)
- return status_ifindex(bus, ifindex, NULL, STATUS_LLMNR, NULL);
+ return status_ifindex(bus, arg_ifindex, NULL, STATUS_LLMNR, NULL);
r = sd_bus_call_method(bus,
"org.freedesktop.resolve1",
"SetLinkLLMNR",
&error,
NULL,
- "is", ifindex, argv[2]);
+ "is", arg_ifindex, argv[2]);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
- return log_interface_is_managed(r, ifindex);
+ return log_interface_is_managed(r, arg_ifindex);
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
static int verb_mdns(int argc, char **argv, void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
- int ifindex, r;
+ int r;
assert(bus);
if (argc <= 1)
return status_all(bus, STATUS_MDNS);
- ifindex = parse_ifindex_with_warn(argv[1]);
- if (ifindex < 0)
- return ifindex;
-
- if (ifindex == LOOPBACK_IFINDEX) {
- log_error("Interface can't be the loopback interface (lo). Sorry.");
- return -EINVAL;
- }
+ r = ifname_mangle(argv[1], false);
+ if (r < 0)
+ return r;
if (argc == 2)
- return status_ifindex(bus, ifindex, NULL, STATUS_MDNS, NULL);
+ return status_ifindex(bus, arg_ifindex, NULL, STATUS_MDNS, NULL);
r = sd_bus_call_method(bus,
"org.freedesktop.resolve1",
"SetLinkMulticastDNS",
&error,
NULL,
- "is", ifindex, argv[2]);
+ "is", arg_ifindex, argv[2]);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
- return log_interface_is_managed(r, ifindex);
+ return log_interface_is_managed(r, arg_ifindex);
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
return 0;
}
-static int verb_dnssec(int argc, char **argv, void *userdata) {
+static int verb_dns_over_tls(int argc, char **argv, void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
- int ifindex, r;
+ int r;
assert(bus);
if (argc <= 1)
- return status_all(bus, STATUS_DNSSEC);
+ return status_all(bus, STATUS_PRIVATE);
+
+ r = ifname_mangle(argv[1], false);
+ if (r < 0)
+ return r;
+
+ if (argc == 2)
+ return status_ifindex(bus, arg_ifindex, NULL, STATUS_PRIVATE, NULL);
- ifindex = parse_ifindex_with_warn(argv[1]);
- if (ifindex < 0)
- return ifindex;
+ r = sd_bus_call_method(bus,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ "SetLinkDNSOverTLS",
+ &error,
+ NULL,
+ "is", arg_ifindex, argv[2]);
+ if (r < 0) {
+ if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
+ return log_interface_is_managed(r, arg_ifindex);
+
+ if (arg_ifindex_permissive &&
+ sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
+ return 0;
- if (ifindex == LOOPBACK_IFINDEX) {
- log_error("Interface can't be the loopback interface (lo). Sorry.");
- return -EINVAL;
+ return log_error_errno(r, "Failed to set DNSOverTLS configuration: %s", bus_error_message(&error, r));
}
+ return 0;
+}
+
+static int verb_dnssec(int argc, char **argv, void *userdata) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus *bus = userdata;
+ int r;
+
+ assert(bus);
+
+ if (argc <= 1)
+ return status_all(bus, STATUS_DNSSEC);
+
+ r = ifname_mangle(argv[1], false);
+ if (r < 0)
+ return r;
+
if (argc == 2)
- return status_ifindex(bus, ifindex, NULL, STATUS_DNSSEC, NULL);
+ return status_ifindex(bus, arg_ifindex, NULL, STATUS_DNSSEC, NULL);
r = sd_bus_call_method(bus,
"org.freedesktop.resolve1",
"SetLinkDNSSEC",
&error,
NULL,
- "is", ifindex, argv[2]);
+ "is", arg_ifindex, argv[2]);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
- return log_interface_is_managed(r, ifindex);
+ return log_interface_is_managed(r, arg_ifindex);
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
sd_bus *bus = userdata;
- int ifindex, i, r;
+ char **p;
+ int r;
+ bool clear;
assert(bus);
if (argc <= 1)
return status_all(bus, STATUS_NTA);
- ifindex = parse_ifindex_with_warn(argv[1]);
- if (ifindex < 0)
- return ifindex;
-
- if (ifindex == LOOPBACK_IFINDEX) {
- log_error("Interface can't be the loopback interface (lo). Sorry.");
- return -EINVAL;
- }
+ r = ifname_mangle(argv[1], false);
+ if (r < 0)
+ return r;
if (argc == 2)
- return status_ifindex(bus, ifindex, NULL, STATUS_NTA, NULL);
+ return status_ifindex(bus, arg_ifindex, NULL, STATUS_NTA, NULL);
- for (i = 2; i < argc; i++) {
- r = dns_name_is_valid(argv[i]);
- if (r < 0)
- return log_error_errno(r, "Failed to validate specified domain %s: %m", argv[i]);
- if (r == 0) {
- log_error("Domain not valid: %s", argv[i]);
- return -EINVAL;
+ /* If only argument is the empty string, then call SetLinkDNSSECNegativeTrustAnchors()
+ * with an empty list, which will clear the list of domains for an interface. */
+ clear = strv_equal(argv + 2, STRV_MAKE(""));
+
+ if (!clear)
+ STRV_FOREACH(p, argv + 2) {
+ r = dns_name_is_valid(*p);
+ if (r < 0)
+ return log_error_errno(r, "Failed to validate specified domain %s: %m", *p);
+ if (r == 0) {
+ log_error("Domain not valid: %s", *p);
+ return -EINVAL;
+ }
}
- }
r = sd_bus_message_new_method_call(
bus,
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append(req, "i", ifindex);
+ r = sd_bus_message_append(req, "i", arg_ifindex);
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append_strv(req, argv + 2);
+ r = sd_bus_message_append_strv(req, clear ? NULL : argv + 2);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_call(bus, req, 0, &error, NULL);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
- return log_interface_is_managed(r, ifindex);
+ return log_interface_is_managed(r, arg_ifindex);
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
static int verb_revert_link(int argc, char **argv, void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
- int ifindex, r;
+ int r;
assert(bus);
- ifindex = parse_ifindex_with_warn(argv[1]);
- if (ifindex < 0)
- return ifindex;
-
- if (ifindex == LOOPBACK_IFINDEX) {
- log_error("Interface can't be the loopback interface (lo). Sorry.");
- return -EINVAL;
- }
+ r = ifname_mangle(argv[1], false);
+ if (r < 0)
+ return r;
r = sd_bus_call_method(bus,
"org.freedesktop.resolve1",
"RevertLink",
&error,
NULL,
- "i", ifindex);
+ "i", arg_ifindex);
if (r < 0) {
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
}
static void help_dns_types(void) {
- const char *t;
- int i;
-
if (arg_legend)
puts("Known DNS RR types:");
- for (i = 0; i < _DNS_TYPE_MAX; i++) {
- t = dns_type_to_string(i);
- if (t)
- puts(t);
- }
+
+ DUMP_STRING_TABLE(dns_type, int, _DNS_TYPE_MAX);
}
static void help_dns_classes(void) {
- const char *t;
- int i;
-
if (arg_legend)
puts("Known DNS RR classes:");
- for (i = 0; i < _DNS_CLASS_MAX; i++) {
- t = dns_class_to_string(i);
- if (t)
- puts(t);
- }
+
+ DUMP_STRING_TABLE(dns_class, int, _DNS_CLASS_MAX);
}
-static void compat_help(void) {
+static int compat_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 [OPTIONS...] HOSTNAME|ADDRESS...\n"
"%1$s [OPTIONS...] --service [[NAME] TYPE] DOMAIN\n"
"%1$s [OPTIONS...] --openpgp EMAIL@DOMAIN...\n"
" --set-domain=DOMAIN Set per-interface search domain\n"
" --set-llmnr=MODE Set per-interface LLMNR mode\n"
" --set-mdns=MODE Set per-interface MulticastDNS mode\n"
+ " --set-dnsovertls=MODE Set per-interface DNS-over-TLS mode\n"
" --set-dnssec=MODE Set per-interface DNSSEC mode\n"
" --set-nta=DOMAIN Set per-interface DNSSEC NTA\n"
" --revert Revert per-interface configuration\n"
- , program_invocation_short_name);
+ "\nSee the %2$s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
-static void native_help(void) {
+static int native_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 [OPTIONS...] {COMMAND} ...\n"
"\n"
"Send control commands to the network name resolution manager, or\n"
" domain [LINK [DOMAIN...]] Get/set per-interface search domain\n"
" llmnr [LINK [MODE]] Get/set per-interface LLMNR mode\n"
" mdns [LINK [MODE]] Get/set per-interface MulticastDNS mode\n"
+ " dnsovertls [LINK [MODE]] Get/set per-interface DNS-over-TLS mode\n"
" dnssec [LINK [MODE]] Get/set per-interface DNSSEC mode\n"
" nta [LINK [DOMAIN...]] Get/set per-interface DNSSEC NTA\n"
" revert LINK Revert per-interface configuration\n"
- , program_invocation_short_name);
+ "\nSee the %2$s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int verb_help(int argc, char **argv, void *userdata) {
- native_help();
- return 0;
+ return native_help();
}
static int compat_parse_argv(int argc, char *argv[]) {
ARG_SET_DOMAIN,
ARG_SET_LLMNR,
ARG_SET_MDNS,
+ ARG_SET_PRIVATE,
ARG_SET_DNSSEC,
ARG_SET_NTA,
ARG_REVERT_LINK,
{ "set-domain", required_argument, NULL, ARG_SET_DOMAIN },
{ "set-llmnr", required_argument, NULL, ARG_SET_LLMNR },
{ "set-mdns", required_argument, NULL, ARG_SET_MDNS },
+ { "set-dnsovertls", required_argument, NULL, ARG_SET_PRIVATE },
{ "set-dnssec", required_argument, NULL, ARG_SET_DNSSEC },
{ "set-nta", required_argument, NULL, ARG_SET_NTA },
{ "revert", no_argument, NULL, ARG_REVERT_LINK },
switch(c) {
case 'h':
- compat_help();
- return 0; /* done */;
+ return compat_help();
case ARG_VERSION:
return version();
break;
case 'i':
- r = parse_ifindex_with_warn(optarg);
+ arg_ifname = mfree(arg_ifname);
+ r = ifname_mangle(optarg, true);
if (r < 0)
return r;
-
- arg_ifname = optarg;
- arg_ifindex = r;
break;
case 't':
arg_flags |= SD_RESOLVED_MDNS_IPV4;
else if (streq(optarg, "mdns-ipv6"))
arg_flags |= SD_RESOLVED_MDNS_IPV6;
- else {
- log_error("Unknown protocol specifier: %s", optarg);
- return -EINVAL;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown protocol specifier: %s", optarg);
break;
case ARG_TLSA:
arg_mode = MODE_RESOLVE_TLSA;
- if (service_family_from_string(arg_service_family) < 0) {
- log_error("Unknown service family \"%s\".", optarg);
- return -EINVAL;
- }
- arg_service_family = optarg;
+ if (!optarg || service_family_is_valid(optarg))
+ arg_service_family = optarg;
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown service family \"%s\".", optarg);
break;
case ARG_RAW:
- if (on_tty()) {
- log_error("Refusing to write binary data to tty.");
- return -ENOTTY;
- }
+ if (on_tty())
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTTY),
+ "Refusing to write binary data to tty.");
if (optarg == NULL || streq(optarg, "payload"))
arg_raw = RAW_PAYLOAD;
else if (streq(optarg, "packet"))
arg_raw = RAW_PACKET;
- else {
- log_error("Unknown --raw specifier \"%s\".", optarg);
- return -EINVAL;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown --raw specifier \"%s\".",
+ optarg);
arg_legend = false;
break;
break;
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_SET_DNS:
arg_mode = MODE_SET_LINK;
break;
+ case ARG_SET_PRIVATE:
+ arg_set_dns_over_tls = optarg;
+ arg_mode = MODE_SET_LINK;
+ break;
+
case ARG_SET_DNSSEC:
arg_set_dnssec = optarg;
arg_mode = MODE_SET_LINK;
assert_not_reached("Unhandled option");
}
- if (arg_type == 0 && arg_class != 0) {
- log_error("--class= may only be used in conjunction with --type=.");
- return -EINVAL;
- }
+ if (arg_type == 0 && arg_class != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--class= may only be used in conjunction with --type=.");
- if (arg_type != 0 && arg_mode == MODE_RESOLVE_SERVICE) {
- log_error("--service and --type= may not be combined.");
- return -EINVAL;
- }
+ if (arg_type != 0 && arg_mode == MODE_RESOLVE_SERVICE)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--service and --type= may not be combined.");
if (arg_type != 0 && arg_class == 0)
arg_class = DNS_CLASS_IN;
if (IN_SET(arg_mode, MODE_SET_LINK, MODE_REVERT_LINK)) {
- if (arg_ifindex <= 0) {
- log_error("--set-dns=, --set-domain=, --set-llmnr=, --set-mdns=, --set-dnssec=, --set-nta= and --revert require --interface=.");
- return -EINVAL;
- }
+ if (arg_ifindex <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--set-dns=, --set-domain=, --set-llmnr=, --set-mdns=, --set-dnsovertls=, --set-dnssec=, --set-nta= and --revert require --interface=.");
- if (arg_ifindex == LOOPBACK_IFINDEX) {
- log_error("Interface can't be the loopback interface (lo). Sorry.");
- return -EINVAL;
- }
+ if (arg_ifindex == LOOPBACK_IFINDEX)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Interface can't be the loopback interface (lo). Sorry.");
}
return 1 /* work to do */;
switch(c) {
case 'h':
- native_help();
- return 0; /* done */;
+ return native_help();
case ARG_VERSION:
return version();
break;
case 'i':
- r = parse_ifindex_with_warn(optarg);
+ arg_ifname = mfree(arg_ifname);
+ r = ifname_mangle(optarg, true);
if (r < 0)
return r;
-
- arg_ifindex = r;
break;
case 't':
arg_flags |= SD_RESOLVED_MDNS_IPV4;
else if (streq(optarg, "mdns-ipv6"))
arg_flags |= SD_RESOLVED_MDNS_IPV6;
- else {
- log_error("Unknown protocol specifier: %s", optarg);
- return -EINVAL;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown protocol specifier: %s",
+ optarg);
break;
case ARG_RAW:
- if (on_tty()) {
- log_error("Refusing to write binary data to tty.");
- return -ENOTTY;
- }
+ if (on_tty())
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTTY),
+ "Refusing to write binary data to tty.");
if (optarg == NULL || streq(optarg, "payload"))
arg_raw = RAW_PAYLOAD;
else if (streq(optarg, "packet"))
arg_raw = RAW_PACKET;
- else {
- log_error("Unknown --raw specifier \"%s\".", optarg);
- return -EINVAL;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown --raw specifier \"%s\".",
+ optarg);
arg_legend = false;
break;
break;
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case '?':
assert_not_reached("Unhandled option");
}
- if (arg_type == 0 && arg_class != 0) {
- log_error("--class= may only be used in conjunction with --type=.");
- return -EINVAL;
- }
+ if (arg_type == 0 && arg_class != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--class= may only be used in conjunction with --type=.");
if (arg_type != 0 && arg_class == 0)
arg_class = DNS_CLASS_IN;
{ "domain", VERB_ANY, VERB_ANY, 0, verb_domain },
{ "llmnr", VERB_ANY, 3, 0, verb_llmnr },
{ "mdns", VERB_ANY, 3, 0, verb_mdns },
+ { "dnsovertls", VERB_ANY, 3, 0, verb_dns_over_tls },
{ "dnssec", VERB_ANY, 3, 0, verb_dnssec },
{ "nta", VERB_ANY, VERB_ANY, 0, verb_nta },
{ "revert", 2, 2, 0, verb_revert_link },
return dispatch_verb(argc, argv, verbs, bus);
}
-static int translate(const char *verb, const char *single_arg, unsigned num_args, char **args, sd_bus *bus) {
+static int translate(const char *verb, const char *single_arg, size_t num_args, char **args, sd_bus *bus) {
char **fake, **p;
- unsigned num, i;
+ size_t num, i;
assert(verb);
assert(num_args == 0 || args);
*p++ = args[i];
optind = 0;
- return native_main(num, fake, bus);
+ return native_main((int) num, fake, bus);
}
static int compat_main(int argc, char *argv[], sd_bus *bus) {
return translate("status", NULL, argc - optind, argv + optind, bus);
case MODE_SET_LINK:
+ assert(arg_ifname);
+
if (arg_set_dns) {
r = translate("dns", arg_ifname, strv_length(arg_set_dns), arg_set_dns, bus);
if (r < 0)
return r;
}
+ if (arg_set_dns_over_tls) {
+ r = translate("dnsovertls", arg_ifname, 1, (char **) &arg_set_dns_over_tls, bus);
+ if (r < 0)
+ return r;
+ }
+
if (arg_set_dnssec) {
r = translate("dnssec", arg_ifname, 1, (char **) &arg_set_dnssec, bus);
if (r < 0)
return r;
case MODE_REVERT_LINK:
+ assert(arg_ifname);
+
return translate("revert", arg_ifname, 0, NULL, bus);
case _MODE_INVALID:
return 0;
}
-int main(int argc, char **argv) {
- sd_bus *bus = NULL;
+static int run(int argc, char **argv) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
setlocale(LC_ALL, "");
else
r = native_parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
r = sd_bus_open_system(&bus);
- if (r < 0) {
- log_error_errno(r, "sd_bus_open_system: %m");
- goto finish;
- }
-
- if (streq(program_invocation_short_name, "systemd-resolve"))
- r = compat_main(argc, argv, bus);
- else
- r = native_main(argc, argv, bus);
-
-finish:
- /* make sure we terminate the bus connection first, and then close the
- * pager, see issue #3543 for the details. */
- sd_bus_flush_close_unref(bus);
- pager_close();
+ if (r < 0)
+ return log_error_errno(r, "sd_bus_open_system: %m");
- strv_free(arg_set_dns);
- strv_free(arg_set_domain);
- strv_free(arg_set_nta);
+ if (STR_IN_SET(program_invocation_short_name, "systemd-resolve", "resolvconf"))
+ return compat_main(argc, argv, bus);
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return native_main(argc, argv, bus);
}
+
+DEFINE_MAIN_FUNCTION(run);