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.
This commit is contained in:
Nick Rosbrook
2024-10-15 16:30:52 -04:00
parent 428c99997d
commit b9335a329d
3 changed files with 64 additions and 0 deletions

View 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);
}

View 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);

View 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: