mirror of
https://github.com/systemd/systemd
synced 2024-07-21 10:17:21 +00:00
Merge pull request #30661 from rpigott/resolved-https-record
resolved: support RFC 9460 SVCB and HTTPS records
This commit is contained in:
commit
0a9735eac2
|
@ -471,6 +471,33 @@ char* octescape(const char *s, size_t len) {
|
|||
return buf;
|
||||
}
|
||||
|
||||
char* decescape(const char *s, const char *bad, size_t len) {
|
||||
char *buf, *t;
|
||||
|
||||
/* Escapes all chars in bad, in addition to \ and " chars, in \nnn decimal style escaping. */
|
||||
|
||||
assert(s || len == 0);
|
||||
|
||||
t = buf = new(char, len * 4 + 1);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
uint8_t u = (uint8_t) s[i];
|
||||
|
||||
if (u < ' ' || u >= 127 || IN_SET(u, '\\', '"') || strchr(bad, u)) {
|
||||
*(t++) = '\\';
|
||||
*(t++) = '0' + (u / 100);
|
||||
*(t++) = '0' + ((u / 10) % 10);
|
||||
*(t++) = '0' + (u % 10);
|
||||
} else
|
||||
*(t++) = u;
|
||||
}
|
||||
|
||||
*t = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
|
||||
assert(bad);
|
||||
assert(t);
|
||||
|
|
|
@ -65,6 +65,7 @@ static inline char* xescape(const char *s, const char *bad) {
|
|||
return xescape_full(s, bad, SIZE_MAX, 0);
|
||||
}
|
||||
char* octescape(const char *s, size_t len);
|
||||
char* decescape(const char *s, const char *bad, size_t len);
|
||||
char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags);
|
||||
|
||||
char* shell_escape(const char *s, const char *bad);
|
||||
|
|
|
@ -1183,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)
|
||||
|
@ -1689,6 +1714,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,
|
||||
|
@ -2123,6 +2183,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)
|
||||
|
@ -2801,6 +2907,27 @@ const char *format_dns_ede_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_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",
|
||||
|
|
|
@ -361,6 +361,25 @@ const char *format_dns_ede_rcode(int i, char buf[static DECIMAL_STR_MAX(int)]);
|
|||
const char* dns_protocol_to_string(DnsProtocol p) _const_;
|
||||
DnsProtocol dns_protocol_from_string(const char *s) _pure_;
|
||||
|
||||
/* https://www.iana.org/assignments/dns-svcb/dns-svcb.xhtml#dns-svcparamkeys */
|
||||
enum {
|
||||
DNS_SVC_PARAM_KEY_MANDATORY = 0, /* RFC 9460 section 8 */
|
||||
DNS_SVC_PARAM_KEY_ALPN = 1, /* RFC 9460 section 7.1 */
|
||||
DNS_SVC_PARAM_KEY_NO_DEFAULT_ALPN = 2, /* RFC 9460 Section 7.1 */
|
||||
DNS_SVC_PARAM_KEY_PORT = 3, /* RFC 9460 section 7.2 */
|
||||
DNS_SVC_PARAM_KEY_IPV4HINT = 4, /* RFC 9460 section 7.3 */
|
||||
DNS_SVC_PARAM_KEY_ECH = 5, /* RFC 9460 */
|
||||
DNS_SVC_PARAM_KEY_IPV6HINT = 6, /* RFC 9460 section 7.3 */
|
||||
DNS_SVC_PARAM_KEY_DOHPATH = 7, /* RFC 9461 */
|
||||
DNS_SVC_PARAM_KEY_OHTTP = 8,
|
||||
_DNS_SVC_PARAM_KEY_MAX_DEFINED,
|
||||
DNS_SVC_PARAM_KEY_INVALID = 65535 /* RFC 9460 */
|
||||
};
|
||||
|
||||
const char* dns_svc_param_key_to_string(int i) _const_;
|
||||
const char *format_dns_svc_param_key(uint16_t i, char buf[static DECIMAL_STR_MAX(uint16_t)+3]);
|
||||
#define FORMAT_DNS_SVC_PARAM_KEY(i) format_dns_svc_param_key(i, (char [DECIMAL_STR_MAX(uint16_t)+3]) {})
|
||||
|
||||
#define LLMNR_MULTICAST_IPV4_ADDRESS ((struct in_addr) { .s_addr = htobe32(224U << 24 | 252U) })
|
||||
#define LLMNR_MULTICAST_IPV6_ADDRESS ((struct in6_addr) { .s6_addr = { 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03 } })
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
#include "unaligned.h"
|
||||
|
||||
DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name) {
|
||||
DnsResourceKey *k;
|
||||
|
@ -469,6 +470,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 +683,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) &&
|
||||
|
@ -814,6 +827,107 @@ static char *format_txt(DnsTxtItem *first) {
|
|||
return s;
|
||||
}
|
||||
|
||||
static char *format_svc_param_value(DnsSvcParam *i) {
|
||||
_cleanup_free_ char *value = NULL;
|
||||
|
||||
assert(i);
|
||||
|
||||
switch (i->key) {
|
||||
case DNS_SVC_PARAM_KEY_ALPN: {
|
||||
size_t offset = 0;
|
||||
_cleanup_strv_free_ char **values_strv = NULL;
|
||||
while (offset < i->length) {
|
||||
size_t sz = (uint8_t) i->value[offset++];
|
||||
|
||||
char *alpn = cescape_length((char *)&i->value[offset], sz);
|
||||
if (!alpn)
|
||||
return NULL;
|
||||
|
||||
if (strv_push(&values_strv, alpn) < 0)
|
||||
return NULL;
|
||||
|
||||
offset += sz;
|
||||
}
|
||||
value = strv_join(values_strv, ",");
|
||||
if (!value)
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
}
|
||||
case DNS_SVC_PARAM_KEY_PORT: {
|
||||
uint16_t port = unaligned_read_be16(i->value);
|
||||
if (asprintf(&value, "%" PRIu16, port) < 0)
|
||||
return NULL;
|
||||
return TAKE_PTR(value);
|
||||
}
|
||||
case DNS_SVC_PARAM_KEY_IPV4HINT: {
|
||||
const struct in_addr *addrs = i->value_in_addr;
|
||||
_cleanup_strv_free_ char **values_strv = NULL;
|
||||
for (size_t n = 0; n < i->length / sizeof (struct in_addr); n++) {
|
||||
char *addr;
|
||||
if (in_addr_to_string(AF_INET, (const union in_addr_union*) &addrs[n], &addr) < 0)
|
||||
return NULL;
|
||||
if (strv_push(&values_strv, addr) < 0)
|
||||
return NULL;
|
||||
}
|
||||
return strv_join(values_strv, ",");
|
||||
}
|
||||
case DNS_SVC_PARAM_KEY_IPV6HINT: {
|
||||
const struct in6_addr *addrs = i->value_in6_addr;
|
||||
_cleanup_strv_free_ char **values_strv = NULL;
|
||||
for (size_t n = 0; n < i->length / sizeof (struct in6_addr); n++) {
|
||||
char *addr;
|
||||
if (in_addr_to_string(AF_INET6, (const union in_addr_union*) &addrs[n], &addr) < 0)
|
||||
return NULL;
|
||||
if (strv_push(&values_strv, addr) < 0)
|
||||
return NULL;
|
||||
}
|
||||
return strv_join(values_strv, ",");
|
||||
}
|
||||
default: {
|
||||
value = decescape((char *)&i->value, " ,", i->length);
|
||||
if (!value)
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
char *qvalue;
|
||||
if (asprintf(&qvalue, "\"%s\"", value) < 0)
|
||||
return NULL;
|
||||
return qvalue;
|
||||
}
|
||||
|
||||
static char *format_svc_param(DnsSvcParam *i) {
|
||||
const char *key = FORMAT_DNS_SVC_PARAM_KEY(i->key);
|
||||
_cleanup_free_ char *value = NULL;
|
||||
|
||||
assert(i);
|
||||
|
||||
if (i->length == 0)
|
||||
return strdup(key);
|
||||
|
||||
value = format_svc_param_value(i);
|
||||
if (!value)
|
||||
return NULL;
|
||||
|
||||
return strjoin(key, "=", value);
|
||||
}
|
||||
|
||||
static char *format_svc_params(DnsSvcParam *first) {
|
||||
_cleanup_strv_free_ char **params = NULL;
|
||||
|
||||
LIST_FOREACH(params, i, first) {
|
||||
char *param = format_svc_param(i);
|
||||
if (!param)
|
||||
return NULL;
|
||||
if (strv_push(¶ms, param) < 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return strv_join(params, " ");
|
||||
}
|
||||
|
||||
const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
|
||||
_cleanup_free_ char *s = NULL, *t = NULL;
|
||||
char k[DNS_RESOURCE_KEY_STRING_MAX];
|
||||
|
@ -1124,6 +1238,19 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
|
|||
|
||||
break;
|
||||
|
||||
case DNS_TYPE_SVCB:
|
||||
case DNS_TYPE_HTTPS:
|
||||
t = format_svc_params(rr->svcb.params);
|
||||
if (!t)
|
||||
return NULL;
|
||||
r = asprintf(&s, "%s %d %s %s", k, rr->svcb.priority,
|
||||
isempty(rr->svcb.target_name) ? "." : rr->svcb.target_name,
|
||||
t);
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
|
||||
break;
|
||||
|
||||
case DNS_TYPE_OPENPGPKEY:
|
||||
r = asprintf(&s, "%s", k);
|
||||
if (r < 0)
|
||||
|
@ -1445,6 +1572,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 +1795,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 +1920,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 +1963,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;
|
||||
|
||||
|
@ -1930,10 +2124,33 @@ static int txt_to_json(DnsTxtItem *items, JsonVariant **ret) {
|
|||
r = json_variant_new_array(ret, elements, n);
|
||||
|
||||
finalize:
|
||||
for (size_t i = 0; i < n; i++)
|
||||
json_variant_unref(elements[i]);
|
||||
json_variant_unref_many(elements, n);
|
||||
return r;
|
||||
}
|
||||
|
||||
free(elements);
|
||||
static int svc_params_to_json(DnsSvcParam *params, JsonVariant **ret) {
|
||||
JsonVariant **elements = NULL;
|
||||
size_t n = 0;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
LIST_FOREACH(params, i, params) {
|
||||
if (!GREEDY_REALLOC(elements, n + 1)) {
|
||||
r = -ENOMEM;
|
||||
goto finalize;
|
||||
}
|
||||
|
||||
r = json_variant_new_base64(elements + n, i->value, i->length);
|
||||
if (r < 0)
|
||||
goto finalize;
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
r = json_variant_new_array(ret, elements, n);
|
||||
finalize:
|
||||
json_variant_unref_many(elements, n);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -2112,6 +2329,21 @@ int dns_resource_record_to_json(DnsResourceRecord *rr, JsonVariant **ret) {
|
|||
JSON_BUILD_PAIR("matchingType", JSON_BUILD_UNSIGNED(rr->tlsa.matching_type)),
|
||||
JSON_BUILD_PAIR("data", JSON_BUILD_HEX(rr->tlsa.data, rr->tlsa.data_size))));
|
||||
|
||||
case DNS_TYPE_SVCB:
|
||||
case DNS_TYPE_HTTPS: {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *p = NULL;
|
||||
r = svc_params_to_json(rr->svcb.params, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return json_build(ret,
|
||||
JSON_BUILD_OBJECT(
|
||||
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
|
||||
JSON_BUILD_PAIR("priority", JSON_BUILD_UNSIGNED(rr->svcb.priority)),
|
||||
JSON_BUILD_PAIR("target", JSON_BUILD_STRING(rr->svcb.target_name)),
|
||||
JSON_BUILD_PAIR("params", JSON_BUILD_VARIANT(p))));
|
||||
}
|
||||
|
||||
case DNS_TYPE_CAA:
|
||||
return json_build(ret,
|
||||
JSON_BUILD_OBJECT(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -59,7 +59,9 @@ VARLINK_DEFINE_STRUCT_TYPE(
|
|||
VARLINK_DEFINE_FIELD(matchingType, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(data, VARLINK_STRING, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(tag, VARLINK_STRING, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(value, VARLINK_STRING, VARLINK_NULLABLE));
|
||||
VARLINK_DEFINE_FIELD(value, VARLINK_STRING, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(target, VARLINK_STRING, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(params, VARLINK_STRING, VARLINK_NULLABLE|VARLINK_ARRAY));
|
||||
|
||||
VARLINK_DEFINE_STRUCT_TYPE(
|
||||
ResourceRecordArray,
|
||||
|
|
|
@ -239,4 +239,22 @@ TEST(octescape) {
|
|||
test_octescape_one("\123\213\222", "\123\\213\\222");
|
||||
}
|
||||
|
||||
static void test_decescape_one(const char *s, const char *bad, const char *expected) {
|
||||
_cleanup_free_ char *ret = NULL;
|
||||
|
||||
assert_se(ret = decescape(s, bad, strlen_ptr(s)));
|
||||
log_debug("decescape(\"%s\") → \"%s\" (expected: \"%s\")", strnull(s), ret, expected);
|
||||
assert_se(streq(ret, expected));
|
||||
}
|
||||
|
||||
TEST(decescape) {
|
||||
test_decescape_one(NULL, "bad", "");
|
||||
test_decescape_one("foo", "", "foo");
|
||||
test_decescape_one("foo", "f", "\\102oo");
|
||||
test_decescape_one("foo", "o", "f\\111\\111");
|
||||
test_decescape_one("go\"bb\\ledyg\x03ook\r\n", "", "go\\034bb\\092ledyg\\003ook\\013\\010");
|
||||
test_decescape_one("\\xff\xff" "f", "f", "\\092x\\102\\102\\255\\102");
|
||||
test_decescape_one("all", "all", "\\097\\108\\108");
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
|
|
|
@ -19,3 +19,6 @@ ns1.unsigned AAAA fd00:dead:beef:cafe::1
|
|||
onlinesign NS ns1.unsigned
|
||||
signed NS ns1.unsigned
|
||||
unsigned NS ns1.unsigned
|
||||
|
||||
svcb SVCB 1 . alpn=dot ipv4hint=10.0.0.1 ipv6hint=fd00:dead:beef:cafe::1
|
||||
https HTTPS 1 . alpn="h2,h3"
|
||||
|
|
|
@ -371,6 +371,12 @@ run dig +noall +authority +comments SRV .
|
|||
grep -qF "status: NOERROR" "$RUN_OUT"
|
||||
grep -qE "IN\s+SOA\s+ns1\.unsigned\.test\." "$RUN_OUT"
|
||||
|
||||
run resolvectl query -t SVCB svcb.test
|
||||
grep -qF 'alpn="dot"' "$RUN_OUT"
|
||||
grep -qF "ipv4hint=10.0.0.1" "$RUN_OUT"
|
||||
|
||||
run resolvectl query -t HTTPS https.test
|
||||
grep -qF 'alpn="h2,h3"' "$RUN_OUT"
|
||||
|
||||
: "--- ZONE: unsigned.test. ---"
|
||||
run dig @ns1.unsigned.test +short unsigned.test A unsigned.test AAAA
|
||||
|
|
Loading…
Reference in a new issue