]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: add a helper to check if DNS server is accessible
authorNick Rosbrook <enr0n@ubuntu.com>
Tue, 15 Oct 2024 20:30:52 +0000 (16:30 -0400)
committerNick Rosbrook <enr0n@ubuntu.com>
Mon, 27 Jan 2025 14:32:24 +0000 (09:32 -0500)
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.

src/resolve/resolved-dns-server.c
src/resolve/resolved-dns-server.h
src/resolve/resolved-link.c

index b48d8e141784e77ed9af9ab4cae372ee0496adc9..1702866a1435f897da6e5db859c23b087278c054 100644 (file)
@@ -1,14 +1,19 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include <net/if_arp.h>
+
 #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);
+}
index 7d7d294c8249f17740d614024157407659a9f51e..8915b87e957bc97d8b41c8c43610e2320bb03499 100644 (file)
@@ -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);
index 928137b967a70b26c2ea82da662fd88366946dad..1c5ad0878c901145b1a365b9074e6ced04462fc2 100644 (file)
@@ -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: