resolved-dns-answer.c revision 81f7fc5e841472b626698f386ed9445dac13944a
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering/***
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering This file is part of systemd.
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering Copyright 2014 Lennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering systemd is free software; you can redistribute it and/or modify it
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering under the terms of the GNU Lesser General Public License as published by
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering the Free Software Foundation; either version 2.1 of the License, or
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering (at your option) any later version.
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering systemd is distributed in the hope that it will be useful, but
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering Lesser General Public License for more details.
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering You should have received a copy of the GNU Lesser General Public License
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering***/
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering#include "alloc-util.h"
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering#include "dns-domain.h"
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering#include "resolved-dns-answer.h"
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering#include "resolved-dns-dnssec.h"
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering#include "string-util.h"
9bf3b53533cdc9b95c921b71da755401f223f765Lennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart PoetteringDnsAnswer *dns_answer_new(unsigned n) {
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering DnsAnswer *a;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering a = malloc0(offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * n);
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering if (!a)
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering return NULL;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering a->n_ref = 1;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering a->n_allocated = n;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering return a;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering}
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart PoetteringDnsAnswer *dns_answer_ref(DnsAnswer *a) {
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering if (!a)
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering return NULL;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering assert(a->n_ref > 0);
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering a->n_ref++;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering return a;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering}
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poetteringstatic void dns_answer_flush(DnsAnswer *a) {
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering DnsResourceRecord *rr;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering if (!a)
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering return;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering DNS_ANSWER_FOREACH(rr, a)
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering dns_resource_record_unref(rr);
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering a->n_rrs = 0;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering}
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart PoetteringDnsAnswer *dns_answer_unref(DnsAnswer *a) {
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering if (!a)
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering return NULL;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering assert(a->n_ref > 0);
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering if (a->n_ref == 1) {
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering dns_answer_flush(a);
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering free(a);
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering } else
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering a->n_ref--;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering return NULL;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering}
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poetteringstatic int dns_answer_add_raw(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering assert(rr);
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering if (!a)
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering return -ENOSPC;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering if (a->n_rrs >= a->n_allocated)
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering return -ENOSPC;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering a->items[a->n_rrs++] = (DnsAnswerItem) {
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering .rr = dns_resource_record_ref(rr),
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering .ifindex = ifindex,
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering .flags = flags,
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering };
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen return 1;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering}
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersenstatic int dns_answer_add_raw_all(DnsAnswer *a, DnsAnswer *source) {
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering DnsResourceRecord *rr;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering DnsAnswerFlags flags;
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt int ifindex, r;
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, source) {
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt r = dns_answer_add_raw(a, rr, ifindex, flags);
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt if (r < 0)
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering return r;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering }
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering return 0;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering}
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poetteringint dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering unsigned i;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering int r;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering assert(rr);
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering if (!a)
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering return -ENOSPC;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering if (a->n_ref > 1)
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering return -EBUSY;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering for (i = 0; i < a->n_rrs; i++) {
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering if (a->items[i].ifindex != ifindex)
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering continue;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering r = dns_resource_record_equal(a->items[i].rr, rr);
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering if (r < 0)
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering return r;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering if (r > 0) {
ef42202ac8ed27e7ff1fc90ef8bc2590046dff25Zbigniew Jędrzejewski-Szmek /* Entry already exists, keep the entry with
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering * the higher RR, or the one with TTL 0 */
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering if (rr->ttl == 0 || (rr->ttl > a->items[i].rr->ttl && a->items[i].rr->ttl != 0)) {
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering dns_resource_record_ref(rr);
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering dns_resource_record_unref(a->items[i].rr);
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering a->items[i].rr = rr;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering }
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering a->items[i].flags |= flags;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering return 0;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering }
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering }
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering return dns_answer_add_raw(a, rr, ifindex, flags);
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering}
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poetteringstatic int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) {
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering DnsResourceRecord *rr;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering DnsAnswerFlags flags;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering int ifindex, r;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, b) {
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering r = dns_answer_add(a, rr, ifindex, flags);
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering if (r < 0)
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering return r;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering }
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering return 0;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering}
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poetteringint dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering int r;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering assert(a);
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering assert(rr);
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering r = dns_answer_reserve_or_clone(a, 1);
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering if (r < 0)
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering return r;
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering return dns_answer_add(*a, rr, ifindex, flags);
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering}
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poettering
30bdd695250eeecbf0b36c1e3c90d67ca03953edLennart Poetteringint 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, 0, DNS_ANSWER_AUTHENTICATED);
}
int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
DnsAnswerFlags flags = 0, i_flags;
DnsResourceRecord *i;
bool found = false;
int r;
assert(key);
DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
r = dns_resource_key_match_rr(key, i, NULL);
if (r < 0)
return r;
if (r == 0)
continue;
if (!ret_flags)
return 1;
if (found)
flags &= i_flags;
else {
flags = i_flags;
found = true;
}
}
if (ret_flags)
*ret_flags = flags;
return found;
}
int dns_answer_contains_rr(DnsAnswer *a, DnsResourceRecord *rr, DnsAnswerFlags *ret_flags) {
DnsAnswerFlags flags = 0, i_flags;
DnsResourceRecord *i;
bool found = false;
int r;
assert(rr);
DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
r = dns_resource_record_equal(i, rr);
if (r < 0)
return r;
if (r == 0)
continue;
if (!ret_flags)
return 1;
if (found)
flags &= i_flags;
else {
flags = i_flags;
found = true;
}
}
if (ret_flags)
*ret_flags = flags;
return found;
}
int dns_answer_contains_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
DnsAnswerFlags flags = 0, i_flags;
DnsResourceRecord *i;
bool found = false;
int r;
assert(key);
DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
r = dns_resource_key_equal(i->key, key);
if (r < 0)
return r;
if (r == 0)
continue;
if (!ret_flags)
return true;
if (found)
flags &= i_flags;
else {
flags = i_flags;
found = true;
}
}
if (ret_flags)
*ret_flags = flags;
return found;
}
int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a) {
DnsResourceRecord *i;
DNS_ANSWER_FOREACH(i, a) {
if (IN_SET(i->key->type, DNS_TYPE_NSEC, DNS_TYPE_NSEC3))
return true;
}
return false;
}
int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
DnsResourceRecord *rr, *soa = NULL;
DnsAnswerFlags rr_flags, soa_flags = 0;
int r;
assert(key);
/* For a SOA record we can never find a matching SOA record */
if (key->type == DNS_TYPE_SOA)
return 0;
DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
r = dns_resource_key_match_soa(key, rr->key);
if (r < 0)
return r;
if (r > 0) {
if (soa) {
r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(soa->key));
if (r < 0)
return r;
if (r > 0)
continue;
}
soa = rr;
soa_flags = rr_flags;
}
}
if (!soa)
return 0;
if (ret)
*ret = soa;
if (flags)
*flags = soa_flags;
return 1;
}
int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
DnsResourceRecord *rr;
DnsAnswerFlags rr_flags;
int r;
assert(key);
/* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
if (key->type == DNS_TYPE_CNAME || key->type == DNS_TYPE_DNAME)
return 0;
DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
r = dns_resource_key_match_cname_or_dname(key, rr->key, NULL);
if (r < 0)
return r;
if (r > 0) {
if (ret)
*ret = rr;
if (flags)
*flags = rr_flags;
return 1;
}
}
return 0;
}
int dns_answer_merge(DnsAnswer *a, DnsAnswer *b, DnsAnswer **ret) {
_cleanup_(dns_answer_unrefp) DnsAnswer *k = NULL;
int r;
assert(ret);
if (dns_answer_size(a) <= 0) {
*ret = dns_answer_ref(b);
return 0;
}
if (dns_answer_size(b) <= 0) {
*ret = dns_answer_ref(a);
return 0;
}
k = dns_answer_new(a->n_rrs + b->n_rrs);
if (!k)
return -ENOMEM;
r = dns_answer_add_raw_all(k, a);
if (r < 0)
return r;
r = dns_answer_add_all(k, b);
if (r < 0)
return r;
*ret = k;
k = NULL;
return 0;
}
int dns_answer_extend(DnsAnswer **a, DnsAnswer *b) {
DnsAnswer *merged;
int r;
assert(a);
r = dns_answer_merge(*a, b, &merged);
if (r < 0)
return r;
dns_answer_unref(*a);
*a = merged;
return 0;
}
int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) {
bool found = false, other = false;
DnsResourceRecord *rr;
unsigned i;
int r;
assert(a);
assert(key);
/* Remove all entries matching the specified key from *a */
DNS_ANSWER_FOREACH(rr, *a) {
r = dns_resource_key_equal(rr->key, key);
if (r < 0)
return r;
if (r > 0)
found = true;
else
other = true;
if (found && other)
break;
}
if (!found)
return 0;
if (!other) {
*a = dns_answer_unref(*a); /* Return NULL for the empty answer */
return 1;
}
if ((*a)->n_ref > 1) {
_cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
DnsAnswerFlags flags;
int ifindex;
copy = dns_answer_new((*a)->n_rrs);
if (!copy)
return -ENOMEM;
DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) {
r = dns_resource_key_equal(rr->key, key);
if (r < 0)
return r;
if (r > 0)
continue;
r = dns_answer_add_raw(copy, rr, ifindex, flags);
if (r < 0)
return r;
}
dns_answer_unref(*a);
*a = copy;
copy = NULL;
return 1;
}
/* Only a single reference, edit in-place */
i = 0;
for (;;) {
if (i >= (*a)->n_rrs)
break;
r = dns_resource_key_equal((*a)->items[i].rr->key, key);
if (r < 0)
return r;
if (r > 0) {
/* Kill this entry */
dns_resource_record_unref((*a)->items[i].rr);
memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1));
(*a)->n_rrs --;
continue;
} else
/* Keep this entry */
i++;
}
return 1;
}
int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
DnsResourceRecord *rr_source;
int ifindex_source, r;
DnsAnswerFlags flags_source;
assert(a);
assert(key);
/* Copy all RRs matching the specified key from source into *a */
DNS_ANSWER_FOREACH_FULL(rr_source, ifindex_source, flags_source, source) {
r = dns_resource_key_equal(rr_source->key, key);
if (r < 0)
return r;
if (r == 0)
continue;
/* Make space for at least one entry */
r = dns_answer_reserve_or_clone(a, 1);
if (r < 0)
return r;
r = dns_answer_add(*a, rr_source, ifindex_source, flags_source|or_flags);
if (r < 0)
return r;
}
return 0;
}
int dns_answer_move_by_key(DnsAnswer **to, DnsAnswer **from, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
int r;
assert(to);
assert(from);
assert(key);
r = dns_answer_copy_by_key(to, *from, key, or_flags);
if (r < 0)
return r;
return dns_answer_remove_by_key(from, key);
}
void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
DnsAnswerItem *items;
unsigned i, start, end;
if (!a)
return;
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. */
items = newa(DnsAnswerItem, a->n_rrs);
for (i = 0; i < a->n_rrs; i++) {
if (a->items[i].rr->key->class == DNS_CLASS_IN &&
((a->items[i].rr->key->type == DNS_TYPE_A && in_addr_is_link_local(AF_INET, (union in_addr_union*) &a->items[i].rr->a.in_addr) != prefer_link_local) ||
(a->items[i].rr->key->type == DNS_TYPE_AAAA && in_addr_is_link_local(AF_INET6, (union in_addr_union*) &a->items[i].rr->aaaa.in6_addr) != prefer_link_local)))
/* Order address records that are are not preferred to the end of the array */
items[end--] = a->items[i];
else
/* Order all other records to the beginning of the array */
items[start++] = a->items[i];
}
assert(start == end+1);
memcpy(a->items, items, sizeof(DnsAnswerItem) * a->n_rrs);
}
int dns_answer_reserve(DnsAnswer **a, unsigned n_free) {
DnsAnswer *n;
assert(a);
if (n_free <= 0)
return 0;
if (*a) {
unsigned ns;
if ((*a)->n_ref > 1)
return -EBUSY;
ns = (*a)->n_rrs + n_free;
if ((*a)->n_allocated >= ns)
return 0;
/* Allocate more than we need */
ns *= 2;
n = realloc(*a, offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * ns);
if (!n)
return -ENOMEM;
n->n_allocated = ns;
} else {
n = dns_answer_new(n_free);
if (!n)
return -ENOMEM;
}
*a = n;
return 0;
}
int dns_answer_reserve_or_clone(DnsAnswer **a, unsigned n_free) {
_cleanup_(dns_answer_unrefp) DnsAnswer *n = NULL;
int r;
assert(a);
/* Tries to extend the DnsAnswer object. And if that's not
* possibly, since we are not the sole owner, then allocate a
* new, appropriately sized one. Either way, after this call
* the object will only have a single reference, and has room
* for at least the specified number of RRs. */
r = dns_answer_reserve(a, n_free);
if (r != -EBUSY)
return r;
assert(*a);
n = dns_answer_new(((*a)->n_rrs + n_free) * 2);
if (!n)
return -ENOMEM;
r = dns_answer_add_raw_all(n, *a);
if (r < 0)
return r;
dns_answer_unref(*a);
*a = n;
n = NULL;
return 0;
}
void dns_answer_dump(DnsAnswer *answer, FILE *f) {
DnsResourceRecord *rr;
DnsAnswerFlags flags;
int ifindex, r;
if (!f)
f = stdout;
DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
_cleanup_free_ char *t = NULL;
fputc('\t', f);
r = dns_resource_record_to_string(rr, &t);
if (r < 0) {
log_oom();
continue;
}
fputs(t, f);
if (ifindex != 0 || flags & (DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHEABLE|DNS_ANSWER_SHARED_OWNER))
fputs("\t;", f);
if (ifindex != 0)
printf(" ifindex=%i", ifindex);
if (flags & DNS_ANSWER_AUTHENTICATED)
fputs(" authenticated", f);
if (flags & DNS_ANSWER_CACHEABLE)
fputs(" cachable", f);
if (flags & DNS_ANSWER_SHARED_OWNER)
fputs(" shared-owner", f);
fputc('\n', f);
}
}