]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: include SOA records in LLMNR replies for non-existing RRs to allow negative...
authorLennart Poettering <lennart@poettering.net>
Wed, 30 Jul 2014 14:30:25 +0000 (16:30 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 30 Jul 2014 14:47:21 +0000 (16:47 +0200)
src/resolve/resolved-dns-answer.c
src/resolve/resolved-dns-answer.h
src/resolve/resolved-dns-rr.c
src/resolve/resolved-dns-rr.h
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-zone.c
src/resolve/resolved-dns-zone.h

index 60969593559ed7cb1939f134cbc66dbbb65e2847..b6883a3abaa057b8c97a557e120a60486a790f1d 100644 (file)
@@ -97,6 +97,30 @@ int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr) {
         return 1;
 }
 
+int dns_answer_add_soa(DnsAnswer *a, const char *name) {
+        _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *soa = NULL;
+
+        soa = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_SOA, name);
+        if (!soa)
+                return -ENOMEM;
+
+        soa->soa.mname = strdup(name);
+        if (!soa->soa.mname)
+                return -ENOMEM;
+
+        soa->soa.rname = strappend("root.", name);
+        if (!soa->soa.rname)
+                return -ENOMEM;
+
+        soa->soa.serial = 1;
+        soa->soa.refresh = 1;
+        soa->soa.retry = 1;
+        soa->soa.expire = 1;
+        soa->soa.minimum = 1;
+
+        return dns_answer_add(a, soa);
+}
+
 int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) {
         unsigned i;
         int r;
index 268bb385376407a436cea5b1f8811c8841d716b0..917bfced5f7897f61ec99cd675b9b25b4efbfba8 100644 (file)
@@ -38,6 +38,7 @@ DnsAnswer *dns_answer_ref(DnsAnswer *a);
 DnsAnswer *dns_answer_unref(DnsAnswer *a);
 
 int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr);
+int dns_answer_add_soa(DnsAnswer *a, const char *name);
 int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key);
 int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret);
 
index 5097eff0838f36c24b46106bc5b4ce1705912916..8b8858848c15543619514fff88551b3e952a57d9 100644 (file)
@@ -172,6 +172,16 @@ DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
         return rr;
 }
 
+DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name) {
+        _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+
+        key = dns_resource_key_new(class, type, name);
+        if (!key)
+                return NULL;
+
+        return dns_resource_record_new(key);
+}
+
 DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr) {
         if (!rr)
                 return NULL;
index a9d14fc22f9805aa68d86830b16e09cbc820da45..50bb74c67bd8d10b4ab3a845e8672f5a897454bc 100644 (file)
@@ -140,6 +140,7 @@ int dns_resource_key_compare_func(const void *a, const void *b);
 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceKey*, dns_resource_key_unref);
 
 DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key);
+DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name);
 DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr);
 DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr);
 int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name);
index b17de0c88fdd3f6febf869b1c05496d1b77af3ca..5141a8d8047920a37c8e071848c8c0c6002f700b 100644 (file)
@@ -389,14 +389,14 @@ int dns_scope_good_dns_server(DnsScope *s, int family, const union in_addr_union
                 return !!manager_find_dns_server(s->manager, family, address);
 }
 
