diff --git a/docs/ares_dns_record.3 b/docs/ares_dns_record.3 index 39232417..fe23b5ee 100644 --- a/docs/ares_dns_record.3 +++ b/docs/ares_dns_record.3 @@ -252,6 +252,28 @@ zone denoted by the Zone Section. .br .RE +.B ares_dns_parse_flags_t - +Flags for altering \fIares_dns_parse(3)\fP behaviour: +.RS 4 +.B ARES_DNS_PARSE_AN_BASE_RAW +- Parse Answer Section from RFC 1035 that allow name compression as RAW RR type +.br +.B ARES_DNS_PARSE_NS_BASE_RAW +- Parse Authority Section from RFC 1035 that allow name compression as RAW RR type +.br +.B ARES_DNS_PARSE_AR_BASE_RAW +- Parse Additional Section from RFC 1035 that allow name compression as RAW RR type +.br +.B ARES_DNS_PARSE_AN_EXT_RAW +- Parse Answer Section from later RFCs (no name compression) as RAW RR type +.br +.B ARES_DNS_PARSE_NS_EXT_RAW +- Parse Authority Section from later RFCs (no name compression) as RAW RR type +.br +.B ARES_DNS_PARSE_AR_EXT_RAW +- Parse Additional Section from later RFCs (no name compression) as RAW RR type +.br +.RE .SH DESCRIPTION diff --git a/include/ares_dns_record.h b/include/ares_dns_record.h index cffe46f6..8a6d7781 100644 --- a/include/ares_dns_record.h +++ b/include/ares_dns_record.h @@ -384,6 +384,16 @@ typedef enum { ARES_OPT_DATATYPE_NAME = 11 } ares_dns_opt_datatype_t; +/*! Data type for flags to ares_dns_parse() */ +typedef enum { + ARES_DNS_PARSE_AN_BASE_RAW = 1 << 0, /*!< Parse Answers from RFC 1035 that allow name compression as RAW */ + ARES_DNS_PARSE_NS_BASE_RAW = 1 << 1, /*!< Parse Authority from RFC 1035 that allow name compression as RAW */ + ARES_DNS_PARSE_AR_BASE_RAW = 1 << 2, /*!< Parse Additional from RFC 1035 that allow name compression as RAW */ + ARES_DNS_PARSE_AN_EXT_RAW = 1 << 3, /*!< Parse Answers from later RFCs (no name compression) RAW */ + ARES_DNS_PARSE_NS_EXT_RAW = 1 << 4, /*!< Parse Authority from later RFCs (no name compression) as RAW */ + ARES_DNS_PARSE_AR_EXT_RAW = 1 << 5, /*!< Parse Additional from later RFCs (no name compression) as RAW */ +} ares_dns_parse_flags_t; + /*! String representation of DNS Record Type * * \param[in] type DNS Record Type @@ -926,7 +936,7 @@ CARES_EXTERN ares_bool_t ares_dns_rr_get_opt_byid(const ares_dns_rr_t *dns_rr * * \param[in] buf pointer to bytes to be parsed * \param[in] buf_len Length of buf provided - * \param[in] flags Flags dictating how the message should be parsed. TBD. + * \param[in] flags Flags dictating how the message should be parsed. * \param[out] dnsrec Pointer passed by reference for a new DNS record object * that must be ares_dns_record_destroy()'d by caller. * \return ARES_SUCCESS on success diff --git a/src/lib/ares_dns_parse.c b/src/lib/ares_dns_parse.c index da26ffdc..b71243da 100644 --- a/src/lib/ares_dns_parse.c +++ b/src/lib/ares_dns_parse.c @@ -1003,8 +1003,7 @@ static ares_status_t ares_dns_parse_rr(ares__buf_t *buf, unsigned int flags, ares_dns_rr_t *rr = NULL; size_t remaining_len = 0; size_t processed_len = 0; - - (void)flags; /* currently unused */ + ares_bool_t namecomp; /* All RRs have the same top level format shown below: * 1 1 1 1 1 1 @@ -1067,6 +1066,17 @@ static ares_status_t ares_dns_parse_rr(ares__buf_t *buf, unsigned int flags, type = ARES_REC_TYPE_RAW_RR; } + namecomp = ares_dns_rec_type_allow_name_compression(type); + if (sect == ARES_SECTION_ANSWER && (flags & (namecomp ? ARES_DNS_PARSE_AN_BASE_RAW : ARES_DNS_PARSE_AN_EXT_RAW))) { + type = ARES_REC_TYPE_RAW_RR; + } + if (sect == ARES_SECTION_AUTHORITY && (flags & (namecomp ? ARES_DNS_PARSE_NS_BASE_RAW : ARES_DNS_PARSE_NS_EXT_RAW))) { + type = ARES_REC_TYPE_RAW_RR; + } + if (sect == ARES_SECTION_ADDITIONAL && (flags & (namecomp ? ARES_DNS_PARSE_AR_BASE_RAW : ARES_DNS_PARSE_AR_EXT_RAW))) { + type = ARES_REC_TYPE_RAW_RR; + } + /* Pull into another buffer for safety */ if (rdlength > ares__buf_len(buf)) { status = ARES_EBADRESP; diff --git a/test/ares-test-internal.cc b/test/ares-test-internal.cc index 8ae002a8..40a39495 100644 --- a/test/ares-test-internal.cc +++ b/test/ares-test-internal.cc @@ -816,6 +816,122 @@ TEST_F(LibraryTest, DNSRecord) { ares_free(msg); } +TEST_F(LibraryTest, DNSParseFlags) { + ares_dns_record_t *dnsrec = NULL; + ares_dns_rr_t *rr = NULL; + struct in_addr addr; + unsigned char *msg = NULL; + size_t msglen = 0; + + EXPECT_EQ(ARES_SUCCESS, + ares_dns_record_create(&dnsrec, 0x1234, + ARES_FLAG_QR|ARES_FLAG_AA|ARES_FLAG_RD|ARES_FLAG_RA, + ARES_OPCODE_QUERY, ARES_RCODE_NOERROR)); + + /* == Question == */ + EXPECT_EQ(ARES_SUCCESS, + ares_dns_record_query_add(dnsrec, "example.com", + ARES_REC_TYPE_ANY, + ARES_CLASS_IN)); + + /* == Answer == */ + /* A */ + EXPECT_EQ(ARES_SUCCESS, + ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ANSWER, "example.com", + ARES_REC_TYPE_A, ARES_CLASS_IN, 300)); + EXPECT_LT(0, ares_inet_net_pton(AF_INET, "1.1.1.1", &addr, sizeof(addr))); + EXPECT_EQ(ARES_SUCCESS, + ares_dns_rr_set_addr(rr, ARES_RR_A_ADDR, &addr)); + /* TLSA */ + EXPECT_EQ(ARES_SUCCESS, + ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ANSWER, + "_443._tcp.example.com", ARES_REC_TYPE_TLSA, ARES_CLASS_IN, 86400)); + EXPECT_EQ(ARES_SUCCESS, + ares_dns_rr_set_u8(rr, ARES_RR_TLSA_CERT_USAGE, ARES_TLSA_USAGE_CA)); + EXPECT_EQ(ARES_SUCCESS, + ares_dns_rr_set_u8(rr, ARES_RR_TLSA_SELECTOR, ARES_TLSA_SELECTOR_FULL)); + EXPECT_EQ(ARES_SUCCESS, + ares_dns_rr_set_u8(rr, ARES_RR_TLSA_MATCH, ARES_TLSA_MATCH_SHA256)); + const unsigned char tlsa[] = { + 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_TLSA_DATA, tlsa, sizeof(tlsa))); + + /* == Authority == */ + /* NS */ + EXPECT_EQ(ARES_SUCCESS, + ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_AUTHORITY, "example.com", + ARES_REC_TYPE_NS, ARES_CLASS_IN, 38400)); + EXPECT_EQ(ARES_SUCCESS, + ares_dns_rr_set_str(rr, ARES_RR_NS_NSDNAME, "ns1.example.com")); + + /* == Additional */ + /* PTR -- doesn't make sense, but ok */ + EXPECT_EQ(ARES_SUCCESS, + ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ADDITIONAL, "example.com", + ARES_REC_TYPE_PTR, ARES_CLASS_IN, 300)); + EXPECT_EQ(ARES_SUCCESS, + ares_dns_rr_set_str(rr, ARES_RR_PTR_DNAME, "b.example.com")); + + /* Write */ + EXPECT_EQ(ARES_SUCCESS, ares_dns_write(dnsrec, &msg, &msglen)); + + /* Cleanup - before reuse */ + ares_dns_record_destroy(dnsrec); + + /* Parse "base" type records (1035) */ + EXPECT_EQ(ARES_SUCCESS, ares_dns_parse(msg, msglen, ARES_DNS_PARSE_AN_BASE_RAW | + ARES_DNS_PARSE_NS_BASE_RAW | ARES_DNS_PARSE_AR_BASE_RAW, &dnsrec)); + + EXPECT_EQ(1, ares_dns_record_query_cnt(dnsrec)); + EXPECT_EQ(2, ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER)); + EXPECT_EQ(1, ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_AUTHORITY)); + EXPECT_EQ(1, ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ADDITIONAL)); + + rr = ares_dns_record_rr_get(dnsrec, ARES_SECTION_ANSWER, 0); + EXPECT_EQ(ARES_REC_TYPE_RAW_RR, ares_dns_rr_get_type(rr)); + + rr = ares_dns_record_rr_get(dnsrec, ARES_SECTION_ANSWER, 1); + EXPECT_EQ(ARES_REC_TYPE_TLSA, ares_dns_rr_get_type(rr)); + + rr = ares_dns_record_rr_get(dnsrec, ARES_SECTION_AUTHORITY, 0); + EXPECT_EQ(ARES_REC_TYPE_RAW_RR, ares_dns_rr_get_type(rr)); + + rr = ares_dns_record_rr_get(dnsrec, ARES_SECTION_ADDITIONAL, 0); + EXPECT_EQ(ARES_REC_TYPE_RAW_RR, ares_dns_rr_get_type(rr)); + + /* Cleanup - before reuse */ + + ares_dns_record_destroy(dnsrec); + + /* Parse later RFCs (no name compression) type records */ + + EXPECT_EQ(ARES_SUCCESS, ares_dns_parse(msg, msglen, ARES_DNS_PARSE_AN_EXT_RAW | + ARES_DNS_PARSE_NS_EXT_RAW | ARES_DNS_PARSE_AR_EXT_RAW, &dnsrec)); + + EXPECT_EQ(1, ares_dns_record_query_cnt(dnsrec)); + EXPECT_EQ(2, ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER)); + EXPECT_EQ(1, ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_AUTHORITY)); + EXPECT_EQ(1, ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ADDITIONAL)); + + rr = ares_dns_record_rr_get(dnsrec, ARES_SECTION_ANSWER, 0); + EXPECT_EQ(ARES_REC_TYPE_A, ares_dns_rr_get_type(rr)); + + rr = ares_dns_record_rr_get(dnsrec, ARES_SECTION_ANSWER, 1); + EXPECT_EQ(ARES_REC_TYPE_RAW_RR, ares_dns_rr_get_type(rr)); + + rr = ares_dns_record_rr_get(dnsrec, ARES_SECTION_AUTHORITY, 0); + EXPECT_EQ(ARES_REC_TYPE_NS, ares_dns_rr_get_type(rr)); + + rr = ares_dns_record_rr_get(dnsrec, ARES_SECTION_ADDITIONAL, 0); + EXPECT_EQ(ARES_REC_TYPE_PTR, ares_dns_rr_get_type(rr)); + + ares_dns_record_destroy(dnsrec); + ares_free(msg); msg = NULL; +} + TEST_F(LibraryTest, CatDomain) { char *s;