From: Chris Hofstaedtler Date: Sat, 24 Oct 2020 15:45:52 +0000 (+0200) Subject: Allow seamless serving of newly-supported TYPExxx records X-Git-Tag: auth-4.4.0-alpha2~14^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b4db4fe4d9421f696d945507bbeac8347d8f2f20;p=thirdparty%2Fpdns.git Allow seamless serving of newly-supported TYPExxx records --- diff --git a/docs/settings.rst b/docs/settings.rst index 7d610cddab..e5bb54f655 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -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`` diff --git a/modules/bindbackend/bindbackend2.cc b/modules/bindbackend/bindbackend2.cc index a42768c584..0c71c7ed4d 100644 --- a/modules/bindbackend/bindbackend2.cc +++ b/modules/bindbackend/bindbackend2.cc @@ -485,7 +485,7 @@ void Bind2Backend::parseZoneFile(BB2DomainInfo *bbd) nsec3zone=getNSEC3PARAM(bbd->d_name, &ns3pr); auto records = std::make_shared(); - 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; diff --git a/modules/bindbackend/bindbackend2.hh b/modules/bindbackend/bindbackend2.hh index aa38223bd0..92d7d629a0 100644 --- a/modules/bindbackend/bindbackend2.hh +++ b/modules/bindbackend/bindbackend2.hh @@ -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 diff --git a/pdns/backends/gsql/gsqlbackend.cc b/pdns/backends/gsql/gsqlbackend.cc index 241bb818d1..1d6833a3a2 100644 --- a/pdns/backends/gsql/gsqlbackend.cc +++ b/pdns/backends/gsql/gsqlbackend.cc @@ -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]; } diff --git a/pdns/backends/gsql/gsqlbackend.hh b/pdns/backends/gsql/gsqlbackend.hh index 563082f92d..6f7c5c5368 100644 --- a/pdns/backends/gsql/gsqlbackend.hh +++ b/pdns/backends/gsql/gsqlbackend.hh @@ -411,4 +411,5 @@ protected: std::unique_ptr d_db{nullptr}; bool d_dnssecQueries; bool d_inTransaction{false}; + bool d_upgradeContent{false}; }; diff --git a/pdns/common_startup.cc b/pdns/common_startup.cc index f73df9d002..3843ea4389 100644 --- a/pdns/common_startup.cc +++ b/pdns/common_startup.cc @@ -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(); diff --git a/pdns/dnsparser.cc b/pdns/dnsparser.cc index 3f84fa91cf..c60dade1b6 100644 --- a/pdns/dnsparser.cc +++ b/pdns/dnsparser.cc @@ -26,74 +26,56 @@ #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 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 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; -}; +void UnknownRecordContent::toPacket(DNSPacketWriter& pw) +{ + pw.xfrBlob(string(d_record.begin(),d_record.end())); +} shared_ptr DNSRecordContent::deserialize(const DNSName& qname, uint16_t qtype, const string& serialized) { @@ -177,6 +159,12 @@ std::shared_ptr 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 rc = DNSRecordContent::deserialize(qname, qtype.getCode(), unknown_content.serialize(qname)); + return rc->getZoneRepresentation(); +} DNSRecordContent::typemap_t& DNSRecordContent::getTypemap() { diff --git a/pdns/dnsparser.hh b/pdns/dnsparser.hh index ea0d2568d3..48010f1d3e 100644 --- a/pdns/dnsparser.hh +++ b/pdns/dnsparser.hh @@ -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 mastermake(const DNSRecord &dr, PacketReader& pr); static std::shared_ptr mastermake(const DNSRecord &dr, PacketReader& pr, uint16_t opcode); static std::shared_ptr 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 d_record; +}; //! This class can be used to parse incoming packets, and is copyable class MOADNSParser : public boost::noncopyable diff --git a/pdns/pdnsutil.cc b/pdns/pdnsutil.cc index b2b5c0f3bb..597d94ba76 100644 --- a/pdns/pdnsutil.cc +++ b/pdns/pdnsutil.cc @@ -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()) { diff --git a/pdns/zoneparser-tng.cc b/pdns/zoneparser-tng.cc index a334a5cb8c..9c171c6164 100644 --- a/pdns/zoneparser-tng.cc +++ b/pdns/zoneparser-tng.cc @@ -39,17 +39,18 @@ 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 zonedata, const DNSName& zname): +ZoneParserTNG::ZoneParserTNG(const vector 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 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 ("< recparts; switch(rr.qtype.getCode()) { case QType::MX: diff --git a/pdns/zoneparser-tng.hh b/pdns/zoneparser-tng.hh index 4b16a46565..1f3fad4322 100644 --- a/pdns/zoneparser-tng.hh +++ b/pdns/zoneparser-tng.hh @@ -30,8 +30,8 @@ class ZoneParserTNG { public: - ZoneParserTNG(const string& fname, const DNSName& zname=g_rootdnsname, const string& reldir=""); - ZoneParserTNG(const vector zonedata, const DNSName& zname); + ZoneParserTNG(const string& fname, const DNSName& zname=g_rootdnsname, const string& reldir="", bool upgradeContent=false); + ZoneParserTNG(const vector 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; };