X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fresolve%2Fresolved-dns-answer.c;h=7c4ab18b5835f97ce8210976340d4365e9441268;hp=fbc282550e87d760f11ebaebf82997e672bc0ff3;hb=0c0cdb06c139b52ff103287f6909b3daa5b2dc54;hpb=faa133f3aa7a18f26563dc5d6b95898cb315c37a diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c index fbc282550..7c4ab18b5 100644 --- a/src/resolve/resolved-dns-answer.c +++ b/src/resolve/resolved-dns-answer.c @@ -20,6 +20,7 @@ ***/ #include "resolved-dns-answer.h" +#include "resolved-dns-domain.h" DnsAnswer *dns_answer_new(unsigned n) { DnsAnswer *a; @@ -65,12 +66,173 @@ DnsAnswer *dns_answer_unref(DnsAnswer *a) { } int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr) { + unsigned i; + int r; + assert(a); assert(rr); + for (i = 0; i < a->n_rrs; i++) { + r = dns_resource_record_equal(a->rrs[i], rr); + if (r < 0) + return r; + if (r > 0) { + /* Entry already exists, keep the entry with + * the higher RR, or the one with TTL 0 */ + + if (rr->ttl == 0 || (rr->ttl > a->rrs[i]->ttl && a->rrs[i]->ttl != 0)) { + dns_resource_record_ref(rr); + dns_resource_record_unref(a->rrs[i]); + a->rrs[i] = rr; + } + + return 0; + } + } + if (a->n_rrs >= a->n_allocated) return -ENOSPC; a->rrs[a->n_rrs++] = dns_resource_record_ref(rr); + return 1; +} + +int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl) { + _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->ttl = ttl; + + 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 = ttl; + + return dns_answer_add(a, soa); +} + +int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) { + unsigned i; + int r; + + assert(a); + assert(key); + + for (i = 0; i < a->n_rrs; i++) { + r = dns_resource_key_match_rr(key, a->rrs[i]); + if (r < 0) + return r; + if (r > 0) + return 1; + } + + return 0; +} + +int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret) { + unsigned i; + + assert(a); + assert(key); + assert(ret); + + /* For a SOA record we can never find a matching SOA record */ + if (key->type == DNS_TYPE_SOA) + return 0; + + for (i = 0; i < a->n_rrs; i++) { + + if (a->rrs[i]->key->class != DNS_CLASS_IN) + continue; + + if (a->rrs[i]->key->type != DNS_TYPE_SOA) + continue; + + if (dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(a->rrs[i]->key))) { + *ret = a->rrs[i]; + return 1; + } + } + return 0; } + +DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b) { + _cleanup_(dns_answer_unrefp) DnsAnswer *ret = NULL; + DnsAnswer *k; + unsigned i; + int r; + + if (a && (!b || b->n_rrs <= 0)) + return dns_answer_ref(a); + if ((!a || a->n_rrs <= 0) && b) + return dns_answer_ref(b); + + ret = dns_answer_new((a ? a->n_rrs : 0) + (b ? b->n_rrs : 0)); + if (!ret) + return NULL; + + if (a) { + for (i = 0; i < a->n_rrs; i++) { + r = dns_answer_add(ret, a->rrs[i]); + if (r < 0) + return NULL; + } + } + + if (b) { + for (i = 0; i < b->n_rrs; i++) { + r = dns_answer_add(ret, b->rrs[i]); + if (r < 0) + return NULL; + } + } + + k = ret; + ret = NULL; + + 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); +}