]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Allow seamless serving of newly-supported TYPExxx records
authorChris Hofstaedtler <chris.hofstaedtler@deduktiva.com>
Sat, 24 Oct 2020 15:45:52 +0000 (17:45 +0200)
committerChris Hofstaedtler <chris.hofstaedtler@deduktiva.com>
Mon, 26 Oct 2020 09:37:57 +0000 (10:37 +0100)
docs/settings.rst
modules/bindbackend/bindbackend2.cc
modules/bindbackend/bindbackend2.hh
pdns/backends/gsql/gsqlbackend.cc
pdns/backends/gsql/gsqlbackend.hh
pdns/common_startup.cc
pdns/dnsparser.cc
pdns/dnsparser.hh
pdns/pdnsutil.cc
pdns/zoneparser-tng.cc
pdns/zoneparser-tng.hh

index 7d610cddabdc6fa7c45b071cf142e35edae40814..e5bb54f655be1b7980855a263a4d7c1477f7d6ff 100644 (file)
@@ -1731,6 +1731,25 @@ of reflection attacks. Maximum value is 65535, but values above
   1232 is the largest number of payload bytes that can fit in the smallest IPv6 packet.
   IPv6 has a minimum MTU of 1280 bytes (:rfc:`RFC 8200, section 5 <8200#section-5>`), minus 40 bytes for the IPv6 header, minus 8 bytes for the UDP header gives 1232, the maximum payload size for the DNS response.
 
+.. _setting-upgrade-unknown-types:
+
+``upgrade-unknown-types``
+-------------------------
+
+-  Boolean
+-  Default: no
+
+.. versionadded:: 4.4.0
+
+Transparently upgrade records stored as `TYPE#xxx` and RFC 3597 (hex format)
+contents, if the type is natively supported.
+When this is disabled, records stored in this format cannot be served.
+
+Recommendation: keep disabled for better performance.
+Enable for testing PowerDNS upgrades, without changing stored records.
+
+This option is supported by the bind and Generic SQL backends. 
+
 .. _setting-version-string:
 
 ``version-string``
index a42768c584a98ffe7d68aad594d653fc0c795609..0c71c7ed4dcc5c10df2bb100e0928827708880be 100644 (file)
@@ -485,7 +485,7 @@ void Bind2Backend::parseZoneFile(BB2DomainInfo *bbd)
     nsec3zone=getNSEC3PARAM(bbd->d_name, &ns3pr);
 
   auto records = std::make_shared<recordstorage_t>();
-  ZoneParserTNG zpt(bbd->d_filename, bbd->d_name, s_binddirectory);
+  ZoneParserTNG zpt(bbd->d_filename, bbd->d_name, s_binddirectory, d_upgradeContent);
   zpt.setMaxGenerateSteps(::arg().asNum("max-generate-steps"));
   DNSResourceRecord rr;
   string hashed;
@@ -725,6 +725,7 @@ Bind2Backend::Bind2Backend(const string &suffix, bool loadZones)
   d_hybrid=mustDo("hybrid");
   d_transaction_id=0;
   s_ignore_broken_records=mustDo("ignore-broken-records");
+  d_upgradeContent=::arg().mustDo("upgrade-unknown-types");
 
   if (!loadZones && d_hybrid)
     return;
index aa38223bd0976ea739d047d813bb1ba40c7c1c5d..92d7d629a015d7954ba560b49c1a8f99a19f347d 100644 (file)
@@ -298,6 +298,7 @@ private:
   int d_transaction_id;
   static bool s_ignore_broken_records;
   bool d_hybrid;
+  bool d_upgradeContent;
 
   BB2DomainInfo createDomainEntry(const DNSName& domain, const string &filename); //!< does not insert in s_state
 
index 241bb818d128205a8336c457745d28ee526a8baa..1d6833a3a2478b01b5436b1be839d2f365ae5b8e 100644 (file)
@@ -44,15 +44,20 @@ GSQLBackend::GSQLBackend(const string &mode, const string &suffix)
   d_db = nullptr;
   d_logprefix="["+mode+"Backend"+suffix+"] ";
 
