resolved: enable RFC9460 SVCB and HTTPS records
This commit is contained in:
@@ -1166,6 +1166,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)
|
||||
@@ -1672,6 +1697,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,
|
||||
@@ -2106,6 +2166,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)
|
||||
|
||||
@@ -469,6 +469,12 @@ static DnsResourceRecord* dns_resource_record_free(DnsResourceRecord *rr) {
|
||||
free(rr->tlsa.data);
|
||||
break;
|
||||
|
||||
case DNS_TYPE_SVCB:
|
||||
case DNS_TYPE_HTTPS:
|
||||
free(rr->svcb.target_name);
|
||||
dns_svc_param_free_all(rr->svcb.params);
|
||||
break;
|
||||
|
||||
case DNS_TYPE_CAA:
|
||||
free(rr->caa.tag);
|
||||
free(rr->caa.value);
|
||||
@@ -676,6 +682,12 @@ int dns_resource_record_payload_equal(const DnsResourceRecord *a, const DnsResou
|
||||
a->tlsa.matching_type == b->tlsa.matching_type &&
|
||||
FIELD_EQUAL(a->tlsa, b->tlsa, data);
|
||||
|
||||
case DNS_TYPE_SVCB:
|
||||
case DNS_TYPE_HTTPS:
|
||||
return a->svcb.priority == b->svcb.priority &&
|
||||
dns_name_equal(a->svcb.target_name, b->svcb.target_name) &&
|
||||
dns_svc_params_equal(a->svcb.params, b->svcb.params);
|
||||
|
||||
case DNS_TYPE_CAA:
|
||||
return a->caa.flags == b->caa.flags &&
|
||||
streq(a->caa.tag, b->caa.tag) &&
|
||||
@@ -1445,6 +1457,16 @@ void dns_resource_record_hash_func(const DnsResourceRecord *rr, struct siphash *
|
||||
siphash24_compress_safe(rr->tlsa.data, rr->tlsa.data_size, state);
|
||||
break;
|
||||
|
||||
case DNS_TYPE_SVCB:
|
||||
case DNS_TYPE_HTTPS:
|
||||
dns_name_hash_func(rr->svcb.target_name, state);
|
||||
siphash24_compress_typesafe(rr->svcb.priority, state);
|
||||
LIST_FOREACH(params, j, rr->svcb.params) {
|
||||
siphash24_compress_typesafe(j->key, state);
|
||||
siphash24_compress_safe(j->value, j->length, state);
|
||||
}
|
||||
break;
|
||||
|
||||
case DNS_TYPE_CAA:
|
||||
siphash24_compress_typesafe(rr->caa.flags, state);
|
||||
string_hash_func(rr->caa.tag, state);
|
||||
@@ -1658,6 +1680,17 @@ DnsResourceRecord *dns_resource_record_copy(DnsResourceRecord *rr) {
|
||||
copy->caa.value_size = rr->caa.value_size;
|
||||
break;
|
||||
|
||||
case DNS_TYPE_SVCB:
|
||||
case DNS_TYPE_HTTPS:
|
||||
copy->svcb.priority = rr->svcb.priority;
|
||||
copy->svcb.target_name = strdup(rr->svcb.target_name);
|
||||
if (!copy->svcb.target_name)
|
||||
return NULL;
|
||||
copy->svcb.params = dns_svc_params_copy(rr->svcb.params);
|
||||
if (rr->svcb.params && !copy->svcb.params)
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
case DNS_TYPE_OPT:
|
||||
default:
|
||||
copy->generic.data = memdup(rr->generic.data, rr->generic.data_size);
|
||||
@@ -1772,6 +1805,13 @@ DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *first) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DnsSvcParam *dns_svc_param_free_all(DnsSvcParam *first) {
|
||||
LIST_FOREACH(params, i, first)
|
||||
free(i);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b) {
|
||||
DnsTxtItem *bb = b;
|
||||
|
||||
@@ -1808,6 +1848,45 @@ DnsTxtItem *dns_txt_item_copy(DnsTxtItem *first) {
|
||||
return copy;
|
||||
}
|
||||
|
||||
bool dns_svc_params_equal(DnsSvcParam *a, DnsSvcParam *b) {
|
||||
DnsSvcParam *bb = b;
|
||||
|
||||
if (a == b)
|
||||
return true;
|
||||
|
||||
LIST_FOREACH(params, aa, a) {
|
||||
if (!bb)
|
||||
return false;
|
||||
|
||||
if (aa->key != bb->key)
|
||||
return false;
|
||||
|
||||
if (memcmp_nn(aa->value, aa->length, bb->value, bb->length) != 0)
|
||||
return false;
|
||||
|
||||
bb = bb->params_next;
|
||||
}
|
||||
|
||||
return !bb;
|
||||
}
|
||||
|
||||
DnsSvcParam *dns_svc_params_copy(DnsSvcParam *first) {
|
||||
DnsSvcParam *copy = NULL, *end = NULL;
|
||||
|
||||
LIST_FOREACH(params, i, first) {
|
||||
DnsSvcParam *j;
|
||||
|
||||
j = memdup(i, offsetof(DnsSvcParam, value) + i->length);
|
||||
if (!j)
|
||||
return dns_svc_param_free_all(copy);
|
||||
|
||||
LIST_INSERT_AFTER(params, copy, end, j);
|
||||
end = j;
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
int dns_txt_item_new_empty(DnsTxtItem **ret) {
|
||||
DnsTxtItem *i;
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
typedef struct DnsResourceKey DnsResourceKey;
|
||||
typedef struct DnsResourceRecord DnsResourceRecord;
|
||||
typedef struct DnsTxtItem DnsTxtItem;
|
||||
typedef struct DnsSvcParam DnsSvcParam;
|
||||
|
||||
/* DNSKEY RR flags */
|
||||
#define DNSKEY_FLAG_SEP (UINT16_C(1) << 0)
|
||||
@@ -90,6 +91,17 @@ struct DnsTxtItem {
|
||||
uint8_t data[];
|
||||
};
|
||||
|
||||
struct DnsSvcParam {
|
||||
uint16_t key;
|
||||
size_t length;
|
||||
LIST_FIELDS(DnsSvcParam, params);
|
||||
union {
|
||||
DECLARE_FLEX_ARRAY(uint8_t, value);
|
||||
DECLARE_FLEX_ARRAY(struct in_addr, value_in_addr);
|
||||
DECLARE_FLEX_ARRAY(struct in6_addr, value_in6_addr);
|
||||
};
|
||||
};
|
||||
|
||||
struct DnsResourceRecord {
|
||||
unsigned n_ref;
|
||||
uint32_t ttl;
|
||||
@@ -243,6 +255,13 @@ struct DnsResourceRecord {
|
||||
uint8_t matching_type;
|
||||
} tlsa;
|
||||
|
||||
/* https://tools.ietf.org/html/rfc9460 */
|
||||
struct {
|
||||
uint16_t priority;
|
||||
char *target_name;
|
||||
DnsSvcParam *params;
|
||||
} svcb, https;
|
||||
|
||||
/* https://tools.ietf.org/html/rfc6844 */
|
||||
struct {
|
||||
char *tag;
|
||||
@@ -368,6 +387,10 @@ bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b);
|
||||
DnsTxtItem *dns_txt_item_copy(DnsTxtItem *i);
|
||||
int dns_txt_item_new_empty(DnsTxtItem **ret);
|
||||
|
||||
DnsSvcParam *dns_svc_param_free_all(DnsSvcParam *i);
|
||||
bool dns_svc_params_equal(DnsSvcParam *a, DnsSvcParam *b);
|
||||
DnsSvcParam *dns_svc_params_copy(DnsSvcParam *first);
|
||||
|
||||
int dns_resource_record_new_from_raw(DnsResourceRecord **ret, const void *data, size_t size);
|
||||
|
||||
int dns_resource_key_to_json(DnsResourceKey *key, JsonVariant **ret);
|
||||
|
||||
Reference in New Issue
Block a user