diff --git a/pdns/Makefile.am b/pdns/Makefile.am index 524110a07be8..f6a5510bd24c 100644 --- a/pdns/Makefile.am +++ b/pdns/Makefile.am @@ -207,6 +207,7 @@ pdns_server_SOURCES = \ rcpgenerator.cc \ receiver.cc \ resolver.cc resolver.hh \ + axfr-retriever.cc axfr-retriever.hh \ responsestats.cc responsestats.hh responsestats-auth.cc \ rfc2136handler.cc \ secpoll.cc secpoll.hh \ @@ -636,6 +637,7 @@ ixfrdist_SOURCES = \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ resolver.cc \ + axfr-retriever.cc \ pollmplexer.cc \ sillyrecords.cc \ sstuff.hh \ @@ -690,6 +692,7 @@ ixplore_SOURCES = \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ resolver.cc \ + axfr-retriever.cc \ ixfr.cc ixfr.hh \ ixfrutils.cc ixfrutils.hh \ ixplore.cc \ @@ -823,6 +826,7 @@ toysdig_LDADD += $(P11KIT1_LIBS) endif tsig_tests_SOURCES = \ + axfr-retriever.cc \ arguments.cc \ base32.cc \ base64.cc base64.hh \ diff --git a/pdns/axfr-retriever.cc b/pdns/axfr-retriever.cc new file mode 100644 index 000000000000..cfac004079d2 --- /dev/null +++ b/pdns/axfr-retriever.cc @@ -0,0 +1,248 @@ +/* + * 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. + */ + +#include "axfr-retriever.hh" +#include "arguments.hh" +#include "dns_random.hh" +#include "utility.hh" +#include "resolver.hh" + +using pdns::resolver::parseResult; + +AXFRRetriever::AXFRRetriever(const ComboAddress& remote, + const DNSName& domain, + const TSIGTriplet& tt, + const ComboAddress* laddr, + size_t maxReceivedBytes, + uint16_t timeout) + : d_tsigVerifier(tt, remote, d_trc), d_receivedBytes(0), d_maxReceivedBytes(maxReceivedBytes) +{ + ComboAddress local; + if (laddr != nullptr) { + local = ComboAddress(*laddr); + } else { + string qlas = remote.sin4.sin_family == AF_INET ? "query-local-address" : "query-local-address6"; + if (::arg()[qlas].empty()) { + throw ResolverException("Unable to determine source address for AXFR request to " + remote.toStringWithPort() + " for " + domain.toLogString() + ". " + qlas + " is unset"); + } + local=ComboAddress(::arg()[qlas]); + } + d_sock = -1; + try { + d_sock = makeQuerySocket(local, false); // make a TCP socket + if (d_sock < 0) + throw ResolverException("Error creating socket for AXFR request to "+d_remote.toStringWithPort()); + d_buf = shared_array(new char[65536]); + d_remote = remote; // mostly for error reporting + this->connect(timeout); + d_soacount = 0; + + vector packet; + DNSPacketWriter pw(packet, domain, QType::AXFR); + pw.getHeader()->id = dns_random_uint16(); + + if(!tt.name.empty()) { + if (tt.algo == DNSName("hmac-md5")) + d_trc.d_algoName = tt.algo + DNSName("sig-alg.reg.int"); + else + d_trc.d_algoName = tt.algo; + d_trc.d_time = time(0); + d_trc.d_fudge = 300; + d_trc.d_origID=ntohs(pw.getHeader()->id); + d_trc.d_eRcode=0; + addTSIG(pw, d_trc, tt.name, tt.secret, "", false); + } + + uint16_t replen=htons(packet.size()); + Utility::iovec iov[2]; + iov[0].iov_base=reinterpret_cast(&replen); + iov[0].iov_len=2; + iov[1].iov_base=packet.data(); + iov[1].iov_len=packet.size(); + + int ret=Utility::writev(d_sock, iov, 2); + if(ret < 0) + throw ResolverException("Error sending question to "+d_remote.toStringWithPort()+": "+stringerror()); + if(ret != (int)(2+packet.size())) { + throw ResolverException("Partial write on AXFR request to "+d_remote.toStringWithPort()); + } + + int res = waitForData(d_sock, timeout, 0); + + if(!res) + throw ResolverException("Timeout waiting for answer from "+d_remote.toStringWithPort()+" during AXFR"); + if(res<0) + throw ResolverException("Error waiting for answer from "+d_remote.toStringWithPort()+": "+stringerror()); + } + catch(...) { + if(d_sock >= 0) + close(d_sock); + d_sock = -1; + throw; + } +} + +AXFRRetriever::~AXFRRetriever() +{ + close(d_sock); +} + + + +int AXFRRetriever::getChunk(Resolver::res_t &res, vector* records, uint16_t timeout) // Implementation is making sure RFC2845 4.4 is followed. +{ + if(d_soacount > 1) + return false; + + // d_sock is connected and is about to spit out a packet + int len=getLength(timeout); + if(len<0) + throw ResolverException("EOF trying to read axfr chunk from remote TCP client"); + + if (d_maxReceivedBytes > 0 && (d_maxReceivedBytes - d_receivedBytes) < (size_t) len) + throw ResolverException("Reached the maximum number of received bytes during AXFR"); + + timeoutReadn(len, timeout); + + d_receivedBytes += (uint16_t) len; + + MOADNSParser mdp(false, d_buf.get(), len); + + int err = mdp.d_header.rcode; + + if(err) { + throw ResolverException("AXFR chunk error: " + RCode::to_s(err)); + } + + try { + d_tsigVerifier.check(std::string(d_buf.get(), len), mdp); + } + catch(const std::runtime_error& re) { + throw ResolverException(re.what()); + } + + if(!records) { + err = parseResult(mdp, DNSName(), 0, 0, &res); + + if (!err) { + for(const auto& answer : mdp.d_answers) + if (answer.first.d_type == QType::SOA) + d_soacount++; + } + } + else { + records->clear(); + records->reserve(mdp.d_answers.size()); + + for(auto& r: mdp.d_answers) { + if (r.first.d_type == QType::SOA) { + d_soacount++; + } + + records->push_back(std::move(r.first)); + } + } + + return true; +} + +void AXFRRetriever::timeoutReadn(uint16_t bytes, uint16_t timeoutsec) +{ + time_t start=time(nullptr); + int n=0; + int numread; + while(n + +#include "iputils.hh" +#include "dnsname.hh" +#include "resolver.hh" + +class AXFRRetriever : public boost::noncopyable +{ + public: + AXFRRetriever(const ComboAddress& remote, + const DNSName& zone, + const TSIGTriplet& tt = TSIGTriplet(), + const ComboAddress* laddr = NULL, + size_t maxReceivedBytes=0, + uint16_t timeout=10); + ~AXFRRetriever(); + int getChunk(Resolver::res_t &res, vector* records=0, uint16_t timeout=10); + + private: + void connect(uint16_t timeout); + int getLength(uint16_t timeout); + void timeoutReadn(uint16_t bytes, uint16_t timeoutsec=10); + + shared_array d_buf; + string d_domain; + int d_sock; + int d_soacount; + ComboAddress d_remote; + TSIGTCPVerifier d_tsigVerifier; + + size_t d_receivedBytes; + size_t d_maxReceivedBytes; + TSIGRecordContent d_trc; +}; diff --git a/pdns/communicator.cc b/pdns/communicator.cc index 816b489673dd..f0154995213f 100644 --- a/pdns/communicator.cc +++ b/pdns/communicator.cc @@ -30,7 +30,6 @@ #include "dnsbackend.hh" #include "ueberbackend.hh" #include "packethandler.hh" -#include "resolver.hh" #include "logger.hh" #include "dns.hh" #include "arguments.hh" diff --git a/pdns/ixfrdist.cc b/pdns/ixfrdist.cc index 7d9a0eb0274c..25e3470e4d44 100644 --- a/pdns/ixfrdist.cc +++ b/pdns/ixfrdist.cc @@ -36,7 +36,7 @@ #include #include "ixfr.hh" #include "ixfrutils.hh" -#include "resolver.hh" +#include "axfr-retriever.hh" #include "dns_random.hh" #include "sstuff.hh" #include "mplexer.hh" diff --git a/pdns/ixplore.cc b/pdns/ixplore.cc index 9b525de1b00f..2f010d77c3a3 100644 --- a/pdns/ixplore.cc +++ b/pdns/ixplore.cc @@ -34,7 +34,7 @@ #include "dns_random.hh" #include "gss_context.hh" #include -#include "resolver.hh" +#include "axfr-retriever.hh" #include #include "ixfr.hh" #include "ixfrutils.hh" diff --git a/pdns/recursordist/Makefile.am b/pdns/recursordist/Makefile.am index 657ea7eec6bc..6e13a653b7f5 100644 --- a/pdns/recursordist/Makefile.am +++ b/pdns/recursordist/Makefile.am @@ -160,6 +160,7 @@ pdns_recursor_SOURCES = \ reczones.cc \ remote_logger.cc remote_logger.hh \ resolver.hh resolver.cc \ + axfr-retriever.hh axfr-retriever.cc \ resolve-context.hh \ responsestats.hh responsestats.cc \ root-addresses.hh \ @@ -259,6 +260,7 @@ testrunner_SOURCES = \ responsestats.cc \ rpzloader.cc rpzloader.hh \ resolver.hh resolver.cc \ + axfr-retriever.hh axfr-retriever.cc \ root-dnssec.hh \ secpoll.cc \ sillyrecords.cc \ diff --git a/pdns/recursordist/axfr-retriever.cc b/pdns/recursordist/axfr-retriever.cc new file mode 120000 index 000000000000..e3e1df89b3f7 --- /dev/null +++ b/pdns/recursordist/axfr-retriever.cc @@ -0,0 +1 @@ +../axfr-retriever.cc \ No newline at end of file diff --git a/pdns/recursordist/axfr-retriever.hh b/pdns/recursordist/axfr-retriever.hh new file mode 120000 index 000000000000..7e9934846879 --- /dev/null +++ b/pdns/recursordist/axfr-retriever.hh @@ -0,0 +1 @@ +../axfr-retriever.hh \ No newline at end of file diff --git a/pdns/resolver.cc b/pdns/resolver.cc index 774fa7e56402..d8977b8db518 100644 --- a/pdns/resolver.cc +++ b/pdns/resolver.cc @@ -51,6 +51,8 @@ #include "gss_context.hh" #include "namespaces.hh" +using pdns::resolver::parseResult; + int makeQuerySocket(const ComboAddress& local, bool udpOrTCP, bool nonLocalBind) { ComboAddress ourLocal(local); @@ -188,36 +190,41 @@ uint16_t Resolver::sendResolve(const ComboAddress& remote, const ComboAddress& l return randomid; } -static int parseResult(MOADNSParser& mdp, const DNSName& origQname, uint16_t origQtype, uint16_t id, Resolver::res_t* result) -{ - result->clear(); - - if(mdp.d_header.rcode) - return mdp.d_header.rcode; - - if(origQname.countLabels()) { // not AXFR - if(mdp.d_header.id != id) - throw ResolverException("Remote nameserver replied with wrong id"); - if(mdp.d_header.qdcount != 1) - throw ResolverException("resolver: received answer with wrong number of questions ("+itoa(mdp.d_header.qdcount)+")"); - if(mdp.d_qname != origQname) - throw ResolverException(string("resolver: received an answer to another question (")+mdp.d_qname.toLogString()+"!="+ origQname.toLogString()+".)"); - } +namespace pdns { + namespace resolver { + int parseResult(MOADNSParser& mdp, const DNSName& origQname, uint16_t origQtype, uint16_t id, Resolver::res_t* result) + { + result->clear(); + + if(mdp.d_header.rcode) + return mdp.d_header.rcode; + + if(origQname.countLabels()) { // not AXFR + if(mdp.d_header.id != id) + throw ResolverException("Remote nameserver replied with wrong id"); + if(mdp.d_header.qdcount != 1) + throw ResolverException("resolver: received answer with wrong number of questions ("+itoa(mdp.d_header.qdcount)+")"); + if(mdp.d_qname != origQname) + throw ResolverException(string("resolver: received an answer to another question (")+mdp.d_qname.toLogString()+"!="+ origQname.toLogString()+".)"); + } - vector ret; - DNSResourceRecord rr; - result->reserve(mdp.d_answers.size()); + vector ret; + DNSResourceRecord rr; + result->reserve(mdp.d_answers.size()); - for (const auto& i: mdp.d_answers) { - rr.qname = i.first.d_name; - rr.qtype = i.first.d_type; - rr.ttl = i.first.d_ttl; - rr.content = i.first.d_content->getZoneRepresentation(true); - result->push_back(rr); - } + for (const auto& i: mdp.d_answers) { + rr.qname = i.first.d_name; + rr.qtype = i.first.d_type; + rr.ttl = i.first.d_ttl; + rr.content = i.first.d_content->getZoneRepresentation(true); + result->push_back(rr); + } - return 0; -} + return 0; + } + + } // namespace resolver +} // namespace pdns bool Resolver::tryGetSOASerial(DNSName *domain, ComboAddress* remote, uint32_t *theirSerial, uint32_t *theirInception, uint32_t *theirExpire, uint16_t* id) { @@ -362,221 +369,3 @@ void Resolver::getSoaSerial(const ComboAddress& ipport, const DNSName &domain, u } } -AXFRRetriever::AXFRRetriever(const ComboAddress& remote, - const DNSName& domain, - const TSIGTriplet& tt, - const ComboAddress* laddr, - size_t maxReceivedBytes, - uint16_t timeout) - : d_tsigVerifier(tt, remote, d_trc), d_receivedBytes(0), d_maxReceivedBytes(maxReceivedBytes) -{ - ComboAddress local; - if (laddr != nullptr) { - local = ComboAddress(*laddr); - } else { - string qlas = remote.sin4.sin_family == AF_INET ? "query-local-address" : "query-local-address6"; - if (::arg()[qlas].empty()) { - throw ResolverException("Unable to determine source address for AXFR request to " + remote.toStringWithPort() + " for " + domain.toLogString() + ". " + qlas + " is unset"); - } - local=ComboAddress(::arg()[qlas]); - } - d_sock = -1; - try { - d_sock = makeQuerySocket(local, false); // make a TCP socket - if (d_sock < 0) - throw ResolverException("Error creating socket for AXFR request to "+d_remote.toStringWithPort()); - d_buf = shared_array(new char[65536]); - d_remote = remote; // mostly for error reporting - this->connect(timeout); - d_soacount = 0; - - vector packet; - DNSPacketWriter pw(packet, domain, QType::AXFR); - pw.getHeader()->id = dns_random_uint16(); - - if(!tt.name.empty()) { - if (tt.algo == DNSName("hmac-md5")) - d_trc.d_algoName = tt.algo + DNSName("sig-alg.reg.int"); - else - d_trc.d_algoName = tt.algo; - d_trc.d_time = time(0); - d_trc.d_fudge = 300; - d_trc.d_origID=ntohs(pw.getHeader()->id); - d_trc.d_eRcode=0; - addTSIG(pw, d_trc, tt.name, tt.secret, "", false); - } - - uint16_t replen=htons(packet.size()); - Utility::iovec iov[2]; - iov[0].iov_base=reinterpret_cast(&replen); - iov[0].iov_len=2; - iov[1].iov_base=packet.data(); - iov[1].iov_len=packet.size(); - - int ret=Utility::writev(d_sock, iov, 2); - if(ret < 0) - throw ResolverException("Error sending question to "+d_remote.toStringWithPort()+": "+stringerror()); - if(ret != (int)(2+packet.size())) { - throw ResolverException("Partial write on AXFR request to "+d_remote.toStringWithPort()); - } - - int res = waitForData(d_sock, timeout, 0); - - if(!res) - throw ResolverException("Timeout waiting for answer from "+d_remote.toStringWithPort()+" during AXFR"); - if(res<0) - throw ResolverException("Error waiting for answer from "+d_remote.toStringWithPort()+": "+stringerror()); - } - catch(...) { - if(d_sock >= 0) - close(d_sock); - d_sock = -1; - throw; - } -} - -AXFRRetriever::~AXFRRetriever() -{ - close(d_sock); -} - - - -int AXFRRetriever::getChunk(Resolver::res_t &res, vector* records, uint16_t timeout) // Implementation is making sure RFC2845 4.4 is followed. -{ - if(d_soacount > 1) - return false; - - // d_sock is connected and is about to spit out a packet - int len=getLength(timeout); - if(len<0) - throw ResolverException("EOF trying to read axfr chunk from remote TCP client"); - - if (d_maxReceivedBytes > 0 && (d_maxReceivedBytes - d_receivedBytes) < (size_t) len) - throw ResolverException("Reached the maximum number of received bytes during AXFR"); - - timeoutReadn(len, timeout); - - d_receivedBytes += (uint16_t) len; - - MOADNSParser mdp(false, d_buf.get(), len); - - int err = mdp.d_header.rcode; - - if(err) { - throw ResolverException("AXFR chunk error: " + RCode::to_s(err)); - } - - try { - d_tsigVerifier.check(std::string(d_buf.get(), len), mdp); - } - catch(const std::runtime_error& re) { - throw ResolverException(re.what()); - } - - if(!records) { - err = parseResult(mdp, DNSName(), 0, 0, &res); - - if (!err) { - for(const auto& answer : mdp.d_answers) - if (answer.first.d_type == QType::SOA) - d_soacount++; - } - } - else { - records->clear(); - records->reserve(mdp.d_answers.size()); - - for(auto& r: mdp.d_answers) { - if (r.first.d_type == QType::SOA) { - d_soacount++; - } - - records->push_back(std::move(r.first)); - } - } - - return true; -} - -void AXFRRetriever::timeoutReadn(uint16_t bytes, uint16_t timeoutsec) -{ - time_t start=time(nullptr); - int n=0; - int numread; - while(n locals; }; -class AXFRRetriever : public boost::noncopyable -{ - public: - AXFRRetriever(const ComboAddress& remote, - const DNSName& zone, - const TSIGTriplet& tt = TSIGTriplet(), - const ComboAddress* laddr = NULL, - size_t maxReceivedBytes=0, - uint16_t timeout=10); - ~AXFRRetriever(); - int getChunk(Resolver::res_t &res, vector* records=0, uint16_t timeout=10); - - private: - void connect(uint16_t timeout); - int getLength(uint16_t timeout); - void timeoutReadn(uint16_t bytes, uint16_t timeoutsec=10); - - shared_array d_buf; - string d_domain; - int d_sock; - int d_soacount; - ComboAddress d_remote; - TSIGTCPVerifier d_tsigVerifier; - - size_t d_receivedBytes; - size_t d_maxReceivedBytes; - TSIGRecordContent d_trc; -}; +namespace pdns { + namespace resolver { + int parseResult(MOADNSParser& mdp, const DNSName& origQname, uint16_t origQtype, uint16_t id, Resolver::res_t* result); + } // namespace resolver +} // namespace pdns diff --git a/pdns/rpzloader.cc b/pdns/rpzloader.cc index 908c3a4384b9..1562c5f94eaa 100644 --- a/pdns/rpzloader.cc +++ b/pdns/rpzloader.cc @@ -3,7 +3,7 @@ #include "dnsrecords.hh" #include "ixfr.hh" #include "syncres.hh" -#include "resolver.hh" +#include "axfr-retriever.hh" #include "logger.hh" #include "rec-lua-conf.hh" #include "rpzloader.hh" diff --git a/pdns/slavecommunicator.cc b/pdns/slavecommunicator.cc index 8a55e4125ba3..7e548ff08f51 100644 --- a/pdns/slavecommunicator.cc +++ b/pdns/slavecommunicator.cc @@ -34,7 +34,7 @@ #include "dnsbackend.hh" #include "ueberbackend.hh" #include "packethandler.hh" -#include "resolver.hh" +#include "axfr-retriever.hh" #include "logger.hh" #include "dns.hh" #include "arguments.hh" diff --git a/pdns/tcpreceiver.cc b/pdns/tcpreceiver.cc index 80cf3f9f4ca9..8ee6102c5c8f 100644 --- a/pdns/tcpreceiver.cc +++ b/pdns/tcpreceiver.cc @@ -53,7 +53,6 @@ #include "common_startup.hh" #include "packethandler.hh" #include "statbag.hh" -#include "resolver.hh" #include "communicator.hh" #include "namespaces.hh" #include "signingpipe.hh" diff --git a/pdns/tsig-tests.cc b/pdns/tsig-tests.cc index 3a705096cd53..d65bd563ab42 100644 --- a/pdns/tsig-tests.cc +++ b/pdns/tsig-tests.cc @@ -11,7 +11,7 @@ #include "digests.hh" #include "base64.hh" #include "dnssecinfra.hh" -#include "resolver.hh" +#include "axfr-retriever.hh" #include "arguments.hh" #include "dns_random.hh"