1
0
Fork 0
bind9/lib/dns/rdata/in_1/svcb_64.c
Daniel Baumann f66ff7eae6
Adding upstream version 1:9.20.9.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-21 13:32:37 +02:00

1304 lines
29 KiB
C

/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
/* draft-ietf-dnsop-svcb-https-02 */
#ifndef RDATA_IN_1_SVCB_64_C
#define RDATA_IN_1_SVCB_64_C
#define RRTYPE_SVCB_ATTRIBUTES (DNS_RDATATYPEATTR_FOLLOWADDITIONAL)
#define SVCB_MAN_KEY 0
#define SVCB_ALPN_KEY 1
#define SVCB_NO_DEFAULT_ALPN_KEY 2
#define SVCB_DOHPATH_KEY 7
#define MAX_CNAMES 16 /* See ns/query.c MAX_RESTARTS */
/*
* Service Binding Parameter Registry
*/
enum encoding {
sbpr_text,
sbpr_port,
sbpr_ipv4s,
sbpr_ipv6s,
sbpr_base64,
sbpr_empty,
sbpr_alpn,
sbpr_keylist,
sbpr_dohpath
};
static const struct {
const char *name; /* Restricted to lowercase LDH by registry. */
unsigned int value;
enum encoding encoding;
bool initial; /* Part of the first defined set of encodings. */
} sbpr[] = {
{ "mandatory", 0, sbpr_keylist, true },
{ "alpn", 1, sbpr_alpn, true },
{ "no-default-alpn", 2, sbpr_empty, true },
{ "port", 3, sbpr_port, true },
{ "ipv4hint", 4, sbpr_ipv4s, true },
{ "ech", 5, sbpr_base64, true },
{ "ipv6hint", 6, sbpr_ipv6s, true },
{ "dohpath", 7, sbpr_dohpath, false },
};
static isc_result_t
alpn_fromtxt(isc_textregion_t *source, isc_buffer_t *target) {
isc_textregion_t source0 = *source;
do {
RETERR(commatxt_fromtext(&source0, true, target));
} while (source0.length != 0);
return ISC_R_SUCCESS;
}
static int
svckeycmp(const void *a1, const void *a2) {
const unsigned char *u1 = a1, *u2 = a2;
if (*u1 != *u2) {
return *u1 - *u2;
}
return *(++u1) - *(++u2);
}
static isc_result_t
svcsortkeylist(isc_buffer_t *target, unsigned int used) {
isc_region_t region;
isc_buffer_usedregion(target, &region);
isc_region_consume(&region, used);
INSIST(region.length > 0U);
qsort(region.base, region.length / 2, 2, svckeycmp);
/* Reject duplicates. */
while (region.length >= 4) {
if (region.base[0] == region.base[2] &&
region.base[1] == region.base[3])
{
return DNS_R_SYNTAX;
}
isc_region_consume(&region, 2);
}
return ISC_R_SUCCESS;
}
static isc_result_t
svcb_validate(uint16_t key, isc_region_t *region) {
size_t i;
for (i = 0; i < ARRAY_SIZE(sbpr); i++) {
if (sbpr[i].value == key) {
switch (sbpr[i].encoding) {
case sbpr_port:
if (region->length != 2) {
return DNS_R_FORMERR;
}
break;
case sbpr_ipv4s:
if ((region->length % 4) != 0 ||
region->length == 0)
{
return DNS_R_FORMERR;
}
break;
case sbpr_ipv6s:
if ((region->length % 16) != 0 ||
region->length == 0)
{
return DNS_R_FORMERR;
}
break;
case sbpr_alpn: {
if (region->length == 0) {
return DNS_R_FORMERR;
}
while (region->length != 0) {
size_t l = *region->base + 1;
if (l == 1U || l > region->length) {
return DNS_R_FORMERR;
}
isc_region_consume(region, l);
}
break;
}
case sbpr_keylist: {
if ((region->length % 2) != 0 ||
region->length == 0)
{
return DNS_R_FORMERR;
}
/* In order? */
while (region->length >= 4) {
if (region->base[0] > region->base[2] ||
(region->base[0] ==
region->base[2] &&
region->base[1] >=
region->base[3]))
{
return DNS_R_FORMERR;
}
isc_region_consume(region, 2);
}
break;
}
case sbpr_text:
case sbpr_base64:
break;
case sbpr_dohpath:
if (!validate_dohpath(region)) {
return DNS_R_FORMERR;
}
break;
case sbpr_empty:
if (region->length != 0) {
return DNS_R_FORMERR;
}
break;
}
}
}
return ISC_R_SUCCESS;
}
/*
* Parse keyname from region.
*/
static isc_result_t
svc_keyfromregion(isc_textregion_t *region, char sep, uint16_t *value,
isc_buffer_t *target) {
char *e = NULL;
size_t i;
unsigned long ul;
/* Look for known key names. */
for (i = 0; i < ARRAY_SIZE(sbpr); i++) {
size_t len = strlen(sbpr[i].name);
if (strncasecmp(region->base, sbpr[i].name, len) != 0 ||
(region->base[len] != 0 && region->base[len] != sep))
{
continue;
}
isc_textregion_consume(region, len);
ul = sbpr[i].value;
goto finish;
}
/* Handle keyXXXXX form. */
if (strncmp(region->base, "key", 3) != 0) {
return DNS_R_SYNTAX;
}
isc_textregion_consume(region, 3);
/* Disallow [+-]XXXXX which is allowed by strtoul. */
if (region->length == 0 || *region->base == '-' || *region->base == '+')
{
return DNS_R_SYNTAX;
}
/* No zero padding. */
if (region->length > 1 && *region->base == '0' &&
region->base[1] != sep)
{
return DNS_R_SYNTAX;
}
ul = strtoul(region->base, &e, 10);
/* Valid number? */
if (e == region->base || (*e != sep && *e != 0)) {
return DNS_R_SYNTAX;
}
if (ul > 0xffff) {
return ISC_R_RANGE;
}
isc_textregion_consume(region, e - region->base);
finish:
if (sep == ',' && region->length == 1) {
return DNS_R_SYNTAX;
}
/* Consume separator. */
if (region->length != 0) {
isc_textregion_consume(region, 1);
}
RETERR(uint16_tobuffer(ul, target));
SET_IF_NOT_NULL(value, ul);
return ISC_R_SUCCESS;
}
static isc_result_t
svc_fromtext(isc_textregion_t *region, isc_buffer_t *target) {
char *e = NULL;
char abuf[16];
char tbuf[sizeof("aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:255.255.255.255,")];
isc_buffer_t sb;
isc_region_t keyregion;
size_t len;
uint16_t key;
unsigned int i;
unsigned int used;
unsigned long ul;
for (i = 0; i < ARRAY_SIZE(sbpr); i++) {
len = strlen(sbpr[i].name);
if (strncmp(region->base, sbpr[i].name, len) != 0 ||
(region->base[len] != 0 && region->base[len] != '='))
{
continue;
}
if (region->base[len] == '=') {
len++;
}
RETERR(uint16_tobuffer(sbpr[i].value, target));
isc_textregion_consume(region, len);
sb = *target;
RETERR(uint16_tobuffer(0, target)); /* length */
switch (sbpr[i].encoding) {
case sbpr_text:
case sbpr_dohpath:
RETERR(multitxt_fromtext(region, target));
break;
case sbpr_alpn:
RETERR(alpn_fromtxt(region, target));
break;
case sbpr_port:
if (!isdigit((unsigned char)*region->base)) {
return DNS_R_SYNTAX;
}
ul = strtoul(region->base, &e, 10);
if (*e != '\0') {
return DNS_R_SYNTAX;
}
if (ul > 0xffff) {
return ISC_R_RANGE;
}
RETERR(uint16_tobuffer(ul, target));
break;
case sbpr_ipv4s:
do {
snprintf(tbuf, sizeof(tbuf), "%*s",
(int)(region->length), region->base);
e = strchr(tbuf, ',');
if (e != NULL) {
*e++ = 0;
isc_textregion_consume(region,
e - tbuf);
}
if (inet_pton(AF_INET, tbuf, abuf) != 1) {
return DNS_R_SYNTAX;
}
mem_tobuffer(target, abuf, 4);
} while (e != NULL);
break;
case sbpr_ipv6s:
do {
snprintf(tbuf, sizeof(tbuf), "%*s",
(int)(region->length), region->base);
e = strchr(tbuf, ',');
if (e != NULL) {
*e++ = 0;
isc_textregion_consume(region,
e - tbuf);
}
if (inet_pton(AF_INET6, tbuf, abuf) != 1) {
return DNS_R_SYNTAX;
}
mem_tobuffer(target, abuf, 16);
} while (e != NULL);
break;
case sbpr_base64:
RETERR(isc_base64_decodestring(region->base, target));
break;
case sbpr_empty:
if (region->length != 0) {
return DNS_R_SYNTAX;
}
break;
case sbpr_keylist:
if (region->length == 0) {
return DNS_R_SYNTAX;
}
used = isc_buffer_usedlength(target);
while (region->length != 0) {
RETERR(svc_keyfromregion(region, ',', NULL,
target));
}
RETERR(svcsortkeylist(target, used));
break;
default:
UNREACHABLE();
}
len = isc_buffer_usedlength(target) -
isc_buffer_usedlength(&sb) - 2;
RETERR(uint16_tobuffer(len, &sb)); /* length */
switch (sbpr[i].encoding) {
case sbpr_dohpath:
/*
* Apply constraints not applied by multitxt_fromtext.
*/
keyregion.base = isc_buffer_used(&sb);
keyregion.length = isc_buffer_usedlength(target) -
isc_buffer_usedlength(&sb);
RETERR(svcb_validate(sbpr[i].value, &keyregion));
break;
default:
break;
}
return ISC_R_SUCCESS;
}
RETERR(svc_keyfromregion(region, '=', &key, target));
if (region->length == 0) {
RETERR(uint16_tobuffer(0, target)); /* length */
/* Sanity check keyXXXXX form. */
keyregion.base = isc_buffer_used(target);
keyregion.length = 0;
return svcb_validate(key, &keyregion);
}
sb = *target;
RETERR(uint16_tobuffer(0, target)); /* dummy length */
RETERR(multitxt_fromtext(region, target));
len = isc_buffer_usedlength(target) - isc_buffer_usedlength(&sb) - 2;
RETERR(uint16_tobuffer(len, &sb)); /* length */
/* Sanity check keyXXXXX form. */
keyregion.base = isc_buffer_used(&sb);
keyregion.length = len;
return svcb_validate(key, &keyregion);
}
static const char *
svcparamkey(unsigned short value, enum encoding *encoding, char *buf,
size_t len) {
size_t i;
int n;
for (i = 0; i < ARRAY_SIZE(sbpr); i++) {
if (sbpr[i].value == value && sbpr[i].initial) {
*encoding = sbpr[i].encoding;
return sbpr[i].name;
}
}
n = snprintf(buf, len, "key%u", value);
INSIST(n > 0 && (unsigned int)n < len);
*encoding = sbpr_text;
return buf;
}
static isc_result_t
svcsortkeys(isc_buffer_t *target, unsigned int used) {
isc_region_t r1, r2, man = { .base = NULL, .length = 0 };
unsigned char buf[1024];
uint16_t mankey = 0;
bool have_alpn = false;
if (isc_buffer_usedlength(target) == used) {
return ISC_R_SUCCESS;
}
/*
* Get the parameters into r1.
*/
isc_buffer_usedregion(target, &r1);
isc_region_consume(&r1, used);
while (1) {
uint16_t key1, len1, key2, len2;
unsigned char *base1, *base2;
r2 = r1;
/*
* Get the first parameter.
*/
base1 = r1.base;
key1 = uint16_fromregion(&r1);
isc_region_consume(&r1, 2);
len1 = uint16_fromregion(&r1);
isc_region_consume(&r1, 2);
isc_region_consume(&r1, len1);
/*
* Was there only one key left?
*/
if (r1.length == 0) {
if (mankey != 0) {
/* Is this the last mandatory key? */
if (key1 != mankey || man.length != 0) {
return DNS_R_INCONSISTENTRR;
}
} else if (key1 == SVCB_MAN_KEY) {
/* Lone mandatory field. */
return DNS_R_DISALLOWED;
} else if (key1 == SVCB_NO_DEFAULT_ALPN_KEY &&
!have_alpn)
{
/* Missing required ALPN field. */
return DNS_R_DISALLOWED;
}
return ISC_R_SUCCESS;
}
/*
* Find the smallest parameter.
*/
while (r1.length != 0) {
base2 = r1.base;
key2 = uint16_fromregion(&r1);
isc_region_consume(&r1, 2);
len2 = uint16_fromregion(&r1);
isc_region_consume(&r1, 2);
isc_region_consume(&r1, len2);
if (key2 == key1) {
return DNS_R_DUPLICATE;
}
if (key2 < key1) {
base1 = base2;
key1 = key2;
len1 = len2;
}
}
/*
* Do we need to move the smallest parameter to the start?
*/
if (base1 != r2.base) {
size_t offset = 0;
size_t bytes = len1 + 4;
size_t length = base1 - r2.base;
/*
* Move the smallest parameter to the start.
*/
while (bytes > 0) {
size_t count;
if (bytes > sizeof(buf)) {
count = sizeof(buf);
} else {
count = bytes;
}
memmove(buf, base1, count);
memmove(r2.base + offset + count,
r2.base + offset, length);
memmove(r2.base + offset, buf, count);
base1 += count;
bytes -= count;
offset += count;
}
}
/*
* Check ALPN is present when NO-DEFAULT-ALPN is set.
*/
if (key1 == SVCB_ALPN_KEY) {
have_alpn = true;
} else if (key1 == SVCB_NO_DEFAULT_ALPN_KEY && !have_alpn) {
/* Missing required ALPN field. */
return DNS_R_DISALLOWED;
}
/*
* Check key against mandatory key list.
*/
if (mankey != 0) {
if (key1 > mankey) {
return DNS_R_INCONSISTENTRR;
}
if (key1 == mankey) {
if (man.length >= 2) {
mankey = uint16_fromregion(&man);
isc_region_consume(&man, 2);
} else {
mankey = 0;
}
}
}
/*
* Is this the mandatory key?
*/
if (key1 == SVCB_MAN_KEY) {
man = r2;
man.length = len1 + 4;
isc_region_consume(&man, 4);
if (man.length >= 2) {
mankey = uint16_fromregion(&man);
isc_region_consume(&man, 2);
if (mankey == SVCB_MAN_KEY) {
return DNS_R_DISALLOWED;
}
} else {
return DNS_R_SYNTAX;
}
}
/*
* Consume the smallest parameter.
*/
isc_region_consume(&r2, len1 + 4);
r1 = r2;
}
}
static isc_result_t
generic_fromtext_in_svcb(ARGS_FROMTEXT) {
isc_token_t token;
dns_name_t name;
isc_buffer_t buffer;
bool alias;
bool ok = true;
unsigned int used;
UNUSED(type);
UNUSED(rdclass);
UNUSED(callbacks);
/*
* SvcPriority.
*/
RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
false));
if (token.value.as_ulong > 0xffffU) {
RETTOK(ISC_R_RANGE);
}
RETERR(uint16_tobuffer(token.value.as_ulong, target));
alias = token.value.as_ulong == 0;
/*
* TargetName.
*/
RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_qstring,
false));
dns_name_init(&name, NULL);
buffer_fromregion(&buffer, &token.value.as_region);
if (origin == NULL) {
origin = dns_rootname;
}
RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
if (!alias && (options & DNS_RDATA_CHECKNAMES) != 0) {
ok = dns_name_ishostname(&name, false);
}
if (!ok && (options & DNS_RDATA_CHECKNAMESFAIL) != 0) {
RETTOK(DNS_R_BADNAME);
}
if (!ok && callbacks != NULL) {
warn_badname(&name, lexer, callbacks);
}
/*
* SvcParams
*/
used = isc_buffer_usedlength(target);
while (1) {
RETERR(isc_lex_getmastertoken(lexer, &token,
isc_tokentype_qvpair, true));
if (token.type == isc_tokentype_eol ||
token.type == isc_tokentype_eof)
{
isc_lex_ungettoken(lexer, &token);
return svcsortkeys(target, used);
}
if (token.type != isc_tokentype_string && /* key only */
token.type != isc_tokentype_qvpair &&
token.type != isc_tokentype_vpair)
{
RETTOK(DNS_R_SYNTAX);
}
RETTOK(svc_fromtext(&token.value.as_textregion, target));
}
}
static isc_result_t
fromtext_in_svcb(ARGS_FROMTEXT) {
REQUIRE(type == dns_rdatatype_svcb);
REQUIRE(rdclass == dns_rdataclass_in);
UNUSED(type);
UNUSED(rdclass);
UNUSED(callbacks);
return generic_fromtext_in_svcb(CALL_FROMTEXT);
}
static isc_result_t
generic_totext_in_svcb(ARGS_TOTEXT) {
isc_region_t region;
dns_name_t name;
dns_name_t prefix;
unsigned int opts;
char buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")];
unsigned short num;
int n;
REQUIRE(rdata->length != 0);
dns_name_init(&name, NULL);
dns_name_init(&prefix, NULL);
dns_rdata_toregion(rdata, &region);
/*
* SvcPriority.
*/
num = uint16_fromregion(&region);
isc_region_consume(&region, 2);
n = snprintf(buf, sizeof(buf), "%u ", num);
INSIST(n > 0 && (unsigned int)n < sizeof(buf));
RETERR(str_totext(buf, target));
/*
* TargetName.
*/
dns_name_fromregion(&name, &region);
isc_region_consume(&region, name_length(&name));
opts = name_prefix(&name, tctx->origin, &prefix) ? DNS_NAME_OMITFINALDOT
: 0;
RETERR(dns_name_totext(&prefix, opts, target));
while (region.length > 0) {
isc_region_t r;
enum encoding encoding;
RETERR(str_totext(" ", target));
INSIST(region.length >= 2);
num = uint16_fromregion(&region);
isc_region_consume(&region, 2);
RETERR(str_totext(svcparamkey(num, &encoding, buf, sizeof(buf)),
target));
INSIST(region.length >= 2);
num = uint16_fromregion(&region);
isc_region_consume(&region, 2);
INSIST(region.length >= num);
r = region;
r.length = num;
isc_region_consume(&region, num);
if (num == 0) {
continue;
}
if (encoding != sbpr_empty) {
RETERR(str_totext("=", target));
}
switch (encoding) {
case sbpr_text:
RETERR(multitxt_totext(&r, target));
break;
case sbpr_port:
num = uint16_fromregion(&r);
isc_region_consume(&r, 2);
n = snprintf(buf, sizeof(buf), "%u", num);
INSIST(n > 0 && (unsigned int)n < sizeof(buf));
RETERR(str_totext(buf, target));
INSIST(r.length == 0U);
break;
case sbpr_ipv4s:
while (r.length > 0U) {
INSIST(r.length >= 4U);
inet_ntop(AF_INET, r.base, buf, sizeof(buf));
RETERR(str_totext(buf, target));
isc_region_consume(&r, 4);
if (r.length != 0U) {
RETERR(str_totext(",", target));
}
}
break;
case sbpr_ipv6s:
while (r.length > 0U) {
INSIST(r.length >= 16U);
inet_ntop(AF_INET6, r.base, buf, sizeof(buf));
RETERR(str_totext(buf, target));
isc_region_consume(&r, 16);
if (r.length != 0U) {
RETERR(str_totext(",", target));
}
}
break;
case sbpr_base64:
RETERR(isc_base64_totext(&r, 0, "", target));
break;
case sbpr_alpn:
INSIST(r.length != 0U);
RETERR(str_totext("\"", target));
while (r.length != 0) {
commatxt_totext(&r, false, true, target);
if (r.length != 0) {
RETERR(str_totext(",", target));
}
}
RETERR(str_totext("\"", target));
break;
case sbpr_empty:
INSIST(r.length == 0U);
break;
case sbpr_keylist:
while (r.length > 0) {
num = uint16_fromregion(&r);
isc_region_consume(&r, 2);
RETERR(str_totext(svcparamkey(num, &encoding,
buf, sizeof(buf)),
target));
if (r.length != 0) {
RETERR(str_totext(",", target));
}
}
break;
default:
UNREACHABLE();
}
}
return ISC_R_SUCCESS;
}
static isc_result_t
totext_in_svcb(ARGS_TOTEXT) {
REQUIRE(rdata->type == dns_rdatatype_svcb);
REQUIRE(rdata->rdclass == dns_rdataclass_in);
REQUIRE(rdata->length != 0);
return generic_totext_in_svcb(CALL_TOTEXT);
}
static isc_result_t
generic_fromwire_in_svcb(ARGS_FROMWIRE) {
dns_name_t name;
isc_region_t region, man = { .base = NULL, .length = 0 };
bool first = true, have_alpn = false;
uint16_t lastkey = 0, mankey = 0;
UNUSED(type);
UNUSED(rdclass);
dctx = dns_decompress_setpermitted(dctx, false);
dns_name_init(&name, NULL);
/*
* SvcPriority.
*/
isc_buffer_activeregion(source, &region);
if (region.length < 2) {
return ISC_R_UNEXPECTEDEND;
}
RETERR(mem_tobuffer(target, region.base, 2));
isc_buffer_forward(source, 2);
/*
* TargetName.
*/
RETERR(dns_name_fromwire(&name, source, dctx, target));
/*
* SvcParams.
*/
isc_buffer_activeregion(source, &region);
while (region.length > 0U) {
isc_region_t keyregion;
uint16_t key, len;
/*
* SvcParamKey
*/
if (region.length < 2U) {
return ISC_R_UNEXPECTEDEND;
}
RETERR(mem_tobuffer(target, region.base, 2));
key = uint16_fromregion(&region);
isc_region_consume(&region, 2);
/*
* Keys must be unique and in order.
*/
if (!first && key <= lastkey) {
return DNS_R_FORMERR;
}
/*
* Check mandatory keys.
*/
if (mankey != 0) {
/* Missing mandatory key? */
if (key > mankey) {
return DNS_R_FORMERR;
}
if (key == mankey) {
/* Get next mandatory key. */
if (man.length >= 2) {
mankey = uint16_fromregion(&man);
isc_region_consume(&man, 2);
} else {
mankey = 0;
}
}
}
/*
* Check alpn present when no-default-alpn is set.
*/
if (key == SVCB_ALPN_KEY) {
have_alpn = true;
} else if (key == SVCB_NO_DEFAULT_ALPN_KEY && !have_alpn) {
return DNS_R_FORMERR;
}
first = false;
lastkey = key;
/*
* SvcParamValue length.
*/
if (region.length < 2U) {
return ISC_R_UNEXPECTEDEND;
}
RETERR(mem_tobuffer(target, region.base, 2));
len = uint16_fromregion(&region);
isc_region_consume(&region, 2);
/*
* SvcParamValue.
*/
if (region.length < len) {
return ISC_R_UNEXPECTEDEND;
}
/*
* Remember manatory key.
*/
if (key == SVCB_MAN_KEY) {
man = region;
man.length = len;
/* Get first mandatory key */
if (man.length >= 2) {
mankey = uint16_fromregion(&man);
isc_region_consume(&man, 2);
if (mankey == SVCB_MAN_KEY) {
return DNS_R_FORMERR;
}
} else {
return DNS_R_FORMERR;
}
}
keyregion = region;
keyregion.length = len;
RETERR(svcb_validate(key, &keyregion));
RETERR(mem_tobuffer(target, region.base, len));
isc_region_consume(&region, len);
isc_buffer_forward(source, len + 4);
}
/*
* Do we have an outstanding mandatory key?
*/
if (mankey != 0) {
return DNS_R_FORMERR;
}
return ISC_R_SUCCESS;
}
static isc_result_t
fromwire_in_svcb(ARGS_FROMWIRE) {
REQUIRE(type == dns_rdatatype_svcb);
REQUIRE(rdclass == dns_rdataclass_in);
return generic_fromwire_in_svcb(CALL_FROMWIRE);
}
static isc_result_t
generic_towire_in_svcb(ARGS_TOWIRE) {
dns_name_t name;
dns_offsets_t offsets;
isc_region_t region;
REQUIRE(rdata->length != 0);
dns_compress_setpermitted(cctx, false);
/*
* SvcPriority.
*/
dns_rdata_toregion(rdata, &region);
RETERR(mem_tobuffer(target, region.base, 2));
isc_region_consume(&region, 2);
/*
* TargetName.
*/
dns_name_init(&name, offsets);
dns_name_fromregion(&name, &region);
RETERR(dns_name_towire(&name, cctx, target, NULL));
isc_region_consume(&region, name_length(&name));
/*
* SvcParams.
*/
return mem_tobuffer(target, region.base, region.length);
}
static isc_result_t
towire_in_svcb(ARGS_TOWIRE) {
REQUIRE(rdata->type == dns_rdatatype_svcb);
REQUIRE(rdata->length != 0);
return generic_towire_in_svcb(CALL_TOWIRE);
}
static int
compare_in_svcb(ARGS_COMPARE) {
isc_region_t region1;
isc_region_t region2;
REQUIRE(rdata1->type == rdata2->type);
REQUIRE(rdata1->rdclass == rdata2->rdclass);
REQUIRE(rdata1->type == dns_rdatatype_svcb);
REQUIRE(rdata1->rdclass == dns_rdataclass_in);
REQUIRE(rdata1->length != 0);
REQUIRE(rdata2->length != 0);
dns_rdata_toregion(rdata1, &region1);
dns_rdata_toregion(rdata2, &region2);
return isc_region_compare(&region1, &region2);
}
static isc_result_t
generic_fromstruct_in_svcb(ARGS_FROMSTRUCT) {
dns_rdata_in_svcb_t *svcb = source;
isc_region_t region;
REQUIRE(svcb != NULL);
REQUIRE(svcb->common.rdtype == type);
REQUIRE(svcb->common.rdclass == rdclass);
UNUSED(type);
UNUSED(rdclass);
RETERR(uint16_tobuffer(svcb->priority, target));
dns_name_toregion(&svcb->svcdomain, &region);
RETERR(isc_buffer_copyregion(target, &region));
return mem_tobuffer(target, svcb->svc, svcb->svclen);
}
static isc_result_t
fromstruct_in_svcb(ARGS_FROMSTRUCT) {
dns_rdata_in_svcb_t *svcb = source;
REQUIRE(type == dns_rdatatype_svcb);
REQUIRE(rdclass == dns_rdataclass_in);
REQUIRE(svcb != NULL);
REQUIRE(svcb->common.rdtype == type);
REQUIRE(svcb->common.rdclass == rdclass);
return generic_fromstruct_in_svcb(CALL_FROMSTRUCT);
}
static isc_result_t
generic_tostruct_in_svcb(ARGS_TOSTRUCT) {
isc_region_t region;
dns_rdata_in_svcb_t *svcb = target;
dns_name_t name;
REQUIRE(svcb != NULL);
REQUIRE(rdata->length != 0);
svcb->common.rdclass = rdata->rdclass;
svcb->common.rdtype = rdata->type;
ISC_LINK_INIT(&svcb->common, link);
dns_rdata_toregion(rdata, &region);
svcb->priority = uint16_fromregion(&region);
isc_region_consume(&region, 2);
dns_name_init(&svcb->svcdomain, NULL);
dns_name_init(&name, NULL);
dns_name_fromregion(&name, &region);
isc_region_consume(&region, name_length(&name));
name_duporclone(&name, mctx, &svcb->svcdomain);
svcb->svclen = region.length;
svcb->svc = mem_maybedup(mctx, region.base, region.length);
svcb->offset = 0;
svcb->mctx = mctx;
return ISC_R_SUCCESS;
}
static isc_result_t
tostruct_in_svcb(ARGS_TOSTRUCT) {
dns_rdata_in_svcb_t *svcb = target;
REQUIRE(rdata->rdclass == dns_rdataclass_in);
REQUIRE(rdata->type == dns_rdatatype_svcb);
REQUIRE(svcb != NULL);
REQUIRE(rdata->length != 0);
return generic_tostruct_in_svcb(CALL_TOSTRUCT);
}
static void
generic_freestruct_in_svcb(ARGS_FREESTRUCT) {
dns_rdata_in_svcb_t *svcb = source;
REQUIRE(svcb != NULL);
if (svcb->mctx == NULL) {
return;
}
dns_name_free(&svcb->svcdomain, svcb->mctx);
isc_mem_free(svcb->mctx, svcb->svc);
svcb->mctx = NULL;
}
static void
freestruct_in_svcb(ARGS_FREESTRUCT) {
dns_rdata_in_svcb_t *svcb = source;
REQUIRE(svcb != NULL);
REQUIRE(svcb->common.rdclass == dns_rdataclass_in);
REQUIRE(svcb->common.rdtype == dns_rdatatype_svcb);
generic_freestruct_in_svcb(CALL_FREESTRUCT);
}
static isc_result_t
generic_additionaldata_in_svcb(ARGS_ADDLDATA) {
bool alias, done = false;
dns_fixedname_t fixed;
dns_name_t name, *fname = NULL;
dns_offsets_t offsets;
dns_rdataset_t rdataset;
isc_region_t region;
unsigned int cnames = 0;
dns_name_init(&name, offsets);
dns_rdata_toregion(rdata, &region);
alias = uint16_fromregion(&region) == 0;
isc_region_consume(&region, 2);
dns_name_fromregion(&name, &region);
if (dns_name_equal(&name, dns_rootname)) {
/*
* "." only means owner name in service form.
*/
if (alias || dns_name_equal(owner, dns_rootname) ||
!dns_name_ishostname(owner, false))
{
return ISC_R_SUCCESS;
}
/* Only lookup address records */
return (add)(arg, owner, dns_rdatatype_a,
NULL DNS__DB_FILELINE);
}
/*
* Follow CNAME chains when processing HTTPS and SVCB records.
*/
dns_rdataset_init(&rdataset);
fname = dns_fixedname_initname(&fixed);
do {
RETERR((add)(arg, &name, dns_rdatatype_cname,
&rdataset DNS__DB_FILELINE));
if (dns_rdataset_isassociated(&rdataset)) {
isc_result_t result;
result = dns_rdataset_first(&rdataset);
if (result == ISC_R_SUCCESS) {
dns_rdata_t current = DNS_RDATA_INIT;
dns_rdata_cname_t cname;
dns_rdataset_current(&rdataset, &current);
result = dns_rdata_tostruct(&current, &cname,
NULL);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
dns_name_copy(&cname.cname, fname);
dns_name_clone(fname, &name);
} else {
done = true;
}
dns_rdataset_disassociate(&rdataset);
} else {
done = true;
}
/*
* Stop following a potentially infinite CNAME chain.
*/
if (!done && cnames++ > MAX_CNAMES) {
return ISC_R_SUCCESS;
}
} while (!done);
/*
* Look up HTTPS/SVCB records when processing the alias form.
*/
if (alias) {
RETERR((add)(arg, &name, rdata->type,
&rdataset DNS__DB_FILELINE));
/*
* Don't return A or AAAA if this is not the last element
* in the HTTP / SVCB chain.
*/
if (dns_rdataset_isassociated(&rdataset)) {
dns_rdataset_disassociate(&rdataset);
return ISC_R_SUCCESS;
}
}
return (add)(arg, &name, dns_rdatatype_a, NULL DNS__DB_FILELINE);
}
static isc_result_t
additionaldata_in_svcb(ARGS_ADDLDATA) {
REQUIRE(rdata->type == dns_rdatatype_svcb);
REQUIRE(rdata->rdclass == dns_rdataclass_in);
return generic_additionaldata_in_svcb(CALL_ADDLDATA);
}
static isc_result_t
digest_in_svcb(ARGS_DIGEST) {
isc_region_t region1;
REQUIRE(rdata->type == dns_rdatatype_svcb);
REQUIRE(rdata->rdclass == dns_rdataclass_in);
dns_rdata_toregion(rdata, &region1);
return (digest)(arg, &region1);
}
static bool
checkowner_in_svcb(ARGS_CHECKOWNER) {
REQUIRE(type == dns_rdatatype_svcb);
REQUIRE(rdclass == dns_rdataclass_in);
UNUSED(name);
UNUSED(type);
UNUSED(rdclass);
UNUSED(wildcard);
return true;
}
static bool
generic_checknames_in_svcb(ARGS_CHECKNAMES) {
isc_region_t region;
dns_name_t name;
bool alias;
UNUSED(owner);
dns_rdata_toregion(rdata, &region);
INSIST(region.length > 1);
alias = uint16_fromregion(&region) == 0;
isc_region_consume(&region, 2);
dns_name_init(&name, NULL);
dns_name_fromregion(&name, &region);
if (!alias && !dns_name_ishostname(&name, false)) {
if (bad != NULL) {
dns_name_clone(&name, bad);
}
return false;
}
return true;
}
static bool
checknames_in_svcb(ARGS_CHECKNAMES) {
REQUIRE(rdata->type == dns_rdatatype_svcb);
REQUIRE(rdata->rdclass == dns_rdataclass_in);
return generic_checknames_in_svcb(CALL_CHECKNAMES);
}
static int
casecompare_in_svcb(ARGS_COMPARE) {
return compare_in_svcb(rdata1, rdata2);
}
static isc_result_t
generic_rdata_in_svcb_first(dns_rdata_in_svcb_t *svcb) {
if (svcb->svclen == 0) {
return ISC_R_NOMORE;
}
svcb->offset = 0;
return ISC_R_SUCCESS;
}
static isc_result_t
generic_rdata_in_svcb_next(dns_rdata_in_svcb_t *svcb) {
isc_region_t region;
size_t len;
if (svcb->offset >= svcb->svclen) {
return ISC_R_NOMORE;
}
region.base = svcb->svc + svcb->offset;
region.length = svcb->svclen - svcb->offset;
INSIST(region.length >= 4);
isc_region_consume(&region, 2);
len = uint16_fromregion(&region);
INSIST(region.length >= len + 2);
svcb->offset += len + 4;
return svcb->offset >= svcb->svclen ? ISC_R_NOMORE : ISC_R_SUCCESS;
}
static void
generic_rdata_in_svcb_current(dns_rdata_in_svcb_t *svcb, isc_region_t *region) {
size_t len;
INSIST(svcb->offset <= svcb->svclen);
region->base = svcb->svc + svcb->offset;
region->length = svcb->svclen - svcb->offset;
INSIST(region->length >= 4);
isc_region_consume(region, 2);
len = uint16_fromregion(region);
INSIST(region->length >= len + 2);
region->base = svcb->svc + svcb->offset;
region->length = len + 4;
}
isc_result_t
dns_rdata_in_svcb_first(dns_rdata_in_svcb_t *svcb) {
REQUIRE(svcb != NULL);
REQUIRE(svcb->common.rdtype == dns_rdatatype_svcb);
REQUIRE(svcb->common.rdclass == dns_rdataclass_in);
return generic_rdata_in_svcb_first(svcb);
}
isc_result_t
dns_rdata_in_svcb_next(dns_rdata_in_svcb_t *svcb) {
REQUIRE(svcb != NULL);
REQUIRE(svcb->common.rdtype == dns_rdatatype_svcb);
REQUIRE(svcb->common.rdclass == dns_rdataclass_in);
return generic_rdata_in_svcb_next(svcb);
}
void
dns_rdata_in_svcb_current(dns_rdata_in_svcb_t *svcb, isc_region_t *region) {
REQUIRE(svcb != NULL);
REQUIRE(svcb->common.rdtype == dns_rdatatype_svcb);
REQUIRE(svcb->common.rdclass == dns_rdataclass_in);
REQUIRE(region != NULL);
generic_rdata_in_svcb_current(svcb, region);
}
#endif /* RDATA_IN_1_SVCB_64_C */