From 4d334d85e03f41b2e32e34818baae7c03736c106 Mon Sep 17 00:00:00 2001 From: Chris Hofstaedtler Date: Tue, 27 Aug 2024 21:28:36 +0200 Subject: [PATCH 1/4] auth: move checkForCorrectTSIG into packethandler To avoid DNSPacket pulling UeberBackend. --- pdns/dnspacket.cc | 48 ++++++++++--------------------------------- pdns/dnspacket.hh | 5 +---- pdns/packethandler.cc | 45 +++++++++++++++++++++++++++++++++++----- pdns/packethandler.hh | 1 + pdns/tcpreceiver.cc | 18 ++++++++-------- 5 files changed, 62 insertions(+), 55 deletions(-) diff --git a/pdns/dnspacket.cc b/pdns/dnspacket.cc index e926a658b408..4ea1b5048374 100644 --- a/pdns/dnspacket.cc +++ b/pdns/dnspacket.cc @@ -535,6 +535,17 @@ bool DNSPacket::getTSIGDetails(TSIGRecordContent* trc, DNSName* keyname, uint16_ return true; } +bool DNSPacket::validateTSIG(const TSIGTriplet& tsigTriplet, const TSIGRecordContent& tsigContent, const std::string& previousMAC, const std::string& theirMAC, bool timersOnly) const +{ + MOADNSParser mdp(d_isQuery, d_rawpacket); + uint16_t tsigPos = mdp.getTSIGPos(); + if (tsigPos == 0) { + return false; + } + + return ::validateTSIG(d_rawpacket, tsigPos, tsigTriplet, tsigContent, previousMAC, theirMAC, timersOnly); +} + bool DNSPacket::getTKEYRecord(TKEYRecordContent *tr, DNSName *keyname) const { MOADNSParser mdp(d_isQuery, d_rawpacket); @@ -724,43 +735,6 @@ void DNSPacket::commitD() d_rawpacket.replace(0,12,(char *)&d,12); // copy in d } -bool DNSPacket::checkForCorrectTSIG(UeberBackend* B, DNSName* keyname, string* secret, TSIGRecordContent* trc) const -{ - uint16_t tsigPos; - - if (!this->getTSIGDetails(trc, keyname, &tsigPos)) { - return false; - } - - TSIGTriplet tt; - tt.name = *keyname; - tt.algo = trc->d_algoName; - if (tt.algo == DNSName("hmac-md5.sig-alg.reg.int")) - tt.algo = DNSName("hmac-md5"); - - if (tt.algo != DNSName("gss-tsig")) { - string secret64; - if(!B->getTSIGKey(*keyname, tt.algo, secret64)) { - g_log << Logger::Error << "Packet for domain '" << this->qdomain << "' denied: can't find TSIG key with name '" << *keyname << "' and algorithm '" << tt.algo << "'" << endl; - return false; - } - B64Decode(secret64, *secret); - tt.secret = *secret; - } - - bool result; - - try { - result = validateTSIG(d_rawpacket, tsigPos, tt, *trc, "", trc->d_mac, false); - } - catch(const std::runtime_error& err) { - g_log<qdomain<<"' denied: "<& getRRS() { return d_rrs; } - bool checkForCorrectTSIG(UeberBackend* B, DNSName* keyname, string* secret, TSIGRecordContent* trc) const; static uint16_t s_udpTruncationThreshold; static bool s_doEDNSSubnetProcessing; diff --git a/pdns/packethandler.cc b/pdns/packethandler.cc index 3d385ed7b54c..b8aa267ddcb4 100644 --- a/pdns/packethandler.cc +++ b/pdns/packethandler.cc @@ -25,6 +25,7 @@ #include "packetcache.hh" #include "utility.hh" #include "base32.hh" +#include "base64.hh" #include #include #include @@ -1394,10 +1395,10 @@ std::unique_ptr PacketHandler::doQuestion(DNSPacket& p) } if(p.d_havetsig) { - DNSName keyname; + DNSName tsigkeyname; string secret; TSIGRecordContent trc; - if(!p.checkForCorrectTSIG(&B, &keyname, &secret, &trc)) { + if (!checkForCorrectTSIG(p, &tsigkeyname, &secret, &trc)) { r=p.replyPacket(); // generate an empty reply packet if(d_logDNSDetails) g_log< PacketHandler::doQuestion(DNSPacket& p) getTSIGHashEnum(trc.d_algoName, p.d_tsig_algo); #ifdef ENABLE_GSS_TSIG if (g_doGssTSIG && p.d_tsig_algo == TSIG_GSS) { - GssContext gssctx(keyname); + GssContext gssctx(tsigkeyname); if (!gssctx.getPeerPrincipal(p.d_peer_principal)) { - g_log< PacketHandler::doQuestion(DNSPacket& p) return r; } + +bool PacketHandler::checkForCorrectTSIG(const DNSPacket& packet, DNSName* tsigkeyname, string* secret, TSIGRecordContent* tsigContent) +{ + uint16_t tsigPos{0}; + + if (!packet.getTSIGDetails(tsigContent, tsigkeyname, &tsigPos)) { + return false; + } + + TSIGTriplet tsigTriplet; + tsigTriplet.name = *tsigkeyname; + tsigTriplet.algo = tsigContent->d_algoName; + if (tsigTriplet.algo == DNSName("hmac-md5.sig-alg.reg.int")) { + tsigTriplet.algo = DNSName("hmac-md5"); + } + + if (tsigTriplet.algo != DNSName("gss-tsig")) { + string secret64; + if (!B.getTSIGKey(*tsigkeyname, tsigTriplet.algo, secret64)) { + g_log << Logger::Error << "Packet for domain '" << packet.qdomain << "' denied: can't find TSIG key with name '" << *tsigkeyname << "' and algorithm '" << tsigTriplet.algo << "'" << endl; + return false; + } + B64Decode(secret64, *secret); + tsigTriplet.secret = *secret; + } + + try { + return packet.validateTSIG(tsigTriplet, *tsigContent, "", tsigContent->d_mac, false); + } + catch(const std::runtime_error& err) { + g_log< s_forwardNotify; static bool s_SVCAutohints; diff --git a/pdns/tcpreceiver.cc b/pdns/tcpreceiver.cc index 8dfa95731588..c4696a8f72cc 100644 --- a/pdns/tcpreceiver.cc +++ b/pdns/tcpreceiver.cc @@ -463,18 +463,18 @@ bool TCPNameserver::canDoAXFR(std::unique_ptr& q, bool isAXFR, std::u string logPrefix=string(isAXFR ? "A" : "I")+"XFR-out zone '"+q->qdomain.toLogString()+"', client '"+q->getInnerRemote().toStringWithPort()+"', "; if(q->d_havetsig) { // if you have one, it must be good - TSIGRecordContent trc; - DNSName keyname; + TSIGRecordContent tsigContent; + DNSName tsigkeyname; string secret; - if(!q->checkForCorrectTSIG(packetHandler->getBackend(), &keyname, &secret, &trc)) { + if (!packetHandler->checkForCorrectTSIG(*q, &tsigkeyname, &secret, &tsigContent)) { return false; } else { - getTSIGHashEnum(trc.d_algoName, q->d_tsig_algo); + getTSIGHashEnum(tsigContent.d_algoName, q->d_tsig_algo); #ifdef ENABLE_GSS_TSIG if (g_doGssTSIG && q->d_tsig_algo == TSIG_GSS) { - GssContext gssctx(keyname); + GssContext gssctx(tsigkeyname); if (!gssctx.getPeerPrincipal(q->d_peer_principal)) { - g_log<& q, bool isAXFR, std::u return false; } #endif - if(!dk.TSIGGrantsAccess(q->qdomain, keyname)) { - g_log<d_tsig_algo)<<"' does not grant access"<qdomain, tsigkeyname)) { + g_log<d_tsig_algo)<<"' does not grant access"<d_tsig_algo)<<"'"<d_tsig_algo)<<"'"< Date: Tue, 27 Aug 2024 15:10:04 +0200 Subject: [PATCH 2/4] Revert "auth dnsproxy: make local port range configurable" This reverts commit 60157e5f88b15ca2c2edd5d81612d525462a895b. --- docs/settings.rst | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index 1c22a24375b4..7545299cf242 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -634,20 +634,6 @@ approximately doubles query load. If this is turned off, DNAME records are treated as any other and served only when queried explicitly. -.. _setting-dnsproxy-udp-port-range: - -``dnsproxy-udp-port-range`` ---------------------------- - -- String -- Default: `10000 60000` - -If :ref:`setting-resolver` enables the DNS Proxy, this setting limits the -port range the DNS Proxy's UDP port is chosen from. - -Default should be fine on most installs, but if you have conflicting local -services, you may choose to limit the range. - .. _setting-dnssec-key-cache-ttl: ``dnssec-key-cache-ttl`` From 312119e115730b1e6c55d25090aebc1ea452efae Mon Sep 17 00:00:00 2001 From: Chris Hofstaedtler Date: Tue, 27 Aug 2024 14:25:43 +0200 Subject: [PATCH 3/4] Use stubDoResolve for ALIAS --- meson.build | 2 - pdns/Makefile.am | 37 ++++- pdns/auth-main.cc | 7 - pdns/auth-main.hh | 2 - pdns/dnsproxy.cc | 362 ------------------------------------------ pdns/dnsproxy.hh | 85 ---------- pdns/packethandler.cc | 84 +++++++--- pdns/stubresolver.cc | 11 +- pdns/stubresolver.hh | 4 +- 9 files changed, 105 insertions(+), 489 deletions(-) delete mode 100644 pdns/dnsproxy.cc delete mode 100644 pdns/dnsproxy.hh diff --git a/meson.build b/meson.build index 0b426d1875cb..ba5112139bde 100644 --- a/meson.build +++ b/meson.build @@ -533,8 +533,6 @@ common_sources += files( src_dir / 'dnspacket.hh', src_dir / 'dnsparser.cc', src_dir / 'dnsparser.hh', - src_dir / 'dnsproxy.cc', - src_dir / 'dnsproxy.hh', src_dir / 'dnsrecords.cc', src_dir / 'dnsrecords.hh', src_dir / 'dnssecinfra.cc', diff --git a/pdns/Makefile.am b/pdns/Makefile.am index bee1d58ef06d..17b2c0c80938 100644 --- a/pdns/Makefile.am +++ b/pdns/Makefile.am @@ -225,7 +225,6 @@ pdns_server_SOURCES = \ dnsname.cc dnsname.hh \ dnspacket.cc dnspacket.hh \ dnsparser.cc \ - dnsproxy.cc dnsproxy.hh \ dnsrecords.cc dnsrecords.hh \ dnssecinfra.cc dnssecinfra.hh \ dnsseckeeper.hh \ @@ -595,7 +594,7 @@ sdig_LDADD += $(LIBSSL_LIBS) endif if LIBSODIUM -sdig_CPPFLAGS +=$(LIBSODIUM_CFLAGS) +sdig_CPPFLAGS += $(LIBSODIUM_CFLAGS) sdig_LDADD += $(LIBSODIUM_LIBS) endif @@ -656,28 +655,49 @@ stubquery_SOURCES = \ arguments.cc arguments.hh \ base32.cc \ base64.cc \ + dns.cc dns.hh \ dnslabeltext.cc \ dnsname.cc \ + dnspacket.cc \ dnsparser.cc \ dnsrecords.cc \ + dnssecinfra.cc dnssecinfra.hh \ dnswriter.cc \ + ednscookies.cc ednscookies.hh \ ednsoptions.cc ednsoptions.hh \ ednssubnet.cc ednssubnet.hh \ + gss_context.cc gss_context.hh \ iputils.cc \ logger.cc \ misc.cc \ nsecrecords.cc \ qtype.cc \ rcpgenerator.cc \ + shuffle.cc shuffle.hh \ sillyrecords.cc \ statbag.cc \ stubquery.cc \ stubresolver.cc stubresolver.hh \ svc-records.cc svc-records.hh \ + tsigverifier.cc tsigverifier.hh \ unix_utility.cc +stubquery_CPPFLAGS = $(AM_CPPFLAGS) stubquery_LDADD = $(LIBCRYPTO_LIBS) stubquery_LDFLAGS = $(AM_LDFLAGS) $(LIBCRYPTO_LDFLAGS) +if GSS_TSIG +stubquery_LDADD += $(GSS_LIBS) +endif + +if PKCS11 +stubquery_SOURCES += pkcs11signers.cc pkcs11signers.hh +stubquery_LDADD += $(P11KIT1_LIBS) +endif + +if LIBSODIUM +stubquery_CPPFLAGS += $(LIBSODIUM_CFLAGS) +stubquery_LDADD += $(LIBSODIUM_LIBS) +endif saxfr_SOURCES = \ base32.cc \ @@ -800,14 +820,16 @@ ixplore_SOURCES = \ axfr-retriever.cc \ base32.cc \ base64.cc base64.hh \ - dns.cc \ + dns.cc dns.hh \ dns_random.hh \ dnslabeltext.cc \ dnsname.cc dnsname.hh \ + dnspacket.cc dnspacket.hh \ dnsparser.cc dnsparser.hh \ dnsrecords.cc \ - dnssecinfra.cc \ + dnssecinfra.cc dnssecinfra.hh \ dnswriter.cc dnswriter.hh \ + ednscookies.cc ednscookies.hh \ ednsoptions.cc ednsoptions.hh \ ednssubnet.cc ednssubnet.hh \ gss_context.cc gss_context.hh \ @@ -822,6 +844,7 @@ ixplore_SOURCES = \ query-local-address.hh query-local-address.cc \ rcpgenerator.cc rcpgenerator.hh \ resolver.cc \ + shuffle.cc shuffle.hh \ sillyrecords.cc \ sstuff.hh \ statbag.cc \ @@ -829,6 +852,7 @@ ixplore_SOURCES = \ tsigverifier.cc tsigverifier.hh \ unix_utility.cc zoneparser-tng.cc +ixplore_CPPFLAGS = $(AM_CPPFLAGS) ixplore_LDADD = $(LIBCRYPTO_LIBS) ixplore_LDFLAGS = $(AM_LDFLAGS) $(LIBCRYPTO_LDFLAGS) if GSS_TSIG @@ -840,6 +864,11 @@ ixplore_SOURCES += pkcs11signers.cc pkcs11signers.hh ixplore_LDADD += $(P11KIT1_LIBS) endif +if LIBSODIUM +ixplore_CPPFLAGS += $(LIBSODIUM_CFLAGS) +ixplore_LDADD += $(LIBSODIUM_LIBS) +endif + dnstcpbench_SOURCES = \ base32.cc \ base64.cc base64.hh \ diff --git a/pdns/auth-main.cc b/pdns/auth-main.cc index 72e05c9e6e7f..6a83ca98a5ab 100644 --- a/pdns/auth-main.cc +++ b/pdns/auth-main.cc @@ -79,7 +79,6 @@ #include "dynlistener.hh" #include "dynhandler.hh" #include "communicator.hh" -#include "dnsproxy.hh" #include "utility.hh" #include "dnsrecords.hh" #include "version.hh" @@ -123,7 +122,6 @@ StatBag S; //!< Statistics are gathered across PDNS via the StatBag class S AuthPacketCache PC; //!< This is the main PacketCache, shared across all threads AuthQueryCache QC; AuthZoneCache g_zoneCache; -std::unique_ptr DP{nullptr}; static std::unique_ptr s_dynListener{nullptr}; CommunicatorClass Communicator; static double avg_latency{0.0}, receive_latency{0.0}, cache_latency{0.0}, backend_latency{0.0}, send_latency{0.0}; @@ -786,11 +784,6 @@ static void mainthread() AuthWebServer webserver; Utility::dropUserPrivs(newuid); - if (::arg().mustDo("resolver")) { - DP = std::make_unique(::arg()["resolver"], ::arg()["dnsproxy-udp-port-range"]); - DP->go(); - } - try { doSecPoll(true); } diff --git a/pdns/auth-main.hh b/pdns/auth-main.hh index bc3d90c0d77d..aea0dd3a3950 100644 --- a/pdns/auth-main.hh +++ b/pdns/auth-main.hh @@ -28,7 +28,6 @@ #include "communicator.hh" #include "distributor.hh" #include "dnspacket.hh" -#include "dnsproxy.hh" #include "dynlistener.hh" #include "nameserver.hh" #include "statbag.hh" @@ -40,7 +39,6 @@ extern ArgvMap theArg; extern StatBag S; //!< Statistics are gathered across PDNS via the StatBag class S extern AuthPacketCache PC; //!< This is the main PacketCache, shared across all threads extern AuthQueryCache QC; -extern std::unique_ptr DP; extern CommunicatorClass Communicator; void carbonDumpThread(); // Implemented in auth-carbon.cc. Avoids having an auth-carbon.hh declaring exactly one function. extern bool g_anyToTcp; diff --git a/pdns/dnsproxy.cc b/pdns/dnsproxy.cc deleted file mode 100644 index 8c14559c92ec..000000000000 --- a/pdns/dnsproxy.cc +++ /dev/null @@ -1,362 +0,0 @@ -/* - * This file is part of PowerDNS or dnsdist. - * Copyright -- PowerDNS.COM B.V. and its contributors - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * In addition, for the avoidance of any doubt, permission is granted to - * link this program with OpenSSL and to (re)distribute the binaries - * produced as the result of such linking. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include - -#include "packetcache.hh" -#include "utility.hh" -#include "dnsproxy.hh" -#include "pdnsexception.hh" -#include "dns.hh" -#include "logger.hh" -#include "statbag.hh" -#include "dns_random.hh" -#include "stubresolver.hh" -#include "arguments.hh" -#include "threadname.hh" -#include "ednsoptions.hh" -#include "ednssubnet.hh" - -#include - -extern StatBag S; - -DNSProxy::DNSProxy(const string& remote, const string& udpPortRange) : - d_xor(dns_random_uint16()) -{ - d_resanswers = S.getPointer("recursing-answers"); - d_resquestions = S.getPointer("recursing-questions"); - d_udpanswers = S.getPointer("udp-answers"); - - vector addresses; - stringtok(addresses, remote, " ,\t"); - d_remote = ComboAddress(addresses[0], 53); - - vector parts; - stringtok(parts, udpPortRange, " "); - if (parts.size() != 2) { - throw PDNSException("DNS Proxy UDP port range must contain exactly one lower and one upper bound"); - } - unsigned long portRangeLow = std::stoul(parts.at(0)); - unsigned long portRangeHigh = std::stoul(parts.at(1)); - if (portRangeLow < 1 || portRangeHigh > 65535) { - throw PDNSException("DNS Proxy UDP port range values out of valid port bounds (1 to 65535)"); - } - if (portRangeLow >= portRangeHigh) { - throw PDNSException("DNS Proxy UDP port range upper bound " + std::to_string(portRangeHigh) + " must be higher than lower bound (" + std::to_string(portRangeLow) + ")"); - } - - if ((d_sock = socket(d_remote.sin4.sin_family, SOCK_DGRAM, 0)) < 0) { - throw PDNSException(string("socket: ") + stringerror()); - } - - ComboAddress local; - if (d_remote.sin4.sin_family == AF_INET) { - local = ComboAddress("0.0.0.0"); - } - else { - local = ComboAddress("::"); - } - - unsigned int attempts = 0; - for (; attempts < 10; attempts++) { - local.sin4.sin_port = htons(portRangeLow + dns_random(portRangeHigh - portRangeLow)); - - if (::bind(d_sock, (struct sockaddr*)&local, local.getSocklen()) >= 0) { // NOLINT(cppcoreguidelines-pro-type-cstyle-cast) - break; - } - } - if (attempts == 10) { - closesocket(d_sock); - d_sock = -1; - throw PDNSException(string("binding dnsproxy socket: ") + stringerror()); - } - - if (connect(d_sock, (sockaddr*)&d_remote, d_remote.getSocklen()) < 0) { // NOLINT(cppcoreguidelines-pro-type-cstyle-cast) - throw PDNSException("Unable to UDP connect to remote nameserver " + d_remote.toStringWithPort() + ": " + stringerror()); - } - - g_log << Logger::Error << "DNS Proxy launched, local port " << ntohs(local.sin4.sin_port) << ", remote " << d_remote.toStringWithPort() << endl; -} - -void DNSProxy::go() -{ - std::thread proxythread([this]() { mainloop(); }); - proxythread.detach(); -} - -//! look up qname 'target' with reply->qtype, plonk it in the answer section of 'reply' with name 'aname' -bool DNSProxy::completePacket(std::unique_ptr& reply, const DNSName& target, const DNSName& aname, const uint8_t scopeMask) -{ - string ECSOptionStr; - - if (reply->hasEDNSSubnet()) { - DLOG(g_log << "dnsproxy::completePacket: Parsed edns source: " << reply->d_eso.source.toString() << ", scope: " << reply->d_eso.scope.toString() << ", family = " << reply->d_eso.scope.getNetwork().sin4.sin_family << endl); - ECSOptionStr = makeEDNSSubnetOptsString(reply->d_eso); - DLOG(g_log << "from dnsproxy::completePacket: Creating ECS option string " << makeHexDump(ECSOptionStr) << endl); - } - - if (reply->d_tcp) { - vector ips; - int ret1 = 0; - int ret2 = 0; - // rip out edns info here, pass it to the stubDoResolve - if (reply->qtype == QType::A || reply->qtype == QType::ANY) { - ret1 = stubDoResolve(target, QType::A, ips, reply->hasEDNSSubnet() ? &reply->d_eso : nullptr); - } - if (reply->qtype == QType::AAAA || reply->qtype == QType::ANY) { - ret2 = stubDoResolve(target, QType::AAAA, ips, reply->hasEDNSSubnet() ? &reply->d_eso : nullptr); - } - - if (ret1 != RCode::NoError || ret2 != RCode::NoError) { - g_log << Logger::Error << "Error resolving for " << aname << " ALIAS " << target << " over UDP, original query came in over TCP"; - if (ret1 != RCode::NoError) { - g_log << Logger::Error << ", A-record query returned " << RCode::to_s(ret1); - } - if (ret2 != RCode::NoError) { - g_log << Logger::Error << ", AAAA-record query returned " << RCode::to_s(ret2); - } - g_log << Logger::Error << ", returning SERVFAIL" << endl; - reply->clearRecords(); - reply->setRcode(RCode::ServFail); - } - else { - for (auto& ip : ips) { // NOLINT(readability-identifier-length) - ip.dr.d_name = aname; - reply->addRecord(std::move(ip)); - } - } - - uint16_t len = htons(reply->getString().length()); - string buffer((const char*)&len, 2); - buffer.append(reply->getString()); - writen2WithTimeout(reply->getSocket(), buffer.c_str(), buffer.length(), timeval{::arg().asNum("tcp-idle-timeout"), 0}); - - return true; - } - - uint16_t id; - uint16_t qtype = reply->qtype.getCode(); - { - auto conntrack = d_conntrack.lock(); - id = getID_locked(*conntrack); - - ConntrackEntry ce; - ce.id = reply->d.id; - ce.remote = reply->d_remote; - ce.outsock = reply->getSocket(); - ce.created = time(nullptr); - ce.qtype = reply->qtype.getCode(); - ce.qname = target; - ce.anyLocal = reply->d_anyLocal; - ce.complete = std::move(reply); - ce.aname = aname; - ce.anameScopeMask = scopeMask; - (*conntrack)[id] = std::move(ce); - } - - vector packet; - DNSPacketWriter pw(packet, target, qtype); - pw.getHeader()->rd = true; - pw.getHeader()->id = id ^ d_xor; - // Add EDNS Subnet if the client sent one - issue #5469 - if (!ECSOptionStr.empty()) { - DLOG(g_log << "from dnsproxy::completePacket: adding ECS option string to packet options " << makeHexDump(ECSOptionStr) << endl); - DNSPacketWriter::optvect_t opts; - opts.emplace_back(EDNSOptionCode::ECS, ECSOptionStr); - pw.addOpt(512, 0, 0, opts); - pw.commit(); - } - - if (send(d_sock, packet.data(), packet.size(), 0) < 0) { // zoom - g_log << Logger::Error << "Unable to send a packet to our recursing backend: " << stringerror() << endl; - } - - return true; -} - -/** This finds us an unused or stale ID. Does not actually clean the contents */ -int DNSProxy::getID_locked(map_t& conntrack) -{ - map_t::iterator iter; - for (int n = 0;; ++n) { // NOLINT(readability-identifier-length) - iter = conntrack.find(n); - if (iter == conntrack.end()) { - return n; - } - if (iter->second.created < time(nullptr) - 60) { - if (iter->second.created != 0) { - g_log << Logger::Warning << "Recursive query for remote " << iter->second.remote.toStringWithPort() << " with internal id " << n << " was not answered by backend within timeout, reusing id" << endl; - iter->second.complete.reset(); - S.inc("recursion-unanswered"); - } - return n; - } - } -} - -void DNSProxy::mainloop() -{ - setThreadName("pdns/dnsproxy"); - try { - char buffer[1500]; - ssize_t len; - - struct msghdr msgh; - struct iovec iov; - cmsgbuf_aligned cbuf; - ComboAddress fromaddr; - - for (;;) { - socklen_t fromaddrSize = sizeof(fromaddr); - len = recvfrom(d_sock, &buffer[0], sizeof(buffer), 0, (struct sockaddr*)&fromaddr, &fromaddrSize); // answer from our backend NOLINT(cppcoreguidelines-pro-type-cstyle-cast) - if (len < (ssize_t)sizeof(dnsheader)) { - if (len < 0) { - g_log << Logger::Error << "Error receiving packet from recursor backend: " << stringerror() << endl; - } - else if (len == 0) { - g_log << Logger::Error << "Error receiving packet from recursor backend, EOF" << endl; - } - else { - g_log << Logger::Error << "Short packet from recursor backend, " << len << " bytes" << endl; - } - - continue; - } - if (fromaddr != d_remote) { - g_log << Logger::Error << "Got answer from unexpected host " << fromaddr.toStringWithPort() << " instead of our recursor backend " << d_remote.toStringWithPort() << endl; - continue; - } - (*d_resanswers)++; - (*d_udpanswers)++; - dnsheader dHead{}; - memcpy(&dHead, &buffer[0], sizeof(dHead)); - { - auto conntrack = d_conntrack.lock(); - if (BYTE_ORDER == BIG_ENDIAN) { - // this is needed because spoof ID down below does not respect the native byteorder - dHead.id = (256 * (uint16_t)buffer[1]) + (uint16_t)buffer[0]; - } - - auto iter = conntrack->find(dHead.id ^ d_xor); - if (iter == conntrack->end()) { - g_log << Logger::Error << "Discarding untracked packet from recursor backend with id " << (dHead.id ^ d_xor) << ". Conntrack table size=" << conntrack->size() << endl; - continue; - } - if (iter->second.created == 0) { - g_log << Logger::Error << "Received packet from recursor backend with id " << (dHead.id ^ d_xor) << " which is a duplicate" << endl; - continue; - } - - dHead.id = iter->second.id; - memcpy(&buffer[0], &dHead, sizeof(dHead)); // commit spoofed id - - DNSPacket packet(false); - packet.parse(&buffer[0], (size_t)len); - - if (packet.qtype.getCode() != iter->second.qtype || packet.qdomain != iter->second.qname) { - g_log << Logger::Error << "Discarding packet from recursor backend with id " << (dHead.id ^ d_xor) << ", qname or qtype mismatch (" << packet.qtype.getCode() << " v " << iter->second.qtype << ", " << packet.qdomain << " v " << iter->second.qname << ")" << endl; - continue; - } - - /* Set up iov and msgh structures. */ - memset(&msgh, 0, sizeof(struct msghdr)); - string reply; // needs to be alive at time of sendmsg! - MOADNSParser mdp(false, packet.getString()); - // update the EDNS options with info from the resolver - issue #5469 - // note that this relies on the ECS string encoder to use the source network, and only take the prefix length from scope - iter->second.complete->d_eso.scope = packet.d_eso.scope; - DLOG(g_log << "from dnsproxy::mainLoop: updated EDNS options from resolver EDNS source: " << iter->second.complete->d_eso.source.toString() << " EDNS scope: " << iter->second.complete->d_eso.scope.toString() << endl); - - if (mdp.d_header.rcode == RCode::NoError) { - for (const auto& answer : mdp.d_answers) { - if (answer.d_place == DNSResourceRecord::ANSWER || (answer.d_place == DNSResourceRecord::AUTHORITY && answer.d_type == QType::SOA)) { - - if (answer.d_type == iter->second.qtype || (iter->second.qtype == QType::ANY && (answer.d_type == QType::A || answer.d_type == QType::AAAA))) { - DNSZoneRecord dzr; - dzr.dr.d_name = iter->second.aname; - dzr.dr.d_type = answer.d_type; - dzr.dr.d_ttl = answer.d_ttl; - dzr.dr.d_place = answer.d_place; - dzr.dr.setContent(answer.getContent()); - iter->second.complete->addRecord(std::move(dzr)); - } - } - } - - iter->second.complete->setRcode(mdp.d_header.rcode); - } - else { - g_log << Logger::Error << "Error resolving for " << iter->second.aname << " ALIAS " << iter->second.qname << " over UDP, " << QType(iter->second.qtype).toString() << "-record query returned " << RCode::to_s(mdp.d_header.rcode) << ", returning SERVFAIL" << endl; - iter->second.complete->clearRecords(); - iter->second.complete->setRcode(RCode::ServFail); - } - reply = iter->second.complete->getString(); - iov.iov_base = (void*)reply.c_str(); - iov.iov_len = reply.length(); - iter->second.complete.reset(); - msgh.msg_iov = &iov; - msgh.msg_iovlen = 1; - msgh.msg_name = (struct sockaddr*)&iter->second.remote; // NOLINT(cppcoreguidelines-pro-type-cstyle-cast) - msgh.msg_namelen = iter->second.remote.getSocklen(); - msgh.msg_control = nullptr; - - if (iter->second.anyLocal) { - addCMsgSrcAddr(&msgh, &cbuf, iter->second.anyLocal.get_ptr(), 0); - } - if (sendmsg(iter->second.outsock, &msgh, 0) < 0) { - int err = errno; - g_log << Logger::Warning << "dnsproxy.cc: Error sending reply with sendmsg (socket=" << iter->second.outsock << "): " << stringerror(err) << endl; - } - iter->second.created = 0; - } - } - } - catch (PDNSException& ae) { - g_log << Logger::Error << "Fatal error in DNS proxy: " << ae.reason << endl; - } - catch (std::exception& e) { - g_log << Logger::Error << "Communicator thread died because of STL error: " << e.what() << endl; - } - catch (...) { - g_log << Logger::Error << "Caught unknown exception." << endl; - } - g_log << Logger::Error << "Exiting because DNS proxy failed" << endl; - _exit(1); -} - -DNSProxy::~DNSProxy() -{ - if (d_sock > -1) { - try { - closesocket(d_sock); - } - catch (const PDNSException& e) { - } - } - - d_sock = -1; -} diff --git a/pdns/dnsproxy.hh b/pdns/dnsproxy.hh deleted file mode 100644 index b5ec5de13642..000000000000 --- a/pdns/dnsproxy.hh +++ /dev/null @@ -1,85 +0,0 @@ -/* - * This file is part of PowerDNS or dnsdist. - * Copyright -- PowerDNS.COM B.V. and its contributors - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * In addition, for the avoidance of any doubt, permission is granted to - * link this program with OpenSSL and to (re)distribute the binaries - * produced as the result of such linking. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -#pragma once -#include -#include -#include -#include -#include "dnspacket.hh" -#include "lock.hh" -#include "iputils.hh" - -#include "namespaces.hh" - -/** - -how will this work. - -This is a thread that just throws packets around. Should handle ~1000 packets/second. - -Consists of a thread receiving packets back from the backend and retransmitting them to the original client. - -Furthermore, it provides a member function that reports the packet to the connection tracker and actually sends it out. - -The sending happens from a source port that is determined by the constructor, but IS random. Furthermore, the ID is XOR-ed with a random value -to make sure outside parties can't spoof us. - -To fix: how to remove the stale entries that will surely accumulate -*/ - -class DNSProxy -{ -public: - DNSProxy(const string& remote, const string& udpPortRange); //!< creates socket - ~DNSProxy(); //& reply, const DNSName& target, const DNSName& aname, uint8_t scopeMask); - - void mainloop(); //!< this is the main loop that receives reply packets and sends them out again -private: - struct ConntrackEntry - { - time_t created; - boost::optional anyLocal; - DNSName qname; - std::unique_ptr complete; - DNSName aname; - uint8_t anameScopeMask; - ComboAddress remote; - uint16_t id; - uint16_t qtype; - int outsock; - }; - - using map_t = map; - - // Data - ComboAddress d_remote; - AtomicCounter* d_resanswers; - AtomicCounter* d_udpanswers; - AtomicCounter* d_resquestions; - LockGuarded d_conntrack; - int d_sock; - const uint16_t d_xor; - - static int getID_locked(map_t&); -}; diff --git a/pdns/packethandler.cc b/pdns/packethandler.cc index b8aa267ddcb4..83595b39d404 100644 --- a/pdns/packethandler.cc +++ b/pdns/packethandler.cc @@ -43,11 +43,11 @@ #include "statbag.hh" #include "resolver.hh" #include "communicator.hh" -#include "dnsproxy.hh" #include "version.hh" #include "auth-main.hh" #include "trusted-notification-proxy.hh" #include "gss_context.hh" +#include "stubresolver.hh" #if 0 #undef DLOG @@ -1337,6 +1337,7 @@ bool PacketHandler::tryWildcard(DNSPacket& p, std::unique_ptr& r, DNS } //! Called by the Distributor to ask a question. Returns 0 in case of an error +// NOLINTNEXTLINE(readability-function-cognitive-complexity): TODO Clean this function up. std::unique_ptr PacketHandler::doQuestion(DNSPacket& p) { DNSZoneRecord rr; @@ -1346,8 +1347,6 @@ std::unique_ptr PacketHandler::doQuestion(DNSPacket& p) vector rrset; bool weDone=false, weRedirected=false, weHaveUnauth=false, doSigs=false; - DNSName haveAlias; - uint8_t aliasScopeMask; std::unique_ptr r{nullptr}; bool noCache=false; @@ -1593,8 +1592,6 @@ std::unique_ptr PacketHandler::doQuestion(DNSPacket& p) // see what we get.. B.lookup(QType(QType::ANY), target, d_sd.domain_id, &p); rrset.clear(); - haveAlias.clear(); - aliasScopeMask = 0; weDone = weRedirected = weHaveUnauth = false; while(B.get(rr)) { @@ -1614,7 +1611,7 @@ std::unique_ptr PacketHandler::doQuestion(DNSPacket& p) for (const auto& r_it : recvec) { rr.dr.d_type = rec->d_type; // might be CNAME rr.dr.setContent(r_it); - rr.scopeMask = p.getRealRemote().getBits(); // this makes sure answer is a specific as your question + rr.scopeMask = p.getRealRemote().getBits(); // this makes sure answer is as specific as your question rrset.push_back(rr); } if(rec->d_type == QType::CNAME && p.qtype.getCode() != QType::CNAME) @@ -1634,6 +1631,63 @@ std::unique_ptr PacketHandler::doQuestion(DNSPacket& p) } } #endif + if (rr.dr.d_type == QType::ALIAS && (p.qtype.getCode() == QType::A || p.qtype.getCode() == QType::AAAA || p.qtype.getCode() == QType::ANY) && !d_dk.isPresigned(d_sd.qname)) { + if (!d_doExpandALIAS) { + g_log<(rr.dr)->getContent(); + if (aliasTarget.empty()) { + continue; + } + + vector ips; + EDNSSubnetOpts eso; + if (r->hasEDNSSubnet()) { + eso.scope = r->d_eso.scope; + eso.source = r->d_eso.source; + } + int ret1 = RCode::NoError; + int ret2 = RCode::NoError; + if (r->qtype == QType::A || r->qtype == QType::ANY) { + ret1 = stubDoResolve(aliasTarget, QType::A, ips, r->hasEDNSSubnet() ? &eso : nullptr); + } + if (r->qtype == QType::AAAA || r->qtype == QType::ANY) { + ret2 = stubDoResolve(aliasTarget, QType::AAAA, ips, r->hasEDNSSubnet() ? &eso : nullptr); + } + + if (ret1 != RCode::NoError || ret2 != RCode::NoError) { + g_log << Logger::Error << "Error resolving for " << target << " ALIAS " << aliasTarget << " over UDP"; + if (ret1 != RCode::NoError) { + g_log << Logger::Error << ", A-record query returned " << RCode::to_s(ret1); + } + if (ret2 != RCode::NoError) { + g_log << Logger::Error << ", AAAA-record query returned " << RCode::to_s(ret2); + } + g_log << Logger::Error << ", returning SERVFAIL" << endl; + + while (B.get(rr)) { + // don't leave DB handle in bad state + } + + r = p.replyPacket(); + r->setRcode(RCode::ServFail); + return r; + } + + for (auto& ip : ips) { // NOLINT(readability-identifier-length) + ip.dr.d_name = target; + if (r->hasEDNSSubnet()) { + // update the EDNS options with info from the resolver - issue #5469 + // note that this relies on the ECS string encoder to use the source network, and only take the prefix length from scope + ip.scopeMask = eso.scope.getBits(); + } + rrset.push_back(ip); + } + + weDone = true; + } + //cerr<<"got content: ["< PacketHandler::doQuestion(DNSPacket& p) if(rr.dr.d_type == QType::CNAME && p.qtype.getCode() != QType::CNAME) weRedirected=true; - if (DP && rr.dr.d_type == QType::ALIAS && (p.qtype.getCode() == QType::A || p.qtype.getCode() == QType::AAAA || p.qtype.getCode() == QType::ANY) && !d_dk.isPresigned(d_sd.qname)) { - if (!d_doExpandALIAS) { - g_log<(rr.dr)->getContent(); - aliasScopeMask=rr.scopeMask; - } - // Filter out all SOA's and add them in later if(rr.dr.d_type == QType::SOA) continue; @@ -1673,20 +1718,13 @@ std::unique_ptr PacketHandler::doQuestion(DNSPacket& p) } - DLOG(g_log<<"After first ANY query for '"<completePacket(r, haveAlias, target, aliasScopeMask); - return nullptr; - } - - // referral for DS query if(p.qtype.getCode() == QType::DS) { DLOG(g_log<<"Qtype is DS"<& ret, const EDNSSubnetOpts* d_eso) +int stubDoResolve(const DNSName& qname, uint16_t qtype, vector& ret, EDNSSubnetOpts* d_eso) { // ensure resolver gets always configured if (!s_stubResolvConfigured) { @@ -176,6 +177,12 @@ int stubDoResolve(const DNSName& qname, uint16_t qtype, vector& r continue; } + if (d_eso != nullptr) { + DNSPacket replyPacket(false); + replyPacket.parse(reply.c_str(), reply.size()); + d_eso->scope = replyPacket.d_eso.scope; + } + for (const auto& answer : mdp.d_answers) { if (answer.d_place == 1 && answer.d_type == qtype) { DNSZoneRecord zrr; @@ -190,7 +197,7 @@ int stubDoResolve(const DNSName& qname, uint16_t qtype, vector& r return RCode::ServFail; } -int stubDoResolve(const DNSName& qname, uint16_t qtype, vector& ret, const EDNSSubnetOpts* d_eso) +int stubDoResolve(const DNSName& qname, uint16_t qtype, vector& ret, EDNSSubnetOpts* d_eso) { vector ret2; int res = stubDoResolve(qname, qtype, ret2, d_eso); diff --git a/pdns/stubresolver.hh b/pdns/stubresolver.hh index 061a3f368697..88f79f4cdfa9 100644 --- a/pdns/stubresolver.hh +++ b/pdns/stubresolver.hh @@ -26,5 +26,5 @@ void stubParseResolveConf(); bool resolversDefined(); -int stubDoResolve(const DNSName& qname, uint16_t qtype, vector& ret, const EDNSSubnetOpts* d_eso = nullptr); -int stubDoResolve(const DNSName& qname, uint16_t qtype, vector& ret, const EDNSSubnetOpts* d_eso = nullptr); +int stubDoResolve(const DNSName& qname, uint16_t qtype, vector& ret, EDNSSubnetOpts* d_eso = nullptr); +int stubDoResolve(const DNSName& qname, uint16_t qtype, vector& ret, EDNSSubnetOpts* d_eso = nullptr); From d127b24d1869c22ed3096c880e1d0c77bb2e7258 Mon Sep 17 00:00:00 2001 From: Chris Hofstaedtler Date: Sun, 8 Sep 2024 00:45:42 +0200 Subject: [PATCH 4/4] auth ALIAS: add DNSSEC tests --- regression-tests.auth-py/authtests.py | 80 +++++++++++++++++++ regression-tests.auth-py/requirements.txt | 1 + regression-tests.auth-py/test_ALIAS.py | 34 +++++--- regression-tests.auth-py/test_GSSTSIG.py | 3 + .../test_XFRIncomplete.py | 2 + 5 files changed, 108 insertions(+), 12 deletions(-) diff --git a/regression-tests.auth-py/authtests.py b/regression-tests.auth-py/authtests.py index 5eb6534df8fe..89e2daa51363 100644 --- a/regression-tests.auth-py/authtests.py +++ b/regression-tests.auth-py/authtests.py @@ -137,6 +137,68 @@ def generateAuthConfig(cls, confdir): except subprocess.CalledProcessError as e: raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd, e.returncode, e.output)) + @classmethod + def listKeys(cls, confdir: str, zonename: str): + zone = '.' if zonename == 'ROOT' else zonename + pdnsutilCmd = [os.environ['PDNSUTIL'], + '--config-dir=%s' % confdir, + 'list-keys', + zone] + + print(' '.join(pdnsutilCmd)) + try: + lines = subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd, e.returncode, e.output)) + + keys = [] + found_header = False + for line in lines.splitlines(): + if not found_header: + if line.startswith(b"----"): + found_header = True + continue + line = line.decode().split() + if not (len(line) >= 8 and line[1] in ("CSK", "KSK", "ZSK")): + continue + print(line) + keys.append( + { + "zone": line[0], + "type": line[1], + "act": line[2], + "pub": line[3], + "size": int(line[4]), + "algorithm": line[5], + "id": int(line[6]), + "location": line[7], + "keytag": int(line[8]), + }) + return keys + + @classmethod + def exportZoneDnsKey(cls, confdir: str, zonename: str, keyid: int): + zone = '.' if zonename == 'ROOT' else zonename + pdnsutilCmd = [os.environ['PDNSUTIL'], + '--config-dir=%s' % confdir, + 'export-zone-dnskey', + zone, + str(keyid)] + + print(' '.join(pdnsutilCmd)) + try: + lines = subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd, e.returncode, e.output)) + + for line in lines.splitlines(): + line = line.strip().decode().split(maxsplit=3) + print(line) + if line[2] == "DNSKEY": + _, rdclass, rdtype, key = line + return dns.rrset.from_text(dns.name.from_text(zone), 3600, rdclass, rdtype, key) + return None + @classmethod def secureZone(cls, confdir, zonename, key=None): zone = '.' if zonename == 'ROOT' else zonename @@ -168,6 +230,7 @@ def secureZone(cls, confdir, zonename, key=None): def generateAllAuthConfig(cls, confdir): cls.generateAuthConfig(confdir) cls.generateAuthNamedConf(confdir, cls._zones.keys()) + cls._zone_dnskeys = {} for zonename, zonecontent in cls._zones.items(): cls.generateAuthZone(confdir, @@ -175,6 +238,10 @@ def generateAllAuthConfig(cls, confdir): zonecontent) if cls._zone_keys.get(zonename, None): cls.secureZone(confdir, zonename, cls._zone_keys.get(zonename)) + zone_keys = cls.listKeys(confdir, zonename) + cls._zone_dnskeys[dns.name.from_text(zonename)] = cls.exportZoneDnsKey( + confdir, zonename, zone_keys[-1]["id"] + ) @classmethod def waitForTCPSocket(cls, ipaddress, port): @@ -590,3 +657,16 @@ def assertAuthorityHasSOA(self, msg): if not found: raise AssertionError("No SOA record found in the authority section:\n%s" % msg.to_text()) + + def assertSigned(self, msg: dns.message.Message, name: str | dns.name.Name, rdatatype: str | int): + if not isinstance(msg, dns.message.Message): + raise TypeError("msg is not a dns.message.Message but a %s" % type(msg)) + + name = dns.name.from_text(name) + + if not isinstance(rdatatype, int): + rdatatype = dns.rdatatype.from_text(rdatatype) + + rrset = msg.find_rrset(msg.answer, name, dns.rdataclass.IN, rdatatype) + rrsig = msg.find_rrset(msg.answer, name, dns.rdataclass.IN, dns.rdatatype.RRSIG, covers=rdatatype) + dns.dnssec.validate(rrset, rrsig, self._zone_dnskeys) diff --git a/regression-tests.auth-py/requirements.txt b/regression-tests.auth-py/requirements.txt index f1236ed89f9b..aaa56746ff34 100644 --- a/regression-tests.auth-py/requirements.txt +++ b/regression-tests.auth-py/requirements.txt @@ -1,3 +1,4 @@ +cryptography dnspython==2.1.0 pytest Twisted>0.15.0 diff --git a/regression-tests.auth-py/test_ALIAS.py b/regression-tests.auth-py/test_ALIAS.py index 8cd7f4f3deb7..8bb8088e7865 100644 --- a/regression-tests.auth-py/test_ALIAS.py +++ b/regression-tests.auth-py/test_ALIAS.py @@ -62,32 +62,37 @@ def startResponders(cls): cls._ALIASResponder.start() def testNoError(self): - expected_a = [dns.rrset.from_text('noerror.example.org.', + name = 'noerror.example.org.' + expected_a = [dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.1')] - expected_aaaa = [dns.rrset.from_text('noerror.example.org.', + expected_aaaa = [dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'AAAA', '2001:DB8::1')] - query = dns.message.make_query('noerror.example.org', 'A') + query = dns.message.make_query(name, 'A', want_dnssec=True) res = self.sendUDPQuery(query) self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertAnyRRsetInAnswer(res, expected_a) self.assertEqual(len(res.options), 0) # this checks that we don't invent ECS on non-ECS queries + self.assertSigned(res, name, dns.rdatatype.A) - query = dns.message.make_query('noerror.example.org', 'AAAA') + query = dns.message.make_query(name, 'AAAA', want_dnssec=True) res = self.sendUDPQuery(query) self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertAnyRRsetInAnswer(res, expected_aaaa) + self.assertSigned(res, name, dns.rdatatype.AAAA) - query = dns.message.make_query('noerror.example.org', 'ANY') + query = dns.message.make_query(name, 'ANY', want_dnssec=True) res = self.sendUDPQuery(query) self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertAnyRRsetInAnswer(res, expected_a) self.assertAnyRRsetInAnswer(res, expected_aaaa) + self.assertSigned(res, name, dns.rdatatype.A) + self.assertSigned(res, name, dns.rdatatype.AAAA) # NODATA - query = dns.message.make_query('noerror.example.org', 'MX') + query = dns.message.make_query(name, 'MX') res = self.sendUDPQuery(query) self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertEqual(len(res.answer), 0) @@ -121,31 +126,36 @@ def testServFail(self): self.assertRcodeEqual(res, dns.rcode.SERVFAIL) def testNoErrorTCP(self): - expected_a = [dns.rrset.from_text('noerror.example.org.', + name = 'noerror.example.org.' + expected_a = [dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.1')] - expected_aaaa = [dns.rrset.from_text('noerror.example.org.', + expected_aaaa = [dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'AAAA', '2001:DB8::1')] - query = dns.message.make_query('noerror.example.org', 'A') + query = dns.message.make_query(name, 'A', want_dnssec=True) res = self.sendTCPQuery(query) self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertAnyRRsetInAnswer(res, expected_a) + self.assertSigned(res, name, dns.rdatatype.A) - query = dns.message.make_query('noerror.example.org', 'AAAA') + query = dns.message.make_query(name, 'AAAA', want_dnssec=True) res = self.sendTCPQuery(query) self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertAnyRRsetInAnswer(res, expected_aaaa) + self.assertSigned(res, name, dns.rdatatype.AAAA) - query = dns.message.make_query('noerror.example.org', 'ANY') + query = dns.message.make_query(name, 'ANY', want_dnssec=True) res = self.sendTCPQuery(query) self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertAnyRRsetInAnswer(res, expected_a) self.assertAnyRRsetInAnswer(res, expected_aaaa) + self.assertSigned(res, name, dns.rdatatype.A) + self.assertSigned(res, name, dns.rdatatype.AAAA) # NODATA - query = dns.message.make_query('noerror.example.org', 'MX') + query = dns.message.make_query(name, 'MX', want_dnssec=True) res = self.sendTCPQuery(query) self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertEqual(len(res.answer), 0) diff --git a/regression-tests.auth-py/test_GSSTSIG.py b/regression-tests.auth-py/test_GSSTSIG.py index 2297f46c2a98..ec30f2089c3f 100644 --- a/regression-tests.auth-py/test_GSSTSIG.py +++ b/regression-tests.auth-py/test_GSSTSIG.py @@ -33,6 +33,9 @@ class GSSTSIGBase(AuthTest): 'KRB5_KTNAME' : './kerberos-client/kt.keytab' } + # zones will be created by our own setUpClass() code + _zones = {} + @classmethod def setUpClass(cls): super(GSSTSIGBase, cls).setUpClass() diff --git a/regression-tests.auth-py/test_XFRIncomplete.py b/regression-tests.auth-py/test_XFRIncomplete.py index 3db145e293f1..effbf19f3653 100644 --- a/regression-tests.auth-py/test_XFRIncomplete.py +++ b/regression-tests.auth-py/test_XFRIncomplete.py @@ -154,6 +154,8 @@ class XFRIncompleteAuthTest(AuthTest): #axfr-fetch-timeout=20 """ + _zones = {} # zone setup happens in setUpClass + @classmethod def setUpClass(cls): super(XFRIncompleteAuthTest, cls).setUpClass()