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

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

Fixed last parts of ticket #13 - the parts I did not consider a bug *were* in fact
a bug hehe.

Thanks to Wichert Akkerman for stubbornly reporting this bug until I got
around to fixing it.

  • 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) 2004  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  Utility::setNonBlocking(d_clientsock);
243}
244
245void makeTCPServerSocket()
246{
247  d_tcpserversock=socket(AF_INET, SOCK_STREAM,0);
248  if(d_tcpserversock<0) 
249    throw AhuException("Making a server socket for resolver: "+stringerror());
250 
251  struct sockaddr_in sin;
252  memset((char *)&sin,0, sizeof(sin));
253 
254  sin.sin_family = AF_INET;
255
256  if(arg()["local-address"]=="0.0.0.0") {
257    sin.sin_addr.s_addr = INADDR_ANY;
258  }
259  else {
260    if(!IpToU32(arg()["local-address"], &sin.sin_addr.s_addr))
261      throw AhuException("Unable to resolve local address"); 
262  }
263
264  sin.sin_port = htons(arg().asNum("local-port")); 
265
266  int tmp=1;
267  if(setsockopt(d_tcpserversock,SOL_SOCKET,SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0) {
268    L<<Logger::Error<<"Setsockopt failed"<<endl;
269    exit(1); 
270  }
271   
272  if (bind(d_tcpserversock, (struct sockaddr *)&sin, sizeof(sin))<0) 
273    throw AhuException("TCP Resolver binding to server socket: "+stringerror());
274 
275
276  Utility::setNonBlocking(d_tcpserversock);
277
278  listen(d_tcpserversock, 128);
279}
280
281void makeServerSocket()
282{
283  d_serversock=socket(AF_INET, SOCK_DGRAM,0);
284  if(d_serversock<0) 
285    throw AhuException("Making a server socket for resolver: "+stringerror());
286 
287  struct sockaddr_in sin;
288  memset((char *)&sin,0, sizeof(sin));
289 
290  sin.sin_family = AF_INET;
291
292  if(arg()["local-address"]=="0.0.0.0") {
293    L<<Logger::Warning<<"It is advised to bind to explicit addresses with the --local-address option"<<endl;
294    sin.sin_addr.s_addr = INADDR_ANY;
295  }
296  else {
297   
298    if(!IpToU32(arg()["local-address"], &sin.sin_addr.s_addr))
299      throw AhuException("Unable to resolve local address"); 
300
301  }
302
303  sin.sin_port = htons(arg().asNum("local-port")); 
304   
305  if (bind(d_serversock, (struct sockaddr *)&sin, sizeof(sin))<0) 
306    throw AhuException("Resolver binding to server socket: "+stringerror());
307
308  Utility::setNonBlocking(d_serversock);
309
310  L<<Logger::Error<<"Incoming query source port: "<<arg().asNum("local-port")<<endl;
311}
312
313
314#ifndef WIN32
315void daemonize(void)
316{
317  if(fork())
318    exit(0); // bye bye
319 
320  setsid(); 
321
322  // cleanup open fds, but skip sockets
323  close(0);
324  close(1);
325  close(2);
326
327}
328#endif
329
330int counter, qcounter;
331bool statsWanted;
332
333void usr1Handler(int)
334{
335  statsWanted=true;
336}
337
338
339void doStats(void)
340{
341  if(qcounter) {
342   
343    L<<Logger::Error<<"stats: "<<qcounter<<" questions, "<<RC.size()<<" cache entries, "<<SyncRes::s_negcache.size()<<" negative entries, "
344     <<(int)((RC.cacheHits*100.0)/(RC.cacheHits+RC.cacheMisses))<<"% cache hits";
345    L<<Logger::Error<<", outpacket/query ratio "<<(int)(SyncRes::s_outqueries*100.0/SyncRes::s_queries)<<"%";
346    L<<Logger::Error<<", "<<(int)(SyncRes::s_throttledqueries*100.0/(SyncRes::s_outqueries+SyncRes::s_throttledqueries))<<"% throttled, "
347     <<SyncRes::s_nodelegated<<" no-delegation drops"<<endl;
348    L<<Logger::Error<<"queries running: "<<MT->numProcesses()<<endl;
349   
350  }
351  statsWanted=false;
352}
353
354void houseKeeping(void *)
355{
356  static time_t last_stat, last_rootupdate, last_prune;
357
358  if(time(0)-last_stat>60) { 
359    RC.doPrune();
360    last_prune=time(0);
361  }
362  if(time(0)-last_stat>1800) { 
363    doStats();
364    last_stat=time(0);
365  }
366  if(time(0)-last_rootupdate>7200) {
367    SyncRes sr;
368    vector<DNSResourceRecord>ret;
369
370    sr.setNoCache();
371    int res=sr.beginResolve("", QType(QType::NS), ret);
372    if(!res) {
373      L<<Logger::Error<<"Refreshed . records"<<endl;
374      last_rootupdate=time(0);
375    }
376    else
377      L<<Logger::Error<<"Failed to update . records, RCODE="<<res<<endl;
378  }
379}
380
381struct TCPConnection
382{
383  int fd;
384  enum {BYTE0, BYTE1, GETQUESTION} state;
385  int qlen;
386  int bytesread;
387  struct sockaddr_in remote;
388  char data[65535];
389};
390
391int main(int argc, char **argv) 
392{
393#ifdef WIN32
394    WSADATA wsaData;
395    WSAStartup( MAKEWORD( 2, 0 ), &wsaData );
396#endif // WIN32
397
398  try {
399    Utility::srandom(time(0));
400    arg().set("soa-minimum-ttl","Don't change")="0";
401    arg().set("soa-serial-offset","Don't change")="0";
402    arg().set("aaaa-additional-processing","turn on to do AAAA additional processing (slow)")="off";
403    arg().set("local-port","port to listen on")="53";
404    arg().set("local-address","single address to listen on")="0.0.0.0";
405    arg().set("trace","if we should output heaps of logging")="off";
406    arg().set("daemon","Operate as a daemon")="yes";
407    arg().set("quiet","Suppress logging of questions and answers")="off";
408    arg().set("config-dir","Location of configuration directory (recursor.conf)")=SYSCONFDIR;
409    arg().set("socket-dir","Where the controlsocket will live")=LOCALSTATEDIR;
410    arg().set("delegation-only","Which domains we only accept delegations from")="";
411    arg().setCmd("help","Provide a helpful message");
412    L.toConsole(Logger::Warning);
413    arg().laxParse(argc,argv); // do a lax parse
414
415    string configname=arg()["config-dir"]+"/recursor.conf";
416    cleanSlashes(configname);
417
418    if(!arg().file(configname.c_str())) 
419      L<<Logger::Warning<<"Unable to parse configuration file '"<<configname<<"'"<<endl;
420
421    arg().parse(argc,argv);
422
423    arg().set("delegation-only")=toLower(arg()["delegation-only"]);
424
425    if(arg().mustDo("help")) {
426      cerr<<"syntax:"<<endl<<endl;
427      cerr<<arg().helpstring(arg()["help"])<<endl;
428      exit(99);
429    }
430
431    L.setName("pdns_recursor");
432
433    if(arg().mustDo("trace"))
434      SyncRes::setLog(true);
435   
436    makeClientSocket();
437    makeServerSocket();
438    makeTCPServerSocket();
439       
440    MT=new MTasker<PacketID,string>(100000);
441
442    char data[1500];
443    struct sockaddr_in fromaddr;
444   
445    PacketID pident;
446    primeHints();   
447    L<<Logger::Warning<<"Done priming cache with root hints"<<endl;
448#ifndef WIN32
449    if(arg().mustDo("daemon")) {
450      L.toConsole(Logger::Critical);
451      daemonize();
452    }
453    signal(SIGUSR1,usr1Handler);
454
455    writePid();
456#endif
457
458    vector<TCPConnection> tcpconnections;
459    counter=0;
460    for(;;) {
461      while(MT->schedule()); // housekeeping, let threads do their thing
462     
463      if(!((counter++)%100)) 
464        MT->makeThread(houseKeeping,0);
465      if(statsWanted)
466        doStats();
467
468      Utility::socklen_t addrlen=sizeof(fromaddr);
469      int d_len;
470      DNSPacket P;
471     
472      struct timeval tv;
473      tv.tv_sec=0;
474      tv.tv_usec=500000;
475     
476      fd_set readfds;
477      FD_ZERO( &readfds );
478      FD_SET( d_clientsock, &readfds );
479      FD_SET( d_serversock, &readfds );
480      FD_SET( d_tcpserversock, &readfds );
481      int fdmax=max(d_tcpserversock,max(d_clientsock,d_serversock));
482      for(vector<TCPConnection>::const_iterator i=tcpconnections.begin();i!=tcpconnections.end();++i) {
483        FD_SET(i->fd, &readfds);
484        fdmax=max(fdmax,i->fd);
485      }
486
487      /* this should listen on a TCP port as well for new connections,  */
488      int selret = select(  fdmax + 1, &readfds, NULL, NULL, &tv );
489      if(selret<=0) 
490        if (selret == -1 && errno!=EINTR) 
491          throw AhuException("Select returned: "+stringerror());
492        else
493          continue;
494
495      if(FD_ISSET(d_clientsock,&readfds)) { // do we have a question response?
496        d_len=recvfrom(d_clientsock, data, sizeof(data), 0, (sockaddr *)&fromaddr, &addrlen);   
497        if(d_len<0) 
498          continue;
499       
500        P.setRemote((struct sockaddr *)&fromaddr, addrlen);
501        if(P.parse(data,d_len)<0) {
502          L<<Logger::Error<<"Unparseable packet from remote server "<<P.getRemote()<<endl;
503        }
504        else { 
505          if(P.d.qr) {
506
507            pident.remote=fromaddr;
508            pident.id=P.d.id;
509            string packet;
510            packet.assign(data,d_len);
511            MT->sendEvent(pident,&packet);
512          }
513          else 
514            L<<Logger::Warning<<"Ignoring question on outgoing socket from "<<P.getRemote()<<endl;
515        }
516      }
517     
518      if(FD_ISSET(d_serversock,&readfds)) { // do we have a new question on udp?
519        d_len=recvfrom(d_serversock, data, sizeof(data), 0, (sockaddr *)&fromaddr, &addrlen);   
520        if(d_len<0) 
521          continue;
522        P.setRemote((struct sockaddr *)&fromaddr, addrlen);
523        if(P.parse(data,d_len)<0) {
524          L<<Logger::Error<<"Unparseable packet from remote client "<<P.getRemote()<<endl;
525        }
526        else { 
527          if(P.d.qr)
528            L<<Logger::Error<<"Ignoring answer on server socket!"<<endl;
529          else {
530            ++qcounter;
531            P.setSocket(0);
532            MT->makeThread(startDoResolve,(void*)new DNSPacket(P));
533
534          }
535        }
536      }
537
538      if(FD_ISSET(d_tcpserversock,&readfds)) { // do we have a new TCP connection
539        struct sockaddr_in addr;
540        socklen_t addrlen=sizeof(addr);
541        int newsock=accept(d_tcpserversock, (struct sockaddr*)&addr, &addrlen);
542
543        if(newsock>0) {
544          Utility::setNonBlocking(newsock);
545          TCPConnection tc;
546          tc.fd=newsock;
547          tc.state=TCPConnection::BYTE0;
548          tc.remote=addr;
549          tcpconnections.push_back(tc);
550        }
551      }
552
553      for(vector<TCPConnection>::iterator i=tcpconnections.begin();i!=tcpconnections.end();++i) {
554        if(FD_ISSET(i->fd, &readfds)) {
555          if(i->state==TCPConnection::BYTE0) {
556            int bytes=read(i->fd,i->data,2);
557            if(bytes==1)
558              i->state=TCPConnection::BYTE1;
559            if(bytes==2) { 
560              i->qlen=(i->data[0]<<8)+i->data[1];
561              i->bytesread=0;
562              i->state=TCPConnection::GETQUESTION;
563            }
564            if(!bytes || bytes < 0) {
565              close(i->fd);
566              tcpconnections.erase(i);
567              break;
568            }
569          }
570          else if(i->state==TCPConnection::BYTE1) {
571            int bytes=read(i->fd,i->data+1,1);
572            if(bytes==1) {
573              i->state=TCPConnection::GETQUESTION;
574              i->qlen=(i->data[0]<<8)+i->data[1];
575              i->bytesread=0;
576            }
577            if(!bytes || bytes < 0) {
578              L<<Logger::Error<<"TCP Remote "<<sockAddrToString(&i->remote,sizeof(i->remote))<<" disconnected after first byte"<<endl;
579              close(i->fd);
580              tcpconnections.erase(i);
581              break;
582            }
583           
584          }
585          else if(i->state==TCPConnection::GETQUESTION) {
586            int bytes=read(i->fd,i->data + i->bytesread,i->qlen - i->bytesread);
587            if(!bytes || bytes < 0) {
588              L<<Logger::Error<<"TCP Remote "<<sockAddrToString(&i->remote,sizeof(i->remote))<<" disconnected while reading question body"<<endl;
589              close(i->fd);
590              tcpconnections.erase(i);
591              break;
592            }
593            i->bytesread+=bytes;
594            if(i->bytesread==i->qlen) {
595              i->state=TCPConnection::BYTE0;
596
597              if(P.parse(i->data,i->qlen)<0) {
598                L<<Logger::Error<<"Unparseable packet from remote client "<<P.getRemote()<<endl;
599                close(i->fd);
600                tcpconnections.erase(i);
601                break;
602              }
603              else { 
604                P.setSocket(i->fd);
605                P.setRemote((struct sockaddr *)&i->remote,sizeof(i->remote));
606                if(P.d.qr)
607                  L<<Logger::Error<<"Ignoring answer on server socket!"<<endl;
608                else {
609                  ++qcounter;
610                  MT->makeThread(startDoResolve,(void*)new DNSPacket(P));
611                }
612              }
613            }
614          }
615        }
616      }
617    }
618  }
619  catch(AhuException &ae) {
620    L<<Logger::Error<<"Exception: "<<ae.reason<<endl;
621  }
622  catch(exception &e) {
623    L<<Logger::Error<<"STL Exception: "<<e.what()<<endl;
624  }
625  catch(...) {
626    L<<Logger::Error<<"any other exception in main: "<<endl;
627  }
628 
629#ifdef WIN32
630  WSACleanup();
631#endif // WIN32
632
633  return 0;
634}
Note: See TracBrowser for help on using the browser.