-  try
-  {
+  try {
     d_dnssecQueries = mustDo("dnssec");
   }
-  catch (const ArgException&)
-  {
+  catch (const ArgException&) {
     d_dnssecQueries = false;
   }
 
+  try {
+    d_upgradeContent = ::arg().mustDo("upgrade-unknown-types");
+  }
+  catch (const ArgException&) {
+    d_upgradeContent = false;
+  }
+
   d_NoIdQuery=getArg("basic-query");
   d_IdQuery=getArg("id-query");
   d_ANYNoIdQuery=getArg("any-query");
@@ -1848,7 +1853,10 @@ void GSQLBackend::extractRecord(SSqlStatement::row_t& row, DNSResourceRecord& r)
 
   r.qtype=row[3];
 
-  if (r.qtype==QType::MX || r.qtype==QType::SRV) {
+  if (d_upgradeContent && DNSRecordContent::isUnknownType(row[3])) {
+    r.content = DNSRecordContent::upgradeContent(r.qname, r.qtype, row[0]);
+  }
+  else if (r.qtype==QType::MX || r.qtype==QType::SRV) {
     r.content.reserve(row[2].size() + row[0].size() + 1);
     r.content=row[2]+" "+row[0];
   }
index 563082f92dce273fcfbf795c101cadd9a4084f9b..6f7c5c53681e13f6718694394ef4e217804e6c3b 100644 (file)
@@ -411,4 +411,5 @@ protected:
   std::unique_ptr<SSql> d_db{nullptr};
   bool d_dnssecQueries;
   bool d_inTransaction{false};
+  bool d_upgradeContent{false};
 };
index f73df9d002a88cfd9c415269614138741bbf6286..3843ea43891460b21330d63ac02e96a67195e33d 100644 (file)
@@ -238,6 +238,7 @@ void declareArguments()
   ::arg().set("tcp-fast-open", "Enable TCP Fast Open support on the listening sockets, using the supplied numerical value as the queue size")="0";
 
   ::arg().set("max-generate-steps", "Maximum number of $GENERATE steps when loading a zone from a file")="0";
+  ::arg().setSwitch("upgrade-unknown-types","Transparently upgrade known TYPExxx records. Recommended to keep off, except for PowerDNS upgrades until data sources are cleaned up")="no";
 
   ::arg().set("rng", "Specify the random number generator to use. Valid values are auto,sodium,openssl,getrandom,arc4random,urandom.")="auto";
   ::arg().setDefaults();
index 3f84fa91cfd15d8ae18ff3c45a24f16a4a776bd1..c60dade1b60da28397f2bb4e92775e360c45c72e 100644 (file)
 
 #include "namespaces.hh"
 
-class UnknownRecordContent : public DNSRecordContent
+UnknownRecordContent::UnknownRecordContent(const string& zone) 
 {
-public:
-  UnknownRecordContent(const DNSRecord& dr, PacketReader& pr) 
-    : d_dr(dr)
-  {
-    pr.copyRecord(d_record, dr.d_clen);
+  // parse the input
+  vector<string> parts;
+  stringtok(parts, zone);
+  // we need exactly 3 parts, except if the length field is set to 0 then we only need 2
+  if (parts.size() != 3 && !(parts.size() == 2 && equals(parts.at(1), "0"))) {
+    throw MOADNSException("Unknown record was stored incorrectly, need 3 fields, got " + std::to_string(parts.size()) + ": " + zone);
   }
 
-  UnknownRecordContent(const string& zone) 
-  {
-    // parse the input
-    vector<string> parts;
-    stringtok(parts, zone);
-    // we need exactly 3 parts, except if the length field is set to 0 then we only need 2
-    if (parts.size() != 3 && !(parts.size() == 2 && equals(parts.at(1), "0"))) {
-      throw MOADNSException("Unknown record was stored incorrectly, need 3 fields, got " + std::to_string(parts.size()) + ": " + zone);
-    }
-
-    if (parts.at(0) != "\\#") {
-      throw MOADNSException("Unknown record was stored incorrectly, first part should be '\\#', got '" + parts.at(0) + "'");
-    }
+  if (parts.at(0) != "\\#") {
+    throw MOADNSException("Unknown record was stored incorrectly, first part should be '\\#', got '" + parts.at(0) + "'");
+  }
 
-    const string& relevant = (parts.size() > 2) ? parts.at(2) : "";
-    unsigned int total = pdns_stou(parts.at(1));
-    if (relevant.size() % 2 || (relevant.size() / 2) != total) {
-      throw MOADNSException((boost::format("invalid unknown record length: size not equal to length field (%d != 2 * %d)") % relevant.size() % total).str());
-    }
+  const string& relevant = (parts.size() > 2) ? parts.at(2) : "";
+  unsigned int total = pdns_stou(parts.at(1));
+  if (relevant.size() % 2 || (relevant.size() / 2) != total) {
+    throw MOADNSException((boost::format("invalid unknown record length: size not equal to length field (%d != 2 * %d)") % relevant.size() % total).str());
+  }
 
-    string out;
-    out.reserve(total + 1);
+  string out;
+  out.reserve(total + 1);
 
-    for (unsigned int n = 0; n < total; ++n) {
-      int c;
-      if (sscanf(&relevant.at(2*n), "%02x", &c) != 1) {
-        throw MOADNSException("unable to read data at position " + std::to_string(2 * n) + " from unknown record of size " + std::to_string(relevant.size()));
-      }
-      out.append(1, (char)c);
+  for (unsigned int n = 0; n < total; ++n) {
+    int c;
+    if (sscanf(&relevant.at(2*n), "%02x", &c) != 1) {
+      throw MOADNSException("unable to read data at position " + std::to_string(2 * n) + " from unknown record of size " + std::to_string(relevant.size()));
     }
-
-    d_record.insert(d_record.end(), out.begin(), out.end());
+    out.append(1, (char)c);
   }
 
-  string getZoneRepresentation(bool noDot) const override
-  {
-    ostringstream str;
-    str<<"\\# "<<(unsigned int)d_record.size()<<" ";
-    char hex[4];
-    for(size_t n=0; n<d_record.size(); ++n) {
-      snprintf(hex, sizeof(hex), "%02x", d_record.at(n));
-      str << hex;
-    }
-    return str.str();
-  }
+  d_record.insert(d_record.end(), out.begin(), out.end());
+}
 
-  void toPacket(DNSPacketWriter& pw) override
-  {
-    pw.xfrBlob(string(d_record.begin(),d_record.end()));
+string UnknownRecordContent::getZoneRepresentation(bool noDot) const
+{
+  ostringstream str;
+  str<<"\\# "<<(unsigned int)d_record.size()<<" ";
+  char hex[4];
+  for (size_t n=0; n<d_record.size(); ++n) {
+    snprintf(hex, sizeof(hex), "%02x", d_record.at(n));
+    str << hex;
   }
+  return str.str();
+}
 
-  uint16_t getType() const override
-  {
-    return d_dr.d_type;
-  }
-private:
-  DNSRecord d_dr;
-  vector<uint8_t> d_record;
-};
+void UnknownRecordContent::toPacket(DNSPacketWriter& pw)
+{
+  pw.xfrBlob(string(d_record.begin(),d_record.end()));
+}
 
 shared_ptr<DNSRecordContent> DNSRecordContent::deserialize(const DNSName& qname, uint16_t qtype, const string& serialized)
 {
@@ -177,6 +159,12 @@ std::shared_ptr<DNSRecordContent> DNSRecordContent::mastermake(const DNSRecord &
   return i->second(dr, pr);
 }
 
+string DNSRecordContent::upgradeContent(const DNSName& qname, const QType qtype, const string& content) {
+  // seamless upgrade for previously unsupported but now implemented types.
+  UnknownRecordContent unknown_content(content);
+  shared_ptr<DNSRecordContent> rc = DNSRecordContent::deserialize(qname, qtype.getCode(), unknown_content.serialize(qname));
+  return rc->getZoneRepresentation();
+}
 
 DNSRecordContent::typemap_t& DNSRecordContent::getTypemap()
 {
index ea0d2568d3db5f7886fcf82587671d8627c1ae62..48010f1d3e53f7a3e28b41461a97a9462762362a 100644 (file)
@@ -51,7 +51,6 @@
     And we might be able to reverse 2 -> 3 as well
 */
     
-#include "namespaces.hh"
 #include "namespaces.hh"
 
 class MOADNSException : public runtime_error
@@ -195,6 +194,7 @@ public:
   static std::shared_ptr<DNSRecordContent> mastermake(const DNSRecord &dr, PacketReader& pr);
   static std::shared_ptr<DNSRecordContent> mastermake(const DNSRecord &dr, PacketReader& pr, uint16_t opcode);
   static std::shared_ptr<DNSRecordContent> mastermake(uint16_t qtype, uint16_t qclass, const string& zone);
+  static string upgradeContent(const DNSName& qname, const QType qtype, const string& content);
 
   virtual std::string getZoneRepresentation(bool noDot=false) const = 0;
   virtual ~DNSRecordContent() {}
@@ -247,13 +247,18 @@ public:
     getZmakermap().erase(key);
   }
 
+  static bool isUnknownType(const string& name)
+  {
+    return boost::starts_with(name, "TYPE") || boost::starts_with(name, "type");
+  }
+
   static uint16_t TypeToNumber(const string& name)
   {
     n2typemap_t::const_iterator iter = getN2Typemap().find(toUpper(name));
     if(iter != getN2Typemap().end())
       return iter->second.second;
     
-    if(boost::starts_with(name, "TYPE") || boost::starts_with(name, "type"))
+    if (isUnknownType(name))
       return (uint16_t) pdns_stou(name.substr(4));
     
     throw runtime_error("Unknown DNS type '"+name+"'");
@@ -357,6 +362,28 @@ struct DNSZoneRecord
   DNSRecord dr;
 };
 
+class UnknownRecordContent : public DNSRecordContent
+{
+public:
+  UnknownRecordContent(const DNSRecord& dr, PacketReader& pr)
+    : d_dr(dr)
+  {
+    pr.copyRecord(d_record, dr.d_clen);
+  }
+
+  UnknownRecordContent(const string& zone);
+
+  string getZoneRepresentation(bool noDot) const override;
+  void toPacket(DNSPacketWriter& pw) override;
+  uint16_t getType() const override
+  {
+    return d_dr.d_type;
+  }
+
+private:
+  DNSRecord d_dr;
+  vector<uint8_t> d_record;
+};
 
 //! This class can be used to parse incoming packets, and is copyable
 class MOADNSParser : public boost::noncopyable
index b2b5c0f3bbb6c40eaa0d03e972efd36fbb8b8dc7..597d94ba76cbd6a38f268afd33033ba968c5ac0d 100644 (file)
@@ -102,6 +102,7 @@ static void loadMainConfig(const std::string& configdir)
   ::arg().set("max-signature-cache-entries", "Maximum number of signatures cache entries")="";
   ::arg().set("rng", "Specify random number generator to use. Valid values are auto,sodium,openssl,getrandom,arc4random,urandom.")="auto";
   ::arg().set("max-generate-steps", "Maximum number of $GENERATE steps when loading a zone from a file")="0";
+  ::arg().setSwitch("upgrade-unknown-types","Transparently upgrade known TYPExxx records. Recommended to keep off, except for PowerDNS upgrades until data sources are cleaned up")="no";
   ::arg().laxFile(configname.c_str());
 
   if(!::arg()["load-modules"].empty()) {
index a334a5cb8c5dd484135cf9871676068337e3731d..9c171c6164ed16914b64256cb8d475abc9a8d595 100644 (file)
 
 static string g_INstr("IN");
 
-ZoneParserTNG::ZoneParserTNG(const string& fname, const DNSName& zname, const string& reldir) : d_reldir(reldir), 
-                                                                                               d_zonename(zname), d_defaultttl(3600), 
-                                                                                               d_templatecounter(0), d_templatestop(0),
-                                                                                               d_templatestep(0), d_havedollarttl(false){
+ZoneParserTNG::ZoneParserTNG(const string& fname, const DNSName& zname, const string& reldir, bool upgradeContent):
+  d_reldir(reldir), d_zonename(zname), d_defaultttl(3600), 
+  d_templatecounter(0), d_templatestop(0), d_templatestep(0),
+  d_havedollarttl(false), d_fromfile(true), d_upgradeContent(upgradeContent)
+{
   stackFile(fname);
 }
 
-ZoneParserTNG::ZoneParserTNG(const vector<string> zonedata, const DNSName& zname):
+ZoneParserTNG::ZoneParserTNG(const vector<string> zonedata, const DNSName& zname, bool upgradeContent):
   d_zonename(zname), d_zonedata(zonedata), d_defaultttl(3600),
   d_templatecounter(0), d_templatestop(0), d_templatestep(0),
-  d_havedollarttl(false), d_fromfile(false)
+  d_havedollarttl(false), d_fromfile(false), d_upgradeContent(upgradeContent)
 {
   d_zonedataline = d_zonedata.begin();
 }
@@ -387,7 +388,8 @@ bool ZoneParserTNG::get(DNSResourceRecord& rr, std::string* comment)
   string nextpart;
   
   rr.ttl=d_defaultttl;
-  bool haveTTL=0, haveQTYPE=0;
+  bool haveTTL{false}, haveQTYPE{false};
+  string qtypeString;
   pair<string::size_type, string::size_type> range;
 
   while(!d_parts.empty()) {
@@ -419,9 +421,10 @@ bool ZoneParserTNG::get(DNSResourceRecord& rr, std::string* comment)
       break;
 
     try {
-      rr.qtype=DNSRecordContent::TypeToNumber(nextpart);
+      rr.qtype = DNSRecordContent::TypeToNumber(nextpart);
       // cout<<"Got qtype ("<<rr.qtype.getCode()<<")\n";
-      haveQTYPE=1;
+      qtypeString = nextpart;
+      haveQTYPE = true;
       continue;
     }
     catch(...) {
@@ -457,6 +460,10 @@ bool ZoneParserTNG::get(DNSResourceRecord& rr, std::string* comment)
   }
   trim_if(rr.content, is_any_of(" \r\n\t\x1a"));
 
+  if (d_upgradeContent && DNSRecordContent::isUnknownType(qtypeString)) {
+    rr.content = DNSRecordContent::upgradeContent(rr.qname, rr.qtype, rr.content);
+  }
+
   vector<string> recparts;
   switch(rr.qtype.getCode()) {
   case QType::MX:
index 4b16a465656a236ff9fa2c8043ca509dcee0db9e..1f3fad432267904bfc59dffaf62d61c2e3768794 100644 (file)
@@ -30,8 +30,8 @@
 class ZoneParserTNG
 {
 public:
-  ZoneParserTNG(const string& fname, const DNSName& zname=g_rootdnsname, const string& reldir="");
-  ZoneParserTNG(const vector<string> zonedata, const DNSName& zname);
+  ZoneParserTNG(const string& fname, const DNSName& zname=g_rootdnsname, const string& reldir="", bool upgradeContent=false);
+  ZoneParserTNG(const vector<string> zonedata, const DNSName& zname, bool upgradeContent=false);
 
   ~ZoneParserTNG();
   bool get(DNSResourceRecord& rr, std::string* comment=0);
@@ -77,4 +77,5 @@ private:
   bool d_havedollarttl;
   bool d_fromfile;
   bool d_generateEnabled{true};
+  bool d_upgradeContent;
 };