diff options
Diffstat (limited to '')
-rw-r--r-- | src/resolve/resolved-dns-packet.c | 312 |
1 files changed, 305 insertions, 7 deletions
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 426711b..e626740 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -6,6 +6,7 @@ #include "alloc-util.h" #include "dns-domain.h" +#include "escape.h" #include "memory-util.h" #include "resolved-dns-packet.h" #include "set.h" @@ -569,7 +570,7 @@ int dns_packet_append_name( while (!dns_name_is_root(name)) { const char *z = name; - char label[DNS_LABEL_MAX]; + char label[DNS_LABEL_MAX+1]; size_t n = 0; if (allow_compression) @@ -792,7 +793,7 @@ int dns_packet_append_opt( static const uint8_t rfc6975[] = { - 0, 5, /* OPTION_CODE: DAU */ + 0, DNS_EDNS_OPT_DAU, /* OPTION_CODE */ #if PREFER_OPENSSL || (HAVE_GCRYPT && GCRYPT_VERSION_NUMBER >= 0x010600) 0, 7, /* LIST_LENGTH */ #else @@ -808,13 +809,13 @@ int dns_packet_append_opt( DNSSEC_ALGORITHM_ED25519, #endif - 0, 6, /* OPTION_CODE: DHU */ + 0, DNS_EDNS_OPT_DHU, /* OPTION_CODE */ 0, 3, /* LIST_LENGTH */ DNSSEC_DIGEST_SHA1, DNSSEC_DIGEST_SHA256, DNSSEC_DIGEST_SHA384, - 0, 7, /* OPTION_CODE: N3U */ + 0, DNS_EDNS_OPT_N3U, /* OPTION_CODE */ 0, 1, /* LIST_LENGTH */ NSEC3_ALGORITHM_SHA1, }; @@ -1182,6 +1183,31 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAns r = dns_packet_append_blob(p, rr->tlsa.data, rr->tlsa.data_size, NULL); break; + case DNS_TYPE_SVCB: + case DNS_TYPE_HTTPS: + r = dns_packet_append_uint16(p, rr->svcb.priority, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_name(p, rr->svcb.target_name, false, false, NULL); + if (r < 0) + goto fail; + + LIST_FOREACH(params, i, rr->svcb.params) { + r = dns_packet_append_uint16(p, i->key, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint16(p, i->length, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_blob(p, i->value, i->length, NULL); + if (r < 0) + goto fail; + } + break; + case DNS_TYPE_CAA: r = dns_packet_append_uint8(p, rr->caa.flags, NULL); if (r < 0) @@ -1194,6 +1220,30 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAns r = dns_packet_append_blob(p, rr->caa.value, rr->caa.value_size, NULL); break; + case DNS_TYPE_NAPTR: + r = dns_packet_append_uint16(p, rr->naptr.order, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint16(p, rr->naptr.preference, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_string(p, rr->naptr.flags, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_string(p, rr->naptr.services, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_string(p, rr->naptr.regexp, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_name(p, rr->naptr.replacement, /* allow_compression= */ false, /* canonical_candidate= */ true, NULL); + break; + case DNS_TYPE_OPT: case DNS_TYPE_OPENPGPKEY: case _DNS_TYPE_INVALID: /* unparsable */ @@ -1688,6 +1738,41 @@ static bool loc_size_ok(uint8_t size) { return m <= 9 && e <= 9 && (m > 0 || e == 0); } +static bool dns_svc_param_is_valid(DnsSvcParam *i) { + if (!i) + return false; + + switch (i->key) { + /* RFC 9460, section 7.1.1: alpn-ids must exactly fill SvcParamValue */ + case DNS_SVC_PARAM_KEY_ALPN: { + size_t sz = 0; + if (i->length <= 0) + return false; + while (sz < i->length) + sz += 1 + i->value[sz]; /* N.B. will not overflow */ + return sz == i->length; + } + + /* RFC 9460, section 7.1.1: value must be empty */ + case DNS_SVC_PARAM_KEY_NO_DEFAULT_ALPN: + return i->length == 0; + + /* RFC 9460, section 7.2 */ + case DNS_SVC_PARAM_KEY_PORT: + return i->length == 2; + + /* RFC 9460, section 7.3: addrs must exactly fill SvcParamValue */ + case DNS_SVC_PARAM_KEY_IPV4HINT: + return i->length % (sizeof (struct in_addr)) == 0; + case DNS_SVC_PARAM_KEY_IPV6HINT: + return i->length % (sizeof (struct in6_addr)) == 0; + + /* Otherwise, permit any value */ + default: + return true; + } +} + int dns_packet_read_rr( DnsPacket *p, DnsResourceRecord **ret, @@ -2122,6 +2207,52 @@ int dns_packet_read_rr( break; + case DNS_TYPE_SVCB: + case DNS_TYPE_HTTPS: + r = dns_packet_read_uint16(p, &rr->svcb.priority, NULL); + if (r < 0) + return r; + + r = dns_packet_read_name(p, &rr->svcb.target_name, false /* uncompressed */, NULL); + if (r < 0) + return r; + + DnsSvcParam *last = NULL; + while (p->rindex - offset < rdlength) { + _cleanup_free_ DnsSvcParam *i = NULL; + uint16_t svc_param_key; + uint16_t sz; + + r = dns_packet_read_uint16(p, &svc_param_key, NULL); + if (r < 0) + return r; + /* RFC 9460, section 2.2 says we must consider an RR malformed if SvcParamKeys are + * not in strictly increasing order */ + if (last && last->key >= svc_param_key) + return -EBADMSG; + + r = dns_packet_read_uint16(p, &sz, NULL); + if (r < 0) + return r; + + i = malloc0(offsetof(DnsSvcParam, value) + sz); + if (!i) + return -ENOMEM; + + i->key = svc_param_key; + i->length = sz; + r = dns_packet_read_blob(p, &i->value, sz, NULL); + if (r < 0) + return r; + if (!dns_svc_param_is_valid(i)) + return -EBADMSG; + + LIST_INSERT_AFTER(params, rr->svcb.params, last, i); + last = TAKE_PTR(i); + } + + break; + case DNS_TYPE_CAA: r = dns_packet_read_uint8(p, &rr->caa.flags, NULL); if (r < 0) @@ -2140,6 +2271,30 @@ int dns_packet_read_rr( break; + case DNS_TYPE_NAPTR: + r = dns_packet_read_uint16(p, &rr->naptr.order, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint16(p, &rr->naptr.preference, NULL); + if (r < 0) + return r; + + r = dns_packet_read_string(p, &rr->naptr.flags, NULL); + if (r < 0) + return r; + + r = dns_packet_read_string(p, &rr->naptr.services, NULL); + if (r < 0) + return r; + + r = dns_packet_read_string(p, &rr->naptr.regexp, NULL); + if (r < 0) + return r; + + r = dns_packet_read_name(p, &rr->naptr.replacement, /* allow_compressed= */ false, NULL); + break; + case DNS_TYPE_OPT: /* we only care about the header of OPT for now. */ case DNS_TYPE_OPENPGPKEY: default: @@ -2197,7 +2352,7 @@ static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) { return false; /* RFC 6975 DAU, DHU or N3U fields found. */ - if (IN_SET(option_code, 5, 6, 7)) + if (IN_SET(option_code, DNS_EDNS_OPT_DAU, DNS_EDNS_OPT_DHU, DNS_EDNS_OPT_N3U)) found_dau_dhu_n3u = true; p += option_length + 4U; @@ -2567,7 +2722,7 @@ int dns_packet_patch_ttls(DnsPacket *p, usec_t timestamp) { static void dns_packet_hash_func(const DnsPacket *s, struct siphash *state) { assert(s); - siphash24_compress(&s->size, sizeof(s->size), state); + siphash24_compress_typesafe(s->size, state); siphash24_compress(DNS_PACKET_DATA((DnsPacket*) s), s->size, state); } @@ -2587,6 +2742,85 @@ bool dns_packet_equal(const DnsPacket *a, const DnsPacket *b) { return dns_packet_compare_func(a, b) == 0; } +int dns_packet_ede_rcode(DnsPacket *p, int *ret_ede_rcode, char **ret_ede_msg) { + const uint8_t *d; + size_t l; + int r; + + assert(p); + + if (!p->opt) + return -ENOENT; + + d = p->opt->opt.data; + l = p->opt->opt.data_size; + + while (l > 0) { + uint16_t code, length; + + if (l < 4U) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "EDNS0 variable part has invalid size."); + + code = unaligned_read_be16(d); + length = unaligned_read_be16(d + 2); + + if (l < 4U + length) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Truncated option in EDNS0 variable part."); + + if (code == DNS_EDNS_OPT_EXT_ERROR) { + _cleanup_free_ char *msg = NULL; + + if (length < 2U) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "EDNS0 truncated EDE info code."); + + r = make_cstring((char *) d + 6, length - 2U, MAKE_CSTRING_ALLOW_TRAILING_NUL, &msg); + if (r < 0) + return log_debug_errno(r, "Invalid EDE text in opt."); + + if (ret_ede_msg) { + if (!utf8_is_valid(msg)) { + _cleanup_free_ char *msg_escaped = NULL; + + msg_escaped = cescape(msg); + if (!msg_escaped) + return log_oom_debug(); + + *ret_ede_msg = TAKE_PTR(msg_escaped); + } else + *ret_ede_msg = TAKE_PTR(msg); + } + + if (ret_ede_rcode) + *ret_ede_rcode = unaligned_read_be16(d + 4); + + return 0; + } + + d += 4U + length; + l -= 4U + length; + } + + return -ENOENT; +} + +bool dns_ede_rcode_is_dnssec(int ede_rcode) { + return IN_SET(ede_rcode, + DNS_EDE_RCODE_UNSUPPORTED_DNSKEY_ALG, + DNS_EDE_RCODE_UNSUPPORTED_DS_DIGEST, + DNS_EDE_RCODE_DNSSEC_INDETERMINATE, + DNS_EDE_RCODE_DNSSEC_BOGUS, + DNS_EDE_RCODE_SIG_EXPIRED, + DNS_EDE_RCODE_SIG_NOT_YET_VALID, + DNS_EDE_RCODE_DNSKEY_MISSING, + DNS_EDE_RCODE_RRSIG_MISSING, + DNS_EDE_RCODE_NO_ZONE_KEY_BIT, + DNS_EDE_RCODE_NSEC_MISSING + ); +} + int dns_packet_has_nsid_request(DnsPacket *p) { bool has_nsid = false; const uint8_t *d; @@ -2614,7 +2848,7 @@ int dns_packet_has_nsid_request(DnsPacket *p) { return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Truncated option in EDNS0 variable part."); - if (code == 3) { + if (code == DNS_EDNS_OPT_NSID) { if (has_nsid) return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Duplicate NSID option in EDNS0 variable part."); @@ -2659,6 +2893,7 @@ static const char* const dns_rcode_table[_DNS_RCODE_MAX_DEFINED] = { [DNS_RCODE_NXRRSET] = "NXRRSET", [DNS_RCODE_NOTAUTH] = "NOTAUTH", [DNS_RCODE_NOTZONE] = "NOTZONE", + [DNS_RCODE_DSOTYPENI] = "DSOTYPENI", [DNS_RCODE_BADVERS] = "BADVERS", [DNS_RCODE_BADKEY] = "BADKEY", [DNS_RCODE_BADTIME] = "BADTIME", @@ -2678,6 +2913,69 @@ const char *format_dns_rcode(int i, char buf[static DECIMAL_STR_MAX(int)]) { return snprintf_ok(buf, DECIMAL_STR_MAX(int), "%i", i); } +static const char* const dns_ede_rcode_table[_DNS_EDE_RCODE_MAX_DEFINED] = { + [DNS_EDE_RCODE_OTHER] = "Other", + [DNS_EDE_RCODE_UNSUPPORTED_DNSKEY_ALG] = "Unsupported DNSKEY Algorithm", + [DNS_EDE_RCODE_UNSUPPORTED_DS_DIGEST] = "Unsupported DS Digest Type", + [DNS_EDE_RCODE_STALE_ANSWER] = "Stale Answer", + [DNS_EDE_RCODE_FORGED_ANSWER] = "Forged Answer", + [DNS_EDE_RCODE_DNSSEC_INDETERMINATE] = "DNSSEC Indeterminate", + [DNS_EDE_RCODE_DNSSEC_BOGUS] = "DNSSEC Bogus", + [DNS_EDE_RCODE_SIG_EXPIRED] = "Signature Expired", + [DNS_EDE_RCODE_SIG_NOT_YET_VALID] = "Signature Not Yet Valid", + [DNS_EDE_RCODE_DNSKEY_MISSING] = "DNSKEY Missing", + [DNS_EDE_RCODE_RRSIG_MISSING] = "RRSIG Missing", + [DNS_EDE_RCODE_NO_ZONE_KEY_BIT] = "No Zone Key Bit Set", + [DNS_EDE_RCODE_NSEC_MISSING] = "NSEC Missing", + [DNS_EDE_RCODE_CACHED_ERROR] = "Cached Error", + [DNS_EDE_RCODE_NOT_READY] = "Not Ready", + [DNS_EDE_RCODE_BLOCKED] = "Blocked", + [DNS_EDE_RCODE_CENSORED] = "Censored", + [DNS_EDE_RCODE_FILTERED] = "Filtered", + [DNS_EDE_RCODE_PROHIBITIED] = "Prohibited", + [DNS_EDE_RCODE_STALE_NXDOMAIN_ANSWER] = "Stale NXDOMAIN Answer", + [DNS_EDE_RCODE_NOT_AUTHORITATIVE] = "Not Authoritative", + [DNS_EDE_RCODE_NOT_SUPPORTED] = "Not Supported", + [DNS_EDE_RCODE_UNREACH_AUTHORITY] = "No Reachable Authority", + [DNS_EDE_RCODE_NET_ERROR] = "Network Error", + [DNS_EDE_RCODE_INVALID_DATA] = "Invalid Data", + [DNS_EDE_RCODE_SIG_NEVER] = "Signature Never Valid", + [DNS_EDE_RCODE_TOO_EARLY] = "Too Early", + [DNS_EDE_RCODE_UNSUPPORTED_NSEC3_ITER] = "Unsupported NSEC3 Iterations", + [DNS_EDE_RCODE_TRANSPORT_POLICY] = "Impossible Transport Policy", + [DNS_EDE_RCODE_SYNTHESIZED] = "Synthesized", +}; +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(dns_ede_rcode, int); + +const char *format_dns_ede_rcode(int i, char buf[static DECIMAL_STR_MAX(int)]) { + const char *p = dns_ede_rcode_to_string(i); + if (p) + return p; + + return snprintf_ok(buf, DECIMAL_STR_MAX(int), "%i", i); +} + +static const char* const dns_svc_param_key_table[_DNS_SVC_PARAM_KEY_MAX_DEFINED] = { + [DNS_SVC_PARAM_KEY_MANDATORY] = "mandatory", + [DNS_SVC_PARAM_KEY_ALPN] = "alpn", + [DNS_SVC_PARAM_KEY_NO_DEFAULT_ALPN] = "no-default-alpn", + [DNS_SVC_PARAM_KEY_PORT] = "port", + [DNS_SVC_PARAM_KEY_IPV4HINT] = "ipv4hint", + [DNS_SVC_PARAM_KEY_ECH] = "ech", + [DNS_SVC_PARAM_KEY_IPV6HINT] = "ipv6hint", + [DNS_SVC_PARAM_KEY_DOHPATH] = "dohpath", + [DNS_SVC_PARAM_KEY_OHTTP] = "ohttp", +}; +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(dns_svc_param_key, int); + +const char *format_dns_svc_param_key(uint16_t i, char buf[static DECIMAL_STR_MAX(uint16_t)+3]) { + const char *p = dns_svc_param_key_to_string(i); + if (p) + return p; + + return snprintf_ok(buf, DECIMAL_STR_MAX(uint16_t)+3, "key%i", i); +} + static const char* const dns_protocol_table[_DNS_PROTOCOL_MAX] = { [DNS_PROTOCOL_DNS] = "dns", [DNS_PROTOCOL_MDNS] = "mdns", |