root/trunk/pdns/pdns/pdns_recursor.cc @ 251

Revision 251, 16.9 KB (checked in by ahu, 9 years ago)

Close bug #13 - or at least, the parts I consider a bug. Furthermore, it
turned out pdns did not properly set the REUSEADDR flag (doing so only after
binding).

Also made some local variables truly local to the file.

Thanks to Christopher Meer.

  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1/*
2    PowerDNS Versatile Database Driven Nameserver
3    Copyright (C) 2002  PowerDNS.COM BV
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18*/
19
20#include "utility.hh"
21#include <iostream>
22#include <errno.h>
23#include <map>
24#include <set>
25#ifndef WIN32
26#include <netdb.h>
27#endif // WIN32
28#include <stdio.h>
29#include <signal.h>
30#include <stdlib.h>
31#include <unistd.h>
32#include "mtasker.hh"
33#include <utility>
34#include "dnspacket.hh"
35#include "statbag.hh"
36#include "arguments.hh"
37#include "syncres.hh"
38#include <fcntl.h>
39#include <fstream>
40#include "recursor_cache.hh"
41
42MemRecursorCache RC;
43
44string s_programname="pdns_recursor";
45
46#ifndef WIN32
47extern "C" {
48  int sem_init(sem_t*, int, unsigned int){return 0;}
49  int sem_wait(sem_t*){return 0;}
50  int sem_trywait(sem_t*){return 0;}
51  int sem_post(sem_t*){return 0;}
52  int sem_getvalue(sem_t*, int*){return 0;}
53  pthread_t pthread_self(void){return (pthread_t) 0;}
54  int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr){ return 0; }
55  int pthread_mutex_lock(pthread_mutex_t *mutex){ return 0; }
56  int pthread_mutex_unlock(pthread_mutex_t *mutex) { return 0; }
57
58}
59#endif // WIN32
60
61StatBag S;
62ArgvMap &arg()
63{
64  static ArgvMap theArg;
65  return theArg;
66}
67static int d_clientsock;
68static int d_serversock;
69static int d_tcpserversock;
70
71struct PacketID
72{
73  u_int16_t id;
74  struct sockaddr_in remote;
75};
76
77bool operator<(const PacketID& a, const PacketID& b)
78{
79  if(a.id<b.id)
80    return true;
81
82  if(a.id==b.id) {
83    if(a.remote.sin_addr.s_addr < b.remote.sin_addr.s_addr)
84      return true;
85    if(a.remote.sin_addr.s_addr == b.remote.sin_addr.s_addr)
86      if(a.remote.sin_port < b.remote.sin_port)
87        return true;
88  }
89
90  return false;
91}
92
93MTasker<PacketID,string>* MT;
94
95/* these two functions are used by LWRes */
96int asendto(const char *data, int len, int flags, struct sockaddr *toaddr, int addrlen, int id) 
97{
98  return sendto(d_clientsock, data, len, flags, toaddr, addrlen);
99}
100
101int arecvfrom(char *data, int len, int flags, struct sockaddr *toaddr, Utility::socklen_t *addrlen, int *d_len, int id)
102{
103  PacketID pident;
104  pident.id=id;
105  memcpy(&pident.remote,toaddr,sizeof(pident.remote));
106
107  string packet;
108  if(!MT->waitEvent(pident,&packet,1)) { // timeout
109    return 0; 
110  }
111
112  *d_len=packet.size();
113  memcpy(data,packet.c_str(),min(len,*d_len));
114
115  return 1;
116}
117
118
119
120
121static void writePid(void)
122{
123  string fname=arg()["socket-dir"]+"/"+s_programname+".pid";
124  ofstream of(fname.c_str());
125  if(of)
126    of<<getpid()<<endl;
127  else
128    L<<Logger::Error<<"Requested to write pid for "<<getpid()<<" to "<<fname<<" failed: "<<strerror(errno)<<endl;
129}
130
131void primeHints(void)
132{
133  // prime root cache
134
135  static char*ips[]={"198.41.0.4", "192.228.79.201", "192.33.4.12", "128.8.10.90", "192.203.230.10", "192.5.5.241", "192.112.36.4", "128.63.2.53", 
136                     "192.36.148.17","192.58.128.30", "193.0.14.129", "198.32.64.12", "202.12.27.33"};
137  DNSResourceRecord arr, nsrr;
138  arr.qtype=QType::A;
139  arr.ttl=time(0)+3600000;
140  nsrr.qtype=QType::NS;
141  nsrr.ttl=time(0)+3600000;
142 
143  set<DNSResourceRecord>nsset;
144  for(char c='a';c<='m';++c) {
145    static char templ[40];
146    strncpy(templ,"a.root-servers.net", sizeof(templ) - 1);
147    *templ=c;
148    arr.qname=nsrr.content=templ;
149    arr.content=ips[c-'a'];
150    set<DNSResourceRecord>aset;
151    aset.insert(arr);
152    RC.replace(string(templ),QType(QType::A),aset);
153
154    nsset.insert(nsrr);
155  }
156  RC.replace("",QType(QType::NS),nsset);
157}
158
159void startDoResolve(void *p)
160{
161  try {
162    bool quiet=arg().mustDo("quiet");
163    DNSPacket P=*(DNSPacket *)p;
164
165    delete (DNSPacket *)p;
166
167    vector<DNSResourceRecord>ret;
168    DNSPacket *R=P.replyPacket();
169    R->setA(false);
170    R->setRA(true);
171
172    SyncRes sr;
173    if(!quiet)
174      L<<Logger::Error<<"["<<MT->getTid()<<"] question for '"<<P.qdomain<<"|"<<P.qtype.getName()<<"' from "<<P.getRemote()<<endl;
175
176    sr.setId(MT->getTid());
177    if(!P.d.rd)
178      sr.setCacheOnly();
179
180    int res=sr.beginResolve(P.qdomain, P.qtype, ret);
181    if(res<0)
182      R->setRcode(RCode::ServFail);
183    else {
184      R->setRcode(res);
185      for(vector<DNSResourceRecord>::const_iterator i=ret.begin();i!=ret.end();++i)
186        R->addRecord(*i);
187    }
188
189    const char *buffer=R->getData();
190    if(!R->getSocket())
191      sendto(d_serversock,buffer,R->len,0,(struct sockaddr *)(R->remote),R->d_socklen);
192    else {
193      char buf[2];
194      buf[0]=R->len/256;
195      buf[1]=R->len%256;
196      if(write(R->getSocket(),buf,2)!=2 || write(R->getSocket(),buffer,R->len)!=R->len)
197        L<<Logger::Error<<"Oops, partial answer sent to "<<P.getRemote()<<" - probably would have trouble receiving our answer anyhow (size="<<R->len<<")"<<endl;
198    }
199
200    if(!quiet) {
201      L<<Logger::Error<<"["<<MT->getTid()<<"] answer to "<<(P.d.rd?"":"non-rd ")<<"question '"<<P.qdomain<<"|"<<P.qtype.getName();
202      L<<"': "<<ntohs(R->d.ancount)<<" answers, "<<ntohs(R->d.arcount)<<" additional, took "<<sr.d_outqueries<<" packets, "<<
203        sr.d_throttledqueries<<" throttled, "<<sr.d_timeouts<<" timeouts, rcode="<<res<<endl;
204    }
205   
206    sr.d_outqueries ? RC.cacheMisses++ : RC.cacheHits++; 
207
208    delete R;
209  }
210  catch(AhuException &ae) {
211    L<<Logger::Error<<"startDoResolve problem: "<<ae.reason<<endl;
212  }
213  catch(...) {
214    L<<Logger::Error<<"Any other exception in a resolver context"<<endl;
215  }
216}
217
218void makeClientSocket()
219{
220  d_clientsock=socket(AF_INET, SOCK_DGRAM,0);
221  if(d_clientsock<0) 
222    throw AhuException("Making a socket for resolver: "+stringerror());
223 
224  struct sockaddr_in sin;
225  memset((char *)&sin,0, sizeof(sin));
226 
227  sin.sin_family = AF_INET;
228  sin.sin_addr.s_addr = INADDR_ANY;
229 
230  int tries=10;
231  while(--tries) {
232    u_int16_t port=10000+Utility::random()%10000;
233    sin.sin_port = htons(port); 
234   
235    if (bind(d_clientsock, (struct sockaddr *)&sin, sizeof(sin)) >= 0) 
236      break;
237   
238  }
239  if(!tries)
240    throw AhuException("Resolver binding to local socket: "+stringerror());
241}
242
243void makeTCPServerSocket()
244{
245  d_tcpserversock=socket(AF_INET, SOCK_STREAM,0);
246  if(d_tcpserversock<0) 
247    throw AhuException("Making a server socket for resolver: "+stringerror());
248 
249  struct sockaddr_in sin;
250  memset((char *)&sin,0, sizeof(sin));
251 
252  sin.sin_family = AF_INET;
253
254  if(arg()["local-address"]=="0.0.0.0") {
255    sin.sin_addr.s_addr = INADDR_ANY;
256  }
257  else {
258    if(!IpToU32(arg()["local-address"], &sin.sin_addr.s_addr))
259      throw AhuException("Unable to resolve local address"); 
260  }
261
262  sin.sin_port = htons(arg().asNum("local-port")); 
263
264  int tmp=1;
265  if(setsockopt(d_tcpserversock,SOL_SOCKET,SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0) {
266    L<<Logger::Error<<"Setsockopt failed"<<endl;
267    exit(1); 
268  }
269   
270  if (bind(d_tcpserversock, (struct sockaddr *)&sin, sizeof(sin))<0) 
271    throw AhuException("TCP Resolver binding to server socket: "+stringerror());
272 
273
274  Utility::setNonBlocking(d_tcpserversock);
275
276  listen(d_tcpserversock, 128);
277}
278
279void makeServerSocket()
280{
281  d_serversock=socket(AF_INET, SOCK_DGRAM,0);
282  if(d_serversock<0) 
283    throw AhuException("Making a server socket for resolver: "+stringerror());
284 
285  struct sockaddr_in sin;
286  memset((char *)&sin,0, sizeof(sin));
287 
288  sin.sin_family = AF_INET;
289
290  if(arg()["local-address"]=="0.0.0.0") {
291    L<<Logger::Warning<<"It is advised to bind to explicit addresses with the --local-address option"<<endl;
292    sin.sin_addr.s_addr = INADDR_ANY;
293  }
294  else {
295   
296    if(!IpToU32(arg()["local-address"], &sin.sin_addr.s_addr))
297      throw AhuException("Unable to resolve local address"); 
298
299  }
300
301  sin.sin_port = htons(arg().asNum("local-port")); 
302   
303  if (bind(d_serversock, (struct sockaddr *)&sin, sizeof(sin))<0) 
304    throw AhuException("Resolver binding to server socket: "+stringerror());
305
306  Utility::setNonBlocking(d_serversock);
307
308  L<<Logger::Error<<"Incoming query source port: "<<arg().asNum("local-port")<<endl;
309}
310
311
312#ifndef WIN32
313void daemonize(void)
314{
315  if(fork())
316    exit(0); // bye bye
317 
318  setsid(); 
319
320  // cleanup open fds, but skip sockets
321  close(0);
322  close(1);
323  close(2);
324
325}
326#endif
327
328int counter, qcounter;
329bool statsWanted;
330
331void usr1Handler(int)
332{
333  statsWanted=true;
334}
335
336
337void doStats(void)
338{
339  if(qcounter) {
340   
341    L<<Logger::Error<<"stats: "<<qcounter<<" questions, "<<RC.size()<<" cache entries, "<<SyncRes::s_negcache.size()<<" negative entries, "
342     <<(int)((RC.cacheHits*100.0)/(RC.cacheHits+RC.cacheMisses))<<"% cache hits";
343    L<<Logger::Error<<", outpacket/query ratio "<<(int)(SyncRes::s_outqueries*100.0/SyncRes::s_queries)<<"%";
344    L<<Logger::Error<<", "<<(int)(SyncRes::s_throttledqueries*100.0/(SyncRes::s_outqueries+SyncRes::s_throttledqueries))<<"% throttled, "
345     <<SyncRes::s_nodelegated<<" no-delegation drops"<<endl;
346    L<<Logger::Error<<"queries running: "<<MT->numProcesses()<<endl;
347   
348  }
349  statsWanted=false;
350}
351
352void houseKeeping(void *)
353{
354  static time_t last_stat, last_rootupdate, last_prune;
355
356  if(time(0)-last_stat>60) { 
357    RC.doPrune();
358    last_prune=time(0);
359  }
360  if(time(0)-last_stat>1800) { 
361    doStats();
362    last_stat=time(0);
363  }
364  if(time(0)-last_rootupdate>7200) {
365    SyncRes sr;
366    vector<DNSResourceRecord>ret;
367
368    sr.setNoCache();
369    int res=sr.beginResolve("", QType(QType::NS), ret);
370    if(!res) {
371      L<<Logger::Error<<"Refreshed . records"<<endl;
372      last_rootupdate=time(0);
373    }
374    else
375      L<<Logger::Error<<"Failed to update . records, RCODE="<<res<<endl;
376  }
377}
378
379struct TCPConnection
380{
381  int fd;
382  enum {BYTE0, BYTE1, GETQUESTION} state;
383  int qlen;
384  int bytesread;
385  struct sockaddr_in remote;
386  char data[65535];
387};
388
389int main(int argc, char **argv) 
390{
391#ifdef WIN32
392    WSADATA wsaData;
393    WSAStartup( MAKEWORD( 2, 0 ), &wsaData );
394#endif // WIN32
395
396  try {
397    Utility::srandom(time(0));
398    arg().set("soa-minimum-ttl","Don't change")="0";
399    arg().set("soa-serial-offset","Don't change")="0";
400    arg().set("aaaa-additional-processing","turn on to do AAAA additional processing (slow)")="off";
401    arg().set("local-port","port to listen on")="53";
402    arg().set("local-address","port to listen on")="0.0.0.0";
403    arg().set("trace","if we should output heaps of logging")="off";
404    arg().set("daemon","Operate as a daemon")="yes";
405    arg().set("quiet","Suppress logging of questions and answers")="off";
406    arg().set("config-dir","Location of configuration directory (recursor.conf)")=SYSCONFDIR;
407    arg().set("socket-dir","Where the controlsocket will live")=LOCALSTATEDIR;
408    arg().set("delegation-only","Which domains we only accept delegations from")="";
409    arg().setCmd("help","Provide a helpful message");
410    L.toConsole(Logger::Warning);
411    arg().laxParse(argc,argv); // do a lax parse
412
413    string configname=arg()["config-dir"]+"/recursor.conf";
414    cleanSlashes(configname);
415
416    if(!arg().file(configname.c_str())) 
417      L<<Logger::Warning<<"Unable to parse configuration file '"<<configname<<"'"<<endl;
418
419    arg().parse(argc,argv);
420
421    arg().set("delegation-only")=toLower(arg()["delegation-only"]);
422
423    if(arg().mustDo("help")) {
424      cerr<<"syntax:"<<endl<<endl;
425      cerr<<arg().helpstring(arg()["help"])<<endl;
426      exit(99);
427    }
428
429    L.setName("pdns_recursor");
430
431    if(arg().mustDo("trace"))
432      SyncRes::setLog(true);
433   
434    makeClientSocket();
435    makeServerSocket();
436    makeTCPServerSocket();
437       
438    MT=new MTasker<PacketID,string>(100000);
439
440    char data[1500];
441    struct sockaddr_in fromaddr;
442   
443    PacketID pident;
444    primeHints();   
445    L<<Logger::Warning<<"Done priming cache with root hints"<<endl;
446#ifndef WIN32
447    if(arg().mustDo("daemon")) {
448      L.toConsole(Logger::Critical);
449      daemonize();
450    }
451    signal(SIGUSR1,usr1Handler);
452
453    writePid();
454#endif
455
456    vector<TCPConnection> tcpconnections;
457    counter=0;
458    for(;;) {
459      while(MT->schedule()); // housekeeping, let threads do their thing
460     
461      if(!((counter++)%100)) 
462        MT->makeThread(houseKeeping,0);
463      if(statsWanted)
464        doStats();
465
466      Utility::socklen_t addrlen=sizeof(fromaddr);
467      int d_len;
468      DNSPacket P;
469     
470      struct timeval tv;
471      tv.tv_sec=0;
472      tv.tv_usec=500000;
473     
474      fd_set readfds;
475      FD_ZERO( &readfds );
476      FD_SET( d_clientsock, &readfds );
477      FD_SET( d_serversock, &readfds );
478      FD_SET( d_tcpserversock, &readfds );
479      int fdmax=max(d_tcpserversock,max(d_clientsock,d_serversock));
480      for(vector<TCPConnection>::const_iterator i=tcpconnections.begin();i!=tcpconnections.end();++i) {
481        FD_SET(i->fd, &readfds);
482        fdmax=max(fdmax,i->fd);
483      }
484
485      /* this should listen on a TCP port as well for new connections,  */
486      int selret = select(  fdmax + 1, &readfds, NULL, NULL, &tv );
487      if(selret<=0) 
488        if (selret == -1 && errno!=EINTR) 
489          throw AhuException("Select returned: "+stringerror());
490        else
491          continue;
492
493      if(FD_ISSET(d_clientsock,&readfds)) { // do we have a question response?
494        d_len=recvfrom(d_clientsock, data, sizeof(data), 0, (sockaddr *)&fromaddr, &addrlen);   
495        if(d_len<0) 
496          continue;
497       
498        P.setRemote((struct sockaddr *)&fromaddr, addrlen);
499        if(P.parse(data,d_len)<0) {
500          L<<Logger::Error<<"Unparseable packet from remote server "<<P.getRemote()<<endl;
501        }
502        else { 
503          if(P.d.qr) {
504
505            pident.remote=fromaddr;
506            pident.id=P.d.id;
507            string packet;
508            packet.assign(data,d_len);
509            MT->sendEvent(pident,&packet);
510          }
511          else 
512            L<<Logger::Warning<<"Ignoring question on outgoing socket from "<<P.getRemote()<<endl;
513        }
514      }
515     
516      if(FD_ISSET(d_serversock,&readfds)) { // do we have a new question on udp?
517        d_len=recvfrom(d_serversock, data, sizeof(data), 0, (sockaddr *)&fromaddr, &addrlen);   
518        if(d_len<0) 
519          continue;
520        P.setRemote((struct sockaddr *)&fromaddr, addrlen);
521        if(P.parse(data,d_len)<0) {
522          L<<Logger::Error<<"Unparseable packet from remote client "<<P.getRemote()<<endl;
523        }
524        else { 
525          if(P.d.qr)
526            L<<Logger::Error<<"Ignoring answer on server socket!"<<endl;
527          else {
528            ++qcounter;
529            P.setSocket(0);
530            MT->makeThread(startDoResolve,(void*)new DNSPacket(P));
531
532          }
533        }
534      }
535
536      if(FD_ISSET(d_tcpserversock,&readfds)) { // do we have a new TCP connection
537        struct sockaddr_in addr;
538        socklen_t addrlen=sizeof(addr);
539        int newsock=accept(d_tcpserversock, (struct sockaddr*)&addr, &addrlen);
540
541        if(newsock>0) {
542          Utility::setNonBlocking(newsock);
543          TCPConnection tc;
544          tc.fd=newsock;
545          tc.state=TCPConnection::BYTE0;
546          tc.remote=addr;
547          tcpconnections.push_back(tc);
548        }
549      }
550
551      for(vector<TCPConnection>::iterator i=tcpconnections.begin();i!=tcpconnections.end();++i) {
552        if(FD_ISSET(i->fd, &readfds)) {
553          if(i->state==TCPConnection::BYTE0) {
554            int bytes=read(i->fd,i->data,2);
555            if(bytes==1)
556              i->state=TCPConnection::BYTE1;
557            if(bytes==2) { 
558              i->qlen=(i->data[0]<<8)+i->data[1];
559              i->bytesread=0;
560              i->state=TCPConnection::GETQUESTION;
561            }
562            if(!bytes || bytes < 0) {
563              close(i->fd);
564              tcpconnections.erase(i);
565              break;
566            }
567          }
568          else if(i->state==TCPConnection::BYTE1) {
569            int bytes=read(i->fd,i->data+1,1);
570            if(bytes==1) {
571              i->state=TCPConnection::GETQUESTION;
572              i->qlen=(i->data[0]<<8)+i->data[1];
573              i->bytesread=0;
574            }
575            if(!bytes || bytes < 0) {
576              L<<Logger::Error<<"TCP Remote "<<sockAddrToString(&i->remote,sizeof(i->remote))<<" disconnected after first byte"<<endl;
577              close(i->fd);
578              tcpconnections.erase(i);
579              break;
580            }
581           
582          }
583          else if(i->state==TCPConnection::GETQUESTION) {
584            int bytes=read(i->fd,i->data + i->bytesread,i->qlen - i->bytesread);
585            if(!bytes || bytes < 0) {
586              L<<Logger::Error<<"TCP Remote "<<sockAddrToString(&i->remote,sizeof(i->remote))<<" disconnected while reading question body"<<endl;
587              close(i->fd);
588              tcpconnections.erase(i);
589              break;
590            }
591            i->bytesread+=bytes;
592            if(i->bytesread==i->qlen) {
593              i->state=TCPConnection::BYTE0;
594
595              if(P.parse(i->data,i->qlen)<0) {
596                L<<Logger::Error<<"Unparseable packet from remote client "<<P.getRemote()<<endl;
597                close(i->fd);
598                tcpconnections.erase(i);
599                break;
600              }
601              else { 
602                P.setSocket(i->fd);
603                P.setRemote((struct sockaddr *)&i->remote,sizeof(i->remote));
604                if(P.d.qr)
605                  L<<Logger::Error<<"Ignoring answer on server socket!"<<endl;
606                else {
607                  ++qcounter;
608                  MT->makeThread(startDoResolve,(void*)new DNSPacket(P));
609                }
610              }
611            }
612          }
613        }
614      }
615    }
616  }
617  catch(AhuException &ae) {
618    L<<Logger::Error<<"Exception: "<<ae.reason<<endl;
619  }
620  catch(exception &e) {
621    L<<Logger::Error<<"STL Exception: "<<e.what()<<endl;
622  }
623  catch(...) {
624    L<<Logger::Error<<"any other exception in main: "<<endl;
625  }
626 
627#ifdef WIN32
628  WSACleanup();
629#endif // WIN32
630
631  return 0;
632}
Note: See TracBrowser for help on using the browser.