From: Nicko Dehaine Date: Tue, 18 Aug 2020 22:26:06 +0000 (+0000) Subject: Add support for zero and multiple items in the APL RDATA X-Git-Tag: rec-4.4.0-beta1~4^2~18 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=343ef20b6c5934432fcf92285d3ac5fcf6d200a7;p=thirdparty%2Fpdns.git Add support for zero and multiple items in the APL RDATA * Added a struct (APLRDataElement) for a single item * Replace fields with a vector of APLRDataElement in APLRecordContent * Adjusted APL methods to loop over backend record or packet accordingly * Added tests with 0 items, 2 ipv4 items and a mix of ipv4 and ipv6 --- diff --git a/pdns/dnsrecords.cc b/pdns/dnsrecords.cc index dd197c2226..3a1349d563 100644 --- a/pdns/dnsrecords.cc +++ b/pdns/dnsrecords.cc @@ -495,87 +495,93 @@ void APLRecordContent::report(void) { regist(1, QType::APL, &make, &make, "APL"); } + // Parse incoming packets (e.g. nsupdate) std::shared_ptr APLRecordContent::make(const DNSRecord &dr, PacketReader& pr) { uint8_t temp; - - if(dr.d_clen < 5 or dr.d_clen > 20) - throw MOADNSException("Wrong size for APL record"); + APLRDataElement ard; + int processed = 0; auto ret=std::make_shared(); - pr.xfr16BitInt(ret->d_family); - pr.xfr8BitInt(ret->d_prefix); - pr.xfr8BitInt(temp); - ret->d_n = (temp & 128) >> 7; - ret->d_afdlength = temp & 127; - - if (ret->d_family == APL_FAMILY_IPV4) { // IPv4 - if (ret->d_afdlength > 4) { - throw MOADNSException("Invalid IP length for IPv4 APL"); - } - bzero(ret->d_ip4, sizeof(ret->d_ip4)); - // Call min because we can't trust d_afdlength in an inbound packet - for (u_int i=0; i < min(ret->d_afdlength, (u_int)4); i++) - pr.xfr8BitInt(ret->d_ip4[i]); - } else if (ret->d_family == APL_FAMILY_IPV6) { - if (ret->d_afdlength > 16) { - throw MOADNSException("Invalid IP length for IPv6 APL"); - } - bzero(ret->d_ip6, sizeof(ret->d_ip6)); - // Call min because we can't trust d_afdlength in an inbound packet - for (u_int i=0; i < min(ret->d_afdlength, (u_int)16); i++) - pr.xfr8BitInt(ret->d_ip6[i]); - } else + + while (processed> 7; + ard.d_afdlength = temp & 127; + + if (ard.d_family == APL_FAMILY_IPV4) { // IPv4 + if (ard.d_afdlength > 4) { + throw MOADNSException("Invalid IP length for IPv4 APL"); + } + bzero(ard.d_ip4, sizeof(ard.d_ip4)); + // Call min because we can't trust d_afdlength in an inbound packet + for (u_int i=0; i < min(ard.d_afdlength, (u_int)4); i++) + pr.xfr8BitInt(ard.d_ip4[i]); + } else if (ard.d_family == APL_FAMILY_IPV6) { + if (ard.d_afdlength > 16) { + throw MOADNSException("Invalid IP length for IPv6 APL"); + } + bzero(ard.d_ip6, sizeof(ard.d_ip6)); + // Call min because we can't trust d_afdlength in an inbound packet + for (u_int i=0; i < min(ard.d_afdlength, (u_int)16); i++) + pr.xfr8BitInt(ard.d_ip6[i]); + } else throw MOADNSException("Unknown family for APL record"); + processed += 4 + ard.d_afdlength; + + ret->aplrdata.push_back(ard); + } + return ret; } -// Parse backend record -std::shared_ptr APLRecordContent::make(const string& zone) { +// Parse a single APL +APLRDataElement APLRecordContent::parseAPLElement(const string& element) { string record; Netmask nm; int bytes; bool done_trimming; - - auto ret=std::make_shared(); + APLRDataElement ard; // Parse the optional leading ! (negate) - if (zone[0] == '!') { - ret->d_n = true; - record = zone.substr(1, zone.length()-1); + if (element[0] == '!') { + ard.d_n = true; + record = element.substr(1, element.length()-1); } else { - ret->d_n = false; - record = zone; + ard.d_n = false; + record = element; } if (record.find("/") == string::npos) { // Required by RFC section 5 - throw MOADNSException("Asked to decode '"+zone+"' as an APL record, but missing subnet mask"); + throw MOADNSException("Asked to decode '"+element+"' as an APL record, but missing subnet mask"); } if (record.find("1:", 0) == 0) { // IPv4 uint32_t v4ip; - ret->d_family = APL_FAMILY_IPV4; + ard.d_family = APL_FAMILY_IPV4; // Ensure that a mask is provided // Read IPv4 string into a Netmask object nm = Netmask(record.substr(2, record.length() - 2)); - ret->d_prefix = nm.getBits(); + ard.d_prefix = nm.getBits(); if (nm.getNetwork().isIPv4() == 0) - throw MOADNSException("Asked to decode '"+zone+"' as an APL v4 record"); + throw MOADNSException("Asked to decode '"+element+"' as an APL v4 record"); // Section 4.1 of RFC 3123 (don't send trailing "0" bytes) // Copy data; using array of bytes since we might end up truncating them in the packet v4ip = ntohl(nm.getNetwork().sin4.sin_addr.s_addr); - bzero(ret->d_ip4, sizeof(ret->d_ip4)); + bzero(ard.d_ip4, sizeof(ard.d_ip4)); bytes = 4; // Start by assuming we'll send 4 bytes done_trimming = false; for (int i=0; i<4; i++) { - ret->d_ip4[3-i] = (v4ip & 255); + ard.d_ip4[3-i] = (v4ip & 255); // Remove trailing "0" bytes from packet and update length if ((v4ip & 255) == 0 and !done_trimming) { bytes--; @@ -584,25 +590,25 @@ std::shared_ptr APLRecordContent::make(const string& zone) { } v4ip = v4ip >> 8; } - ret->d_afdlength = bytes; + ard.d_afdlength = bytes; } else if (record.find("2:", 0) == 0) { // IPv6 - ret->d_family = APL_FAMILY_IPV6; + ard.d_family = APL_FAMILY_IPV6; // Parse IPv6 string into a Netmask object nm = Netmask(record.substr(2, record.length() - 2)); - ret->d_prefix = nm.getBits(); + ard.d_prefix = nm.getBits(); if (nm.getNetwork().isIPv6() == 0) - throw MOADNSException("Asked to decode '"+zone+"' as an APL v6 record"); + throw MOADNSException("Asked to decode '"+element+"' as an APL v6 record"); // Section 4.2 of RFC 3123 (don't send trailing "0" bytes) // Remove trailing "0" bytes from packet and reduce length - bzero(ret->d_ip6, sizeof(ret->d_ip6)); + bzero(ard.d_ip6, sizeof(ard.d_ip6)); bytes = 16; // Start by assuming we'll send 16 bytes done_trimming = false; for (int i=0; i<16; i++) { - ret->d_ip6[15-i] = nm.getNetwork().sin6.sin6_addr.s6_addr[15-i]; + ard.d_ip6[15-i] = nm.getNetwork().sin6.sin6_addr.s6_addr[15-i]; if (nm.getNetwork().sin6.sin6_addr.s6_addr[15-i] == 0 and !done_trimming) { // trailing 0 byte, update length bytes--; @@ -610,68 +616,96 @@ std::shared_ptr APLRecordContent::make(const string& zone) { done_trimming = true; } } - ret->d_afdlength = bytes; + ard.d_afdlength = bytes; } else { - throw MOADNSException("Asked to encode '"+zone+"' as an IPv6 APL record but got unknown Address Family"); + throw MOADNSException("Asked to encode '"+element+"' as an IPv6 APL record but got unknown Address Family"); } + return ard; +} + +// Parse backend record (0, 1 or more ) +std::shared_ptr APLRecordContent::make(const string& zone) { + APLRDataElement ard; + vector elements; + + auto ret=std::make_shared(); + + boost::split(elements, zone, boost::is_any_of(" ")); + for (std::vector::iterator elem = elements.begin() ; elem != elements.end(); ++elem) { + if (!elem->empty()) { + ard = ret->parseAPLElement(*elem); + ret->aplrdata.push_back(ard); + } + } return ret; } + // DNSRecord to Packet conversion void APLRecordContent::toPacket(DNSPacketWriter& pw) { - - pw.xfr16BitInt(d_family); - pw.xfr8BitInt(d_prefix); - pw.xfr8BitInt((d_n << 7) + d_afdlength); - if (d_family == APL_FAMILY_IPV4) { - for (int i=0; i::iterator ard = aplrdata.begin() ; ard != aplrdata.end(); ++ard) { + pw.xfr16BitInt(ard->d_family); + pw.xfr8BitInt(ard->d_prefix); + pw.xfr8BitInt((ard->d_n << 7) + ard->d_afdlength); + if (ard->d_family == APL_FAMILY_IPV4) { + for (int i=0; id_afdlength; i++) { + pw.xfr8BitInt(ard->d_ip4[i]); } - } else if (d_family == APL_FAMILY_IPV6) { - for (int i=0; id_family == APL_FAMILY_IPV6) { + for (int i=0; id_afdlength; i++) { + pw.xfr8BitInt(ard->d_ip6[i]); } } + } } // Decode record into string string APLRecordContent::getZoneRepresentation(bool noDot) const { - string s_n, s_family; + string s_n, s_family, output; ComboAddress ca; Netmask nm; - // Negation flag - if (d_n) { - s_n = "!"; - } else { - s_n = ""; - } + output = ""; + + for (std::vector::const_iterator ard = aplrdata.begin() ; ard != aplrdata.end(); ++ard) { - if (d_family == APL_FAMILY_IPV4) { // IPv4 - s_family = std::to_string(APL_FAMILY_IPV4); - ca = ComboAddress(); - for (int i=0; i < 4; i++) { - ca.sin4.sin_addr.s_addr |= d_ip4[i] << (i*8); + // Negation flag + if (ard->d_n) { + s_n = "!"; + } else { + s_n = ""; } - } else if (d_family == APL_FAMILY_IPV6) { // IPv6 - s_family = std::to_string(APL_FAMILY_IPV6); - ca = ComboAddress(); - ca.sin4.sin_family = AF_INET6; - for (int i=0; i < 16; i++) { - if (i < d_afdlength) { - ca.sin6.sin6_addr.s6_addr[i] = d_ip6[i]; - } else { - ca.sin6.sin6_addr.s6_addr[i] = 0; + + if (ard->d_family == APL_FAMILY_IPV4) { // IPv4 + s_family = std::to_string(APL_FAMILY_IPV4); + ca = ComboAddress(); + for (int i=0; i < 4; i++) { + ca.sin4.sin_addr.s_addr |= ard->d_ip4[i] << (i*8); } + } else if (ard->d_family == APL_FAMILY_IPV6) { // IPv6 + s_family = std::to_string(APL_FAMILY_IPV6); + ca = ComboAddress(); + ca.sin4.sin_family = AF_INET6; + for (int i=0; i < 16; i++) { + if (i < ard->d_afdlength) { + ca.sin6.sin6_addr.s6_addr[i] = ard->d_ip6[i]; + } else { + ca.sin6.sin6_addr.s6_addr[i] = 0; + } + } + } else { + throw MOADNSException("Asked to decode APL record but got unknown Address Family"); } - } else { - throw MOADNSException("Asked to decode APL record but got unknown Address Family"); - } - nm = Netmask(ca, d_prefix); - return s_n + s_family + ":" + nm.toString(); + nm = Netmask(ca, ard->d_prefix); + + output += s_n + s_family + ":" + nm.toString(); + if (std::next(ard) != aplrdata.end()) + output += " "; + } + return output; } /* APL end */ @@ -819,7 +853,6 @@ void reportOtherTypes() RRSIGRecordContent::report(); DSRecordContent::report(); CDSRecordContent::report(); - IPSECKEYRecordContent::report(); SSHFPRecordContent::report(); CERTRecordContent::report(); NSECRecordContent::report(); diff --git a/pdns/dnsrecords.hh b/pdns/dnsrecords.hh index 0653d5bfcb..4b88d53bdc 100644 --- a/pdns/dnsrecords.hh +++ b/pdns/dnsrecords.hh @@ -786,18 +786,22 @@ private: #define APL_FAMILY_IPV4 1 #define APL_FAMILY_IPV6 2 +typedef struct s_APLRDataElement { + uint16_t d_family; + uint8_t d_prefix; + bool d_n : 1; + unsigned int d_afdlength : 7; + uint8_t d_ip4[4]; + uint8_t d_ip6[16]; +} APLRDataElement; class APLRecordContent : public DNSRecordContent { public: APLRecordContent() {}; includeboilerplate(APL) private: - uint16_t d_family; - uint8_t d_prefix; - bool d_n : 1; - unsigned int d_afdlength : 7; - uint8_t d_ip4[4]; - unsigned char d_ip6[16]; + std::vector aplrdata; + APLRDataElement parseAPLElement(const string &element); }; diff --git a/pdns/test-dnsrecords_cc.cc b/pdns/test-dnsrecords_cc.cc index b19593b9d7..0731f02eda 100644 --- a/pdns/test-dnsrecords_cc.cc +++ b/pdns/test-dnsrecords_cc.cc @@ -148,6 +148,9 @@ BOOST_AUTO_TEST_CASE(test_record_types) { (CASE_S(QType::APL,"!2:2001::1/128", "\x00\x02\x80\x90\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01")) (CASE_S(QType::APL,"2:fe80:1234:5678:9910:8bc:3359:b2e8:720e/128", "\x00\x02\x80\x10\xfe\x80\x12\x34\x56\x78\x99\x10\x08\xbc\x33\x59\xb2\xe8\x72\x0e")) (CASE_S(QType::APL,"2:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128","\x00\x02\x80\x10\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff")) + (CASE_S(QType::APL,"", "")) + (CASE_S(QType::APL,"1:10.0.0.0/32 1:10.1.1.1/32", "\x00\x01\x20\x01\x0a\x00\x01\x20\x04\x0a\x01\x01\x01")) + (CASE_S(QType::APL,"1:10.0.0.0/32 2:100::/8", "\x00\x01\x20\x01\x0a\x00\x02\x08\x01\x01")) (CASE_L(QType::DS, "20642 8 2 04443ABE7E94C3985196BEAE5D548C727B044DDA5151E60D7CD76A9F D931D00E", "20642 8 2 04443abe7e94c3985196beae5d548c727b044dda5151e60d7cd76a9fd931d00e", "\x50\xa2\x08\x02\x04\x44\x3a\xbe\x7e\x94\xc3\x98\x51\x96\xbe\xae\x5d\x54\x8c\x72\x7b\x04\x4d\xda\x51\x51\xe6\x0d\x7c\xd7\x6a\x9f\xd9\x31\xd0\x0e")) (CASE_S(QType::SSHFP, "1 1 aa65e3415a50d9b3519c2b17aceb815fc2538d88", "\x01\x01\xaa\x65\xe3\x41\x5a\x50\xd9\xb3\x51\x9c\x2b\x17\xac\xeb\x81\x5f\xc2\x53\x8d\x88")) (CASE_L(QType::SSHFP, "1 1 aa65e3415a50d9b3519c2b17aceb815fc253 8d88", "1 1 aa65e3415a50d9b3519c2b17aceb815fc2538d88", "\x01\x01\xaa\x65\xe3\x41\x5a\x50\xd9\xb3\x51\x9c\x2b\x17\xac\xeb\x81\x5f\xc2\x53\x8d\x88"))