diff options
Diffstat (limited to 'src/resolve')
-rw-r--r-- | src/resolve/dns-type.c | 5 | ||||
-rw-r--r-- | src/resolve/dns-type.h | 1 | ||||
-rw-r--r-- | src/resolve/resolved-dns-packet.c | 435 | ||||
-rw-r--r-- | src/resolve/resolved-dns-packet.h | 2 | ||||
-rw-r--r-- | src/resolve/resolved-dns-rr.c | 112 | ||||
-rw-r--r-- | src/resolve/resolved-dns-rr.h | 24 | ||||
-rw-r--r-- | src/resolve/resolved-dns-scope.c | 70 | ||||
-rw-r--r-- | src/resolve/resolved-dns-scope.h | 4 | ||||
-rw-r--r-- | src/resolve/resolved-dns-transaction.c | 192 | ||||
-rw-r--r-- | src/resolve/resolved-dns-transaction.h | 10 | ||||
-rw-r--r-- | src/resolve/resolved-manager.c | 46 | ||||
-rw-r--r-- | src/resolve/resolved-manager.h | 1 |
12 files changed, 664 insertions, 238 deletions
diff --git a/src/resolve/dns-type.c b/src/resolve/dns-type.c index a3e740896f..e1087b3219 100644 --- a/src/resolve/dns-type.c +++ b/src/resolve/dns-type.c @@ -43,3 +43,8 @@ int dns_type_from_string(const char *s) { return sc->id; } + +/* XXX: find an authorotative list of all pseudo types? */ +bool dns_type_is_pseudo(int n) { + return IN_SET(n, DNS_TYPE_ANY, DNS_TYPE_AXFR, DNS_TYPE_IXFR, DNS_TYPE_OPT); +} diff --git a/src/resolve/dns-type.h b/src/resolve/dns-type.h index 86951d233a..950af36ee3 100644 --- a/src/resolve/dns-type.h +++ b/src/resolve/dns-type.h @@ -25,6 +25,7 @@ const char *dns_type_to_string(int type); int dns_type_from_string(const char *s); +bool dns_type_is_pseudo(int n); /* DNS record types, taken from * http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml. diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 1ebebf8ea4..649e8b74e1 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -275,7 +275,7 @@ static void dns_packet_truncate(DnsPacket *p, size_t sz) { if (p->size <= sz) return; - HASHMAP_FOREACH_KEY(s, n, p->names, i) { + HASHMAP_FOREACH_KEY(n, s, p->names, i) { if (PTR_TO_SIZE(n) < sz) continue; @@ -502,6 +502,90 @@ fail: return r; } +static int dns_packet_append_type_window(DnsPacket *p, uint8_t window, uint8_t length, uint8_t *types, size_t *start) { + size_t saved_size; + int r; + + assert(p); + assert(types); + + saved_size = p->size; + + if (length != 0) { + + r = dns_packet_append_uint8(p, window, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, length, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_blob(p, types, length, NULL); + if (r < 0) + goto fail; + } + + if (start) + *start = saved_size; + + return 0; +fail: + dns_packet_truncate(p, saved_size); + return r; +} + +static int dns_packet_append_types(DnsPacket *p, Bitmap *types, size_t *start) { + Iterator i; + uint8_t window = 0; + uint8_t len = 0; + uint8_t bitmaps[32] = {}; + unsigned n; + size_t saved_size; + int r; + + assert(p); + assert(types); + + saved_size = p->size; + + BITMAP_FOREACH(n, types, i) { + uint8_t entry; + + assert(n <= 0xffff); + + if ((n << 8) != window) { + r = dns_packet_append_type_window(p, window, len, bitmaps, NULL); + if (r < 0) + goto fail; + + if (len > 0) { + len = 0; + zero(bitmaps); + } + } + + window = n << 8; + len ++; + + entry = n & 255; + + bitmaps[entry / 8] |= 1 << (7 - (entry % 8)); + } + + r = dns_packet_append_type_window(p, window, len, bitmaps, NULL); + if (r < 0) + goto fail; + + if (start) + *start = saved_size; + + return 0; +fail: + dns_packet_truncate(p, saved_size); + return r; +} + int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start) { size_t saved_size, rdlength_offset, end, rdlength; int r; @@ -677,7 +761,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star if (r < 0) goto fail; - r = dns_packet_append_blob(p, rr->sshfp.key, rr->sshfp.key_size, NULL); + r = dns_packet_append_blob(p, rr->sshfp.fingerprint, rr->sshfp.fingerprint_size, NULL); break; case DNS_TYPE_DNSKEY: @@ -732,6 +816,50 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star r = dns_packet_append_blob(p, rr->rrsig.signature, rr->rrsig.signature_size, NULL); break; + case DNS_TYPE_NSEC: + r = dns_packet_append_name(p, rr->nsec.next_domain_name, false, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_types(p, rr->nsec.types, NULL); + if (r < 0) + goto fail; + + break; + case DNS_TYPE_NSEC3: + r = dns_packet_append_uint8(p, rr->nsec3.algorithm, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->nsec3.flags, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint16(p, rr->nsec3.iterations, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->nsec3.salt_size, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_blob(p, rr->nsec3.salt, rr->nsec3.salt_size, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->nsec3.next_hashed_name_size, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_blob(p, rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_types(p, rr->nsec3.types, NULL); + if (r < 0) + goto fail; + + break; case _DNS_TYPE_INVALID: /* unparseable */ default: @@ -805,6 +933,42 @@ int dns_packet_read_blob(DnsPacket *p, void *d, size_t sz, size_t *start) { return 0; } +static int dns_packet_read_memdup( + DnsPacket *p, size_t size, + void **ret, size_t *ret_size, + size_t *ret_start) { + + const void *src; + size_t start; + int r; + + assert(p); + assert(ret); + + r = dns_packet_read(p, size, &src, &start); + if (r < 0) + return r; + + if (size <= 0) + *ret = NULL; + else { + void *copy; + + copy = memdup(src, size); + if (!copy) + return -ENOMEM; + + *ret = copy; + } + + if (ret_size) + *ret_size = size; + if (ret_start) + *ret_start = start; + + return 0; +} + int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start) { const void *d; int r; @@ -996,6 +1160,114 @@ fail: return r; } +static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *start) { + uint8_t window; + uint8_t length; + const uint8_t *bitmap; + unsigned i; + bool found = false; + size_t saved_rindex; + int r; + + assert(p); + assert(types); + + saved_rindex = p->rindex; + + r = bitmap_ensure_allocated(types); + if (r < 0) + goto fail; + + r = dns_packet_read_uint8(p, &window, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_uint8(p, &length, NULL); + if (r < 0) + goto fail; + + if (length == 0 || length > 32) + return -EBADMSG; + + r = dns_packet_read(p, length, (const void **)&bitmap, NULL); + if (r < 0) + goto fail; + + for (i = 0; i < length; i++) { + uint8_t bitmask = 1 << 7; + uint8_t bit = 0; + + if (!bitmap[i]) { + found = false; + continue; + } + + found = true; + + while (bitmask) { + if (bitmap[i] & bitmask) { + uint16_t n; + + n = (uint16_t) window << 8 | (uint16_t) bit; + + /* Ignore pseudo-types. see RFC4034 section 4.1.2 */ + if (dns_type_is_pseudo(n)) + continue; + + r = bitmap_set(*types, n); + if (r < 0) + goto fail; + } + + bit ++; + bitmask >>= 1; + } + } + + if (!found) + return -EBADMSG; + + if (start) + *start = saved_rindex; + + return 0; +fail: + dns_packet_rewind(p, saved_rindex); + return r; +} + +static int dns_packet_read_type_windows(DnsPacket *p, Bitmap **types, size_t size, size_t *start) { + size_t saved_rindex; + int r; + + saved_rindex = p->rindex; + + while (p->rindex < saved_rindex + size) { + r = dns_packet_read_type_window(p, types, NULL); + if (r < 0) + goto fail; + + /* don't read past end of current RR */ + if (p->rindex > saved_rindex + size) { + r = -EBADMSG; + goto fail; + } + } + + if (p->rindex != saved_rindex + size) { + r = -EBADMSG; + goto fail; + } + + if (start) + *start = saved_rindex; + + return 0; +fail: + dns_packet_rewind(p, saved_rindex); + return r; +} + int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) { _cleanup_free_ char *name = NULL; uint16_t class, type; @@ -1038,26 +1310,6 @@ fail: return r; } -static int dns_packet_read_public_key(DnsPacket *p, size_t length, - void **dp, size_t *lengthp, - size_t *start) { - int r; - const void *d; - void *d2; - - r = dns_packet_read(p, length, &d, NULL); - if (r < 0) - return r; - - d2 = memdup(d, length); - if (!d2) - return -ENOMEM; - - *dp = d2; - *lengthp = length; - return 0; -} - static bool loc_size_ok(uint8_t size) { uint8_t m = size >> 4, e = size & 0xF; @@ -1080,7 +1332,6 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; size_t saved_rindex, offset; uint16_t rdlength; - const void *d; int r; assert(p); @@ -1291,12 +1542,19 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { if (r < 0) goto fail; - r = dns_packet_read_public_key(p, rdlength - 4, - &rr->ds.digest, &rr->ds.digest_size, - NULL); + r = dns_packet_read_memdup(p, rdlength - 4, + &rr->ds.digest, &rr->ds.digest_size, + NULL); if (r < 0) goto fail; + if (rr->ds.digest_size <= 0) { + /* the accepted size depends on the algorithm, but for now + just ensure that the value is greater than zero */ + r = -EBADMSG; + goto fail; + } + break; case DNS_TYPE_SSHFP: r = dns_packet_read_uint8(p, &rr->sshfp.algorithm, NULL); @@ -1307,9 +1565,17 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { if (r < 0) goto fail; - r = dns_packet_read_public_key(p, rdlength - 2, - &rr->sshfp.key, &rr->sshfp.key_size, - NULL); + r = dns_packet_read_memdup(p, rdlength - 2, + &rr->sshfp.fingerprint, &rr->sshfp.fingerprint_size, + NULL); + + if (rr->sshfp.fingerprint_size <= 0) { + /* the accepted size depends on the algorithm, but for now + just ensure that the value is greater than zero */ + r = -EBADMSG; + goto fail; + } + break; case DNS_TYPE_DNSKEY: { @@ -1338,9 +1604,17 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { if (r < 0) goto fail; - r = dns_packet_read_public_key(p, rdlength - 4, - &rr->dnskey.key, &rr->dnskey.key_size, - NULL); + r = dns_packet_read_memdup(p, rdlength - 4, + &rr->dnskey.key, &rr->dnskey.key_size, + NULL); + + if (rr->dnskey.key_size <= 0) { + /* the accepted size depends on the algorithm, but for now + just ensure that the value is greater than zero */ + r = -EBADMSG; + goto fail; + } + break; } @@ -1377,24 +1651,83 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { if (r < 0) goto fail; - r = dns_packet_read_public_key(p, offset + rdlength - p->rindex, - &rr->rrsig.signature, &rr->rrsig.signature_size, - NULL); + r = dns_packet_read_memdup(p, offset + rdlength - p->rindex, + &rr->rrsig.signature, &rr->rrsig.signature_size, + NULL); + + if (rr->rrsig.signature_size <= 0) { + /* the accepted size depends on the algorithm, but for now + just ensure that the value is greater than zero */ + r = -EBADMSG; + goto fail; + } + break; - default: - unparseable: - r = dns_packet_read(p, rdlength, &d, NULL); + case DNS_TYPE_NSEC: + r = dns_packet_read_name(p, &rr->nsec.next_domain_name, false, NULL); if (r < 0) goto fail; - rr->generic.data = memdup(d, rdlength); - if (!rr->generic.data) { - r = -ENOMEM; + r = dns_packet_read_type_windows(p, &rr->nsec.types, offset + rdlength - p->rindex, NULL); + if (r < 0) + goto fail; + + /* NSEC RRs with empty bitmpas makes no sense, but the RFC does not explicitly forbid them + so we allow it */ + + break; + + case DNS_TYPE_NSEC3: { + uint8_t size; + + r = dns_packet_read_uint8(p, &rr->nsec3.algorithm, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_uint8(p, &rr->nsec3.flags, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_uint16(p, &rr->nsec3.iterations, NULL); + if (r < 0) + goto fail; + + /* this may be zero */ + r = dns_packet_read_uint8(p, &size, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_memdup(p, size, &rr->nsec3.salt, &rr->nsec3.salt_size, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_uint8(p, &size, NULL); + if (r < 0) + goto fail; + + if (size <= 0) { + r = -EBADMSG; goto fail; } - rr->generic.size = rdlength; + r = dns_packet_read_memdup(p, size, &rr->nsec3.next_hashed_name, &rr->nsec3.next_hashed_name_size, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_type_windows(p, &rr->nsec.types, offset + rdlength - p->rindex, NULL); + if (r < 0) + goto fail; + + /* empty non-terminals can have NSEC3 records, so empty bitmaps are allowed */ + + break; + } + default: + unparseable: + r = dns_packet_read_memdup(p, rdlength, &rr->generic.data, &rr->generic.size, NULL); + if (r < 0) + goto fail; break; } if (r < 0) @@ -1516,13 +1849,15 @@ static const char* const dns_protocol_table[_DNS_PROTOCOL_MAX] = { DEFINE_STRING_TABLE_LOOKUP(dns_protocol, DnsProtocol); static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = { - [DNSSEC_ALGORITHM_RSAMD5] = "RSAMD5", - [DNSSEC_ALGORITHM_DH] = "DH", - [DNSSEC_ALGORITHM_DSA] = "DSA", - [DNSSEC_ALGORITHM_ECC] = "ECC", - [DNSSEC_ALGORITHM_RSASHA1] = "RSASHA1", - [DNSSEC_ALGORITHM_INDIRECT] = "INDIRECT", - [DNSSEC_ALGORITHM_PRIVATEDNS] = "PRIVATEDNS", - [DNSSEC_ALGORITHM_PRIVATEOID] = "PRIVATEOID", + [DNSSEC_ALGORITHM_RSAMD5] = "RSAMD5", + [DNSSEC_ALGORITHM_DH] = "DH", + [DNSSEC_ALGORITHM_DSA] = "DSA", + [DNSSEC_ALGORITHM_ECC] = "ECC", + [DNSSEC_ALGORITHM_RSASHA1] = "RSASHA1", + [DNSSEC_ALGORITHM_DSA_NSEC3_SHA1] = "DSA-NSEC3-SHA1", + [DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1] = "RSASHA1-NSEC3-SHA1", + [DNSSEC_ALGORITHM_INDIRECT] = "INDIRECT", + [DNSSEC_ALGORITHM_PRIVATEDNS] = "PRIVATEDNS", + [DNSSEC_ALGORITHM_PRIVATEOID] = "PRIVATEOID", }; DEFINE_STRING_TABLE_LOOKUP(dnssec_algorithm, int); diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 6588ed9df5..58559c85df 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -223,6 +223,8 @@ enum { DNSSEC_ALGORITHM_DSA, DNSSEC_ALGORITHM_ECC, DNSSEC_ALGORITHM_RSASHA1, + DNSSEC_ALGORITHM_DSA_NSEC3_SHA1, + DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1, DNSSEC_ALGORITHM_INDIRECT = 252, DNSSEC_ALGORITHM_PRIVATEDNS, DNSSEC_ALGORITHM_PRIVATEOID, diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index 676b77713e..ad7ca26cfe 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -276,7 +276,7 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) { break; case DNS_TYPE_SSHFP: - free(rr->sshfp.key); + free(rr->sshfp.fingerprint); break; case DNS_TYPE_DNSKEY: @@ -288,6 +288,17 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) { free(rr->rrsig.signature); break; + case DNS_TYPE_NSEC: + free(rr->nsec.next_domain_name); + bitmap_free(rr->nsec.types); + break; + + case DNS_TYPE_NSEC3: + free(rr->nsec3.next_hashed_name); + free(rr->nsec3.salt); + bitmap_free(rr->nsec3.types); + break; + case DNS_TYPE_LOC: case DNS_TYPE_A: case DNS_TYPE_AAAA: @@ -423,8 +434,8 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor case DNS_TYPE_SSHFP: return a->sshfp.algorithm == b->sshfp.algorithm && a->sshfp.fptype == b->sshfp.fptype && - a->sshfp.key_size == b->sshfp.key_size && - memcmp(a->sshfp.key, b->sshfp.key, a->sshfp.key_size) == 0; + a->sshfp.fingerprint_size == b->sshfp.fingerprint_size && + memcmp(a->sshfp.fingerprint, b->sshfp.fingerprint, a->sshfp.fingerprint_size) == 0; case DNS_TYPE_DNSKEY: return a->dnskey.zone_key_flag == b->dnskey.zone_key_flag && @@ -448,6 +459,19 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor return dns_name_equal(a->rrsig.signer, b->rrsig.signer); + case DNS_TYPE_NSEC: + return dns_name_equal(a->nsec.next_domain_name, b->nsec.next_domain_name) && + bitmap_equal(a->nsec.types, b->nsec.types); + + case DNS_TYPE_NSEC3: + return a->nsec3.algorithm == b->nsec3.algorithm && + a->nsec3.flags == b->nsec3.flags && + a->nsec3.iterations == b->nsec3.iterations && + a->nsec3.salt_size == b->nsec3.salt_size && + memcmp(a->nsec3.salt, b->nsec3.salt, a->nsec3.salt_size) == 0 && + memcmp(a->nsec3.next_hashed_name, b->nsec3.next_hashed_name, a->nsec3.next_hashed_name_size) == 0 && + bitmap_equal(a->nsec3.types, b->nsec3.types); + default: return a->generic.size == b->generic.size && memcmp(a->generic.data, b->generic.data, a->generic.size) == 0; @@ -500,6 +524,38 @@ static int format_timestamp_dns(char *buf, size_t l, time_t sec) { return 0; } +static char *format_types(Bitmap *types) { + _cleanup_strv_free_ char **strv = NULL; + _cleanup_free_ char *str = NULL; + Iterator i; + unsigned type; + int r; + + BITMAP_FOREACH(type, types, i) { + if (dns_type_to_string(type)) { + r = strv_extend(&strv, dns_type_to_string(type)); + if (r < 0) + return NULL; + } else { + char *t; + + r = asprintf(&t, "TYPE%u", type); + if (r < 0) + return NULL; + + r = strv_consume(&strv, t); + if (r < 0) + return NULL; + } + } + + str = strv_join(strv, " "); + if (!str) + return NULL; + + return strjoin("( ", str, " )", NULL); +} + int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) { _cleanup_free_ char *k = NULL, *t = NULL; char *s; @@ -631,7 +687,7 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) { break; case DNS_TYPE_SSHFP: - t = hexmem(rr->sshfp.key, rr->sshfp.key_size); + t = hexmem(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size); if (!t) return -ENOMEM; @@ -704,12 +760,56 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) { break; } + case DNS_TYPE_NSEC: + t = format_types(rr->nsec.types); + if (!t) + return -ENOMEM; + + r = asprintf(&s, "%s %s %s", + k, + rr->nsec.next_domain_name, + t); + if (r < 0) + return -ENOMEM; + break; + + case DNS_TYPE_NSEC3: { + _cleanup_free_ char *salt = NULL, *hash = NULL; + + if (rr->nsec3.salt_size > 0) { + salt = hexmem(rr->nsec3.salt, rr->nsec3.salt_size); + if (!salt) + return -ENOMEM; + } + + hash = base32hexmem(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, false); + if (!hash) + return -ENOMEM; + + t = format_types(rr->nsec3.types); + if (!t) + return -ENOMEM; + + r = asprintf(&s, "%s %"PRIu8" %"PRIu8" %"PRIu16" %s %s %s", + k, + rr->nsec3.algorithm, + rr->nsec3.flags, + rr->nsec3.iterations, + rr->nsec3.salt_size > 0 ? salt : "-", + hash, + t); + if (r < 0) + return -ENOMEM; + + break; + } + default: t = hexmem(rr->generic.data, rr->generic.size); if (!t) return -ENOMEM; - r = asprintf(&s, "%s \\# %"PRIu8" %s", k, rr->generic.size, t); + r = asprintf(&s, "%s \\# %zu %s", k, rr->generic.size, t); if (r < 0) return -ENOMEM; break; @@ -740,7 +840,7 @@ int dns_class_from_string(const char *s, uint16_t *class) { if (strcaseeq(s, "IN")) *class = DNS_CLASS_IN; else if (strcaseeq(s, "ANY")) - *class = DNS_TYPE_ANY; + *class = DNS_CLASS_ANY; else return -EINVAL; diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index b375d6b9fc..0f40f3ceef 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -23,6 +23,7 @@ #include <netinet/in.h> +#include "bitmap.h" #include "hashmap.h" #include "in-addr-util.h" #include "dns-type.h" @@ -52,7 +53,7 @@ struct DnsResourceRecord { union { struct { void *data; - uint16_t size; + size_t size; } generic; struct { @@ -116,11 +117,12 @@ struct DnsResourceRecord { size_t digest_size; } ds; + /* https://tools.ietf.org/html/rfc4255#section-3.1 */ struct { uint8_t algorithm; uint8_t fptype; - void *key; - size_t key_size; + void *fingerprint; + size_t fingerprint_size; } sshfp; /* http://tools.ietf.org/html/rfc4034#section-2.1 */ @@ -145,6 +147,22 @@ struct DnsResourceRecord { void *signature; size_t signature_size; } rrsig; + + struct { + char *next_domain_name; + Bitmap *types; + } nsec; + + struct { + uint8_t algorithm; + uint8_t flags; + uint16_t iterations; + void *salt; + size_t salt_size; + void *next_hashed_name; + size_t next_hashed_name_size; + Bitmap *types; + } nsec3; }; }; diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 7b72c090c2..927a1ddc26 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -125,18 +125,17 @@ void dns_scope_next_dns_server(DnsScope *s) { manager_next_dns_server(s->manager); } -int dns_scope_emit(DnsScope *s, DnsTransaction *t, DnsPacket *p, DnsServer **server) { - DnsServer *srv = NULL; +int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) { union in_addr_union addr; int ifindex = 0, r; int family; uint16_t port; uint32_t mtu; - int fd; assert(s); assert(p); assert(p->protocol == s->protocol); + assert((s->protocol == DNS_PROTOCOL_DNS) != (fd < 0)); if (s->link) { mtu = s->link->mtu; @@ -148,28 +147,15 @@ int dns_scope_emit(DnsScope *s, DnsTransaction *t, DnsPacket *p, DnsServer **ser if (DNS_PACKET_QDCOUNT(p) > 1) return -EOPNOTSUPP; - srv = dns_scope_get_dns_server(s); - if (!srv) - return -ESRCH; - - family = srv->family; - addr = srv->address; - port = 53; - if (p->size > DNS_PACKET_UNICAST_SIZE_MAX) return -EMSGSIZE; if (p->size + UDP_PACKET_HEADER_SIZE > mtu) return -EMSGSIZE; - if (family == AF_INET) - fd = transaction_dns_ipv4_fd(t); - else if (family == AF_INET6) - fd = transaction_dns_ipv6_fd(t); - else - return -EAFNOSUPPORT; - if (fd < 0) - return fd; + r = manager_write(s->manager, fd, p); + if (r < 0) + return r; } else if (s->protocol == DNS_PROTOCOL_LLMNR) { @@ -192,20 +178,17 @@ int dns_scope_emit(DnsScope *s, DnsTransaction *t, DnsPacket *p, DnsServer **ser return -EAFNOSUPPORT; if (fd < 0) return fd; + + r = manager_send(s->manager, fd, ifindex, family, &addr, port, p); + if (r < 0) + return r; } else return -EAFNOSUPPORT; - r = manager_send(s->manager, fd, ifindex, family, &addr, port, p); - if (r < 0) - return r; - - if (server) - *server = srv; - return 1; } -int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) { +static int dns_scope_socket(DnsScope *s, int type, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) { DnsServer *srv = NULL; _cleanup_close_ int fd = -1; union sockaddr_union sa = {}; @@ -249,13 +232,15 @@ int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *add return -EAFNOSUPPORT; } - fd = socket(sa.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + fd = socket(sa.sa.sa_family, type|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (fd < 0) return -errno; - r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); - if (r < 0) - return -errno; + if (type == SOCK_STREAM) { + r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); + if (r < 0) + return -errno; + } if (s->link) { uint32_t ifindex = htobe32(s->link->ifindex); @@ -298,6 +283,14 @@ int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *add return ret; } +int dns_scope_udp_dns_socket(DnsScope *s, DnsServer **server) { + return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, 53, server); +} + +int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) { + return dns_scope_socket(s, SOCK_STREAM, family, address, port, server); +} + DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) { char **i; @@ -420,19 +413,6 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b) { return 0; } -int dns_scope_good_dns_server(DnsScope *s, int family, const union in_addr_union *address) { - assert(s); - assert(address); - - if (s->protocol != DNS_PROTOCOL_DNS) - return 1; - - if (s->link) - return !!link_find_dns_server(s->link, family, address); - else - return !!manager_find_dns_server(s->manager, family, address); -} - static int dns_scope_make_reply_packet( DnsScope *s, uint16_t id, @@ -700,7 +680,7 @@ static int on_conflict_dispatch(sd_event_source *es, usec_t usec, void *userdata return 0; } - r = dns_scope_emit(scope, NULL, p, NULL); + r = dns_scope_emit(scope, -1, p); if (r < 0) log_debug_errno(r, "Failed to send conflict packet: %m"); } diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h index 5c5ccc71c5..29479ad550 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -65,12 +65,12 @@ struct DnsScope { int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol p, int family); DnsScope* dns_scope_free(DnsScope *s); -int dns_scope_emit(DnsScope *s, DnsTransaction *t, DnsPacket *p, DnsServer **server); +int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p); int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server); +int dns_scope_udp_dns_socket(DnsScope *s, DnsServer **server); DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain); int dns_scope_good_key(DnsScope *s, DnsResourceKey *key); -int dns_scope_good_dns_server(DnsScope *s, int family, const union in_addr_union *address); DnsServer *dns_scope_get_dns_server(DnsScope *s); void dns_scope_next_dns_server(DnsScope *s); diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index e468f245f7..b235fda3d2 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -39,10 +39,8 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) { dns_packet_unref(t->received); dns_answer_unref(t->cached); - sd_event_source_unref(t->dns_ipv4_event_source); - sd_event_source_unref(t->dns_ipv6_event_source); - safe_close(t->dns_ipv4_fd); - safe_close(t->dns_ipv6_fd); + sd_event_source_unref(t->dns_event_source); + safe_close(t->dns_fd); dns_server_unref(t->server); dns_stream_free(t->stream); @@ -94,7 +92,7 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsQuestion *q) { if (!t) return -ENOMEM; - t->dns_ipv4_fd = t->dns_ipv6_fd = -1; + t->dns_fd = -1; t->question = dns_question_ref(q); @@ -245,7 +243,7 @@ static int on_stream_complete(DnsStream *s, int error) { } static int dns_transaction_open_tcp(DnsTransaction *t) { - _cleanup_(dns_server_unrefp) DnsServer *server = NULL; + DnsServer *server = NULL; _cleanup_close_ int fd = -1; int r; @@ -310,6 +308,16 @@ static int dns_transaction_open_tcp(DnsTransaction *t) { return 0; } +static void dns_transaction_next_dns_server(DnsTransaction *t) { + assert(t); + + t->server = dns_server_unref(t->server); + t->dns_event_source = sd_event_source_unref(t->dns_event_source); + t->dns_fd = safe_close(t->dns_fd); + + dns_scope_next_dns_server(t->scope); +} + void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { int r; @@ -342,24 +350,6 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { } } - if (t->scope->protocol == DNS_PROTOCOL_DNS) { - - /* For DNS we are fine with accepting packets on any - * interface, but the source IP address must be the - * one of the DNS server we queried */ - - assert(t->server); - - if (t->server->family != p->family) - return; - - if (!in_addr_equal(p->family, &p->sender, &t->server->address)) - return; - - if (p->sender_port != 53) - return; - } - if (t->received != p) { dns_packet_unref(t->received); t->received = dns_packet_ref(p); @@ -396,7 +386,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { } /* On DNS, couldn't send? Try immediately again, with a new server */ - dns_scope_next_dns_server(t->scope); + dns_transaction_next_dns_server(t); r = dns_transaction_go(t); if (r < 0) { @@ -417,8 +407,10 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { /* Only consider responses with equivalent query section to the request */ if (!dns_question_is_superset(p->question, t->question) || - !dns_question_is_superset(t->question, p->question)) + !dns_question_is_superset(t->question, p->question)) { dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); + return; + } /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */ dns_cache_put(&t->scope->cache, p->question, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender); @@ -429,6 +421,56 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { dns_transaction_complete(t, DNS_TRANSACTION_FAILURE); } +static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + DnsTransaction *t = userdata; + int r; + + assert(t); + assert(t->scope); + + r = manager_recv(t->scope->manager, fd, DNS_PROTOCOL_DNS, &p); + if (r <= 0) + return r; + + if (dns_packet_validate_reply(p) > 0 && + DNS_PACKET_ID(p) == t->id) { + dns_transaction_process_reply(t, p); + } else + log_debug("Invalid DNS packet."); + + return 0; +} + +static int dns_transaction_emit(DnsTransaction *t) { + int r; + + assert(t); + + if (t->scope->protocol == DNS_PROTOCOL_DNS && !t->server) { + DnsServer *server = NULL; + _cleanup_close_ int fd = -1; + + fd = dns_scope_udp_dns_socket(t->scope, &server); + if (fd < 0) + return fd; + + r = sd_event_add_io(t->scope->manager->event, &t->dns_event_source, fd, EPOLLIN, on_dns_packet, t); + if (r < 0) + return r; + + t->dns_fd = fd; + fd = -1; + t->server = dns_server_ref(server); + } + + r = dns_scope_emit(t->scope, t->dns_fd, t->sent); + if (r < 0) + return r; + + return 0; +} + static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) { DnsTransaction *t = userdata; int r; @@ -437,7 +479,7 @@ static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdat assert(t); /* Timeout reached? Try again, with a new server */ - dns_scope_next_dns_server(t->scope); + dns_transaction_next_dns_server(t); r = dns_transaction_go(t); if (r < 0) @@ -514,7 +556,6 @@ int dns_transaction_go(DnsTransaction *t) { } t->n_attempts++; - t->server = dns_server_unref(t->server); t->received = dns_packet_unref(t->received); t->cached = dns_answer_unref(t->cached); t->cached_rcode = 0; @@ -594,13 +635,9 @@ int dns_transaction_go(DnsTransaction *t) { * always be made via TCP on LLMNR */ r = dns_transaction_open_tcp(t); } else { - DnsServer *server; - /* Try via UDP, and if that fails due to large size try via TCP */ - r = dns_scope_emit(t->scope, t, t->sent, &server); - if (r >= 0) - t->server = dns_server_ref(server); - else if (r == -EMSGSIZE) + r = dns_transaction_emit(t); + if (r == -EMSGSIZE) r = dns_transaction_open_tcp(t); } if (r == -ESRCH) { @@ -614,7 +651,7 @@ int dns_transaction_go(DnsTransaction *t) { } /* Couldn't send? Try immediately again, with a new server */ - dns_scope_next_dns_server(t->scope); + dns_transaction_next_dns_server(t); return dns_transaction_go(t); } @@ -632,91 +669,6 @@ int dns_transaction_go(DnsTransaction *t) { return 1; } -static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - DnsTransaction *t = userdata; - int r; - - assert(t); - assert(t->scope); - - r = manager_recv(t->scope->manager, fd, DNS_PROTOCOL_DNS, &p); - if (r <= 0) - return r; - - if (dns_packet_validate_reply(p) > 0 && - DNS_PACKET_ID(p) == t->id) { - dns_transaction_process_reply(t, p); - } else - log_debug("Invalid DNS packet."); - - return 0; -} - -int transaction_dns_ipv4_fd(DnsTransaction *t) { - const int one = 1; - int r; - - assert(t); - assert(t->scope); - assert(t->scope->manager); - - if (t->dns_ipv4_fd >= 0) - return t->dns_ipv4_fd; - - t->dns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (t->dns_ipv4_fd < 0) - return -errno; - - r = setsockopt(t->dns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = sd_event_add_io(t->scope->manager->event, &t->dns_ipv4_event_source, t->dns_ipv4_fd, EPOLLIN, on_dns_packet, t); - if (r < 0) - goto fail; - - return t->dns_ipv4_fd; - -fail: - t->dns_ipv4_fd = safe_close(t->dns_ipv4_fd); - return r; -} - -int transaction_dns_ipv6_fd(DnsTransaction *t) { - const int one = 1; - int r; - - assert(t); - assert(t->scope); - assert(t->scope->manager); - - if (t->dns_ipv6_fd >= 0) - return t->dns_ipv6_fd; - - t->dns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (t->dns_ipv6_fd < 0) - return -errno; - - r = setsockopt(t->dns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = sd_event_add_io(t->scope->manager->event, &t->dns_ipv6_event_source, t->dns_ipv6_fd, EPOLLIN, on_dns_packet, t); - if (r < 0) - goto fail; - - return t->dns_ipv6_fd; - -fail: - t->dns_ipv6_fd = safe_close(t->dns_ipv6_fd); - return r; -} - static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX] = { [DNS_TRANSACTION_NULL] = "null", [DNS_TRANSACTION_PENDING] = "pending", diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h index 87f342ca11..a8f4267bc8 100644 --- a/src/resolve/resolved-dns-transaction.h +++ b/src/resolve/resolved-dns-transaction.h @@ -61,11 +61,8 @@ struct DnsTransaction { sd_event_source *timeout_event_source; unsigned n_attempts; - int dns_ipv4_fd; - int dns_ipv6_fd; - - sd_event_source *dns_ipv4_event_source; - sd_event_source *dns_ipv6_event_source; + int dns_fd; + sd_event_source *dns_event_source; /* the active server */ DnsServer *server; @@ -95,9 +92,6 @@ int dns_transaction_go(DnsTransaction *t); void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p); void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state); -int transaction_dns_ipv4_fd(DnsTransaction *t); -int transaction_dns_ipv6_fd(DnsTransaction *t); - const char* dns_transaction_state_to_string(DnsTransactionState p) _const_; DnsTransactionState dns_transaction_state_from_string(const char *s) _pure_; diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 17de14bae1..5be01d3cb8 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -912,10 +912,12 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) { if (p->ifindex == LOOPBACK_IFINDEX) p->ifindex = 0; - /* If we don't know the interface index still, we look for the - * first local interface with a matching address. Yuck! */ - if (p->ifindex <= 0) - p->ifindex = manager_find_ifindex(m, p->family, &p->destination); + if (protocol != DNS_PROTOCOL_DNS) { + /* If we don't know the interface index still, we look for the + * first local interface with a matching address. Yuck! */ + if (p->ifindex <= 0) + p->ifindex = manager_find_ifindex(m, p->family, &p->destination); + } *ret = p; p = NULL; @@ -947,6 +949,42 @@ static int sendmsg_loop(int fd, struct msghdr *mh, int flags) { } } +static int write_loop(int fd, void *message, size_t length) { + int r; + + assert(fd >= 0); + assert(message); + + for (;;) { + if (write(fd, message, length) >= 0) + return 0; + + if (errno == EINTR) + continue; + + if (errno != EAGAIN) + return -errno; + + r = fd_wait_for_event(fd, POLLOUT, SEND_TIMEOUT_USEC); + if (r < 0) + return r; + if (r == 0) + return -ETIMEDOUT; + } +} + +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)); + + r = write_loop(fd, DNS_PACKET_DATA(p), p->size); + if (r < 0) + return r; + + return 0; +} + static int manager_ipv4_send(Manager *m, int fd, int ifindex, const struct in_addr *addr, uint16_t port, DnsPacket *p) { union sockaddr_union sa = { .in.sin_family = AF_INET, diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index 005f844df2..53b5acb33c 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -119,6 +119,7 @@ void manager_next_dns_server(Manager *m); uint32_t manager_find_mtu(Manager *m); +int manager_write(Manager *m, int fd, DnsPacket *p); int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *addr, uint16_t port, DnsPacket *p); int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret); |