Skip to content

Commit

Permalink
Split the AXFRRetriever from resolver.cc
Browse files Browse the repository at this point in the history
ALso, this moves the `parseResult` function into the pdns::resolver
namespace
  • Loading branch information
pieterlexis committed Apr 1, 2020
1 parent 6a10f08 commit 0dd42db
Show file tree
Hide file tree
Showing 15 changed files with 356 additions and 280 deletions.
4 changes: 4 additions & 0 deletions pdns/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down Expand Up @@ -636,6 +637,7 @@ ixfrdist_SOURCES = \
qtype.cc \
rcpgenerator.cc rcpgenerator.hh \
resolver.cc \
axfr-retriever.cc \
pollmplexer.cc \
sillyrecords.cc \
sstuff.hh \
Expand Down Expand Up @@ -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 \
Expand Down Expand Up @@ -823,6 +826,7 @@ toysdig_LDADD += $(P11KIT1_LIBS)
endif

tsig_tests_SOURCES = \
axfr-retriever.cc \
arguments.cc \
base32.cc \
base64.cc base64.hh \
Expand Down
248 changes: 248 additions & 0 deletions pdns/axfr-retriever.cc
Original file line number Diff line number Diff line change
@@ -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<char>(new char[65536]);
d_remote = remote; // mostly for error reporting
this->connect(timeout);
d_soacount = 0;

vector<uint8_t> 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<char*>(&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<DNSRecord>* 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<bytes) {
int res=waitForData(d_sock, timeoutsec-(time(nullptr)-start));
if(res<0)
throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
if(!res)
throw ResolverException("Timeout while reading data from remote nameserver over TCP");

numread=recv(d_sock, d_buf.get()+n, bytes-n, 0);
if(numread<0)
throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
if(numread==0)
throw ResolverException("Remote nameserver closed TCP connection");
n+=numread;
}
}

void AXFRRetriever::connect(uint16_t timeout)
{
setNonBlocking( d_sock );

int err;

if((err=::connect(d_sock,(struct sockaddr*)&d_remote, d_remote.getSocklen()))<0 && errno!=EINPROGRESS) {
try {
closesocket(d_sock);
}
catch(const PDNSException& e) {
d_sock=-1;
throw ResolverException("Error closing AXFR socket after connect() failed: "+e.reason);
}

throw ResolverException("connect: "+stringerror());
}

if(!err)
goto done;

err=waitForRWData(d_sock, false, timeout, 0); // wait for writeability

if(!err) {
try {
closesocket(d_sock); // timeout
}
catch(const PDNSException& e) {
d_sock=-1;
throw ResolverException("Error closing AXFR socket after timeout: "+e.reason);
}

d_sock=-1;
errno=ETIMEDOUT;

throw ResolverException("Timeout connecting to server");
}
else if(err < 0) {
throw ResolverException("Error connecting: "+stringerror());
}
else {
Utility::socklen_t len=sizeof(err);
if(getsockopt(d_sock, SOL_SOCKET,SO_ERROR,(char *)&err,&len)<0)
throw ResolverException("Error connecting: "+stringerror()); // Solaris

if(err)
throw ResolverException("Error connecting: "+string(strerror(err)));
}

done:
setBlocking( d_sock );
// d_sock now connected
}

int AXFRRetriever::getLength(uint16_t timeout)
{
timeoutReadn(2, timeout);
return (unsigned char)d_buf[0]*256+(unsigned char)d_buf[1];
}

56 changes: 56 additions & 0 deletions pdns/axfr-retriever.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* 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 <boost/utility.hpp>

#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<DNSRecord>* 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<char> 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;
};
1 change: 0 additions & 1 deletion pdns/communicator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion pdns/ixfrdist.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
#include <condition_variable>
#include "ixfr.hh"
#include "ixfrutils.hh"
#include "resolver.hh"
#include "axfr-retriever.hh"
#include "dns_random.hh"
#include "sstuff.hh"
#include "mplexer.hh"
Expand Down
2 changes: 1 addition & 1 deletion pdns/ixplore.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
#include "dns_random.hh"
#include "gss_context.hh"
#include <boost/multi_index_container.hpp>
#include "resolver.hh"
#include "axfr-retriever.hh"
#include <fstream>
#include "ixfr.hh"
#include "ixfrutils.hh"
Expand Down
2 changes: 2 additions & 0 deletions pdns/recursordist/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down Expand Up @@ -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 \
Expand Down
1 change: 1 addition & 0 deletions pdns/recursordist/axfr-retriever.cc
1 change: 1 addition & 0 deletions pdns/recursordist/axfr-retriever.hh
Loading

0 comments on commit 0dd42db

Please sign in to comment.