-static int dns_scope_make_reply_packet(DnsScope *s, uint16_t id, int rcode, DnsQuestion *q, DnsAnswer *a, DnsPacket **ret) {
+static int dns_scope_make_reply_packet(DnsScope *s, uint16_t id, int rcode, DnsQuestion *q, DnsAnswer *answer, DnsAnswer *soa, DnsPacket **ret) {
         _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
         unsigned i;
         int r;
 
         assert(s);
 
-        if (q->n_keys <= 0 && a->n_rrs <= 0)
+        if (q->n_keys <= 0 && answer->n_rrs <= 0 && soa->n_rrs <= 0)
                 return -EINVAL;
 
         r = dns_packet_new(&p, s->protocol, 0);
@@ -425,14 +425,24 @@ static int dns_scope_make_reply_packet(DnsScope *s, uint16_t id, int rcode, DnsQ
                 DNS_PACKET_HEADER(p)->qdcount = htobe16(q->n_keys);
         }
 
-        if (a) {
-                for (i = 0; i < a->n_rrs; i++) {
-                        r = dns_packet_append_rr(p, a->rrs[i], NULL);
+        if (answer) {
+                for (i = 0; i < answer->n_rrs; i++) {
+                        r = dns_packet_append_rr(p, answer->rrs[i], NULL);
                         if (r < 0)
                                 return r;
                 }
 
-                DNS_PACKET_HEADER(p)->ancount = htobe16(a->n_rrs);
+                DNS_PACKET_HEADER(p)->ancount = htobe16(answer->n_rrs);
+        }
+
+        if (soa) {
+                for (i = 0; i < soa->n_rrs; i++) {
+                        r = dns_packet_append_rr(p, soa->rrs[i], NULL);
+                        if (r < 0)
+                                return r;
+                }
+
+                DNS_PACKET_HEADER(p)->arcount = htobe16(soa->n_rrs);
         }
 
         *ret = p;
@@ -443,7 +453,7 @@ static int dns_scope_make_reply_packet(DnsScope *s, uint16_t id, int rcode, DnsQ
 
 void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
         _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
-        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
         int r, fd;
 
         assert(s);
@@ -475,7 +485,7 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
                 return;
         }
 
-        r = dns_zone_lookup(&s->zone, p->question, &answer);
+        r = dns_zone_lookup(&s->zone, p->question, &answer, &soa);
         if (r < 0) {
                 log_debug("Failed to lookup key: %s", strerror(-r));
                 return;
@@ -485,7 +495,7 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
 
         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);
+        r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, p->question, answer, soa, &reply);
         if (r < 0) {
                 log_debug("Failed to build reply packet: %s", strerror(-r));
                 return;
index 65dc17764b37b6c3984fff93ce3c844dd37626dc..b51f503df8c1c39ebd852e389ba80aef89b62f66 100644 (file)
@@ -192,21 +192,23 @@ int dns_zone_put(DnsZone *z, DnsResourceRecord *rr) {
         return 0;
 }
 
-int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret) {
-        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
-        bool has_other_rrs = false;
-        unsigned i, n = 0;
+int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswer **ret_soa) {
+        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
+        unsigned i, n_answer = 0, n_soa = 0;
         int r;
 
         assert(z);
         assert(q);
-        assert(ret);
+        assert(ret_answer);
+        assert(ret_soa);
 
         if (q->n_keys <= 0) {
-                *ret = NULL;
+                *ret_answer = NULL;
+                *ret_soa = NULL;
                 return 0;
         }
 
+        /* First iteration, count what we have */
         for (i = 0; i < q->n_keys; i++) {
                 DnsZoneItem *j;
 
@@ -220,40 +222,46 @@ int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret) {
 
                         j = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]));
                         LIST_FOREACH(by_name, j, j) {
-                                has_other_rrs = true;
-
                                 k = dns_resource_key_match_rr(q->keys[i], j->rr);
                                 if (k < 0)
                                         return k;
                                 if (k == 0)
-                                        continue;
-
-                                n++;
+                                        n_soa++;
+                                else
+                                        n_answer++;
                         }
 
                 } else {
                         j = hashmap_get(z->by_key, q->keys[i]);
-                        if (!j) {
+                        if (j) {
+                                LIST_FOREACH(by_key, j, j)
+                                        n_answer++;
+                        } else {
                                 if (hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i])))
-                                        has_other_rrs = true;
-
-                                continue;
+                                        n_soa ++;
                         }
-
-                        LIST_FOREACH(by_key, j, j)
-                                n++;
                 }
         }
 
-        if (n <= 0) {
-                *ret = NULL;
-                return has_other_rrs;
+        if (n_answer <= 0 && n_soa <= 0) {
+                *ret_answer = NULL;
+                *ret_soa = NULL;
+                return 0;
         }
 
-        answer = dns_answer_new(n);
-        if (!answer)
-                return -ENOMEM;
+        if (n_answer > 0) {
+                answer = dns_answer_new(n_answer);
+                if (!answer)
+                        return -ENOMEM;
+        }
 
+        if (n_soa > 0) {
+                soa = dns_answer_new(n_soa);
+                if (!soa)
+                        return -ENOMEM;
+        }
+
+        /* Second iteration, actually add the RRs to the answers */
         for (i = 0; i < q->n_keys; i++) {
                 DnsZoneItem *j;
 
@@ -267,25 +275,36 @@ int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret) {
                                 if (k < 0)
                                         return k;
                                 if (k == 0)
-                                        continue;
-
-                                r = dns_answer_add(answer, j->rr);
+                                        r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(q->keys[i]));
+                                else
+                                        r = dns_answer_add(answer, j->rr);
                                 if (r < 0)
                                         return r;
                         }
                 } else {
 
                         j = hashmap_get(z->by_key, q->keys[i]);
-                        LIST_FOREACH(by_key, j, j) {
-                                r = dns_answer_add(answer, j->rr);
-                                if (r < 0)
-                                        return r;
+                        if (j) {
+                                LIST_FOREACH(by_key, j, j) {
+                                        r = dns_answer_add(answer, j->rr);
+                                        if (r < 0)
+                                                return r;
+                                }
+                        } else {
+                                if (hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]))) {
+                                        r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(q->keys[i]));
+                                        if (r < 0)
+                                                return r;
+                                }
                         }
                 }
         }
 
-        *ret = answer;
+        *ret_answer = answer;
         answer = NULL;
 
+        *ret_soa = soa;
+        soa = NULL;
+
         return 1;
 }
index 89d0bbe351174b0132cfd543d821a631383453e8..5e7a1f72d01f455a0779dd16afe35fcd8b48fd36 100644 (file)
@@ -37,4 +37,4 @@ void dns_zone_flush(DnsZone *z);
 int dns_zone_put(DnsZone *z, DnsResourceRecord *rr);
 void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr);
 
-int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **answer);
+int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **answer, DnsAnswer **soa);