diff --git a/man/resolvectl.xml b/man/resolvectl.xml
index 0ae70dc34b..a4bd8f52d7 100644
--- a/man/resolvectl.xml
+++ b/man/resolvectl.xml
@@ -145,15 +145,20 @@
settings for network interfaces. These commands may be used to inform
systemd-resolved or systemd-networkd about per-interface DNS
configuration determined through external means. The dns command expects IPv4 or
- IPv6 address specifications of DNS servers to use. The domain command expects
- valid DNS domains, possibly prefixed with ~, and configures a per-interface
- search or route-only domain. The default-route command expects a boolean
- parameter, and configures whether the link may be used as default route for DNS lookups, i.e. if it
- is suitable for lookups on domains no other link explicitly is configured for. The
- llmnr, mdns, dnssec and
- dnsovertls commands may be used to configure the per-interface LLMNR,
- MulticastDNS, DNSSEC and DNSOverTLS settings. Finally, nta command may be used
- to configure additional per-interface DNSSEC NTA domains.
+ IPv6 address specifications of DNS servers to use. Each address can optionally take a port number
+ separated with :, a network interface name or index separated with
+ %, and a Server Name Indication (SNI) separated with #. When
+ IPv6 address is specified with a port number, then the address must be in the square brackets. That
+ is, the acceptable full formats are 111.222.333.444:9953%ifname#example.com for
+ IPv4 and [1111:2222::3333]:9953%ifname#example.com for IPv6. The
+ domain command expects valid DNS domains, possibly prefixed with
+ ~, and configures a per-interface search or route-only domain. The
+ default-route command expects a boolean parameter, and configures whether the
+ link may be used as default route for DNS lookups, i.e. if it is suitable for lookups on domains no
+ other link explicitly is configured for. The llmnr, mdns,
+ dnssec and dnsovertls commands may be used to configure the
+ per-interface LLMNR, MulticastDNS, DNSSEC and DNSOverTLS settings. Finally, nta
+ command may be used to configure additional per-interface DNSSEC NTA domains.
Commands dns, domain and nta can take
a single empty string argument to clear their respective value lists.
diff --git a/man/resolved.conf.xml b/man/resolved.conf.xml
index 0e7a9f4bc6..535a23f500 100644
--- a/man/resolved.conf.xml
+++ b/man/resolved.conf.xml
@@ -47,8 +47,13 @@
DNS=
- A space-separated list of IPv4 and IPv6 addresses to use as system DNS servers. DNS requests
- are sent to one of the listed DNS servers in parallel to suitable per-link DNS servers acquired from
+ A space-separated list of IPv4 and IPv6 addresses to use as system DNS servers. Each address can
+ optionally take a port number separated with :, a network interface name or index separated with
+ %, and a Server Name Indication (SNI) separated with #. When IPv6 address is
+ specified with a port number, then the address must be in the square brackets. That is, the acceptable full formats
+ are 111.222.333.444:9953%ifname#example.com for IPv4 and
+ [1111:2222::3333]:9953%ifname#example.com for IPv6. DNS requests are sent to one of the listed
+ DNS servers in parallel to suitable per-link DNS servers acquired from
systemd-networkd.service8 or
set at runtime by external applications. For compatibility reasons, if this setting is not specified, the DNS
servers listed in /etc/resolv.conf are used instead, if that file exists and any servers
@@ -57,8 +62,8 @@
FallbackDNS=
- A space-separated list of IPv4 and IPv6 addresses to use as the fallback DNS servers. Any
- per-link DNS servers obtained from
+ A space-separated list of IPv4 and IPv6 addresses to use as the fallback DNS servers. Please see
+ DNS= for acceptable format of adddresses. Any per-link DNS servers obtained from
systemd-networkd.service8
take precedence over this setting, as do any servers set via DNS= above or
/etc/resolv.conf. This setting is hence only used if no other DNS server information is
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 58d1f2c717..0b0c751e91 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -606,7 +606,15 @@
A DNS server address, which must be in the format
described in
inet_pton3.
- This option may be specified more than once. This setting is read by
+ This option may be specified more than once. Each address can optionally take a port number
+ separated with :, a network interface name or index separated with
+ %, and a Server Name Indication (SNI) separated with #.
+ When IPv6 address is specified with a port number, then the address must be in the square
+ brackets. That is, the acceptable full formats are
+ 111.222.333.444:9953%ifname#example.com for IPv4 and
+ [1111:2222::3333]:9953%ifname#example.com for IPv6. This setting can be
+ specified multiple times. If an empty string is assigned, then the all previous assignments
+ are cleared. This setting is read by
systemd-resolved.service8.
diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c
index 9feee66343..828ea11816 100644
--- a/src/basic/in-addr-util.c
+++ b/src/basic/in-addr-util.c
@@ -14,6 +14,7 @@
#include "macro.h"
#include "parse-util.h"
#include "random-util.h"
+#include "string-util.h"
#include "strxcpyx.h"
#include "util.h"
@@ -445,6 +446,61 @@ fallback:
return in_addr_to_string(family, u, ret);
}
+int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret) {
+ _cleanup_free_ char *ip_str = NULL, *x = NULL;
+ int r;
+
+ assert(IN_SET(family, AF_INET, AF_INET6));
+ assert(u);
+ assert(ret);
+
+ /* Much like in_addr_to_string(), but optionally appends the zone interface index to the address, to properly
+ * handle IPv6 link-local addresses. */
+
+ r = in_addr_to_string(family, u, &ip_str);
+ if (r < 0)
+ return r;
+
+ if (family == AF_INET6) {
+ r = in_addr_is_link_local(family, u);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ ifindex = 0;
+ } else
+ ifindex = 0; /* For IPv4 address, ifindex is always ignored. */
+
+ if (port == 0 && ifindex == 0 && isempty(server_name)) {
+ *ret = TAKE_PTR(ip_str);
+ return 0;
+ }
+
+ const char *separator = isempty(server_name) ? "" : "#";
+ server_name = strempty(server_name);
+
+ if (port > 0) {
+ if (family == AF_INET6) {
+ if (ifindex > 0)
+ r = asprintf(&x, "[%s]:%"PRIu16"%%%i%s%s", ip_str, port, ifindex, separator, server_name);
+ else
+ r = asprintf(&x, "[%s]:%"PRIu16"%s%s", ip_str, port, separator, server_name);
+ } else
+ r = asprintf(&x, "%s:%"PRIu16"%s%s", ip_str, port, separator, server_name);
+ } else {
+ if (ifindex > 0)
+ r = asprintf(&x, "%s%%%i%s%s", ip_str, ifindex, separator, server_name);
+ else {
+ x = strjoin(ip_str, separator, server_name);
+ r = x ? 0 : -ENOMEM;
+ }
+ }
+ if (r < 0)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(x);
+ return 0;
+}
+
int in_addr_from_string(int family, const char *s, union in_addr_union *ret) {
union in_addr_union buffer;
assert(s);
diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h
index 90d79a5ef5..dc3f575bc9 100644
--- a/src/basic/in-addr-util.h
+++ b/src/basic/in-addr-util.h
@@ -41,6 +41,7 @@ int in_addr_random_prefix(int family, union in_addr_union *u, unsigned prefixlen
int in_addr_to_string(int family, const union in_addr_union *u, char **ret);
int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret);
int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret);
+int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret);
int in_addr_from_string(int family, const char *s, union in_addr_union *ret);
int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret);
diff --git a/src/network/networkd-dhcp-server.c b/src/network/networkd-dhcp-server.c
index 265d9dbc79..5129a2e37e 100644
--- a/src/network/networkd-dhcp-server.c
+++ b/src/network/networkd-dhcp-server.c
@@ -61,10 +61,10 @@ static int link_push_uplink_to_dhcp_server(
struct in_addr ia;
/* Only look for IPv4 addresses */
- if (link->network->dns[i].family != AF_INET)
+ if (link->network->dns[i]->family != AF_INET)
continue;
- ia = link->network->dns[i].address.in;
+ ia = link->network->dns[i]->address.in;
/* Never propagate obviously borked data */
if (in4_addr_is_null(&ia) || in4_addr_is_localhost(&ia))
diff --git a/src/network/networkd-link-bus.c b/src/network/networkd-link-bus.c
index 38b005fb63..be19671505 100644
--- a/src/network/networkd-link-bus.c
+++ b/src/network/networkd-link-bus.c
@@ -7,6 +7,7 @@
#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-get-properties.h"
+#include "bus-message-util.h"
#include "bus-polkit.h"
#include "dns-domain.h"
#include "networkd-link-bus.h"
@@ -14,6 +15,7 @@
#include "networkd-manager.h"
#include "parse-util.h"
#include "resolve-util.h"
+#include "socket-netlink.h"
#include "strv.h"
#include "user-util.h"
@@ -113,10 +115,10 @@ int bus_link_method_set_ntp_servers(sd_bus_message *message, void *userdata, sd_
return sd_bus_reply_method_return(message, NULL);
}
-int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_free_ struct in_addr_data *dns = NULL;
- size_t allocated = 0, n = 0;
+static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, void *userdata, sd_bus_error *error, bool extended) {
+ struct in_addr_full **dns;
Link *l = userdata;
+ size_t n;
int r;
assert(message);
@@ -126,52 +128,7 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_
if (r < 0)
return r;
- r = sd_bus_message_enter_container(message, 'a', "(iay)");
- if (r < 0)
- return r;
-
- for (;;) {
- int family;
- size_t sz;
- const void *d;
-
- assert_cc(sizeof(int) == sizeof(int32_t));
-
- r = sd_bus_message_enter_container(message, 'r', "iay");
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- r = sd_bus_message_read(message, "i", &family);
- if (r < 0)
- return r;
-
- if (!IN_SET(family, AF_INET, AF_INET6))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
-
- r = sd_bus_message_read_array(message, 'y', &d, &sz);
- if (r < 0)
- return r;
- if (sz != FAMILY_ADDRESS_SIZE(family))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
-
- if (!dns_server_address_valid(family, d))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address");
-
- r = sd_bus_message_exit_container(message);
- if (r < 0)
- return r;
-
- if (!GREEDY_REALLOC(dns, allocated, n+1))
- return -ENOMEM;
-
- dns[n].family = family;
- memcpy(&dns[n].address, d, sz);
- n++;
- }
-
- r = sd_bus_message_exit_container(message);
+ r = bus_message_read_dns_servers(message, error, extended, &dns, &n);
if (r < 0)
return r;
@@ -180,9 +137,15 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_
NULL, true, UID_INVALID,
&l->manager->polkit_registry, error);
if (r < 0)
- return r;
- if (r == 0)
- return 1; /* Polkit will call us back */
+ goto finalize;
+ if (r == 0) {
+ r = 1; /* Polkit will call us back */
+ goto finalize;
+ }
+
+ if (l->n_dns != (unsigned) -1)
+ for (unsigned i = 0; i < l->n_dns; i++)
+ in_addr_full_free(l->dns[i]);
free_and_replace(l->dns, dns);
l->n_dns = n;
@@ -190,6 +153,21 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_
(void) link_dirty(l);
return sd_bus_reply_method_return(message, NULL);
+
+finalize:
+ for (size_t i = 0; i < n; i++)
+ in_addr_full_free(dns[i]);
+ free(dns);
+
+ return r;
+}
+
+int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return bus_link_method_set_dns_servers_internal(message, userdata, error, false);
+}
+
+int bus_link_method_set_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return bus_link_method_set_dns_servers_internal(message, userdata, error, true);
}
int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -670,6 +648,7 @@ const sd_bus_vtable link_vtable[] = {
SD_BUS_METHOD("SetNTP", "as", NULL, bus_link_method_set_ntp_servers, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetDNS", "a(iay)", NULL, bus_link_method_set_dns_servers, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetDNSEx", "a(iayqs)", NULL, bus_link_method_set_dns_servers_ex, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetDomains", "a(sb)", NULL, bus_link_method_set_domains, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetDefaultRoute", "b", NULL, bus_link_method_set_default_route, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetLLMNR", "s", NULL, bus_link_method_set_llmnr, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/network/networkd-link-bus.h b/src/network/networkd-link-bus.h
index afe0197dbd..94474f22ff 100644
--- a/src/network/networkd-link-bus.h
+++ b/src/network/networkd-link-bus.h
@@ -21,6 +21,7 @@ int property_get_address_state(sd_bus *bus, const char *path, const char *interf
int bus_link_method_set_ntp_servers(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 24de9a7ac8..1d73a3c34f 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -666,6 +666,9 @@ void link_ntp_settings_clear(Link *link) {
}
void link_dns_settings_clear(Link *link) {
+ if (link->n_dns != (unsigned) -1)
+ for (unsigned i = 0; i < link->n_dns; i++)
+ in_addr_full_free(link->dns[i]);
link->dns = mfree(link->dns);
link->n_dns = (unsigned) -1;
@@ -4108,20 +4111,20 @@ static void print_link_hashmap(FILE *f, const char *prefix, Hashmap* h) {
fputc('\n', f);
}
-static void link_save_dns(FILE *f, struct in_addr_data *dns, unsigned n_dns, bool *space) {
+static void link_save_dns(Link *link, FILE *f, struct in_addr_full **dns, unsigned n_dns, bool *space) {
for (unsigned j = 0; j < n_dns; j++) {
- _cleanup_free_ char *b = NULL;
- int r;
+ const char *str;
- r = in_addr_to_string(dns[j].family, &dns[j].address, &b);
- if (r < 0) {
- log_debug_errno(r, "Failed to format address, ignoring: %m");
+ if (dns[j]->ifindex != 0 && dns[j]->ifindex != link->ifindex)
+ continue;
+
+ str = in_addr_full_to_string(dns[j]);
+ if (!str)
continue;
- }
if (*space)
fputc(' ', f);
- fputs(b, f);
+ fputs(str, f);
*space = true;
}
}
@@ -4251,9 +4254,9 @@ int link_save(Link *link) {
fputs("DNS=", f);
space = false;
if (link->n_dns != (unsigned) -1)
- link_save_dns(f, link->dns, link->n_dns, &space);
+ link_save_dns(link, f, link->dns, link->n_dns, &space);
else
- link_save_dns(f, link->network->dns, link->network->n_dns, &space);
+ link_save_dns(link, f, link->network->dns, link->network->n_dns, &space);
serialize_addresses(f, NULL, &space,
NULL,
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index fb12301fe6..e736970503 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -163,7 +163,7 @@ typedef struct Link {
bool stats_updated;
/* All kinds of DNS configuration the user configured via D-Bus */
- struct in_addr_data *dns;
+ struct in_addr_full **dns;
unsigned n_dns;
OrderedSet *search_domains, *route_domains;
diff --git a/src/network/networkd-manager-bus.c b/src/network/networkd-manager-bus.c
index e956c5ed9a..9db59d93f8 100644
--- a/src/network/networkd-manager-bus.c
+++ b/src/network/networkd-manager-bus.c
@@ -6,6 +6,7 @@
#include "alloc-util.h"
#include "bus-common-errors.h"
+#include "bus-message-util.h"
#include "bus-polkit.h"
#include "networkd-link-bus.h"
#include "networkd-link.h"
@@ -93,17 +94,16 @@ static int method_get_link_by_index(sd_bus_message *message, void *userdata, sd_
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *path = NULL;
Manager *manager = userdata;
- int32_t index;
+ int ifindex, r;
Link *link;
- int r;
- r = sd_bus_message_read(message, "i", &index);
+ r = bus_message_read_ifindex(message, error, &ifindex);
if (r < 0)
return r;
- link = hashmap_get(manager->links, INT_TO_PTR((int) index));
+ link = hashmap_get(manager->links, INT_TO_PTR(ifindex));
if (!link)
- return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %" PRIi32 " not known", index);
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %i not known", ifindex);
r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
@@ -128,14 +128,10 @@ static int call_link_method(Manager *m, sd_bus_message *message, sd_bus_message_
assert(message);
assert(handler);
- assert_cc(sizeof(int) == sizeof(int32_t));
- r = sd_bus_message_read(message, "i", &ifindex);
+ r = bus_message_read_ifindex(message, error, &ifindex);
if (r < 0)
return r;
- if (ifindex <= 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
-
l = hashmap_get(m->links, INT_TO_PTR(ifindex));
if (!l)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %i not known", ifindex);
@@ -151,6 +147,10 @@ static int bus_method_set_link_dns_servers(sd_bus_message *message, void *userda
return call_link_method(userdata, message, bus_link_method_set_dns_servers, error);
}
+static int bus_method_set_link_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_dns_servers_ex, error);
+}
+
static int bus_method_set_link_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return call_link_method(userdata, message, bus_link_method_set_domains, error);
}
@@ -243,6 +243,7 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_METHOD("GetLinkByIndex", "i", "so", method_get_link_by_index, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetLinkNTP", "ias", NULL, bus_method_set_link_ntp_servers, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetLinkDNS", "ia(iay)", NULL, bus_method_set_link_dns_servers, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetLinkDNSEx", "ia(iayqs)", NULL, bus_method_set_link_dns_servers_ex, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetLinkDomains", "ia(sb)", NULL, bus_method_set_link_domains, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetLinkDefaultRoute", "ib", NULL, bus_method_set_link_default_route, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetLinkLLMNR", "is", NULL, bus_method_set_link_llmnr, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index 4fbb7965be..273c00c36b 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -1425,33 +1425,36 @@ static int manager_connect_rtnl(Manager *m) {
return 0;
}
-static int ordered_set_put_in_addr_data(OrderedSet *s, const struct in_addr_data *address) {
- char *p;
+static int ordered_set_put_dns_server(OrderedSet *s, int ifindex, struct in_addr_full *dns) {
+ const char *p;
int r;
assert(s);
- assert(address);
+ assert(dns);
- r = in_addr_to_string(address->family, &address->address, &p);
- if (r < 0)
- return r;
+ if (dns->ifindex != 0 && dns->ifindex != ifindex)
+ return 0;
- r = ordered_set_consume(s, p);
+ p = in_addr_full_to_string(dns);
+ if (!p)
+ return 0;
+
+ r = ordered_set_put_strdup(s, p);
if (r == -EEXIST)
return 0;
return r;
}
-static int ordered_set_put_in_addr_datav(OrderedSet *s, const struct in_addr_data *addresses, unsigned n) {
+static int ordered_set_put_dns_servers(OrderedSet *s, int ifindex, struct in_addr_full **dns, unsigned n) {
int r, c = 0;
unsigned i;
assert(s);
- assert(addresses || n == 0);
+ assert(dns || n == 0);
for (i = 0; i < n; i++) {
- r = ordered_set_put_in_addr_data(s, addresses+i);
+ r = ordered_set_put_dns_server(s, ifindex, dns[i]);
if (r < 0)
return r;
@@ -1558,7 +1561,10 @@ static int manager_save(Manager *m) {
continue;
/* First add the static configured entries */
- r = ordered_set_put_in_addr_datav(dns, link->network->dns, link->network->n_dns);
+ if (link->n_dns != (unsigned) -1)
+ r = ordered_set_put_dns_servers(dns, link->ifindex, link->dns, link->n_dns);
+ else
+ r = ordered_set_put_dns_servers(dns, link->ifindex, link->network->dns, link->network->n_dns);
if (r < 0)
return r;
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 6a3920adab..94a1a6100f 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -682,6 +682,8 @@ static Network *network_free(Network *network) {
sd_ipv4acd_unref(network->dhcp_acd);
strv_free(network->ntp);
+ for (unsigned i = 0; i < network->n_dns; i++)
+ in_addr_full_free(network->dns[i]);
free(network->dns);
ordered_set_free_free(network->search_domains);
ordered_set_free_free(network->route_domains);
@@ -1194,16 +1196,17 @@ int config_parse_dns(
assert(rvalue);
if (isempty(rvalue)) {
+ for (unsigned i = 0; i < n->n_dns; i++)
+ in_addr_full_free(n->dns[i]);
n->dns = mfree(n->dns);
n->n_dns = 0;
return 0;
}
for (const char *p = rvalue;;) {
+ _cleanup_(in_addr_full_freep) struct in_addr_full *dns = NULL;
_cleanup_free_ char *w = NULL;
- union in_addr_union a;
- struct in_addr_data *m;
- int family;
+ struct in_addr_full **m;
r = extract_first_word(&p, &w, NULL, 0);
if (r == -ENOMEM)
@@ -1216,22 +1219,21 @@ int config_parse_dns(
if (r == 0)
return 0;
- r = in_addr_from_string_auto(w, &family, &a);
+ r = in_addr_full_new_from_string(w, &dns);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to parse dns server address, ignoring: %s", w);
continue;
}
- m = reallocarray(n->dns, n->n_dns + 1, sizeof(struct in_addr_data));
+ if (IN_SET(dns->port, 53, 853))
+ dns->port = 0;
+
+ m = reallocarray(n->dns, n->n_dns + 1, sizeof(struct in_addr_full*));
if (!m)
return log_oom();
- m[n->n_dns++] = (struct in_addr_data) {
- .family = family,
- .address = a,
- };
-
+ m[n->n_dns++] = TAKE_PTR(dns);
n->dns = m;
}
}
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 424298248f..17109d139c 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -32,6 +32,7 @@
#include "networkd-util.h"
#include "ordered-set.h"
#include "resolve-util.h"
+#include "socket-netlink.h"
typedef enum IPv6PrivacyExtensions {
/* The values map to the kernel's /proc/sys/net/ipv6/conf/xxx/use_tempaddr values */
@@ -316,7 +317,7 @@ struct Network {
OrderedHashmap *sr_iov_by_section;
/* All kinds of DNS configuration */
- struct in_addr_data *dns;
+ struct in_addr_full **dns;
unsigned n_dns;
OrderedSet *search_domains, *route_domains;
diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c
index 8b473836db..088cdf11a3 100644
--- a/src/network/networkd-radv.c
+++ b/src/network/networkd-radv.c
@@ -463,10 +463,10 @@ static int radv_get_ip6dns(Network *network, struct in6_addr **dns,
for (i = 0; i < network->n_dns; i++) {
union in_addr_union *addr;
- if (network->dns[i].family != AF_INET6)
+ if (network->dns[i]->family != AF_INET6)
continue;
- addr = &network->dns[i].address;
+ addr = &network->dns[i]->address;
if (in_addr_is_null(AF_INET6, addr) ||
in_addr_is_link_local(AF_INET6, addr) ||
diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c
index c01524b9cc..476c5e4a3c 100644
--- a/src/resolve/resolvectl.c
+++ b/src/resolve/resolvectl.c
@@ -13,6 +13,7 @@
#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"
@@ -209,34 +210,29 @@ static int resolve_host(sd_bus *bus, const char *name) {
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);
@@ -740,33 +736,29 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons
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);
@@ -1120,16 +1112,18 @@ static int reset_server_features(int argc, char **argv, void *userdata) {
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;
@@ -1139,39 +1133,37 @@ static int read_dns_server_one(sd_bus_message *m, bool with_ifindex, char **ret)
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 (k < 0) {
+ log_debug("Invalid DNS server, ignoring: %s", bus_error_message(&error, k));
+ *ret = NULL;
+ return 1;
+ }
+
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);
+ r = in_addr_port_ifindex_name_to_string(family, &a, port, ifindex, name, &pretty);
if (r < 0)
return r;
@@ -1180,7 +1172,7 @@ static int read_dns_server_one(sd_bus_message *m, bool with_ifindex, char **ret)
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;
@@ -1189,14 +1181,14 @@ static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message
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)
@@ -1217,11 +1209,26 @@ static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message
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) {
@@ -1315,7 +1322,9 @@ struct link_info {
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;
@@ -1324,7 +1333,9 @@ struct link_info {
static void link_info_clear(struct link_info *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);
}
@@ -1346,17 +1357,19 @@ static int dump_list(Table *table, const char *prefix, char * const *l) {
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(struct link_info, scopes_mask) },
+ { "DNS", "a(iay)", map_link_dns_servers, offsetof(struct link_info, dns) },
+ { "DNSEx", "a(iayqs)", map_link_dns_servers_ex, offsetof(struct link_info, dns_ex) },
+ { "CurrentDNSServer", "(iay)", map_link_current_dns_server, offsetof(struct link_info, current_dns) },
+ { "CurrentDNSServerEx", "(iayqs)", map_link_current_dns_server_ex, offsetof(struct link_info, current_dns_ex) },
+ { "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) },
{}
};
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -1396,7 +1409,7 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
(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);
@@ -1504,12 +1517,12 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
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;
@@ -1527,7 +1540,7 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
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;
@@ -1536,14 +1549,14 @@ static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_messag
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)
@@ -1564,11 +1577,26 @@ static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_messag
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) {
@@ -1623,8 +1651,11 @@ static int status_print_strv_global(char **p) {
struct global_info {
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;
@@ -1636,24 +1667,30 @@ struct global_info {
static void global_info_clear(struct global_info *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);
}
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(struct global_info, dns) },
+ { "DNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(struct global_info, dns_ex) },
+ { "FallbackDNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, fallback_dns) },
+ { "FallbackDNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(struct global_info, fallback_dns_ex) },
+ { "CurrentDNSServer", "(iiay)", map_global_current_dns_server, offsetof(struct global_info, current_dns) },
+ { "CurrentDNSServerEx", "(iiayqs)", map_global_current_dns_server_ex, offsetof(struct global_info, current_dns_ex) },
+ { "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;
@@ -1679,7 +1716,7 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
(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);
@@ -1741,16 +1778,16 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
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;
@@ -1867,12 +1904,12 @@ static int verb_status(int argc, char **argv, void *userdata) {
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);
@@ -1880,7 +1917,7 @@ static int call_dns(sd_bus *bus, char **dns, const BusLocator *locator, sd_bus_e
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);
@@ -1888,13 +1925,19 @@ static int call_dns(sd_bus *bus, char **dns, const BusLocator *locator, sd_bus_e
* 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);
@@ -1906,6 +1949,16 @@ static int call_dns(sd_bus *bus, char **dns, const BusLocator *locator, sd_bus_e
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);
@@ -1915,7 +1968,10 @@ static int call_dns(sd_bus *bus, char **dns, const BusLocator *locator, sd_bus_e
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))
+ return call_dns(bus, dns, locator, error, false);
+ return r;
}
static int verb_dns(int argc, char **argv, void *userdata) {
@@ -1937,11 +1993,11 @@ 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 &&
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index 45687c8fca..dba1639a11 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -4,6 +4,7 @@
#include "bus-common-errors.h"
#include "bus-get-properties.h"
#include "bus-log-control-api.h"
+#include "bus-message-util.h"
#include "bus-polkit.h"
#include "dns-domain.h"
#include "memory-util.h"
@@ -454,11 +455,10 @@ finish:
static int bus_method_resolve_address(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
Manager *m = userdata;
+ union in_addr_union a;
int family, ifindex;
uint64_t flags;
- const void *d;
DnsQuery *q;
- size_t sz;
int r;
assert(message);
@@ -466,20 +466,14 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s
assert_cc(sizeof(int) == sizeof(int32_t));
- r = sd_bus_message_read(message, "ii", &ifindex, &family);
+ r = sd_bus_message_read(message, "i", &ifindex);
if (r < 0)
return r;
- if (!IN_SET(family, AF_INET, AF_INET6))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
-
- r = sd_bus_message_read_array(message, 'y', &d, &sz);
+ r = bus_message_read_in_addr_auto(message, error, &family, &a);
if (r < 0)
return r;
- if (sz != FAMILY_ADDRESS_SIZE(family))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
-
r = sd_bus_message_read(message, "t", &flags);
if (r < 0)
return r;
@@ -488,7 +482,7 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s
if (r < 0)
return r;
- r = dns_question_new_reverse(&question, family, d);
+ r = dns_question_new_reverse(&question, family, &a);
if (r < 0)
return r;
@@ -498,7 +492,7 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s
q->request = sd_bus_message_ref(message);
q->request_family = family;
- memcpy(&q->request_address, d, sz);
+ q->request_address = a;
q->complete = bus_method_resolve_address_complete;
r = dns_query_bus_track(q, message);
@@ -1218,19 +1212,26 @@ fail:
return r;
}
-int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex) {
+int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex, bool extended) {
int r;
assert(reply);
if (!s) {
- if (with_ifindex)
- return sd_bus_message_append(reply, "(iiay)", 0, AF_UNSPEC, 0);
- else
- return sd_bus_message_append(reply, "(iay)", AF_UNSPEC, 0);
+ if (with_ifindex) {
+ if (extended)
+ return sd_bus_message_append(reply, "(iiayqs)", 0, AF_UNSPEC, 0, 0, NULL);
+ else
+ return sd_bus_message_append(reply, "(iiay)", 0, AF_UNSPEC, 0);
+ } else {
+ if (extended)
+ return sd_bus_message_append(reply, "(iayqs)", AF_UNSPEC, 0, 0, NULL);
+ else
+ return sd_bus_message_append(reply, "(iay)", AF_UNSPEC, 0);
+ }
}
- r = sd_bus_message_open_container(reply, 'r', with_ifindex ? "iiay" : "iay");
+ r = sd_bus_message_open_container(reply, 'r', with_ifindex ? (extended ? "iiayqs" : "iiay") : (extended ? "iayqs" : "iay"));
if (r < 0)
return r;
@@ -1248,6 +1249,55 @@ int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex
if (r < 0)
return r;
+ if (extended) {
+ r = sd_bus_message_append(reply, "q", s->port);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "s", s->server_name);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
+static int bus_property_get_dns_servers_internal(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error,
+ bool extended) {
+
+ Manager *m = userdata;
+ DnsServer *s;
+ Iterator i;
+ Link *l;
+ int r;
+
+ assert(reply);
+ assert(m);
+
+ r = sd_bus_message_open_container(reply, 'a', extended ? "(iiayqs)" : "(iiay)");
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(servers, s, m->dns_servers) {
+ r = bus_dns_server_append(reply, s, true, extended);
+ if (r < 0)
+ return r;
+ }
+
+ HASHMAP_FOREACH(l, m->links, i)
+ LIST_FOREACH(servers, s, l->dns_servers) {
+ r = bus_dns_server_append(reply, s, true, extended);
+ if (r < 0)
+ return r;
+ }
+
return sd_bus_message_close_container(reply);
}
@@ -1259,33 +1309,46 @@ static int bus_property_get_dns_servers(
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
+ return bus_property_get_dns_servers_internal(bus, path, interface, property, reply, userdata, error, false);
+}
- Manager *m = userdata;
- DnsServer *s;
- Iterator i;
- Link *l;
+static int bus_property_get_dns_servers_ex(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+ return bus_property_get_dns_servers_internal(bus, path, interface, property, reply, userdata, error, true);
+}
+
+static int bus_property_get_fallback_dns_servers_internal(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error,
+ bool extended) {
+
+ DnsServer *s, **f = userdata;
int r;
assert(reply);
- assert(m);
+ assert(f);
- r = sd_bus_message_open_container(reply, 'a', "(iiay)");
+ r = sd_bus_message_open_container(reply, 'a', extended ? "(iiayqs)" : "(iiay)");
if (r < 0)
return r;
- LIST_FOREACH(servers, s, m->dns_servers) {
- r = bus_dns_server_append(reply, s, true);
+ LIST_FOREACH(servers, s, *f) {
+ r = bus_dns_server_append(reply, s, true, extended);
if (r < 0)
return r;
}
- HASHMAP_FOREACH(l, m->links, i)
- LIST_FOREACH(servers, s, l->dns_servers) {
- r = bus_dns_server_append(reply, s, true);
- if (r < 0)
- return r;
- }
-
return sd_bus_message_close_container(reply);
}
@@ -1297,24 +1360,38 @@ static int bus_property_get_fallback_dns_servers(
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
+ return bus_property_get_fallback_dns_servers_internal(bus, path, interface, property, reply, userdata, error, false);
+}
- DnsServer *s, **f = userdata;
- int r;
+static int bus_property_get_fallback_dns_servers_ex(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+ return bus_property_get_fallback_dns_servers_internal(bus, path, interface, property, reply, userdata, error, true);
+}
+
+static int bus_property_get_current_dns_server_internal(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error,
+ bool extended) {
+
+ DnsServer *s;
assert(reply);
- assert(f);
+ assert(userdata);
- r = sd_bus_message_open_container(reply, 'a', "(iiay)");
- if (r < 0)
- return r;
+ s = *(DnsServer **) userdata;
- LIST_FOREACH(servers, s, *f) {
- r = bus_dns_server_append(reply, s, true);
- if (r < 0)
- return r;
- }
-
- return sd_bus_message_close_container(reply);
+ return bus_dns_server_append(reply, s, true, extended);
}
static int bus_property_get_current_dns_server(
@@ -1325,15 +1402,18 @@ static int bus_property_get_current_dns_server(
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
+ return bus_property_get_current_dns_server_internal(bus, path, interface, property, reply, userdata, error, false);
+}
- DnsServer *s;
-
- assert(reply);
- assert(userdata);
-
- s = *(DnsServer **) userdata;
-
- return bus_dns_server_append(reply, s, true);
+static int bus_property_get_current_dns_server_ex(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+ return bus_property_get_current_dns_server_internal(bus, path, interface, property, reply, userdata, error, true);
}
static int bus_property_get_domains(
@@ -1497,9 +1577,6 @@ static int get_any_link(Manager *m, int ifindex, Link **ret, sd_bus_error *error
assert(m);
assert(ret);
- if (ifindex <= 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
-
l = hashmap_get(m->links, INT_TO_PTR(ifindex));
if (!l)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %i not known", ifindex);
@@ -1516,8 +1593,7 @@ static int call_link_method(Manager *m, sd_bus_message *message, sd_bus_message_
assert(message);
assert(handler);
- assert_cc(sizeof(int) == sizeof(int32_t));
- r = sd_bus_message_read(message, "i", &ifindex);
+ r = bus_message_read_ifindex(message, error, &ifindex);
if (r < 0)
return r;
@@ -1532,6 +1608,10 @@ static int bus_method_set_link_dns_servers(sd_bus_message *message, void *userda
return call_link_method(userdata, message, bus_link_method_set_dns_servers, error);
}
+static int bus_method_set_link_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_dns_servers_ex, error);
+}
+
static int bus_method_set_link_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return call_link_method(userdata, message, bus_link_method_set_domains, error);
}
@@ -1573,8 +1653,7 @@ static int bus_method_get_link(sd_bus_message *message, void *userdata, sd_bus_e
assert(message);
assert(m);
- assert_cc(sizeof(int) == sizeof(int32_t));
- r = sd_bus_message_read(message, "i", &ifindex);
+ r = bus_message_read_ifindex(message, error, &ifindex);
if (r < 0)
return r;
@@ -1844,8 +1923,11 @@ static const sd_bus_vtable resolve_vtable[] = {
SD_BUS_PROPERTY("MulticastDNS", "s", bus_property_get_resolve_support, offsetof(Manager, mdns_support), 0),
SD_BUS_PROPERTY("DNSOverTLS", "s", bus_property_get_dns_over_tls_mode, 0, 0),
SD_BUS_PROPERTY("DNS", "a(iiay)", bus_property_get_dns_servers, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("DNSEx", "a(iiayqs)", bus_property_get_dns_servers_ex, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("FallbackDNS", "a(iiay)", bus_property_get_fallback_dns_servers, offsetof(Manager, fallback_dns_servers), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("FallbackDNSEx", "a(iiayqs)", bus_property_get_fallback_dns_servers_ex, offsetof(Manager, fallback_dns_servers), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CurrentDNSServer", "(iiay)", bus_property_get_current_dns_server, offsetof(Manager, current_dns_server), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("CurrentDNSServerEx", "(iiayqs)", bus_property_get_current_dns_server_ex, offsetof(Manager, current_dns_server), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Domains", "a(isb)", bus_property_get_domains, 0, 0),
SD_BUS_PROPERTY("TransactionStatistics", "(tt)", bus_property_get_transaction_statistics, 0, 0),
SD_BUS_PROPERTY("CacheStatistics", "(ttt)", bus_property_get_cache_statistics, 0, 0),
@@ -1895,6 +1977,11 @@ static const sd_bus_vtable resolve_vtable[] = {
SD_BUS_NO_RESULT,
bus_method_set_link_dns_servers,
SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetLinkDNSEx",
+ SD_BUS_ARGS("i", ifindex, "a(iayqs)", addresses),
+ SD_BUS_NO_RESULT,
+ bus_method_set_link_dns_servers_ex,
+ SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_ARGS("SetLinkDomains",
SD_BUS_ARGS("i", ifindex, "a(sb)", domains),
SD_BUS_NO_RESULT,
diff --git a/src/resolve/resolved-bus.h b/src/resolve/resolved-bus.h
index 6d6b095323..28caa64a6b 100644
--- a/src/resolve/resolved-bus.h
+++ b/src/resolve/resolved-bus.h
@@ -9,7 +9,7 @@ extern const BusObjectImplementation manager_object;
int manager_connect_bus(Manager *m);
int _manager_send_changed(Manager *manager, const char *property, ...) _sentinel_;
#define manager_send_changed(manager, ...) _manager_send_changed(manager, __VA_ARGS__, NULL)
-int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex);
+int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex, bool extended);
int bus_property_get_resolve_support(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply,
void *userdata, sd_bus_error *error);
diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c
index 9a6b1e88e1..6b99271245 100644
--- a/src/resolve/resolved-conf.c
+++ b/src/resolve/resolved-conf.c
@@ -28,24 +28,33 @@ static const char* const dns_stub_listener_mode_table[_DNS_STUB_LISTENER_MODE_MA
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_stub_listener_mode, DnsStubListenerMode, DNS_STUB_LISTENER_YES);
static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) {
+ _cleanup_free_ char *server_name = NULL;
union in_addr_union address;
int family, r, ifindex = 0;
+ uint16_t port;
DnsServer *s;
- _cleanup_free_ char *server_name = NULL;
assert(m);
assert(word);
- r = in_addr_ifindex_name_from_string_auto(word, &family, &address, &ifindex, &server_name);
+ r = in_addr_port_ifindex_name_from_string_auto(word, &family, &address, &port, &ifindex, &server_name);
if (r < 0)
return r;
+ if (IN_SET(port, 53, 853))
+ port = 0;
+
/* Silently filter out 0.0.0.0 and 127.0.0.53 (our own stub DNS listener) */
if (!dns_server_address_valid(family, &address))
return 0;
+ /* By default, the port number is determined with the transaction feature level.
+ * See dns_transaction_port() and dns_server_port(). */
+ if (IN_SET(port, 53, 853))
+ port = 0;
+
/* Filter out duplicates */
- s = dns_server_find(manager_get_first_dns_server(m, type), family, &address, ifindex);
+ s = dns_server_find(manager_get_first_dns_server(m, type), family, &address, port, ifindex, server_name);
if (s) {
/*
* Drop the marker. This is used to find the servers
@@ -57,7 +66,7 @@ static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, cons
return 0;
}
- return dns_server_new(m, NULL, type, NULL, family, &address, ifindex, server_name);
+ return dns_server_new(m, NULL, type, NULL, family, &address, port, ifindex, server_name);
}
int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) {
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index 764ccee0e0..bd4b59ea8e 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -447,8 +447,8 @@ static int dns_scope_socket(
return TAKE_FD(fd);
}
-int dns_scope_socket_udp(DnsScope *s, DnsServer *server, uint16_t port) {
- return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, server, port, NULL);
+int dns_scope_socket_udp(DnsScope *s, DnsServer *server) {
+ return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, server, dns_server_port(server), NULL);
}
int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port, union sockaddr_union *ret_socket_address) {
diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h
index b356b92120..8b1a958551 100644
--- a/src/resolve/resolved-dns-scope.h
+++ b/src/resolve/resolved-dns-scope.h
@@ -75,7 +75,7 @@ void dns_scope_packet_lost(DnsScope *s, usec_t usec);
int dns_scope_emit_udp(DnsScope *s, int fd, DnsPacket *p);
int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port, union sockaddr_union *ret_socket_address);
-int dns_scope_socket_udp(DnsScope *s, DnsServer *server, uint16_t port);
+int dns_scope_socket_udp(DnsScope *s, DnsServer *server);
DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain);
bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key);
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index 60de1b29e7..c87026f434 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -26,6 +26,7 @@ int dns_server_new(
Link *l,
int family,
const union in_addr_union *in_addr,
+ uint16_t port,
int ifindex,
const char *server_name) {
@@ -47,7 +48,7 @@ int dns_server_new(
return -E2BIG;
}
- if (server_name) {
+ if (!isempty(server_name)) {
name = strdup(server_name);
if (!name)
return -ENOMEM;
@@ -63,6 +64,7 @@ int dns_server_new(
.type = type,
.family = family,
.address = *in_addr,
+ .port = port,
.ifindex = ifindex,
.server_name = TAKE_PTR(name),
};
@@ -117,6 +119,7 @@ static DnsServer* dns_server_free(DnsServer *s) {
#endif
free(s->server_string);
+ free(s->server_string_full);
free(s->server_name);
return mfree(s);
}
@@ -223,7 +226,7 @@ static void dns_server_verified(DnsServer *s, DnsServerFeatureLevel level) {
if (s->verified_feature_level != level) {
log_debug("Verified we get a response at feature level %s from DNS server %s.",
dns_server_feature_level_to_string(level),
- dns_server_string(s));
+ strna(dns_server_string_full(s)));
s->verified_feature_level = level;
}
@@ -360,7 +363,7 @@ void dns_server_packet_rcode_downgrade(DnsServer *s, DnsServerFeatureLevel level
dns_server_reset_counters(s);
}
- log_debug("Downgrading transaction feature level fixed an RCODE error, downgrading server %s too.", dns_server_string(s));
+ log_debug("Downgrading transaction feature level fixed an RCODE error, downgrading server %s too.", strna(dns_server_string_full(s)));
}
static bool dns_server_grace_period_expired(DnsServer *s) {
@@ -414,7 +417,7 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
log_info("Grace period over, resuming full feature set (%s) for DNS server %s.",
dns_server_feature_level_to_string(s->possible_feature_level),
- dns_server_string(s));
+ strna(dns_server_string_full(s)));
dns_server_flush_cache(s);
@@ -500,7 +503,7 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
log_full(log_level, "Using degraded feature set %s instead of %s for DNS server %s.",
dns_server_feature_level_to_string(s->possible_feature_level),
- dns_server_feature_level_to_string(p), dns_server_string(s));
+ dns_server_feature_level_to_string(p), strna(dns_server_string_full(s)));
}
}
@@ -548,13 +551,37 @@ int dns_server_ifindex(const DnsServer *s) {
return 0;
}
+uint16_t dns_server_port(const DnsServer *s) {
+ assert(s);
+
+ if (s->port > 0)
+ return s->port;
+
+ return 53;
+}
+
const char *dns_server_string(DnsServer *server) {
assert(server);
if (!server->server_string)
(void) in_addr_ifindex_to_string(server->family, &server->address, dns_server_ifindex(server), &server->server_string);
- return strna(server->server_string);
+ return server->server_string;
+}
+
+const char *dns_server_string_full(DnsServer *server) {
+ assert(server);
+
+ if (!server->server_string_full)
+ (void) in_addr_port_ifindex_name_to_string(
+ server->family,
+ &server->address,
+ server->port,
+ dns_server_ifindex(server),
+ server->server_name,
+ &server->server_string_full);
+
+ return server->server_string_full;
}
bool dns_server_dnssec_supported(DnsServer *server) {
@@ -586,8 +613,8 @@ void dns_server_warn_downgrade(DnsServer *server) {
log_struct(LOG_NOTICE,
"MESSAGE_ID=" SD_MESSAGE_DNSSEC_DOWNGRADE_STR,
- LOG_MESSAGE("Server %s does not support DNSSEC, downgrading to non-DNSSEC mode.", dns_server_string(server)),
- "DNS_SERVER=%s", dns_server_string(server),
+ LOG_MESSAGE("Server %s does not support DNSSEC, downgrading to non-DNSSEC mode.", strna(dns_server_string_full(server))),
+ "DNS_SERVER=%s", strna(dns_server_string_full(server)),
"DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(server->possible_feature_level));
server->warned_downgrade = true;
@@ -598,7 +625,10 @@ static void dns_server_hash_func(const DnsServer *s, struct siphash *state) {
siphash24_compress(&s->family, sizeof(s->family), state);
siphash24_compress(&s->address, FAMILY_ADDRESS_SIZE(s->family), state);
+ siphash24_compress(&s->port, sizeof(s->port), state);
siphash24_compress(&s->ifindex, sizeof(s->ifindex), state);
+ if (s->server_name)
+ siphash24_compress(s->server_name, strlen(s->server_name), state);
}
static int dns_server_compare_func(const DnsServer *x, const DnsServer *y) {
@@ -612,11 +642,15 @@ static int dns_server_compare_func(const DnsServer *x, const DnsServer *y) {
if (r != 0)
return r;
+ r = CMP(x->port, y->port);
+ if (r != 0)
+ return r;
+
r = CMP(x->ifindex, y->ifindex);
if (r != 0)
return r;
- return 0;
+ return streq_ptr(x->server_name, y->server_name);
}
DEFINE_HASH_OPS(dns_server_hash_ops, DnsServer, dns_server_hash_func, dns_server_compare_func);
@@ -655,11 +689,15 @@ void dns_server_mark_all(DnsServer *first) {
dns_server_mark_all(first->servers_next);
}
-DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, int ifindex) {
+DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, uint16_t port, int ifindex, const char *name) {
DnsServer *s;
LIST_FOREACH(servers, s, first)
- if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0 && s->ifindex == ifindex)
+ if (s->family == family &&
+ in_addr_equal(family, &s->address, in_addr) > 0 &&
+ s->port == port &&
+ s->ifindex == ifindex &&
+ streq_ptr(s->server_name, name))
return s;
return NULL;
@@ -690,7 +728,7 @@ DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
if (s)
log_debug("Switching to %s DNS server %s.",
dns_server_type_to_string(s->type),
- dns_server_string(s));
+ strna(dns_server_string_full(s)));
dns_server_unref(m->current_dns_server);
m->current_dns_server = dns_server_ref(s);
@@ -830,7 +868,7 @@ void dns_server_dump(DnsServer *s, FILE *f) {
f = stdout;
fputs("[Server ", f);
- fputs(dns_server_string(s), f);
+ fputs(strna(dns_server_string_full(s)), f);
fputs(" type=", f);
fputs(dns_server_type_to_string(s->type), f);
diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h
index 792f966121..464e8dc251 100644
--- a/src/resolve/resolved-dns-server.h
+++ b/src/resolve/resolved-dns-server.h
@@ -56,10 +56,11 @@ struct DnsServer {
int family;
union in_addr_union address;
int ifindex; /* for IPv6 link-local DNS servers */
+ uint16_t port;
+ char *server_name;
char *server_string;
-
- char *server_name;
+ char *server_string_full;
/* The long-lived stream towards this server. */
DnsStream *stream;
@@ -102,6 +103,7 @@ int dns_server_new(
Link *link,
int family,
const union in_addr_union *address,
+ uint16_t port,
int ifindex,
const char *server_string);
@@ -123,13 +125,15 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s);
int dns_server_adjust_opt(DnsServer *server, DnsPacket *packet, DnsServerFeatureLevel level);
const char *dns_server_string(DnsServer *server);
+const char *dns_server_string_full(DnsServer *server);
int dns_server_ifindex(const DnsServer *s);
+uint16_t dns_server_port(const DnsServer *s);
bool dns_server_dnssec_supported(DnsServer *server);
void dns_server_warn_downgrade(DnsServer *server);
-DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, int ifindex);
+DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, uint16_t port, int ifindex, const char *name);
void dns_server_unlink_all(DnsServer *first);
void dns_server_unlink_marked(DnsServer *first);
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index cd5a0e3dd9..e23ea273e7 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -314,7 +314,7 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
"DNS_TRANSACTION=%" PRIu16, t->id,
"DNS_QUESTION=%s", key_str,
"DNSSEC_RESULT=%s", dnssec_result_to_string(t->answer_dnssec_result),
- "DNS_SERVER=%s", dns_server_string(t->server),
+ "DNS_SERVER=%s", strna(dns_server_string_full(t->server)),
"DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(t->server->possible_feature_level));
}
@@ -398,7 +398,7 @@ static int dns_transaction_pick_server(DnsTransaction *t) {
t->n_picked_servers ++;
- log_debug("Using DNS server %s for transaction %u.", dns_server_string(t->server), t->id);
+ log_debug("Using DNS server %s for transaction %u.", strna(dns_server_string_full(t->server)), t->id);
return 1;
}
@@ -544,8 +544,10 @@ static int on_stream_packet(DnsStream *s) {
return 0;
}
-static uint16_t dns_port_for_feature_level(DnsServerFeatureLevel level) {
- return DNS_SERVER_FEATURE_LEVEL_IS_TLS(level) ? 853 : 53;
+static uint16_t dns_transaction_port(DnsTransaction *t) {
+ if (t->server->port > 0)
+ return t->server->port;
+ return DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level) ? 853 : 53;
}
static int dns_transaction_emit_tcp(DnsTransaction *t) {
@@ -576,7 +578,7 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) {
if (t->server->stream && (DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level) == t->server->stream->encrypted))
s = dns_stream_ref(t->server->stream);
else
- fd = dns_scope_socket_tcp(t->scope, AF_UNSPEC, NULL, t->server, dns_port_for_feature_level(t->current_feature_level), &sa);
+ fd = dns_scope_socket_tcp(t->scope, AF_UNSPEC, NULL, t->server, dns_transaction_port(t), &sa);
type = DNS_STREAM_LOOKUP;
break;
@@ -1243,7 +1245,7 @@ static int dns_transaction_emit_udp(DnsTransaction *t) {
dns_transaction_close_connection(t);
- fd = dns_scope_socket_udp(t->scope, t->server, 53);
+ fd = dns_scope_socket_udp(t->scope, t->server);
if (fd < 0)
return fd;
diff --git a/src/resolve/resolved-link-bus.c b/src/resolve/resolved-link-bus.c
index 0fa62208c3..42d4ae7aaf 100644
--- a/src/resolve/resolved-link-bus.c
+++ b/src/resolve/resolved-link-bus.c
@@ -7,12 +7,14 @@
#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-get-properties.h"
+#include "bus-message-util.h"
#include "bus-polkit.h"
#include "parse-util.h"
#include "resolve-util.h"
#include "resolved-bus.h"
#include "resolved-link-bus.h"
#include "resolved-resolv-conf.h"
+#include "socket-netlink.h"
#include "stdio-util.h"
#include "strv.h"
#include "user-util.h"
@@ -37,14 +39,15 @@ static int property_get_dns_over_tls_mode(
return sd_bus_message_append(reply, "s", dns_over_tls_mode_to_string(link_get_dns_over_tls_mode(l)));
}
-static int property_get_dns(
+static int property_get_dns_internal(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
- sd_bus_error *error) {
+ sd_bus_error *error,
+ bool extended) {
Link *l = userdata;
DnsServer *s;
@@ -53,12 +56,12 @@ static int property_get_dns(
assert(reply);
assert(l);
- r = sd_bus_message_open_container(reply, 'a', "(iay)");
+ r = sd_bus_message_open_container(reply, 'a', extended ? "(iayqs)" : "(iay)");
if (r < 0)
return r;
LIST_FOREACH(servers, s, l->dns_servers) {
- r = bus_dns_server_append(reply, s, false);
+ r = bus_dns_server_append(reply, s, false, extended);
if (r < 0)
return r;
}
@@ -66,6 +69,48 @@ static int property_get_dns(
return sd_bus_message_close_container(reply);
}
+static int property_get_dns(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+ return property_get_dns_internal(bus, path, interface, property, reply, userdata, error, false);
+}
+
+static int property_get_dns_ex(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+ return property_get_dns_internal(bus, path, interface, property, reply, userdata, error, true);
+}
+
+static int property_get_current_dns_server_internal(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error,
+ bool extended) {
+
+ DnsServer *s;
+
+ assert(reply);
+ assert(userdata);
+
+ s = *(DnsServer **) userdata;
+
+ return bus_dns_server_append(reply, s, false, extended);
+}
+
static int property_get_current_dns_server(
sd_bus *bus,
const char *path,
@@ -74,15 +119,18 @@ static int property_get_current_dns_server(
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
+ return property_get_current_dns_server_internal(bus, path, interface, property, reply, userdata, error, false);
+}
- DnsServer *s;
-
- assert(reply);
- assert(userdata);
-
- s = *(DnsServer **) userdata;
-
- return bus_dns_server_append(reply, s, false);
+static int property_get_current_dns_server_ex(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+ return property_get_current_dns_server_internal(bus, path, interface, property, reply, userdata, error, true);
}
static int property_get_domains(
@@ -204,11 +252,10 @@ static int verify_unmanaged_link(Link *l, sd_bus_error *error) {
return 0;
}
-int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_free_ struct in_addr_data *dns = NULL;
- size_t allocated = 0, n = 0;
+static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, void *userdata, sd_bus_error *error, bool extended) {
+ struct in_addr_full **dns;
Link *l = userdata;
- unsigned i;
+ size_t n;
int r;
assert(message);
@@ -218,52 +265,7 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_
if (r < 0)
return r;
- r = sd_bus_message_enter_container(message, 'a', "(iay)");
- if (r < 0)
- return r;
-
- for (;;) {
- int family;
- size_t sz;
- const void *d;
-
- assert_cc(sizeof(int) == sizeof(int32_t));
-
- r = sd_bus_message_enter_container(message, 'r', "iay");
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- r = sd_bus_message_read(message, "i", &family);
- if (r < 0)
- return r;
-
- if (!IN_SET(family, AF_INET, AF_INET6))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
-
- r = sd_bus_message_read_array(message, 'y', &d, &sz);
- if (r < 0)
- return r;
- if (sz != FAMILY_ADDRESS_SIZE(family))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
-
- if (!dns_server_address_valid(family, d))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address");
-
- r = sd_bus_message_exit_container(message);
- if (r < 0)
- return r;
-
- if (!GREEDY_REALLOC(dns, allocated, n+1))
- return -ENOMEM;
-
- dns[n].family = family;
- memcpy(&dns[n].address, d, sz);
- n++;
- }
-
- r = sd_bus_message_exit_container(message);
+ r = bus_message_read_dns_servers(message, error, extended, &dns, &n);
if (r < 0)
return r;
@@ -272,22 +274,26 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_
NULL, true, UID_INVALID,
&l->manager->polkit_registry, error);
if (r < 0)
- return r;
- if (r == 0)
- return 1; /* Polkit will call us back */
+ goto finalize;
+ if (r == 0) {
+ r = 1; /* Polkit will call us back */
+ goto finalize;
+ }
dns_server_mark_all(l->dns_servers);
- for (i = 0; i < n; i++) {
+ for (size_t i = 0; i < n; i++) {
DnsServer *s;
- s = dns_server_find(l->dns_servers, dns[i].family, &dns[i].address, 0);
+ s = dns_server_find(l->dns_servers, dns[i]->family, &dns[i]->address, dns[i]->port, 0, dns[i]->server_name);
if (s)
dns_server_move_back_and_unmark(s);
else {
- r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i].family, &dns[i].address, 0, NULL);
- if (r < 0)
- goto clear;
+ r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i]->family, &dns[i]->address, dns[i]->port, 0, dns[i]->server_name);
+ if (r < 0) {
+ dns_server_unlink_all(l->dns_servers);
+ goto finalize;
+ }
}
}
@@ -299,13 +305,24 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_
(void) manager_write_resolv_conf(l->manager);
(void) manager_send_changed(l->manager, "DNS");
- return sd_bus_reply_method_return(message, NULL);
+ r = sd_bus_reply_method_return(message, NULL);
+
+finalize:
+ for (size_t i = 0; i < n; i++)
+ in_addr_full_free(dns[i]);
+ free(dns);
-clear:
- dns_server_unlink_all(l->dns_servers);
return r;
}
+int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return bus_link_method_set_dns_servers_internal(message, userdata, error, false);
+}
+
+int bus_link_method_set_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return bus_link_method_set_dns_servers_internal(message, userdata, error, true);
+}
+
int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Link *l = userdata;
int r;
@@ -762,7 +779,9 @@ static const sd_bus_vtable link_vtable[] = {
SD_BUS_PROPERTY("ScopesMask", "t", property_get_scopes_mask, 0, 0),
SD_BUS_PROPERTY("DNS", "a(iay)", property_get_dns, 0, 0),
+ SD_BUS_PROPERTY("DNSEx", "a(iayqs)", property_get_dns_ex, 0, 0),
SD_BUS_PROPERTY("CurrentDNSServer", "(iay)", property_get_current_dns_server, offsetof(Link, current_dns_server), 0),
+ SD_BUS_PROPERTY("CurrentDNSServerEx", "(iayqs)", property_get_current_dns_server_ex, offsetof(Link, current_dns_server), 0),
SD_BUS_PROPERTY("Domains", "a(sb)", property_get_domains, 0, 0),
SD_BUS_PROPERTY("DefaultRoute", "b", property_get_default_route, 0, 0),
SD_BUS_PROPERTY("LLMNR", "s", bus_property_get_resolve_support, offsetof(Link, llmnr_support), 0),
@@ -777,6 +796,11 @@ static const sd_bus_vtable link_vtable[] = {
SD_BUS_NO_RESULT,
bus_link_method_set_dns_servers,
SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetDNSEx",
+ SD_BUS_ARGS("a(iayqs)", addresses),
+ SD_BUS_NO_RESULT,
+ bus_link_method_set_dns_servers_ex,
+ SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_ARGS("SetDomains",
SD_BUS_ARGS("a(sb)", domains),
SD_BUS_NO_RESULT,
diff --git a/src/resolve/resolved-link-bus.h b/src/resolve/resolved-link-bus.h
index c249960024..fc85ff855c 100644
--- a/src/resolve/resolved-link-bus.h
+++ b/src/resolve/resolved-link-bus.h
@@ -11,6 +11,7 @@ extern const BusObjectImplementation link_object;
char *link_bus_path(const Link *link);
int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c
index 5eb184a10f..f52c556bd1 100644
--- a/src/resolve/resolved-link.c
+++ b/src/resolve/resolved-link.c
@@ -15,6 +15,7 @@
#include "resolved-link.h"
#include "resolved-llmnr.h"
#include "resolved-mdns.h"
+#include "socket-netlink.h"
#include "string-util.h"
#include "strv.h"
#include "tmpfile-util.h"
@@ -251,25 +252,35 @@ int link_process_rtnl(Link *l, sd_netlink_message *m) {
return 0;
}
-static int link_update_dns_server_one(Link *l, const char *name) {
+static int link_update_dns_server_one(Link *l, const char *str) {
+ _cleanup_free_ char *name = NULL;
+ int family, ifindex, r;
union in_addr_union a;
DnsServer *s;
- int family, r;
+ uint16_t port;
assert(l);
- assert(name);
+ assert(str);
- r = in_addr_from_string_auto(name, &family, &a);
+ r = in_addr_port_ifindex_name_from_string_auto(str, &family, &a, &port, &ifindex, &name);
if (r < 0)
return r;
- s = dns_server_find(l->dns_servers, family, &a, 0);
+ if (ifindex != 0 && ifindex != l->ifindex)
+ return -EINVAL;
+
+ /* By default, the port number is determined with the transaction feature level.
+ * See dns_transaction_port() and dns_server_port(). */
+ if (IN_SET(port, 53, 853))
+ port = 0;
+
+ s = dns_server_find(l->dns_servers, family, &a, port, 0, name);
if (s) {
dns_server_move_back_and_unmark(s);
return 0;
}
- return dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a, 0, NULL);
+ return dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a, port, 0, name);
}
static int link_update_dns_servers(Link *l) {
@@ -652,7 +663,9 @@ int link_update(Link *l) {
assert(l);
link_read_settings(l);
- link_load_user(l);
+ r = link_load_user(l);
+ if (r < 0)
+ return r;
if (l->llmnr_support != RESOLVE_SUPPORT_NO) {
r = manager_llmnr_start(l->manager);
@@ -730,7 +743,7 @@ DnsServer* link_set_dns_server(Link *l, DnsServer *s) {
return s;
if (s)
- log_debug("Switching to DNS server %s for interface %s.", dns_server_string(s), l->ifname);
+ log_debug("Switching to DNS server %s for interface %s.", strna(dns_server_string_full(s)), l->ifname);
dns_server_unref(l->current_dns_server);
l->current_dns_server = dns_server_ref(s);
@@ -1207,7 +1220,7 @@ int link_save_user(Link *l) {
if (server != l->dns_servers)
fputc(' ', f);
- v = dns_server_string(server);
+ v = dns_server_string_full(server);
if (!v) {
r = -ENOMEM;
goto fail;
diff --git a/src/shared/bus-get-properties.h b/src/shared/bus-get-properties.h
index d853b79d8d..81af74309d 100644
--- a/src/shared/bus-get-properties.h
+++ b/src/shared/bus-get-properties.h
@@ -3,8 +3,6 @@
#include "sd-bus.h"
-#include "sd-bus.h"
-
#include "macro.h"
int bus_property_get_bool(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
diff --git a/src/shared/bus-message-util.c b/src/shared/bus-message-util.c
new file mode 100644
index 0000000000..85e1e98e19
--- /dev/null
+++ b/src/shared/bus-message-util.c
@@ -0,0 +1,182 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "bus-message-util.h"
+
+#include "resolve-util.h"
+
+int bus_message_read_ifindex(sd_bus_message *message, sd_bus_error *error, int *ret) {
+ int ifindex, r;
+
+ assert(message);
+ assert(ret);
+
+ assert_cc(sizeof(int) == sizeof(int32_t));
+
+ r = sd_bus_message_read(message, "i", &ifindex);
+ if (r < 0)
+ return r;
+
+ if (ifindex <= 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
+
+ *ret = ifindex;
+
+ return 0;
+}
+
+int bus_message_read_family(sd_bus_message *message, sd_bus_error *error, int *ret) {
+ int family, r;
+
+ assert(message);
+ assert(ret);
+
+ assert_cc(sizeof(int) == sizeof(int32_t));
+
+ r = sd_bus_message_read(message, "i", &family);
+ if (r < 0)
+ return r;
+
+ if (!IN_SET(family, AF_INET, AF_INET6))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
+
+ *ret = family;
+ return 0;
+}
+
+int bus_message_read_in_addr_auto(sd_bus_message *message, sd_bus_error *error, int *ret_family, union in_addr_union *ret_addr) {
+ int family, r;
+ const void *d;
+ size_t sz;
+
+ assert(message);
+
+ r = sd_bus_message_read(message, "i", &family);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_array(message, 'y', &d, &sz);
+ if (r < 0)
+ return r;
+
+ if (!IN_SET(family, AF_INET, AF_INET6))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
+
+ if (sz != FAMILY_ADDRESS_SIZE(family))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
+
+ if (ret_family)
+ *ret_family = family;
+ if (ret_addr)
+ memcpy(ret_addr, d, sz);
+ return 0;
+}
+
+static int bus_message_read_dns_one(
+ sd_bus_message *message,
+ sd_bus_error *error,
+ bool extended,
+ int *ret_family,
+ union in_addr_union *ret_address,
+ uint16_t *ret_port,
+ const char **ret_server_name) {
+ const char *server_name = NULL;
+ union in_addr_union a;
+ uint16_t port = 0;
+ int family, r;
+
+ assert(message);
+ assert(ret_family);
+ assert(ret_address);
+ assert(ret_port);
+ assert(ret_server_name);
+
+ r = sd_bus_message_enter_container(message, 'r', extended ? "iayqs" : "iay");
+ if (r <= 0)
+ return r;
+
+ r = bus_message_read_in_addr_auto(message, error, &family, &a);
+ if (r < 0)
+ return r;
+
+ if (!dns_server_address_valid(family, &a))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address");
+
+ if (extended) {
+ r = sd_bus_message_read(message, "q", &port);
+ if (r < 0)
+ return r;
+
+ if (IN_SET(port, 53, 853))
+ port = 0;
+
+ r = sd_bus_message_read(message, "s", &server_name);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ *ret_family = family;
+ *ret_address = a;
+ *ret_port = port;
+ *ret_server_name = server_name;
+
+ return 1;
+}
+
+int bus_message_read_dns_servers(
+ sd_bus_message *message,
+ sd_bus_error *error,
+ bool extended,
+ struct in_addr_full ***ret_dns,
+ size_t *ret_n_dns) {
+
+ struct in_addr_full **dns = NULL;
+ size_t n = 0, allocated = 0;
+ int r;
+
+ assert(message);
+ assert(ret_dns);
+ assert(ret_n_dns);
+
+ r = sd_bus_message_enter_container(message, 'a', extended ? "(iayqs)" : "(iay)");
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ const char *server_name;
+ union in_addr_union a;
+ uint16_t port;
+ int family;
+
+ r = bus_message_read_dns_one(message, error, extended, &family, &a, &port, &server_name);
+ if (r < 0)
+ goto clear;
+ if (r == 0)
+ break;
+
+ if (!GREEDY_REALLOC(dns, allocated, n+1)) {
+ r = -ENOMEM;
+ goto clear;
+ }
+
+ r = in_addr_full_new(family, &a, port, 0, server_name, dns + n);
+ if (r < 0)
+ goto clear;
+
+ n++;
+ }
+
+ *ret_dns = TAKE_PTR(dns);
+ *ret_n_dns = n;
+ return 0;
+
+clear:
+ for (size_t i = 0; i < n; i++)
+ in_addr_full_free(dns[i]);
+ free(dns);
+
+ return r;
+}
diff --git a/src/shared/bus-message-util.h b/src/shared/bus-message-util.h
new file mode 100644
index 0000000000..98ad035ee4
--- /dev/null
+++ b/src/shared/bus-message-util.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+#include "in-addr-util.h"
+#include "socket-netlink.h"
+
+int bus_message_read_ifindex(sd_bus_message *message, sd_bus_error *error, int *ret);
+int bus_message_read_family(sd_bus_message *message, sd_bus_error *error, int *ret);
+int bus_message_read_in_addr_auto(sd_bus_message *message, sd_bus_error *error, int *ret_family, union in_addr_union *ret_addr);
+
+int bus_message_read_dns_servers(
+ sd_bus_message *message,
+ sd_bus_error *error,
+ bool extended,
+ struct in_addr_full ***ret_dns,
+ size_t *ret_n_dns);
diff --git a/src/shared/meson.build b/src/shared/meson.build
index 1868e8aede..0da733c3fe 100644
--- a/src/shared/meson.build
+++ b/src/shared/meson.build
@@ -35,6 +35,8 @@ shared_sources = files('''
bus-log-control-api.h
bus-map-properties.c
bus-map-properties.h
+ bus-message-util.c
+ bus-message-util.h
bus-object.c
bus-object.h
bus-polkit.c
diff --git a/src/shared/socket-netlink.c b/src/shared/socket-netlink.c
index 16b0e6a5c3..d72a70503a 100644
--- a/src/shared/socket-netlink.c
+++ b/src/shared/socket-netlink.c
@@ -327,68 +327,197 @@ int make_socket_fd(int log_level, const char* address, int type, int flags) {
return fd;
}
-int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret_addr, int *ret_ifindex) {
- _cleanup_free_ char *buf = NULL;
- const char *suffix;
- int r, ifindex = 0;
+int in_addr_port_ifindex_name_from_string_auto(
+ const char *s,
+ int *ret_family,
+ union in_addr_union *ret_address,
+ uint16_t *ret_port,
+ int *ret_ifindex,
+ char **ret_server_name) {
+
+ _cleanup_free_ char *buf1 = NULL, *buf2 = NULL, *name = NULL;
+ int family, ifindex = 0, r;
+ union in_addr_union a;
+ uint16_t port = 0;
+ const char *m;
assert(s);
- assert(family);
- assert(ret_addr);
- /* Similar to in_addr_from_string_auto() but also parses an optionally appended IPv6 zone suffix ("scope id")
- * if one is found. */
+ /* This accepts the following:
+ * 192.168.0.1:53#example.com
+ * [2001:4860:4860::8888]:53%eth0#example.com */
+
+ /* if ret_port is NULL, then strings with port cannot be specified.
+ * Also, if ret_server_name is NULL, then server_name cannot be specified. */
+
+ m = strchr(s, '#');
+ if (m) {
+ if (!ret_server_name)
+ return -EINVAL;
+
+ if (isempty(m + 1))
+ return -EINVAL;
+
+ name = strdup(m + 1);
+ if (!name)
+ return -ENOMEM;
+
+ s = buf1 = strndup(s, m - s);
+ if (!buf1)
+ return -ENOMEM;
+ }
+
+ m = strchr(s, '%');
+ if (m) {
+ if (isempty(m + 1))
+ return -EINVAL;
- suffix = strchr(s, '%');
- if (suffix) {
if (ret_ifindex) {
/* If we shall return the interface index, try to parse it */
- ifindex = resolve_interface(NULL, suffix + 1);
+ ifindex = resolve_interface(NULL, m + 1);
if (ifindex < 0)
return ifindex;
}
- s = buf = strndup(s, suffix - s);
- if (!buf)
+ s = buf2 = strndup(s, m - s);
+ if (!buf2)
return -ENOMEM;
}
- r = in_addr_from_string_auto(s, family, ret_addr);
- if (r < 0)
- return r;
+ m = strrchr(s, ':');
+ if (m) {
+ if (*s == '[') {
+ _cleanup_free_ char *ip_str = NULL;
+ if (!ret_port)
+ return -EINVAL;
+
+ if (*(m - 1) != ']')
+ return -EINVAL;
+
+ family = AF_INET6;
+
+ r = parse_ip_port(m + 1, &port);
+ if (r < 0)
+ return r;
+
+ ip_str = strndup(s + 1, m - s - 2);
+ if (!ip_str)
+ return -ENOMEM;
+
+ r = in_addr_from_string(family, ip_str, &a);
+ if (r < 0)
+ return r;
+ } else {
+ /* First try to parse the string as IPv6 address without port number */
+ r = in_addr_from_string(AF_INET6, s, &a);
+ if (r < 0) {
+ /* Then the input should be IPv4 address with port number */
+ _cleanup_free_ char *ip_str = NULL;
+
+ if (!ret_port)
+ return -EINVAL;
+
+ family = AF_INET;
+
+ ip_str = strndup(s, m - s);
+ if (!ip_str)
+ return -ENOMEM;
+
+ r = in_addr_from_string(family, ip_str, &a);
+ if (r < 0)
+ return r;
+
+ r = parse_ip_port(m + 1, &port);
+ if (r < 0)
+ return r;
+ } else
+ family = AF_INET6;
+ }
+ } else {
+ family = AF_INET;
+ r = in_addr_from_string(family, s, &a);
+ if (r < 0)
+ return r;
+ }
+
+ if (ret_family)
+ *ret_family = family;
+ if (ret_address)
+ *ret_address = a;
+ if (ret_port)
+ *ret_port = port;
if (ret_ifindex)
*ret_ifindex = ifindex;
+ if (ret_server_name)
+ *ret_server_name = TAKE_PTR(name);
return r;
}
-int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name) {
- _cleanup_free_ char *buf = NULL, *name = NULL;
- const char *m;
- int r;
+struct in_addr_full *in_addr_full_free(struct in_addr_full *a) {
+ if (!a)
+ return NULL;
+
+ free(a->server_name);
+ free(a->cached_server_string);
+ return mfree(a);
+}
+
+int in_addr_full_new(int family, union in_addr_union *a, uint16_t port, int ifindex, const char *server_name, struct in_addr_full **ret) {
+ _cleanup_free_ char *name = NULL;
+ struct in_addr_full *x;
+
+ assert(ret);
+
+ if (!isempty(server_name)) {
+ name = strdup(server_name);
+ if (!name)
+ return -ENOMEM;
+ }
+
+ x = new(struct in_addr_full, 1);
+ if (!x)
+ return -ENOMEM;
+
+ *x = (struct in_addr_full) {
+ .family = family,
+ .address = *a,
+ .port = port,
+ .ifindex = ifindex,
+ .server_name = TAKE_PTR(name),
+ };
+
+ *ret = x;
+ return 0;
+}
+
+int in_addr_full_new_from_string(const char *s, struct in_addr_full **ret) {
+ _cleanup_free_ char *server_name = NULL;
+ int family, ifindex, r;
+ union in_addr_union a;
+ uint16_t port;
assert(s);
- m = strchr(s, '#');
- if (m) {
- name = strdup(m+1);
- if (!name)
- return -ENOMEM;
-
- buf = strndup(s, m - s);
- if (!buf)
- return -ENOMEM;
-
- s = buf;
- }
-
- r = in_addr_ifindex_from_string_auto(s, family, ret, ifindex);
+ r = in_addr_port_ifindex_name_from_string_auto(s, &family, &a, &port, &ifindex, &server_name);
if (r < 0)
return r;
- if (server_name)
- *server_name = TAKE_PTR(name);
+ return in_addr_full_new(family, &a, port, ifindex, server_name, ret);
+}
- return r;
+const char *in_addr_full_to_string(struct in_addr_full *a) {
+ assert(a);
+
+ if (!a->cached_server_string)
+ (void) in_addr_port_ifindex_name_to_string(
+ a->family,
+ &a->address,
+ a->port,
+ a->ifindex,
+ a->server_name,
+ &a->cached_server_string);
+
+ return a->cached_server_string;
}
diff --git a/src/shared/socket-netlink.h b/src/shared/socket-netlink.h
index 35c35db52d..9517f6dd6d 100644
--- a/src/shared/socket-netlink.h
+++ b/src/shared/socket-netlink.h
@@ -20,5 +20,31 @@ int socket_address_parse_netlink(SocketAddress *a, const char *s);
bool socket_address_is(const SocketAddress *a, const char *s, int type);
bool socket_address_is_netlink(const SocketAddress *a, const char *s);
-int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex);
-int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name);
+int in_addr_port_ifindex_name_from_string_auto(
+ const char *s,
+ int *ret_family,
+ union in_addr_union *ret_address,
+ uint16_t *ret_port,
+ int *ret_ifindex,
+ char **ret_server_name);
+static inline int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name) {
+ return in_addr_port_ifindex_name_from_string_auto(s, family, ret, NULL, ifindex, server_name);
+}
+static inline int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex) {
+ return in_addr_ifindex_name_from_string_auto(s, family, ret, ifindex, NULL);
+}
+
+struct in_addr_full {
+ int family;
+ union in_addr_union address;
+ uint16_t port;
+ int ifindex;
+ char *server_name;
+ char *cached_server_string; /* Should not be handled directly, but through in_addr_full_to_string(). */
+};
+
+struct in_addr_full *in_addr_full_free(struct in_addr_full *a);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct in_addr_full*, in_addr_full_free);
+int in_addr_full_new(int family, union in_addr_union *a, uint16_t port, int ifindex, const char *server_name, struct in_addr_full **ret);
+int in_addr_full_new_from_string(const char *s, struct in_addr_full **ret);
+const char *in_addr_full_to_string(struct in_addr_full *a);
diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c
index 024e12a799..b007dd6276 100644
--- a/src/test/test-socket-util.c
+++ b/src/test/test-socket-util.c
@@ -302,6 +302,38 @@ static void test_in_addr_ifindex_name_from_string_auto(void) {
test_in_addr_ifindex_name_from_string_auto_one("fe80::18%19#another.test.com", "another.test.com");
}
+static void test_in_addr_port_ifindex_name_from_string_auto_one(const char *str, int family, uint16_t port, int ifindex, const char *server_name) {
+ _cleanup_free_ char *name = NULL, *x = NULL;
+ union in_addr_union a;
+ uint16_t p;
+ int f, i;
+
+ assert_se(in_addr_port_ifindex_name_from_string_auto(str, &f, &a, &p, &i, &name) >= 0);
+ assert_se(family == f);
+ assert_se(port == p);
+ assert_se(ifindex == i);
+ assert_se(streq_ptr(server_name, name));
+ assert_se(in_addr_port_ifindex_name_to_string(f, &a, p, i, name, &x) >= 0);
+ assert_se(streq(str, x));
+}
+
+static void test_in_addr_port_ifindex_name_from_string_auto(void) {
+ log_info("/* %s */", __func__);
+
+ test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1", AF_INET, 0, 0, NULL);
+ test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1#test.com", AF_INET, 0, 0, "test.com");
+ test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1:53", AF_INET, 53, 0, NULL);
+ test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1:53#example.com", AF_INET, 53, 0, "example.com");
+ test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18", AF_INET6, 0, 0, NULL);
+ test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18#hoge.com", AF_INET6, 0, 0, "hoge.com");
+ test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18%19", AF_INET6, 0, 19, NULL);
+ test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53", AF_INET6, 53, 0, NULL);
+ test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18%19#hoge.com", AF_INET6, 0, 19, "hoge.com");
+ test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53#hoge.com", AF_INET6, 53, 0, "hoge.com");
+ test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%19", AF_INET6, 53, 19, NULL);
+ test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%19#hoge.com", AF_INET6, 53, 19, "hoge.com");
+}
+
static void test_sockaddr_equal(void) {
union sockaddr_union a = {
.in.sin_family = AF_INET,
@@ -735,6 +767,7 @@ int main(int argc, char *argv[]) {
test_in_addr_ifindex_to_string();
test_in_addr_ifindex_from_string_auto();
test_in_addr_ifindex_name_from_string_auto();
+ test_in_addr_port_ifindex_name_from_string_auto();
test_sockaddr_equal();
diff --git a/test/test-network/conf/state-file-tests.network b/test/test-network/conf/state-file-tests.network
index 468669c6cc..1f7e7d16f0 100644
--- a/test/test-network/conf/state-file-tests.network
+++ b/test/test-network/conf/state-file-tests.network
@@ -6,7 +6,7 @@ RequiredForOnline=routable
[Network]
IPv6AcceptRA=no
-DNS=10.10.10.10 10.10.10.11
+DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com
NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org
Domains=hogehoge ~foofoo
LLMNR=no
diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py
index 2d7f915850..677e39765e 100755
--- a/test/test-network/systemd-networkd-tests.py
+++ b/test/test-network/systemd-networkd-tests.py
@@ -2601,7 +2601,7 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities):
self.assertRegex(data, r'REQUIRED_FOR_ONLINE=yes')
self.assertRegex(data, r'REQUIRED_OPER_STATE_FOR_ONLINE=routable')
self.assertRegex(data, r'NETWORK_FILE=/run/systemd/network/state-file-tests.network')
- self.assertRegex(data, r'DNS=10.10.10.10 10.10.10.11')
+ self.assertRegex(data, r'DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com \[1111:2222::3333\]:1234#ccc.com')
self.assertRegex(data, r'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org')
self.assertRegex(data, r'DOMAINS=hogehoge')
self.assertRegex(data, r'ROUTE_DOMAINS=foofoo')
@@ -2610,7 +2610,7 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities):
self.assertRegex(data, r'DNSSEC=no')
self.assertRegex(data, r'ADDRESSES=192.168.(10.10|12.12)/24 192.168.(12.12|10.10)/24')
- check_output(*resolvectl_cmd, 'dns', 'dummy98', '10.10.10.12', '10.10.10.13', env=env)
+ check_output(*resolvectl_cmd, 'dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333', env=env)
check_output(*resolvectl_cmd, 'domain', 'dummy98', 'hogehogehoge', '~foofoofoo', env=env)
check_output(*resolvectl_cmd, 'llmnr', 'dummy98', 'yes', env=env)
check_output(*resolvectl_cmd, 'mdns', 'dummy98', 'no', env=env)
@@ -2620,7 +2620,7 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities):
with open(path) as f:
data = f.read()
- self.assertRegex(data, r'DNS=10.10.10.12 10.10.10.13')
+ self.assertRegex(data, r'DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333')
self.assertRegex(data, r'NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org')
self.assertRegex(data, r'DOMAINS=hogehogehoge')
self.assertRegex(data, r'ROUTE_DOMAINS=foofoofoo')
@@ -2633,7 +2633,7 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities):
with open(path) as f:
data = f.read()
- self.assertRegex(data, r'DNS=10.10.10.12 10.10.10.13')
+ self.assertRegex(data, r'DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333')
self.assertRegex(data, r'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org')
self.assertRegex(data, r'DOMAINS=hogehogehoge')
self.assertRegex(data, r'ROUTE_DOMAINS=foofoofoo')
@@ -2646,7 +2646,7 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities):
with open(path) as f:
data = f.read()
- self.assertRegex(data, r'DNS=10.10.10.10 10.10.10.11')
+ self.assertRegex(data, r'DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com \[1111:2222::3333\]:1234#ccc.com')
self.assertRegex(data, r'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org')
self.assertRegex(data, r'DOMAINS=hogehoge')
self.assertRegex(data, r'ROUTE_DOMAINS=foofoo')