[Pdns-dev] Multiple backend recursors patch
Andrew
andrewm at afrihost.com
Tue Aug 11 18:33:54 CEST 2015
Hi there,
I did a proof of concept today for pdns-authoritative to query multiple back-ends simultaneously. This is a useful performance hack for scenarios involving slow inter-continental transit. With this patch, and assistance from a remote DNS server, our server can beat the warmed-up google cache (occasionally).
The patch modifies the semantics of the --recursor configuration item to be a comma-separated list of IP-address:port. For TCP queries, only the first recursor is used (it was too hard to fix).
I would like to modify this code to have a delay schedule for each recursor, so queries are only sent to an alternate recursor if they are not answered by the primary recursor in (say) 20ms ... but I probably won't.
Please excuse the code -- C++ is not my native language -- I suspect I should have used boost-something. This patch is also not as intrusive as it looks - it's mostly changes in indentation:
diff -ur pdns-3.3/pdns/dnsproxy.cc pdns-3.3-hacked/pdns/dnsproxy.cc
--- pdns-3.3/pdns/dnsproxy.cc 2013-04-26 21:54:34.000000000 +0200
+++ pdns-3.3-hacked/pdns/dnsproxy.cc 2015-08-11 15:21:15.128488212 +0200
@@ -24,6 +24,8 @@
#include "dns.hh"
#include "logger.hh"
#include "statbag.hh"
+#include <sstream>
+#include <string>
extern StatBag S;
@@ -31,39 +33,49 @@
DNSProxy::DNSProxy(const string &remote)
{
+ for (int backend=0;backend<MAXRECURSORS;backend++) { d_sock[backend]=0; }
pthread_mutex_init(&d_lock,0);
d_resanswers=S.getPointer("recursing-answers");
d_resquestions=S.getPointer("recursing-questions");
d_udpanswers=S.getPointer("udp-answers");
- ComboAddress remaddr(remote, 53);
- if((d_sock=socket(remaddr.sin4.sin_family, SOCK_DGRAM,0))<0)
- throw AhuException(string("socket: ")+strerror(errno));
-
- ComboAddress local;
- if(remaddr.sin4.sin_family==AF_INET)
- local = ComboAddress("0.0.0.0");
- else
- local = ComboAddress("::");
-
- int n=0;
- for(;n<10;n++) {
- local.sin4.sin_port = htons(10000+( Utility::random()%50000));
-
- if(::bind(d_sock, (struct sockaddr *)&local, local.getSocklen()) >= 0)
- break;
- }
- if(n==10) {
- Utility::closesocket(d_sock);
- d_sock=-1;
- throw AhuException(string("binding dnsproxy socket: ")+strerror(errno));
- }
+ std::stringstream ss(remote);
+ int sock_index = 0;
+ while (ss.good()) {
+ string recursor_ip;
+ getline( ss, recursor_ip, ',' );
- if(connect(d_sock, (sockaddr *)&remaddr, remaddr.getSocklen())<0)
- throw AhuException("Unable to UDP connect to remote nameserver "+remaddr.toStringWithPort()+": "+stringerror());
+ ComboAddress remaddr(recursor_ip, 53);
+
+ if((d_sock[sock_index]=socket(remaddr.sin4.sin_family, SOCK_DGRAM,0))<0)
+ throw AhuException(string("socket: ")+strerror(errno));
+
+ ComboAddress local;
+ if(remaddr.sin4.sin_family==AF_INET)
+ local = ComboAddress("0.0.0.0");
+ else
+ local = ComboAddress("::");
+
+ int n=0;
+ for(;n<10;n++) {
+ local.sin4.sin_port = htons(10000+( Utility::random()%50000));
+
+ if(::bind(d_sock[sock_index], (struct sockaddr *)&local, local.getSocklen()) >= 0)
+ break;
+ }
+ if(n==10) {
+ Utility::closesocket(d_sock[sock_index]);
+ d_sock[sock_index]=-1;
+ throw AhuException(string("binding dnsproxy socket: ")+strerror(errno));
+ }
+
+ if(connect(d_sock[sock_index], (sockaddr *)&remaddr, remaddr.getSocklen())<0)
+ throw AhuException("Unable to UDP connect to remote nameserver "+remaddr.toStringWithPort()+": "+stringerror());
+ L<<Logger::Error<<"DNS Proxy launched, local port "<<ntohs(local.sin4.sin_port)<<", remote "<<remaddr.toStringWithPort()<<endl;
+ sock_index++;
+ }
d_xor=Utility::random()&0xffff;
- L<<Logger::Error<<"DNS Proxy launched, local port "<<ntohs(local.sin4.sin_port)<<", remote "<<remaddr.toStringWithPort()<<endl;
}
void DNSProxy::go()
@@ -113,8 +125,10 @@
const string& buffer = p->getString();
- if(send(d_sock,buffer.c_str(), buffer.length() , 0)<0) { // zoom
- L<<Logger::Error<<"Unable to send a packet to our recursing backend: "<<stringerror()<<endl;
+ for (int backend=0; d_sock[backend] && backend<MAXRECURSORS; backend++) {
+ if(send(d_sock[backend],buffer.c_str(), buffer.length() , 0)<0) { // zoom
+ L<<Logger::Error<<"Unable to send a packet to our recursing backend: "<<stringerror()<<endl;
+ }
}
(*d_resquestions)++;
return true;
@@ -146,54 +160,75 @@
char buffer[1500];
int len;
+ fd_set rfds;
+ struct timeval tv;
+
+ /* Watch stdin (fd 0) to see when it has input. */
+
for(;;) {
- len=recv(d_sock, buffer, sizeof(buffer),0); // answer from our backend
- if(len<12) {
- if(len<0)
- L<<Logger::Error<<"Error receiving packet from recursor backend: "<<stringerror()<<endl;
- else if(len==0)
- L<<Logger::Error<<"Error receiving packet from recursor backend, EOF"<<endl;
- else
- L<<Logger::Error<<"Short packet from recursor backend, "<<len<<" bytes"<<endl;
-
- continue;
+ FD_ZERO(&rfds);
+ int nfds = 0;
+ for (int backend=0;backend<MAXRECURSORS && d_sock[backend]; backend++ ) {
+ FD_SET(d_sock[backend], &rfds);
+ if (d_sock[backend]>=nfds) {
+ nfds = d_sock[backend]+1;
+ }
}
- (*d_resanswers)++;
- (*d_udpanswers)++;
- dnsheader d;
- memcpy(&d,buffer,sizeof(d));
- {
- Lock l(&d_lock);
-#ifdef WORDS_BIGENDIAN
- // this is needed because spoof ID down below does not respect the native byteorder
- d.id = ( 256 * (uint16_t)buffer[1] ) + (uint16_t)buffer[0];
-#endif
- map_t::iterator i=d_conntrack.find(d.id^d_xor);
- if(i==d_conntrack.end()) {
- L<<Logger::Error<<"Discarding untracked packet from recursor backend with id "<<(d.id^d_xor)<<
- ". Conntrack table size="<<d_conntrack.size()<<endl;
+ tv.tv_sec = 8640000; // 100 days ...
+ tv.tv_usec = 0;
+ select(nfds, &rfds, NULL, NULL, &tv);
+ for (int backend=0; backend<MAXRECURSORS && d_sock[backend]; backend++) {
+ if (!FD_ISSET(d_sock[backend],&rfds) )
+ continue;
+
+ len=recv(d_sock[backend], buffer, sizeof(buffer),0); // answer from our backend
+ if(len<12) {
+ if(len<0)
+ L<<Logger::Error<<"Error receiving packet from recursor backend "<<backend<<": "<<stringerror()<<endl;
+ else if(len==0)
+ L<<Logger::Error<<"Error receiving packet from recursor backend "<<backend<<", EOF"<<endl;
+ else
+ L<<Logger::Error<<"Short packet from recursor backend "<<backend<<", "<<len<<" bytes"<<endl;
+
continue;
}
- else if(i->second.created==0) {
- L<<Logger::Error<<"Received packet from recursor backend with id "<<(d.id^d_xor)<<" which is a duplicate"<<endl;
- continue;
- }
- d.id=i->second.id;
- memcpy(buffer,&d,sizeof(d)); // commit spoofed id
-
- DNSPacket p,q;
- p.parse(buffer,len);
- q.parse(buffer,len);
-
- if(p.qtype.getCode() != i->second.qtype || p.qdomain != i->second.qname) {
- L<<Logger::Error<<"Discarding packet from recursor backend with id "<<(d.id^d_xor)<<
- ", qname or qtype mismatch"<<endl;
- continue;
+ (*d_resanswers)++;
+ (*d_udpanswers)++;
+ dnsheader d;
+ memcpy(&d,buffer,sizeof(d));
+ {
+ Lock l(&d_lock);
+ #ifdef WORDS_BIGENDIAN
+ // this is needed because spoof ID down below does not respect the native byteorder
+ d.id = ( 256 * (uint16_t)buffer[1] ) + (uint16_t)buffer[0];
+ #endif
+ map_t::iterator i=d_conntrack.find(d.id^d_xor);
+ if(i==d_conntrack.end()) {
+ L<<Logger::Error<<"Discarding untracked packet from recursor backend "<<backend<<" with id "<<(d.id^d_xor)<<
+ ". Conntrack table size="<<d_conntrack.size()<<endl;
+ continue;
+ }
+ else if(i->second.created==0) {
+ L<<Logger::Error<<"Received packet from recursor backend "<<backend<<" with id "<<(d.id^d_xor)<<" which is a duplicate"<<endl;
+ continue;
+ }
+ d.id=i->second.id;
+ memcpy(buffer,&d,sizeof(d)); // commit spoofed id
+
+ DNSPacket p,q;
+ p.parse(buffer,len);
+ q.parse(buffer,len);
+
+ if(p.qtype.getCode() != i->second.qtype || p.qdomain != i->second.qname) {
+ L<<Logger::Error<<"Discarding packet from recursor backend "<<backend<<" with id "<<(d.id^d_xor)<<
+ ", qname or qtype mismatch"<<endl;
+ continue;
+ }
+ sendto(i->second.outsock, buffer, len, 0, (struct sockaddr*)&i->second.remote, i->second.remote.getSocklen());
+
+ PC.insert(&q, &p);
+ i->second.created=0;
}
- sendto(i->second.outsock, buffer, len, 0, (struct sockaddr*)&i->second.remote, i->second.remote.getSocklen());
-
- PC.insert(&q, &p);
- i->second.created=0;
}
}
}
diff -ur pdns-3.3/pdns/dnsproxy.hh pdns-3.3-hacked/pdns/dnsproxy.hh
--- pdns-3.3/pdns/dnsproxy.hh 2013-04-26 21:54:34.000000000 +0200
+++ pdns-3.3-hacked/pdns/dnsproxy.hh 2015-08-11 13:25:34.188595751 +0200
@@ -49,6 +49,7 @@
To fix: how to remove the stale entries that will surely accumulate
*/
+#define MAXRECURSORS 10
class DNSProxy
{
public:
@@ -66,7 +67,7 @@
bool recurseFor(DNSPacket* p);
private:
NetmaskGroup d_ng;
- int d_sock;
+ int d_sock[MAXRECURSORS];
unsigned int* d_resanswers;
unsigned int* d_udpanswers;
unsigned int* d_resquestions;
File pdns-3.3/pdns/pdns.controlsocket is a socket while file pdns-3.3-hacked/pdns/pdns.controlsocket is a socket
diff -ur pdns-3.3/pdns/tcpreceiver.cc pdns-3.3-hacked/pdns/tcpreceiver.cc
--- pdns-3.3/pdns/tcpreceiver.cc 2013-07-04 20:32:30.000000000 +0200
+++ pdns-3.3-hacked/pdns/tcpreceiver.cc 2015-08-11 14:30:18.000535577 +0200
@@ -46,6 +46,7 @@
#include "communicator.hh"
#include "namespaces.hh"
#include "signingpipe.hh"
+#include <sstream>
extern PacketCache PC;
extern StatBag S;
@@ -202,7 +203,11 @@
Utility::setNonBlocking(sock);
ServiceTuple st;
st.port=53;
- parseService(::arg()["recursor"],st);
+ /* use first listed recursor for tcp queries */
+ std::stringstream ss(::arg()["recursor"]);
+ string recursor_ip;
+ getline( ss, recursor_ip, ',' );
+ parseService(recursor_ip,st);
try {
ComboAddress recursor(st.host, st.port);
Kind Regards
Andrew McGill
Afrihost Internet Services
office: 011 612 7200 | mobile: 0116127258 | fax: 086 552 8000
physical address:Afrihost HQ, 376 Rivonia Boulevard, Sandton, Gauteng
online: www.afrihost.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: pdns-3-3-multi-recursor.diff
Type: text/x-patch
Size: 9756 bytes
Desc: not available
URL: <http://mailman.powerdns.com/pipermail/pdns-dev/attachments/20150811/d95ca4e4/attachment.bin>
More information about the Pdns-dev
mailing list