From 8bf52d3d17d364438191077d0750b8b80b5dc53a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 30 Jul 2014 16:30:25 +0200 Subject: [PATCH] resolved: include SOA records in LLMNR replies for non-existing RRs to allow negative caching --- src/resolve/resolved-dns-answer.c | 24 +++++++++ src/resolve/resolved-dns-answer.h | 1 + src/resolve/resolved-dns-rr.c | 10 ++++ src/resolve/resolved-dns-rr.h | 1 + src/resolve/resolved-dns-scope.c | 28 +++++++---- src/resolve/resolved-dns-zone.c | 83 +++++++++++++++++++------------ src/resolve/resolved-dns-zone.h | 2 +- 7 files changed, 107 insertions(+), 42 deletions(-) diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c index 609695935..b6883a3ab 100644 --- a/src/resolve/resolved-dns-answer.c +++ b/src/resolve/resolved-dns-answer.c @@ -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; diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h index 268bb3853..917bfced5 100644 --- a/src/resolve/resolved-dns-answer.h +++ b/src/resolve/resolved-dns-answer.h @@ -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); diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index 5097eff08..8b8858848 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -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; diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index a9d14fc22..50bb74c67 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -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); diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index b17de0c88..5141a8d80 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -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; diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c index 65dc17764..b51f503df 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -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; } diff --git a/src/resolve/resolved-dns-zone.h b/src/resolve/resolved-dns-zone.h index 89d0bbe35..5e7a1f72d 100644 --- a/src/resolve/resolved-dns-zone.h +++ b/src/resolve/resolved-dns-zone.h @@ -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); -- 2.30.2