Merge pull request #2190 from poettering/dnssec6
Add DNSSEC proof of unsignedness and NSEC3 proof
This commit is contained in:
commit
d73fe9134f
|
@ -38,7 +38,7 @@
|
|||
|
||||
static int arg_family = AF_UNSPEC;
|
||||
static int arg_ifindex = 0;
|
||||
static int arg_type = 0;
|
||||
static uint16_t arg_type = 0;
|
||||
static uint16_t arg_class = 0;
|
||||
static bool arg_legend = true;
|
||||
static uint64_t arg_flags = 0;
|
||||
|
@ -347,7 +347,6 @@ static int resolve_record(sd_bus *bus, const char *name) {
|
|||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
assert((uint16_t) arg_type == arg_type);
|
||||
r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, arg_class, arg_type, arg_flags);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
@ -399,7 +398,7 @@ static int resolve_record(sd_bus *bus, const char *name) {
|
|||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = dns_packet_read_rr(p, &rr, NULL);
|
||||
r = dns_packet_read_rr(p, &rr, NULL, NULL);
|
||||
if (r < 0) {
|
||||
log_error("Failed to parse RR.");
|
||||
return r;
|
||||
|
@ -758,12 +757,13 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
arg_type = dns_type_from_string(optarg);
|
||||
if (arg_type < 0) {
|
||||
r = dns_type_from_string(optarg);
|
||||
if (r < 0) {
|
||||
log_error("Failed to parse RR record type %s", optarg);
|
||||
return arg_type;
|
||||
return r;
|
||||
}
|
||||
assert(arg_type > 0 && (uint16_t) arg_type == arg_type);
|
||||
arg_type = (uint16_t) r;
|
||||
assert((int) arg_type == r);
|
||||
|
||||
break;
|
||||
|
||||
|
@ -773,11 +773,13 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
r = dns_class_from_string(optarg, &arg_class);
|
||||
r = dns_class_from_string(optarg);
|
||||
if (r < 0) {
|
||||
log_error("Failed to parse RR record class %s", optarg);
|
||||
return r;
|
||||
}
|
||||
arg_class = (uint16_t) r;
|
||||
assert((int) arg_class == r);
|
||||
|
||||
break;
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
***/
|
||||
|
||||
#include "dns-type.h"
|
||||
#include "string-util.h"
|
||||
|
||||
typedef const struct {
|
||||
uint16_t type;
|
||||
|
@ -64,6 +65,10 @@ bool dns_type_is_pseudo(uint16_t type) {
|
|||
);
|
||||
}
|
||||
|
||||
bool dns_class_is_pseudo(uint16_t class) {
|
||||
return class == DNS_TYPE_ANY;
|
||||
}
|
||||
|
||||
bool dns_type_is_valid_query(uint16_t type) {
|
||||
|
||||
/* The types valid as questions in packets */
|
||||
|
@ -85,3 +90,34 @@ bool dns_type_is_valid_rr(uint16_t type) {
|
|||
DNS_TYPE_AXFR,
|
||||
DNS_TYPE_IXFR);
|
||||
}
|
||||
|
||||
bool dns_class_is_valid_rr(uint16_t class) {
|
||||
return class != DNS_CLASS_ANY;
|
||||
}
|
||||
|
||||
const char *dns_class_to_string(uint16_t class) {
|
||||
|
||||
switch (class) {
|
||||
|
||||
case DNS_CLASS_IN:
|
||||
return "IN";
|
||||
|
||||
case DNS_CLASS_ANY:
|
||||
return "ANY";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int dns_class_from_string(const char *s) {
|
||||
|
||||
if (!s)
|
||||
return _DNS_CLASS_INVALID;
|
||||
|
||||
if (strcaseeq(s, "IN"))
|
||||
return DNS_CLASS_IN;
|
||||
else if (strcaseeq(s, "ANY"))
|
||||
return DNS_CLASS_ANY;
|
||||
|
||||
return _DNS_CLASS_INVALID;
|
||||
}
|
||||
|
|
|
@ -23,13 +23,6 @@
|
|||
|
||||
#include "macro.h"
|
||||
|
||||
const char *dns_type_to_string(int type);
|
||||
int dns_type_from_string(const char *s);
|
||||
|
||||
bool dns_type_is_pseudo(uint16_t type);
|
||||
bool dns_type_is_valid_query(uint16_t type);
|
||||
bool dns_type_is_valid_rr(uint16_t type);
|
||||
|
||||
/* DNS record types, taken from
|
||||
* http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml.
|
||||
*/
|
||||
|
@ -122,3 +115,25 @@ enum {
|
|||
assert_cc(DNS_TYPE_SSHFP == 44);
|
||||
assert_cc(DNS_TYPE_TLSA == 52);
|
||||
assert_cc(DNS_TYPE_ANY == 255);
|
||||
|
||||
/* DNS record classes, see RFC 1035 */
|
||||
enum {
|
||||
DNS_CLASS_IN = 0x01,
|
||||
DNS_CLASS_ANY = 0xFF,
|
||||
|
||||
_DNS_CLASS_MAX,
|
||||
_DNS_CLASS_INVALID = -1
|
||||
};
|
||||
|
||||
bool dns_type_is_pseudo(uint16_t type);
|
||||
bool dns_type_is_valid_query(uint16_t type);
|
||||
bool dns_type_is_valid_rr(uint16_t type);
|
||||
|
||||
bool dns_class_is_pseudo(uint16_t class);
|
||||
bool dns_class_is_valid_rr(uint16_t class);
|
||||
|
||||
const char *dns_type_to_string(int type);
|
||||
int dns_type_from_string(const char *s);
|
||||
|
||||
const char *dns_class_to_string(uint16_t type);
|
||||
int dns_class_from_string(const char *name);
|
||||
|
|
|
@ -61,9 +61,10 @@ static int reply_query_state(DnsQuery *q) {
|
|||
return sd_bus_reply_method_errorf(q->request, BUS_ERROR_ABORTED, "Query aborted");
|
||||
|
||||
case DNS_TRANSACTION_DNSSEC_FAILED:
|
||||
return sd_bus_reply_method_errorf(q->request, BUS_ERROR_ABORTED, "DNSSEC validation failed");
|
||||
return sd_bus_reply_method_errorf(q->request, BUS_ERROR_ABORTED, "DNSSEC validation failed: %s",
|
||||
dnssec_result_to_string(q->answer_dnssec_result));
|
||||
|
||||
case DNS_TRANSACTION_FAILURE: {
|
||||
case DNS_TRANSACTION_RCODE_FAILURE: {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
if (q->answer_rcode == DNS_RCODE_NXDOMAIN)
|
||||
|
|
|
@ -74,7 +74,7 @@ DnsAnswer *dns_answer_unref(DnsAnswer *a) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int dns_answer_add_raw(DnsAnswer *a, DnsResourceRecord *rr, int ifindex) {
|
||||
static int dns_answer_add_raw(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
|
||||
assert(rr);
|
||||
|
||||
if (!a)
|
||||
|
@ -83,19 +83,22 @@ static int dns_answer_add_raw(DnsAnswer *a, DnsResourceRecord *rr, int ifindex)
|
|||
if (a->n_rrs >= a->n_allocated)
|
||||
return -ENOSPC;
|
||||
|
||||
a->items[a->n_rrs].rr = dns_resource_record_ref(rr);
|
||||
a->items[a->n_rrs].ifindex = ifindex;
|
||||
a->n_rrs++;
|
||||
a->items[a->n_rrs++] = (DnsAnswerItem) {
|
||||
.rr = dns_resource_record_ref(rr),
|
||||
.ifindex = ifindex,
|
||||
.flags = flags,
|
||||
};
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dns_answer_add_raw_all(DnsAnswer *a, DnsAnswer *source) {
|
||||
DnsResourceRecord *rr;
|
||||
DnsAnswerFlags flags;
|
||||
int ifindex, r;
|
||||
|
||||
DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, source) {
|
||||
r = dns_answer_add_raw(a, rr, ifindex);
|
||||
DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, source) {
|
||||
r = dns_answer_add_raw(a, rr, ifindex, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -103,7 +106,7 @@ static int dns_answer_add_raw_all(DnsAnswer *a, DnsAnswer *source) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex) {
|
||||
int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
|
||||
unsigned i;
|
||||
int r;
|
||||
|
||||
|
@ -131,19 +134,21 @@ int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex) {
|
|||
a->items[i].rr = rr;
|
||||
}
|
||||
|
||||
a->items[i].flags |= flags;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return dns_answer_add_raw(a, rr, ifindex);
|
||||
return dns_answer_add_raw(a, rr, ifindex, flags);
|
||||
}
|
||||
|
||||
static int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) {
|
||||
DnsResourceRecord *rr;
|
||||
DnsAnswerFlags flags;
|
||||
int ifindex, r;
|
||||
|
||||
DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, b) {
|
||||
r = dns_answer_add(a, rr, ifindex);
|
||||
DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, b) {
|
||||
r = dns_answer_add(a, rr, ifindex, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -151,7 +156,7 @@ static int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex) {
|
||||
int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
|
||||
int r;
|
||||
|
||||
assert(a);
|
||||
|
@ -161,7 +166,7 @@ int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return dns_answer_add(*a, rr, ifindex);
|
||||
return dns_answer_add(*a, rr, ifindex, flags);
|
||||
}
|
||||
|
||||
int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl) {
|
||||
|
@ -187,66 +192,136 @@ int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl) {
|
|||
soa->soa.expire = 1;
|
||||
soa->soa.minimum = ttl;
|
||||
|
||||
return dns_answer_add(a, soa, 0);
|
||||
return dns_answer_add(a, soa, 0, DNS_ANSWER_AUTHENTICATED);
|
||||
}
|
||||
|
||||
int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key) {
|
||||
int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
|
||||
DnsAnswerFlags flags = 0, i_flags;
|
||||
DnsResourceRecord *i;
|
||||
bool found = false;
|
||||
int r;
|
||||
|
||||
assert(key);
|
||||
|
||||
if (!a)
|
||||
return 0;
|
||||
|
||||
DNS_ANSWER_FOREACH(i, a) {
|
||||
DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
|
||||
r = dns_resource_key_match_rr(key, i, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
if (!ret_flags)
|
||||
return 1;
|
||||
|
||||
if (found)
|
||||
flags &= i_flags;
|
||||
else {
|
||||
flags = i_flags;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (ret_flags)
|
||||
*ret_flags = flags;
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
int dns_answer_contains_rr(DnsAnswer *a, DnsResourceRecord *rr) {
|
||||
int dns_answer_contains_rr(DnsAnswer *a, DnsResourceRecord *rr, DnsAnswerFlags *ret_flags) {
|
||||
DnsAnswerFlags flags = 0, i_flags;
|
||||
DnsResourceRecord *i;
|
||||
bool found = false;
|
||||
int r;
|
||||
|
||||
assert(rr);
|
||||
|
||||
DNS_ANSWER_FOREACH(i, a) {
|
||||
DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
|
||||
r = dns_resource_record_equal(i, rr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
if (!ret_flags)
|
||||
return 1;
|
||||
|
||||
if (found)
|
||||
flags &= i_flags;
|
||||
else {
|
||||
flags = i_flags;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (ret_flags)
|
||||
*ret_flags = flags;
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret) {
|
||||
DnsResourceRecord *rr;
|
||||
int dns_answer_contains_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
|
||||
DnsAnswerFlags flags = 0, i_flags;
|
||||
DnsResourceRecord *i;
|
||||
bool found = false;
|
||||
int r;
|
||||
|
||||
assert(key);
|
||||
|
||||
if (!a)
|
||||
return 0;
|
||||
DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
|
||||
r = dns_resource_key_equal(i->key, key);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
if (!ret_flags)
|
||||
return true;
|
||||
|
||||
if (found)
|
||||
flags &= i_flags;
|
||||
else {
|
||||
flags = i_flags;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret_flags)
|
||||
*ret_flags = flags;
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a) {
|
||||
DnsResourceRecord *i;
|
||||
|
||||
DNS_ANSWER_FOREACH(i, a) {
|
||||
if (IN_SET(i->key->type, DNS_TYPE_NSEC, DNS_TYPE_NSEC3))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
|
||||
DnsResourceRecord *rr;
|
||||
DnsAnswerFlags rr_flags;
|
||||
int r;
|
||||
|
||||
assert(key);
|
||||
|
||||
/* For a SOA record we can never find a matching SOA record */
|
||||
if (key->type == DNS_TYPE_SOA)
|
||||
return 0;
|
||||
|
||||
DNS_ANSWER_FOREACH(rr, a) {
|
||||
DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
|
||||
r = dns_resource_key_match_soa(key, rr->key);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0) {
|
||||
if (ret)
|
||||
*ret = rr;
|
||||
if (flags)
|
||||
*flags = rr_flags;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -254,26 +329,26 @@ int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceReco
|
|||
return 0;
|
||||
}
|
||||
|
||||
int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret) {
|
||||
int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
|
||||
DnsResourceRecord *rr;
|
||||
DnsAnswerFlags rr_flags;
|
||||
int r;
|
||||
|
||||
assert(key);
|
||||
|
||||
if (!a)
|
||||
return 0;
|
||||
|
||||
/* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
|
||||
if (key->type == DNS_TYPE_CNAME || key->type == DNS_TYPE_DNAME)
|
||||
return 0;
|
||||
|
||||
DNS_ANSWER_FOREACH(rr, a) {
|
||||
DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
|
||||
r = dns_resource_key_match_cname_or_dname(key, rr->key, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0) {
|
||||
if (ret)
|
||||
*ret = rr;
|
||||
if (flags)
|
||||
*flags = rr_flags;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -365,20 +440,21 @@ 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;
|
||||
|
||||
copy = dns_answer_new((*a)->n_rrs);
|
||||
if (!copy)
|
||||
return -ENOMEM;
|
||||
|
||||
DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, *a) {
|
||||
DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) {
|
||||
r = dns_resource_key_equal(rr->key, key);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
continue;
|
||||
|
||||
r = dns_answer_add_raw(copy, rr, ifindex);
|
||||
r = dns_answer_add_raw(copy, rr, ifindex, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -416,16 +492,17 @@ int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key) {
|
||||
int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
|
||||
DnsResourceRecord *rr_source;
|
||||
int ifindex_source, r;
|
||||
DnsAnswerFlags flags_source;
|
||||
|
||||
assert(a);
|
||||
assert(key);
|
||||
|
||||
/* Copy all RRs matching the specified key from source into *a */
|
||||
|
||||
DNS_ANSWER_FOREACH_IFINDEX(rr_source, ifindex_source, source) {
|
||||
DNS_ANSWER_FOREACH_FULL(rr_source, ifindex_source, flags_source, source) {
|
||||
|
||||
r = dns_resource_key_equal(rr_source->key, key);
|
||||
if (r < 0)
|
||||
|
@ -438,7 +515,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);
|
||||
r = dns_answer_add(*a, rr_source, ifindex_source, flags_source|or_flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -446,6 +523,20 @@ 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 r;
|
||||
|
||||
assert(to);
|
||||
assert(from);
|
||||
assert(key);
|
||||
|
||||
r = dns_answer_copy_by_key(to, *from, key, or_flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return dns_answer_remove_by_key(from, key);
|
||||
}
|
||||
|
||||
void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
|
||||
DnsAnswerItem *items;
|
||||
unsigned i, start, end;
|
||||
|
@ -548,3 +639,40 @@ int dns_answer_reserve_or_clone(DnsAnswer **a, unsigned n_free) {
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dns_answer_dump(DnsAnswer *answer, FILE *f) {
|
||||
DnsResourceRecord *rr;
|
||||
DnsAnswerFlags flags;
|
||||
int ifindex, r;
|
||||
|
||||
if (!f)
|
||||
f = stdout;
|
||||
|
||||
DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
|
||||
fputc('\t', f);
|
||||
|
||||
r = dns_resource_record_to_string(rr, &t);
|
||||
if (r < 0) {
|
||||
log_oom();
|
||||
continue;
|
||||
}
|
||||
|
||||
fputs(t, f);
|
||||
|
||||
if (ifindex != 0 || flags & (DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHEABLE|DNS_ANSWER_SHARED_OWNER))
|
||||
fputs("\t;", f);
|
||||
|
||||
if (ifindex != 0)
|
||||
printf(" ifindex=%i", ifindex);
|
||||
if (flags & DNS_ANSWER_AUTHENTICATED)
|
||||
fputs(" authenticated", f);
|
||||
if (flags & DNS_ANSWER_CACHEABLE)
|
||||
fputs(" cachable", f);
|
||||
if (flags & DNS_ANSWER_SHARED_OWNER)
|
||||
fputs(" shared-owner", f);
|
||||
|
||||
fputc('\n', f);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,11 +32,18 @@ typedef struct DnsAnswerItem DnsAnswerItem;
|
|||
* can qualify A and AAAA RRs referring to a local link with the
|
||||
* right ifindex.
|
||||
*
|
||||
* Note that we usually encode the empty answer as a simple NULL. */
|
||||
* Note that we usually encode the the empty DnsAnswer object as a simple NULL. */
|
||||
|
||||
typedef enum DnsAnswerFlags {
|
||||
DNS_ANSWER_AUTHENTICATED = 1, /* Item has been authenticated */
|
||||
DNS_ANSWER_CACHEABLE = 2, /* Item is subject to caching */
|
||||
DNS_ANSWER_SHARED_OWNER = 4, /* For mDNS: RRset may be owner by multiple peers */
|
||||
} DnsAnswerFlags;
|
||||
|
||||
struct DnsAnswerItem {
|
||||
DnsResourceRecord *rr;
|
||||
int ifindex;
|
||||
DnsAnswerFlags flags;
|
||||
};
|
||||
|
||||
struct DnsAnswer {
|
||||
|
@ -49,15 +56,17 @@ DnsAnswer *dns_answer_new(unsigned n);
|
|||
DnsAnswer *dns_answer_ref(DnsAnswer *a);
|
||||
DnsAnswer *dns_answer_unref(DnsAnswer *a);
|
||||
|
||||
int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex);
|
||||
int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex);
|
||||
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_soa(DnsAnswer *a, const char *name, uint32_t ttl);
|
||||
|
||||
int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key);
|
||||
int dns_answer_contains_rr(DnsAnswer *a, DnsResourceRecord *rr);
|
||||
int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *combined_flags);
|
||||
int dns_answer_contains_rr(DnsAnswer *a, DnsResourceRecord *rr, DnsAnswerFlags *combined_flags);
|
||||
int dns_answer_contains_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *combined_flags);
|
||||
int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a);
|
||||
|
||||
int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret);
|
||||
int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret);
|
||||
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_merge(DnsAnswer *a, DnsAnswer *b, DnsAnswer **ret);
|
||||
int dns_answer_extend(DnsAnswer **a, DnsAnswer *b);
|
||||
|
@ -68,12 +77,15 @@ int dns_answer_reserve(DnsAnswer **a, unsigned n_free);
|
|||
int dns_answer_reserve_or_clone(DnsAnswer **a, unsigned n_free);
|
||||
|
||||
int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key);
|
||||
int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key);
|
||||
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);
|
||||
|
||||
static inline unsigned dns_answer_size(DnsAnswer *a) {
|
||||
return a ? a->n_rrs : 0;
|
||||
}
|
||||
|
||||
void dns_answer_dump(DnsAnswer *answer, FILE *f);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref);
|
||||
|
||||
#define _DNS_ANSWER_FOREACH(q, kk, a) \
|
||||
|
@ -93,6 +105,36 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref);
|
|||
0; \
|
||||
}); \
|
||||
(a) && (UNIQ_T(i, q) < (a)->n_rrs); \
|
||||
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))
|
||||
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))
|
||||
|
||||
#define DNS_ANSWER_FOREACH_IFINDEX(kk, ifindex, a) _DNS_ANSWER_FOREACH_IFINDEX(UNIQ, kk, ifindex, a)
|
||||
|
||||
#define _DNS_ANSWER_FOREACH_FLAGS(q, kk, fl, a) \
|
||||
for (unsigned UNIQ_T(i, q) = ({ \
|
||||
(kk) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].rr : NULL; \
|
||||
(fl) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].flags : 0; \
|
||||
0; \
|
||||
}); \
|
||||
(a) && (UNIQ_T(i, q) < (a)->n_rrs); \
|
||||
UNIQ_T(i, q)++, \
|
||||
(kk) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].rr : NULL), \
|
||||
(fl) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].flags : 0))
|
||||
|
||||
#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) \
|
||||
for (unsigned 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; \
|
||||
0; \
|
||||
}); \
|
||||
(a) && (UNIQ_T(i, q) < (a)->n_rrs); \
|
||||
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))
|
||||
|
||||
#define DNS_ANSWER_FOREACH_FULL(kk, ifindex, flags, a) _DNS_ANSWER_FOREACH_FULL(UNIQ, kk, ifindex, flags, a)
|
||||
|
|
|
@ -26,11 +26,11 @@
|
|||
#include "resolved-dns-packet.h"
|
||||
#include "string-util.h"
|
||||
|
||||
/* Never cache more than 1K entries */
|
||||
#define CACHE_MAX 1024
|
||||
/* Never cache more than 4K entries */
|
||||
#define CACHE_MAX 4096
|
||||
|
||||
/* We never keep any item longer than 10min in our cache */
|
||||
#define CACHE_TTL_MAX_USEC (10 * USEC_PER_MINUTE)
|
||||
/* We never keep any item longer than 2h in our cache */
|
||||
#define CACHE_TTL_MAX_USEC (2 * USEC_PER_HOUR)
|
||||
|
||||
typedef enum DnsCacheItemType DnsCacheItemType;
|
||||
typedef struct DnsCacheItem DnsCacheItem;
|
||||
|
@ -42,14 +42,18 @@ enum DnsCacheItemType {
|
|||
};
|
||||
|
||||
struct DnsCacheItem {
|
||||
DnsCacheItemType type;
|
||||
DnsResourceKey *key;
|
||||
DnsResourceRecord *rr;
|
||||
|
||||
usec_t until;
|
||||
DnsCacheItemType type;
|
||||
unsigned prioq_idx;
|
||||
bool authenticated;
|
||||
bool authenticated:1;
|
||||
bool shared_owner:1;
|
||||
|
||||
int owner_family;
|
||||
union in_addr_union owner_address;
|
||||
|
||||
unsigned prioq_idx;
|
||||
LIST_FIELDS(DnsCacheItem, by_key);
|
||||
};
|
||||
|
||||
|
@ -64,7 +68,7 @@ static void dns_cache_item_free(DnsCacheItem *i) {
|
|||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsCacheItem*, dns_cache_item_free);
|
||||
|
||||
static void dns_cache_item_remove_and_free(DnsCache *c, DnsCacheItem *i) {
|
||||
static void dns_cache_item_unlink_and_free(DnsCache *c, DnsCacheItem *i) {
|
||||
DnsCacheItem *first;
|
||||
|
||||
assert(c);
|
||||
|
@ -85,13 +89,49 @@ static void dns_cache_item_remove_and_free(DnsCache *c, DnsCacheItem *i) {
|
|||
dns_cache_item_free(i);
|
||||
}
|
||||
|
||||
static bool dns_cache_remove_by_rr(DnsCache *c, DnsResourceRecord *rr) {
|
||||
DnsCacheItem *first, *i;
|
||||
int r;
|
||||
|
||||
first = hashmap_get(c->by_key, rr->key);
|
||||
LIST_FOREACH(by_key, i, first) {
|
||||
r = dns_resource_record_equal(i->rr, rr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0) {
|
||||
dns_cache_item_unlink_and_free(c, i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool dns_cache_remove_by_key(DnsCache *c, DnsResourceKey *key) {
|
||||
DnsCacheItem *first, *i, *n;
|
||||
|
||||
assert(c);
|
||||
assert(key);
|
||||
|
||||
first = hashmap_remove(c->by_key, key);
|
||||
if (!first)
|
||||
return false;
|
||||
|
||||
LIST_FOREACH_SAFE(by_key, i, n, first) {
|
||||
prioq_remove(c->by_expiry, i, &i->prioq_idx);
|
||||
dns_cache_item_free(i);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void dns_cache_flush(DnsCache *c) {
|
||||
DnsCacheItem *i;
|
||||
DnsResourceKey *key;
|
||||
|
||||
assert(c);
|
||||
|
||||
while ((i = hashmap_first(c->by_key)))
|
||||
dns_cache_item_remove_and_free(c, i);
|
||||
while ((key = hashmap_first_key(c->by_key)))
|
||||
dns_cache_remove_by_key(c, key);
|
||||
|
||||
assert(hashmap_size(c->by_key) == 0);
|
||||
assert(prioq_size(c->by_expiry) == 0);
|
||||
|
@ -100,21 +140,6 @@ void dns_cache_flush(DnsCache *c) {
|
|||
c->by_expiry = prioq_free(c->by_expiry);
|
||||
}
|
||||
|
||||
static bool dns_cache_remove(DnsCache *c, DnsResourceKey *key) {
|
||||
DnsCacheItem *i;
|
||||
bool exist = false;
|
||||
|
||||
assert(c);
|
||||
assert(key);
|
||||
|
||||
while ((i = hashmap_get(c->by_key, key))) {
|
||||
dns_cache_item_remove_and_free(c, i);
|
||||
exist = true;
|
||||
}
|
||||
|
||||
return exist;
|
||||
}
|
||||
|
||||
static void dns_cache_make_space(DnsCache *c, unsigned add) {
|
||||
assert(c);
|
||||
|
||||
|
@ -142,7 +167,7 @@ static void dns_cache_make_space(DnsCache *c, unsigned add) {
|
|||
/* Take an extra reference to the key so that it
|
||||
* doesn't go away in the middle of the remove call */
|
||||
key = dns_resource_key_ref(i->key);
|
||||
dns_cache_remove(c, key);
|
||||
dns_cache_remove_by_key(c, key);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,7 +179,6 @@ void dns_cache_prune(DnsCache *c) {
|
|||
/* Remove all entries that are past their TTL */
|
||||
|
||||
for (;;) {
|
||||
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
|
||||
DnsCacheItem *i;
|
||||
|
||||
i = prioq_peek(c->by_expiry);
|
||||
|
@ -167,10 +191,19 @@ void dns_cache_prune(DnsCache *c) {
|
|||
if (i->until > t)
|
||||
break;
|
||||
|
||||
/* Take an extra reference to the key so that it
|
||||
* doesn't go away in the middle of the remove call */
|
||||
key = dns_resource_key_ref(i->key);
|
||||
dns_cache_remove(c, key);
|
||||
/* Depending whether this is an mDNS shared entry
|
||||
* either remove only this one RR or the whole
|
||||
* RRset */
|
||||
if (i->shared_owner)
|
||||
dns_cache_item_unlink_and_free(c, i);
|
||||
else {
|
||||
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
|
||||
|
||||
/* Take an extra reference to the key so that it
|
||||
* doesn't go away in the middle of the remove call */
|
||||
key = dns_resource_key_ref(i->key);
|
||||
dns_cache_remove_by_key(c, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,10 +272,20 @@ static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsResourceRecord *rr, bool authenticated, usec_t timestamp) {
|
||||
static void dns_cache_item_update_positive(
|
||||
DnsCache *c,
|
||||
DnsCacheItem *i,
|
||||
DnsResourceRecord *rr,
|
||||
bool authenticated,
|
||||
bool shared_owner,
|
||||
usec_t timestamp,
|
||||
int owner_family,
|
||||
const union in_addr_union *owner_address) {
|
||||
|
||||
assert(c);
|
||||
assert(i);
|
||||
assert(rr);
|
||||
assert(owner_address);
|
||||
|
||||
i->type = DNS_CACHE_POSITIVE;
|
||||
|
||||
|
@ -259,8 +302,12 @@ static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsReso
|
|||
dns_resource_key_unref(i->key);
|
||||
i->key = dns_resource_key_ref(rr->key);
|
||||
|
||||
i->authenticated = authenticated;
|
||||
i->until = timestamp + MIN(rr->ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
|
||||
i->authenticated = authenticated;
|
||||
i->shared_owner = shared_owner;
|
||||
|
||||
i->owner_family = owner_family;
|
||||
i->owner_address = *owner_address;
|
||||
|
||||
prioq_reshuffle(c->by_expiry, i, &i->prioq_idx);
|
||||
}
|
||||
|
@ -269,6 +316,7 @@ static int dns_cache_put_positive(
|
|||
DnsCache *c,
|
||||
DnsResourceRecord *rr,
|
||||
bool authenticated,
|
||||
bool shared_owner,
|
||||
usec_t timestamp,
|
||||
int owner_family,
|
||||
const union in_addr_union *owner_address) {
|
||||
|
@ -282,9 +330,15 @@ static int dns_cache_put_positive(
|
|||
assert(rr);
|
||||
assert(owner_address);
|
||||
|
||||
/* New TTL is 0? Delete the entry... */
|
||||
/* Never cache pseudo RRs */
|
||||
if (dns_class_is_pseudo(rr->key->class))
|
||||
return 0;
|
||||
if (dns_type_is_pseudo(rr->key->type))
|
||||
return 0;
|
||||
|
||||
/* New TTL is 0? Delete this specific entry... */
|
||||
if (rr->ttl <= 0) {
|
||||
k = dns_cache_remove(c, rr->key);
|
||||
k = dns_cache_remove_by_rr(c, rr);
|
||||
|
||||
if (log_get_max_level() >= LOG_DEBUG) {
|
||||
r = dns_resource_key_to_string(rr->key, &key_str);
|
||||
|
@ -300,15 +354,18 @@ static int dns_cache_put_positive(
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (rr->key->class == DNS_CLASS_ANY)
|
||||
return 0;
|
||||
if (dns_type_is_pseudo(rr->key->type))
|
||||
return 0;
|
||||
|
||||
/* Entry exists already? Update TTL and timestamp */
|
||||
/* Entry exists already? Update TTL, timestamp and owner*/
|
||||
existing = dns_cache_get(c, rr);
|
||||
if (existing) {
|
||||
dns_cache_item_update_positive(c, existing, rr, authenticated, timestamp);
|
||||
dns_cache_item_update_positive(
|
||||
c,
|
||||
existing,
|
||||
rr,
|
||||
authenticated,
|
||||
shared_owner,
|
||||
timestamp,
|
||||
owner_family,
|
||||
owner_address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -327,10 +384,11 @@ static int dns_cache_put_positive(
|
|||
i->key = dns_resource_key_ref(rr->key);
|
||||
i->rr = dns_resource_record_ref(rr);
|
||||
i->until = timestamp + MIN(i->rr->ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
|
||||
i->prioq_idx = PRIOQ_IDX_NULL;
|
||||
i->authenticated = authenticated;
|
||||
i->shared_owner = shared_owner;
|
||||
i->owner_family = owner_family;
|
||||
i->owner_address = *owner_address;
|
||||
i->authenticated = authenticated;
|
||||
i->prioq_idx = PRIOQ_IDX_NULL;
|
||||
|
||||
r = dns_cache_link_item(c, i);
|
||||
if (r < 0)
|
||||
|
@ -341,7 +399,7 @@ static int dns_cache_put_positive(
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
log_debug("Added cache entry for %s", key_str);
|
||||
log_debug("Added positive cache entry for %s", key_str);
|
||||
}
|
||||
|
||||
i = NULL;
|
||||
|
@ -366,14 +424,14 @@ static int dns_cache_put_negative(
|
|||
assert(key);
|
||||
assert(owner_address);
|
||||
|
||||
dns_cache_remove(c, key);
|
||||
|
||||
if (key->class == DNS_CLASS_ANY)
|
||||
/* Never cache pseudo RR keys. DNS_TYPE_ANY is particularly
|
||||
* important to filter out as we use this as a pseudo-type for
|
||||
* NXDOMAIN entries */
|
||||
if (dns_class_is_pseudo(key->class))
|
||||
return 0;
|
||||
if (dns_type_is_pseudo(key->type))
|
||||
/* ANY is particularly important to filter out as we
|
||||
* use this as a pseudo-type for NXDOMAIN entries */
|
||||
return 0;
|
||||
|
||||
if (soa_ttl <= 0) {
|
||||
if (log_get_max_level() >= LOG_DEBUG) {
|
||||
r = dns_resource_key_to_string(key, &key_str);
|
||||
|
@ -401,10 +459,10 @@ static int dns_cache_put_negative(
|
|||
|
||||
i->type = rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA : DNS_CACHE_NXDOMAIN;
|
||||
i->until = timestamp + MIN(soa_ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
|
||||
i->prioq_idx = PRIOQ_IDX_NULL;
|
||||
i->authenticated = authenticated;
|
||||
i->owner_family = owner_family;
|
||||
i->owner_address = *owner_address;
|
||||
i->authenticated = authenticated;
|
||||
i->prioq_idx = PRIOQ_IDX_NULL;
|
||||
|
||||
if (i->type == DNS_CACHE_NXDOMAIN) {
|
||||
/* NXDOMAIN entries should apply equally to all types, so we use ANY as
|
||||
|
@ -431,30 +489,57 @@ static int dns_cache_put_negative(
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void dns_cache_remove_previous(
|
||||
DnsCache *c,
|
||||
DnsResourceKey *key,
|
||||
DnsAnswer *answer) {
|
||||
|
||||
DnsResourceRecord *rr;
|
||||
DnsAnswerFlags flags;
|
||||
|
||||
assert(c);
|
||||
|
||||
/* First, if we were passed a key (i.e. on LLMNR/DNS, but
|
||||
* not on mDNS), delete all matching old RRs, so that we only
|
||||
* keep complete by_key in place. */
|
||||
if (key)
|
||||
dns_cache_remove_by_key(c, key);
|
||||
|
||||
/* Second, flush all entries matching the answer, unless this
|
||||
* is an RR that is explicitly marked to be "shared" between
|
||||
* peers (i.e. mDNS RRs without the flush-cache bit set). */
|
||||
DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
|
||||
if ((flags & DNS_ANSWER_CACHEABLE) == 0)
|
||||
continue;
|
||||
|
||||
if (flags & DNS_ANSWER_SHARED_OWNER)
|
||||
continue;
|
||||
|
||||
dns_cache_remove_by_key(c, rr->key);
|
||||
}
|
||||
}
|
||||
|
||||
int dns_cache_put(
|
||||
DnsCache *c,
|
||||
DnsResourceKey *key,
|
||||
int rcode,
|
||||
DnsAnswer *answer,
|
||||
unsigned max_rrs,
|
||||
bool authenticated,
|
||||
usec_t timestamp,
|
||||
int owner_family,
|
||||
const union in_addr_union *owner_address) {
|
||||
|
||||
DnsResourceRecord *soa = NULL, *rr;
|
||||
unsigned cache_keys, i;
|
||||
DnsAnswerFlags flags;
|
||||
unsigned cache_keys;
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
assert(owner_address);
|
||||
|
||||
if (key) {
|
||||
/* First, if we were passed a key, delete all matching old RRs,
|
||||
* so that we only keep complete by_key in place. */
|
||||
dns_cache_remove(c, key);
|
||||
}
|
||||
dns_cache_remove_previous(c, key, answer);
|
||||
|
||||
if (!answer) {
|
||||
if (dns_answer_size(answer) <= 0) {
|
||||
if (log_get_max_level() >= LOG_DEBUG) {
|
||||
_cleanup_free_ char *key_str = NULL;
|
||||
|
||||
|
@ -468,19 +553,13 @@ int dns_cache_put(
|
|||
return 0;
|
||||
}
|
||||
|
||||
DNS_ANSWER_FOREACH(rr, answer)
|
||||
if (rr->key->cache_flush)
|
||||
dns_cache_remove(c, rr->key);
|
||||
|
||||
/* We only care for positive replies and NXDOMAINs, on all
|
||||
* other replies we will simply flush the respective entries,
|
||||
* and that's it */
|
||||
|
||||
if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
|
||||
return 0;
|
||||
|
||||
cache_keys = answer->n_rrs;
|
||||
|
||||
cache_keys = dns_answer_size(answer);
|
||||
if (key)
|
||||
cache_keys ++;
|
||||
|
||||
|
@ -491,19 +570,26 @@ int dns_cache_put(
|
|||
timestamp = now(clock_boottime_or_monotonic());
|
||||
|
||||
/* Second, add in positive entries for all contained RRs */
|
||||
for (i = 0; i < MIN(max_rrs, answer->n_rrs); i++) {
|
||||
rr = answer->items[i].rr;
|
||||
DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
|
||||
if ((flags & DNS_ANSWER_CACHEABLE) == 0)
|
||||
continue;
|
||||
|
||||
r = dns_cache_put_positive(c, rr, authenticated, timestamp, owner_family, owner_address);
|
||||
r = dns_cache_put_positive(
|
||||
c,
|
||||
rr,
|
||||
flags & DNS_ANSWER_AUTHENTICATED,
|
||||
flags & DNS_ANSWER_SHARED_OWNER,
|
||||
timestamp,
|
||||
owner_family, owner_address);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!key)
|
||||
if (!key) /* mDNS doesn't know negative caching, really */
|
||||
return 0;
|
||||
|
||||
/* Third, add in negative entries if the key has no RR */
|
||||
r = dns_answer_match_key(answer, key);
|
||||
r = dns_answer_match_key(answer, key, NULL);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
if (r > 0)
|
||||
|
@ -512,7 +598,7 @@ int dns_cache_put(
|
|||
/* But not if it has a matching CNAME/DNAME (the negative
|
||||
* caching will be done on the canonical name, not on the
|
||||
* alias) */
|
||||
r = dns_answer_find_cname_or_dname(answer, key, NULL);
|
||||
r = dns_answer_find_cname_or_dname(answer, key, NULL, NULL);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
if (r > 0)
|
||||
|
@ -522,13 +608,25 @@ int dns_cache_put(
|
|||
* matching SOA record in the packet is used to to enable
|
||||
* negative caching. */
|
||||
|
||||
r = dns_answer_find_soa(answer, key, &soa);
|
||||
r = dns_answer_find_soa(answer, key, &soa, &flags);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
if (r == 0)
|
||||
return 0;
|
||||
|
||||
r = dns_cache_put_negative(c, key, rcode, authenticated, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address);
|
||||
/* Refuse using the SOA data if it is unsigned, but the key is
|
||||
* signed */
|
||||
if (authenticated && (flags & DNS_ANSWER_AUTHENTICATED) == 0)
|
||||
return 0;
|
||||
|
||||
r = dns_cache_put_negative(
|
||||
c,
|
||||
key,
|
||||
rcode,
|
||||
authenticated,
|
||||
timestamp,
|
||||
MIN(soa->soa.minimum, soa->ttl),
|
||||
owner_family, owner_address);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
|
@ -539,10 +637,14 @@ fail:
|
|||
* added, just in case */
|
||||
|
||||
if (key)
|
||||
dns_cache_remove(c, key);
|
||||
dns_cache_remove_by_key(c, key);
|
||||
|
||||
for (i = 0; i < answer->n_rrs; i++)
|
||||
dns_cache_remove(c, answer->items[i].rr->key);
|
||||
DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
|
||||
if ((flags & DNS_ANSWER_CACHEABLE) == 0)
|
||||
continue;
|
||||
|
||||
dns_cache_remove_by_key(c, rr->key);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
@ -582,8 +684,6 @@ static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, D
|
|||
|
||||
/* OK, let's look for cached DNAME records. */
|
||||
for (;;) {
|
||||
char label[DNS_LABEL_MAX];
|
||||
|
||||
if (isempty(n))
|
||||
return NULL;
|
||||
|
||||
|
@ -592,13 +692,13 @@ static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, D
|
|||
return i;
|
||||
|
||||
/* Jump one label ahead */
|
||||
r = dns_label_unescape(&n, label, sizeof(label));
|
||||
r = dns_name_parent(&n);
|
||||
if (r <= 0)
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (k-> type != DNS_TYPE_NSEC) {
|
||||
if (k->type != DNS_TYPE_NSEC) {
|
||||
/* Check if we have an NSEC record instead for the name. */
|
||||
i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_NSEC, n));
|
||||
if (i)
|
||||
|
@ -722,7 +822,7 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
|
|||
if (!j->rr)
|
||||
continue;
|
||||
|
||||
r = dns_answer_add(answer, j->rr, 0);
|
||||
r = dns_answer_add(answer, j->rr, 0, j->authenticated ? DNS_ANSWER_AUTHENTICATED : 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -787,7 +887,7 @@ int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
|
|||
if (!j->rr)
|
||||
continue;
|
||||
|
||||
if (!dns_key_is_shared(j->rr->key))
|
||||
if (!j->shared_owner)
|
||||
continue;
|
||||
|
||||
r = dns_packet_append_rr(p, j->rr, NULL, NULL);
|
||||
|
|
|
@ -39,7 +39,7 @@ typedef struct DnsCache {
|
|||
void dns_cache_flush(DnsCache *c);
|
||||
void dns_cache_prune(DnsCache *c);
|
||||
|
||||
int dns_cache_put(DnsCache *c, DnsResourceKey *key, int rcode, DnsAnswer *answer, unsigned max_rrs, bool authenticated, usec_t timestamp, int owner_family, const union in_addr_union *owner_address);
|
||||
int dns_cache_put(DnsCache *c, DnsResourceKey *key, int rcode, DnsAnswer *answer, bool authenticated, usec_t timestamp, int owner_family, const union in_addr_union *owner_address);
|
||||
int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **answer, bool *authenticated);
|
||||
|
||||
int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address);
|
||||
|
|
|
@ -35,16 +35,13 @@
|
|||
*
|
||||
* TODO:
|
||||
*
|
||||
* - Iterative validation
|
||||
* - NSEC proof of non-existance
|
||||
* - NSEC3 proof of non-existance
|
||||
* - Make trust anchor store read additional DS+DNSKEY data from disk
|
||||
* - wildcard zones compatibility
|
||||
* - multi-label zone compatibility
|
||||
* - DNSSEC cname/dname compatibility
|
||||
* - cname/dname compatibility
|
||||
* - per-interface DNSSEC setting
|
||||
* - retry on failed validation
|
||||
* - fix TTL for cache entries to match RRSIG TTL
|
||||
* - retry on failed validation?
|
||||
* - DSA support
|
||||
* - EC support?
|
||||
*
|
||||
|
@ -499,7 +496,7 @@ int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnske
|
|||
return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer);
|
||||
}
|
||||
|
||||
int dnssec_key_match_rrsig(DnsResourceKey *key, DnsResourceRecord *rrsig) {
|
||||
int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
|
||||
assert(key);
|
||||
assert(rrsig);
|
||||
|
||||
|
@ -529,7 +526,7 @@ int dnssec_verify_rrset_search(
|
|||
assert(key);
|
||||
assert(result);
|
||||
|
||||
/* Verifies all RRs from "a" that match the key "key", against DNSKEY and DS RRs in "validated_dnskeys" */
|
||||
/* Verifies all RRs from "a" that match the key "key" against DNSKEYs in "validated_dnskeys" */
|
||||
|
||||
if (!a || a->n_rrs <= 0)
|
||||
return -ENODATA;
|
||||
|
@ -537,6 +534,7 @@ int dnssec_verify_rrset_search(
|
|||
/* Iterate through each RRSIG RR. */
|
||||
DNS_ANSWER_FOREACH(rrsig, a) {
|
||||
DnsResourceRecord *dnskey;
|
||||
DnsAnswerFlags flags;
|
||||
|
||||
/* Is this an RRSIG RR that applies to RRs matching our key? */
|
||||
r = dnssec_key_match_rrsig(key, rrsig);
|
||||
|
@ -548,9 +546,12 @@ int dnssec_verify_rrset_search(
|
|||
found_rrsig = true;
|
||||
|
||||
/* Look for a matching key */
|
||||
DNS_ANSWER_FOREACH(dnskey, validated_dnskeys) {
|
||||
DNS_ANSWER_FOREACH_FLAGS(dnskey, flags, validated_dnskeys) {
|
||||
DnssecResult one_result;
|
||||
|
||||
if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
|
||||
continue;
|
||||
|
||||
/* Is this a DNSKEY RR that matches they key of our RRSIG? */
|
||||
r = dnssec_rrsig_match_dnskey(rrsig, dnskey);
|
||||
if (r < 0)
|
||||
|
@ -626,6 +627,23 @@ int dnssec_verify_rrset_search(
|
|||
return 0;
|
||||
}
|
||||
|
||||
int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
|
||||
DnsResourceRecord *rr;
|
||||
int r;
|
||||
|
||||
/* Checks whether there's at least one RRSIG in 'a' that proctects RRs of the specified key */
|
||||
|
||||
DNS_ANSWER_FOREACH(rr, a) {
|
||||
r = dnssec_key_match_rrsig(key, rr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
|
||||
size_t c = 0;
|
||||
int r;
|
||||
|
@ -776,6 +794,7 @@ finish:
|
|||
|
||||
int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
|
||||
DnsResourceRecord *ds;
|
||||
DnsAnswerFlags flags;
|
||||
int r;
|
||||
|
||||
assert(dnskey);
|
||||
|
@ -783,7 +802,10 @@ int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_
|
|||
if (dnskey->key->type != DNS_TYPE_DNSKEY)
|
||||
return 0;
|
||||
|
||||
DNS_ANSWER_FOREACH(ds, validated_ds) {
|
||||
DNS_ANSWER_FOREACH_FLAGS(ds, flags, validated_ds) {
|
||||
|
||||
if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
|
||||
continue;
|
||||
|
||||
if (ds->key->type != DNS_TYPE_DS)
|
||||
continue;
|
||||
|
@ -866,8 +888,172 @@ finish:
|
|||
return r;
|
||||
}
|
||||
|
||||
static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result) {
|
||||
_cleanup_free_ char *next_closer_domain = NULL, *l = NULL;
|
||||
uint8_t hashed[DNSSEC_HASH_SIZE_MAX];
|
||||
const char *p, *pp = NULL;
|
||||
DnsResourceRecord *rr;
|
||||
DnsAnswerFlags flags;
|
||||
int hashed_size, r;
|
||||
|
||||
assert(key);
|
||||
assert(result);
|
||||
|
||||
/* First step, look for the closest encloser NSEC3 RR in 'answer' that matches 'key' */
|
||||
p = DNS_RESOURCE_KEY_NAME(key);
|
||||
for (;;) {
|
||||
DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
|
||||
_cleanup_free_ char *hashed_domain = NULL, *label = NULL;
|
||||
|
||||
if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
|
||||
continue;
|
||||
|
||||
if (rr->key->type != DNS_TYPE_NSEC3)
|
||||
continue;
|
||||
|
||||
/* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
|
||||
if (!IN_SET(rr->nsec3.flags, 0, 1))
|
||||
continue;
|
||||
|
||||
r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr->key), p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
hashed_size = dnssec_nsec3_hash(rr, p, hashed);
|
||||
if (hashed_size == -EOPNOTSUPP) {
|
||||
*result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM;
|
||||
return 0;
|
||||
}
|
||||
if (hashed_size < 0)
|
||||
return hashed_size;
|
||||
if (rr->nsec3.next_hashed_name_size != (size_t) hashed_size)
|
||||
return -EBADMSG;
|
||||
|
||||
label = base32hexmem(hashed, hashed_size, false);
|
||||
if (!label)
|
||||
return -ENOMEM;
|
||||
|
||||
hashed_domain = strjoin(label, ".", p, NULL);
|
||||
if (!hashed_domain)
|
||||
return -ENOMEM;
|
||||
|
||||
r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), hashed_domain);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
goto found;
|
||||
}
|
||||
|
||||
/* We didn't find the closest encloser with this name,
|
||||
* but let's remember this domain name, it might be
|
||||
* the next closer name */
|
||||
|
||||
pp = p;
|
||||
|
||||
/* Strip one label from the front */
|
||||
r = dns_name_parent(&p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
*result = DNSSEC_NSEC_NO_RR;
|
||||
return 0;
|
||||
|
||||
found:
|
||||
/* We found a closest encloser in 'p'; next closer is 'pp' */
|
||||
|
||||
/* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
|
||||
if (bitmap_isset(rr->nsec3.types, DNS_TYPE_DNAME))
|
||||
return -EBADMSG;
|
||||
|
||||
/* Ensure that this data is from the delegated domain
|
||||
* (i.e. originates from the "lower" DNS server), and isn't
|
||||
* just glue records (i.e. doesn't originate from the "upper"
|
||||
* DNS server). */
|
||||
if (bitmap_isset(rr->nsec3.types, DNS_TYPE_NS) &&
|
||||
!bitmap_isset(rr->nsec3.types, DNS_TYPE_SOA))
|
||||
return -EBADMSG;
|
||||
|
||||
if (!pp) {
|
||||
/* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
|
||||
*result = bitmap_isset(rr->nsec3.types, key->type) ? DNSSEC_NSEC_FOUND : DNSSEC_NSEC_NODATA;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = dnssec_nsec3_hash(rr, pp, hashed);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r != hashed_size)
|
||||
return -EBADMSG;
|
||||
|
||||
l = base32hexmem(hashed, hashed_size, false);
|
||||
if (!l)
|
||||
return -ENOMEM;
|
||||
|
||||
next_closer_domain = strjoin(l, ".", p, NULL);
|
||||
if (!next_closer_domain)
|
||||
return -ENOMEM;
|
||||
|
||||
DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
|
||||
_cleanup_free_ char *label = NULL, *next_hashed_domain = NULL;
|
||||
const char *nsec3_parent;
|
||||
|
||||
if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
|
||||
continue;
|
||||
|
||||
if (rr->key->type != DNS_TYPE_NSEC3)
|
||||
continue;
|
||||
|
||||
/* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
|
||||
if (!IN_SET(rr->nsec3.flags, 0, 1))
|
||||
continue;
|
||||
|
||||
nsec3_parent = DNS_RESOURCE_KEY_NAME(rr->key);
|
||||
r = dns_name_parent(&nsec3_parent);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
r = dns_name_equal(p, nsec3_parent);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
label = base32hexmem(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, false);
|
||||
if (!label)
|
||||
return -ENOMEM;
|
||||
|
||||
next_hashed_domain = strjoin(label, ".", p, NULL);
|
||||
if (!next_hashed_domain)
|
||||
return -ENOMEM;
|
||||
|
||||
r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), next_closer_domain, next_hashed_domain);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0) {
|
||||
if (rr->nsec3.flags & 1)
|
||||
*result = DNSSEC_NSEC_OPTOUT;
|
||||
else
|
||||
*result = DNSSEC_NSEC_NXDOMAIN;
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
*result = DNSSEC_NSEC_NO_RR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result) {
|
||||
DnsResourceRecord *rr;
|
||||
bool have_nsec3 = false;
|
||||
DnsAnswerFlags flags;
|
||||
int r;
|
||||
|
||||
assert(key);
|
||||
|
@ -875,11 +1061,14 @@ int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r
|
|||
|
||||
/* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
|
||||
|
||||
DNS_ANSWER_FOREACH(rr, answer) {
|
||||
DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
|
||||
|
||||
if (rr->key->class != key->class)
|
||||
continue;
|
||||
|
||||
if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
|
||||
continue;
|
||||
|
||||
switch (rr->key->type) {
|
||||
|
||||
case DNS_TYPE_NSEC:
|
||||
|
@ -901,79 +1090,16 @@ int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r
|
|||
}
|
||||
break;
|
||||
|
||||
case DNS_TYPE_NSEC3: {
|
||||
_cleanup_free_ void *decoded = NULL;
|
||||
size_t decoded_size;
|
||||
char label[DNS_LABEL_MAX];
|
||||
uint8_t hashed[DNSSEC_HASH_SIZE_MAX];
|
||||
int label_length, c, q;
|
||||
const char *p;
|
||||
bool covered;
|
||||
|
||||
/* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
|
||||
if (!IN_SET(rr->nsec3.flags, 0, 1))
|
||||
continue;
|
||||
|
||||
p = DNS_RESOURCE_KEY_NAME(rr->key);
|
||||
label_length = dns_label_unescape(&p, label, sizeof(label));
|
||||
if (label_length < 0)
|
||||
return label_length;
|
||||
if (label_length == 0)
|
||||
continue;
|
||||
|
||||
r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
r = unbase32hexmem(label, label_length, false, &decoded, &decoded_size);
|
||||
if (r == -EINVAL)
|
||||
continue;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (decoded_size != rr->nsec3.next_hashed_name_size)
|
||||
continue;
|
||||
|
||||
c = memcmp(decoded, rr->nsec3.next_hashed_name, decoded_size);
|
||||
if (c == 0)
|
||||
continue;
|
||||
|
||||
r = dnssec_nsec3_hash(rr, DNS_RESOURCE_KEY_NAME(key), hashed);
|
||||
/* RFC 5155, Section 8.1 says we MUST ignore NSEC3 RRs with unknown algorithms */
|
||||
if (r == -EOPNOTSUPP)
|
||||
continue;
|
||||
if (r < 0)
|
||||
return r;
|
||||
if ((size_t) r != decoded_size)
|
||||
continue;
|
||||
|
||||
r = memcmp(decoded, hashed, decoded_size);
|
||||
if (r == 0) {
|
||||
*result = bitmap_isset(rr->nsec3.types, key->type) ? DNSSEC_NSEC_FOUND : DNSSEC_NSEC_NODATA;
|
||||
return 0;
|
||||
}
|
||||
|
||||
q = memcmp(hashed, rr->nsec3.next_hashed_name, decoded_size);
|
||||
|
||||
covered = c < 0 ?
|
||||
r < 0 && q < 0 :
|
||||
q < 0 || r < 0;
|
||||
|
||||
if (covered) {
|
||||
*result = DNSSEC_NSEC_NXDOMAIN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
case DNS_TYPE_NSEC3:
|
||||
have_nsec3 = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* OK, this was not sufficient. Let's see if NSEC3 can help. */
|
||||
if (have_nsec3)
|
||||
return dnssec_test_nsec3(answer, key, result);
|
||||
|
||||
/* No approproate NSEC RR found, report this. */
|
||||
*result = DNSSEC_NSEC_NO_RR;
|
||||
return 0;
|
||||
|
@ -981,7 +1107,6 @@ int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r
|
|||
|
||||
static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = {
|
||||
[DNSSEC_NO] = "no",
|
||||
[DNSSEC_TRUST] = "trust",
|
||||
[DNSSEC_YES] = "yes",
|
||||
};
|
||||
DEFINE_STRING_TABLE_LOOKUP(dnssec_mode, DnssecMode);
|
||||
|
|
|
@ -32,9 +32,6 @@ enum DnssecMode {
|
|||
/* No DNSSEC validation is done */
|
||||
DNSSEC_NO,
|
||||
|
||||
/* Trust the AD bit sent by the server. UNSAFE! */
|
||||
DNSSEC_TRUST,
|
||||
|
||||
/* Validate locally, if the server knows DO, but if not, don't. Don't trust the AD bit */
|
||||
DNSSEC_YES,
|
||||
|
||||
|
@ -67,7 +64,7 @@ enum DnssecResult {
|
|||
#define DNSSEC_HASH_SIZE_MAX (MAX(20, 32))
|
||||
|
||||
int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey);
|
||||
int dnssec_key_match_rrsig(DnsResourceKey *key, DnsResourceRecord *rrsig);
|
||||
int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig);
|
||||
|
||||
int dnssec_verify_rrset(DnsAnswer *answer, DnsResourceKey *key, DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, usec_t realtime, DnssecResult *result);
|
||||
int dnssec_verify_rrset_search(DnsAnswer *answer, DnsResourceKey *key, DnsAnswer *validated_dnskeys, usec_t realtime, DnssecResult *result);
|
||||
|
@ -75,6 +72,8 @@ int dnssec_verify_rrset_search(DnsAnswer *answer, DnsResourceKey *key, DnsAnswer
|
|||
int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds);
|
||||
int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds);
|
||||
|
||||
int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key);
|
||||
|
||||
uint16_t dnssec_keytag(DnsResourceRecord *dnskey);
|
||||
|
||||
int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max);
|
||||
|
@ -83,9 +82,11 @@ int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret);
|
|||
|
||||
typedef enum DnssecNsecResult {
|
||||
DNSSEC_NSEC_NO_RR, /* No suitable NSEC/NSEC3 RR found */
|
||||
DNSSEC_NSEC_UNSUPPORTED_ALGORITHM,
|
||||
DNSSEC_NSEC_NXDOMAIN,
|
||||
DNSSEC_NSEC_NODATA,
|
||||
DNSSEC_NSEC_FOUND,
|
||||
DNSSEC_NSEC_OPTOUT,
|
||||
} DnssecNsecResult;
|
||||
|
||||
int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result);
|
||||
|
|
|
@ -1455,9 +1455,9 @@ fail:
|
|||
return r;
|
||||
}
|
||||
|
||||
int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) {
|
||||
int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush, size_t *start) {
|
||||
_cleanup_free_ char *name = NULL;
|
||||
bool cache_flush = true;
|
||||
bool cache_flush = false;
|
||||
uint16_t class, type;
|
||||
DnsResourceKey *key;
|
||||
size_t saved_rindex;
|
||||
|
@ -1483,10 +1483,10 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) {
|
|||
if (p->protocol == DNS_PROTOCOL_MDNS) {
|
||||
/* See RFC6762, Section 10.2 */
|
||||
|
||||
if (class & MDNS_RR_CACHE_FLUSH)
|
||||
if (type != DNS_TYPE_OPT && (class & MDNS_RR_CACHE_FLUSH)) {
|
||||
class &= ~MDNS_RR_CACHE_FLUSH;
|
||||
else
|
||||
cache_flush = false;
|
||||
cache_flush = true;
|
||||
}
|
||||
}
|
||||
|
||||
key = dns_resource_key_new_consume(class, type, name);
|
||||
|
@ -1495,11 +1495,11 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
key->cache_flush = cache_flush;
|
||||
|
||||
name = NULL;
|
||||
*ret = key;
|
||||
|
||||
if (ret_cache_flush)
|
||||
*ret_cache_flush = cache_flush;
|
||||
if (start)
|
||||
*start = saved_rindex;
|
||||
|
||||
|
@ -1515,11 +1515,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, size_t *start) {
|
||||
int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_flush, size_t *start) {
|
||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
|
||||
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
|
||||
size_t saved_rindex, offset;
|
||||
uint16_t rdlength;
|
||||
bool cache_flush;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
|
@ -1527,11 +1528,11 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
|
|||
|
||||
saved_rindex = p->rindex;
|
||||
|
||||
r = dns_packet_read_key(p, &key, NULL);
|
||||
r = dns_packet_read_key(p, &key, &cache_flush, NULL);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
if (key->class == DNS_CLASS_ANY ||
|
||||
if (!dns_class_is_valid_rr(key->class)||
|
||||
!dns_type_is_valid_rr(key->type)) {
|
||||
r = -EBADMSG;
|
||||
goto fail;
|
||||
|
@ -1939,6 +1940,8 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
|
|||
*ret = rr;
|
||||
rr = NULL;
|
||||
|
||||
if (ret_cache_flush)
|
||||
*ret_cache_flush = cache_flush;
|
||||
if (start)
|
||||
*start = saved_rindex;
|
||||
|
||||
|
@ -1971,11 +1974,17 @@ int dns_packet_extract(DnsPacket *p) {
|
|||
|
||||
for (i = 0; i < n; i++) {
|
||||
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
|
||||
bool cache_flush;
|
||||
|
||||
r = dns_packet_read_key(p, &key, NULL);
|
||||
r = dns_packet_read_key(p, &key, &cache_flush, NULL);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
if (cache_flush) {
|
||||
r = -EBADMSG;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!dns_type_is_valid_query(key->type)) {
|
||||
r = -EBADMSG;
|
||||
goto finish;
|
||||
|
@ -1997,13 +2006,19 @@ int dns_packet_extract(DnsPacket *p) {
|
|||
|
||||
for (i = 0; i < n; i++) {
|
||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
|
||||
bool cache_flush;
|
||||
|
||||
r = dns_packet_read_rr(p, &rr, NULL);
|
||||
r = dns_packet_read_rr(p, &rr, &cache_flush, NULL);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
if (rr->key->type == DNS_TYPE_OPT) {
|
||||
|
||||
if (!dns_name_is_root(DNS_RESOURCE_KEY_NAME(rr->key))) {
|
||||
r = -EBADMSG;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* The OPT RR is only valid in the Additional section */
|
||||
if (i < DNS_PACKET_ANCOUNT(p) + DNS_PACKET_NSCOUNT(p)) {
|
||||
r = -EBADMSG;
|
||||
|
@ -2018,7 +2033,18 @@ int dns_packet_extract(DnsPacket *p) {
|
|||
|
||||
p->opt = dns_resource_record_ref(rr);
|
||||
} else {
|
||||
r = dns_answer_add(answer, rr, p->ifindex);
|
||||
|
||||
/* 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. */
|
||||
|
||||
r = dns_answer_add(answer, rr, p->ifindex,
|
||||
(i < DNS_PACKET_ANCOUNT(p) ? DNS_ANSWER_CACHEABLE : 0) |
|
||||
(p->protocol == DNS_PROTOCOL_MDNS && !cache_flush ? DNS_ANSWER_SHARED_OWNER : 0));
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
}
|
||||
|
|
|
@ -185,8 +185,8 @@ int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start);
|
|||
int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start);
|
||||
int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, size_t *start);
|
||||
int dns_packet_read_name(DnsPacket *p, char **ret, bool allow_compression, size_t *start);
|
||||
int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start);
|
||||
int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start);
|
||||
int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush, size_t *start);
|
||||
int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_flush, size_t *start);
|
||||
|
||||
void dns_packet_rewind(DnsPacket *p, size_t idx);
|
||||
|
||||
|
|
|
@ -554,7 +554,7 @@ static int synthesize_localhost_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer *
|
|||
|
||||
rr->a.in_addr.s_addr = htobe32(INADDR_LOOPBACK);
|
||||
|
||||
r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex));
|
||||
r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -568,7 +568,7 @@ static int synthesize_localhost_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer *
|
|||
|
||||
rr->aaaa.in6_addr = in6addr_loopback;
|
||||
|
||||
r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex));
|
||||
r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -576,7 +576,7 @@ static int synthesize_localhost_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer *
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to, int ifindex) {
|
||||
static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to, int ifindex, DnsAnswerFlags flags) {
|
||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
|
||||
|
||||
rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, from);
|
||||
|
@ -587,7 +587,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);
|
||||
return dns_answer_add(*answer, rr, ifindex, flags);
|
||||
}
|
||||
|
||||
static int synthesize_localhost_ptr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) {
|
||||
|
@ -597,12 +597,12 @@ static int synthesize_localhost_ptr(DnsQuery *q, DnsResourceKey *key, DnsAnswer
|
|||
assert(key);
|
||||
assert(answer);
|
||||
|
||||
r = dns_answer_reserve(answer, 1);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (IN_SET(key->type, DNS_TYPE_PTR, DNS_TYPE_ANY)) {
|
||||
r = answer_add_ptr(answer, DNS_RESOURCE_KEY_NAME(key), "localhost", SYNTHESIZE_IFINDEX(q->ifindex));
|
||||
r = dns_answer_reserve(answer, 1);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = answer_add_ptr(answer, DNS_RESOURCE_KEY_NAME(key), "localhost", SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -633,7 +633,7 @@ static int answer_add_addresses_rr(
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_answer_add(*answer, rr, addresses[j].ifindex);
|
||||
r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -674,7 +674,7 @@ static int answer_add_addresses_ptr(
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_answer_add(*answer, rr, addresses[j].ifindex);
|
||||
r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -740,15 +740,15 @@ static int synthesize_system_hostname_ptr(DnsQuery *q, int af, const union in_ad
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->llmnr_hostname, SYNTHESIZE_IFINDEX(q->ifindex));
|
||||
r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->llmnr_hostname, SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->mdns_hostname, SYNTHESIZE_IFINDEX(q->ifindex));
|
||||
r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->mdns_hostname, SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", SYNTHESIZE_IFINDEX(q->ifindex));
|
||||
r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -810,7 +810,7 @@ static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
|
|||
/* Tries to synthesize localhost RR replies where appropriate */
|
||||
|
||||
if (!IN_SET(*state,
|
||||
DNS_TRANSACTION_FAILURE,
|
||||
DNS_TRANSACTION_RCODE_FAILURE,
|
||||
DNS_TRANSACTION_NO_SERVERS,
|
||||
DNS_TRANSACTION_TIMEOUT,
|
||||
DNS_TRANSACTION_ATTEMPTS_MAX_REACHED))
|
||||
|
@ -986,6 +986,7 @@ fail:
|
|||
static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
|
||||
DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
|
||||
bool has_authenticated = false, has_non_authenticated = false;
|
||||
DnssecResult dnssec_result_authenticated = _DNSSEC_RESULT_INVALID, dnssec_result_non_authenticated = _DNSSEC_RESULT_INVALID;
|
||||
DnsTransaction *t;
|
||||
Iterator i;
|
||||
int r;
|
||||
|
@ -1009,12 +1010,16 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
|
|||
dns_query_complete(q, DNS_TRANSACTION_RESOURCES);
|
||||
return;
|
||||
}
|
||||
|
||||
q->answer_rcode = t->answer_rcode;
|
||||
|
||||
if (t->answer_authenticated)
|
||||
if (t->answer_authenticated) {
|
||||
has_authenticated = true;
|
||||
else
|
||||
dnssec_result_authenticated = t->answer_dnssec_result;
|
||||
} else {
|
||||
has_non_authenticated = true;
|
||||
dnssec_result_non_authenticated = t->answer_dnssec_result;
|
||||
}
|
||||
|
||||
state = DNS_TRANSACTION_SUCCESS;
|
||||
break;
|
||||
|
@ -1031,22 +1036,26 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
|
|||
/* Any kind of failure? Store the data away,
|
||||
* if there's nothing stored yet. */
|
||||
|
||||
if (state != DNS_TRANSACTION_SUCCESS) {
|
||||
if (state == DNS_TRANSACTION_SUCCESS)
|
||||
continue;
|
||||
|
||||
dns_answer_unref(q->answer);
|
||||
q->answer = dns_answer_ref(t->answer);
|
||||
q->answer_rcode = t->answer_rcode;
|
||||
|
||||
state = t->state;
|
||||
}
|
||||
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;
|
||||
|
||||
state = t->state;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (state == DNS_TRANSACTION_SUCCESS) {
|
||||
q->answer_authenticated = has_authenticated && !has_non_authenticated;
|
||||
q->answer_dnssec_result = q->answer_authenticated ? dnssec_result_authenticated : dnssec_result_non_authenticated;
|
||||
}
|
||||
|
||||
q->answer_protocol = c->scope->protocol;
|
||||
q->answer_family = c->scope->family;
|
||||
q->answer_authenticated = has_authenticated && !has_non_authenticated;
|
||||
|
||||
dns_search_domain_unref(q->answer_search_domain);
|
||||
q->answer_search_domain = dns_search_domain_ref(c->search_domain);
|
||||
|
|
|
@ -72,10 +72,11 @@ struct DnsQuery {
|
|||
/* Discovered data */
|
||||
DnsAnswer *answer;
|
||||
int answer_rcode;
|
||||
DnssecResult answer_dnssec_result;
|
||||
bool answer_authenticated;
|
||||
DnsProtocol answer_protocol;
|
||||
int answer_family;
|
||||
DnsSearchDomain *answer_search_domain;
|
||||
bool answer_authenticated;
|
||||
|
||||
/* Bus client information */
|
||||
sd_bus_message *request;
|
||||
|
|
|
@ -184,7 +184,7 @@ int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain) {
|
||||
int dns_resource_key_match_rr(const DnsResourceKey *key, DnsResourceRecord *rr, const char *search_domain) {
|
||||
int r;
|
||||
|
||||
assert(key);
|
||||
|
@ -1074,34 +1074,6 @@ int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
const char *dns_class_to_string(uint16_t class) {
|
||||
|
||||
switch (class) {
|
||||
|
||||
case DNS_CLASS_IN:
|
||||
return "IN";
|
||||
|
||||
case DNS_CLASS_ANY:
|
||||
return "ANY";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int dns_class_from_string(const char *s, uint16_t *class) {
|
||||
assert(s);
|
||||
assert(class);
|
||||
|
||||
if (strcaseeq(s, "IN"))
|
||||
*class = DNS_CLASS_IN;
|
||||
else if (strcaseeq(s, "ANY"))
|
||||
*class = DNS_CLASS_ANY;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i) {
|
||||
DnsTxtItem *n;
|
||||
|
||||
|
|
|
@ -33,14 +33,6 @@ typedef struct DnsResourceKey DnsResourceKey;
|
|||
typedef struct DnsResourceRecord DnsResourceRecord;
|
||||
typedef struct DnsTxtItem DnsTxtItem;
|
||||
|
||||
/* DNS record classes, see RFC 1035 */
|
||||
enum {
|
||||
DNS_CLASS_IN = 0x01,
|
||||
DNS_CLASS_ANY = 0xFF,
|
||||
_DNS_CLASS_MAX,
|
||||
_DNS_CLASS_INVALID = -1
|
||||
};
|
||||
|
||||
/* DNSKEY RR flags */
|
||||
#define DNSKEY_FLAG_ZONE_KEY (UINT16_C(1) << 8)
|
||||
#define DNSKEY_FLAG_SEP (UINT16_C(1) << 0)
|
||||
|
@ -79,7 +71,6 @@ struct DnsResourceKey {
|
|||
unsigned n_ref;
|
||||
uint16_t class, type;
|
||||
char *_name; /* don't access directy, use DNS_RESOURCE_KEY_NAME()! */
|
||||
bool cache_flush:1;
|
||||
};
|
||||
|
||||
/* Creates a temporary resource key. This is only useful to quickly
|
||||
|
@ -245,7 +236,7 @@ DnsResourceKey* dns_resource_key_ref(DnsResourceKey *key);
|
|||
DnsResourceKey* dns_resource_key_unref(DnsResourceKey *key);
|
||||
bool dns_resource_key_is_address(const DnsResourceKey *key);
|
||||
int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b);
|
||||
int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain);
|
||||
int dns_resource_key_match_rr(const DnsResourceKey *key, DnsResourceRecord *rr, const char *search_domain);
|
||||
int dns_resource_key_match_cname_or_dname(const DnsResourceKey *key, const DnsResourceKey *cname, const char *search_domain);
|
||||
int dns_resource_key_match_soa(const DnsResourceKey *key, const DnsResourceKey *soa);
|
||||
int dns_resource_key_to_string(const DnsResourceKey *key, char **ret);
|
||||
|
@ -270,9 +261,6 @@ int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical);
|
|||
DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i);
|
||||
bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b);
|
||||
|
||||
const char *dns_class_to_string(uint16_t type);
|
||||
int dns_class_from_string(const char *name, uint16_t *class);
|
||||
|
||||
extern const struct hash_ops dns_resource_key_hash_ops;
|
||||
|
||||
const char* dnssec_algorithm_to_string(int i) _const_;
|
||||
|
|
|
@ -81,7 +81,8 @@ static void dns_scope_abort_transactions(DnsScope *s) {
|
|||
* freed while we still look at it */
|
||||
|
||||
t->block_gc++;
|
||||
dns_transaction_complete(t, DNS_TRANSACTION_ABORTED);
|
||||
if (DNS_TRANSACTION_IS_LIVE(t->state))
|
||||
dns_transaction_complete(t, DNS_TRANSACTION_ABORTED);
|
||||
t->block_gc--;
|
||||
|
||||
dns_transaction_free(t);
|
||||
|
@ -789,7 +790,7 @@ DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key,
|
|||
/* 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_FAILURE) &&
|
||||
IN_SET(t->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_RCODE_FAILURE) &&
|
||||
t->answer_source != DNS_TRANSACTION_NETWORK)
|
||||
return NULL;
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -29,7 +29,7 @@ enum DnsTransactionState {
|
|||
DNS_TRANSACTION_NULL,
|
||||
DNS_TRANSACTION_PENDING,
|
||||
DNS_TRANSACTION_VALIDATING,
|
||||
DNS_TRANSACTION_FAILURE,
|
||||
DNS_TRANSACTION_RCODE_FAILURE,
|
||||
DNS_TRANSACTION_SUCCESS,
|
||||
DNS_TRANSACTION_NO_SERVERS,
|
||||
DNS_TRANSACTION_TIMEOUT,
|
||||
|
@ -62,24 +62,34 @@ struct DnsTransaction {
|
|||
DnsScope *scope;
|
||||
|
||||
DnsResourceKey *key;
|
||||
char *key_string;
|
||||
|
||||
DnsTransactionState state;
|
||||
DnssecResult dnssec_result;
|
||||
|
||||
uint16_t id;
|
||||
|
||||
bool initial_jitter_scheduled;
|
||||
bool initial_jitter_elapsed;
|
||||
bool initial_jitter_scheduled:1;
|
||||
bool initial_jitter_elapsed:1;
|
||||
|
||||
DnsPacket *sent, *received;
|
||||
|
||||
DnsAnswer *answer;
|
||||
unsigned n_answer_cacheable; /* Specifies how many RRs of the answer shall be cached, from the beginning */
|
||||
int answer_rcode;
|
||||
DnssecResult answer_dnssec_result;
|
||||
DnsTransactionSource answer_source;
|
||||
|
||||
/* Indicates whether the primary answer is authenticated,
|
||||
* i.e. whether the RRs from answer which directly match the
|
||||
* question are authenticated, or, if there are none, whether
|
||||
* the NODATA or NXDOMAIN case is. It says nothing about
|
||||
* additional RRs listed in the answer, however they have
|
||||
* their own DNS_ANSWER_AUTHORIZED FLAGS. Note that this bit
|
||||
* is defined different than the AD bit in DNS packets, as
|
||||
* that covers more than just the actual primary answer. */
|
||||
bool answer_authenticated;
|
||||
|
||||
/* Contains DS and DNSKEY RRs we already verified and need to authenticate this reply */
|
||||
/* Contains DNSKEY, DS, SOA RRs we already verified and need
|
||||
* to authenticate this reply */
|
||||
DnsAnswer *validated_keys;
|
||||
|
||||
usec_t start_usec;
|
||||
|
@ -136,6 +146,8 @@ void dns_transaction_notify(DnsTransaction *t, DnsTransaction *source);
|
|||
int dns_transaction_validate_dnssec(DnsTransaction *t);
|
||||
int dns_transaction_request_dnssec_keys(DnsTransaction *t);
|
||||
|
||||
const char *dns_transaction_key_string(DnsTransaction *t);
|
||||
|
||||
const char* dns_transaction_state_to_string(DnsTransactionState p) _const_;
|
||||
DnsTransactionState dns_transaction_state_from_string(const char *s) _pure_;
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ int dns_trust_anchor_load(DnsTrustAnchor *d) {
|
|||
if (!answer)
|
||||
return -ENOMEM;
|
||||
|
||||
r = dns_answer_add(answer, rr, 0);
|
||||
r = dns_answer_add(answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
|
|
@ -223,9 +223,9 @@ int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) {
|
|||
assert(s);
|
||||
assert(rr);
|
||||
|
||||
if (rr->key->class == DNS_CLASS_ANY)
|
||||
if (dns_class_is_pseudo(rr->key->class))
|
||||
return -EINVAL;
|
||||
if (rr->key->type == DNS_TYPE_ANY)
|
||||
if (dns_type_is_pseudo(rr->key->type))
|
||||
return -EINVAL;
|
||||
|
||||
existing = dns_zone_get(z, rr);
|
||||
|
@ -386,7 +386,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns
|
|||
if (k < 0)
|
||||
return k;
|
||||
if (k > 0) {
|
||||
r = dns_answer_add(answer, j->rr, 0);
|
||||
r = dns_answer_add(answer, j->rr, 0, DNS_ANSWER_AUTHENTICATED);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -412,7 +412,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns
|
|||
if (j->state != DNS_ZONE_ITEM_PROBING)
|
||||
tentative = false;
|
||||
|
||||
r = dns_answer_add(answer, j->rr, 0);
|
||||
r = dns_answer_add(answer, j->rr, 0, DNS_ANSWER_AUTHENTICATED);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
|
|
@ -183,6 +183,10 @@ static int link_update_dns_servers(Link *l) {
|
|||
assert(l);
|
||||
|
||||
r = sd_network_link_get_dns(l->ifindex, &nameservers);
|
||||
if (r == -ENODATA) {
|
||||
r = 0;
|
||||
goto clear;
|
||||
}
|
||||
if (r < 0)
|
||||
goto clear;
|
||||
|
||||
|
@ -222,6 +226,10 @@ static int link_update_llmnr_support(Link *l) {
|
|||
assert(l);
|
||||
|
||||
r = sd_network_link_get_llmnr(l->ifindex, &b);
|
||||
if (r == -ENODATA) {
|
||||
r = 0;
|
||||
goto clear;
|
||||
}
|
||||
if (r < 0)
|
||||
goto clear;
|
||||
|
||||
|
@ -252,6 +260,11 @@ static int link_update_search_domains(Link *l) {
|
|||
assert(l);
|
||||
|
||||
r = sd_network_link_get_domains(l->ifindex, &domains);
|
||||
if (r == -ENODATA) {
|
||||
/* networkd knows nothing about this interface, and that's fine. */
|
||||
r = 0;
|
||||
goto clear;
|
||||
}
|
||||
if (r < 0)
|
||||
goto clear;
|
||||
|
||||
|
|
|
@ -772,7 +772,7 @@ static int write_loop(int fd, void *message, size_t length) {
|
|||
int manager_write(Manager *m, int fd, DnsPacket *p) {
|
||||
int r;
|
||||
|
||||
log_debug("Sending %s packet with id %u", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p));
|
||||
log_debug("Sending %s packet with id %" PRIu16 ".", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p));
|
||||
|
||||
r = write_loop(fd, DNS_PACKET_DATA(p), p->size);
|
||||
if (r < 0)
|
||||
|
@ -887,7 +887,7 @@ int manager_send(Manager *m, int fd, int ifindex, int family, const union in_add
|
|||
assert(port > 0);
|
||||
assert(p);
|
||||
|
||||
log_debug("Sending %s packet with id %u on interface %i/%s", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p), ifindex, af_to_name(family));
|
||||
log_debug("Sending %s packet with id %" PRIu16 " on interface %i/%s.", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p), ifindex, af_to_name(family));
|
||||
|
||||
if (family == AF_INET)
|
||||
return manager_ipv4_send(m, fd, ifindex, &addr->in, port, p);
|
||||
|
|
|
@ -122,8 +122,7 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
|
|||
dns_transaction_process_reply(t, p);
|
||||
}
|
||||
|
||||
dns_cache_put(&scope->cache, NULL, DNS_PACKET_RCODE(p), p->answer,
|
||||
p->answer->n_rrs, false, 0, p->family, &p->sender);
|
||||
dns_cache_put(&scope->cache, NULL, DNS_PACKET_RCODE(p), p->answer, false, 0, 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));
|
||||
|
|
|
@ -118,7 +118,7 @@ static void test_dnssec_verify_rrset2(void) {
|
|||
|
||||
answer = dns_answer_new(1);
|
||||
assert_se(answer);
|
||||
assert_se(dns_answer_add(answer, nsec, 0) >= 0);
|
||||
assert_se(dns_answer_add(answer, nsec, 0, DNS_ANSWER_AUTHENTICATED) >= 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);
|
||||
|
@ -201,7 +201,7 @@ static void test_dnssec_verify_rrset(void) {
|
|||
|
||||
answer = dns_answer_new(1);
|
||||
assert_se(answer);
|
||||
assert_se(dns_answer_add(answer, a, 0) >= 0);
|
||||
assert_se(dns_answer_add(answer, a, 0, DNS_ANSWER_AUTHENTICATED) >= 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);
|
||||
|
|
|
@ -48,7 +48,6 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
|
|||
|
||||
assert(name);
|
||||
assert(*name);
|
||||
assert(dest);
|
||||
|
||||
n = *name;
|
||||
d = dest;
|
||||
|
@ -79,9 +78,12 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
|
|||
|
||||
else if (*n == '\\' || *n == '.') {
|
||||
/* Escaped backslash or dot */
|
||||
*(d++) = *(n++);
|
||||
|
||||
if (d)
|
||||
*(d++) = *n;
|
||||
sz--;
|
||||
r++;
|
||||
n++;
|
||||
|
||||
} else if (n[0] >= '0' && n[0] <= '9') {
|
||||
unsigned k;
|
||||
|
@ -100,7 +102,8 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
|
|||
if (k < ' ' || k > 255 || k == 127)
|
||||
return -EINVAL;
|
||||
|
||||
*(d++) = (char) k;
|
||||
if (d)
|
||||
*(d++) = (char) k;
|
||||
sz--;
|
||||
r++;
|
||||
|
||||
|
@ -111,9 +114,12 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
|
|||
} else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) {
|
||||
|
||||
/* Normal character */
|
||||
*(d++) = *(n++);
|
||||
|
||||
if (d)
|
||||
*(d++) = *n;
|
||||
sz--;
|
||||
r++;
|
||||
n++;
|
||||
} else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -122,7 +128,7 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
|
|||
if (r == 0 && *n)
|
||||
return -EINVAL;
|
||||
|
||||
if (sz >= 1)
|
||||
if (sz >= 1 && d)
|
||||
*d = 0;
|
||||
|
||||
*name = n;
|
||||
|
|
|
@ -47,6 +47,10 @@ int dns_label_unescape_suffix(const char *name, const char **label_end, char *de
|
|||
int dns_label_escape(const char *p, size_t l, char *dest, size_t sz);
|
||||
int dns_label_escape_new(const char *p, size_t l, char **ret);
|
||||
|
||||
static inline int dns_name_parent(const char **name) {
|
||||
return dns_label_unescape(name, NULL, DNS_LABEL_MAX);
|
||||
}
|
||||
|
||||
int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
|
||||
int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
|
||||
|
||||
|
|
Loading…
Reference in New Issue