diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index b48d8e1417..1702866a14 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -1,14 +1,19 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include + #include "sd-messages.h" #include "alloc-util.h" +#include "errno-util.h" +#include "fd-util.h" #include "resolved-bus.h" #include "resolved-dns-server.h" #include "resolved-dns-stub.h" #include "resolved-manager.h" #include "resolved-resolv-conf.h" #include "siphash24.h" +#include "socket-util.h" #include "string-table.h" #include "string-util.h" @@ -69,6 +74,7 @@ int dns_server_new( .ifindex = ifindex, .server_name = TAKE_PTR(name), .config_source = config_source, + .accessible = -1, }; dns_server_reset_features(s); @@ -1128,3 +1134,49 @@ int dns_server_dump_state_to_json(DnsServer *server, sd_json_variant **ret) { SD_JSON_BUILD_PAIR_BOOLEAN("PacketInvalid", server->packet_invalid), SD_JSON_BUILD_PAIR_BOOLEAN("PacketDoOff", server->packet_do_off)); } + +int dns_server_is_accessible(DnsServer *s) { + _cleanup_close_ int fd = -EBADF; + union sockaddr_union sa; + int r; + + assert(s); + + if (s->accessible >= 0) + return s->accessible; + + r = sockaddr_set_in_addr(&sa, s->family, &s->address, dns_server_port(s)); + if (r < 0) + return r; + + fd = socket(s->family, SOCK_DGRAM|SOCK_CLOEXEC, 0); + if (fd < 0) + return -errno; + + if (s->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &s->address)) { + /* Connecting to ipv6 link-local requires binding to an interface. */ + r = socket_bind_to_ifindex(fd, dns_server_ifindex(s)); + if (r < 0) + return r; + } + + r = RET_NERRNO(connect(fd, &sa.sa, SOCKADDR_LEN(sa))); + if (!IN_SET(r, + 0, + -ENETUNREACH, + -EHOSTDOWN, + -EHOSTUNREACH, + -ENETDOWN, + -ENETRESET, + -ENONET)) + /* If we did not receive one of the expected return values, + * then leave the accessible flag untouched. */ + return r; + + return (s->accessible = r >= 0); +} + +void dns_server_reset_accessible_all(DnsServer *first) { + LIST_FOREACH(servers, s, first) + dns_server_reset_accessible(s); +} diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h index 7d7d294c82..8915b87e95 100644 --- a/src/resolve/resolved-dns-server.h +++ b/src/resolve/resolved-dns-server.h @@ -106,6 +106,9 @@ struct DnsServer { /* Servers registered via D-Bus are not removed on reload */ ResolveConfigSource config_source; + + /* Tri-state to indicate if the DNS server is accessible. */ + int accessible; }; int dns_server_new( @@ -187,3 +190,9 @@ static inline bool dns_server_is_fallback(DnsServer *s) { } int dns_server_dump_state_to_json(DnsServer *server, sd_json_variant **ret); + +int dns_server_is_accessible(DnsServer *s); +static inline void dns_server_reset_accessible(DnsServer *s) { + s->accessible = -1; +} +void dns_server_reset_accessible_all(DnsServer *first); diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index 928137b967..1c5ad0878c 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -299,6 +299,9 @@ static int link_update_dns_servers(Link *l) { } dns_server_unlink_marked(l->dns_servers); + + dns_server_reset_accessible_all(l->dns_servers); + return 0; clear: