Merge pull request #2190 from poettering/dnssec6

Add DNSSEC proof of unsignedness and NSEC3 proof
This commit is contained in:
Tom Gundersen 2015-12-20 01:35:44 +00:00
commit d73fe9134f
27 changed files with 1527 additions and 467 deletions

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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_;

View File

@ -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

View File

@ -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_;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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));

View File

@ -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);

View File

@ -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;

View File

@ -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);