mirror of
https://github.com/systemd/systemd.git
synced 2024-11-01 00:51:24 +03:00
Merge pull request #17823 from poettering/resolved-just-bypass
resolved: just the dnssec bypass logic
This commit is contained in:
commit
39755e0014
@ -75,6 +75,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_LINK, ENXIO),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_LINK_BUSY, EBUSY),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_NETWORK_DOWN, ENETDOWN),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SOURCE, ESRCH),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_DNSSD_SERVICE, ENOENT),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_DNSSD_SERVICE_EXISTS, EEXIST),
|
||||
|
||||
|
@ -73,6 +73,7 @@
|
||||
#define BUS_ERROR_NO_SUCH_LINK "org.freedesktop.resolve1.NoSuchLink"
|
||||
#define BUS_ERROR_LINK_BUSY "org.freedesktop.resolve1.LinkBusy"
|
||||
#define BUS_ERROR_NETWORK_DOWN "org.freedesktop.resolve1.NetworkDown"
|
||||
#define BUS_ERROR_NO_SOURCE "org.freedesktop.resolve1.NoSource"
|
||||
#define BUS_ERROR_NO_SUCH_DNSSD_SERVICE "org.freedesktop.resolve1.NoSuchDnssdService"
|
||||
#define BUS_ERROR_DNSSD_SERVICE_EXISTS "org.freedesktop.resolve1.DnssdServiceExists"
|
||||
#define _BUS_ERROR_DNS "org.freedesktop.resolve1.DnsError."
|
||||
|
@ -101,6 +101,9 @@ static int reply_query_state(DnsQuery *q) {
|
||||
* thus quickly know that we cannot resolve an in-addr.arpa or ip6.arpa address. */
|
||||
return sd_bus_reply_method_errorf(q->bus_request, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", dns_query_string(q));
|
||||
|
||||
case DNS_TRANSACTION_NO_SOURCE:
|
||||
return sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_NO_SOURCE, "All suitable resolution sources turned off");
|
||||
|
||||
case DNS_TRANSACTION_RCODE_FAILURE: {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
@ -274,8 +277,8 @@ static int validate_and_mangle_flags(
|
||||
*
|
||||
* 1. Checks that the interface index is either 0 (meaning *all* interfaces) or positive
|
||||
*
|
||||
* 2. Only the protocols flags and the NO_CNAME flag are set, at most. Plus additional flags specific
|
||||
* to our method, passed in the "ok" parameter.
|
||||
* 2. Only the protocols flags and a bunch of NO_XYZ flags are set, at most. Plus additional flags
|
||||
* specific to our method, passed in the "ok" parameter.
|
||||
*
|
||||
* 3. If zero protocol flags are specified it is automatically turned into *all* protocols. This way
|
||||
* clients can simply pass 0 as flags and all will work as it should. They can also use this so
|
||||
@ -283,7 +286,15 @@ static int validate_and_mangle_flags(
|
||||
* to mean "all supported protocols".
|
||||
*/
|
||||
|
||||
if (*flags & ~(SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_CNAME|ok))
|
||||
if (*flags & ~(SD_RESOLVED_PROTOCOLS_ALL|
|
||||
SD_RESOLVED_NO_CNAME|
|
||||
SD_RESOLVED_NO_VALIDATE|
|
||||
SD_RESOLVED_NO_SYNTHESIZE|
|
||||
SD_RESOLVED_NO_CACHE|
|
||||
SD_RESOLVED_NO_ZONE|
|
||||
SD_RESOLVED_NO_TRUST_ANCHOR|
|
||||
SD_RESOLVED_NO_NETWORK|
|
||||
ok))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter");
|
||||
|
||||
if ((*flags & SD_RESOLVED_PROTOCOLS_ALL) == 0) /* If no protocol is enabled, enable all */
|
||||
@ -406,7 +417,7 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata,
|
||||
if (r < 0 && r != -EALREADY)
|
||||
return r;
|
||||
|
||||
r = dns_query_new(m, &q, question_utf8, question_idna ?: question_utf8, ifindex, flags);
|
||||
r = dns_query_new(m, &q, question_utf8, question_idna ?: question_utf8, NULL, ifindex, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -548,7 +559,7 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_query_new(m, &q, question, question, ifindex, flags|SD_RESOLVED_NO_SEARCH);
|
||||
r = dns_query_new(m, &q, question, question, NULL, ifindex, flags|SD_RESOLVED_NO_SEARCH);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -724,14 +735,12 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_query_new(m, &q, question, question, ifindex, flags|SD_RESOLVED_NO_SEARCH);
|
||||
/* Setting SD_RESOLVED_CLAMP_TTL: let's request that the TTL is fixed up for locally cached entries,
|
||||
* after all we return it in the wire format blob. */
|
||||
r = dns_query_new(m, &q, question, question, NULL, ifindex, flags|SD_RESOLVED_NO_SEARCH|SD_RESOLVED_CLAMP_TTL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Let's request that the TTL is fixed up for locally cached entries, after all we return it in the wire format
|
||||
* blob */
|
||||
q->clamp_ttl = true;
|
||||
|
||||
q->bus_request = sd_bus_message_ref(message);
|
||||
q->complete = bus_method_resolve_record_complete;
|
||||
|
||||
@ -1088,7 +1097,7 @@ static int resolve_service_hostname(DnsQuery *q, DnsResourceRecord *rr, int ifin
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_query_new(q->manager, &aux, question, question, ifindex, q->flags|SD_RESOLVED_NO_SEARCH);
|
||||
r = dns_query_new(q->manager, &aux, question, question, NULL, ifindex, q->flags|SD_RESOLVED_NO_SEARCH);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -1258,7 +1267,7 @@ static int bus_method_resolve_service(sd_bus_message *message, void *userdata, s
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_query_new(m, &q, question_utf8, question_idna, ifindex, flags|SD_RESOLVED_NO_SEARCH);
|
||||
r = dns_query_new(m, &q, question_utf8, question_idna, NULL, ifindex, flags|SD_RESOLVED_NO_SEARCH);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -27,6 +27,31 @@
|
||||
/* Output: Result is authenticated */
|
||||
#define SD_RESOLVED_AUTHENTICATED (UINT64_C(1) << 9)
|
||||
|
||||
/* Input: Don't DNSSEC validate request */
|
||||
#define SD_RESOLVED_NO_VALIDATE (UINT64_C(1) << 10)
|
||||
|
||||
/* Input: Don't answer request from locally synthesized records (which includes /etc/hosts) */
|
||||
#define SD_RESOLVED_NO_SYNTHESIZE (UINT64_C(1) << 11)
|
||||
|
||||
/* Input: Don't answer request from cache */
|
||||
#define SD_RESOLVED_NO_CACHE (UINT64_C(1) << 12)
|
||||
|
||||
/* Input: Don't answer request from locally registered public LLMNR/mDNS RRs */
|
||||
#define SD_RESOLVED_NO_ZONE (UINT64_C(1) << 13)
|
||||
|
||||
/* Input: Don't answer request from locally registered public LLMNR/mDNS RRs */
|
||||
#define SD_RESOLVED_NO_TRUST_ANCHOR (UINT64_C(1) << 14)
|
||||
|
||||
/* Input: Don't go to network for this request */
|
||||
#define SD_RESOLVED_NO_NETWORK (UINT64_C(1) << 15)
|
||||
|
||||
/* Input: Require that request is answered from a "primary" answer, i.e. not from RRs acquired as
|
||||
* side-effect of a previous transaction */
|
||||
#define SD_RESOLVED_REQUIRE_PRIMARY (UINT64_C(1) << 16)
|
||||
|
||||
/* Input: If reply is answered from cache, the TTLs will be adjusted by age of cache entry */
|
||||
#define SD_RESOLVED_CLAMP_TTL (UINT64_C(1) << 17)
|
||||
|
||||
#define SD_RESOLVED_LLMNR (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6)
|
||||
#define SD_RESOLVED_MDNS (SD_RESOLVED_MDNS_IPV4|SD_RESOLVED_MDNS_IPV6)
|
||||
#define SD_RESOLVED_PROTOCOLS_ALL (SD_RESOLVED_MDNS|SD_RESOLVED_LLMNR|SD_RESOLVED_DNS)
|
||||
|
@ -55,20 +55,21 @@ DnsAnswer *dns_answer_new(size_t n) {
|
||||
a->n_ref = 1;
|
||||
a->n_allocated = n;
|
||||
a->set_items = TAKE_PTR(s);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
static void dns_answer_flush(DnsAnswer *a) {
|
||||
DnsResourceRecord *rr;
|
||||
DnsAnswerItem *item;
|
||||
|
||||
if (!a)
|
||||
return;
|
||||
|
||||
a->set_items = set_free(a->set_items);
|
||||
|
||||
DNS_ANSWER_FOREACH(rr, a)
|
||||
dns_resource_record_unref(rr);
|
||||
DNS_ANSWER_FOREACH_ITEM(item, a) {
|
||||
dns_resource_record_unref(item->rr);
|
||||
dns_resource_record_unref(item->rrsig);
|
||||
}
|
||||
|
||||
a->n_rrs = 0;
|
||||
}
|
||||
@ -82,7 +83,13 @@ static DnsAnswer *dns_answer_free(DnsAnswer *a) {
|
||||
|
||||
DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsAnswer, dns_answer, dns_answer_free);
|
||||
|
||||
static int dns_answer_add_raw(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
|
||||
static int dns_answer_add_raw(
|
||||
DnsAnswer *a,
|
||||
DnsResourceRecord *rr,
|
||||
int ifindex,
|
||||
DnsAnswerFlags flags,
|
||||
DnsResourceRecord *rrsig) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(rr);
|
||||
@ -97,6 +104,7 @@ static int dns_answer_add_raw(DnsAnswer *a, DnsResourceRecord *rr, int ifindex,
|
||||
.rr = rr,
|
||||
.ifindex = ifindex,
|
||||
.flags = flags,
|
||||
.rrsig = dns_resource_record_ref(rrsig),
|
||||
};
|
||||
|
||||
r = set_put(a->set_items, &a->items[a->n_rrs]);
|
||||
@ -112,12 +120,16 @@ static int dns_answer_add_raw(DnsAnswer *a, DnsResourceRecord *rr, int ifindex,
|
||||
}
|
||||
|
||||
static int dns_answer_add_raw_all(DnsAnswer *a, DnsAnswer *source) {
|
||||
DnsResourceRecord *rr;
|
||||
DnsAnswerFlags flags;
|
||||
int ifindex, r;
|
||||
DnsAnswerItem *item;
|
||||
int r;
|
||||
|
||||
DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, source) {
|
||||
r = dns_answer_add_raw(a, rr, ifindex, flags);
|
||||
DNS_ANSWER_FOREACH_ITEM(item, source) {
|
||||
r = dns_answer_add_raw(
|
||||
a,
|
||||
item->rr,
|
||||
item->ifindex,
|
||||
item->flags,
|
||||
item->rrsig);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -125,7 +137,13 @@ static int dns_answer_add_raw_all(DnsAnswer *a, DnsAnswer *source) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
|
||||
int dns_answer_add(
|
||||
DnsAnswer *a,
|
||||
DnsResourceRecord *rr,
|
||||
int ifindex,
|
||||
DnsAnswerFlags flags,
|
||||
DnsResourceRecord *rrsig) {
|
||||
|
||||
DnsAnswerItem tmp, *exist;
|
||||
|
||||
assert(rr);
|
||||
@ -152,22 +170,28 @@ int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFl
|
||||
if (rr->ttl > exist->rr->ttl) {
|
||||
dns_resource_record_unref(exist->rr);
|
||||
exist->rr = dns_resource_record_ref(rr);
|
||||
|
||||
/* Update RRSIG and RR at the same time */
|
||||
if (rrsig) {
|
||||
dns_resource_record_ref(rrsig);
|
||||
dns_resource_record_unref(exist->rrsig);
|
||||
exist->rrsig = rrsig;
|
||||
}
|
||||
}
|
||||
|
||||
exist->flags |= flags;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return dns_answer_add_raw(a, rr, ifindex, flags);
|
||||
return dns_answer_add_raw(a, rr, ifindex, flags, rrsig);
|
||||
}
|
||||
|
||||
static int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) {
|
||||
DnsResourceRecord *rr;
|
||||
DnsAnswerFlags flags;
|
||||
int ifindex, r;
|
||||
DnsAnswerItem *item;
|
||||
int r;
|
||||
|
||||
DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, b) {
|
||||
r = dns_answer_add(a, rr, ifindex, flags);
|
||||
DNS_ANSWER_FOREACH_ITEM(item, b) {
|
||||
r = dns_answer_add(a, item->rr, item->ifindex, item->flags, item->rrsig);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -175,7 +199,13 @@ static int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
|
||||
int dns_answer_add_extend(
|
||||
DnsAnswer **a,
|
||||
DnsResourceRecord *rr,
|
||||
int ifindex,
|
||||
DnsAnswerFlags flags,
|
||||
DnsResourceRecord *rrsig) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(a);
|
||||
@ -185,7 +215,7 @@ int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex, Dns
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return dns_answer_add(*a, rr, ifindex, flags);
|
||||
return dns_answer_add(*a, rr, ifindex, flags, rrsig);
|
||||
}
|
||||
|
||||
int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl, int ifindex) {
|
||||
@ -211,7 +241,7 @@ int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl, int ifindex
|
||||
soa->soa.expire = 1;
|
||||
soa->soa.minimum = ttl;
|
||||
|
||||
return dns_answer_add(a, soa, ifindex, DNS_ANSWER_AUTHENTICATED);
|
||||
return dns_answer_add(a, soa, ifindex, DNS_ANSWER_AUTHENTICATED, NULL);
|
||||
}
|
||||
|
||||
int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
|
||||
@ -249,10 +279,9 @@ int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags
|
||||
int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a) {
|
||||
DnsResourceRecord *i;
|
||||
|
||||
DNS_ANSWER_FOREACH(i, a) {
|
||||
DNS_ANSWER_FOREACH(i, a)
|
||||
if (IN_SET(i->key->type, DNS_TYPE_NSEC, DNS_TYPE_NSEC3))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -284,7 +313,22 @@ int dns_answer_contains_zone_nsec3(DnsAnswer *answer, const char *zone) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
|
||||
int dns_answer_contains(DnsAnswer *answer, DnsResourceRecord *rr) {
|
||||
DnsResourceRecord *i;
|
||||
|
||||
DNS_ANSWER_FOREACH(i, answer)
|
||||
if (dns_resource_record_equal(i, rr))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int dns_answer_find_soa(
|
||||
DnsAnswer *a,
|
||||
const DnsResourceKey *key,
|
||||
DnsResourceRecord **ret,
|
||||
DnsAnswerFlags *ret_flags) {
|
||||
|
||||
DnsResourceRecord *rr, *soa = NULL;
|
||||
DnsAnswerFlags rr_flags, soa_flags = 0;
|
||||
int r;
|
||||
@ -293,7 +337,7 @@ int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceReco
|
||||
|
||||
/* For a SOA record we can never find a matching SOA record */
|
||||
if (key->type == DNS_TYPE_SOA)
|
||||
return 0;
|
||||
goto not_found;
|
||||
|
||||
DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
|
||||
r = dns_resource_key_match_soa(key, rr->key);
|
||||
@ -315,17 +359,30 @@ int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceReco
|
||||
}
|
||||
|
||||
if (!soa)
|
||||
return 0;
|
||||
goto not_found;
|
||||
|
||||
if (ret)
|
||||
*ret = soa;
|
||||
if (flags)
|
||||
*flags = soa_flags;
|
||||
if (ret_flags)
|
||||
*ret_flags = soa_flags;
|
||||
|
||||
return 1;
|
||||
|
||||
not_found:
|
||||
if (ret)
|
||||
*ret = NULL;
|
||||
if (ret_flags)
|
||||
*ret_flags = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
|
||||
int dns_answer_find_cname_or_dname(
|
||||
DnsAnswer *a,
|
||||
const DnsResourceKey *key,
|
||||
DnsResourceRecord **ret,
|
||||
DnsAnswerFlags *ret_flags) {
|
||||
|
||||
DnsResourceRecord *rr;
|
||||
DnsAnswerFlags rr_flags;
|
||||
int r;
|
||||
@ -343,12 +400,17 @@ int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsR
|
||||
if (r > 0) {
|
||||
if (ret)
|
||||
*ret = rr;
|
||||
if (flags)
|
||||
*flags = rr_flags;
|
||||
if (ret_flags)
|
||||
*ret_flags = rr_flags;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret)
|
||||
*ret = NULL;
|
||||
if (ret_flags)
|
||||
*ret_flags = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -440,21 +502,20 @@ int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) {
|
||||
|
||||
if ((*a)->n_ref > 1) {
|
||||
_cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
|
||||
DnsAnswerFlags flags;
|
||||
int ifindex;
|
||||
DnsAnswerItem *item;
|
||||
|
||||
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);
|
||||
DNS_ANSWER_FOREACH_ITEM(item, *a) {
|
||||
r = dns_resource_key_equal(item->rr->key, key);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
continue;
|
||||
|
||||
r = dns_answer_add_raw(copy, rr, ifindex, flags);
|
||||
r = dns_answer_add_raw(copy, item->rr, item->ifindex, item->flags, item->rrsig);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -479,6 +540,8 @@ int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) {
|
||||
/* Kill this entry */
|
||||
|
||||
dns_resource_record_unref((*a)->items[i].rr);
|
||||
dns_resource_record_unref((*a)->items[i].rrsig);
|
||||
|
||||
memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1));
|
||||
(*a)->n_rrs--;
|
||||
continue;
|
||||
@ -525,21 +588,20 @@ int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rm) {
|
||||
|
||||
if ((*a)->n_ref > 1) {
|
||||
_cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
|
||||
DnsAnswerFlags flags;
|
||||
int ifindex;
|
||||
DnsAnswerItem *item;
|
||||
|
||||
copy = dns_answer_new((*a)->n_rrs);
|
||||
if (!copy)
|
||||
return -ENOMEM;
|
||||
|
||||
DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) {
|
||||
r = dns_resource_record_equal(rr, rm);
|
||||
DNS_ANSWER_FOREACH_ITEM(item, *a) {
|
||||
r = dns_resource_record_equal(item->rr, rm);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
continue;
|
||||
|
||||
r = dns_answer_add_raw(copy, rr, ifindex, flags);
|
||||
r = dns_answer_add_raw(copy, item->rr, item->ifindex, item->flags, item->rrsig);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -564,6 +626,7 @@ int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rm) {
|
||||
/* Kill this entry */
|
||||
|
||||
dns_resource_record_unref((*a)->items[i].rr);
|
||||
dns_resource_record_unref((*a)->items[i].rrsig);
|
||||
memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1));
|
||||
(*a)->n_rrs--;
|
||||
continue;
|
||||
@ -576,19 +639,24 @@ int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rm) {
|
||||
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;
|
||||
int dns_answer_copy_by_key(
|
||||
DnsAnswer **a,
|
||||
DnsAnswer *source,
|
||||
const DnsResourceKey *key,
|
||||
DnsAnswerFlags or_flags,
|
||||
DnsResourceRecord *rrsig) {
|
||||
|
||||
DnsAnswerItem *item;
|
||||
int r;
|
||||
|
||||
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) {
|
||||
DNS_ANSWER_FOREACH_ITEM(item, source) {
|
||||
|
||||
r = dns_resource_key_equal(rr_source->key, key);
|
||||
r = dns_resource_key_equal(item->rr->key, key);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
@ -599,7 +667,7 @@ int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKe
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_answer_add(*a, rr_source, ifindex_source, flags_source|or_flags);
|
||||
r = dns_answer_add(*a, item->rr, item->ifindex, item->flags|or_flags, item->rrsig);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -607,14 +675,19 @@ int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKe
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_answer_move_by_key(DnsAnswer **to, DnsAnswer **from, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
|
||||
int dns_answer_move_by_key(
|
||||
DnsAnswer **to,
|
||||
DnsAnswer **from,
|
||||
const DnsResourceKey *key,
|
||||
DnsAnswerFlags or_flags,
|
||||
DnsResourceRecord *rrsig) {
|
||||
int r;
|
||||
|
||||
assert(to);
|
||||
assert(from);
|
||||
assert(key);
|
||||
|
||||
r = dns_answer_copy_by_key(to, *from, key, or_flags);
|
||||
r = dns_answer_copy_by_key(to, *from, key, or_flags, rrsig);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -744,19 +817,17 @@ int dns_answer_reserve_or_clone(DnsAnswer **a, size_t n_free) {
|
||||
* This function is not used in the code base, but is useful when debugging. Do not delete.
|
||||
*/
|
||||
void dns_answer_dump(DnsAnswer *answer, FILE *f) {
|
||||
DnsResourceRecord *rr;
|
||||
DnsAnswerFlags flags;
|
||||
int ifindex;
|
||||
DnsAnswerItem *item;
|
||||
|
||||
if (!f)
|
||||
f = stdout;
|
||||
|
||||
DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
|
||||
DNS_ANSWER_FOREACH_ITEM(item, answer) {
|
||||
const char *t;
|
||||
|
||||
fputc('\t', f);
|
||||
|
||||
t = dns_resource_record_to_string(rr);
|
||||
t = dns_resource_record_to_string(item->rr);
|
||||
if (!t) {
|
||||
log_oom();
|
||||
continue;
|
||||
@ -764,21 +835,29 @@ void dns_answer_dump(DnsAnswer *answer, FILE *f) {
|
||||
|
||||
fputs(t, f);
|
||||
|
||||
if (ifindex != 0 || flags != 0)
|
||||
if (item->ifindex != 0 || item->rrsig || item->flags != 0)
|
||||
fputs("\t;", f);
|
||||
|
||||
if (ifindex != 0)
|
||||
fprintf(f, " ifindex=%i", ifindex);
|
||||
if (flags & DNS_ANSWER_AUTHENTICATED)
|
||||
if (item->ifindex != 0)
|
||||
fprintf(f, " ifindex=%i", item->ifindex);
|
||||
if (item->rrsig)
|
||||
fputs(" rrsig", f);
|
||||
if (item->flags & DNS_ANSWER_AUTHENTICATED)
|
||||
fputs(" authenticated", f);
|
||||
if (flags & DNS_ANSWER_CACHEABLE)
|
||||
if (item->flags & DNS_ANSWER_CACHEABLE)
|
||||
fputs(" cacheable", f);
|
||||
if (flags & DNS_ANSWER_SHARED_OWNER)
|
||||
if (item->flags & DNS_ANSWER_SHARED_OWNER)
|
||||
fputs(" shared-owner", f);
|
||||
if (flags & DNS_ANSWER_CACHE_FLUSH)
|
||||
if (item->flags & DNS_ANSWER_CACHE_FLUSH)
|
||||
fputs(" cache-flush", f);
|
||||
if (flags & DNS_ANSWER_GOODBYE)
|
||||
if (item->flags & DNS_ANSWER_GOODBYE)
|
||||
fputs(" goodbye", f);
|
||||
if (item->flags & DNS_ANSWER_SECTION_ANSWER)
|
||||
fputs(" section-answer", f);
|
||||
if (item->flags & DNS_ANSWER_SECTION_AUTHORITY)
|
||||
fputs(" section-authority", f);
|
||||
if (item->flags & DNS_ANSWER_SECTION_ADDITIONAL)
|
||||
fputs(" section-additional", f);
|
||||
|
||||
fputc('\n', f);
|
||||
}
|
||||
|
@ -8,23 +8,25 @@ typedef struct DnsAnswerItem DnsAnswerItem;
|
||||
#include "resolved-dns-rr.h"
|
||||
#include "set.h"
|
||||
|
||||
/* A simple array of resource records. We keep track of the
|
||||
* originating ifindex for each RR where that makes sense, so that we
|
||||
* can qualify A and AAAA RRs referring to a local link with the
|
||||
* right ifindex.
|
||||
/* A simple array of resource records. We keep track of the originating ifindex for each RR where that makes
|
||||
* sense, so that we can qualify A and AAAA RRs referring to a local link with the right ifindex.
|
||||
*
|
||||
* Note that we usually encode the empty DnsAnswer object as a simple NULL. */
|
||||
|
||||
typedef enum DnsAnswerFlags {
|
||||
DNS_ANSWER_AUTHENTICATED = 1 << 0, /* Item has been authenticated */
|
||||
DNS_ANSWER_CACHEABLE = 1 << 1, /* Item is subject to caching */
|
||||
DNS_ANSWER_SHARED_OWNER = 1 << 2, /* For mDNS: RRset may be owner by multiple peers */
|
||||
DNS_ANSWER_CACHE_FLUSH = 1 << 3, /* For mDNS: sets cache-flush bit in the rrclass of response records */
|
||||
DNS_ANSWER_GOODBYE = 1 << 4, /* For mDNS: item is subject to disappear */
|
||||
DNS_ANSWER_AUTHENTICATED = 1 << 0, /* Item has been authenticated */
|
||||
DNS_ANSWER_CACHEABLE = 1 << 1, /* Item is subject to caching */
|
||||
DNS_ANSWER_SHARED_OWNER = 1 << 2, /* For mDNS: RRset may be owner by multiple peers */
|
||||
DNS_ANSWER_CACHE_FLUSH = 1 << 3, /* For mDNS: sets cache-flush bit in the rrclass of response records */
|
||||
DNS_ANSWER_GOODBYE = 1 << 4, /* For mDNS: item is subject to disappear */
|
||||
DNS_ANSWER_SECTION_ANSWER = 1 << 5, /* When parsing: RR originates from answer section */
|
||||
DNS_ANSWER_SECTION_AUTHORITY = 1 << 6, /* When parsing: RR originates from authority section */
|
||||
DNS_ANSWER_SECTION_ADDITIONAL = 1 << 7, /* When parsing: RR originates from additional section */
|
||||
} DnsAnswerFlags;
|
||||
|
||||
struct DnsAnswerItem {
|
||||
DnsResourceRecord *rr;
|
||||
DnsResourceRecord *rrsig; /* Optionally, also store RRSIG RR that successfully validates this item */
|
||||
int ifindex;
|
||||
DnsAnswerFlags flags;
|
||||
};
|
||||
@ -40,16 +42,17 @@ DnsAnswer *dns_answer_new(size_t n);
|
||||
DnsAnswer *dns_answer_ref(DnsAnswer *a);
|
||||
DnsAnswer *dns_answer_unref(DnsAnswer *a);
|
||||
|
||||
int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags);
|
||||
int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags);
|
||||
int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags, DnsResourceRecord *rrsig);
|
||||
int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags, DnsResourceRecord *rrsig);
|
||||
int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl, int ifindex);
|
||||
|
||||
int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *combined_flags);
|
||||
int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a);
|
||||
int dns_answer_contains_zone_nsec3(DnsAnswer *answer, const char *zone);
|
||||
int dns_answer_contains(DnsAnswer *answer, DnsResourceRecord *rr);
|
||||
|
||||
int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags);
|
||||
int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags);
|
||||
int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *ret_flags);
|
||||
int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *ret_flags);
|
||||
|
||||
int dns_answer_merge(DnsAnswer *a, DnsAnswer *b, DnsAnswer **ret);
|
||||
int dns_answer_extend(DnsAnswer **a, DnsAnswer *b);
|
||||
@ -62,8 +65,8 @@ int dns_answer_reserve_or_clone(DnsAnswer **a, size_t n_free);
|
||||
int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key);
|
||||
int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rr);
|
||||
|
||||
int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags);
|
||||
int dns_answer_move_by_key(DnsAnswer **to, DnsAnswer **from, const DnsResourceKey *key, DnsAnswerFlags or_flags);
|
||||
int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags, DnsResourceRecord *rrsig);
|
||||
int dns_answer_move_by_key(DnsAnswer **to, DnsAnswer **from, const DnsResourceKey *key, DnsAnswerFlags or_flags, DnsResourceRecord *rrsig);
|
||||
|
||||
int dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname);
|
||||
|
||||
@ -115,17 +118,13 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref);
|
||||
|
||||
#define DNS_ANSWER_FOREACH_FLAGS(kk, flags, a) _DNS_ANSWER_FOREACH_FLAGS(UNIQ, kk, flags, a)
|
||||
|
||||
#define _DNS_ANSWER_FOREACH_FULL(q, kk, ifi, fl, a) \
|
||||
#define _DNS_ANSWER_FOREACH_ITEM(q, item, a) \
|
||||
for (size_t UNIQ_T(i, q) = ({ \
|
||||
(kk) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].rr : NULL; \
|
||||
(ifi) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].ifindex : 0; \
|
||||
(fl) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].flags : 0; \
|
||||
(item) = dns_answer_isempty(a) ? NULL : (a)->items; \
|
||||
0; \
|
||||
}); \
|
||||
(a) && (UNIQ_T(i, q) < (a)->n_rrs); \
|
||||
UNIQ_T(i, q) < dns_answer_size(a); \
|
||||
UNIQ_T(i, q)++, \
|
||||
(kk) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].rr : NULL), \
|
||||
(ifi) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].ifindex : 0), \
|
||||
(fl) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].flags : 0))
|
||||
(item) = ((UNIQ_T(i, q) < dns_answer_size(a)) ? (a)->items + UNIQ_T(i, q) : NULL))
|
||||
|
||||
#define DNS_ANSWER_FOREACH_FULL(kk, ifindex, flags, a) _DNS_ANSWER_FOREACH_FULL(UNIQ, kk, ifindex, flags, a)
|
||||
#define DNS_ANSWER_FOREACH_ITEM(item, a) _DNS_ANSWER_FOREACH_ITEM(UNIQ, item, a)
|
||||
|
@ -34,13 +34,16 @@ enum DnsCacheItemType {
|
||||
|
||||
struct DnsCacheItem {
|
||||
DnsCacheItemType type;
|
||||
DnsResourceKey *key;
|
||||
DnsResourceRecord *rr;
|
||||
DnsResourceKey *key; /* The key for this item, i.e. the lookup key */
|
||||
DnsResourceRecord *rr; /* The RR for this item, i.e. the lookup value for positive queries */
|
||||
DnsAnswer *answer; /* The full validated answer, if this is an RRset acquired via a "primary" lookup */
|
||||
DnsPacket *full_packet; /* The full packet this information was acquired with */
|
||||
int rcode;
|
||||
|
||||
usec_t until;
|
||||
bool authenticated:1;
|
||||
bool shared_owner:1;
|
||||
DnssecResult dnssec_result;
|
||||
|
||||
int ifindex;
|
||||
int owner_family;
|
||||
@ -50,6 +53,12 @@ struct DnsCacheItem {
|
||||
LIST_FIELDS(DnsCacheItem, by_key);
|
||||
};
|
||||
|
||||
/* Returns true if this is a cache item created as result of an explicit lookup, or created as "side-effect"
|
||||
* of another request. "Primary" entries will carry the full answer data (with NSEC, …) that can aso prove
|
||||
* wildcard expansion, non-existance and such, while entries that were created as "side-effect" just contain
|
||||
* immediate RR data for the specified RR key, but nothing else. */
|
||||
#define DNS_CACHE_ITEM_IS_PRIMARY(item) (!!(item)->answer)
|
||||
|
||||
static const char *dns_cache_item_type_to_string(DnsCacheItem *item) {
|
||||
assert(item);
|
||||
|
||||
@ -77,6 +86,8 @@ static void dns_cache_item_free(DnsCacheItem *i) {
|
||||
|
||||
dns_resource_record_unref(i->rr);
|
||||
dns_resource_key_unref(i->key);
|
||||
dns_answer_unref(i->answer);
|
||||
dns_packet_unref(i->full_packet);
|
||||
free(i);
|
||||
}
|
||||
|
||||
@ -340,8 +351,11 @@ static void dns_cache_item_update_positive(
|
||||
DnsCache *c,
|
||||
DnsCacheItem *i,
|
||||
DnsResourceRecord *rr,
|
||||
DnsAnswer *answer,
|
||||
DnsPacket *full_packet,
|
||||
bool authenticated,
|
||||
bool shared_owner,
|
||||
DnssecResult dnssec_result,
|
||||
usec_t timestamp,
|
||||
int ifindex,
|
||||
int owner_family,
|
||||
@ -367,9 +381,18 @@ static void dns_cache_item_update_positive(
|
||||
dns_resource_key_unref(i->key);
|
||||
i->key = dns_resource_key_ref(rr->key);
|
||||
|
||||
i->until = calculate_until(rr, (uint32_t) -1, timestamp, false);
|
||||
dns_answer_ref(answer);
|
||||
dns_answer_unref(i->answer);
|
||||
i->answer = answer;
|
||||
|
||||
dns_packet_ref(full_packet);
|
||||
dns_packet_unref(i->full_packet);
|
||||
i->full_packet = full_packet;
|
||||
|
||||
i->until = calculate_until(rr, UINT32_MAX, timestamp, false);
|
||||
i->authenticated = authenticated;
|
||||
i->shared_owner = shared_owner;
|
||||
i->dnssec_result = dnssec_result;
|
||||
|
||||
i->ifindex = ifindex;
|
||||
|
||||
@ -382,8 +405,11 @@ static void dns_cache_item_update_positive(
|
||||
static int dns_cache_put_positive(
|
||||
DnsCache *c,
|
||||
DnsResourceRecord *rr,
|
||||
DnsAnswer *answer,
|
||||
DnsPacket *full_packet,
|
||||
bool authenticated,
|
||||
bool shared_owner,
|
||||
DnssecResult dnssec_result,
|
||||
usec_t timestamp,
|
||||
int ifindex,
|
||||
int owner_family,
|
||||
@ -420,8 +446,11 @@ static int dns_cache_put_positive(
|
||||
c,
|
||||
existing,
|
||||
rr,
|
||||
answer,
|
||||
full_packet,
|
||||
authenticated,
|
||||
shared_owner,
|
||||
dnssec_result,
|
||||
timestamp,
|
||||
ifindex,
|
||||
owner_family,
|
||||
@ -444,9 +473,12 @@ static int dns_cache_put_positive(
|
||||
.type = DNS_CACHE_POSITIVE,
|
||||
.key = dns_resource_key_ref(rr->key),
|
||||
.rr = dns_resource_record_ref(rr),
|
||||
.answer = dns_answer_ref(answer),
|
||||
.full_packet = dns_packet_ref(full_packet),
|
||||
.until = calculate_until(rr, (uint32_t) -1, timestamp, false),
|
||||
.authenticated = authenticated,
|
||||
.shared_owner = shared_owner,
|
||||
.dnssec_result = dnssec_result,
|
||||
.ifindex = ifindex,
|
||||
.owner_family = owner_family,
|
||||
.owner_address = *owner_address,
|
||||
@ -481,7 +513,10 @@ static int dns_cache_put_negative(
|
||||
DnsCache *c,
|
||||
DnsResourceKey *key,
|
||||
int rcode,
|
||||
DnsAnswer *answer,
|
||||
DnsPacket *full_packet,
|
||||
bool authenticated,
|
||||
DnssecResult dnssec_result,
|
||||
uint32_t nsec_ttl,
|
||||
usec_t timestamp,
|
||||
DnsResourceRecord *soa,
|
||||
@ -532,10 +567,13 @@ static int dns_cache_put_negative(
|
||||
rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA :
|
||||
rcode == DNS_RCODE_NXDOMAIN ? DNS_CACHE_NXDOMAIN : DNS_CACHE_RCODE,
|
||||
.authenticated = authenticated,
|
||||
.dnssec_result = dnssec_result,
|
||||
.owner_family = owner_family,
|
||||
.owner_address = *owner_address,
|
||||
.prioq_idx = PRIOQ_IDX_NULL,
|
||||
.rcode = rcode,
|
||||
.answer = dns_answer_ref(answer),
|
||||
.full_packet = dns_packet_ref(full_packet),
|
||||
};
|
||||
|
||||
i->until =
|
||||
@ -630,17 +668,20 @@ int dns_cache_put(
|
||||
DnsResourceKey *key,
|
||||
int rcode,
|
||||
DnsAnswer *answer,
|
||||
DnsPacket *full_packet,
|
||||
bool authenticated,
|
||||
DnssecResult dnssec_result,
|
||||
uint32_t nsec_ttl,
|
||||
usec_t timestamp,
|
||||
int owner_family,
|
||||
const union in_addr_union *owner_address) {
|
||||
|
||||
DnsResourceRecord *soa = NULL, *rr;
|
||||
DnsResourceRecord *soa = NULL;
|
||||
bool weird_rcode = false;
|
||||
DnsAnswerItem *item;
|
||||
DnsAnswerFlags flags;
|
||||
unsigned cache_keys;
|
||||
int r, ifindex;
|
||||
usec_t timestamp;
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
assert(owner_address);
|
||||
@ -660,6 +701,7 @@ int dns_cache_put(
|
||||
log_debug("Not caching negative entry without a SOA record: %s",
|
||||
dns_resource_key_to_string(key, key_str, sizeof key_str));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -679,23 +721,53 @@ int dns_cache_put(
|
||||
/* Make some space for our new entries */
|
||||
dns_cache_make_space(c, cache_keys);
|
||||
|
||||
if (timestamp <= 0)
|
||||
timestamp = now(clock_boottime_or_monotonic());
|
||||
timestamp = now(clock_boottime_or_monotonic());
|
||||
|
||||
/* Second, add in positive entries for all contained RRs */
|
||||
DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
|
||||
if ((flags & DNS_ANSWER_CACHEABLE) == 0 ||
|
||||
!rr_eligible(rr))
|
||||
DNS_ANSWER_FOREACH_ITEM(item, answer) {
|
||||
int primary = false;
|
||||
|
||||
if (!FLAGS_SET(item->flags, DNS_ANSWER_CACHEABLE) ||
|
||||
!rr_eligible(item->rr))
|
||||
continue;
|
||||
|
||||
if (key) {
|
||||
/* We store the auxiliary RRs and packet data in the cache only if they were in
|
||||
* direct response to the original query. If we cache an RR we also received, and
|
||||
* that is just auxiliary information we can't use the data, hence don't. */
|
||||
|
||||
primary = dns_resource_key_match_rr(key, item->rr, NULL);
|
||||
if (primary < 0)
|
||||
return primary;
|
||||
if (primary == 0) {
|
||||
primary = dns_resource_key_match_cname_or_dname(key, item->rr->key, NULL);
|
||||
if (primary < 0)
|
||||
return primary;
|
||||
}
|
||||
}
|
||||
|
||||
if (!primary) {
|
||||
DnsCacheItem *first;
|
||||
|
||||
/* Do not replace existing cache items for primary lookups with non-primary
|
||||
* data. After all the primary lookup data is a lot more useful. */
|
||||
first = hashmap_get(c->by_key, item->rr->key);
|
||||
if (first && DNS_CACHE_ITEM_IS_PRIMARY(first))
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = dns_cache_put_positive(
|
||||
c,
|
||||
rr,
|
||||
flags & DNS_ANSWER_AUTHENTICATED,
|
||||
flags & DNS_ANSWER_SHARED_OWNER,
|
||||
item->rr,
|
||||
primary ? answer : NULL,
|
||||
primary ? full_packet : NULL,
|
||||
item->flags & DNS_ANSWER_AUTHENTICATED,
|
||||
item->flags & DNS_ANSWER_SHARED_OWNER,
|
||||
dnssec_result,
|
||||
timestamp,
|
||||
ifindex,
|
||||
owner_family, owner_address);
|
||||
item->ifindex,
|
||||
owner_family,
|
||||
owner_address);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
@ -745,7 +817,10 @@ int dns_cache_put(
|
||||
c,
|
||||
key,
|
||||
rcode,
|
||||
answer,
|
||||
full_packet,
|
||||
authenticated,
|
||||
dnssec_result,
|
||||
nsec_ttl,
|
||||
timestamp,
|
||||
soa,
|
||||
@ -762,11 +837,11 @@ fail:
|
||||
if (key)
|
||||
dns_cache_remove_by_key(c, key);
|
||||
|
||||
DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
|
||||
if ((flags & DNS_ANSWER_CACHEABLE) == 0)
|
||||
DNS_ANSWER_FOREACH_ITEM(item, answer) {
|
||||
if ((item->flags & DNS_ANSWER_CACHEABLE) == 0)
|
||||
continue;
|
||||
|
||||
dns_cache_remove_by_key(c, rr->key);
|
||||
dns_cache_remove_by_key(c, item->rr->key);
|
||||
}
|
||||
|
||||
return r;
|
||||
@ -827,7 +902,59 @@ static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, D
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcode, DnsAnswer **ret, bool *authenticated) {
|
||||
static int answer_add_clamp_ttl(
|
||||
DnsAnswer **answer,
|
||||
DnsResourceRecord *rr,
|
||||
int ifindex,
|
||||
DnsAnswerFlags answer_flags,
|
||||
DnsResourceRecord *rrsig,
|
||||
uint64_t query_flags,
|
||||
usec_t until,
|
||||
usec_t current) {
|
||||
|
||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *patched = NULL, *patched_rrsig = NULL;
|
||||
int r;
|
||||
|
||||
assert(answer);
|
||||
assert(rr);
|
||||
|
||||
if (FLAGS_SET(query_flags, SD_RESOLVED_CLAMP_TTL)) {
|
||||
patched = dns_resource_record_ref(rr);
|
||||
|
||||
r = dns_resource_record_clamp_ttl(&patched, LESS_BY(until, current) / USEC_PER_SEC);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
rr = patched;
|
||||
|
||||
if (rrsig) {
|
||||
patched_rrsig = dns_resource_record_ref(rrsig);
|
||||
r = dns_resource_record_clamp_ttl(&patched_rrsig, LESS_BY(until, current) / USEC_PER_SEC);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
rrsig = patched_rrsig;
|
||||
}
|
||||
}
|
||||
|
||||
r = dns_answer_add_extend(answer, rr, ifindex, answer_flags, rrsig);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_cache_lookup(
|
||||
DnsCache *c,
|
||||
DnsResourceKey *key,
|
||||
uint64_t query_flags,
|
||||
int *ret_rcode,
|
||||
DnsAnswer **ret_answer,
|
||||
DnsPacket **ret_full_packet,
|
||||
bool *ret_authenticated,
|
||||
DnssecResult *ret_dnssec_result) {
|
||||
|
||||
_cleanup_(dns_packet_unrefp) DnsPacket *full_packet = NULL;
|
||||
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
|
||||
char key_str[DNS_RESOURCE_KEY_STRING_MAX];
|
||||
unsigned n = 0;
|
||||
@ -837,27 +964,19 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod
|
||||
bool have_authenticated = false, have_non_authenticated = false;
|
||||
usec_t current;
|
||||
int found_rcode = -1;
|
||||
DnssecResult dnssec_result = -1;
|
||||
int have_dnssec_result = -1;
|
||||
|
||||
assert(c);
|
||||
assert(key);
|
||||
assert(rcode);
|
||||
assert(ret);
|
||||
assert(authenticated);
|
||||
|
||||
if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
|
||||
/* If we have ANY lookups we don't use the cache, so
|
||||
* that the caller refreshes via the network. */
|
||||
/* If we have ANY lookups we don't use the cache, so that the caller refreshes via the
|
||||
* network. */
|
||||
|
||||
log_debug("Ignoring cache for ANY lookup: %s",
|
||||
dns_resource_key_to_string(key, key_str, sizeof key_str));
|
||||
|
||||
c->n_miss++;
|
||||
|
||||
*ret = NULL;
|
||||
*rcode = DNS_RCODE_SUCCESS;
|
||||
*authenticated = false;
|
||||
|
||||
return 0;
|
||||
goto miss;
|
||||
}
|
||||
|
||||
first = dns_cache_get_by_key_follow_cname_dname_nsec(c, key);
|
||||
@ -866,31 +985,80 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod
|
||||
|
||||
log_debug("Cache miss for %s",
|
||||
dns_resource_key_to_string(key, key_str, sizeof key_str));
|
||||
|
||||
c->n_miss++;
|
||||
|
||||
*ret = NULL;
|
||||
*rcode = DNS_RCODE_SUCCESS;
|
||||
*authenticated = false;
|
||||
|
||||
return 0;
|
||||
goto miss;
|
||||
}
|
||||
|
||||
if (FLAGS_SET(query_flags, SD_RESOLVED_CLAMP_TTL))
|
||||
current = now(clock_boottime_or_monotonic());
|
||||
|
||||
LIST_FOREACH(by_key, j, first) {
|
||||
if (j->rr) {
|
||||
/* If the caller doesn't allow us to answer questions from cache data learned from
|
||||
* "side-effect", skip this entry. */
|
||||
if (FLAGS_SET(query_flags, SD_RESOLVED_REQUIRE_PRIMARY) &&
|
||||
!DNS_CACHE_ITEM_IS_PRIMARY(j)) {
|
||||
log_debug("Primary answer was requested for cache lookup for %s, which we don't have.",
|
||||
dns_resource_key_to_string(key, key_str, sizeof key_str));
|
||||
|
||||
goto miss;
|
||||
}
|
||||
|
||||
if (j->type == DNS_CACHE_NXDOMAIN)
|
||||
nxdomain = true;
|
||||
else if (j->type == DNS_CACHE_RCODE)
|
||||
found_rcode = j->rcode;
|
||||
else if (j->rr) {
|
||||
if (j->rr->key->type == DNS_TYPE_NSEC)
|
||||
nsec = j;
|
||||
|
||||
n++;
|
||||
} else if (j->type == DNS_CACHE_NXDOMAIN)
|
||||
nxdomain = true;
|
||||
else if (j->type == DNS_CACHE_RCODE)
|
||||
found_rcode = j->rcode;
|
||||
}
|
||||
|
||||
if (j->authenticated)
|
||||
have_authenticated = true;
|
||||
else
|
||||
have_non_authenticated = true;
|
||||
|
||||
if (j->dnssec_result < 0) {
|
||||
have_dnssec_result = false; /* an entry without dnssec result? then invalidate things for good */
|
||||
dnssec_result = _DNSSEC_RESULT_INVALID;
|
||||
} else if (have_dnssec_result < 0) {
|
||||
have_dnssec_result = true; /* So far no result seen, let's pick this one up */
|
||||
dnssec_result = j->dnssec_result;
|
||||
} else if (have_dnssec_result > 0 && j->dnssec_result != dnssec_result) {
|
||||
have_dnssec_result = false; /* conflicting result seen? then invalidate for good */
|
||||
dnssec_result = _DNSSEC_RESULT_INVALID;
|
||||
}
|
||||
|
||||
/* Append the answer RRs to our answer. Ideally we have the answer object, which we
|
||||
* preferably use. But if the cached entry was generated as "side-effect" of a reply,
|
||||
* i.e. from validated auxiliary records rather than from the main reply, then we use the
|
||||
* individual RRs only instead. */
|
||||
if (j->answer) {
|
||||
|
||||
/* Minor optimization, if the full answer object of this and the previous RR is the
|
||||
* same, don't bother adding it again. Typically we store a full RRset here, hence
|
||||
* that should be the case. */
|
||||
if (!j->by_key_prev || j->answer != j->by_key_prev->answer) {
|
||||
DnsAnswerItem *item;
|
||||
|
||||
DNS_ANSWER_FOREACH_ITEM(item, j->answer) {
|
||||
r = answer_add_clamp_ttl(&answer, item->rr, item->ifindex, item->flags, item->rrsig, query_flags, j->until, current);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (j->rr) {
|
||||
r = answer_add_clamp_ttl(&answer, j->rr, j->ifindex, j->authenticated ? DNS_ANSWER_AUTHENTICATED : 0, NULL, query_flags, j->until, current);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* We'll return any packet we have for this. Typically all cache entries for the same key
|
||||
* should come from the same packet anyway, hence it doesn't really matter which packet we
|
||||
* return here, they should all resolve to the same anyway. */
|
||||
if (!full_packet && j->full_packet)
|
||||
full_packet = dns_packet_ref(j->full_packet);
|
||||
}
|
||||
|
||||
if (found_rcode >= 0) {
|
||||
@ -898,28 +1066,41 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod
|
||||
dns_rcode_to_string(found_rcode),
|
||||
dns_resource_key_to_string(key, key_str, sizeof(key_str)));
|
||||
|
||||
*ret = NULL;
|
||||
*rcode = found_rcode;
|
||||
*authenticated = false;
|
||||
if (ret_rcode)
|
||||
*ret_rcode = found_rcode;
|
||||
if (ret_answer)
|
||||
*ret_answer = TAKE_PTR(answer);
|
||||
if (ret_full_packet)
|
||||
*ret_full_packet = TAKE_PTR(full_packet);
|
||||
if (ret_authenticated)
|
||||
*ret_authenticated = false;
|
||||
if (ret_dnssec_result)
|
||||
*ret_dnssec_result = dnssec_result;
|
||||
|
||||
c->n_hit++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (nsec && !IN_SET(key->type, DNS_TYPE_NSEC, DNS_TYPE_DS)) {
|
||||
/* Note that we won't derive information for DS RRs from an NSEC, because we only cache NSEC RRs from
|
||||
* the lower-zone of a zone cut, but the DS RRs are on the upper zone. */
|
||||
/* Note that we won't derive information for DS RRs from an NSEC, because we only cache NSEC
|
||||
* RRs from the lower-zone of a zone cut, but the DS RRs are on the upper zone. */
|
||||
|
||||
log_debug("NSEC NODATA cache hit for %s",
|
||||
dns_resource_key_to_string(key, key_str, sizeof key_str));
|
||||
|
||||
/* We only found an NSEC record that matches our name.
|
||||
* If it says the type doesn't exist report
|
||||
* NODATA. Otherwise report a cache miss. */
|
||||
/* We only found an NSEC record that matches our name. If it says the type doesn't exist
|
||||
* report NODATA. Otherwise report a cache miss. */
|
||||
|
||||
*ret = NULL;
|
||||
*rcode = DNS_RCODE_SUCCESS;
|
||||
*authenticated = nsec->authenticated;
|
||||
if (ret_rcode)
|
||||
*ret_rcode = DNS_RCODE_SUCCESS;
|
||||
if (ret_answer)
|
||||
*ret_answer = TAKE_PTR(answer);
|
||||
if (ret_full_packet)
|
||||
*ret_full_packet = TAKE_PTR(full_packet);
|
||||
if (ret_authenticated)
|
||||
*ret_authenticated = nsec->authenticated;
|
||||
if (ret_dnssec_result)
|
||||
*ret_dnssec_result = nsec->dnssec_result;
|
||||
|
||||
if (!bitmap_isset(nsec->rr->nsec.types, key->type) &&
|
||||
!bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_CNAME) &&
|
||||
@ -940,46 +1121,49 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod
|
||||
if (n <= 0) {
|
||||
c->n_hit++;
|
||||
|
||||
*ret = NULL;
|
||||
*rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS;
|
||||
*authenticated = have_authenticated && !have_non_authenticated;
|
||||
if (ret_rcode)
|
||||
*ret_rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS;
|
||||
if (ret_answer)
|
||||
*ret_answer = TAKE_PTR(answer);
|
||||
if (ret_full_packet)
|
||||
*ret_full_packet = TAKE_PTR(full_packet);
|
||||
if (ret_authenticated)
|
||||
*ret_authenticated = have_authenticated && !have_non_authenticated;
|
||||
if (ret_dnssec_result)
|
||||
*ret_dnssec_result = dnssec_result;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
answer = dns_answer_new(n);
|
||||
if (!answer)
|
||||
return -ENOMEM;
|
||||
|
||||
if (clamp_ttl)
|
||||
current = now(clock_boottime_or_monotonic());
|
||||
|
||||
LIST_FOREACH(by_key, j, first) {
|
||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
|
||||
|
||||
if (!j->rr)
|
||||
continue;
|
||||
|
||||
if (clamp_ttl) {
|
||||
rr = dns_resource_record_ref(j->rr);
|
||||
|
||||
r = dns_resource_record_clamp_ttl(&rr, LESS_BY(j->until, current) / USEC_PER_SEC);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = dns_answer_add(answer, rr ?: j->rr, j->ifindex, j->authenticated ? DNS_ANSWER_AUTHENTICATED : 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
c->n_hit++;
|
||||
|
||||
*ret = answer;
|
||||
*rcode = DNS_RCODE_SUCCESS;
|
||||
*authenticated = have_authenticated && !have_non_authenticated;
|
||||
answer = NULL;
|
||||
if (ret_rcode)
|
||||
*ret_rcode = DNS_RCODE_SUCCESS;
|
||||
if (ret_answer)
|
||||
*ret_answer = TAKE_PTR(answer);
|
||||
if (ret_full_packet)
|
||||
*ret_full_packet = TAKE_PTR(full_packet);
|
||||
if (ret_authenticated)
|
||||
*ret_authenticated = have_authenticated && !have_non_authenticated;
|
||||
if (ret_dnssec_result)
|
||||
*ret_dnssec_result = dnssec_result;
|
||||
|
||||
return n;
|
||||
|
||||
miss:
|
||||
if (ret_rcode)
|
||||
*ret_rcode = DNS_RCODE_SUCCESS;
|
||||
if (ret_answer)
|
||||
*ret_answer = NULL;
|
||||
if (ret_full_packet)
|
||||
*ret_full_packet = NULL;
|
||||
if (ret_authenticated)
|
||||
*ret_authenticated = false;
|
||||
if (ret_dnssec_result)
|
||||
*ret_dnssec_result = _DNSSEC_RESULT_INVALID;
|
||||
|
||||
c->n_miss++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address) {
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "list.h"
|
||||
#include "prioq.h"
|
||||
#include "resolve-util.h"
|
||||
#include "resolved-dns-dnssec.h"
|
||||
#include "time-util.h"
|
||||
|
||||
typedef struct DnsCache {
|
||||
@ -22,8 +23,28 @@ typedef struct DnsCache {
|
||||
void dns_cache_flush(DnsCache *c);
|
||||
void dns_cache_prune(DnsCache *c);
|
||||
|
||||
int dns_cache_put(DnsCache *c, DnsCacheMode cache_mode, DnsResourceKey *key, int rcode, DnsAnswer *answer, bool authenticated, uint32_t nsec_ttl, usec_t timestamp, int owner_family, const union in_addr_union *owner_address);
|
||||
int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcode, DnsAnswer **answer, bool *authenticated);
|
||||
int dns_cache_put(
|
||||
DnsCache *c,
|
||||
DnsCacheMode cache_mode,
|
||||
DnsResourceKey *key,
|
||||
int rcode,
|
||||
DnsAnswer *answer,
|
||||
DnsPacket *full_packet,
|
||||
bool authenticated,
|
||||
DnssecResult dnssec_result,
|
||||
uint32_t nsec_ttl,
|
||||
int owner_family,
|
||||
const union in_addr_union *owner_address);
|
||||
|
||||
int dns_cache_lookup(
|
||||
DnsCache *c,
|
||||
DnsResourceKey *key,
|
||||
uint64_t query_flags,
|
||||
int *ret_rcode,
|
||||
DnsAnswer **ret_answer,
|
||||
DnsPacket **ret_full_packet,
|
||||
bool *ret_authenticated,
|
||||
DnssecResult *ret_dnssec_result);
|
||||
|
||||
int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address);
|
||||
|
||||
|
@ -160,6 +160,38 @@ int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t min_alloc
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_packet_dup(DnsPacket **ret, DnsPacket *p) {
|
||||
DnsPacket *c;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
assert(p);
|
||||
|
||||
r = dns_packet_validate(p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
c = malloc(ALIGN(sizeof(DnsPacket)) + p->size);
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
*c = (DnsPacket) {
|
||||
.n_ref = 1,
|
||||
.protocol = p->protocol,
|
||||
.size = p->size,
|
||||
.rindex = DNS_PACKET_HEADER_SIZE,
|
||||
.allocated = p->size,
|
||||
.max_size = p->max_size,
|
||||
.opt_start = (size_t) -1,
|
||||
.opt_size = (size_t) -1,
|
||||
};
|
||||
|
||||
memcpy(DNS_PACKET_DATA(c), DNS_PACKET_DATA(p), p->size);
|
||||
|
||||
*ret = c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DnsPacket *dns_packet_ref(DnsPacket *p) {
|
||||
|
||||
if (!p)
|
||||
@ -1183,7 +1215,7 @@ int dns_packet_append_question(DnsPacket *p, DnsQuestion *q) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_packet_append_answer(DnsPacket *p, DnsAnswer *a) {
|
||||
int dns_packet_append_answer(DnsPacket *p, DnsAnswer *a, unsigned *completed) {
|
||||
DnsResourceRecord *rr;
|
||||
DnsAnswerFlags flags;
|
||||
int r;
|
||||
@ -1194,6 +1226,9 @@ int dns_packet_append_answer(DnsPacket *p, DnsAnswer *a) {
|
||||
r = dns_packet_append_rr(p, rr, flags, NULL, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (completed)
|
||||
(*completed)++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1298,7 +1333,8 @@ int dns_packet_read_uint16(DnsPacket *p, uint16_t *ret, size_t *start) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = unaligned_read_be16(d);
|
||||
if (ret)
|
||||
*ret = unaligned_read_be16(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1384,19 +1420,19 @@ int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, siz
|
||||
|
||||
int dns_packet_read_name(
|
||||
DnsPacket *p,
|
||||
char **_ret,
|
||||
char **ret,
|
||||
bool allow_compression,
|
||||
size_t *start) {
|
||||
size_t *ret_start) {
|
||||
|
||||
_cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
|
||||
size_t after_rindex = 0, jump_barrier;
|
||||
_cleanup_free_ char *ret = NULL;
|
||||
_cleanup_free_ char *name = NULL;
|
||||
size_t n = 0, allocated = 0;
|
||||
bool first = true;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(_ret);
|
||||
|
||||
INIT_REWINDER(rewinder, p);
|
||||
jump_barrier = p->rindex;
|
||||
|
||||
@ -1421,15 +1457,15 @@ int dns_packet_read_name(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
|
||||
if (!GREEDY_REALLOC(name, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
|
||||
return -ENOMEM;
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
ret[n++] = '.';
|
||||
name[n++] = '.';
|
||||
|
||||
r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
|
||||
r = dns_label_escape(label, c, name + n, DNS_LABEL_ESCAPED_MAX);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -1457,18 +1493,19 @@ int dns_packet_read_name(
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
if (!GREEDY_REALLOC(ret, allocated, n + 1))
|
||||
if (!GREEDY_REALLOC(name, allocated, n + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
ret[n] = 0;
|
||||
name[n] = 0;
|
||||
|
||||
if (after_rindex != 0)
|
||||
p->rindex= after_rindex;
|
||||
|
||||
*_ret = TAKE_PTR(ret);
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(name);
|
||||
if (ret_start)
|
||||
*ret_start = rewinder.saved_rindex;
|
||||
|
||||
if (start)
|
||||
*start = rewinder.saved_rindex;
|
||||
CANCEL_REWINDER(rewinder);
|
||||
|
||||
return 0;
|
||||
@ -1570,16 +1607,19 @@ static int dns_packet_read_type_windows(DnsPacket *p, Bitmap **types, size_t siz
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush, size_t *start) {
|
||||
int dns_packet_read_key(
|
||||
DnsPacket *p,
|
||||
DnsResourceKey **ret,
|
||||
bool *ret_cache_flush,
|
||||
size_t *ret_start) {
|
||||
|
||||
_cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
|
||||
_cleanup_free_ char *name = NULL;
|
||||
bool cache_flush = false;
|
||||
uint16_t class, type;
|
||||
DnsResourceKey *key;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(ret);
|
||||
INIT_REWINDER(rewinder, p);
|
||||
|
||||
r = dns_packet_read_name(p, &name, true, NULL);
|
||||
@ -1603,19 +1643,23 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flus
|
||||
}
|
||||
}
|
||||
|
||||
key = dns_resource_key_new_consume(class, type, name);
|
||||
if (!key)
|
||||
return -ENOMEM;
|
||||
if (ret) {
|
||||
DnsResourceKey *key;
|
||||
|
||||
name = NULL;
|
||||
*ret = key;
|
||||
key = dns_resource_key_new_consume(class, type, name);
|
||||
if (!key)
|
||||
return -ENOMEM;
|
||||
|
||||
TAKE_PTR(name);
|
||||
*ret = key;
|
||||
}
|
||||
|
||||
if (ret_cache_flush)
|
||||
*ret_cache_flush = cache_flush;
|
||||
if (start)
|
||||
*start = rewinder.saved_rindex;
|
||||
CANCEL_REWINDER(rewinder);
|
||||
if (ret_start)
|
||||
*ret_start = rewinder.saved_rindex;
|
||||
|
||||
CANCEL_REWINDER(rewinder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1625,7 +1669,12 @@ static bool loc_size_ok(uint8_t size) {
|
||||
return m <= 9 && e <= 9 && (m > 0 || e == 0);
|
||||
}
|
||||
|
||||
int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_flush, size_t *start) {
|
||||
int dns_packet_read_rr(
|
||||
DnsPacket *p,
|
||||
DnsResourceRecord **ret,
|
||||
bool *ret_cache_flush,
|
||||
size_t *ret_start) {
|
||||
|
||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
|
||||
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
|
||||
_cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
|
||||
@ -1635,7 +1684,6 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(ret);
|
||||
|
||||
INIT_REWINDER(rewinder, p);
|
||||
|
||||
@ -2076,14 +2124,14 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
|
||||
if (p->rindex != offset + rdlength)
|
||||
return -EBADMSG;
|
||||
|
||||
*ret = TAKE_PTR(rr);
|
||||
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(rr);
|
||||
if (ret_cache_flush)
|
||||
*ret_cache_flush = cache_flush;
|
||||
if (start)
|
||||
*start = rewinder.saved_rindex;
|
||||
CANCEL_REWINDER(rewinder);
|
||||
if (ret_start)
|
||||
*ret_start = rewinder.saved_rindex;
|
||||
|
||||
CANCEL_REWINDER(rewinder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2203,8 +2251,9 @@ static int dns_packet_extract_answer(DnsPacket *p, DnsAnswer **ret_answer) {
|
||||
for (i = 0; i < n; i++) {
|
||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
|
||||
bool cache_flush = false;
|
||||
size_t start;
|
||||
|
||||
r = dns_packet_read_rr(p, &rr, &cache_flush, NULL);
|
||||
r = dns_packet_read_rr(p, &rr, &cache_flush, &start);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -2272,15 +2321,26 @@ static int dns_packet_extract_answer(DnsPacket *p, DnsAnswer **ret_answer) {
|
||||
}
|
||||
|
||||
p->opt = dns_resource_record_ref(rr);
|
||||
p->opt_start = start;
|
||||
assert(p->rindex >= start);
|
||||
p->opt_size = p->rindex - start;
|
||||
} else {
|
||||
/* According to RFC 4795, section 2.9. only the RRs from the Answer section
|
||||
* shall be cached. Hence mark only those RRs as cacheable by default, but
|
||||
* not the ones from the Additional or Authority sections. */
|
||||
DnsAnswerFlags flags =
|
||||
(i < DNS_PACKET_ANCOUNT(p) ? DNS_ANSWER_CACHEABLE : 0) |
|
||||
(p->protocol == DNS_PROTOCOL_MDNS && !cache_flush ? DNS_ANSWER_SHARED_OWNER : 0);
|
||||
DnsAnswerFlags flags = 0;
|
||||
|
||||
r = dns_answer_add(answer, rr, p->ifindex, flags);
|
||||
if (p->protocol == DNS_PROTOCOL_MDNS && !cache_flush)
|
||||
flags |= DNS_ANSWER_SHARED_OWNER;
|
||||
|
||||
/* According to RFC 4795, section 2.9. only the RRs from the Answer section shall be
|
||||
* cached. Hence mark only those RRs as cacheable by default, but not the ones from
|
||||
* the Additional or Authority sections. */
|
||||
if (i < DNS_PACKET_ANCOUNT(p))
|
||||
flags |= DNS_ANSWER_CACHEABLE|DNS_ANSWER_SECTION_ANSWER;
|
||||
else if (i < DNS_PACKET_ANCOUNT(p) + DNS_PACKET_NSCOUNT(p))
|
||||
flags |= DNS_ANSWER_SECTION_AUTHORITY;
|
||||
else
|
||||
flags |= DNS_ANSWER_SECTION_ADDITIONAL;
|
||||
|
||||
r = dns_answer_add(answer, rr, p->ifindex, flags, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -2356,6 +2416,106 @@ int dns_packet_is_reply_for(DnsPacket *p, const DnsResourceKey *key) {
|
||||
return dns_resource_key_equal(p->question->keys[0], key);
|
||||
}
|
||||
|
||||
int dns_packet_patch_max_udp_size(DnsPacket *p, uint16_t max_udp_size) {
|
||||
assert(p);
|
||||
assert(max_udp_size >= DNS_PACKET_UNICAST_SIZE_MAX);
|
||||
|
||||
if (p->opt_start == (size_t) -1) /* No OPT section, nothing to patch */
|
||||
return 0;
|
||||
|
||||
assert(p->opt_size != (size_t) -1);
|
||||
assert(p->opt_size >= 5);
|
||||
|
||||
unaligned_write_be16(DNS_PACKET_DATA(p) + p->opt_start + 3, max_udp_size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int patch_rr(DnsPacket *p, usec_t age) {
|
||||
_cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
|
||||
size_t ttl_index;
|
||||
uint32_t ttl;
|
||||
uint16_t type, rdlength;
|
||||
int r;
|
||||
|
||||
INIT_REWINDER(rewinder, p);
|
||||
|
||||
/* Patches the RR at the current rindex, substracts the specified time from the TTL */
|
||||
|
||||
r = dns_packet_read_name(p, NULL, true, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_packet_read_uint16(p, &type, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_packet_read_uint16(p, NULL, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_packet_read_uint32(p, &ttl, &ttl_index);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (type != DNS_TYPE_OPT) { /* The TTL of the OPT field is not actually a TTL, skip it */
|
||||
ttl = LESS_BY(ttl * USEC_PER_SEC, age) / USEC_PER_SEC;
|
||||
unaligned_write_be32(DNS_PACKET_DATA(p) + ttl_index, ttl);
|
||||
}
|
||||
|
||||
r = dns_packet_read_uint16(p, &rdlength, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_packet_read(p, rdlength, NULL, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
CANCEL_REWINDER(rewinder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_packet_patch_ttls(DnsPacket *p, usec_t timestamp) {
|
||||
_cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = {};
|
||||
unsigned i, n;
|
||||
usec_t k;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(timestamp_is_set(timestamp));
|
||||
|
||||
/* Adjusts all TTLs in the packet by subtracting the time difference between now and the specified timestamp */
|
||||
|
||||
k = now(clock_boottime_or_monotonic());
|
||||
assert(k >= timestamp);
|
||||
k -= timestamp;
|
||||
|
||||
INIT_REWINDER(rewinder, p);
|
||||
|
||||
dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE);
|
||||
|
||||
n = DNS_PACKET_QDCOUNT(p);
|
||||
for (i = 0; i < n; i++) {
|
||||
r = dns_packet_read_key(p, NULL, NULL, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
n = DNS_PACKET_RRCOUNT(p);
|
||||
for (i = 0; i < n; i++) {
|
||||
|
||||
/* DNS servers suck, hence the RR count is in many servers off. If we reached the end
|
||||
* prematurely, accept that, exit early */
|
||||
if (p->rindex == p->size)
|
||||
break;
|
||||
|
||||
r = patch_rr(p, k);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dns_packet_hash_func(const DnsPacket *s, struct siphash *state) {
|
||||
assert(s);
|
||||
|
||||
|
@ -176,6 +176,8 @@ static inline unsigned DNS_PACKET_RRCOUNT(DnsPacket *p) {
|
||||
int dns_packet_new(DnsPacket **p, DnsProtocol protocol, size_t min_alloc_dsize, size_t max_size);
|
||||
int dns_packet_new_query(DnsPacket **p, DnsProtocol protocol, size_t min_alloc_dsize, bool dnssec_checking_disabled);
|
||||
|
||||
int dns_packet_dup(DnsPacket **ret, DnsPacket *p);
|
||||
|
||||
void dns_packet_set_flags(DnsPacket *p, bool dnssec_checking_disabled, bool truncated);
|
||||
|
||||
DnsPacket *dns_packet_ref(DnsPacket *p);
|
||||
@ -201,7 +203,10 @@ int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *key, const DnsAnsw
|
||||
int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAnswerFlags flags, size_t *start, size_t *rdata_start);
|
||||
int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, bool include_rfc6975, int rcode, size_t *start);
|
||||
int dns_packet_append_question(DnsPacket *p, DnsQuestion *q);
|
||||
int dns_packet_append_answer(DnsPacket *p, DnsAnswer *a);
|
||||
int dns_packet_append_answer(DnsPacket *p, DnsAnswer *a, unsigned *completed);
|
||||
|
||||
int dns_packet_patch_max_udp_size(DnsPacket *p, uint16_t max_udp_size);
|
||||
int dns_packet_patch_ttls(DnsPacket *p, usec_t timestamp);
|
||||
|
||||
void dns_packet_truncate(DnsPacket *p, size_t sz);
|
||||
int dns_packet_truncate_opt(DnsPacket *p);
|
||||
|
@ -97,20 +97,35 @@ static int dns_query_candidate_next_search_domain(DnsQueryCandidate *c) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResourceKey *key) {
|
||||
static int dns_query_candidate_add_transaction(
|
||||
DnsQueryCandidate *c,
|
||||
DnsResourceKey *key,
|
||||
DnsPacket *bypass) {
|
||||
|
||||
_cleanup_(dns_transaction_gcp) DnsTransaction *t = NULL;
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
assert(key);
|
||||
|
||||
t = dns_scope_find_transaction(c->scope, key, true);
|
||||
if (!t) {
|
||||
r = dns_transaction_new(&t, c->scope, key);
|
||||
if (key) {
|
||||
/* Regular lookup with a resource key */
|
||||
assert(!bypass);
|
||||
|
||||
t = dns_scope_find_transaction(c->scope, key, c->query->flags);
|
||||
if (!t) {
|
||||
r = dns_transaction_new(&t, c->scope, key, NULL, c->query->flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else if (set_contains(c->transactions, t))
|
||||
return 0;
|
||||
} else {
|
||||
/* "Bypass" lookup with a query packet */
|
||||
assert(bypass);
|
||||
|
||||
r = dns_transaction_new(&t, c->scope, NULL, bypass, c->query->flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else if (set_contains(c->transactions, t))
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = set_ensure_allocated(&t->notify_query_candidates_done, NULL);
|
||||
if (r < 0)
|
||||
@ -126,7 +141,6 @@ static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResource
|
||||
return r;
|
||||
}
|
||||
|
||||
t->clamp_ttl = c->query->clamp_ttl;
|
||||
TAKE_PTR(t);
|
||||
return 1;
|
||||
}
|
||||
@ -214,6 +228,21 @@ static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
|
||||
|
||||
dns_query_candidate_stop(c);
|
||||
|
||||
if (c->query->question_bypass) {
|
||||
/* If this is a bypass query, then pass the original query packet along to the transaction */
|
||||
|
||||
assert(dns_question_size(c->query->question_bypass->question) == 1);
|
||||
|
||||
if (!dns_scope_good_key(c->scope, c->query->question_bypass->question->keys[0]))
|
||||
return 0;
|
||||
|
||||
r = dns_query_candidate_add_transaction(c, NULL, c->query->question_bypass);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
question = dns_query_question_for_protocol(c->query, c->scope->protocol);
|
||||
|
||||
/* Create one transaction per question key */
|
||||
@ -233,7 +262,7 @@ static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
|
||||
if (!dns_scope_good_key(c->scope, qkey))
|
||||
continue;
|
||||
|
||||
r = dns_query_candidate_add_transaction(c, qkey);
|
||||
r = dns_query_candidate_add_transaction(c, qkey, NULL);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
@ -321,6 +350,7 @@ static void dns_query_reset_answer(DnsQuery *q) {
|
||||
q->answer_protocol = _DNS_PROTOCOL_INVALID;
|
||||
q->answer_family = AF_UNSPEC;
|
||||
q->answer_search_domain = dns_search_domain_unref(q->answer_search_domain);
|
||||
q->answer_full_packet = dns_packet_unref(q->answer_full_packet);
|
||||
}
|
||||
|
||||
DnsQuery *dns_query_free(DnsQuery *q) {
|
||||
@ -340,6 +370,7 @@ DnsQuery *dns_query_free(DnsQuery *q) {
|
||||
|
||||
dns_question_unref(q->question_idna);
|
||||
dns_question_unref(q->question_utf8);
|
||||
dns_packet_unref(q->question_bypass);
|
||||
|
||||
dns_query_reset_answer(q);
|
||||
|
||||
@ -351,13 +382,15 @@ DnsQuery *dns_query_free(DnsQuery *q) {
|
||||
varlink_unref(q->varlink_request);
|
||||
}
|
||||
|
||||
dns_packet_unref(q->request_dns_packet);
|
||||
dns_packet_unref(q->reply_dns_packet);
|
||||
dns_packet_unref(q->request_packet);
|
||||
dns_answer_unref(q->reply_answer);
|
||||
dns_answer_unref(q->reply_authoritative);
|
||||
dns_answer_unref(q->reply_additional);
|
||||
|
||||
if (q->request_dns_stream) {
|
||||
if (q->request_stream) {
|
||||
/* Detach the stream from our query, in case something else keeps a reference to it. */
|
||||
(void) set_remove(q->request_dns_stream->queries, q);
|
||||
q->request_dns_stream = dns_stream_unref(q->request_dns_stream);
|
||||
(void) set_remove(q->request_stream->queries, q);
|
||||
q->request_stream = dns_stream_unref(q->request_stream);
|
||||
}
|
||||
|
||||
free(q->request_address_string);
|
||||
@ -375,36 +408,27 @@ int dns_query_new(
|
||||
DnsQuery **ret,
|
||||
DnsQuestion *question_utf8,
|
||||
DnsQuestion *question_idna,
|
||||
DnsPacket *question_bypass,
|
||||
int ifindex,
|
||||
uint64_t flags) {
|
||||
|
||||
_cleanup_(dns_query_freep) DnsQuery *q = NULL;
|
||||
DnsResourceKey *key;
|
||||
bool good = false;
|
||||
int r;
|
||||
char key_str[DNS_RESOURCE_KEY_STRING_MAX];
|
||||
DnsResourceKey *key;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
if (dns_question_size(question_utf8) > 0) {
|
||||
r = dns_question_is_valid_for_query(question_utf8);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
if (question_bypass) {
|
||||
/* It's either a "bypass" query, or a regular one, but can't be both. */
|
||||
if (question_utf8 || question_idna)
|
||||
return -EINVAL;
|
||||
|
||||
good = true;
|
||||
}
|
||||
} else {
|
||||
bool good = false;
|
||||
|
||||
/* If the IDNA and UTF8 questions are the same, merge their references */
|
||||
r = dns_question_is_equal(question_idna, question_utf8);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
question_idna = question_utf8;
|
||||
else {
|
||||
if (dns_question_size(question_idna) > 0) {
|
||||
r = dns_question_is_valid_for_query(question_idna);
|
||||
if (dns_question_size(question_utf8) > 0) {
|
||||
r = dns_question_is_valid_for_query(question_utf8);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
@ -412,10 +436,28 @@ int dns_query_new(
|
||||
|
||||
good = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!good) /* don't allow empty queries */
|
||||
return -EINVAL;
|
||||
/* If the IDNA and UTF8 questions are the same, merge their references */
|
||||
r = dns_question_is_equal(question_idna, question_utf8);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
question_idna = question_utf8;
|
||||
else {
|
||||
if (dns_question_size(question_idna) > 0) {
|
||||
r = dns_question_is_valid_for_query(question_idna);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EINVAL;
|
||||
|
||||
good = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!good) /* don't allow empty queries */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (m->n_dns_queries >= QUERIES_MAX)
|
||||
return -EBUSY;
|
||||
@ -427,6 +469,7 @@ int dns_query_new(
|
||||
*q = (DnsQuery) {
|
||||
.question_utf8 = dns_question_ref(question_utf8),
|
||||
.question_idna = dns_question_ref(question_idna),
|
||||
.question_bypass = dns_packet_ref(question_bypass),
|
||||
.ifindex = ifindex,
|
||||
.flags = flags,
|
||||
.answer_dnssec_result = _DNSSEC_RESULT_INVALID,
|
||||
@ -434,21 +477,27 @@ int dns_query_new(
|
||||
.answer_family = AF_UNSPEC,
|
||||
};
|
||||
|
||||
/* First dump UTF8 question */
|
||||
DNS_QUESTION_FOREACH(key, question_utf8)
|
||||
log_debug("Looking up RR for %s.",
|
||||
dns_resource_key_to_string(key, key_str, sizeof key_str));
|
||||
if (question_bypass) {
|
||||
DNS_QUESTION_FOREACH(key, question_bypass->question)
|
||||
log_debug("Looking up bypass packet for %s.",
|
||||
dns_resource_key_to_string(key, key_str, sizeof key_str));
|
||||
} else {
|
||||
/* First dump UTF8 question */
|
||||
DNS_QUESTION_FOREACH(key, question_utf8)
|
||||
log_debug("Looking up RR for %s.",
|
||||
dns_resource_key_to_string(key, key_str, sizeof key_str));
|
||||
|
||||
/* And then dump the IDNA question, but only what hasn't been dumped already through the UTF8 question. */
|
||||
DNS_QUESTION_FOREACH(key, question_idna) {
|
||||
r = dns_question_contains(question_utf8, key);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
continue;
|
||||
/* And then dump the IDNA question, but only what hasn't been dumped already through the UTF8 question. */
|
||||
DNS_QUESTION_FOREACH(key, question_idna) {
|
||||
r = dns_question_contains(question_utf8, key);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
continue;
|
||||
|
||||
log_debug("Looking up IDNA RR for %s.",
|
||||
dns_resource_key_to_string(key, key_str, sizeof key_str));
|
||||
log_debug("Looking up IDNA RR for %s.",
|
||||
dns_resource_key_to_string(key, key_str, sizeof key_str));
|
||||
}
|
||||
}
|
||||
|
||||
LIST_PREPEND(queries, m->dns_queries, q);
|
||||
@ -457,8 +506,8 @@ int dns_query_new(
|
||||
|
||||
if (ret)
|
||||
*ret = q;
|
||||
q = NULL;
|
||||
|
||||
TAKE_PTR(q);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -559,9 +608,12 @@ static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
|
||||
DNS_TRANSACTION_NOT_FOUND))
|
||||
return 0;
|
||||
|
||||
if (FLAGS_SET(q->flags, SD_RESOLVED_NO_SYNTHESIZE))
|
||||
return 0;
|
||||
|
||||
r = dns_synthesize_answer(
|
||||
q->manager,
|
||||
q->question_utf8,
|
||||
q->question_bypass ? q->question_bypass->question : q->question_utf8,
|
||||
q->ifindex,
|
||||
&answer);
|
||||
if (r == -ENXIO) {
|
||||
@ -601,9 +653,12 @@ static int dns_query_try_etc_hosts(DnsQuery *q) {
|
||||
/* Looks in /etc/hosts for matching entries. Note that this is done *before* the normal lookup is
|
||||
* done. The data from /etc/hosts hence takes precedence over the network. */
|
||||
|
||||
if (FLAGS_SET(q->flags, SD_RESOLVED_NO_SYNTHESIZE))
|
||||
return 0;
|
||||
|
||||
r = manager_etc_hosts_lookup(
|
||||
q->manager,
|
||||
q->question_utf8,
|
||||
q->question_bypass ? q->question_bypass->question : q->question_utf8,
|
||||
&answer);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
@ -757,6 +812,7 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
|
||||
q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
|
||||
q->answer_authenticated = false;
|
||||
q->answer_errno = c->error_code;
|
||||
q->answer_full_packet = dns_packet_unref(q->answer_full_packet);
|
||||
}
|
||||
|
||||
SET_FOREACH(t, c->transactions) {
|
||||
@ -764,14 +820,24 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
|
||||
switch (t->state) {
|
||||
|
||||
case DNS_TRANSACTION_SUCCESS: {
|
||||
/* We found a successfully reply, merge it into the answer */
|
||||
r = dns_answer_extend(&q->answer, t->answer);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
/* We found a successful reply, merge it into the answer */
|
||||
|
||||
if (state == DNS_TRANSACTION_SUCCESS) {
|
||||
r = dns_answer_extend(&q->answer, t->answer);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
} else {
|
||||
/* Override non-successful previous answers */
|
||||
dns_answer_unref(q->answer);
|
||||
q->answer = dns_answer_ref(t->answer);
|
||||
}
|
||||
|
||||
q->answer_rcode = t->answer_rcode;
|
||||
q->answer_errno = 0;
|
||||
|
||||
dns_packet_unref(q->answer_full_packet);
|
||||
q->answer_full_packet = dns_packet_ref(t->received);
|
||||
|
||||
if (t->answer_authenticated) {
|
||||
has_authenticated = true;
|
||||
dnssec_result_authenticated = t->answer_dnssec_result;
|
||||
@ -800,11 +866,14 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
|
||||
if (q->answer_authenticated && !t->answer_authenticated)
|
||||
continue;
|
||||
|
||||
q->answer = dns_answer_unref(q->answer);
|
||||
dns_answer_unref(q->answer);
|
||||
q->answer = dns_answer_ref(t->answer);
|
||||
q->answer_rcode = t->answer_rcode;
|
||||
q->answer_dnssec_result = t->answer_dnssec_result;
|
||||
q->answer_authenticated = t->answer_authenticated;
|
||||
q->answer_errno = t->answer_errno;
|
||||
dns_packet_unref(q->answer_full_packet);
|
||||
q->answer_full_packet = dns_packet_ref(t->received);
|
||||
|
||||
state = t->state;
|
||||
break;
|
||||
@ -998,6 +1067,9 @@ int dns_query_process_cname(DnsQuery *q) {
|
||||
DnsQuestion* dns_query_question_for_protocol(DnsQuery *q, DnsProtocol protocol) {
|
||||
assert(q);
|
||||
|
||||
if (q->question_bypass)
|
||||
return q->question_bypass->question;
|
||||
|
||||
switch (protocol) {
|
||||
|
||||
case DNS_PROTOCOL_DNS:
|
||||
@ -1018,6 +1090,9 @@ const char *dns_query_string(DnsQuery *q) {
|
||||
|
||||
/* Returns a somewhat useful human-readable lookup key string for this query */
|
||||
|
||||
if (q->question_bypass)
|
||||
return dns_question_first_name(q->question_bypass->question);
|
||||
|
||||
if (q->request_address_string)
|
||||
return q->request_address_string;
|
||||
|
||||
|
@ -49,12 +49,13 @@ struct DnsQuery {
|
||||
DnsQuestion *question_idna;
|
||||
DnsQuestion *question_utf8;
|
||||
|
||||
/* If this is not a question by ourselves, but a "bypass" request, we propagate the original packet
|
||||
* here, and use that instead. */
|
||||
DnsPacket *question_bypass;
|
||||
|
||||
uint64_t flags;
|
||||
int ifindex;
|
||||
|
||||
/* If true, the RR TTLs of the answer will be clamped by their current left validity in the cache */
|
||||
bool clamp_ttl;
|
||||
|
||||
DnsTransactionState state;
|
||||
unsigned n_cname_redirects;
|
||||
|
||||
@ -71,6 +72,7 @@ struct DnsQuery {
|
||||
DnsSearchDomain *answer_search_domain;
|
||||
int answer_errno; /* if state is DNS_TRANSACTION_ERRNO */
|
||||
bool previous_redirect_unauthenticated;
|
||||
DnsPacket *answer_full_packet;
|
||||
|
||||
/* Bus + Varlink client information */
|
||||
sd_bus_message *bus_request;
|
||||
@ -82,9 +84,11 @@ struct DnsQuery {
|
||||
char *request_address_string;
|
||||
|
||||
/* DNS stub information */
|
||||
DnsPacket *request_dns_packet;
|
||||
DnsStream *request_dns_stream;
|
||||
DnsPacket *reply_dns_packet;
|
||||
DnsPacket *request_packet;
|
||||
DnsStream *request_stream;
|
||||
DnsAnswer *reply_answer;
|
||||
DnsAnswer *reply_authoritative;
|
||||
DnsAnswer *reply_additional;
|
||||
DnsStubListenerExtra *stub_listener_extra;
|
||||
|
||||
/* Completion callback */
|
||||
@ -109,7 +113,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQueryCandidate*, dns_query_candidate_unref);
|
||||
|
||||
void dns_query_candidate_notify(DnsQueryCandidate *c);
|
||||
|
||||
int dns_query_new(Manager *m, DnsQuery **q, DnsQuestion *question_utf8, DnsQuestion *question_idna, int family, uint64_t flags);
|
||||
int dns_query_new(Manager *m, DnsQuery **q, DnsQuestion *question_utf8, DnsQuestion *question_idna, DnsPacket *question_bypass, int family, uint64_t flags);
|
||||
DnsQuery *dns_query_free(DnsQuery *q);
|
||||
|
||||
int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for);
|
||||
|
@ -646,11 +646,10 @@ bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key) {
|
||||
assert(s);
|
||||
assert(key);
|
||||
|
||||
/* Check if it makes sense to resolve the specified key on
|
||||
* this scope. Note that this call assumes as fully qualified
|
||||
* name, i.e. the search suffixes already appended. */
|
||||
/* Check if it makes sense to resolve the specified key on this scope. Note that this call assumes a
|
||||
* fully qualified name, i.e. the search suffixes already appended. */
|
||||
|
||||
if (key->class != DNS_CLASS_IN)
|
||||
if (!IN_SET(key->class, DNS_CLASS_IN, DNS_CLASS_ANY))
|
||||
return false;
|
||||
|
||||
if (s->protocol == DNS_PROTOCOL_DNS) {
|
||||
@ -672,8 +671,7 @@ bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key) {
|
||||
return !dns_name_is_root(name);
|
||||
}
|
||||
|
||||
/* On mDNS and LLMNR, send A and AAAA queries only on the
|
||||
* respective scopes */
|
||||
/* On mDNS and LLMNR, send A and AAAA queries only on the respective scopes */
|
||||
|
||||
key_family = dns_type_to_af(key->type);
|
||||
if (key_family < 0)
|
||||
@ -765,6 +763,7 @@ int dns_scope_make_reply_packet(
|
||||
DnsPacket **ret) {
|
||||
|
||||
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
|
||||
unsigned n_answer = 0, n_soa = 0;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
@ -796,15 +795,15 @@ int dns_scope_make_reply_packet(
|
||||
return r;
|
||||
DNS_PACKET_HEADER(p)->qdcount = htobe16(dns_question_size(q));
|
||||
|
||||
r = dns_packet_append_answer(p, answer);
|
||||
r = dns_packet_append_answer(p, answer, &n_answer);
|
||||
if (r < 0)
|
||||
return r;
|
||||
DNS_PACKET_HEADER(p)->ancount = htobe16(dns_answer_size(answer));
|
||||
DNS_PACKET_HEADER(p)->ancount = htobe16(n_answer);
|
||||
|
||||
r = dns_packet_append_answer(p, soa);
|
||||
r = dns_packet_append_answer(p, soa, &n_soa);
|
||||
if (r < 0)
|
||||
return r;
|
||||
DNS_PACKET_HEADER(p)->arcount = htobe16(dns_answer_size(soa));
|
||||
DNS_PACKET_HEADER(p)->arcount = htobe16(n_soa);
|
||||
|
||||
*ret = TAKE_PTR(p);
|
||||
|
||||
@ -926,26 +925,50 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
|
||||
}
|
||||
}
|
||||
|
||||
DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key, bool cache_ok) {
|
||||
DnsTransaction *t;
|
||||
DnsTransaction *dns_scope_find_transaction(
|
||||
DnsScope *scope,
|
||||
DnsResourceKey *key,
|
||||
uint64_t query_flags) {
|
||||
|
||||
DnsTransaction *first, *t;
|
||||
|
||||
assert(scope);
|
||||
assert(key);
|
||||
|
||||
/* Try to find an ongoing transaction that is a equal to the
|
||||
* specified question */
|
||||
t = hashmap_get(scope->transactions_by_key, key);
|
||||
if (!t)
|
||||
return NULL;
|
||||
/* Iterate through the list of transactions with a matching key */
|
||||
first = hashmap_get(scope->transactions_by_key, key);
|
||||
LIST_FOREACH(transactions_by_key, t, first) {
|
||||
|
||||
/* Refuse reusing transactions that completed based on cached
|
||||
* data instead of a real packet, if that's requested. */
|
||||
if (!cache_ok &&
|
||||
IN_SET(t->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_RCODE_FAILURE) &&
|
||||
t->answer_source != DNS_TRANSACTION_NETWORK)
|
||||
return NULL;
|
||||
/* These four flags must match exactly: we cannot use a validated response for a
|
||||
* non-validating client, and we cannot use a non-validated response for a validating
|
||||
* client. Similar, if the sources don't match things aren't usable either. */
|
||||
if (((query_flags ^ t->query_flags) &
|
||||
(SD_RESOLVED_NO_VALIDATE|
|
||||
SD_RESOLVED_NO_ZONE|
|
||||
SD_RESOLVED_NO_TRUST_ANCHOR|
|
||||
SD_RESOLVED_NO_NETWORK)) != 0)
|
||||
continue;
|
||||
|
||||
return t;
|
||||
/* We can reuse a primary query if a regular one is requested, but not vice versa */
|
||||
if ((query_flags & SD_RESOLVED_REQUIRE_PRIMARY) &&
|
||||
!(t->query_flags & SD_RESOLVED_REQUIRE_PRIMARY))
|
||||
continue;
|
||||
|
||||
/* Don't reuse a transaction that allowed caching when we got told not to use it */
|
||||
if ((query_flags & SD_RESOLVED_NO_CACHE) &&
|
||||
!(t->query_flags & SD_RESOLVED_NO_CACHE))
|
||||
continue;
|
||||
|
||||
/* If we are are asked to clamp ttls an the existing transaction doesn't do it, we can't
|
||||
* reuse */
|
||||
if ((query_flags & SD_RESOLVED_CLAMP_TTL) &&
|
||||
!(t->query_flags & SD_RESOLVED_CLAMP_TTL))
|
||||
continue;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int dns_scope_make_conflict_packet(
|
||||
@ -1289,7 +1312,7 @@ int dns_scope_announce(DnsScope *scope, bool goodbye) {
|
||||
else
|
||||
flags = goodbye ? (DNS_ANSWER_GOODBYE|DNS_ANSWER_CACHE_FLUSH) : DNS_ANSWER_CACHE_FLUSH;
|
||||
|
||||
r = dns_answer_add(answer, i->rr, 0 , flags);
|
||||
r = dns_answer_add(answer, i->rr, 0, flags, NULL);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to add RR to announce: %m");
|
||||
}
|
||||
@ -1307,7 +1330,7 @@ int dns_scope_announce(DnsScope *scope, bool goodbye) {
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to add DNS-SD PTR record to MDNS zone: %m");
|
||||
|
||||
r = dns_answer_add(answer, rr, 0 , 0);
|
||||
r = dns_answer_add(answer, rr, 0, 0, NULL);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to add RR to announce: %m");
|
||||
}
|
||||
|
@ -53,14 +53,12 @@ struct DnsScope {
|
||||
|
||||
LIST_HEAD(DnsQueryCandidate, query_candidates);
|
||||
|
||||
/* Note that we keep track of ongoing transactions in two
|
||||
* ways: once in a hashmap, indexed by the rr key, and once in
|
||||
* a linked list. We use the hashmap to quickly find
|
||||
* transactions we can reuse for a key. But note that there
|
||||
* might be multiple transactions for the same key (because
|
||||
* the zone probing can't reuse a transaction answered from
|
||||
* the zone or the cache), and the hashmap only tracks the
|
||||
* most recent entry. */
|
||||
/* Note that we keep track of ongoing transactions in two ways: once in a hashmap, indexed by the rr
|
||||
* key, and once in a linked list. We use the hashmap to quickly find transactions we can reuse for a
|
||||
* key. But note that there might be multiple transactions for the same key (because the associated
|
||||
* query flags might differ in incompatible ways: e.g. we may not reuse a non-validating transaction
|
||||
* as validating. Hence we maintain a per-key list of transactions, which we iterate through to find
|
||||
* one we can reuse with matching flags. */
|
||||
Hashmap *transactions_by_key;
|
||||
LIST_HEAD(DnsTransaction, transactions);
|
||||
|
||||
@ -90,7 +88,7 @@ int dns_scope_mdns_membership(DnsScope *s, bool b);
|
||||
int dns_scope_make_reply_packet(DnsScope *s, uint16_t id, int rcode, DnsQuestion *q, DnsAnswer *answer, DnsAnswer *soa, bool tentative, DnsPacket **ret);
|
||||
void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p);
|
||||
|
||||
DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key, bool cache_ok);
|
||||
DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key, uint64_t query_flags);
|
||||
|
||||
int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr);
|
||||
void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p);
|
||||
|
@ -85,69 +85,259 @@ DnsStubListenerExtra *dns_stub_listener_extra_free(DnsStubListenerExtra *p) {
|
||||
return mfree(p);
|
||||
}
|
||||
|
||||
static int dns_stub_make_reply_packet(
|
||||
DnsPacket **p,
|
||||
size_t max_size,
|
||||
DnsQuestion *q,
|
||||
static int dns_stub_collect_answer_by_question(
|
||||
DnsAnswer **reply,
|
||||
DnsAnswer *answer,
|
||||
bool *ret_truncated) {
|
||||
DnsQuestion *question,
|
||||
bool with_rrsig) { /* Add RRSIG RR matching each RR */
|
||||
|
||||
bool truncated = false;
|
||||
DnsResourceRecord *rr;
|
||||
DnsAnswerItem *item;
|
||||
int r;
|
||||
|
||||
assert(reply);
|
||||
|
||||
/* Copies all RRs from 'answer' into 'reply', if they match 'question'. */
|
||||
|
||||
DNS_ANSWER_FOREACH_ITEM(item, answer) {
|
||||
|
||||
if (question) {
|
||||
bool match = false;
|
||||
|
||||
r = dns_question_matches_rr(question, item->rr, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else if (r > 0)
|
||||
match = true;
|
||||
else {
|
||||
r = dns_question_matches_cname_or_dname(question, item->rr, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
match = true;
|
||||
}
|
||||
|
||||
if (!match)
|
||||
continue;
|
||||
}
|
||||
|
||||
r = dns_answer_add_extend(reply, item->rr, item->ifindex, item->flags, item->rrsig);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (with_rrsig && item->rrsig) {
|
||||
r = dns_answer_add_extend(reply, item->rrsig, item->ifindex, item->flags, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dns_stub_collect_answer_by_section(
|
||||
DnsAnswer **reply,
|
||||
DnsAnswer *answer,
|
||||
DnsAnswerFlags section,
|
||||
DnsAnswer *exclude1,
|
||||
DnsAnswer *exclude2,
|
||||
bool with_dnssec) { /* Include DNSSEC RRs. RRSIG, NSEC, … */
|
||||
|
||||
DnsAnswerItem *item;
|
||||
unsigned c = 0;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(reply);
|
||||
|
||||
/* Note that we don't bother with any additional RRs, as this is stub is for local lookups only, and hence
|
||||
* roundtrips aren't expensive. */
|
||||
/* Copies all RRs from 'answer' into 'reply', if they originate from the specified section. Also,
|
||||
* avoid any RRs listed in 'exclude'. */
|
||||
|
||||
if (!*p) {
|
||||
r = dns_packet_new(p, DNS_PROTOCOL_DNS, 0, max_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
DNS_ANSWER_FOREACH_ITEM(item, answer) {
|
||||
|
||||
r = dns_packet_append_question(*p, q);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (dns_answer_contains(exclude1, item->rr) ||
|
||||
dns_answer_contains(exclude2, item->rr))
|
||||
continue;
|
||||
|
||||
DNS_PACKET_HEADER(*p)->qdcount = htobe16(dns_question_size(q));
|
||||
}
|
||||
if (!with_dnssec &&
|
||||
dns_type_is_dnssec(item->rr->key->type))
|
||||
continue;
|
||||
|
||||
DNS_ANSWER_FOREACH(rr, answer) {
|
||||
if (((item->flags ^ section) & (DNS_ANSWER_SECTION_ANSWER|DNS_ANSWER_SECTION_AUTHORITY|DNS_ANSWER_SECTION_ADDITIONAL)) != 0)
|
||||
continue;
|
||||
|
||||
r = dns_question_matches_rr(q, rr, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
goto add;
|
||||
|
||||
r = dns_question_matches_cname_or_dname(q, rr, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
goto add;
|
||||
|
||||
continue;
|
||||
add:
|
||||
r = dns_packet_append_rr(*p, rr, 0, NULL, NULL);
|
||||
if (r == -EMSGSIZE) {
|
||||
truncated = true;
|
||||
break;
|
||||
}
|
||||
r = dns_answer_add_extend(reply, item->rr, item->ifindex, item->flags, item->rrsig);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
c++;
|
||||
|
||||
if (with_dnssec && item->rrsig) {
|
||||
r = dns_answer_add_extend(reply, item->rrsig, item->ifindex, item->flags, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
c++;
|
||||
}
|
||||
}
|
||||
|
||||
return (int) c;
|
||||
}
|
||||
|
||||
static int dns_stub_assign_sections(
|
||||
DnsQuery *q,
|
||||
DnsQuestion *question,
|
||||
bool edns0_do) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(q);
|
||||
assert(question);
|
||||
|
||||
/* Let's assign the 'answer' and 'answer_auxiliary' RRs we collected to their respective sections in
|
||||
* the reply datagram. We try to reproduce a section assignment similar to what the upstream DNS
|
||||
* server responded to us. We use the DNS_ANSWER_SECTION_xyz flags to match things up, which is where
|
||||
* the original upstream's packet section assignment is stored in the DnsAnswer object. Not all RRs
|
||||
* in the 'answer' and 'answer_auxiliary' objects come with section information though (for example,
|
||||
* because they were synthesized locally, and not from a DNS packet). To deal with that we extend the
|
||||
* assignment logic a bit: anything from the 'answer' object that directly matches the original
|
||||
* question is always put in the ANSWER section, regardless if it carries section info, or what that
|
||||
* section info says. Then, anything from the 'answer' and 'answer_auxiliary' objects that is from
|
||||
* the ANSWER or AUTHORITY sections, and wasn't already added to the ANSWER section is placed in the
|
||||
* AUTHORITY section. Everything else from either object is added to the ADDITIONAL section. */
|
||||
|
||||
/* Include all RRs that directly answer the question in the answer section */
|
||||
r = dns_stub_collect_answer_by_question(
|
||||
&q->reply_answer,
|
||||
q->answer,
|
||||
question,
|
||||
edns0_do);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Include all RRs that originate from the answer or authority sections, and aren't listed in the
|
||||
* answer section, in the authority section */
|
||||
r = dns_stub_collect_answer_by_section(
|
||||
&q->reply_authoritative,
|
||||
q->answer,
|
||||
DNS_ANSWER_SECTION_ANSWER,
|
||||
q->reply_answer, NULL,
|
||||
edns0_do);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Include all RRs that originate from the answer or authority sections, and aren't listed in the
|
||||
* answer section, in the authority section */
|
||||
r = dns_stub_collect_answer_by_section(
|
||||
&q->reply_authoritative,
|
||||
q->answer,
|
||||
DNS_ANSWER_SECTION_AUTHORITY,
|
||||
q->reply_answer, NULL,
|
||||
edns0_do);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Include all RRs that originate from the additional sections in the additional section (except if
|
||||
* already listed in the other two sections). Also add all RRs with no section marking. */
|
||||
r = dns_stub_collect_answer_by_section(
|
||||
&q->reply_additional,
|
||||
q->answer,
|
||||
DNS_ANSWER_SECTION_ADDITIONAL,
|
||||
q->reply_answer, q->reply_authoritative,
|
||||
edns0_do);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = dns_stub_collect_answer_by_section(
|
||||
&q->reply_additional,
|
||||
q->answer,
|
||||
0,
|
||||
q->reply_answer, q->reply_authoritative,
|
||||
edns0_do);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dns_stub_make_reply_packet(
|
||||
DnsPacket **ret,
|
||||
size_t max_size,
|
||||
DnsQuestion *q,
|
||||
bool *ret_truncated) {
|
||||
|
||||
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
|
||||
bool tc = false;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0, max_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_packet_append_question(p, q);
|
||||
if (r == -EMSGSIZE)
|
||||
tc = true;
|
||||
else if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret_truncated)
|
||||
*ret_truncated = truncated;
|
||||
else if (truncated)
|
||||
*ret_truncated = tc;
|
||||
else if (tc)
|
||||
return -EMSGSIZE;
|
||||
|
||||
DNS_PACKET_HEADER(*p)->ancount = htobe16(be16toh(DNS_PACKET_HEADER(*p)->ancount) + c);
|
||||
DNS_PACKET_HEADER(p)->qdcount = htobe16(dns_question_size(q));
|
||||
|
||||
*ret = TAKE_PTR(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dns_stub_add_reply_packet_body(
|
||||
DnsPacket *p,
|
||||
DnsAnswer *answer,
|
||||
DnsAnswer *authoritative,
|
||||
DnsAnswer *additional,
|
||||
bool edns0_do, /* Client expects DNSSEC RRs? */
|
||||
bool *truncated) {
|
||||
|
||||
unsigned n_answer = 0, n_authoritative = 0, n_additional = 0;
|
||||
bool tc = false;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
|
||||
/* Add the three sections to the packet. If the answer section doesn't fit we'll signal that as
|
||||
* truncation. If the authoritative section doesn't fit and we are in DNSSEC mode, also signal
|
||||
* truncation. In all other cases where things don't fit don't signal truncation, as for those cases
|
||||
* the dropped RRs should not be essential. */
|
||||
|
||||
r = dns_packet_append_answer(p, answer, &n_answer);
|
||||
if (r == -EMSGSIZE)
|
||||
tc = true;
|
||||
else if (r < 0)
|
||||
return r;
|
||||
else {
|
||||
r = dns_packet_append_answer(p, authoritative, &n_authoritative);
|
||||
if (r == -EMSGSIZE) {
|
||||
if (edns0_do)
|
||||
tc = true;
|
||||
} else if (r < 0)
|
||||
return r;
|
||||
else {
|
||||
r = dns_packet_append_answer(p, additional, &n_additional);
|
||||
if (r < 0 && r != -EMSGSIZE)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
if (tc) {
|
||||
if (!truncated)
|
||||
return -EMSGSIZE;
|
||||
|
||||
*truncated = true;
|
||||
}
|
||||
|
||||
DNS_PACKET_HEADER(p)->ancount = htobe16(n_answer);
|
||||
DNS_PACKET_HEADER(p)->nscount = htobe16(n_authoritative);
|
||||
DNS_PACKET_HEADER(p)->arcount = htobe16(n_additional);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -159,6 +349,7 @@ static int dns_stub_finish_reply_packet(
|
||||
bool add_opt, /* add an OPT RR to this packet? */
|
||||
bool edns0_do, /* set the EDNS0 DNSSEC OK bit? */
|
||||
bool ad, /* set the DNSSEC authenticated data bit? */
|
||||
bool cd, /* set the DNSSEC checking disabled bit? */
|
||||
uint16_t max_udp_size) { /* The maximum UDP datagram size to advertise to clients */
|
||||
|
||||
int r;
|
||||
@ -171,19 +362,21 @@ static int dns_stub_finish_reply_packet(
|
||||
tc = true;
|
||||
else if (r < 0)
|
||||
return r;
|
||||
|
||||
} else {
|
||||
/* If the client can't to EDNS0, don't do DO either */
|
||||
edns0_do = false;
|
||||
|
||||
/* If the client didn't do EDNS, clamp the rcode to 4 bit */
|
||||
/* If we don't do EDNS, clamp the rcode to 4 bit */
|
||||
if (rcode > 0xF)
|
||||
rcode = DNS_RCODE_SERVFAIL;
|
||||
}
|
||||
|
||||
/* Don't set the AD bit unless DO is on, too */
|
||||
if (!edns0_do)
|
||||
/* Don't set the AD or CD bit unless DO is on, too */
|
||||
if (!edns0_do) {
|
||||
ad = false;
|
||||
cd = false;
|
||||
|
||||
}
|
||||
|
||||
DNS_PACKET_HEADER(p)->id = id;
|
||||
|
||||
@ -195,7 +388,7 @@ static int dns_stub_finish_reply_packet(
|
||||
1 /* rd */,
|
||||
1 /* ra */,
|
||||
ad /* ad */,
|
||||
0 /* cd */,
|
||||
cd /* cd */,
|
||||
rcode));
|
||||
|
||||
return 0;
|
||||
@ -231,6 +424,66 @@ static int dns_stub_send(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dns_stub_send_reply(
|
||||
DnsQuery *q,
|
||||
int rcode) {
|
||||
|
||||
_cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
|
||||
bool truncated, edns0_do;
|
||||
int r;
|
||||
|
||||
assert(q);
|
||||
|
||||
/* Reply with DNSSEC DO set? Only if client supports it; and we did any DNSSEC verification
|
||||
* ourselves, or consider the data fully authenticated because we generated it locally, or
|
||||
* the client set cd */
|
||||
edns0_do =
|
||||
DNS_PACKET_DO(q->request_packet) &&
|
||||
(q->answer_dnssec_result >= 0 || /* we did proper DNSSEC validation … */
|
||||
dns_query_fully_authenticated(q) || /* … or we considered it authentic otherwise … */
|
||||
DNS_PACKET_CD(q->request_packet)); /* … or client set CD */
|
||||
|
||||
r = dns_stub_assign_sections(
|
||||
q,
|
||||
q->request_packet->question,
|
||||
edns0_do);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to assign sections: %m");
|
||||
|
||||
r = dns_stub_make_reply_packet(
|
||||
&reply,
|
||||
DNS_PACKET_PAYLOAD_SIZE_MAX(q->request_packet),
|
||||
q->request_packet->question,
|
||||
&truncated);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to build reply packet: %m");
|
||||
|
||||
r = dns_stub_add_reply_packet_body(
|
||||
reply,
|
||||
q->reply_answer,
|
||||
q->reply_authoritative,
|
||||
q->reply_additional,
|
||||
edns0_do,
|
||||
&truncated);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to append reply packet body: %m");
|
||||
|
||||
r = dns_stub_finish_reply_packet(
|
||||
reply,
|
||||
DNS_PACKET_ID(q->request_packet),
|
||||
rcode,
|
||||
truncated,
|
||||
!!q->request_packet->opt,
|
||||
edns0_do,
|
||||
dns_query_fully_authenticated(q),
|
||||
DNS_PACKET_CD(q->request_packet),
|
||||
q->stub_listener_extra ? ADVERTISE_EXTRA_DATAGRAM_SIZE_MAX : ADVERTISE_DATAGRAM_SIZE_MAX);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to build failure packet: %m");
|
||||
|
||||
return dns_stub_send(q->manager, q->stub_listener_extra, q->request_stream, q->request_packet, reply);
|
||||
}
|
||||
|
||||
static int dns_stub_send_failure(
|
||||
Manager *m,
|
||||
DnsStubListenerExtra *l,
|
||||
@ -240,12 +493,17 @@ static int dns_stub_send_failure(
|
||||
bool authenticated) {
|
||||
|
||||
_cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
|
||||
bool truncated;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(p);
|
||||
|
||||
r = dns_stub_make_reply_packet(&reply, DNS_PACKET_PAYLOAD_SIZE_MAX(p), p->question, NULL, NULL);
|
||||
r = dns_stub_make_reply_packet(
|
||||
&reply,
|
||||
DNS_PACKET_PAYLOAD_SIZE_MAX(p),
|
||||
p->question,
|
||||
&truncated);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to make failure packet: %m");
|
||||
|
||||
@ -253,10 +511,11 @@ static int dns_stub_send_failure(
|
||||
reply,
|
||||
DNS_PACKET_ID(p),
|
||||
rcode,
|
||||
/* truncated = */ false,
|
||||
truncated,
|
||||
!!p->opt,
|
||||
DNS_PACKET_DO(p),
|
||||
authenticated,
|
||||
DNS_PACKET_CD(p),
|
||||
l ? ADVERTISE_EXTRA_DATAGRAM_SIZE_MAX : ADVERTISE_DATAGRAM_SIZE_MAX);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to build failure packet: %m");
|
||||
@ -264,27 +523,87 @@ static int dns_stub_send_failure(
|
||||
return dns_stub_send(m, l, s, p, reply);
|
||||
}
|
||||
|
||||
static int dns_stub_patch_bypass_reply_packet(
|
||||
DnsPacket **ret, /* Where to place the patched packet */
|
||||
DnsPacket *original, /* The packet to patch */
|
||||
DnsPacket *request) { /* The packet the patched packet shall look like a reply to */
|
||||
_cleanup_(dns_packet_unrefp) DnsPacket *c = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
assert(original);
|
||||
assert(request);
|
||||
|
||||
r = dns_packet_dup(&c, original);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Extract the packet, so that we know where the OPT field is */
|
||||
r = dns_packet_extract(c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Copy over the original client request ID, so that we can make the upstream query look like our own reply. */
|
||||
DNS_PACKET_HEADER(c)->id = DNS_PACKET_HEADER(request)->id;
|
||||
|
||||
/* Patch in our own maximum datagram size, if EDNS0 was on */
|
||||
r = dns_packet_patch_max_udp_size(c, ADVERTISE_DATAGRAM_SIZE_MAX);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Lower all TTLs by the time passed since we received the datagram. */
|
||||
if (timestamp_is_set(original->timestamp)) {
|
||||
r = dns_packet_patch_ttls(c, original->timestamp);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Our upstream connection might have supported larger DNS requests than our downstream one, hence
|
||||
* set the TC bit if our reply is larger than what the client supports, and truncate. */
|
||||
if (c->size > DNS_PACKET_PAYLOAD_SIZE_MAX(request)) {
|
||||
log_debug("Artificially truncating stub response, as advertised size of client is smaller than upstream one.");
|
||||
dns_packet_truncate(c, DNS_PACKET_PAYLOAD_SIZE_MAX(request));
|
||||
DNS_PACKET_HEADER(c)->flags = htobe16(be16toh(DNS_PACKET_HEADER(c)->flags) | DNS_PACKET_FLAG_TC);
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dns_stub_query_complete(DnsQuery *q) {
|
||||
int r;
|
||||
|
||||
assert(q);
|
||||
assert(q->request_dns_packet);
|
||||
assert(q->request_packet);
|
||||
|
||||
if (q->question_bypass) {
|
||||
/* This is a bypass reply. If so, let's propagate the upstream packet, if we have it and it
|
||||
* is regular DNS. (We can't do this if the upstream packet is LLMNR or mDNS, since the
|
||||
* packets are not 100% compatible.) */
|
||||
|
||||
if (q->answer_full_packet &&
|
||||
q->answer_full_packet->protocol == DNS_PROTOCOL_DNS) {
|
||||
_cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
|
||||
|
||||
r = dns_stub_patch_bypass_reply_packet(&reply, q->answer_full_packet, q->request_packet);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to patch bypass reply packet: %m");
|
||||
else
|
||||
(void) dns_stub_send(q->manager, q->stub_listener_extra, q->request_stream, q->request_packet, reply);
|
||||
|
||||
dns_query_free(q);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (q->state) {
|
||||
|
||||
case DNS_TRANSACTION_SUCCESS: {
|
||||
bool truncated;
|
||||
|
||||
r = dns_stub_make_reply_packet(&q->reply_dns_packet, DNS_PACKET_PAYLOAD_SIZE_MAX(q->request_dns_packet), q->question_idna, q->answer, &truncated);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to build reply packet: %m");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!truncated) {
|
||||
case DNS_TRANSACTION_SUCCESS:
|
||||
/* Follow CNAMEs, and accumulate answers. Except if DNSSEC is requested, then let the client do that. */
|
||||
if (!DNS_PACKET_DO(q->request_packet)) {
|
||||
r = dns_query_process_cname(q);
|
||||
if (r == -ELOOP) {
|
||||
(void) dns_stub_send_failure(q->manager, q->stub_listener_extra, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL, false);
|
||||
if (r == -ELOOP) { /* CNAME loop */
|
||||
(void) dns_stub_send_reply(q, DNS_RCODE_SERVFAIL);
|
||||
break;
|
||||
}
|
||||
if (r < 0) {
|
||||
@ -295,30 +614,15 @@ static void dns_stub_query_complete(DnsQuery *q) {
|
||||
return;
|
||||
}
|
||||
|
||||
r = dns_stub_finish_reply_packet(
|
||||
q->reply_dns_packet,
|
||||
DNS_PACKET_ID(q->request_dns_packet),
|
||||
q->answer_rcode,
|
||||
truncated,
|
||||
!!q->request_dns_packet->opt,
|
||||
DNS_PACKET_DO(q->request_dns_packet),
|
||||
dns_query_fully_authenticated(q),
|
||||
q->stub_listener_extra ? ADVERTISE_EXTRA_DATAGRAM_SIZE_MAX : ADVERTISE_DATAGRAM_SIZE_MAX);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to finish reply packet: %m");
|
||||
break;
|
||||
}
|
||||
|
||||
(void) dns_stub_send(q->manager, q->stub_listener_extra, q->request_dns_stream, q->request_dns_packet, q->reply_dns_packet);
|
||||
(void) dns_stub_send_reply(q, q->answer_rcode);
|
||||
break;
|
||||
}
|
||||
|
||||
case DNS_TRANSACTION_RCODE_FAILURE:
|
||||
(void) dns_stub_send_failure(q->manager, q->stub_listener_extra, q->request_dns_stream, q->request_dns_packet, q->answer_rcode, dns_query_fully_authenticated(q));
|
||||
(void) dns_stub_send_reply(q, q->answer_rcode);
|
||||
break;
|
||||
|
||||
case DNS_TRANSACTION_NOT_FOUND:
|
||||
(void) dns_stub_send_failure(q->manager, q->stub_listener_extra, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_NXDOMAIN, dns_query_fully_authenticated(q));
|
||||
(void) dns_stub_send_reply(q, DNS_RCODE_NXDOMAIN);
|
||||
break;
|
||||
|
||||
case DNS_TRANSACTION_TIMEOUT:
|
||||
@ -334,7 +638,8 @@ static void dns_stub_query_complete(DnsQuery *q) {
|
||||
case DNS_TRANSACTION_NO_TRUST_ANCHOR:
|
||||
case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED:
|
||||
case DNS_TRANSACTION_NETWORK_DOWN:
|
||||
(void) dns_stub_send_failure(q->manager, q->stub_listener_extra, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL, false);
|
||||
case DNS_TRANSACTION_NO_SOURCE:
|
||||
(void) dns_stub_send_reply(q, DNS_RCODE_SERVFAIL);
|
||||
break;
|
||||
|
||||
case DNS_TRANSACTION_NULL:
|
||||
@ -398,13 +703,13 @@ static void dns_stub_process_query(Manager *m, DnsStubListenerExtra *l, DnsStrea
|
||||
|
||||
if (dns_type_is_obsolete(p->question->keys[0]->type)) {
|
||||
log_debug("Got message with obsolete key type, refusing.");
|
||||
dns_stub_send_failure(m, l, s, p, DNS_RCODE_NOTIMP, false);
|
||||
dns_stub_send_failure(m, l, s, p, DNS_RCODE_REFUSED, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dns_type_is_zone_transer(p->question->keys[0]->type)) {
|
||||
log_debug("Got request for zone transfer, refusing.");
|
||||
dns_stub_send_failure(m, l, s, p, DNS_RCODE_NOTIMP, false);
|
||||
dns_stub_send_failure(m, l, s, p, DNS_RCODE_REFUSED, false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -416,23 +721,29 @@ static void dns_stub_process_query(Manager *m, DnsStubListenerExtra *l, DnsStrea
|
||||
}
|
||||
|
||||
if (DNS_PACKET_DO(p) && DNS_PACKET_CD(p)) {
|
||||
log_debug("Got request with DNSSEC CD bit set, refusing.");
|
||||
dns_stub_send_failure(m, l, s, p, DNS_RCODE_NOTIMP, false);
|
||||
return;
|
||||
}
|
||||
log_debug("Got request with DNSSEC checking disabled, enabling bypass logic.");
|
||||
|
||||
r = dns_query_new(m, &q, p->question, p->question, 0, SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_SEARCH);
|
||||
r = dns_query_new(m, &q, NULL, NULL, p, 0,
|
||||
SD_RESOLVED_PROTOCOLS_ALL|
|
||||
SD_RESOLVED_NO_CNAME|
|
||||
SD_RESOLVED_NO_SEARCH|
|
||||
SD_RESOLVED_NO_VALIDATE|
|
||||
SD_RESOLVED_REQUIRE_PRIMARY|
|
||||
SD_RESOLVED_CLAMP_TTL);
|
||||
} else
|
||||
r = dns_query_new(m, &q, p->question, p->question, NULL, 0,
|
||||
SD_RESOLVED_PROTOCOLS_ALL|
|
||||
SD_RESOLVED_NO_SEARCH|
|
||||
(DNS_PACKET_DO(p) ? SD_RESOLVED_NO_CNAME|SD_RESOLVED_REQUIRE_PRIMARY : 0)|
|
||||
SD_RESOLVED_CLAMP_TTL);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to generate query object: %m");
|
||||
dns_stub_send_failure(m, l, s, p, DNS_RCODE_SERVFAIL, false);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Request that the TTL is corrected by the cached time for this lookup, so that we return vaguely useful TTLs */
|
||||
q->clamp_ttl = true;
|
||||
|
||||
q->request_dns_packet = dns_packet_ref(p);
|
||||
q->request_dns_stream = dns_stream_ref(s); /* make sure the stream stays around until we can send a reply through it */
|
||||
q->request_packet = dns_packet_ref(p);
|
||||
q->request_stream = dns_stream_ref(s); /* make sure the stream stays around until we can send a reply through it */
|
||||
q->stub_listener_extra = l;
|
||||
q->complete = dns_stub_query_complete;
|
||||
|
||||
|
@ -76,7 +76,7 @@ static int synthesize_localhost_rr(Manager *m, const DnsResourceKey *key, int if
|
||||
|
||||
rr->a.in_addr.s_addr = htobe32(INADDR_LOOPBACK);
|
||||
|
||||
r = dns_answer_add(*answer, rr, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
|
||||
r = dns_answer_add(*answer, rr, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -90,7 +90,7 @@ static int synthesize_localhost_rr(Manager *m, const DnsResourceKey *key, int if
|
||||
|
||||
rr->aaaa.in6_addr = in6addr_loopback;
|
||||
|
||||
r = dns_answer_add(*answer, rr, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
|
||||
r = dns_answer_add(*answer, rr, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -109,7 +109,7 @@ static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to,
|
||||
if (!rr->ptr.name)
|
||||
return -ENOMEM;
|
||||
|
||||
return dns_answer_add(*answer, rr, ifindex, flags);
|
||||
return dns_answer_add(*answer, rr, ifindex, flags, NULL);
|
||||
}
|
||||
|
||||
static int synthesize_localhost_ptr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
|
||||
@ -155,7 +155,7 @@ static int answer_add_addresses_rr(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED);
|
||||
r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -197,7 +197,7 @@ static int answer_add_addresses_ptr(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED);
|
||||
r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -5,6 +5,7 @@
|
||||
#include "in-addr-util.h"
|
||||
|
||||
typedef struct DnsTransaction DnsTransaction;
|
||||
typedef struct DnsTransactionFinder DnsTransactionFinder;
|
||||
typedef enum DnsTransactionState DnsTransactionState;
|
||||
typedef enum DnsTransactionSource DnsTransactionSource;
|
||||
|
||||
@ -31,6 +32,7 @@ enum DnsTransactionState {
|
||||
DNS_TRANSACTION_RR_TYPE_UNSUPPORTED,
|
||||
DNS_TRANSACTION_NETWORK_DOWN,
|
||||
DNS_TRANSACTION_NOT_FOUND, /* like NXDOMAIN, but when LLMNR/TCP connections fail */
|
||||
DNS_TRANSACTION_NO_SOURCE, /* All suitable DnsTransactionSource turned off */
|
||||
_DNS_TRANSACTION_STATE_MAX,
|
||||
_DNS_TRANSACTION_STATE_INVALID = -EINVAL,
|
||||
};
|
||||
@ -49,7 +51,10 @@ enum DnsTransactionSource {
|
||||
struct DnsTransaction {
|
||||
DnsScope *scope;
|
||||
|
||||
DnsResourceKey *key;
|
||||
DnsResourceKey *key; /* For regular lookups the RR key to look for */
|
||||
DnsPacket *bypass; /* For bypass lookups the full original request packet */
|
||||
|
||||
uint64_t query_flags;
|
||||
|
||||
DnsTransactionState state;
|
||||
|
||||
@ -60,8 +65,6 @@ struct DnsTransaction {
|
||||
bool initial_jitter_scheduled:1;
|
||||
bool initial_jitter_elapsed:1;
|
||||
|
||||
bool clamp_ttl:1;
|
||||
|
||||
bool probing:1;
|
||||
|
||||
DnsPacket *sent, *received;
|
||||
@ -133,9 +136,10 @@ struct DnsTransaction {
|
||||
|
||||
LIST_FIELDS(DnsTransaction, transactions_by_scope);
|
||||
LIST_FIELDS(DnsTransaction, transactions_by_stream);
|
||||
LIST_FIELDS(DnsTransaction, transactions_by_key);
|
||||
};
|
||||
|
||||
int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key);
|
||||
int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key, DnsPacket *bypass, uint64_t flags);
|
||||
DnsTransaction* dns_transaction_free(DnsTransaction *t);
|
||||
|
||||
bool dns_transaction_gc(DnsTransaction *t);
|
||||
@ -150,6 +154,23 @@ void dns_transaction_notify(DnsTransaction *t, DnsTransaction *source);
|
||||
int dns_transaction_validate_dnssec(DnsTransaction *t);
|
||||
int dns_transaction_request_dnssec_keys(DnsTransaction *t);
|
||||
|
||||
static inline DnsResourceKey *dns_transaction_key(DnsTransaction *t) {
|
||||
assert(t);
|
||||
|
||||
/* Return the lookup key of this transaction. Either takes the lookup key from the bypass packet if
|
||||
* we are a bypass transaction. Or take the configured key for regular transactions. */
|
||||
|
||||
if (t->key)
|
||||
return t->key;
|
||||
|
||||
assert(t->bypass);
|
||||
|
||||
if (dns_question_isempty(t->bypass->question))
|
||||
return NULL;
|
||||
|
||||
return t->bypass->question->keys[0];
|
||||
}
|
||||
|
||||
const char* dns_transaction_state_to_string(DnsTransactionState p) _const_;
|
||||
DnsTransactionState dns_transaction_state_from_string(const char *s) _pure_;
|
||||
|
||||
|
@ -60,7 +60,7 @@ static int add_root_ksk(
|
||||
if (!rr->ds.digest)
|
||||
return -ENOMEM;
|
||||
|
||||
r = dns_answer_add(answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
|
||||
r = dns_answer_add(answer, rr, 0, DNS_ANSWER_AUTHENTICATED, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -354,7 +354,7 @@ static int dns_trust_anchor_load_positive(DnsTrustAnchor *d, const char *path, u
|
||||
old_answer = hashmap_get(d->positive_by_key, rr->key);
|
||||
answer = dns_answer_ref(old_answer);
|
||||
|
||||
r = dns_answer_add_extend(&answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
|
||||
r = dns_answer_add_extend(&answer, rr, 0, DNS_ANSWER_AUTHENTICATED, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add trust anchor RR: %m");
|
||||
|
||||
|
@ -170,7 +170,10 @@ static int dns_zone_item_probe_start(DnsZoneItem *i) {
|
||||
if (i->probe_transaction)
|
||||
return 0;
|
||||
|
||||
t = dns_scope_find_transaction(i->scope, &DNS_RESOURCE_KEY_CONST(i->rr->key->class, DNS_TYPE_ANY, dns_resource_key_name(i->rr->key)), false);
|
||||
t = dns_scope_find_transaction(
|
||||
i->scope,
|
||||
&DNS_RESOURCE_KEY_CONST(i->rr->key->class, DNS_TYPE_ANY, dns_resource_key_name(i->rr->key)),
|
||||
SD_RESOLVED_NO_CACHE|SD_RESOLVED_NO_ZONE);
|
||||
if (!t) {
|
||||
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
|
||||
|
||||
@ -178,7 +181,7 @@ static int dns_zone_item_probe_start(DnsZoneItem *i) {
|
||||
if (!key)
|
||||
return -ENOMEM;
|
||||
|
||||
r = dns_transaction_new(&t, i->scope, key);
|
||||
r = dns_transaction_new(&t, i->scope, key, NULL, SD_RESOLVED_NO_CACHE|SD_RESOLVED_NO_ZONE);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -296,7 +299,7 @@ static int dns_zone_add_authenticated_answer(DnsAnswer *a, DnsZoneItem *i, int i
|
||||
else
|
||||
flags = DNS_ANSWER_AUTHENTICATED;
|
||||
|
||||
return dns_answer_add(a, i->rr, ifindex, flags);
|
||||
return dns_answer_add(a, i->rr, ifindex, flags, NULL);
|
||||
}
|
||||
|
||||
int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, int ifindex, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) {
|
||||
|
@ -406,7 +406,7 @@ int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
|
||||
if (!rr->ptr.name)
|
||||
return -ENOMEM;
|
||||
|
||||
r = dns_answer_add(*answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
|
||||
r = dns_answer_add(*answer, rr, 0, DNS_ANSWER_AUTHENTICATED, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -458,7 +458,7 @@ int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_answer_add(*answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
|
||||
r = dns_answer_add(*answer, rr, 0, DNS_ANSWER_AUTHENTICATED, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -301,25 +301,25 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
|
||||
rr->ttl = 1;
|
||||
}
|
||||
|
||||
t = dns_scope_find_transaction(scope, rr->key, false);
|
||||
t = dns_scope_find_transaction(scope, rr->key, SD_RESOLVED_NO_CACHE|SD_RESOLVED_NO_ZONE);
|
||||
if (t)
|
||||
dns_transaction_process_reply(t, p);
|
||||
|
||||
/* Also look for the various types of ANY transactions */
|
||||
t = dns_scope_find_transaction(scope, &DNS_RESOURCE_KEY_CONST(rr->key->class, DNS_TYPE_ANY, dns_resource_key_name(rr->key)), false);
|
||||
t = dns_scope_find_transaction(scope, &DNS_RESOURCE_KEY_CONST(rr->key->class, DNS_TYPE_ANY, dns_resource_key_name(rr->key)), SD_RESOLVED_NO_CACHE|SD_RESOLVED_NO_ZONE);
|
||||
if (t)
|
||||
dns_transaction_process_reply(t, p);
|
||||
|
||||
t = dns_scope_find_transaction(scope, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_ANY, rr->key->type, dns_resource_key_name(rr->key)), false);
|
||||
t = dns_scope_find_transaction(scope, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_ANY, rr->key->type, dns_resource_key_name(rr->key)), SD_RESOLVED_NO_CACHE|SD_RESOLVED_NO_ZONE);
|
||||
if (t)
|
||||
dns_transaction_process_reply(t, p);
|
||||
|
||||
t = dns_scope_find_transaction(scope, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_ANY, DNS_TYPE_ANY, dns_resource_key_name(rr->key)), false);
|
||||
t = dns_scope_find_transaction(scope, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_ANY, DNS_TYPE_ANY, dns_resource_key_name(rr->key)), SD_RESOLVED_NO_CACHE|SD_RESOLVED_NO_ZONE);
|
||||
if (t)
|
||||
dns_transaction_process_reply(t, p);
|
||||
}
|
||||
|
||||
dns_cache_put(&scope->cache, scope->manager->enable_cache, NULL, DNS_PACKET_RCODE(p), p->answer, false, (uint32_t) -1, 0, p->family, &p->sender);
|
||||
dns_cache_put(&scope->cache, scope->manager->enable_cache, NULL, DNS_PACKET_RCODE(p), p->answer, NULL, false, _DNSSEC_RESULT_INVALID, (uint32_t) -1, p->family, &p->sender);
|
||||
|
||||
} else if (dns_packet_validate_query(p) > 0) {
|
||||
log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p));
|
||||
|
@ -57,6 +57,9 @@ static int reply_query_state(DnsQuery *q) {
|
||||
case DNS_TRANSACTION_NETWORK_DOWN:
|
||||
return varlink_error(q->varlink_request, "io.systemd.Resolve.NetworkDown", NULL);
|
||||
|
||||
case DNS_TRANSACTION_NO_SOURCE:
|
||||
return varlink_error(q->varlink_request, "io.systemd.Resolve.NoSource", NULL);
|
||||
|
||||
case DNS_TRANSACTION_NOT_FOUND:
|
||||
/* We return this as NXDOMAIN. This is only generated when a host doesn't implement LLMNR/TCP, and we
|
||||
* thus quickly know that we cannot resolve an in-addr.arpa or ip6.arpa address. */
|
||||
@ -103,7 +106,7 @@ static bool validate_and_mangle_flags(
|
||||
/* This checks that the specified client-provided flags parameter actually makes sense, and mangles
|
||||
* it slightly. Specifically:
|
||||
*
|
||||
* 1. We check that only the protocol flags and the NO_CNAME flag are on at most, plus the
|
||||
* 1. We check that only the protocol flags and a bunch of NO_XYZ flags are on at most, plus the
|
||||
* method-specific flags specified in 'ok'.
|
||||
*
|
||||
* 2. If no protocols are enabled we automatically convert that to "all protocols are enabled".
|
||||
@ -114,7 +117,15 @@ static bool validate_and_mangle_flags(
|
||||
* "everything".
|
||||
*/
|
||||
|
||||
if (*flags & ~(SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_CNAME|ok))
|
||||
if (*flags & ~(SD_RESOLVED_PROTOCOLS_ALL|
|
||||
SD_RESOLVED_NO_CNAME|
|
||||
SD_RESOLVED_NO_VALIDATE|
|
||||
SD_RESOLVED_NO_SYNTHESIZE|
|
||||
SD_RESOLVED_NO_CACHE|
|
||||
SD_RESOLVED_NO_ZONE|
|
||||
SD_RESOLVED_NO_TRUST_ANCHOR|
|
||||
SD_RESOLVED_NO_NETWORK|
|
||||
ok))
|
||||
return false;
|
||||
|
||||
if ((*flags & SD_RESOLVED_PROTOCOLS_ALL) == 0) /* If no protocol is enabled, enable all */
|
||||
@ -312,7 +323,7 @@ static int vl_method_resolve_hostname(Varlink *link, JsonVariant *parameters, Va
|
||||
if (r < 0 && r != -EALREADY)
|
||||
return r;
|
||||
|
||||
r = dns_query_new(m, &q, question_utf8, question_idna ?: question_utf8, p.ifindex, p.flags);
|
||||
r = dns_query_new(m, &q, question_utf8, question_idna ?: question_utf8, NULL, p.ifindex, p.flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -481,7 +492,7 @@ static int vl_method_resolve_address(Varlink *link, JsonVariant *parameters, Var
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_query_new(m, &q, question, question, p.ifindex, p.flags|SD_RESOLVED_NO_SEARCH);
|
||||
r = dns_query_new(m, &q, question, question, NULL, p.ifindex, p.flags|SD_RESOLVED_NO_SEARCH);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -170,7 +170,7 @@ static void test_dnssec_verify_rfc8080_ed25519_example1(void) {
|
||||
|
||||
answer = dns_answer_new(1);
|
||||
assert_se(answer);
|
||||
assert_se(dns_answer_add(answer, mx, 0, DNS_ANSWER_AUTHENTICATED) >= 0);
|
||||
assert_se(dns_answer_add(answer, mx, 0, DNS_ANSWER_AUTHENTICATED, NULL) >= 0);
|
||||
|
||||
assert_se(dnssec_verify_rrset(answer, mx->key, rrsig, dnskey,
|
||||
rrsig->rrsig.inception * USEC_PER_SEC, &result) >= 0);
|
||||
@ -262,7 +262,7 @@ static void test_dnssec_verify_rfc8080_ed25519_example2(void) {
|
||||
|
||||
answer = dns_answer_new(1);
|
||||
assert_se(answer);
|
||||
assert_se(dns_answer_add(answer, mx, 0, DNS_ANSWER_AUTHENTICATED) >= 0);
|
||||
assert_se(dns_answer_add(answer, mx, 0, DNS_ANSWER_AUTHENTICATED, NULL) >= 0);
|
||||
|
||||
assert_se(dnssec_verify_rrset(answer, mx->key, rrsig, dnskey,
|
||||
rrsig->rrsig.inception * USEC_PER_SEC, &result) >= 0);
|
||||
@ -344,7 +344,7 @@ static void test_dnssec_verify_rrset(void) {
|
||||
|
||||
answer = dns_answer_new(1);
|
||||
assert_se(answer);
|
||||
assert_se(dns_answer_add(answer, a, 0, DNS_ANSWER_AUTHENTICATED) >= 0);
|
||||
assert_se(dns_answer_add(answer, a, 0, DNS_ANSWER_AUTHENTICATED, NULL) >= 0);
|
||||
|
||||
/* Validate the RR as it if was 2015-12-2 today */
|
||||
assert_se(dnssec_verify_rrset(answer, a->key, rrsig, dnskey, 1449092754*USEC_PER_SEC, &result) >= 0);
|
||||
@ -436,7 +436,7 @@ static void test_dnssec_verify_rrset2(void) {
|
||||
|
||||
answer = dns_answer_new(1);
|
||||
assert_se(answer);
|
||||
assert_se(dns_answer_add(answer, nsec, 0, DNS_ANSWER_AUTHENTICATED) >= 0);
|
||||
assert_se(dns_answer_add(answer, nsec, 0, DNS_ANSWER_AUTHENTICATED, NULL) >= 0);
|
||||
|
||||
/* Validate the RR as it if was 2015-12-11 today */
|
||||
assert_se(dnssec_verify_rrset(answer, nsec->key, rrsig, dnskey, 1449849318*USEC_PER_SEC, &result) >= 0);
|
||||
@ -563,10 +563,10 @@ static void test_dnssec_verify_rrset3(void) {
|
||||
|
||||
answer = dns_answer_new(4);
|
||||
assert_se(answer);
|
||||
assert_se(dns_answer_add(answer, mx1, 0, DNS_ANSWER_AUTHENTICATED) >= 0);
|
||||
assert_se(dns_answer_add(answer, mx2, 0, DNS_ANSWER_AUTHENTICATED) >= 0);
|
||||
assert_se(dns_answer_add(answer, mx3, 0, DNS_ANSWER_AUTHENTICATED) >= 0);
|
||||
assert_se(dns_answer_add(answer, mx4, 0, DNS_ANSWER_AUTHENTICATED) >= 0);
|
||||
assert_se(dns_answer_add(answer, mx1, 0, DNS_ANSWER_AUTHENTICATED, NULL) >= 0);
|
||||
assert_se(dns_answer_add(answer, mx2, 0, DNS_ANSWER_AUTHENTICATED, NULL) >= 0);
|
||||
assert_se(dns_answer_add(answer, mx3, 0, DNS_ANSWER_AUTHENTICATED, NULL) >= 0);
|
||||
assert_se(dns_answer_add(answer, mx4, 0, DNS_ANSWER_AUTHENTICATED, NULL) >= 0);
|
||||
|
||||
/* Validate the RR as it if was 2020-02-24 today */
|
||||
assert_se(dnssec_verify_rrset(answer, mx1->key, rrsig, dnskey, 1582534685*USEC_PER_SEC, &result) >= 0);
|
||||
|
Loading…
Reference in New Issue
Block a user