Package: pdns Severity: wishlist Tags: patch Hi pdns maintainers,
the attached dpatch ## DP: Adds support for maximum TCP request time (command line arg). is used here (UI) to improve pdns. It applies cleanly to 2.9.21.2-1 after the existing dpatches. We would be enlightened if you could review it and eventually consider adding it to the Debian package. Thanks! Stephan -- System Information: Debian Release: 5.0 APT prefers testing APT policy: (500, 'testing') Architecture: amd64 (x86_64) Kernel: Linux 2.6.26-1-amd64 (SMP w/2 CPU cores) Locale: LANG=en_GB.UTF-8, LC_CTYPE=en_GB.UTF-8 (charmap=UTF-8) Shell: /bin/sh linked to /bin/bash
#! /bin/sh /usr/share/dpatch/dpatch-run ## ui-20_max-tcp-request-time.dpatch by Stephan Suerken <[email protected]> ## ## All lines beginning with `## DP:' are a description of the patch. ## ## DP: Adds support for maximum TCP request time (command line arg). @DPATCH@ diff -urNad trunk~/pdns/common_startup.cc trunk/pdns/common_startup.cc --- trunk~/pdns/common_startup.cc 2009-01-05 13:14:42.000000000 +0000 +++ trunk/pdns/common_startup.cc 2009-01-05 13:14:42.000000000 +0000 @@ -111,6 +111,7 @@ arg().set("soa-expire-default","Default SOA expire")="604800"; arg().set("default-ttl","Seconds a result is valid if not set otherwise")="3600"; + arg().set("max-tcp-request-time","Maximum number of seconds a TCP client may take completing a request")="120"; arg().set("max-tcp-connections","Maximum number of TCP connections")="10"; arg().set("tcp-idle-timeout","Number of seconds a TCP client can be idle during a request")="10"; arg().setSwitch("no-shuffle","Set this to prevent random shuffling of answers - for regression testing")="off"; diff -urNad trunk~/pdns/misc.cc trunk/pdns/misc.cc --- trunk~/pdns/misc.cc 2009-01-05 13:14:42.000000000 +0000 +++ trunk/pdns/misc.cc 2009-01-05 13:14:42.000000000 +0000 @@ -597,3 +597,23 @@ return dom.substr(0,dom.size()-1); } + +void addTimeval(struct timeval & tv, const struct timeval & add) +{ + tv.tv_usec += add.tv_usec; + if (tv.tv_usec >= 1000000) { + tv.tv_usec -= 1000000; + ++tv.tv_sec; + } + tv.tv_sec += add.tv_sec; +} + +void subTimeval(struct timeval & tv, const struct timeval & sub) +{ + if (tv.tv_usec < sub.tv_usec) { + tv.tv_usec += 1000000; + --tv.tv_sec; + } + tv.tv_usec -= sub.tv_usec; + tv.tv_sec -= sub.tv_sec; +} diff -urNad trunk~/pdns/misc.hh trunk/pdns/misc.hh --- trunk~/pdns/misc.hh 2009-01-05 13:14:42.000000000 +0000 +++ trunk/pdns/misc.hh 2009-01-05 13:14:42.000000000 +0000 @@ -91,6 +91,18 @@ uint32_t getLong(const unsigned char *p); uint32_t getLong(const char *p); boost::optional<int> logFacilityToLOG(unsigned int facility); +void addTimeval(struct timeval & tv, const struct timeval & add); +void subTimeval(struct timeval & tv, const struct timeval & sub); + +inline bool timevalZero(const struct timeval & tv) +{ + return (tv.tv_sec<0 || (tv.tv_sec==0 && tv.tv_usec==0)); +} + +inline int waitForData(int fd, const struct timeval & tv) +{ + return waitForData(fd, tv.tv_sec, tv.tv_usec); +} inline void putLong(unsigned char* p, uint32_t val) { diff -urNad trunk~/pdns/pdns.conf-dist trunk/pdns/pdns.conf-dist --- trunk~/pdns/pdns.conf-dist 2009-01-05 13:14:42.000000000 +0000 +++ trunk/pdns/pdns.conf-dist 2009-01-05 13:14:42.000000000 +0000 @@ -334,4 +334,8 @@ # # wildcards= +################################# +# max-tcp-request-time Maximum number of seconds a TCP client may take completing a request +# +# max-tcp-request-time=120 diff -urNad trunk~/pdns/resolver.cc trunk/pdns/resolver.cc --- trunk~/pdns/resolver.cc 2009-01-05 13:14:42.000000000 +0000 +++ trunk/pdns/resolver.cc 2009-01-05 13:14:42.000000000 +0000 @@ -111,30 +111,6 @@ } } -char* Resolver::sendReceive(const string &ip, uint16_t remotePort, const char *packet, int length, unsigned int *replen) -{ - makeTCPSocket(ip, remotePort); - - if(sendData(packet,length,d_sock)<0) - throw ResolverException("Unable to send packet to remote nameserver "+ip+": "+stringerror()); - - int plen=getLength(); - if(plen<0) - throw ResolverException("EOF trying to get length of answer from remote TCP server"); - - char *answer=new char[plen]; - try { - timeoutReadn(answer,plen); - *replen=plen; - return answer; - } - catch(...) { - delete answer; - throw; // whop! - } - return 0; -} - int Resolver::notify(int sock, const string &domain, const string &ip, uint16_t id) { vector<uint8_t> packet; @@ -337,15 +313,21 @@ return 1; } -int Resolver::getLength() +int Resolver::getLength(struct timeval &req_time) { int bytesLeft=2; unsigned char buf[2]; while(bytesLeft) { + // FIXED, check for expired request time + if(timevalZero(req_time)) + throw ResolverException("Closing connection to remote TCP client max. request time reached"); + // FIXED, decrease request time + struct timeval tv={10,0}; + subTimeval(req_time, tv); // FIXED, select timeout was not handled int ret=0; - if(waitForData(d_sock, 10)<1) { + if(waitForData(d_sock, tv)<1) { Utility::closesocket(d_sock); d_sock=-1; throw ResolverException("Waiting on data from remote TCP client: "+stringerror()); @@ -357,6 +339,8 @@ if(!ret) return -1; + // FIXED, increase request time again + addTimeval(req_time, tv); bytesLeft-=ret; } return buf[0]*256+buf[1]; @@ -371,7 +355,9 @@ } // d_sock is connected and is about to spit out a packet - int len=getLength(); + // FIXED, get and pass max request time + struct timeval time_left={arg().asNum("max-tcp-request-time"),0}; + int len=getLength(time_left); if(len<0) throw ResolverException("EOF trying to read axfr chunk from remote TCP client"); diff -urNad trunk~/pdns/resolver.hh trunk/pdns/resolver.hh --- trunk~/pdns/resolver.hh 2007-04-21 13:56:36.000000000 +0000 +++ trunk/pdns/resolver.hh 2009-01-05 13:14:42.000000000 +0000 @@ -60,7 +60,6 @@ void sendResolve(const string &ip, const char *domain, int type); int receiveResolve(struct sockaddr* fromaddr, Utility::socklen_t addrlen); - char* sendReceive(const string &ip, uint16_t remotePort, const char *packet, int length, unsigned int *replylen); void getSoaSerial(const string &, const string &, uint32_t *); int axfrChunk(Resolver::res_t &res); vector<DNSResourceRecord> result(); @@ -72,7 +71,7 @@ void timeoutReadn(char *buffer, int bytes); int d_sock; unsigned char *d_buf; - int getLength(); + int getLength(struct timeval &req_time); int d_len; int d_soacount; string d_domain; diff -urNad trunk~/pdns/tcpreceiver.cc trunk/pdns/tcpreceiver.cc --- trunk~/pdns/tcpreceiver.cc 2009-01-05 13:14:42.000000000 +0000 +++ trunk/pdns/tcpreceiver.cc 2009-01-05 13:14:42.000000000 +0000 @@ -76,16 +76,22 @@ } // throws AhuException if things didn't go according to plan, returns 0 if really 0 bytes were read -int readnWithTimeout(int fd, void* buffer, unsigned int n, bool throwOnEOF=true) +int readnWithTimeout(int fd, void* buffer, unsigned int n, struct timeval &req_time, bool throwOnEOF=true) { unsigned int bytes=n; char *ptr = (char*)buffer; int ret; while(bytes) { + // FIXED, check for expired request time + if(timevalZero(req_time)) + throw AhuException("Closing connection to remote TCP client. Maximum request time reached"); + // FIXED, decrease request time + struct timeval tv={5,0}; + subTimeval(req_time, tv); ret=read(fd, ptr, bytes); if(ret < 0) { if(errno==EAGAIN) { - ret=waitForData(fd, 5); + ret=waitForData(fd, tv); if(ret < 0) throw AhuException("Waiting for data read"); if(!ret) @@ -102,6 +108,8 @@ throw AhuException("Did not fulfill read from TCP due to EOF"); } + // FIXED, increase request time again + addTimeval(req_time, tv); ptr += ret; bytes -= ret; } @@ -177,16 +185,16 @@ } -void TCPNameserver::getQuestion(int fd, char *mesg, int pktlen, const ComboAddress &remote) +void TCPNameserver::getQuestion(int fd, char *mesg, int pktlen, const ComboAddress& remote, struct timeval &req_time) try { - readnWithTimeout(fd, mesg, pktlen); + readnWithTimeout(fd, mesg, pktlen, req_time); } catch(AhuException& ae) { throw AhuException("Error reading DNS data from TCP client "+remote.toString()+": "+ae.reason); } -static void proxyQuestion(shared_ptr<DNSPacket> packet) +static void proxyQuestion(shared_ptr<DNSPacket> packet, struct timeval &req_time) { int sock=socket(AF_INET, SOCK_STREAM, 0); if(sock < 0) @@ -209,11 +217,13 @@ int ret; - ret=readnWithTimeout(sock, &len, 2); + // FIXED, pass max request time to readnWithTimeout call + ret=readnWithTimeout(sock, &len, 2, req_time); len=ntohs(len); char answer[len]; - ret=readnWithTimeout(sock, answer, len); + // FIXED, pass max request time to readnWithTimeout call + ret=readnWithTimeout(sock, answer, len, req_time); slen=htons(len); writenWithTimeout(packet->getSocket(), &slen, 2); @@ -241,6 +251,8 @@ DLOG(L<<"TCP Connection accepted on fd "<<fd<<endl); for(;;) { + // FIXED, getting max request value + struct timeval time_left={arg().asNum("max-tcp-request-time"),0}; ComboAddress remote; socklen_t remotelen=sizeof(remote); if(getpeername(fd, (struct sockaddr *)&remote, &remotelen) < 0) { @@ -249,7 +261,8 @@ } uint16_t pktlen; - if(!readnWithTimeout(fd, &pktlen, 2, false)) + // fix, passing max request time to readnWithTimeout call + if(!readnWithTimeout(fd, &pktlen, 2, time_left, false)) break; else pktlen=ntohs(pktlen); @@ -259,7 +272,8 @@ break; } - getQuestion(fd,mesg,pktlen,remote); + // fix, passing max request time to getQuestion call + getQuestion(fd,mesg,pktlen,remote,time_left); S.inc("tcp-queries"); packet=shared_ptr<DNSPacket>(new DNSPacket); @@ -297,7 +311,7 @@ reply=shared_ptr<DNSPacket>(s_P->questionOrRecurse(packet.get(), &shouldRecurse)); // we really need to ask the backend :-) if(shouldRecurse) { - proxyQuestion(packet); + proxyQuestion(packet,time_left); continue; } } diff -urNad trunk~/pdns/tcpreceiver.hh trunk/pdns/tcpreceiver.hh --- trunk~/pdns/tcpreceiver.hh 2007-04-21 13:56:36.000000000 +0000 +++ trunk/pdns/tcpreceiver.hh 2009-01-05 13:14:42.000000000 +0000 @@ -50,7 +50,7 @@ static void sendPacket(boost::shared_ptr<DNSPacket> p, int outsock); static int readLength(int fd, ComboAddress *remote); - static void getQuestion(int fd, char *mesg, int pktlen, const ComboAddress& remote); + static void getQuestion(int fd, char *mesg, int pktlen, const ComboAddress& remote, struct timeval &req_time); static int doAXFR(const string &target, boost::shared_ptr<DNSPacket> q, int outsock); static bool canDoAXFR(boost::shared_ptr<DNSPacket> q); static void *doConnection(void *data);

