]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Implement RFC 6742 RR Types 10121/head
authorPieter Lexis <pieter.lexis@powerdns.com>
Thu, 25 Feb 2021 09:49:43 +0000 (10:49 +0100)
committerPieter Lexis <pieter.lexis@powerdns.com>
Wed, 31 Mar 2021 13:02:39 +0000 (15:02 +0200)
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

15 files changed:
docs/upgrading.rst
pdns/dnsparser.cc
pdns/dnsparser.hh
pdns/dnsrecords.cc
pdns/dnsrecords.hh
pdns/dnswriter.cc
pdns/dnswriter.hh
pdns/misc.hh
pdns/qtype.cc
pdns/qtype.hh
pdns/rcpgenerator.cc
pdns/rcpgenerator.hh
pdns/test-dnsrecords_cc.cc
pdns/test-dnswriter_cc.cc
pdns/test-rcpgenerator_cc.cc

index edfd248f72043e0a175fc07017a0f478aa069767..d99094bdc6a313ed11f9acd9b9ca741c47693fbd 100644 (file)
@@ -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.
index 1c9f92dc3f33854fa592a09a2c9b984bb0dcf77b..1a1fbde99e3a956f3be125bbaa4dc37d4d3a86ee 100644 (file)
@@ -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;
index ad2aff9f8032aaf4f8aa61b633bdf64055874554..fcc63cad8603dccb0cf9159dcd8d24bedcaab17c 100644 (file)
@@ -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)
index 6bb5d7e14f8f70c716aae35c4c373c077afaf92f..e953aebe94180bc349ecdcfd644fc942b3df394f 100644 (file)
@@ -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()
index 8d2cea1014da348cbfc89e1e6ce1c4f4a794f00b..abee42ed64b1885edf90c63d0c7a312af37871bd 100644 (file)
@@ -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:
index c106ee6f903567f2540f722af8b0efa7f5feaeb8..775793dcd0f122af75a66df0add0cae3ee24fc78 100644 (file)
@@ -140,6 +140,10 @@ template <typename Container> void GenericDNSPacketWriter<Container>::xfr48BitIn
   d_content.insert(d_content.end(), bytes, bytes + sizeof(bytes));
 }
 
+template <typename Container> void GenericDNSPacketWriter<Container>::xfrNodeOrLocatorID(NodeOrLocatorID val)
+{
+  d_content.insert(d_content.end(), val, val + sizeof(val));
+}
 
 template <typename Container> void GenericDNSPacketWriter<Container>::xfr32BitInt(uint32_t val)
 {
index d5596a3a2a05470849fe876417ad349ba8712b0d..96e8d4f9a227d7a3ea24eb2883394e5f113f4a86 100644 (file)
@@ -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)
index 193750446ca2573ba77ebed3fb24dc11c3c7d675..fd377cfc4a667fcb34be05f7b169ba8d131dfd0f 100644 (file)
@@ -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];
index 7c23075b979700b5e46e855cb714a0535a21c050..a3f9d0849ef3283ebf4d3eb0b4404d1c9f5fff5e 100644 (file)
@@ -75,6 +75,10 @@ const map<const string, uint16_t> QType::names = {
   {"SVCB", 64},
   {"HTTPS", 65},
   {"SPF", 99},
+  {"NID", 104},
+  {"L32", 105},
+  {"L64", 106},
+  {"LP", 107},
   {"EUI48", 108},
   {"EUI64", 109},
   {"TKEY", 249},
index bcb02e2dd933614cc3af2d4059ec29e1e10b39d0..cabf4998401383b4138cecef830886aacd3a4f18 100644 (file)
@@ -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,
index c9cd1afa3deaf17b60a00b4009ab0c5d10af558e..ea9580d5a0bdd6edb4c335f64cac5c66015ebdb5 100644 (file)
@@ -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())
index d824dac8652f5bcbe2b55005dff93dfe2c994855..826e829961bc0971e1223cb6762cc24286da27df 100644 (file)
@@ -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);
index 22f87700efe290708c5901c24239f2dcb2d2eccb..bf521f197084b67fe0c27d6e4e2e7197b595d292 100644 (file)
@@ -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"))
index 1c6588287ac67a11558a32d2dfcac3f94f4a8788..dd939c0c1915c072c27f5513d9be2b78e619a138 100644 (file)
@@ -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<uint8_t> 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<start; i++)
+    cit++;
+
+  vector<uint8_t> c(cit, writer.getContent().end());
+  BOOST_CHECK(c == vector<uint8_t>({
+    0, 0, 0, 0,
+    0, 0, 0, 1}));
+}
+
 BOOST_AUTO_TEST_SUITE_END()
index e7653f216c8e1a7e9834c404c7fd21d3f597f3f8..b1738cf9d7139abdc4caa3550e8585d521c182eb 100644 (file)
@@ -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()