summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/resolve-host/resolve-host.c8
-rw-r--r--src/resolve/resolved-dns-cache.c49
-rw-r--r--src/resolve/resolved-dns-dnssec.c382
-rw-r--r--src/resolve/resolved-dns-rr.c1
-rw-r--r--src/resolve/resolved-dns-rr.h4
-rw-r--r--src/resolve/resolved-dns-transaction.c15
-rw-r--r--src/shared/dns-domain.c14
-rw-r--r--src/test/test-dns-domain.c6
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);