From 786ed0ff1cfb63cabd6227d3927ca877a0ee8139 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Thu, 25 Feb 2021 10:49:43 +0100 Subject: [PATCH] Implement RFC 6742 RR Types This commit implements the NID, L32, L64, and LP record types from RFC 6742. `dig` can properly parse the wiredata we send, and with the added tests, I'm certain of the implementation. The RFC has optional additional processing, this is not implemented. Closes #10119 --- docs/upgrading.rst | 2 +- pdns/dnsparser.cc | 9 ++++++++ pdns/dnsparser.hh | 1 + pdns/dnsrecords.cc | 20 ++++++++++++++++++ pdns/dnsrecords.hh | 41 ++++++++++++++++++++++++++++++++++++ pdns/dnswriter.cc | 4 ++++ pdns/dnswriter.hh | 1 + pdns/misc.hh | 3 +++ pdns/qtype.cc | 4 ++++ pdns/qtype.hh | 4 ++++ pdns/rcpgenerator.cc | 37 ++++++++++++++++++++++++++++++++ pdns/rcpgenerator.hh | 3 +++ pdns/test-dnsrecords_cc.cc | 7 ++++++ pdns/test-dnswriter_cc.cc | 25 ++++++++++++++++++++++ pdns/test-rcpgenerator_cc.cc | 20 ++++++++++++++++++ 15 files changed, 180 insertions(+), 1 deletion(-) diff --git a/docs/upgrading.rst b/docs/upgrading.rst index edfd248f72..d99094bdc6 100644 --- a/docs/upgrading.rst +++ b/docs/upgrading.rst @@ -14,7 +14,7 @@ upgrade notes if your version is older than 3.4.2. Record type changes ^^^^^^^^^^^^^^^^^^^ -The in-database format of ``CSYNC`` and ``IPSECKEY`` records has changed from 'generic' format to its specialized format. +The in-database format of ``CSYNC``, ``IPSECKEY``, ``NID``, ``L32``, ``L64``, and ``LP`` records has changed from 'generic' format to its specialized format. API users might notice that replacing records of these types leaves the old TYPExx records around, even if PowerDNS is not serving them. To fix this, enable :ref:`setting-upgrade-unknown-types` and replace the records; this will then delete those TYPExx records. diff --git a/pdns/dnsparser.cc b/pdns/dnsparser.cc index 1c9f92dc3f..1a1fbde99e 100644 --- a/pdns/dnsparser.cc +++ b/pdns/dnsparser.cc @@ -381,6 +381,15 @@ void PacketReader::copyRecord(unsigned char* dest, uint16_t len) d_pos+=len; } +void PacketReader::xfrNodeOrLocatorID(NodeOrLocatorID& ret) +{ + if (d_pos + sizeof(ret) > d_content.size()) { + throw std::out_of_range("Attempt to read 64 bit value outside of packet"); + } + memcpy(&ret, &d_content.at(d_pos), sizeof(ret)); + d_pos += sizeof(ret); +} + void PacketReader::xfr48BitInt(uint64_t& ret) { ret=0; diff --git a/pdns/dnsparser.hh b/pdns/dnsparser.hh index ad2aff9f80..fcc63cad86 100644 --- a/pdns/dnsparser.hh +++ b/pdns/dnsparser.hh @@ -81,6 +81,7 @@ public: uint16_t get16BitInt(); uint8_t get8BitInt(); + void xfrNodeOrLocatorID(NodeOrLocatorID& val); void xfr48BitInt(uint64_t& val); void xfr32BitInt(uint32_t& val) diff --git a/pdns/dnsrecords.cc b/pdns/dnsrecords.cc index 6bb5d7e14f..e953aebe94 100644 --- a/pdns/dnsrecords.cc +++ b/pdns/dnsrecords.cc @@ -417,6 +417,22 @@ boilerplate_conv(RKEY, ) RKEYRecordContent::RKEYRecordContent() {} +boilerplate_conv(NID, + conv.xfr16BitInt(d_preference); + conv.xfrNodeOrLocatorID(d_node_id);) + +boilerplate_conv(L32, + conv.xfr16BitInt(d_preference); + conv.xfrIP(d_locator);) + +boilerplate_conv(L64, + conv.xfr16BitInt(d_preference); + conv.xfrNodeOrLocatorID(d_locator);) + +boilerplate_conv(LP, + conv.xfr16BitInt(d_preference); + conv.xfrName(d_fqdn, false);) + /* EUI48 start */ void EUI48RecordContent::report() { @@ -882,6 +898,10 @@ void reportOtherTypes() APLRecordContent::report(); IPSECKEYRecordContent::report(); CSYNCRecordContent::report(); + NIDRecordContent::report(); + L32RecordContent::report(); + L64RecordContent::report(); + LPRecordContent::report(); } void reportAllTypes() diff --git a/pdns/dnsrecords.hh b/pdns/dnsrecords.hh index 8d2cea1014..abee42ed64 100644 --- a/pdns/dnsrecords.hh +++ b/pdns/dnsrecords.hh @@ -812,6 +812,47 @@ public: private: }; + +class NIDRecordContent : public DNSRecordContent +{ +public: + includeboilerplate(NID); + +private: + uint16_t d_preference; + NodeOrLocatorID d_node_id; +}; + +class L32RecordContent : public DNSRecordContent +{ +public: + includeboilerplate(L32); + +private: + uint16_t d_preference; + uint32_t d_locator; +}; + +class L64RecordContent : public DNSRecordContent +{ +public: + includeboilerplate(L64); + +private: + uint16_t d_preference; + NodeOrLocatorID d_locator; +}; + +class LPRecordContent : public DNSRecordContent +{ +public: + includeboilerplate(LP); + +private: + uint16_t d_preference; + DNSName d_fqdn; +}; + class EUI48RecordContent : public DNSRecordContent { public: diff --git a/pdns/dnswriter.cc b/pdns/dnswriter.cc index c106ee6f90..775793dcd0 100644 --- a/pdns/dnswriter.cc +++ b/pdns/dnswriter.cc @@ -140,6 +140,10 @@ template void GenericDNSPacketWriter::xfr48BitIn d_content.insert(d_content.end(), bytes, bytes + sizeof(bytes)); } +template void GenericDNSPacketWriter::xfrNodeOrLocatorID(NodeOrLocatorID val) +{ + d_content.insert(d_content.end(), val, val + sizeof(val)); +} template void GenericDNSPacketWriter::xfr32BitInt(uint32_t val) { diff --git a/pdns/dnswriter.hh b/pdns/dnswriter.hh index d5596a3a2a..96e8d4f9a2 100644 --- a/pdns/dnswriter.hh +++ b/pdns/dnswriter.hh @@ -86,6 +86,7 @@ public: void truncate(); void xfr48BitInt(uint64_t val); + void xfrNodeOrLocatorID(NodeOrLocatorID val); void xfr32BitInt(uint32_t val); void xfr16BitInt(uint16_t val); void xfrType(uint16_t val) diff --git a/pdns/misc.hh b/pdns/misc.hh index 193750446c..fd377cfc4a 100644 --- a/pdns/misc.hh +++ b/pdns/misc.hh @@ -626,3 +626,6 @@ std::string getCarbonHostName(); size_t parseRFC1035CharString(const std::string &in, std::string &val); // from ragel std::string makeLuaString(const std::string& in); + +// Used in NID and L64 records +typedef uint8_t NodeOrLocatorID[8]; diff --git a/pdns/qtype.cc b/pdns/qtype.cc index 7c23075b97..a3f9d0849e 100644 --- a/pdns/qtype.cc +++ b/pdns/qtype.cc @@ -75,6 +75,10 @@ const map QType::names = { {"SVCB", 64}, {"HTTPS", 65}, {"SPF", 99}, + {"NID", 104}, + {"L32", 105}, + {"L64", 106}, + {"LP", 107}, {"EUI48", 108}, {"EUI64", 109}, {"TKEY", 249}, diff --git a/pdns/qtype.hh b/pdns/qtype.hh index bcb02e2dd9..cabf499840 100644 --- a/pdns/qtype.hh +++ b/pdns/qtype.hh @@ -105,6 +105,10 @@ public: SVCB = 64, HTTPS = 65, SPF = 99, + NID = 104, + L32 = 105, + L64 = 106, + LP = 107, EUI48 = 108, EUI64 = 109, TKEY = 249, diff --git a/pdns/rcpgenerator.cc b/pdns/rcpgenerator.cc index c9cd1afa3d..ea9580d5a0 100644 --- a/pdns/rcpgenerator.cc +++ b/pdns/rcpgenerator.cc @@ -50,6 +50,25 @@ void RecordTextReader::xfr48BitInt(uint64_t &val) throw RecordTextException("Overflow reading 48 bit integer from record content"); // fixme improve } +void RecordTextReader::xfrNodeOrLocatorID(NodeOrLocatorID& val) { + skipSpaces(); + size_t len; + for(len=0; + d_pos+len < d_string.length() && (isxdigit(d_string.at(d_pos+len)) || d_string.at(d_pos+len) == ':'); + len++) ; // find length of ID + + // Parse as v6, and then strip the final 64 zero bytes + struct in6_addr tmpbuf; + string to_parse = d_string.substr(d_pos, len) + ":0:0:0:0"; + + if (inet_pton(AF_INET6, to_parse.c_str(), &tmpbuf) != 1) { + throw RecordTextException("while parsing colon-delimited 64-bit field: '" + d_string.substr(d_pos, len) + "' is invalid"); + } + + std::memcpy(&val, tmpbuf.s6_addr, sizeof(val)); + d_pos += len; +} + void RecordTextReader::xfr64BitInt(uint64_t &val) { skipSpaces(); @@ -564,6 +583,24 @@ RecordTextWriter::RecordTextWriter(string& str, bool noDot) : d_string(str) d_nodot=noDot; } +void RecordTextWriter::xfrNodeOrLocatorID(const NodeOrLocatorID& val) +{ + if(!d_string.empty()) { + d_string.append(1,' '); + } + + size_t ctr = 0; + char tmp[5]; + for (auto const &c : val) { + snprintf(tmp, sizeof(tmp), "%02X", c); + d_string+=tmp; + ctr++; + if (ctr % 2 == 0 && ctr != 8) { + d_string+=':'; + } + } +} + void RecordTextWriter::xfr48BitInt(const uint64_t& val) { if(!d_string.empty()) diff --git a/pdns/rcpgenerator.hh b/pdns/rcpgenerator.hh index d824dac865..826e829961 100644 --- a/pdns/rcpgenerator.hh +++ b/pdns/rcpgenerator.hh @@ -27,6 +27,7 @@ #include "namespaces.hh" #include "dnsname.hh" #include "iputils.hh" +#include "misc.hh" #include "svc-records.hh" class RecordTextException : public runtime_error @@ -40,6 +41,7 @@ class RecordTextReader { public: RecordTextReader(string str, DNSName zone=DNSName("")); + void xfrNodeOrLocatorID(NodeOrLocatorID& val); void xfr64BitInt(uint64_t& val); void xfr48BitInt(uint64_t& val); void xfr32BitInt(uint32_t& val); @@ -83,6 +85,7 @@ class RecordTextWriter { public: RecordTextWriter(string& str, bool noDot=false); + void xfrNodeOrLocatorID(const NodeOrLocatorID& val); void xfr48BitInt(const uint64_t& val); void xfr32BitInt(const uint32_t& val); void xfr16BitInt(const uint16_t& val); diff --git a/pdns/test-dnsrecords_cc.cc b/pdns/test-dnsrecords_cc.cc index 22f87700ef..bf521f1970 100644 --- a/pdns/test-dnsrecords_cc.cc +++ b/pdns/test-dnsrecords_cc.cc @@ -230,6 +230,13 @@ BOOST_AUTO_TEST_CASE(test_record_types) { (CASE_L(QType::SVCB, "16 foo.powerdns.org. alpn=h2,h3 mandatory=alpn ipv4hint=192.0.2.1", "16 foo.powerdns.org. mandatory=alpn alpn=h2,h3 ipv4hint=192.0.2.1", "\0\x10\3foo\x08powerdns\x03org\x00\x00\x00\x00\x02\x00\x01\x00\x01\x00\x06\x02h2\x02h3\x00\x04\x00\x04\xc0\x00\x02\x01")) (CASE_S(QType::SPF, "\"v=spf1 a:mail.rec.test ~all\"", "\x1bv=spf1 a:mail.rec.test ~all")) + + (CASE_S(QType::NID, "15 0123:4567:89AB:CDEF", "\x00\x0F\x01\x23\x45\x67\x89\xab\xcd\xef")) + (CASE_S(QType::NID, "15 2001:0DB8:1234:ABCD", "\x00\x0F\x20\x01\x0d\xb8\x12\x34\xab\xcd")) + (CASE_S(QType::L32, "513 192.0.2.1", "\x02\x01\xc0\x00\x02\x01")) + (CASE_S(QType::L64, "255 2001:0DB8:1234:ABCD", "\x00\xFF\x20\x01\x0d\xb8\x12\x34\xab\xcd")) + (CASE_S(QType::LP, "512 foo.powerdns.org.", "\x02\x00\3foo\x08powerdns\x03org\x00")) + (CASE_S(QType::EUI48, "00-11-22-33-44-55", "\x00\x11\x22\x33\x44\x55")) (CASE_S(QType::EUI64, "00-11-22-33-44-55-66-77", "\x00\x11\x22\x33\x44\x55\x66\x77")) (CASE_S(QType::TKEY, "gss-tsig. 12345 12345 3 21 4 dGVzdA== 4 dGVzdA==", "\x08gss-tsig\x00\x00\x00\x30\x39\x00\x00\x30\x39\x00\x03\x00\x15\x00\x04test\x00\x04test")) diff --git a/pdns/test-dnswriter_cc.cc b/pdns/test-dnswriter_cc.cc index 1c6588287a..dd939c0c19 100644 --- a/pdns/test-dnswriter_cc.cc +++ b/pdns/test-dnswriter_cc.cc @@ -298,4 +298,29 @@ BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_multiple) { 32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,2})); } +BOOST_AUTO_TEST_CASE(test_NodeOrLocatorID) { + DNSName name("powerdns.com."); + vector packet; + + NodeOrLocatorID in = {0, 0, 0, 0, 0, 0, 0, 1}; + + DNSPacketWriter writer(packet, name, QType::NID, QClass::IN, 0); + writer.getHeader()->qr = 1; + + writer.startRecord(name, QType::NID); + writer.commit(); + auto start = writer.getContent().size(); + + writer.xfrNodeOrLocatorID(in); + writer.commit(); + auto cit = writer.getContent().begin(); + for (size_t i = 0; i c(cit, writer.getContent().end()); + BOOST_CHECK(c == vector({ + 0, 0, 0, 0, + 0, 0, 0, 1})); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/pdns/test-rcpgenerator_cc.cc b/pdns/test-rcpgenerator_cc.cc index e7653f216c..b1738cf9d7 100644 --- a/pdns/test-rcpgenerator_cc.cc +++ b/pdns/test-rcpgenerator_cc.cc @@ -380,4 +380,24 @@ BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_echconfig) { BOOST_CHECK_EQUAL(source, target); } +BOOST_AUTO_TEST_CASE(test_xfrNodeOrLocatorID) { + string source("0000:0000:0000:0001"); + RecordTextReader rtr(source); + NodeOrLocatorID v; + rtr.xfrNodeOrLocatorID(v); + BOOST_CHECK_EQUAL(v[0], 0); + BOOST_CHECK_EQUAL(v[1], 0); + BOOST_CHECK_EQUAL(v[2], 0); + BOOST_CHECK_EQUAL(v[3], 0); + BOOST_CHECK_EQUAL(v[4], 0); + BOOST_CHECK_EQUAL(v[5], 0); + BOOST_CHECK_EQUAL(v[6], 0); + BOOST_CHECK_EQUAL(v[7], 1); + + string target; + RecordTextWriter rtw(target); + rtw.xfrNodeOrLocatorID(v); + BOOST_CHECK_EQUAL(source, target); +} + BOOST_AUTO_TEST_SUITE_END() -- 2.47.2