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
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);
_______________________________________________ Pdns-dev mailing list Pdns-dev@mailman.powerdns.com http://mailman.powerdns.com/mailman/listinfo/pdns-dev