From: Nick Rosbrook Date: Tue, 15 Oct 2024 20:30:52 +0000 (-0400) Subject: resolved: add a helper to check if DNS server is accessible X-Git-Tag: v258-rc1~1462^2~6 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b9335a329db0ea7e2aa4aa9ca1853abb770cdb93;p=thirdparty%2Fsystemd.git resolved: add a helper to check if DNS server is accessible We check this by opening a UDP socket and attempting to connect. We do not send any traffic on it, but this will tell us if there are routes to the DNS server. This will be used in a later commit. --- diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index b48d8e14178..1702866a143 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 7d7d294c824..8915b87e957 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 928137b967a..1c5ad0878c9 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: