diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/resolve-host/resolve-host.c | 8 | ||||
-rw-r--r-- | src/resolve/resolved-dns-cache.c | 49 | ||||
-rw-r--r-- | src/resolve/resolved-dns-dnssec.c | 382 | ||||
-rw-r--r-- | src/resolve/resolved-dns-rr.c | 1 | ||||
-rw-r--r-- | src/resolve/resolved-dns-rr.h | 4 | ||||
-rw-r--r-- | src/resolve/resolved-dns-transaction.c | 15 | ||||
-rw-r--r-- | src/shared/dns-domain.c | 14 | ||||
-rw-r--r-- | src/test/test-dns-domain.c | 6 |
8 files changed, 359 insertions, 120 deletions
diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c index fa0dffc03c..dc82769ac6 100644 --- a/src/resolve-host/resolve-host.c +++ b/src/resolve-host/resolve-host.c @@ -408,13 +408,11 @@ static int resolve_record(sd_bus *bus, const char *name) { r = dns_packet_read_rr(p, &rr, NULL, NULL); if (r < 0) - return log_error_errno(r, "Failed to parse RR."); + return log_error_errno(r, "Failed to parse RR: %m"); s = dns_resource_record_to_string(rr); - if (!s) { - log_error("Failed to format RR."); - return -ENOMEM; - } + if (!s) + return log_oom(); ifname[0] = 0; if (ifindex > 0 && !if_indextoname(ifindex, ifname)) diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 49d5090d36..3193985542 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -272,6 +272,42 @@ static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) { return NULL; } +static usec_t calculate_until(DnsResourceRecord *rr, usec_t timestamp, bool use_soa_minimum) { + uint32_t ttl; + usec_t u; + + assert(rr); + + ttl = rr->ttl; + if (rr->key->type == DNS_TYPE_SOA && use_soa_minimum) { + /* If this is a SOA RR, and it is requested, clamp to + * the SOA's minimum field. This is used when we do + * negative caching, to determine the TTL for the + * negative caching entry. See RFC 2308, Section + * 5. */ + + if (ttl > rr->soa.minimum) + ttl = rr->soa.minimum; + } + + u = ttl * USEC_PER_SEC; + if (u > CACHE_TTL_MAX_USEC) + u = CACHE_TTL_MAX_USEC; + + if (rr->expiry != USEC_INFINITY) { + usec_t left; + + /* Make use of the DNSSEC RRSIG expiry info, if we + * have it */ + + left = LESS_BY(rr->expiry, now(CLOCK_REALTIME)); + if (u > left) + u = left; + } + + return timestamp + u; +} + static void dns_cache_item_update_positive( DnsCache *c, DnsCacheItem *i, @@ -302,7 +338,7 @@ static void dns_cache_item_update_positive( dns_resource_key_unref(i->key); i->key = dns_resource_key_ref(rr->key); - i->until = timestamp + MIN(rr->ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC); + i->until = calculate_until(rr, timestamp, false); i->authenticated = authenticated; i->shared_owner = shared_owner; @@ -383,7 +419,7 @@ static int dns_cache_put_positive( i->type = DNS_CACHE_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->until = calculate_until(rr, timestamp, false); i->authenticated = authenticated; i->shared_owner = shared_owner; i->owner_family = owner_family; @@ -412,7 +448,7 @@ static int dns_cache_put_negative( int rcode, bool authenticated, usec_t timestamp, - uint32_t soa_ttl, + DnsResourceRecord *soa, int owner_family, const union in_addr_union *owner_address) { @@ -422,6 +458,7 @@ static int dns_cache_put_negative( assert(c); assert(key); + assert(soa); assert(owner_address); /* Never cache pseudo RR keys. DNS_TYPE_ANY is particularly @@ -432,7 +469,7 @@ static int dns_cache_put_negative( if (dns_type_is_pseudo(key->type)) return 0; - if (soa_ttl <= 0) { + if (soa->soa.minimum <= 0 || soa->ttl <= 0) { if (log_get_max_level() >= LOG_DEBUG) { r = dns_resource_key_to_string(key, &key_str); if (r < 0) @@ -458,7 +495,7 @@ static int dns_cache_put_negative( return -ENOMEM; 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->until = calculate_until(soa, timestamp, true); i->authenticated = authenticated; i->owner_family = owner_family; i->owner_address = *owner_address; @@ -632,7 +669,7 @@ int dns_cache_put( rcode, authenticated, timestamp, - MIN(soa->soa.minimum, soa->ttl), + soa, owner_family, owner_address); if (r < 0) goto fail; diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index fae4762eb6..a3aa90e98d 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -40,10 +40,9 @@ * - multi-label zone compatibility * - cname/dname compatibility * - per-interface DNSSEC setting - * - fix TTL for cache entries to match RRSIG TTL + * - nxdomain on qname * - retry on failed validation? - * - DSA support - * - EC support? + * - DSA support? * * */ @@ -77,14 +76,6 @@ static void initialize_libgcrypt(void) { gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); } -static bool dnssec_algorithm_supported(int algorithm) { - return IN_SET(algorithm, - DNSSEC_ALGORITHM_RSASHA1, - DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1, - DNSSEC_ALGORITHM_RSASHA256, - DNSSEC_ALGORITHM_RSASHA512); -} - uint16_t dnssec_keytag(DnsResourceRecord *dnskey) { const uint8_t *p; uint32_t sum; @@ -136,7 +127,7 @@ static int rr_compare(const void *a, const void *b) { return 0; } -static int dnssec_rsa_verify( +static int dnssec_rsa_verify_raw( const char *hash_algorithm, const void *signature, size_t signature_size, const void *data, size_t data_size, @@ -226,6 +217,196 @@ finish: return r; } +static int dnssec_rsa_verify( + const char *hash_algorithm, + const void *hash, size_t hash_size, + DnsResourceRecord *rrsig, + DnsResourceRecord *dnskey) { + + size_t exponent_size, modulus_size; + void *exponent, *modulus; + + assert(hash_algorithm); + assert(hash); + assert(hash_size > 0); + assert(rrsig); + assert(dnskey); + + if (*(uint8_t*) dnskey->dnskey.key == 0) { + /* exponent is > 255 bytes long */ + + exponent = (uint8_t*) dnskey->dnskey.key + 3; + exponent_size = + ((size_t) (((uint8_t*) dnskey->dnskey.key)[0]) << 8) | + ((size_t) ((uint8_t*) dnskey->dnskey.key)[1]); + + if (exponent_size < 256) + return -EINVAL; + + if (3 + exponent_size >= dnskey->dnskey.key_size) + return -EINVAL; + + modulus = (uint8_t*) dnskey->dnskey.key + 3 + exponent_size; + modulus_size = dnskey->dnskey.key_size - 3 - exponent_size; + + } else { + /* exponent is <= 255 bytes long */ + + exponent = (uint8_t*) dnskey->dnskey.key + 1; + exponent_size = (size_t) ((uint8_t*) dnskey->dnskey.key)[0]; + + if (exponent_size <= 0) + return -EINVAL; + + if (1 + exponent_size >= dnskey->dnskey.key_size) + return -EINVAL; + + modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size; + modulus_size = dnskey->dnskey.key_size - 1 - exponent_size; + } + + return dnssec_rsa_verify_raw( + hash_algorithm, + rrsig->rrsig.signature, rrsig->rrsig.signature_size, + hash, hash_size, + exponent, exponent_size, + modulus, modulus_size); +} + +static int dnssec_ecdsa_verify_raw( + const char *hash_algorithm, + const char *curve, + const void *signature_r, size_t signature_r_size, + const void *signature_s, size_t signature_s_size, + const void *data, size_t data_size, + const void *key, size_t key_size) { + + gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL; + gcry_mpi_t q = NULL, r = NULL, s = NULL; + gcry_error_t ge; + int k; + + assert(hash_algorithm); + + ge = gcry_mpi_scan(&r, GCRYMPI_FMT_USG, signature_r, signature_r_size, NULL); + if (ge != 0) { + k = -EIO; + goto finish; + } + + ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature_s, signature_s_size, NULL); + if (ge != 0) { + k = -EIO; + goto finish; + } + + ge = gcry_mpi_scan(&q, GCRYMPI_FMT_USG, key, key_size, NULL); + if (ge != 0) { + k = -EIO; + goto finish; + } + + ge = gcry_sexp_build(&signature_sexp, + NULL, + "(sig-val (ecdsa (r %m) (s %m)))", + r, + s); + if (ge != 0) { + k = -EIO; + goto finish; + } + + ge = gcry_sexp_build(&data_sexp, + NULL, + "(data (flags rfc6979) (hash %s %b))", + hash_algorithm, + (int) data_size, + data); + if (ge != 0) { + k = -EIO; + goto finish; + } + + ge = gcry_sexp_build(&public_key_sexp, + NULL, + "(public-key (ecc (curve %s) (q %m)))", + curve, + q); + if (ge != 0) { + k = -EIO; + goto finish; + } + + ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp); + if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE) + k = 0; + else if (ge != 0) { + log_debug("ECDSA signature check failed: %s", gpg_strerror(ge)); + k = -EIO; + } else + k = 1; +finish: + if (r) + gcry_mpi_release(r); + if (s) + gcry_mpi_release(s); + if (q) + gcry_mpi_release(q); + + if (public_key_sexp) + gcry_sexp_release(public_key_sexp); + if (signature_sexp) + gcry_sexp_release(signature_sexp); + if (data_sexp) + gcry_sexp_release(data_sexp); + + return k; +} + +static int dnssec_ecdsa_verify( + const char *hash_algorithm, + int algorithm, + const void *hash, size_t hash_size, + DnsResourceRecord *rrsig, + DnsResourceRecord *dnskey) { + + const char *curve; + size_t key_size; + uint8_t *q; + + assert(hash); + assert(hash_size); + assert(rrsig); + assert(dnskey); + + if (algorithm == DNSSEC_ALGORITHM_ECDSAP256SHA256) { + key_size = 32; + curve = "NIST P-256"; + } else if (algorithm == DNSSEC_ALGORITHM_ECDSAP384SHA384) { + key_size = 48; + curve = "NIST P-384"; + } else + return -EOPNOTSUPP; + + if (dnskey->dnskey.key_size != key_size * 2) + return -EINVAL; + + if (rrsig->rrsig.signature_size != key_size * 2) + return -EINVAL; + + q = alloca(key_size*2 + 1); + q[0] = 0x04; /* Prepend 0x04 to indicate an uncompressed key */ + memcpy(q+1, dnskey->dnskey.key, key_size*2); + + return dnssec_ecdsa_verify_raw( + hash_algorithm, + curve, + rrsig->rrsig.signature, key_size, + (uint8_t*) rrsig->rrsig.signature + key_size, key_size, + hash, hash_size, + q, key_size*2+1); +} + static void md_add_uint8(gcry_md_hd_t md, uint8_t v) { gcry_md_write(md, &v, sizeof(v)); } @@ -275,6 +456,31 @@ static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) { return realtime < inception || realtime > expiration; } +static int algorithm_to_gcrypt_md(uint8_t algorithm) { + + /* Translates a DNSSEC signature algorithm into a gcrypt digest identifier */ + + switch (algorithm) { + + case DNSSEC_ALGORITHM_RSASHA1: + case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1: + return GCRY_MD_SHA1; + + case DNSSEC_ALGORITHM_RSASHA256: + case DNSSEC_ALGORITHM_ECDSAP256SHA256: + return GCRY_MD_SHA256; + + case DNSSEC_ALGORITHM_ECDSAP384SHA384: + return GCRY_MD_SHA384; + + case DNSSEC_ALGORITHM_RSASHA512: + return GCRY_MD_SHA512; + + default: + return -EOPNOTSUPP; + } +} + int dnssec_verify_rrset( DnsAnswer *a, DnsResourceKey *key, @@ -284,12 +490,12 @@ int dnssec_verify_rrset( DnssecResult *result) { uint8_t wire_format_name[DNS_WIRE_FOMAT_HOSTNAME_MAX]; - size_t exponent_size, modulus_size, hash_size; - void *exponent, *modulus, *hash; + size_t hash_size; + void *hash; DnsResourceRecord **list, *rr; gcry_md_hd_t md = NULL; + int r, md_algorithm; size_t k, n = 0; - int r; assert(key); assert(rrsig); @@ -302,10 +508,13 @@ int dnssec_verify_rrset( * using the signature "rrsig" and the key "dnskey". It's * assumed the RRSIG and DNSKEY match. */ - if (!dnssec_algorithm_supported(rrsig->rrsig.algorithm)) { + md_algorithm = algorithm_to_gcrypt_md(rrsig->rrsig.algorithm); + if (md_algorithm == -EOPNOTSUPP) { *result = DNSSEC_UNSUPPORTED_ALGORITHM; return 0; } + if (md_algorithm < 0) + return md_algorithm; if (a->n_rrs > VERIFY_RRS_MAX) return -E2BIG; @@ -342,31 +551,13 @@ int dnssec_verify_rrset( /* Bring the RRs into canonical order */ qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare); - initialize_libgcrypt(); - /* OK, the RRs are now in canonical order. Let's calculate the digest */ - switch (rrsig->rrsig.algorithm) { - - case DNSSEC_ALGORITHM_RSASHA1: - case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1: - gcry_md_open(&md, GCRY_MD_SHA1, 0); - hash_size = 20; - break; - - case DNSSEC_ALGORITHM_RSASHA256: - gcry_md_open(&md, GCRY_MD_SHA256, 0); - hash_size = 32; - break; - - case DNSSEC_ALGORITHM_RSASHA512: - gcry_md_open(&md, GCRY_MD_SHA512, 0); - hash_size = 64; - break; + initialize_libgcrypt(); - default: - assert_not_reached("Unknown digest"); - } + hash_size = gcry_md_get_algo_dlen(md_algorithm); + assert(hash_size > 0); + gcry_md_open(&md, md_algorithm, 0); if (!md) return -EIO; @@ -417,53 +608,30 @@ int dnssec_verify_rrset( goto finish; } - if (*(uint8_t*) dnskey->dnskey.key == 0) { - /* exponent is > 255 bytes long */ - - exponent = (uint8_t*) dnskey->dnskey.key + 3; - exponent_size = - ((size_t) (((uint8_t*) dnskey->dnskey.key)[0]) << 8) | - ((size_t) ((uint8_t*) dnskey->dnskey.key)[1]); - - if (exponent_size < 256) { - r = -EINVAL; - goto finish; - } - - if (3 + exponent_size >= dnskey->dnskey.key_size) { - r = -EINVAL; - goto finish; - } - - modulus = (uint8_t*) dnskey->dnskey.key + 3 + exponent_size; - modulus_size = dnskey->dnskey.key_size - 3 - exponent_size; - - } else { - /* exponent is <= 255 bytes long */ - - exponent = (uint8_t*) dnskey->dnskey.key + 1; - exponent_size = (size_t) ((uint8_t*) dnskey->dnskey.key)[0]; - - if (exponent_size <= 0) { - r = -EINVAL; - goto finish; - } + switch (rrsig->rrsig.algorithm) { - if (1 + exponent_size >= dnskey->dnskey.key_size) { - r = -EINVAL; - goto finish; - } + case DNSSEC_ALGORITHM_RSASHA1: + case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1: + case DNSSEC_ALGORITHM_RSASHA256: + case DNSSEC_ALGORITHM_RSASHA512: + r = dnssec_rsa_verify( + gcry_md_algo_name(md_algorithm), + hash, hash_size, + rrsig, + dnskey); + break; - modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size; - modulus_size = dnskey->dnskey.key_size - 1 - exponent_size; + case DNSSEC_ALGORITHM_ECDSAP256SHA256: + case DNSSEC_ALGORITHM_ECDSAP384SHA384: + r = dnssec_ecdsa_verify( + gcry_md_algo_name(md_algorithm), + rrsig->rrsig.algorithm, + hash, hash_size, + rrsig, + dnskey); + break; } - r = dnssec_rsa_verify( - gcry_md_algo_name(gcry_md_get_algo(md)), - rrsig->rrsig.signature, rrsig->rrsig.signature_size, - hash, hash_size, - exponent, exponent_size, - modulus, modulus_size); if (r < 0) goto finish; @@ -533,6 +701,30 @@ int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key)); } +static int dnssec_fix_rrset_ttl(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord *rrsig, usec_t realtime) { + DnsResourceRecord *rr; + int r; + + assert(key); + assert(rrsig); + + DNS_ANSWER_FOREACH(rr, a) { + r = dns_resource_key_equal(key, rr->key); + if (r < 0) + return r; + if (r == 0) + continue; + + /* Pick the TTL as the minimum of the RR's TTL, the + * RR's original TTL according to the RRSIG and the + * RRSIG's own TTL, see RFC 4035, Section 5.3.3 */ + rr->ttl = MIN3(rr->ttl, rrsig->rrsig.original_ttl, rrsig->ttl); + rr->expiry = rrsig->rrsig.expiration * USEC_PER_SEC; + } + + return 0; +} + int dnssec_verify_rrset_search( DnsAnswer *a, DnsResourceKey *key, @@ -599,7 +791,11 @@ int dnssec_verify_rrset_search( case DNSSEC_VALIDATED: /* Yay, the RR has been validated, - * return immediately. */ + * return immediately, but fix up the expiry */ + r = dnssec_fix_rrset_ttl(a, key, rrsig, realtime); + if (r < 0) + return r; + *result = DNSSEC_VALIDATED; return 0; @@ -730,9 +926,9 @@ int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) { return (int) c; } -static int digest_to_gcrypt(uint8_t algorithm) { +static int digest_to_gcrypt_md(uint8_t algorithm) { - /* Translates a DNSSEC digest algorithm into a gcrypt digest iedntifier */ + /* Translates a DNSSEC digest algorithm into a gcrypt digest identifier */ switch (algorithm) { @@ -742,6 +938,9 @@ static int digest_to_gcrypt(uint8_t algorithm) { case DNSSEC_DIGEST_SHA256: return GCRY_MD_SHA256; + case DNSSEC_DIGEST_SHA384: + return GCRY_MD_SHA384; + default: return -EOPNOTSUPP; } @@ -751,9 +950,8 @@ int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) { char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX]; gcry_md_hd_t md = NULL; size_t hash_size; - int algorithm; + int md_algorithm, r; void *result; - int r; assert(dnskey); assert(ds); @@ -776,11 +974,11 @@ int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) { initialize_libgcrypt(); - algorithm = digest_to_gcrypt(ds->ds.digest_type); - if (algorithm < 0) - return algorithm; + md_algorithm = digest_to_gcrypt_md(ds->ds.digest_type); + if (md_algorithm < 0) + return md_algorithm; - hash_size = gcry_md_get_algo_dlen(algorithm); + hash_size = gcry_md_get_algo_dlen(md_algorithm); assert(hash_size > 0); if (ds->ds.digest_size != hash_size) @@ -790,7 +988,7 @@ int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) { if (r < 0) return r; - gcry_md_open(&md, algorithm, 0); + gcry_md_open(&md, md_algorithm, 0); if (!md) return -EIO; @@ -866,7 +1064,7 @@ int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) { if (nsec3->key->type != DNS_TYPE_NSEC3) return -EINVAL; - algorithm = digest_to_gcrypt(nsec3->nsec3.algorithm); + algorithm = digest_to_gcrypt_md(nsec3->nsec3.algorithm); if (algorithm < 0) return algorithm; diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index 04d442bf03..d479de7125 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -339,6 +339,7 @@ DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) { rr->n_ref = 1; rr->key = dns_resource_key_ref(key); + rr->expiry = USEC_INFINITY; return rr; } diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index f2997883a8..fccc4dba6a 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -53,6 +53,8 @@ enum { DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1, DNSSEC_ALGORITHM_RSASHA256 = 8, /* RFC 5702 */ DNSSEC_ALGORITHM_RSASHA512 = 10, /* RFC 5702 */ + DNSSEC_ALGORITHM_ECDSAP256SHA256 = 13, /* RFC 6605 */ + DNSSEC_ALGORITHM_ECDSAP384SHA384 = 14, /* RFC 6605 */ DNSSEC_ALGORITHM_INDIRECT = 252, DNSSEC_ALGORITHM_PRIVATEDNS, DNSSEC_ALGORITHM_PRIVATEOID, @@ -64,6 +66,7 @@ enum { enum { DNSSEC_DIGEST_SHA1 = 1, DNSSEC_DIGEST_SHA256 = 2, + DNSSEC_DIGEST_SHA384 = 4, _DNSSEC_DIGEST_MAX_DEFINED }; @@ -97,6 +100,7 @@ struct DnsResourceRecord { DnsResourceKey *key; char *to_string; uint32_t ttl; + usec_t expiry; /* RRSIG signature expiry */ bool unparseable:1; bool wire_format_canonical:1; void *wire_format; diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 3ca4a5ab74..fb95554db3 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -48,14 +48,10 @@ static void dns_transaction_close_connection(DnsTransaction *t) { t->dns_udp_fd = safe_close(t->dns_udp_fd); } -static void dns_transaction_stop(DnsTransaction *t) { +static void dns_transaction_stop_timeout(DnsTransaction *t) { assert(t); t->timeout_event_source = sd_event_source_unref(t->timeout_event_source); - t->stream = dns_stream_free(t->stream); - - /* Note that we do not drop the UDP socket here, as we want to - * reuse it to repeat the interaction. */ } DnsTransaction* dns_transaction_free(DnsTransaction *t) { @@ -67,7 +63,7 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) { return NULL; dns_transaction_close_connection(t); - dns_transaction_stop(t); + dns_transaction_stop_timeout(t); dns_packet_unref(t->sent); dns_transaction_reset_answer(t); @@ -264,7 +260,7 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { t->state = state; dns_transaction_close_connection(t); - dns_transaction_stop(t); + dns_transaction_stop_timeout(t); /* Notify all queries that are interested, but make sure the * transaction isn't freed while we are still looking at it */ @@ -725,7 +721,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { if (r > 0) { /* There are DNSSEC transactions pending now. Update the state accordingly. */ t->state = DNS_TRANSACTION_VALIDATING; - dns_transaction_stop(t); + dns_transaction_close_connection(t); + dns_transaction_stop_timeout(t); return; } } @@ -869,7 +866,7 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) { assert(t); - dns_transaction_stop(t); + dns_transaction_stop_timeout(t); if (t->n_attempts >= TRANSACTION_ATTEMPTS_MAX(t->scope->protocol)) { dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED); diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 0273b9e3c9..68404ca9e5 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -98,8 +98,13 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { ((unsigned) (n[1] - '0') * 10) + ((unsigned) (n[2] - '0')); - /* Don't allow CC characters or anything that doesn't fit in 8bit */ - if (k < ' ' || k > 255 || k == 127) + /* Don't allow anything that doesn't + * fit in 8bit. Note that we do allow + * control characters, as some servers + * (e.g. cloudflare) are happy to + * generate labels with them + * inside. */ + if (k > 255) return -EINVAL; if (d) @@ -245,7 +250,7 @@ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) { *(q++) = *p; sz -= 1; - } else if ((uint8_t) *p >= (uint8_t) ' ' && *p != 127) { + } else { /* Everything else */ @@ -259,8 +264,7 @@ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) { sz -= 4; - } else - return -EINVAL; + } p++; l--; diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index 1b0cb153f7..6c3c49908f 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -168,7 +168,7 @@ static void test_dns_label_escape_one(const char *what, size_t l, const char *ex static void test_dns_label_escape(void) { test_dns_label_escape_one("", 0, NULL, -EINVAL); test_dns_label_escape_one("hallo", 5, "hallo", 5); - test_dns_label_escape_one("hallo", 6, NULL, -EINVAL); + test_dns_label_escape_one("hallo", 6, "hallo\\000", 9); test_dns_label_escape_one("hallo hallo.foobar,waldi", 24, "hallo\\032hallo\\.foobar\\044waldi", 31); } @@ -190,7 +190,7 @@ static void test_dns_name_normalize(void) { test_dns_name_normalize_one("f", "f", 0); test_dns_name_normalize_one("f.waldi", "f.waldi", 0); test_dns_name_normalize_one("f \\032.waldi", "f\\032\\032.waldi", 0); - test_dns_name_normalize_one("\\000", NULL, -EINVAL); + test_dns_name_normalize_one("\\000", "\\000", 0); test_dns_name_normalize_one("..", NULL, -EINVAL); test_dns_name_normalize_one(".foobar", NULL, -EINVAL); test_dns_name_normalize_one("foobar.", "foobar", 0); @@ -216,7 +216,7 @@ static void test_dns_name_equal(void) { test_dns_name_equal_one("abc.def", "CBA.def", false); test_dns_name_equal_one("", "xxx", false); test_dns_name_equal_one("ab", "a", false); - test_dns_name_equal_one("\\000", "xxxx", -EINVAL); + test_dns_name_equal_one("\\000", "\\000", true); test_dns_name_equal_one(".", "", true); test_dns_name_equal_one(".", ".", true); test_dns_name_equal_one("..", "..", -EINVAL); |