Previously, if a hostanem is resolved with AF_UNSPEC specified, this would be used as indication to resolve both an
AF_INET and an AF_INET6 address. With this change this logic is altered: an AF_INET address is only resolved if there's
actually a routable IPv4 address on the specific interface, and similar an AF_INET6 address is only resolved if there's
a routable IPv6 address. With this in place, it's ensured that the returned data is actually connectable by
applications. This logic mimics glibc's resolver behaviour.
Note that if the client asks explicitly for AF_INET or AF_INET6 it will get what it asked for.
This also simplifies the logic how it is determined whether a specific lookup shall take place on a scope.
Specifically, the checks with dns_scope_good_key() are now moved out of the transaction code and into the query code,
so that we don't even create a transaction object on a specific scope if we cannot execute the resolution on it anyway.
q->request = sd_bus_message_ref(message);
q->request_family = family;
q->complete = bus_method_resolve_hostname_complete;
+ q->suppress_unroutable_family = family == AF_UNSPEC;
r = dns_query_bus_track(q, message);
if (r < 0)
#include "alloc-util.h"
#include "dns-domain.h"
+#include "dns-type.h"
#include "hostname-util.h"
#include "local-addresses.h"
#include "resolved-dns-query.h"
DnsTransaction *t;
Iterator i;
int r;
+ unsigned n = 0;
assert(c);
r = dns_transaction_go(t);
if (r < 0)
return r;
+
+ n++;
}
+ /* If there was nothing to start, then let's proceed immediately */
+ if (n == 0)
+ dns_query_candidate_notify(c);
+
return 0;
}
return state;
}
+static bool dns_query_candidate_is_routable(DnsQueryCandidate *c, uint16_t type) {
+ int family;
+
+ assert(c);
+
+ /* Checks whether the specified RR type matches an address family that is routable on the link(s) the scope of
+ * this candidate belongs to. Specifically, whether there's a routable IPv4 address on it if we query an A RR,
+ * or a routable IPv6 address if we query an AAAA RR. */
+
+ if (!c->query->suppress_unroutable_family)
+ return true;
+
+ if (c->scope->protocol != DNS_PROTOCOL_DNS)
+ return true;
+
+ family = dns_type_to_af(type);
+ if (family < 0)
+ return true;
+
+ if (c->scope->link)
+ return link_relevant(c->scope->link, family, false);
+ else
+ return manager_routable(c->scope->manager, family);
+}
+
static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
DnsQuestion *question;
DnsResourceKey *key;
/* Create one transaction per question key */
DNS_QUESTION_FOREACH(key, question) {
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *new_key = NULL;
+ DnsResourceKey *qkey;
+
+ if (!dns_query_candidate_is_routable(c, key->type))
+ continue;
if (c->search_domain) {
r = dns_resource_key_new_append_suffix(&new_key, key, c->search_domain->name);
if (r < 0)
goto fail;
- }
- r = dns_query_candidate_add_transaction(c, new_key ?: key);
+ qkey = new_key;
+ } else
+ qkey = key;
+
+ if (!dns_scope_good_key(c->scope, qkey))
+ continue;
+
+ r = dns_query_candidate_add_transaction(c, qkey);
if (r < 0)
goto fail;
uint64_t flags;
int ifindex;
+ /* If true, A or AAAA RR lookups will be suppressed on links with no routable address of the matching address
+ * family */
+ bool suppress_unroutable_family;
+
DnsTransactionState state;
unsigned n_cname_redirects;
}
}
-int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
+bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key) {
+ int key_family;
+
assert(s);
assert(key);
* this scope. Note that this call assumes as fully qualified
* name, i.e. the search suffixes already appended. */
+ if (key->class != DNS_CLASS_IN)
+ return false;
+
if (s->protocol == DNS_PROTOCOL_DNS) {
/* On classic DNS, looking up non-address RRs is always
/* On mDNS and LLMNR, send A and AAAA queries only on the
* respective scopes */
- if (s->family == AF_INET && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_AAAA)
- return false;
-
- if (s->family == AF_INET6 && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_A)
- return false;
+ key_family = dns_type_to_af(key->type);
+ if (key_family < 0)
+ return true;
- return true;
+ return key_family == s->family;
}
static int dns_scope_multicast_membership(DnsScope *s, bool b, struct in_addr in, struct in6_addr in6) {
}
bool dns_scope_network_good(DnsScope *s) {
- Iterator i;
- Link *l;
-
/* Checks whether the network is in good state for lookups on this scope. For mDNS/LLMNR/Classic DNS scopes
* bound to links this is easy, as they don't even exist if the link isn't in a suitable state. For the global
* DNS scope we check whether there are any links that are up and have an address. */
if (s->link)
return true;
- HASHMAP_FOREACH(l, s->manager->links, i) {
- if (link_relevant(l, AF_UNSPEC, false))
- return true;
- }
-
- return false;
+ return manager_routable(s->manager, AF_UNSPEC);
}
int dns_scope_socket_udp(DnsScope *s, DnsServer *server, uint16_t port);
DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain);
-int dns_scope_good_key(DnsScope *s, DnsResourceKey *key);
+bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key);
DnsServer *dns_scope_get_dns_server(DnsScope *s);
void dns_scope_next_dns_server(DnsScope *s);
if (r < 0)
return r;
- r = dns_scope_good_key(t->scope, t->key);
- if (r < 0)
- return r;
- if (r == 0)
- return -EDOM;
-
r = dns_packet_append_key(p, t->key, NULL);
if (r < 0)
return r;
/* Otherwise, we need to ask the network */
r = dns_transaction_make_packet(t);
- if (r == -EDOM) {
- /* Not the right request to make on this network?
- * (i.e. an A request made on IPv6 or an AAAA request
- * made on IPv4, on LLMNR or mDNS.) */
- dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
- return 0;
- }
if (r < 0)
return r;
return 0;
}
-bool link_relevant(Link *l, int family, bool multicast) {
+bool link_relevant(Link *l, int family, bool local_multicast) {
_cleanup_free_ char *state = NULL;
LinkAddress *a;
assert(l);
- /* A link is relevant for multicast traffic if it isn't a loopback or pointopoint device, has a link beat, can
- * do multicast and has at least one relevant IP address */
+ /* A link is relevant for local multicast traffic if it isn't a loopback or pointopoint device, has a link
+ * beat, can do multicast and has at least one link-local (or better) IP address.
+ *
+ * A link is relevant for non-multicast traffic if it isn't a loopback device, has a link beat, and has at
+ * least one routable address.*/
if (l->flags & (IFF_LOOPBACK|IFF_DORMANT))
return false;
if ((l->flags & (IFF_UP|IFF_LOWER_UP)) != (IFF_UP|IFF_LOWER_UP))
return false;
- if (multicast) {
+ if (local_multicast) {
if (l->flags & IFF_POINTOPOINT)
return false;
return false;
LIST_FOREACH(addresses, a, l->addresses)
- if ((family == AF_UNSPEC || a->family == family) && link_address_relevant(a))
+ if ((family == AF_UNSPEC || a->family == family) && link_address_relevant(a, local_multicast))
return true;
return false;
if (a->family == AF_INET) {
if (!force_remove &&
- link_address_relevant(a) &&
+ link_address_relevant(a, true) &&
a->link->llmnr_ipv4_scope &&
a->link->llmnr_support == RESOLVE_SUPPORT_YES &&
a->link->manager->llmnr_support == RESOLVE_SUPPORT_YES) {
if (a->family == AF_INET6) {
if (!force_remove &&
- link_address_relevant(a) &&
+ link_address_relevant(a, true) &&
a->link->llmnr_ipv6_scope &&
a->link->llmnr_support == RESOLVE_SUPPORT_YES &&
a->link->manager->llmnr_support == RESOLVE_SUPPORT_YES) {
return 0;
}
-bool link_address_relevant(LinkAddress *a) {
+bool link_address_relevant(LinkAddress *a, bool local_multicast) {
assert(a);
if (a->flags & (IFA_F_DEPRECATED|IFA_F_TENTATIVE))
return false;
- if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
+ if (a->scope >= (local_multicast ? RT_SCOPE_HOST : RT_SCOPE_LINK))
return false;
return true;
Link *link_free(Link *l);
int link_update_rtnl(Link *l, sd_netlink_message *m);
int link_update_monitor(Link *l);
-bool link_relevant(Link *l, int family, bool multicast);
+bool link_relevant(Link *l, int family, bool local_multicast);
LinkAddress* link_find_address(Link *l, int family, const union in_addr_union *in_addr);
void link_add_rrs(Link *l, bool force_remove);
int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr);
LinkAddress *link_address_free(LinkAddress *a);
int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m);
-bool link_address_relevant(LinkAddress *l);
+bool link_address_relevant(LinkAddress *l, bool local_multicast);
void link_address_add_rrs(LinkAddress *a, bool force_remove);
DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_free);
m->n_dnssec_verdict[verdict]++;
}
+
+bool manager_routable(Manager *m, int family) {
+ Iterator i;
+ Link *l;
+
+ assert(m);
+
+ /* Returns true if the host has at least one interface with a routable address of the specified type */
+
+ HASHMAP_FOREACH(l, m->links, i)
+ if (link_relevant(l, family, false))
+ return true;
+
+ return false;
+}
bool manager_dnssec_supported(Manager *m);
void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResourceKey *key);
+
+bool manager_routable(Manager *m, int family);