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)
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,
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)
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);
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) &&
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);
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);
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;
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;
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)
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;
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;
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);