From 6129d9b79fce0783a515c3de3fa057a0314e3bdc Mon Sep 17 00:00:00 2001 From: Brad House Date: Mon, 3 Jun 2024 09:11:16 -0400 Subject: [PATCH] Basic support for SIG RR record (RFC 2931 / RFC 2535) (#773) With the current c-ares parser, as per PR #765 parsing was broken due to validation that didn't understand the `SIG` record class. This PR adds basic, non validating, and incomplete support for the `SIG` record type. The additional `KEY` and `NXT` which would be required for additional verification of the records is not implemented. It also does not store the raw unprocessed RR data that would be required for the validation. The primary purpose of this PR is to be able to recognize the record and handle some periphery aspects such as validation of the class associated with the RR and to not honor the TTL in the RR in the c-ares query cache since it will always be 0. Fixes #765 Fix By: Brad House (@bradh352) --- docs/ares_dns_record.3 | 5 ++- docs/ares_dns_rr.3 | 27 ++++++++++++++ include/ares_dns_record.h | 19 ++++++++++ src/lib/ares_dns_mapping.c | 73 ++++++++++++++++++++++++++++++++++++-- src/lib/ares_dns_parse.c | 70 ++++++++++++++++++++++++++++++++++++ src/lib/ares_dns_private.h | 19 ++++++++-- src/lib/ares_dns_record.c | 40 +++++++++++++++++++-- src/lib/ares_dns_write.c | 71 ++++++++++++++++++++++++++++++++++++ src/lib/ares_qcache.c | 5 ++- test/ares-test-internal.cc | 27 ++++++++++++++ 10 files changed, 347 insertions(+), 9 deletions(-) diff --git a/docs/ares_dns_record.3 b/docs/ares_dns_record.3 index 3ec42b54..11c59a64 100644 --- a/docs/ares_dns_record.3 +++ b/docs/ares_dns_record.3 @@ -89,6 +89,9 @@ on requests, and some may only be valid on responses: .B ARES_REC_TYPE_TXT - Text strings .br +.B ARES_REC_TYPE_SIG +- RFC 2535. RFC 2931. SIG Record +.br .B ARES_REC_TYPE_AAAA - RFC 3596. Ip6 Address .br @@ -161,7 +164,7 @@ DNS Header Opcodes: - Zone change notification (RFC 1996) .br .B ARES_OPCODE_UPDATE -- Zone update message (RFC2136) +- Zone update message (RFC 2136) .br .RE diff --git a/docs/ares_dns_rr.3 b/docs/ares_dns_rr.3 index 9ea38fa6..1294fd55 100644 --- a/docs/ares_dns_rr.3 +++ b/docs/ares_dns_rr.3 @@ -190,6 +190,33 @@ Keys used for handling RR record parameters: .B ARES_RR_TXT_DATA - TXT Record. Data. Datatype: \fIARES_DATATYPE_BINP\fP .br +.B ARES_RR_SIG_TYPE_COVERED +- SIG Record. Type Covered. Datatype: \fIARES_DATATYPE_U16\fP +.br +.B ARES_RR_SIG_ALGORITHM +- SIG Record. Algorithm. Datatype: \fIARES_DATATYPE_U8\fP +.br +.B ARES_RR_SIG_LABELS +- SIG Record. Labels. Datatype: \fIARES_DATATYPE_U8\fP +.br +.B ARES_RR_SIG_ORIGINAL_TTL +- SIG Record. Original TTL. Datatype: \fIARES_DATATYPE_U32\fP +.br +.B ARES_RR_SIG_EXPIRATION +- SIG Record. Signature Expiration. Datatype: \fIARES_DATATYPE_U32\fP +.br +.B ARES_RR_SIG_INCEPTION +- SIG Record. Signature Inception. Datatype: \fIARES_DATATYPE_U32\fP +.br +.B ARES_RR_SIG_KEY_TAG +- SIG Record. Key Tag. Datatype: \fIARES_DATATYPE_U16\fP +.br +.B ARES_RR_SIG_SIGNERS_NAME +- SIG Record. Signer's Name. Datatype: \fIARES_DATATYPE_NAME\fP +.br +.B ARES_RR_SIG_SIGNATURE +- SIG Record. Signature. Datatype: \fIARES_DATATYPE_BIN\fP +.br .B ARES_RR_AAAA_ADDR - AAAA Record. Address. Datatype: \fIARES_DATATYPE_INADDR6\fP .br diff --git a/include/ares_dns_record.h b/include/ares_dns_record.h index 8d09bd0a..5533929f 100644 --- a/include/ares_dns_record.h +++ b/include/ares_dns_record.h @@ -51,6 +51,7 @@ typedef enum { ARES_REC_TYPE_HINFO = 13, /*!< Host information. */ ARES_REC_TYPE_MX = 15, /*!< Mail routing information. */ ARES_REC_TYPE_TXT = 16, /*!< Text strings. */ + ARES_REC_TYPE_SIG = 24, /*!< RFC 2535 / RFC 2931. SIG Record */ ARES_REC_TYPE_AAAA = 28, /*!< RFC 3596. Ip6 Address. */ ARES_REC_TYPE_SRV = 33, /*!< RFC 2782. Server Selection. */ ARES_REC_TYPE_NAPTR = 35, /*!< RFC 3403. Naming Authority Pointer */ @@ -208,6 +209,24 @@ typedef enum { ARES_RR_MX_EXCHANGE = (ARES_REC_TYPE_MX * 100) + 2, /*! TXT Record. Data. Datatype: BINP */ ARES_RR_TXT_DATA = (ARES_REC_TYPE_TXT * 100) + 1, + /*! SIG Record. Type Covered. Datatype: U16 */ + ARES_RR_SIG_TYPE_COVERED = (ARES_REC_TYPE_SIG * 100) + 1, + /*! SIG Record. Algorithm. Datatype: U8 */ + ARES_RR_SIG_ALGORITHM = (ARES_REC_TYPE_SIG * 100) + 2, + /*! SIG Record. Labels. Datatype: U8 */ + ARES_RR_SIG_LABELS = (ARES_REC_TYPE_SIG * 100) + 3, + /*! SIG Record. Original TTL. Datatype: U32 */ + ARES_RR_SIG_ORIGINAL_TTL = (ARES_REC_TYPE_SIG * 100) + 4, + /*! SIG Record. Signature Expiration. Datatype: U32 */ + ARES_RR_SIG_EXPIRATION = (ARES_REC_TYPE_SIG * 100) + 5, + /*! SIG Record. Signature Inception. Datatype: U32 */ + ARES_RR_SIG_INCEPTION = (ARES_REC_TYPE_SIG * 100) + 6, + /*! SIG Record. Key Tag. Datatype: U16 */ + ARES_RR_SIG_KEY_TAG = (ARES_REC_TYPE_SIG * 100) + 7, + /*! SIG Record. Signers Name. Datatype: NAME */ + ARES_RR_SIG_SIGNERS_NAME = (ARES_REC_TYPE_SIG * 100) + 8, + /*! SIG Record. Signature. Datatype: BIN */ + ARES_RR_SIG_SIGNATURE = (ARES_REC_TYPE_SIG * 100) + 9, /*! AAAA Record. Address. Datatype: INADDR6 */ ARES_RR_AAAA_ADDR = (ARES_REC_TYPE_AAAA * 100) + 1, /*! SRV Record. Priority. Datatype: U16 */ diff --git a/src/lib/ares_dns_mapping.c b/src/lib/ares_dns_mapping.c index a877f999..6c4362f0 100644 --- a/src/lib/ares_dns_mapping.c +++ b/src/lib/ares_dns_mapping.c @@ -93,6 +93,7 @@ ares_bool_t ares_dns_rec_type_isvalid(ares_dns_rec_type_t type, case ARES_REC_TYPE_HINFO: case ARES_REC_TYPE_MX: case ARES_REC_TYPE_TXT: + case ARES_REC_TYPE_SIG: case ARES_REC_TYPE_AAAA: case ARES_REC_TYPE_SRV: case ARES_REC_TYPE_NAPTR: @@ -133,9 +134,18 @@ ares_bool_t ares_dns_rec_type_allow_name_compression(ares_dns_rec_type_t type) return ARES_FALSE; } -ares_bool_t ares_dns_class_isvalid(ares_dns_class_t qclass, - ares_bool_t is_query) +ares_bool_t ares_dns_class_isvalid(ares_dns_class_t qclass, + ares_dns_rec_type_t type, + ares_bool_t is_query) { + /* If we don't understand the record type, we shouldn't validate the class + * as there are some instances like on RFC 2391 (SIG RR) the class is + * meaningless, but since we didn't support that record type, we didn't + * know it shouldn't be validated */ + if (type == ARES_REC_TYPE_RAW_RR) { + return ARES_TRUE; + } + switch (qclass) { case ARES_CLASS_IN: case ARES_CLASS_CHAOS: @@ -143,7 +153,13 @@ ares_bool_t ares_dns_class_isvalid(ares_dns_class_t qclass, case ARES_CLASS_NONE: return ARES_TRUE; case ARES_CLASS_ANY: - return is_query ? ARES_TRUE : ARES_FALSE; + if (type == ARES_REC_TYPE_SIG) { + return ARES_TRUE; + } + if (is_query) { + return ARES_TRUE; + } + return ARES_FALSE; } return ARES_FALSE; } @@ -191,6 +207,8 @@ const char *ares_dns_rec_type_tostr(ares_dns_rec_type_t type) return "MX"; case ARES_REC_TYPE_TXT: return "TXT"; + case ARES_REC_TYPE_SIG: + return "SIG"; case ARES_REC_TYPE_AAAA: return "AAAA"; case ARES_REC_TYPE_SRV: @@ -305,6 +323,33 @@ const char *ares_dns_rr_key_tostr(ares_dns_rr_key_t key) case ARES_RR_TXT_DATA: return "DATA"; + case ARES_RR_SIG_TYPE_COVERED: + return "TYPE_COVERED"; + + case ARES_RR_SIG_ALGORITHM: + return "ALGORITHM"; + + case ARES_RR_SIG_LABELS: + return "LABELS"; + + case ARES_RR_SIG_ORIGINAL_TTL: + return "ORIGINAL_TTL"; + + case ARES_RR_SIG_EXPIRATION: + return "EXPIRATION"; + + case ARES_RR_SIG_INCEPTION: + return "INCEPTION"; + + case ARES_RR_SIG_KEY_TAG: + return "KEY_TAG"; + + case ARES_RR_SIG_SIGNERS_NAME: + return "SIGNERS_NAME"; + + case ARES_RR_SIG_SIGNATURE: + return "SIGNATURE"; + case ARES_RR_SRV_PRIORITY: return "PRIORITY"; @@ -420,6 +465,7 @@ ares_dns_datatype_t ares_dns_rr_key_datatype(ares_dns_rr_key_t key) case ARES_RR_SOA_RNAME: case ARES_RR_PTR_DNAME: case ARES_RR_MX_EXCHANGE: + case ARES_RR_SIG_SIGNERS_NAME: case ARES_RR_SRV_TARGET: case ARES_RR_SVCB_TARGET: case ARES_RR_HTTPS_TARGET: @@ -440,9 +486,14 @@ ares_dns_datatype_t ares_dns_rr_key_datatype(ares_dns_rr_key_t key) case ARES_RR_SOA_RETRY: case ARES_RR_SOA_EXPIRE: case ARES_RR_SOA_MINIMUM: + case ARES_RR_SIG_ORIGINAL_TTL: + case ARES_RR_SIG_EXPIRATION: + case ARES_RR_SIG_INCEPTION: return ARES_DATATYPE_U32; case ARES_RR_MX_PREFERENCE: + case ARES_RR_SIG_TYPE_COVERED: + case ARES_RR_SIG_KEY_TAG: case ARES_RR_SRV_PRIORITY: case ARES_RR_SRV_WEIGHT: case ARES_RR_SRV_PORT: @@ -457,6 +508,8 @@ ares_dns_datatype_t ares_dns_rr_key_datatype(ares_dns_rr_key_t key) case ARES_RR_RAW_RR_TYPE: return ARES_DATATYPE_U16; + case ARES_RR_SIG_ALGORITHM: + case ARES_RR_SIG_LABELS: case ARES_RR_OPT_VERSION: case ARES_RR_TLSA_CERT_USAGE: case ARES_RR_TLSA_SELECTOR: @@ -468,6 +521,7 @@ ares_dns_datatype_t ares_dns_rr_key_datatype(ares_dns_rr_key_t key) case ARES_RR_TXT_DATA: return ARES_DATATYPE_BINP; + case ARES_RR_SIG_SIGNATURE: case ARES_RR_TLSA_DATA: case ARES_RR_RAW_RR_DATA: return ARES_DATATYPE_BIN; @@ -494,6 +548,15 @@ static const ares_dns_rr_key_t rr_hinfo_keys[] = { ARES_RR_HINFO_CPU, ARES_RR_HINFO_OS }; static const ares_dns_rr_key_t rr_mx_keys[] = { ARES_RR_MX_PREFERENCE, ARES_RR_MX_EXCHANGE }; +static const ares_dns_rr_key_t rr_sig_keys[] = { ARES_RR_SIG_TYPE_COVERED, + ARES_RR_SIG_ALGORITHM, + ARES_RR_SIG_LABELS, + ARES_RR_SIG_ORIGINAL_TTL, + ARES_RR_SIG_EXPIRATION, + ARES_RR_SIG_INCEPTION, + ARES_RR_SIG_KEY_TAG, + ARES_RR_SIG_SIGNERS_NAME, + ARES_RR_SIG_SIGNATURE }; static const ares_dns_rr_key_t rr_txt_keys[] = { ARES_RR_TXT_DATA }; static const ares_dns_rr_key_t rr_aaaa_keys[] = { ARES_RR_AAAA_ADDR }; static const ares_dns_rr_key_t rr_srv_keys[] = { @@ -560,6 +623,9 @@ const ares_dns_rr_key_t *ares_dns_rr_get_keys(ares_dns_rec_type_t type, case ARES_REC_TYPE_TXT: *cnt = sizeof(rr_txt_keys) / sizeof(*rr_txt_keys); return rr_txt_keys; + case ARES_REC_TYPE_SIG: + *cnt = sizeof(rr_sig_keys) / sizeof(*rr_sig_keys); + return rr_sig_keys; case ARES_REC_TYPE_AAAA: *cnt = sizeof(rr_aaaa_keys) / sizeof(*rr_aaaa_keys); return rr_aaaa_keys; @@ -644,6 +710,7 @@ ares_bool_t ares_dns_rec_type_fromstr(ares_dns_rec_type_t *qtype, { "HINFO", ARES_REC_TYPE_HINFO }, { "MX", ARES_REC_TYPE_MX }, { "TXT", ARES_REC_TYPE_TXT }, + { "SIG", ARES_REC_TYPE_SIG }, { "AAAA", ARES_REC_TYPE_AAAA }, { "SRV", ARES_REC_TYPE_SRV }, { "NAPTR", ARES_REC_TYPE_NAPTR }, diff --git a/src/lib/ares_dns_parse.c b/src/lib/ares_dns_parse.c index 3944820f..8b771ee1 100644 --- a/src/lib/ares_dns_parse.c +++ b/src/lib/ares_dns_parse.c @@ -296,6 +296,74 @@ static ares_status_t ares_dns_parse_rr_txt(ares__buf_t *buf, ares_dns_rr_t *rr, ARES_RR_TXT_DATA); } +static ares_status_t ares_dns_parse_rr_sig(ares__buf_t *buf, ares_dns_rr_t *rr, + size_t rdlength) +{ + ares_status_t status; + size_t orig_len = ares__buf_len(buf); + size_t len; + unsigned char *data; + + status = ares_dns_parse_and_set_be16(buf, rr, ARES_RR_SIG_TYPE_COVERED); + if (status != ARES_SUCCESS) { + return status; + } + + status = ares_dns_parse_and_set_u8(buf, rr, ARES_RR_SIG_ALGORITHM); + if (status != ARES_SUCCESS) { + return status; + } + + status = ares_dns_parse_and_set_u8(buf, rr, ARES_RR_SIG_LABELS); + if (status != ARES_SUCCESS) { + return status; + } + + status = ares_dns_parse_and_set_be32(buf, rr, ARES_RR_SIG_ORIGINAL_TTL); + if (status != ARES_SUCCESS) { + return status; + } + + status = ares_dns_parse_and_set_be32(buf, rr, ARES_RR_SIG_EXPIRATION); + if (status != ARES_SUCCESS) { + return status; + } + + status = ares_dns_parse_and_set_be32(buf, rr, ARES_RR_SIG_INCEPTION); + if (status != ARES_SUCCESS) { + return status; + } + + status = ares_dns_parse_and_set_be16(buf, rr, ARES_RR_SIG_KEY_TAG); + if (status != ARES_SUCCESS) { + return status; + } + + status = ares_dns_parse_and_set_dns_name(buf, ARES_FALSE, rr, + ARES_RR_SIG_SIGNERS_NAME); + if (status != ARES_SUCCESS) { + return status; + } + + len = ares_dns_rr_remaining_len(buf, orig_len, rdlength); + if (len == 0) { + return ARES_EBADRESP; + } + + status = ares__buf_fetch_bytes_dup(buf, len, ARES_FALSE, &data); + if (status != ARES_SUCCESS) { + return status; + } + + status = ares_dns_rr_set_bin_own(rr, ARES_RR_SIG_SIGNATURE, data, len); + if (status != ARES_SUCCESS) { + ares_free(data); + return status; + } + + return ARES_SUCCESS; +} + static ares_status_t ares_dns_parse_rr_aaaa(ares__buf_t *buf, ares_dns_rr_t *rr, size_t rdlength) { @@ -912,6 +980,8 @@ static ares_status_t return ares_dns_parse_rr_mx(buf, rr, rdlength); case ARES_REC_TYPE_TXT: return ares_dns_parse_rr_txt(buf, rr, rdlength); + case ARES_REC_TYPE_SIG: + return ares_dns_parse_rr_sig(buf, rr, rdlength); case ARES_REC_TYPE_AAAA: return ares_dns_parse_rr_aaaa(buf, rr, rdlength); case ARES_REC_TYPE_SRV: diff --git a/src/lib/ares_dns_private.h b/src/lib/ares_dns_private.h index 3af4b3c9..e8e783d5 100644 --- a/src/lib/ares_dns_private.h +++ b/src/lib/ares_dns_private.h @@ -32,8 +32,9 @@ ares_bool_t ares_dns_rcode_isvalid(ares_dns_rcode_t rcode); ares_bool_t ares_dns_flags_arevalid(unsigned short flags); ares_bool_t ares_dns_rec_type_isvalid(ares_dns_rec_type_t type, ares_bool_t is_query); -ares_bool_t ares_dns_class_isvalid(ares_dns_class_t qclass, - ares_bool_t is_query); +ares_bool_t ares_dns_class_isvalid(ares_dns_class_t qclass, + ares_dns_rec_type_t type, + ares_bool_t is_query); ares_bool_t ares_dns_section_isvalid(ares_dns_section_t sect); ares_status_t ares_dns_rr_set_str_own(ares_dns_rr_t *dns_rr, ares_dns_rr_key_t key, char *val); @@ -123,6 +124,19 @@ typedef struct { size_t data_len; } ares__dns_txt_t; +typedef struct { + unsigned short type_covered; + unsigned char algorithm; + unsigned char labels; + unsigned int original_ttl; + unsigned int expiration; + unsigned int inception; + unsigned short key_tag; + char *signers_name; + unsigned char *signature; + size_t signature_len; +} ares__dns_sig_t; + typedef struct { struct ares_in6_addr addr; } ares__dns_aaaa_t; @@ -216,6 +230,7 @@ struct ares_dns_rr { ares__dns_hinfo_t hinfo; ares__dns_mx_t mx; ares__dns_txt_t txt; + ares__dns_sig_t sig; ares__dns_aaaa_t aaaa; ares__dns_srv_t srv; ares__dns_naptr_t naptr; diff --git a/src/lib/ares_dns_record.c b/src/lib/ares_dns_record.c index ec7f7e73..e6b7bf7f 100644 --- a/src/lib/ares_dns_record.c +++ b/src/lib/ares_dns_record.c @@ -148,6 +148,11 @@ static void ares__dns_rr_free(ares_dns_rr_t *rr) ares_free(rr->r.txt.data); break; + case ARES_REC_TYPE_SIG: + ares_free(rr->r.sig.signers_name); + ares_free(rr->r.sig.signature); + break; + case ARES_REC_TYPE_SRV: ares_free(rr->r.srv.target); break; @@ -245,7 +250,7 @@ ares_status_t ares_dns_record_query_add(ares_dns_record_t *dnsrec, if (dnsrec == NULL || name == NULL || !ares_dns_rec_type_isvalid(qtype, ARES_TRUE) || - !ares_dns_class_isvalid(qclass, ARES_TRUE)) { + !ares_dns_class_isvalid(qclass, qtype, ARES_TRUE)) { return ARES_EFORMERR; } @@ -412,7 +417,7 @@ ares_status_t ares_dns_record_rr_add(ares_dns_rr_t **rr_out, if (dnsrec == NULL || name == NULL || rr_out == NULL || !ares_dns_section_isvalid(sect) || !ares_dns_rec_type_isvalid(type, ARES_FALSE) || - !ares_dns_class_isvalid(rclass, ARES_FALSE)) { + !ares_dns_class_isvalid(rclass, type, ARES_FALSE)) { return ARES_EFORMERR; } @@ -627,6 +632,37 @@ static void *ares_dns_rr_data_ptr(ares_dns_rr_t *dns_rr, ares_dns_rr_key_t key, case ARES_RR_MX_EXCHANGE: return &dns_rr->r.mx.exchange; + case ARES_RR_SIG_TYPE_COVERED: + return &dns_rr->r.sig.type_covered; + + case ARES_RR_SIG_ALGORITHM: + return &dns_rr->r.sig.algorithm; + + case ARES_RR_SIG_LABELS: + return &dns_rr->r.sig.labels; + + case ARES_RR_SIG_ORIGINAL_TTL: + return &dns_rr->r.sig.original_ttl; + + case ARES_RR_SIG_EXPIRATION: + return &dns_rr->r.sig.expiration; + + case ARES_RR_SIG_INCEPTION: + return &dns_rr->r.sig.inception; + + case ARES_RR_SIG_KEY_TAG: + return &dns_rr->r.sig.key_tag; + + case ARES_RR_SIG_SIGNERS_NAME: + return &dns_rr->r.sig.signers_name; + + case ARES_RR_SIG_SIGNATURE: + if (lenptr == NULL) { + return NULL; + } + *lenptr = &dns_rr->r.sig.signature_len; + return &dns_rr->r.sig.signature; + case ARES_RR_TXT_DATA: if (lenptr == NULL) { return NULL; diff --git a/src/lib/ares_dns_write.c b/src/lib/ares_dns_write.c index b49ec07b..1643f081 100644 --- a/src/lib/ares_dns_write.c +++ b/src/lib/ares_dns_write.c @@ -424,6 +424,74 @@ static ares_status_t ares_dns_write_rr_txt(ares__buf_t *buf, return ares_dns_write_rr_binstrs(buf, rr, ARES_RR_TXT_DATA); } +static ares_status_t ares_dns_write_rr_sig(ares__buf_t *buf, + const ares_dns_rr_t *rr, + ares__llist_t **namelist) +{ + ares_status_t status; + const unsigned char *data; + size_t len = 0; + + (void)namelist; + + /* TYPE COVERED */ + status = ares_dns_write_rr_be16(buf, rr, ARES_RR_SIG_TYPE_COVERED); + if (status != ARES_SUCCESS) { + return status; + } + + /* ALGORITHM */ + status = ares_dns_write_rr_u8(buf, rr, ARES_RR_SIG_ALGORITHM); + if (status != ARES_SUCCESS) { + return status; + } + + /* LABELS */ + status = ares_dns_write_rr_u8(buf, rr, ARES_RR_SIG_LABELS); + if (status != ARES_SUCCESS) { + return status; + } + + /* ORIGINAL TTL */ + status = ares_dns_write_rr_be32(buf, rr, ARES_RR_SIG_ORIGINAL_TTL); + if (status != ARES_SUCCESS) { + return status; + } + + /* EXPIRATION */ + status = ares_dns_write_rr_be32(buf, rr, ARES_RR_SIG_EXPIRATION); + if (status != ARES_SUCCESS) { + return status; + } + + /* INCEPTION */ + status = ares_dns_write_rr_be32(buf, rr, ARES_RR_SIG_INCEPTION); + if (status != ARES_SUCCESS) { + return status; + } + + /* KEY TAG */ + status = ares_dns_write_rr_be16(buf, rr, ARES_RR_SIG_KEY_TAG); + if (status != ARES_SUCCESS) { + return status; + } + + /* SIGNERS NAME */ + status = ares_dns_write_rr_name(buf, rr, namelist, ARES_FALSE, + ARES_RR_SIG_SIGNERS_NAME); + if (status != ARES_SUCCESS) { + return status; + } + + /* SIGNATURE -- binary, rest of buffer, required to be non-zero length */ + data = ares_dns_rr_get_bin(rr, ARES_RR_SIG_SIGNATURE, &len); + if (data == NULL || len == 0) { + return ARES_EFORMERR; + } + + return ares__buf_append(buf, data, len); +} + static ares_status_t ares_dns_write_rr_aaaa(ares__buf_t *buf, const ares_dns_rr_t *rr, ares__llist_t **namelist) @@ -925,6 +993,9 @@ static ares_status_t ares_dns_write_rr(const ares_dns_record_t *dnsrec, case ARES_REC_TYPE_TXT: status = ares_dns_write_rr_txt(buf, rr, namelistptr); break; + case ARES_REC_TYPE_SIG: + status = ares_dns_write_rr_sig(buf, rr, namelistptr); + break; case ARES_REC_TYPE_AAAA: status = ares_dns_write_rr_aaaa(buf, rr, namelistptr); break; diff --git a/src/lib/ares_qcache.c b/src/lib/ares_qcache.c index e1ee3cc3..087518d7 100644 --- a/src/lib/ares_qcache.c +++ b/src/lib/ares_qcache.c @@ -254,7 +254,10 @@ static unsigned int ares__qcache_calc_minttl(ares_dns_record_t *dnsrec) ares_dns_record_rr_get(dnsrec, (ares_dns_section_t)sect, i); ares_dns_rec_type_t type = ares_dns_rr_get_type(rr); unsigned int ttl = ares_dns_rr_get_ttl(rr); - if (type == ARES_REC_TYPE_OPT || type == ARES_REC_TYPE_SOA) { + + /* TTL is meaningless on these record types */ + if (type == ARES_REC_TYPE_OPT || type == ARES_REC_TYPE_SOA || + type == ARES_REC_TYPE_SIG) { continue; } diff --git a/test/ares-test-internal.cc b/test/ares-test-internal.cc index 853fbbb6..b5771878 100644 --- a/test/ares-test-internal.cc +++ b/test/ares-test-internal.cc @@ -467,6 +467,33 @@ TEST_F(LibraryTest, DNSRecord) { EXPECT_EQ(ARES_SUCCESS, ares_dns_rr_set_bin(rr, ARES_RR_TXT_DATA, (unsigned char *)txt, sizeof(txt))); + /* SIG */ + EXPECT_EQ(ARES_SUCCESS, + ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ANSWER, "example.com", + ARES_REC_TYPE_SIG, ARES_CLASS_ANY, 0)); + EXPECT_EQ(ARES_SUCCESS, + ares_dns_rr_set_u16(rr, ARES_RR_SIG_TYPE_COVERED, ARES_REC_TYPE_TXT)); + EXPECT_EQ(ARES_SUCCESS, + ares_dns_rr_set_u8(rr, ARES_RR_SIG_ALGORITHM, 1)); + EXPECT_EQ(ARES_SUCCESS, + ares_dns_rr_set_u8(rr, ARES_RR_SIG_LABELS, 1)); + EXPECT_EQ(ARES_SUCCESS, + ares_dns_rr_set_u32(rr, ARES_RR_SIG_ORIGINAL_TTL, 3200)); + EXPECT_EQ(ARES_SUCCESS, + ares_dns_rr_set_u32(rr, ARES_RR_SIG_EXPIRATION, (unsigned int)time(NULL))); + EXPECT_EQ(ARES_SUCCESS, + ares_dns_rr_set_u32(rr, ARES_RR_SIG_INCEPTION, (unsigned int)time(NULL) - (86400 * 365))); + EXPECT_EQ(ARES_SUCCESS, + ares_dns_rr_set_u16(rr, ARES_RR_SIG_KEY_TAG, 0x1234)); + EXPECT_EQ(ARES_SUCCESS, + ares_dns_rr_set_str(rr, ARES_RR_SIG_SIGNERS_NAME, "signer.example.com")); + const unsigned char sig[] = { + 0xd2, 0xab, 0xde, 0x24, 0x0d, 0x7c, 0xd3, 0xee, 0x6b, 0x4b, 0x28, 0xc5, + 0x4d, 0xf0, 0x34, 0xb9, 0x79, 0x83, 0xa1, 0xd1, 0x6e, 0x8a, 0x41, 0x0e, + 0x45, 0x61, 0xcb, 0x10, 0x66, 0x18, 0xe9, 0x71 }; + EXPECT_EQ(ARES_SUCCESS, + ares_dns_rr_set_bin(rr, ARES_RR_SIG_SIGNATURE, sig, sizeof(sig))); + /* == Authority == */ /* NS */