From 303cc146801816813891de51cd427b488c05a6a6 Mon Sep 17 00:00:00 2001 From: Nipa Kumar Date: Thu, 12 May 2016 15:38:35 -0700 Subject: [PATCH] Add support for SRV DNS record type. Service Record type returns the hostname and port number of servers providing a specified service. 0 1 80 api.rubygems.org. Add testcase. Change-Id: I2b8abde3fc6932f31af435e7182a6703ed4919d2 Closes-Bug:1577680 --- src/dns/bind/bind_util.cc | 24 +++++++++++++++++++ src/dns/bind/bind_util.h | 14 ++++++++++++ src/dns/test/dns_bind_test.cc | 43 +++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) diff --git a/src/dns/bind/bind_util.cc b/src/dns/bind/bind_util.cc index 20889978e93..b90d81dcab1 100644 --- a/src/dns/bind/bind_util.cc +++ b/src/dns/bind/bind_util.cc @@ -17,6 +17,7 @@ DnsTypeMap g_dns_type_map = map_list_of ("MX", 0x0F) ("TXT", 0x10) ("AAAA", 0x1C) + ("SRV", 0x21) ("ANY", 0xFF); DnsTypeNumMap g_dns_type_num_map = map_list_of @@ -28,6 +29,7 @@ DnsTypeNumMap g_dns_type_num_map = map_list_of (DNS_MX_RECORD, "MX") (DNS_TXT_RECORD, "TXT") (DNS_AAAA_RECORD, "AAAA") + (DNS_SRV_RECORD, "SRV") (DNS_TYPE_ANY, "ANY"); DnsTypeNumMap g_dns_class_num_map = map_list_of @@ -192,6 +194,19 @@ uint8_t *BindUtil::AddData(uint8_t *ptr, const DnsItem &item, ptr = WriteShort(ptr, item.priority); ptr = AddName(ptr, item.data, item.data_plen, item.data_offset, length); length += 2 + 2; + } else if (item.type == DNS_SRV_RECORD) { + uint16_t data_len = 6 + DataLength(item.srv.hn_plen, item.srv.hn_offset, + item.srv.hostname.size()); + ptr = WriteShort(ptr, data_len); + ptr = WriteShort(ptr, item.srv.priority); + ptr = WriteShort(ptr, item.srv.weight); + ptr = WriteShort(ptr, item.srv.port); + ptr = AddName(ptr, item.srv.hostname, item.srv.hn_plen, + item.srv.hn_offset, length); + length += 2 + 6; + } else { + DNS_BIND_TRACE(DnsBindError, + "Unsupported record type in response : " << item.type); } return ptr; @@ -319,6 +334,15 @@ bool BindUtil::ReadData(uint8_t *dns, uint16_t dnslen, int *remlen, if (ReadShort(dns, dnslen, remlen, item.priority) == false) return false; return ReadName(dns, dnslen, remlen, item.data, item.data_plen, item.data_offset); + } else if (item.type == DNS_SRV_RECORD) { + if (ReadShort(dns, dnslen, remlen, item.srv.priority) == false) + return false; + if (ReadShort(dns, dnslen, remlen, item.srv.weight) == false) + return false; + if (ReadShort(dns, dnslen, remlen, item.srv.port) == false) + return false; + return ReadName(dns, dnslen, remlen, item.srv.hostname, + item.srv.hn_plen, item.srv.hn_offset); } DNS_BIND_TRACE(DnsBindError, diff --git a/src/dns/bind/bind_util.h b/src/dns/bind/bind_util.h index ab209fcc2e1..64fe2d9ec28 100644 --- a/src/dns/bind/bind_util.h +++ b/src/dns/bind/bind_util.h @@ -40,6 +40,7 @@ do { \ #define DNS_MX_RECORD 0x0F #define DNS_TXT_RECORD 0x10 #define DNS_AAAA_RECORD 0x1C +#define DNS_SRV_RECORD 0x21 #define DNS_TYPE_ANY 0x00ff // DNS return codes @@ -131,6 +132,18 @@ struct DnsSOAData { } }; +// Data format in an SRV record +struct DnsSRVData { + uint16_t priority; + uint16_t weight; + uint16_t port; + std::string hostname; + uint16_t hn_plen; // length of the prefix in hostname that is unique + uint16_t hn_offset; // offset from where rest of hostname name exists + + DnsSRVData() : hn_plen(0), hn_offset(0) {} +}; + struct DnsItem { uint16_t eclass; uint16_t type; @@ -144,6 +157,7 @@ struct DnsItem { std::string name; std::string data; DnsSOAData soa; + DnsSRVData srv; DnsItem() : eclass(1), type(0), ttl(0), priority(0), offset(0), name_plen(0), name_offset(0), data_plen(0), data_offset(0), soa() {} diff --git a/src/dns/test/dns_bind_test.cc b/src/dns/test/dns_bind_test.cc index 82dcc62ee7e..5f4b72d7430 100644 --- a/src/dns/test/dns_bind_test.cc +++ b/src/dns/test/dns_bind_test.cc @@ -1057,6 +1057,49 @@ TEST_F(DnsBindTest, DnsUpdateErrorParse) { EXPECT_FALSE(BindUtil::ParseDnsUpdate(buf, len, data)); } +// Check the parsing of a DNS SRV Response +TEST_F(DnsBindTest, DnsResponseSRVParse) { + uint8_t buf[1024]; + int count = 1; + DnsItems ans_in, auth_in, add_in; + + DnsItem item; + item.eclass = DNS_CLASS_IN; + item.type = DNS_SRV_RECORD; + item.ttl = 100; + item.name = "_rubygems._tcp.rubygems.org"; + item.srv.priority = 0; + item.srv.weight = 1; + item.srv.port = 80; + item.srv.hostname = "api.rubygems.org"; + ans_in.push_back(item); + + dnshdr *dns = (dnshdr *) buf; + BindUtil::BuildDnsHeader(dns, 0x0102, DNS_QUERY_RESPONSE, DNS_OPCODE_QUERY, + 0, 0, 0, 0); + dns->ques_rrcount = htons(count); + dns->ans_rrcount = htons(count); + dns->auth_rrcount = 0; + dns->add_rrcount = 0; + + uint16_t len = sizeof(dnshdr); + uint8_t *ptr = (uint8_t *) (dns + 1); + for (int i = 0; i < count; i++) + ptr = BindUtil::AddQuestionSection(ptr, "_rubygems._tcp.rubygems.org", + DNS_SRV_RECORD, DNS_CLASS_IN, len); + for (DnsItems::iterator it = ans_in.begin(); it != ans_in.end(); it++) + ptr = BindUtil::AddAnswerSection(ptr, *it, len); + + uint16_t xid; + dns_flags flags; + DnsItems ques, ans, auth, add; + EXPECT_TRUE(BindUtil::ParseDnsResponse(buf, len, xid, flags, + ques, ans, auth, add)); + EXPECT_TRUE(ans == ans_in); +} + + + } // namespace int main(int argc, char **argv) {