Add SVCB and HTTPS RR (RFC 9460) (#603)

This PR adds parsing and writing support for SVCB and HTTPS RRs as defined in RFC 9460.

This should fix #566

Fix By: Brad House (@bradh352)
pull/604/head
Brad House 1 year ago committed by GitHub
parent df1cbdccf7
commit c433477753
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 48
      src/lib/ares_dns_mapping.c
  2. 107
      src/lib/ares_dns_parse.c
  3. 33
      src/lib/ares_dns_record.c
  4. 51
      src/lib/ares_dns_record.h
  5. 108
      src/lib/ares_dns_write.c
  6. 28
      test/ares-test-internal.cc

@ -90,10 +90,8 @@ ares_bool_t ares_dns_rec_type_isvalid(ares_dns_rec_type_t type,
case ARES_REC_TYPE_NAPTR:
case ARES_REC_TYPE_OPT:
case ARES_REC_TYPE_TLSA:
#if 0
case ARES_REC_TYPE_SVBC:
case ARES_REC_TYPE_SVCB:
case ARES_REC_TYPE_HTTPS:
#endif
case ARES_REC_TYPE_ANY:
case ARES_REC_TYPE_URI:
case ARES_REC_TYPE_CAA:
@ -195,12 +193,10 @@ const char *ares_dns_rec_type_tostr(ares_dns_rec_type_t type)
return "OPT";
case ARES_REC_TYPE_TLSA:
return "TLSA";
#if 0
case ARES_REC_TYPE_SVBC:
return "SVBC";
case ARES_REC_TYPE_SVCB:
return "SVCB";
case ARES_REC_TYPE_HTTPS:
return "HTTPS";
#endif
case ARES_REC_TYPE_ANY:
return "ANY";
case ARES_REC_TYPE_URI:
@ -358,6 +354,24 @@ const char *ares_dns_rr_key_tostr(ares_dns_rr_key_t key)
case ARES_RR_TLSA_DATA:
return "DATA";
case ARES_RR_SVCB_PRIORITY:
return "PRIORITY";
case ARES_RR_SVCB_TARGET:
return "TARGET";
case ARES_RR_SVCB_PARAMS:
return "PARAMS";
case ARES_RR_HTTPS_PRIORITY:
return "PRIORITY";
case ARES_RR_HTTPS_TARGET:
return "TARGET";
case ARES_RR_HTTPS_PARAMS:
return "PARAMS";
case ARES_RR_URI_PRIORITY:
return "PRIORITY";
@ -404,6 +418,8 @@ ares_dns_datatype_t ares_dns_rr_key_datatype(ares_dns_rr_key_t key)
case ARES_RR_HINFO_OS:
case ARES_RR_MX_EXCHANGE:
case ARES_RR_SRV_TARGET:
case ARES_RR_SVCB_TARGET:
case ARES_RR_HTTPS_TARGET:
case ARES_RR_NAPTR_FLAGS:
case ARES_RR_NAPTR_SERVICES:
case ARES_RR_NAPTR_REGEXP:
@ -427,6 +443,8 @@ ares_dns_datatype_t ares_dns_rr_key_datatype(ares_dns_rr_key_t key)
case ARES_RR_NAPTR_PREFERENCE:
case ARES_RR_OPT_UDP_SIZE:
case ARES_RR_OPT_FLAGS:
case ARES_RR_SVCB_PRIORITY:
case ARES_RR_HTTPS_PRIORITY:
case ARES_RR_URI_PRIORITY:
case ARES_RR_URI_WEIGHT:
case ARES_RR_RAW_RR_TYPE:
@ -449,6 +467,8 @@ ares_dns_datatype_t ares_dns_rr_key_datatype(ares_dns_rr_key_t key)
return ARES_DATATYPE_BIN;
case ARES_RR_OPT_OPTIONS:
case ARES_RR_SVCB_PARAMS:
case ARES_RR_HTTPS_PARAMS:
return ARES_DATATYPE_OPT;
}
@ -486,6 +506,12 @@ static const ares_dns_rr_key_t rr_tlsa_keys[] = { ARES_RR_TLSA_CERT_USAGE,
ARES_RR_TLSA_SELECTOR,
ARES_RR_TLSA_MATCH,
ARES_RR_TLSA_DATA };
static const ares_dns_rr_key_t rr_svcb_keys[] = { ARES_RR_SVCB_PRIORITY,
ARES_RR_SVCB_TARGET,
ARES_RR_SVCB_PARAMS };
static const ares_dns_rr_key_t rr_https_keys[] = { ARES_RR_HTTPS_PRIORITY,
ARES_RR_HTTPS_TARGET,
ARES_RR_HTTPS_PARAMS };
static const ares_dns_rr_key_t rr_uri_keys[] = { ARES_RR_URI_PRIORITY,
ARES_RR_URI_WEIGHT,
ARES_RR_URI_TARGET };
@ -544,14 +570,12 @@ const ares_dns_rr_key_t *ares_dns_rr_get_keys(ares_dns_rec_type_t type,
case ARES_REC_TYPE_TLSA:
*cnt = sizeof(rr_tlsa_keys) / sizeof(*rr_tlsa_keys);
return rr_tlsa_keys;
#if 0
case ARES_REC_TYPE_SVBC:
*cnt = sizeof(rr_svbc_keys) / sizeof(*rr_svbc_keys);
return rr_svbc_keys;
case ARES_REC_TYPE_SVCB:
*cnt = sizeof(rr_svcb_keys) / sizeof(*rr_svcb_keys);
return rr_svcb_keys;
case ARES_REC_TYPE_HTTPS:
*cnt = sizeof(rr_https_keys) / sizeof(*rr_https_keys);
return rr_https_keys;
#endif
case ARES_REC_TYPE_ANY:
/* Not real */
break;

@ -492,6 +492,109 @@ static ares_status_t ares_dns_parse_rr_tlsa(ares__buf_t *buf, ares_dns_rr_t *rr,
return ARES_SUCCESS;
}
static ares_status_t ares_dns_parse_rr_svcb(ares__buf_t *buf, ares_dns_rr_t *rr,
size_t rdlength)
{
ares_status_t status;
size_t orig_len = ares__buf_len(buf);
status = ares_dns_parse_and_set_be16(buf, rr, ARES_RR_SVCB_PRIORITY);
if (status != ARES_SUCCESS) {
return status;
}
status = ares_dns_parse_and_set_dns_name(buf, ARES_FALSE, rr,
ARES_RR_SVCB_TARGET);
if (status != ARES_SUCCESS) {
return status;
}
/* Parse params */
while (ares_dns_rr_remaining_len(buf, orig_len, rdlength)) {
unsigned short opt = 0;
unsigned short len = 0;
unsigned char *val = NULL;
/* Fetch be16 option */
status = ares__buf_fetch_be16(buf, &opt);
if (status != ARES_SUCCESS) {
return status;
}
/* Fetch be16 length */
status = ares__buf_fetch_be16(buf, &len);
if (status != ARES_SUCCESS) {
return status;
}
if (len) {
status = ares__buf_fetch_bytes_dup(buf, len, ARES_TRUE, &val);
if (status != ARES_SUCCESS) {
return status;
}
}
status = ares_dns_rr_set_opt_own(rr, ARES_RR_SVCB_PARAMS, opt, val, len);
if (status != ARES_SUCCESS) {
return status;
}
}
return ARES_SUCCESS;
}
static ares_status_t ares_dns_parse_rr_https(ares__buf_t *buf,
ares_dns_rr_t *rr,
size_t rdlength)
{
ares_status_t status;
size_t orig_len = ares__buf_len(buf);
status = ares_dns_parse_and_set_be16(buf, rr, ARES_RR_HTTPS_PRIORITY);
if (status != ARES_SUCCESS) {
return status;
}
status = ares_dns_parse_and_set_dns_name(buf, ARES_FALSE, rr,
ARES_RR_HTTPS_TARGET);
if (status != ARES_SUCCESS) {
return status;
}
/* Parse params */
while (ares_dns_rr_remaining_len(buf, orig_len, rdlength)) {
unsigned short opt = 0;
unsigned short len = 0;
unsigned char *val = NULL;
/* Fetch be16 option */
status = ares__buf_fetch_be16(buf, &opt);
if (status != ARES_SUCCESS) {
return status;
}
/* Fetch be16 length */
status = ares__buf_fetch_be16(buf, &len);
if (status != ARES_SUCCESS) {
return status;
}
if (len) {
status = ares__buf_fetch_bytes_dup(buf, len, ARES_TRUE, &val);
if (status != ARES_SUCCESS) {
return status;
}
}
status = ares_dns_rr_set_opt_own(rr, ARES_RR_HTTPS_PARAMS, opt, val, len);
if (status != ARES_SUCCESS) {
return status;
}
}
return ARES_SUCCESS;
}
static ares_status_t ares_dns_parse_rr_uri(ares__buf_t *buf, ares_dns_rr_t *rr,
size_t rdlength)
{
@ -810,6 +913,10 @@ static ares_status_t
return ares_dns_parse_rr_opt(buf, rr, rdlength, raw_class, raw_ttl);
case ARES_REC_TYPE_TLSA:
return ares_dns_parse_rr_tlsa(buf, rr, rdlength);
case ARES_REC_TYPE_SVCB:
return ares_dns_parse_rr_svcb(buf, rr, rdlength);
case ARES_REC_TYPE_HTTPS:
return ares_dns_parse_rr_https(buf, rr, rdlength);
case ARES_REC_TYPE_URI:
return ares_dns_parse_rr_uri(buf, rr, rdlength);
case ARES_REC_TYPE_CAA:

@ -167,19 +167,16 @@ static void ares__dns_rr_free(ares_dns_rr_t *rr)
ares_free(rr->r.tlsa.data);
break;
#if 0
case ARES_REC_TYPE_SVBC:
/* Once this record type is supported, need to free here
* ares_free(rr->r.svbc.);
*/
case ARES_REC_TYPE_SVCB:
ares_free(rr->r.svcb.target);
ares__dns_options_free(rr->r.svcb.params);
break;
case ARES_REC_TYPE_HTTPS:
/* Once this record type is supported, need to free here
* ares_free(rr->r.https.);
*/
ares_free(rr->r.https.target);
ares__dns_options_free(rr->r.https.params);
break;
#endif
case ARES_REC_TYPE_URI:
ares_free(rr->r.uri.target);
break;
@ -657,6 +654,24 @@ static void *ares_dns_rr_data_ptr(ares_dns_rr_t *dns_rr, ares_dns_rr_key_t key,
*lenptr = &dns_rr->r.tlsa.data_len;
return &dns_rr->r.tlsa.data;
case ARES_RR_SVCB_PRIORITY:
return &dns_rr->r.svcb.priority;
case ARES_RR_SVCB_TARGET:
return &dns_rr->r.svcb.target;
case ARES_RR_SVCB_PARAMS:
return &dns_rr->r.svcb.params;
case ARES_RR_HTTPS_PRIORITY:
return &dns_rr->r.https.priority;
case ARES_RR_HTTPS_TARGET:
return &dns_rr->r.https.target;
case ARES_RR_HTTPS_PARAMS:
return &dns_rr->r.https.params;
case ARES_RR_URI_PRIORITY:
return &dns_rr->r.uri.priority;

@ -52,13 +52,12 @@ typedef enum {
ARES_REC_TYPE_NAPTR = 35, /*!< RFC 3403. Naming Authority Pointer */
ARES_REC_TYPE_OPT = 41, /*!< RFC 6891. EDNS0 option (meta-RR) */
ARES_REC_TYPE_TLSA = 52, /*!< RFC 6698. DNS-Based Authentication of Named
* Entities (DANE) Transport Layer Security
* (TLS) Protocol: TLSA */
#if 0
ARES_REC_TYPE_SVBC = 64, /*!< General Purpose Service Binding */
ARES_REC_TYPE_HTTPS = 65, /*!< Service Binding type for use with HTTP */
#endif
ARES_REC_TYPE_TLSA = 52, /*!< RFC 6698. DNS-Based Authentication of Named
* Entities (DANE) Transport Layer Security
* (TLS) Protocol: TLSA */
ARES_REC_TYPE_SVCB = 64, /*!< RFC 9460. General Purpose Service Binding */
ARES_REC_TYPE_HTTPS = 65, /*!< RFC 9460. Service Binding type for use with
* HTTPS */
ARES_REC_TYPE_ANY = 255, /*!< Wildcard match. Not response RR. */
ARES_REC_TYPE_URI = 256, /*!< RFC 7553. Uniform Resource Identifier */
ARES_REC_TYPE_CAA = 257, /*!< RFC 6844. Certification Authority
@ -235,6 +234,18 @@ typedef enum {
ARES_RR_TLSA_MATCH = (ARES_REC_TYPE_TLSA * 100) + 3,
/*! TLSA Record. Certificate Association Data. Datatype: BIN */
ARES_RR_TLSA_DATA = (ARES_REC_TYPE_TLSA * 100) + 4,
/*! SVCB Record. SvcPriority. Datatype: U16 */
ARES_RR_SVCB_PRIORITY = (ARES_REC_TYPE_SVCB * 100) + 1,
/*! SVCB Record. TargetName. Datatype: STR */
ARES_RR_SVCB_TARGET = (ARES_REC_TYPE_SVCB * 100) + 2,
/*! SVCB Record. SvcParams. Datatype: OPT */
ARES_RR_SVCB_PARAMS = (ARES_REC_TYPE_SVCB * 100) + 3,
/*! HTTPS Record. SvcPriority. Datatype: U16 */
ARES_RR_HTTPS_PRIORITY = (ARES_REC_TYPE_HTTPS * 100) + 1,
/*! HTTPS Record. TargetName. Datatype: STR */
ARES_RR_HTTPS_TARGET = (ARES_REC_TYPE_HTTPS * 100) + 2,
/*! HTTPS Record. SvcParams. Datatype: OPT */
ARES_RR_HTTPS_PARAMS = (ARES_REC_TYPE_HTTPS * 100) + 3,
/*! URI Record. Priority. Datatype: U16 */
ARES_RR_URI_PRIORITY = (ARES_REC_TYPE_URI * 100) + 1,
/*! URI Record. Weight. Datatype: U16 */
@ -283,6 +294,24 @@ typedef enum {
ARES_TLSA_MATCH_SHA512 = 2
} ares_tlsa_match_t;
/*! SVCB (and HTTPS) known parameters */
typedef enum {
/*! Mandatory keys in this RR (RFC 9460 Section 8) */
ARES_SVCB_PARAM_MANDATORY = 0,
/*! Additional supported protocols (RFC 9460 Section 7.1) */
ARES_SVCB_PARAM_ALPN = 1,
/*! No support for default protocol (RFC 9460 Section 7.1) */
ARES_SVCB_PARAM_NO_DEFAULT_ALPN = 2,
/*! Port for alternative endpoint (RFC 9460 Section 7.2) */
ARES_SVCB_PARAM_PORT = 3,
/*! IPv4 address hints (RFC 9460 Section 7.3) */
ARES_SVCB_PARAM_IPV4HINT = 4,
/*! RESERVED (held for Encrypted ClientHello) */
ARES_SVCB_PARAM_ECH = 5,
/*! IPv6 address hints (RFC 9460 Section 7.3) */
ARES_SVCB_PARAM_IPV6HINT = 6
} ares_svcb_param_t;
/*! String representation of DNS Record Type
*
* \param[in] type DNS Record Type
@ -872,6 +901,12 @@ typedef struct {
size_t data_len;
} ares__dns_tlsa_t;
typedef struct {
unsigned short priority;
char *target;
ares__dns_options_t *params;
} ares__dns_svcb_t;
typedef struct {
unsigned short priority;
unsigned short weight;
@ -914,6 +949,8 @@ struct ares_dns_rr {
ares__dns_naptr_t naptr;
ares__dns_opt_t opt;
ares__dns_tlsa_t tlsa;
ares__dns_svcb_t svcb;
ares__dns_svcb_t https; /*!< https is a type of svcb, so this is right */
ares__dns_uri_t uri;
ares__dns_caa_t caa;
ares__dns_raw_rr_t raw_rr;

@ -611,6 +611,108 @@ static ares_status_t ares_dns_write_rr_tlsa(ares__buf_t *buf,
return ares__buf_append(buf, data, len);
}
static ares_status_t ares_dns_write_rr_svcb(ares__buf_t *buf,
const ares_dns_rr_t *rr,
ares__llist_t **namelist)
{
ares_status_t status;
size_t i;
/* PRIORITY */
status = ares_dns_write_rr_be16(buf, rr, ARES_RR_SVCB_PRIORITY);
if (status != ARES_SUCCESS) {
return status;
}
/* TARGET */
status = ares_dns_write_rr_name(buf, rr, namelist, ARES_FALSE,
ARES_RR_SVCB_TARGET);
if (status != ARES_SUCCESS) {
return status;
}
/* Append Params */
for (i=0; i<ares_dns_rr_get_opt_cnt(rr, ARES_RR_SVCB_PARAMS); i++) {
unsigned short opt;
size_t val_len;
const unsigned char *val;
opt = ares_dns_rr_get_opt(rr, ARES_RR_SVCB_PARAMS, i, &val, &val_len);
/* BE16 option */
status = ares__buf_append_be16(buf, opt);
if (status != ARES_SUCCESS) {
return status;
}
/* BE16 length */
status = ares__buf_append_be16(buf, (unsigned short)(val_len & 0xFFFF));
if (status != ARES_SUCCESS) {
return status;
}
/* Value */
if (val && val_len) {
status = ares__buf_append(buf, val, val_len);
if (status != ARES_SUCCESS) {
return status;
}
}
}
return ARES_SUCCESS;
}
static ares_status_t ares_dns_write_rr_https(ares__buf_t *buf,
const ares_dns_rr_t *rr,
ares__llist_t **namelist)
{
ares_status_t status;
size_t i;
/* PRIORITY */
status = ares_dns_write_rr_be16(buf, rr, ARES_RR_HTTPS_PRIORITY);
if (status != ARES_SUCCESS) {
return status;
}
/* TARGET */
status = ares_dns_write_rr_name(buf, rr, namelist, ARES_FALSE,
ARES_RR_HTTPS_TARGET);
if (status != ARES_SUCCESS) {
return status;
}
/* Append Params */
for (i=0; i<ares_dns_rr_get_opt_cnt(rr, ARES_RR_HTTPS_PARAMS); i++) {
unsigned short opt;
size_t val_len;
const unsigned char *val;
opt = ares_dns_rr_get_opt(rr, ARES_RR_HTTPS_PARAMS, i, &val, &val_len);
/* BE16 option */
status = ares__buf_append_be16(buf, opt);
if (status != ARES_SUCCESS) {
return status;
}
/* BE16 length */
status = ares__buf_append_be16(buf, (unsigned short)(val_len & 0xFFFF));
if (status != ARES_SUCCESS) {
return status;
}
/* Value */
if (val && val_len) {
status = ares__buf_append(buf, val, val_len);
if (status != ARES_SUCCESS) {
return status;
}
}
}
return ARES_SUCCESS;
}
static ares_status_t ares_dns_write_rr_uri(ares__buf_t *buf,
const ares_dns_rr_t *rr,
ares__llist_t **namelist)
@ -824,6 +926,12 @@ static ares_status_t ares_dns_write_rr(ares_dns_record_t *dnsrec,
case ARES_REC_TYPE_TLSA:
status = ares_dns_write_rr_tlsa(buf, rr, namelistptr);
break;
case ARES_REC_TYPE_SVCB:
status = ares_dns_write_rr_svcb(buf, rr, namelistptr);
break;
case ARES_REC_TYPE_HTTPS:
status = ares_dns_write_rr_https(buf, rr, namelistptr);
break;
case ARES_REC_TYPE_URI:
status = ares_dns_write_rr_uri(buf, rr, namelistptr);
break;

@ -613,6 +613,34 @@ TEST_F(LibraryTest, DNSRecord) {
0x45, 0x61, 0xcb, 0x10, 0x66, 0x18, 0xe9, 0x71 };
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_bin(rr, ARES_RR_TLSA_DATA, tlsa, sizeof(tlsa)));
/* SVCB */
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ADDITIONAL,
"_1234._bar.example.com", ARES_REC_TYPE_SVCB, ARES_CLASS_IN, 300));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_u16(rr, ARES_RR_SVCB_PRIORITY, 1));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_str(rr, ARES_RR_SVCB_TARGET, "svc1.example.net"));
const unsigned char svcb_ipv6hint[] = "2001:db8::1";
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_opt(rr, ARES_RR_SVCB_PARAMS, ARES_SVCB_PARAM_IPV6HINT,
svcb_ipv6hint, sizeof(svcb_ipv6hint)-1));
const unsigned char svcb_port[] = "1234";
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_opt(rr, ARES_RR_SVCB_PARAMS, ARES_SVCB_PARAM_IPV6HINT,
svcb_port, sizeof(svcb_port)-1));
/* HTTPS */
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ADDITIONAL,
"example.com", ARES_REC_TYPE_HTTPS, ARES_CLASS_IN, 300));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_u16(rr, ARES_RR_HTTPS_PRIORITY, 1));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_str(rr, ARES_RR_HTTPS_TARGET, ""));
const unsigned char https_alpn[] = "h3";
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_opt(rr, ARES_RR_HTTPS_PARAMS, ARES_SVCB_PARAM_ALPN,
https_alpn, sizeof(https_alpn)-1));
/* URI */
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ADDITIONAL,

Loading…
Cancel
Save