]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: rework what ResolveHostname() with family == AF_UNSPEC means
authorLennart Poettering <lennart@poettering.net>
Sun, 31 Jan 2016 23:00:01 +0000 (00:00 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 1 Feb 2016 21:18:15 +0000 (22:18 +0100)
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.

src/resolve/resolved-bus.c
src/resolve/resolved-dns-query.c
src/resolve/resolved-dns-query.h
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-scope.h
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-link.c
src/resolve/resolved-link.h
src/resolve/resolved-manager.c
src/resolve/resolved-manager.h

index 834ae837de3cf6ec17eb0d380619edf1d75e4f75..251e7a50a4bb65e53b9d89a3e96d2562fd2e62a9 100644 (file)
@@ -281,6 +281,7 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata,
         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)
index a00851658e4661544c263fffe4628c4dfa4ae5b7..06d30d78638e75c241af148a0720ed6db7c421e2 100644 (file)
@@ -21,6 +21,7 @@
 
 #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"
@@ -161,6 +162,7 @@ static int dns_query_candidate_go(DnsQueryCandidate *c) {
         DnsTransaction *t;
         Iterator i;
         int r;
+        unsigned n = 0;
 
         assert(c);
 
@@ -172,8 +174,14 @@ static int dns_query_candidate_go(DnsQueryCandidate *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;
 }
 
@@ -222,6 +230,31 @@ static DnsTransactionState dns_query_candidate_state(DnsQueryCandidate *c) {
         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;
@@ -236,14 +269,24 @@ static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
         /* 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;
 
index c7e4ce9a005546d6c24797d5dee93e76b378b1e2..75c2c14c1f3c4c8f993f3740a4eb631e25bfb3ab 100644 (file)
@@ -69,6 +69,10 @@ struct DnsQuery {
         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;
 
index ac4887abea4cc7e5fda2cc5f768dae34540450cf..03239794ee059564027c9867fb7525ef4f913618 100644 (file)
@@ -490,7 +490,9 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
         }
 }
 
-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);
 
@@ -498,6 +500,9 @@ int dns_scope_good_key(DnsScope *s, DnsResourceKey *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
@@ -519,13 +524,11 @@ int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
         /* 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) {
@@ -1017,9 +1020,6 @@ bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name) {
 }
 
 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. */
@@ -1027,10 +1027,5 @@ bool dns_scope_network_good(DnsScope *s) {
         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);
 }
index f9b63d56d96f802d5134e1cec51efdb3058a44ac..05b8d66de0f21ee15de797be7daad7b89e96c8fc 100644 (file)
@@ -87,7 +87,7 @@ int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *add
 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);
index 637b99aaa522121a4d4c3a627217151af4c1409e..501f13063e40d7b897689f8bea568b029a0f4c13 100644 (file)
@@ -1411,12 +1411,6 @@ static int dns_transaction_make_packet(DnsTransaction *t) {
         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;
@@ -1498,13 +1492,6 @@ int dns_transaction_go(DnsTransaction *t) {
 
         /* 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;
 
index 37dd4a6e786f61d8b4ea2b9d6e6f856babdf06e8..7412e6462296bdc1a781654f913e35d504d557f9 100644 (file)
@@ -515,14 +515,17 @@ int link_update_monitor(Link *l) {
         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;
@@ -530,7 +533,7 @@ bool link_relevant(Link *l, int family, bool multicast) {
         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;
 
@@ -548,7 +551,7 @@ bool link_relevant(Link *l, int family, bool multicast) {
                 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;
@@ -692,7 +695,7 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
         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) {
@@ -749,7 +752,7 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
         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) {
@@ -826,13 +829,13 @@ int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m) {
         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;
index 3b6aafb8f0c2f19cacd3becf052395007d203f6d..29e7b72247679f6f29a2a20a9dbea356f5e5db2f 100644 (file)
@@ -89,7 +89,7 @@ int link_new(Manager *m, Link **ret, int ifindex);
 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);
 
@@ -107,7 +107,7 @@ bool link_dnssec_supported(Link *l);
 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);
index fbd188c2acf23ae3be09e5d2847408b7d2c4f9e0..f1dbda1a6a695bd86d760b9760e9bb6dd7a0754c 100644 (file)
@@ -1226,3 +1226,18 @@ void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResource
 
         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;
+}
index 1af49c8fb9ce21655b27ca2f55f6148867ad266f..e2c539d3d24a9deea6d1f9a8d29ad0da73e14586 100644 (file)
@@ -169,3 +169,5 @@ DnssecMode manager_get_dnssec_mode(Manager *m);
 bool manager_dnssec_supported(Manager *m);
 
 void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResourceKey *key);
+
+bool manager_routable(Manager *m, int family);