diff options
author | Luca Boccassi <bluca@debian.org> | 2024-01-17 17:20:35 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-17 17:20:35 +0100 |
commit | 0a9735eac2823ab96121852c94c8b3f0efa1bdf7 (patch) | |
tree | 275d9383e5a13fa5f4fc91915f9c2fa15e8751c4 | |
parent | Restart the DHCPv4 client when max REQUEST attempts is reached (diff) | |
parent | test-resolve: add basic test for SVCB/HTTPS RRs (diff) | |
download | systemd-0a9735eac2823ab96121852c94c8b3f0efa1bdf7.tar.xz systemd-0a9735eac2823ab96121852c94c8b3f0efa1bdf7.zip |
Merge pull request #30661 from rpigott/resolved-https-record
resolved: support RFC 9460 SVCB and HTTPS records
-rw-r--r-- | src/basic/escape.c | 27 | ||||
-rw-r--r-- | src/basic/escape.h | 1 | ||||
-rw-r--r-- | src/resolve/resolved-dns-packet.c | 127 | ||||
-rw-r--r-- | src/resolve/resolved-dns-packet.h | 19 | ||||
-rw-r--r-- | src/resolve/resolved-dns-rr.c | 238 | ||||
-rw-r--r-- | src/resolve/resolved-dns-rr.h | 23 | ||||
-rw-r--r-- | src/shared/varlink-io.systemd.Resolve.Monitor.c | 4 | ||||
-rw-r--r-- | src/test/test-escape.c | 18 | ||||
-rw-r--r-- | test/knot-data/zones/test.zone | 3 | ||||
-rwxr-xr-x | test/units/testsuite-75.sh | 6 |
10 files changed, 462 insertions, 4 deletions
diff --git a/src/basic/escape.c b/src/basic/escape.c index 75a1d68e96..d95f35e798 100644 --- a/src/basic/escape.c +++ b/src/basic/escape.c @@ -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); diff --git a/src/basic/escape.h b/src/basic/escape.h index 318da6f220..65caf0dbcf 100644 --- a/src/basic/escape.h +++ b/src/basic/escape.h @@ -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); diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 44e1e4faab..a7d04449b2 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -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", diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 705fc511d6..393b7b2364 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -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 } }) diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index 2bdcc7c1dc..7d824ee806 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -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; +} + +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++; + } - free(elements); + 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( diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index fd15cc343d..961d3c7852 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -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); diff --git a/src/shared/varlink-io.systemd.Resolve.Monitor.c b/src/shared/varlink-io.systemd.Resolve.Monitor.c index 96a58ca768..e46d1975d7 100644 --- a/src/shared/varlink-io.systemd.Resolve.Monitor.c +++ b/src/shared/varlink-io.systemd.Resolve.Monitor.c @@ -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, diff --git a/src/test/test-escape.c b/src/test/test-escape.c index 21786ae72a..364e0f3956 100644 --- a/src/test/test-escape.c +++ b/src/test/test-escape.c @@ -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); diff --git a/test/knot-data/zones/test.zone b/test/knot-data/zones/test.zone index ba5fcebc2d..065ff7e2a0 100644 --- a/test/knot-data/zones/test.zone +++ b/test/knot-data/zones/test.zone @@ -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" diff --git a/test/units/testsuite-75.sh b/test/units/testsuite-75.sh index f1fb5d943a..92f6b8e030 100755 --- a/test/units/testsuite-75.sh +++ b/test/units/testsuite-75.sh @@ -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 |