-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include <locale.h>
#include "bus-error.h"
#include "bus-locator.h"
#include "bus-map-properties.h"
+#include "bus-message-util.h"
#include "dns-domain.h"
#include "escape.h"
#include "format-table.h"
#include "string-table.h"
#include "strv.h"
#include "terminal-util.h"
+#include "utf8.h"
#include "verbs.h"
static int arg_family = AF_UNSPEC;
printf("\n%s-- Information acquired via", ansi_grey());
- if (flags != 0)
- printf(" protocol%s%s%s%s%s",
- flags & SD_RESOLVED_DNS ? " DNS" :"",
- flags & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "",
- flags & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "",
- flags & SD_RESOLVED_MDNS_IPV4 ? " mDNS/IPv4" : "",
- flags & SD_RESOLVED_MDNS_IPV6 ? " mDNS/IPv6" : "");
+ printf(" protocol%s%s%s%s%s",
+ flags & SD_RESOLVED_DNS ? " DNS" :"",
+ flags & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "",
+ flags & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "",
+ flags & SD_RESOLVED_MDNS_IPV4 ? " mDNS/IPv4" : "",
+ flags & SD_RESOLVED_MDNS_IPV6 ? " mDNS/IPv6" : "");
assert_se(format_timespan(rtt_str, sizeof(rtt_str), rtt, 100));
while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
_cleanup_free_ char *pretty = NULL;
int ifindex, family, k;
- const void *a;
- size_t sz;
+ union in_addr_union a;
assert_cc(sizeof(int) == sizeof(int32_t));
- r = sd_bus_message_read(reply, "ii", &ifindex, &family);
+ r = sd_bus_message_read(reply, "i", &ifindex);
if (r < 0)
return bus_log_parse_error(r);
- r = sd_bus_message_read_array(reply, 'y', &a, &sz);
- if (r < 0)
- return bus_log_parse_error(r);
+ sd_bus_error_free(&error);
+ r = bus_message_read_in_addr_auto(reply, &error, &family, &a);
+ if (r < 0 && !sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS))
+ return log_error_errno(r, "%s: systemd-resolved returned invalid result: %s", name, bus_error_message(&error, r));
r = sd_bus_message_exit_container(reply);
if (r < 0)
return bus_log_parse_error(r);
- if (!IN_SET(family, AF_INET, AF_INET6)) {
- log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
+ if (sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)) {
+ log_debug_errno(r, "%s: systemd-resolved returned invalid result, ignoring: %s", name, bus_error_message(&error, r));
continue;
}
- if (sz != FAMILY_ADDRESS_SIZE(family)) {
- log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown");
- return -EINVAL;
- }
-
- r = in_addr_ifindex_to_string(family, a, ifindex, &pretty);
+ r = in_addr_ifindex_to_string(family, &a, ifindex, &pretty);
if (r < 0)
return log_error_errno(r, "Failed to print address for %s: %m", name);
(int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
canonical);
- if (c == 0) {
- log_error("%s: no addresses found", name);
- return -ESRCH;
- }
+ if (c == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
+ "%s: no addresses found", name);
print_source(flags, ts);
if (r < 0)
return bus_log_parse_error(r);
- if (c == 0) {
- log_error("%s: no names found", pretty);
- return -ESRCH;
- }
+ if (c == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
+ "%s: no names found", pretty);
print_source(flags, ts);
while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
_cleanup_free_ char *pretty = NULL;
int ifindex, family, k;
- const void *a;
+ union in_addr_union a;;
assert_cc(sizeof(int) == sizeof(int32_t));
- r = sd_bus_message_read(reply, "ii", &ifindex, &family);
+ r = sd_bus_message_read(reply, "i", &ifindex);
if (r < 0)
return bus_log_parse_error(r);
- r = sd_bus_message_read_array(reply, 'y', &a, &sz);
- if (r < 0)
- return bus_log_parse_error(r);
+ sd_bus_error_free(&error);
+ r = bus_message_read_in_addr_auto(reply, &error, &family, &a);
+ if (r < 0 && !sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS))
+ return log_error_errno(r, "%s: systemd-resolved returned invalid result: %s", name, bus_error_message(&error, r));
r = sd_bus_message_exit_container(reply);
if (r < 0)
return bus_log_parse_error(r);
- if (!IN_SET(family, AF_INET, AF_INET6)) {
- log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
+ if (sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)) {
+ log_debug_errno(r, "%s: systemd-resolved returned invalid result, ignoring: %s", name, bus_error_message(&error, r));
continue;
}
- if (sz != FAMILY_ADDRESS_SIZE(family)) {
- log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown");
- return -EINVAL;
- }
-
- r = in_addr_ifindex_to_string(family, a, ifindex, &pretty);
+ r = in_addr_ifindex_to_string(family, &a, ifindex, &pretty);
if (r < 0)
return log_error_errno(r, "Failed to print address for %s: %m", name);
r = table_print(table, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to print table: %m");
+ return table_log_print_error(r);
return 0;
}
return 0;
}
-static int read_dns_server_one(sd_bus_message *m, bool with_ifindex, char **ret) {
+static int read_dns_server_one(sd_bus_message *m, bool with_ifindex, bool extended, char **ret) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *pretty = NULL;
- int ifindex, family, r;
- const void *a;
- size_t sz;
+ int ifindex, family, r, k;
+ union in_addr_union a;
+ const char *name = NULL;
+ uint16_t port = 0;
assert(m);
assert(ret);
- r = sd_bus_message_enter_container(m, 'r', with_ifindex ? "iiay" : "iay");
+ r = sd_bus_message_enter_container(m, 'r', with_ifindex ? (extended ? "iiayqs" : "iiay") : (extended ? "iayqs" : "iay"));
if (r <= 0)
return r;
return r;
}
- r = sd_bus_message_read(m, "i", &family);
- if (r < 0)
- return r;
+ k = bus_message_read_in_addr_auto(m, &error, &family, &a);
+ if (k < 0 && !sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS))
+ return k;
- r = sd_bus_message_read_array(m, 'y', &a, &sz);
- if (r < 0)
- return r;
+ if (extended) {
+ r = sd_bus_message_read(m, "q", &port);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(m, "s", &name);
+ 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);
-
+ if (k < 0) {
+ log_debug("Invalid DNS server, ignoring: %s", bus_error_message(&error, k));
*ret = NULL;
return 1;
}
- if (sz != FAMILY_ADDRESS_SIZE(family)) {
- log_debug("Address size mismatch, ignoring.");
-
+ if (with_ifindex && ifindex != 0) {
+ /* only show the global ones here */
*ret = NULL;
return 1;
}
- r = in_addr_to_string(family, a, &pretty);
+ r = in_addr_port_ifindex_name_to_string(family, &a, port, ifindex, name, &pretty);
if (r < 0)
return r;
return 1;
}
-static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+static int map_link_dns_servers_internal(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata, bool extended) {
char ***l = userdata;
int r;
assert(m);
assert(l);
- r = sd_bus_message_enter_container(m, 'a', "(iay)");
+ r = sd_bus_message_enter_container(m, 'a', extended ? "(iayqs)" : "(iay)");
if (r < 0)
return r;
for (;;) {
_cleanup_free_ char *pretty = NULL;
- r = read_dns_server_one(m, false, &pretty);
+ r = read_dns_server_one(m, false, extended, &pretty);
if (r < 0)
return r;
if (r == 0)
return 0;
}
+static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ return map_link_dns_servers_internal(bus, member, m, error, userdata, false);
+}
+
+static int map_link_dns_servers_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ return map_link_dns_servers_internal(bus, member, m, error, userdata, true);
+}
+
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);
+ return read_dns_server_one(m, false, false, userdata);
+}
+
+static int map_link_current_dns_server_ex(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, true, userdata);
}
static int read_domain_one(sd_bus_message *m, bool with_ifindex, char **ret) {
if (r < 0)
return r;
+ strv_sort(*l);
+
return 0;
}
static int status_print_strv_ifindex(int ifindex, const char *ifname, char **p) {
+ const unsigned indent = strlen("Global: "); /* Use the same indentation everywhere to make things nice */
+ int pos1, pos2;
+
+ if (ifname)
+ printf("%s%nLink %i (%s)%n%s:", ansi_highlight(), &pos1, ifindex, ifname, &pos2, ansi_normal());
+ else
+ printf("%s%nGlobal%n%s:", ansi_highlight(), &pos1, &pos2, ansi_normal());
+
+ size_t cols = columns(), position = pos2 - pos1 + 2;
char **i;
- printf("%sLink %i (%s)%s:",
- ansi_highlight(), ifindex, ifname, ansi_normal());
+ STRV_FOREACH(i, p) {
+ size_t our_len = utf8_console_width(*i); /* This returns -1 on invalid utf-8 (which shouldn't happen).
+ * If that happens, we'll just print one item per line. */
- STRV_FOREACH(i, p)
- printf(" %s", *i);
+ if (position <= indent || size_add(size_add(position, 1), our_len) < cols) {
+ printf(" %s", *i);
+ position = size_add(size_add(position, 1), our_len);
+ } else {
+ printf("\n%*s%s", indent, "", *i);
+ position = size_add(our_len, indent);
+ }
+ }
printf("\n");
return 0;
}
-struct link_info {
+static int status_print_strv_global(char **p) {
+ return status_print_strv_ifindex(0, NULL, p);
+}
+
+typedef struct LinkInfo {
uint64_t scopes_mask;
const char *llmnr;
const char *mdns;
const char *dns_over_tls;
const char *dnssec;
char *current_dns;
+ char *current_dns_ex;
char **dns;
+ char **dns_ex;
char **domains;
char **ntas;
bool dnssec_supported;
bool default_route;
-};
+} LinkInfo;
+
+typedef struct GlobalInfo {
+ char *current_dns;
+ char *current_dns_ex;
+ char **dns;
+ char **dns_ex;
+ char **fallback_dns;
+ char **fallback_dns_ex;
+ char **domains;
+ char **ntas;
+ const char *llmnr;
+ const char *mdns;
+ const char *dns_over_tls;
+ const char *dnssec;
+ const char *resolv_conf_mode;
+ bool dnssec_supported;
+} GlobalInfo;
-static void link_info_clear(struct link_info *p) {
+static void link_info_clear(LinkInfo *p) {
free(p->current_dns);
+ free(p->current_dns_ex);
strv_free(p->dns);
+ strv_free(p->dns_ex);
+ strv_free(p->domains);
+ strv_free(p->ntas);
+}
+
+static void global_info_clear(GlobalInfo *p) {
+ free(p->current_dns);
+ free(p->current_dns_ex);
+ strv_free(p->dns);
+ strv_free(p->dns_ex);
+ strv_free(p->fallback_dns);
+ strv_free(p->fallback_dns_ex);
strv_free(p->domains);
strv_free(p->ntas);
}
r = table_add_many(table,
TABLE_STRING, prefix,
- TABLE_STRV, l);
+ TABLE_STRV_WRAPPED, l);
if (r < 0)
return table_log_add_error(r);
return 0;
}
+static int strv_extend_extended_bool(char ***strv, const char *name, const char *value) {
+ int r;
+
+ if (value) {
+ r = parse_boolean(value);
+ if (r >= 0)
+ return strv_extendf(strv, "%s%s", plus_minus(r), name);
+ }
+
+ return strv_extendf(strv, "%s=%s", name, value ?: "???");
+}
+
+static char** link_protocol_status(const LinkInfo *info) {
+ _cleanup_strv_free_ char **s = NULL;
+
+ if (strv_extendf(&s, "%sDefaultRoute", plus_minus(info->default_route)) < 0)
+ return NULL;
+
+ if (strv_extend_extended_bool(&s, "LLMNR", info->llmnr) < 0)
+ return NULL;
+
+ if (strv_extend_extended_bool(&s, "mDNS", info->mdns) < 0)
+ return NULL;
+
+ if (strv_extend_extended_bool(&s, "DNSOverTLS", info->dns_over_tls) < 0)
+ return NULL;
+
+ if (strv_extendf(&s, "DNSSEC=%s/%s",
+ info->dnssec ?: "???",
+ info->dnssec_supported ? "supported" : "unsupported") < 0)
+ return NULL;
+
+ return TAKE_PTR(s);
+}
+
+static char** global_protocol_status(const GlobalInfo *info) {
+ _cleanup_strv_free_ char **s = NULL;
+
+ if (strv_extend_extended_bool(&s, "LLMNR", info->llmnr) < 0)
+ return NULL;
+
+ if (strv_extend_extended_bool(&s, "mDNS", info->mdns) < 0)
+ return NULL;
+
+ if (strv_extend_extended_bool(&s, "DNSOverTLS", info->dns_over_tls) < 0)
+ return NULL;
+
+ if (strv_extendf(&s, "DNSSEC=%s/%s",
+ info->dnssec ?: "???",
+ info->dnssec_supported ? "supported" : "unsupported") < 0)
+ return NULL;
+
+ return TAKE_PTR(s);
+}
+
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) },
- { "CurrentDNSServer", "(iay)", map_link_current_dns_server, offsetof(struct link_info, current_dns) },
- { "Domains", "a(sb)", map_link_domains, offsetof(struct link_info, domains) },
- { "DefaultRoute", "b", NULL, offsetof(struct link_info, default_route) },
- { "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) },
+ { "ScopesMask", "t", NULL, offsetof(LinkInfo, scopes_mask) },
+ { "DNS", "a(iay)", map_link_dns_servers, offsetof(LinkInfo, dns) },
+ { "DNSEx", "a(iayqs)", map_link_dns_servers_ex, offsetof(LinkInfo, dns_ex) },
+ { "CurrentDNSServer", "(iay)", map_link_current_dns_server, offsetof(LinkInfo, current_dns) },
+ { "CurrentDNSServerEx", "(iayqs)", map_link_current_dns_server_ex, offsetof(LinkInfo, current_dns_ex) },
+ { "Domains", "a(sb)", map_link_domains, offsetof(LinkInfo, domains) },
+ { "DefaultRoute", "b", NULL, offsetof(LinkInfo, default_route) },
+ { "LLMNR", "s", NULL, offsetof(LinkInfo, llmnr) },
+ { "MulticastDNS", "s", NULL, offsetof(LinkInfo, mdns) },
+ { "DNSOverTLS", "s", NULL, offsetof(LinkInfo, dns_over_tls) },
+ { "DNSSEC", "s", NULL, offsetof(LinkInfo, dnssec) },
+ { "DNSSECNegativeTrustAnchors", "as", bus_map_strv_sort, offsetof(LinkInfo, ntas) },
+ { "DNSSECSupported", "b", NULL, offsetof(LinkInfo, 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_(link_info_clear) LinkInfo link_info = {};
_cleanup_(table_unrefp) Table *table = NULL;
_cleanup_free_ char *p = NULL;
char ifi[DECIMAL_STR_MAX(int)], ifname[IF_NAMESIZE + 1] = "";
(void) pager_open(arg_pager_flags);
if (mode == STATUS_DNS)
- return status_print_strv_ifindex(ifindex, name, link_info.dns);
+ return status_print_strv_ifindex(ifindex, name, link_info.dns_ex ?: link_info.dns);
if (mode == STATUS_DOMAIN)
return status_print_strv_ifindex(ifindex, name, link_info.domains);
if (r < 0)
return table_log_add_error(r);
+ _cleanup_strv_free_ char **pstatus = link_protocol_status(&link_info);
+ if (!pstatus)
+ return log_oom();
+
r = table_add_many(table,
- TABLE_STRING, "DefaultRoute setting:",
- TABLE_BOOLEAN, link_info.default_route,
- TABLE_STRING, "LLMNR setting:",
- TABLE_STRING, strna(link_info.llmnr),
- TABLE_STRING, "MulticastDNS setting:",
- TABLE_STRING, strna(link_info.mdns),
- TABLE_STRING, "DNSOverTLS setting:",
- TABLE_STRING, strna(link_info.dns_over_tls),
- TABLE_STRING, "DNSSEC setting:",
- TABLE_STRING, strna(link_info.dnssec),
- TABLE_STRING, "DNSSEC supported:",
- TABLE_BOOLEAN, link_info.dnssec_supported);
+ TABLE_STRING, "Protocols:",
+ TABLE_STRV_WRAPPED, pstatus);
if (r < 0)
return table_log_add_error(r);
if (link_info.current_dns) {
r = table_add_many(table,
TABLE_STRING, "Current DNS Server:",
- TABLE_STRING, link_info.current_dns);
+ TABLE_STRING, link_info.current_dns_ex ?: link_info.current_dns);
if (r < 0)
return table_log_add_error(r);
}
- r = dump_list(table, "DNS Servers:", link_info.dns);
+ r = dump_list(table, "DNS Servers:", link_info.dns_ex ?: link_info.dns);
if (r < 0)
return r;
r = table_print(table, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to print table: %m");
+ return table_log_print_error(r);
if (empty_line)
*empty_line = true;
return 0;
}
-static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+static int map_global_dns_servers_internal(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata, bool extended) {
char ***l = userdata;
int r;
assert(m);
assert(l);
- r = sd_bus_message_enter_container(m, 'a', "(iiay)");
+ r = sd_bus_message_enter_container(m, 'a', extended ? "(iiayqs)" : "(iiay)");
if (r < 0)
return r;
for (;;) {
_cleanup_free_ char *pretty = NULL;
- r = read_dns_server_one(m, true, &pretty);
+ r = read_dns_server_one(m, true, extended, &pretty);
if (r < 0)
return r;
if (r == 0)
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 map_global_dns_servers_internal(bus, member, m, error, userdata, false);
+}
+
+static int map_global_dns_servers_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ return map_global_dns_servers_internal(bus, member, m, error, userdata, true);
+}
+
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);
+ return read_dns_server_one(m, true, false, userdata);
+}
+
+static int map_global_current_dns_server_ex(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, true, userdata);
}
static int map_global_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
if (r < 0)
return r;
- return 0;
-}
-
-static int status_print_strv_global(char **p) {
- char **i;
-
- printf("%sGlobal%s:", ansi_highlight(), ansi_normal());
-
- STRV_FOREACH(i, p)
- printf(" %s", *i);
-
- printf("\n");
+ strv_sort(*l);
return 0;
}
-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) },
- { "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) },
+ { "DNS", "a(iiay)", map_global_dns_servers, offsetof(GlobalInfo, dns) },
+ { "DNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(GlobalInfo, dns_ex) },
+ { "FallbackDNS", "a(iiay)", map_global_dns_servers, offsetof(GlobalInfo, fallback_dns) },
+ { "FallbackDNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(GlobalInfo, fallback_dns_ex) },
+ { "CurrentDNSServer", "(iiay)", map_global_current_dns_server, offsetof(GlobalInfo, current_dns) },
+ { "CurrentDNSServerEx", "(iiayqs)", map_global_current_dns_server_ex, offsetof(GlobalInfo, current_dns_ex) },
+ { "Domains", "a(isb)", map_global_domains, offsetof(GlobalInfo, domains) },
+ { "DNSSECNegativeTrustAnchors", "as", bus_map_strv_sort, offsetof(GlobalInfo, ntas) },
+ { "LLMNR", "s", NULL, offsetof(GlobalInfo, llmnr) },
+ { "MulticastDNS", "s", NULL, offsetof(GlobalInfo, mdns) },
+ { "DNSOverTLS", "s", NULL, offsetof(GlobalInfo, dns_over_tls) },
+ { "DNSSEC", "s", NULL, offsetof(GlobalInfo, dnssec) },
+ { "DNSSECSupported", "b", NULL, offsetof(GlobalInfo, dnssec_supported) },
+ { "ResolvConfMode", "s", NULL, offsetof(GlobalInfo, resolv_conf_mode) },
{}
};
_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 = {};
+ _cleanup_(global_info_clear) GlobalInfo global_info = {};
_cleanup_(table_unrefp) Table *table = NULL;
int r;
(void) pager_open(arg_pager_flags);
if (mode == STATUS_DNS)
- return status_print_strv_global(global_info.dns);
+ return status_print_strv_global(global_info.dns_ex ?: global_info.dns);
if (mode == STATUS_DOMAIN)
return status_print_strv_global(global_info.domains);
table_set_header(table, false);
+ _cleanup_strv_free_ char **pstatus = global_protocol_status(&global_info);
+ if (!pstatus)
+ return log_oom();
+
r = table_add_many(table,
- TABLE_STRING, "LLMNR setting:",
+ TABLE_STRING, "Protocols:",
TABLE_SET_ALIGN_PERCENT, 100,
- TABLE_STRING, strna(global_info.llmnr),
- TABLE_STRING, "MulticastDNS setting:",
- TABLE_STRING, strna(global_info.mdns),
- TABLE_STRING, "DNSOverTLS setting:",
- TABLE_STRING, strna(global_info.dns_over_tls),
- TABLE_STRING, "DNSSEC setting:",
- TABLE_STRING, strna(global_info.dnssec),
- TABLE_STRING, "DNSSEC supported:",
- TABLE_BOOLEAN, global_info.dnssec_supported);
+ TABLE_STRV_WRAPPED, pstatus);
if (r < 0)
return table_log_add_error(r);
+ if (global_info.resolv_conf_mode) {
+ r = table_add_many(table,
+ TABLE_STRING, "resolv.conf mode:",
+ TABLE_STRING, global_info.resolv_conf_mode);
+ if (r < 0)
+ return table_log_add_error(r);
+ }
+
if (global_info.current_dns) {
r = table_add_many(table,
TABLE_STRING, "Current DNS Server:",
- TABLE_STRING, global_info.current_dns);
+ TABLE_STRING, global_info.current_dns_ex ?: global_info.current_dns);
if (r < 0)
return table_log_add_error(r);
}
- r = dump_list(table, "DNS Servers:", global_info.dns);
+ r = dump_list(table, "DNS Servers:", global_info.dns_ex ?: global_info.dns);
if (r < 0)
return r;
- r = dump_list(table, "Fallback DNS Servers:", global_info.fallback_dns);
+ r = dump_list(table, "Fallback DNS Servers:", global_info.fallback_dns_ex ?: global_info.fallback_dns);
if (r < 0)
return r;
r = table_print(table, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to print table: %m");
+ return table_log_print_error(r);
*empty_line = true;
return r;
}
-static int call_dns(sd_bus *bus, char **dns, const BusLocator *locator, sd_bus_error *error) {
+static int call_dns(sd_bus *bus, char **dns, const BusLocator *locator, sd_bus_error *error, bool extended) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
char **p;
int r;
- r = bus_message_new_method_call(bus, &req, locator, "SetLinkDNS");
+ r = bus_message_new_method_call(bus, &req, locator, extended ? "SetLinkDNSEx" : "SetLinkDNS");
if (r < 0)
return bus_log_create_error(r);
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_open_container(req, 'a', "(iay)");
+ r = sd_bus_message_open_container(req, 'a', extended ? "(iayqs)" : "(iay)");
if (r < 0)
return bus_log_create_error(r);
* empty list, which will clear the list of domains for an interface. */
if (!strv_equal(dns, STRV_MAKE("")))
STRV_FOREACH(p, dns) {
+ _cleanup_free_ char *name = NULL;
struct in_addr_data data;
+ uint16_t port;
+ int ifindex;
- r = in_addr_from_string_auto(*p, &data.family, &data.address);
+ r = in_addr_port_ifindex_name_from_string_auto(*p, &data.family, &data.address, &port, &ifindex, &name);
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 (ifindex != 0 && ifindex != arg_ifindex)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid ifindex: %i", ifindex);
+
+ r = sd_bus_message_open_container(req, 'r', extended ? "iayqs" : "iay");
if (r < 0)
return bus_log_create_error(r);
if (r < 0)
return bus_log_create_error(r);
+ if (extended) {
+ r = sd_bus_message_append(req, "q", port);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(req, "s", name);
+ 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);
if (r < 0)
return bus_log_create_error(r);
- return sd_bus_call(bus, req, 0, error, NULL);
+ r = sd_bus_call(bus, req, 0, error, NULL);
+ if (r < 0 && extended && sd_bus_error_has_name(error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
+ sd_bus_error_free(error);
+ return call_dns(bus, dns, locator, error, false);
+ }
+ return r;
}
static int verb_dns(int argc, char **argv, void *userdata) {
if (argc < 3)
return status_ifindex(bus, arg_ifindex, NULL, STATUS_DNS, NULL);
- r = call_dns(bus, argv + 2, bus_resolve_mgr, &error);
+ r = call_dns(bus, argv + 2, bus_resolve_mgr, &error, true);
if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
sd_bus_error_free(&error);
- r = call_dns(bus, argv + 2, bus_network_mgr, &error);
+ r = call_dns(bus, argv + 2, bus_network_mgr, &error, true);
}
if (r < 0) {
if (arg_ifindex_permissive &&
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;
- }
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Domain not valid: %s",
+ n);
r = sd_bus_message_append(req, "(sb)", n, **p == '~');
if (r < 0)
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;
- }
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Domain not valid: %s",
+ *p);
}
r = call_nta(bus, clear ? NULL : argv + 2, bus_resolve_mgr, &error);
return 0;
}
+static const char *resolve_flags_to_string(uint64_t flags) {
+ return flags & SD_RESOLVED_DNS ? "DNS" :
+ flags & SD_RESOLVED_LLMNR_IPV4 ? "LLMNR/IPv4" :
+ flags & SD_RESOLVED_LLMNR_IPV6 ? "LLMNR/IPv6" :
+ flags & SD_RESOLVED_MDNS_IPV4 ? "mDNS/IPv4" :
+ flags & SD_RESOLVED_MDNS_IPV6 ? "mDNS/IPv6" :
+ "";
+}
+
+static int verb_show_multicast(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(table_unrefp) Table *table = NULL;
+ sd_bus *bus = userdata;
+ int r;
+
+ assert(bus);
+
+ table = table_new("Hostname", "Address", "Source");
+ if (!table)
+ return log_oom();
+
+ r = bus_call_method(bus, bus_resolve_mgr, "GetMulticastHosts", &error, &reply, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to query systemd-resolved: %s", bus_error_message(&error, r));
+
+ r = sd_bus_message_enter_container(reply, 'a', "(stiiay)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_enter_container(reply, 'r', "stiiay")) > 0) {
+ char *canonical;
+ uint64_t flags;
+ _cleanup_free_ char *pretty = NULL;
+ int ifindex, family;
+ union in_addr_union a;
+
+ r = sd_bus_message_read(reply, "st", &canonical, &flags);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_read(reply, "i", &ifindex);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ sd_bus_error_free(&error);
+ r = bus_message_read_in_addr_auto(reply, &error, &family, &a);
+ if (r < 0)
+ return log_error_errno(
+ r,
+ "systemd-resolved returned invalid result: %s",
+ bus_error_message(&error, r));
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = in_addr_ifindex_to_string(family, &a, ifindex, &pretty);
+ if (r < 0)
+ return log_error_errno(r, "Failed to print address: %m");
+
+ r = table_add_many(
+ table,
+ TABLE_STRING,
+ canonical,
+ TABLE_STRING,
+ pretty,
+ TABLE_STRING,
+ resolve_flags_to_string(flags));
+ if (r < 0)
+ return table_log_add_error(r);
+ }
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = table_print(table, NULL);
+ if (r < 0)
+ return table_log_print_error(r);
+
+ return 0;
+}
+
static void help_protocol_types(void) {
if (arg_legend)
puts("Known protocol types:");
" --set-dnssec=MODE Set per-interface DNSSEC mode\n"
" --set-nta=DOMAIN Set per-interface DNSSEC NTA\n"
" --revert Revert per-interface configuration\n"
- "\nSee the %4$s for details.\n"
- , program_invocation_short_name
- , ansi_highlight()
- , ansi_normal()
- , link
- );
+ "\nSee the %4$s for details.\n",
+ program_invocation_short_name,
+ ansi_highlight(),
+ ansi_normal(),
+ link);
return 0;
}
" 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"
+ " log-level [LEVEL] Get/set logging threshold for systemd-resolved\n"
+ " show-multicast Show domain names discovered via LLMNR/mDNS\n"
"\nOptions:\n"
" -h --help Show this help\n"
" --version Show package version\n"
" (default: yes)\n"
" --raw[=payload|packet] Dump the answer as binary data\n"
" --legend=BOOL Print headers and additional info (default: yes)\n"
- "\nSee the %s for details.\n"
- , program_invocation_short_name
- , ansi_highlight()
- , ansi_normal()
- , ansi_highlight()
- , ansi_normal()
- , link
- );
+ "\nSee the %s for details.\n",
+ program_invocation_short_name,
+ ansi_highlight(),
+ ansi_normal(),
+ ansi_highlight(),
+ ansi_normal(),
+ link);
return 0;
}
}
static int native_main(int argc, char *argv[], sd_bus *bus) {
-
+ /* clang-format off */
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, verb_help },
{ "status", VERB_ANY, VERB_ANY, VERB_DEFAULT, verb_status },
{ "nta", VERB_ANY, VERB_ANY, 0, verb_nta },
{ "revert", VERB_ANY, 2, 0, verb_revert_link },
{ "log-level", VERB_ANY, 2, 0, verb_log_level },
+ { "show-multicast", VERB_ANY, VERB_ANY, 0, verb_show_multicast },
{}
};
+ /* clang-format on */
return dispatch_verb(argc, argv, verbs, bus);
}