[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