diff --git a/src/lib/ares_dns_mapping.c b/src/lib/ares_dns_mapping.c index 88bed782..564991e8 100644 --- a/src/lib/ares_dns_mapping.c +++ b/src/lib/ares_dns_mapping.c @@ -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; diff --git a/src/lib/ares_dns_parse.c b/src/lib/ares_dns_parse.c index 1e9223c1..44226b4b 100644 --- a/src/lib/ares_dns_parse.c +++ b/src/lib/ares_dns_parse.c @@ -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: diff --git a/src/lib/ares_dns_record.c b/src/lib/ares_dns_record.c index db78b9f7..821636be 100644 --- a/src/lib/ares_dns_record.c +++ b/src/lib/ares_dns_record.c @@ -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; diff --git a/src/lib/ares_dns_record.h b/src/lib/ares_dns_record.h index a986913b..71ecb073 100644 --- a/src/lib/ares_dns_record.h +++ b/src/lib/ares_dns_record.h @@ -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; diff --git a/src/lib/ares_dns_write.c b/src/lib/ares_dns_write.c index 161499ac..b88d605a 100644 --- a/src/lib/ares_dns_write.c +++ b/src/lib/ares_dns_write.c @@ -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