chiark / gitweb /
resolved: when answer A or AAAA questions, order responses by whether addresses are...
authorLennart Poettering <lennart@poettering.net>
Tue, 29 Jul 2014 22:48:59 +0000 (00:48 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 29 Jul 2014 22:48:59 +0000 (00:48 +0200)
src/network/networkd-address.c
src/network/networkd-link.c
src/resolve/resolved-dns-answer.c
src/resolve/resolved-dns-answer.h
src/resolve/resolved-dns-scope.c
src/shared/in-addr-util.c
src/shared/in-addr-util.h

index 539bd98b52c98f1a5b5e5a41b0e14bf90d5876b7..1b2d7d52ff433cde261736fb3239c9da5bd305ce 100644 (file)
@@ -243,7 +243,7 @@ static int address_acquire(Link *link, Address *original, Address **ret) {
         assert(ret);
 
         /* Something useful was configured? just use it */
-        if (in_addr_null(original->family, &original->in_addr) <= 0)
+        if (in_addr_is_null(original->family, &original->in_addr) <= 0)
                 return 0;
 
         /* The address is configured to be 0.0.0.0 or [::] by the user?
@@ -345,7 +345,7 @@ int address_configure(Address *address, Link *link,
                 return r;
         }
 
-        if (!in_addr_null(address->family, &address->in_addr_peer)) {
+        if (!in_addr_is_null(address->family, &address->in_addr_peer)) {
                 if (address->family == AF_INET)
                         r = sd_rtnl_message_append_in_addr(req, IFA_ADDRESS, &address->in_addr_peer.in);
                 else if (address->family == AF_INET6)
index 7db71c0b70a97b0c5514529dccac86f90d4192af..87bdd85644dc480ae643426a09a2f1f172bd8653 100644 (file)
@@ -283,7 +283,7 @@ static Address* link_find_dhcp_server_address(Link *link) {
                 if (address->family != AF_INET)
                         continue;
 
-                if (in_addr_null(address->family, &address->in_addr))
+                if (in_addr_is_null(address->family, &address->in_addr))
                         continue;
 
                 return address;
index d90766452747adfb0d8dcfaa886be5d1fee8f578..93e51fc2488662b9ddb471b3f3583732029caf8f 100644 (file)
@@ -175,3 +175,34 @@ DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b) {
 
         return k;
 }
+
+void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
+        DnsResourceRecord **rrs;
+        unsigned i, start, end;
+        assert(a);
+
+        if (a->n_rrs <= 1)
+                return;
+
+        start = 0;
+        end = a->n_rrs-1;
+
+        /* RFC 4795, Section 2.6 suggests we should order entries
+         * depending on whether the sender is a link-local address. */
+
+        rrs = newa(DnsResourceRecord*, a->n_rrs);
+        for (i = 0; i < a->n_rrs; i++) {
+
+                if (a->rrs[i]->key->class == DNS_CLASS_IN &&
+                    ((a->rrs[i]->key->type == DNS_TYPE_A && in_addr_is_link_local(AF_INET, (union in_addr_union*) &a->rrs[i]->a.in_addr) != prefer_link_local) ||
+                     (a->rrs[i]->key->type == DNS_TYPE_AAAA && in_addr_is_link_local(AF_INET6, (union in_addr_union*) &a->rrs[i]->aaaa.in6_addr) != prefer_link_local)))
+                        /* Order address records that are are not preferred to the end of the array */
+                        rrs[end--] = a->rrs[i];
+                else
+                        /* Order all other records to the beginning of the array */
+                        rrs[start++] = a->rrs[i];
+        }
+
+        assert(start == end+1);
+        memcpy(a->rrs, rrs, sizeof(DnsResourceRecord*) * a->n_rrs);
+}
index 135a421f298a3af1a02b5e89b91ee62cad85473f..268bb385376407a436cea5b1f8811c8841d716b0 100644 (file)
@@ -42,5 +42,6 @@ int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key);
 int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret);
 
 DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b);
+void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref);
index b975ac409230797e89915cf6cf64cf06da2e981e..b17de0c88fdd3f6febf869b1c05496d1b77af3ca 100644 (file)
@@ -483,6 +483,8 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
         if (r == 0)
                 return;
 
+        dns_answer_order_by_scope(answer, in_addr_is_link_local(p->family, &p->sender) > 0);
+
         r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, p->question, answer, &reply);
         if (r < 0) {
                 log_debug("Failed to build reply packet: %s", strerror(-r));
index e9a99177fcaf424923827db9074a06ad38820555..459f8461738f935fc0c09af5ce6e70e00af3c5d6 100644 (file)
@@ -23,7 +23,7 @@
 
 #include "in-addr-util.h"
 
-int in_addr_null(int family, const union in_addr_union *u) {
+int in_addr_is_null(int family, const union in_addr_union *u) {
         assert(u);
 
         if (family == AF_INET)
@@ -39,6 +39,17 @@ int in_addr_null(int family, const union in_addr_union *u) {
         return -EAFNOSUPPORT;
 }
 
+int in_addr_is_link_local(int family, const union in_addr_union *u) {
+        assert(u);
+
+        if (family == AF_INET)
+                return (be32toh(u->in.s_addr) & 0xFFFF0000) == (169U << 24 | 254U << 16);
+
+        if (family == AF_INET6)
+                return IN6_IS_ADDR_LINKLOCAL(&u->in6);
+
+        return -EAFNOSUPPORT;
+}
 
 int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) {
         assert(a);
index cff2c321ed980b2191d84f249fb5e1da24d63874..7d1d6baa27ec1f574b4412a0479a0348f4a12649 100644 (file)
@@ -31,7 +31,8 @@ union in_addr_union {
         struct in6_addr in6;
 };
 
-int in_addr_null(int family, const union in_addr_union *u);
+int in_addr_is_null(int family, const union in_addr_union *u);
+int in_addr_is_link_local(int family, const union in_addr_union *u);
 int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b);
 int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen);
 int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen);