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

Revision 156, 12.2 KB (checked in by ahu, 10 years ago)

working up to 2.9.7

  • 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
39#ifndef WIN32
40extern "C" {
41  int sem_init(sem_t*, int, unsigned int){return 0;}
42  int sem_wait(sem_t*){return 0;}
43  int sem_trywait(sem_t*){return 0;}
44  int sem_post(sem_t*){return 0;}
45  int sem_getvalue(sem_t*, int*){return 0;}
46}
47#endif // WIN32
48
49StatBag S;
50ArgvMap &arg()
51{
52  static ArgvMap theArg;
53  return theArg;
54}
55int d_clientsock;
56int d_serversock;
57
58struct PacketID
59{
60  u_int16_t id;
61  struct sockaddr_in remote;
62};
63
64bool operator<(const PacketID& a, const PacketID& b)
65{
66  if(a.id<b.id)
67    return true;
68
69  if(a.id==b.id) {
70    if(a.remote.sin_addr.s_addr < b.remote.sin_addr.s_addr)
71      return true;
72    if(a.remote.sin_addr.s_addr == b.remote.sin_addr.s_addr)
73      if(a.remote.sin_port < b.remote.sin_port)
74        return true;
75  }
76
77  return false;
78}
79
80MTasker<PacketID,string> MT(100000); // could probably be way lower
81
82/* these two functions are used by LWRes */
83int asendto(const char *data, int len, int flags, struct sockaddr *toaddr, int addrlen, int id) 
84{
85  return sendto(d_clientsock, data, len, flags, toaddr, addrlen);
86}
87
88int arecvfrom(char *data, int len, int flags, struct sockaddr *toaddr, Utility::socklen_t *addrlen, int *d_len, int id)
89{
90  PacketID pident;
91  pident.id=id;
92  memcpy(&pident.remote,toaddr,sizeof(pident.remote));
93
94  string packet;
95  if(!MT.waitEvent(pident,&packet,1)) { // timeout
96    return 0; 
97  }
98
99  *d_len=packet.size();
100  memcpy(data,packet.c_str(),min(len,*d_len));
101
102  return 1;
103}
104
105typedef map<string,set<DNSResourceRecord> > cache_t;
106cache_t cache;
107int cacheHits, cacheMisses;
108int getCache(const string &qname, const QType& qt, set<DNSResourceRecord>* res)
109{
110  cache_t::const_iterator j=cache.find(toLower(qname)+"|"+qt.getName());
111  if(j!=cache.end() && j->first==toLower(qname)+"|"+qt.getName() && j->second.begin()->ttl>(unsigned int)time(0)) {
112    if(res)
113      *res=j->second;
114
115    return (unsigned int)j->second.begin()->ttl-time(0);
116  }
117
118  return -1;
119}
120
121void replaceCache(const string &qname, const QType& qt,  const set<DNSResourceRecord>& content)
122{
123  cache[toLower(qname)+"|"+qt.getName()]=content;
124}
125
126void init(void)
127{
128  // prime root cache
129  static char*ips[]={"198.41.0.4", "128.9.0.107", "192.33.4.12", "128.8.10.90", "192.203.230.10", "192.5.5.241", "192.112.36.4", "128.63.2.53", 
130                     "192.36.148.17","192.58.128.30", "193.0.14.129", "198.32.64.12", "202.12.27.33"};
131  DNSResourceRecord arr, nsrr;
132  arr.qtype=QType::A;
133  arr.ttl=time(0)+3600000;
134  nsrr.qtype=QType::NS;
135  nsrr.ttl=time(0)+3600000;
136 
137  set<DNSResourceRecord>nsset;
138  for(char c='a';c<='m';++c) {
139    static char templ[40];
140    strncpy(templ,"a.root-servers.net", sizeof(templ) - 1);
141    *templ=c;
142    arr.qname=nsrr.content=templ;
143    arr.content=ips[c-'a'];
144    set<DNSResourceRecord>aset;
145    aset.insert(arr);
146    replaceCache(string(templ),QType(QType::A),aset);
147
148    nsset.insert(nsrr);
149  }
150  replaceCache("",QType(QType::NS),nsset);
151}
152
153void startDoResolve(void *p)
154{
155  try {
156    bool quiet=arg().mustDo("quiet");
157    DNSPacket P=*(DNSPacket *)p;
158
159    delete (DNSPacket *)p;
160
161    vector<DNSResourceRecord>ret;
162    DNSPacket *R=P.replyPacket();
163    R->setA(false);
164    R->setRA(true);
165
166    SyncRes sr;
167    if(!quiet)
168      L<<Logger::Error<<"["<<MT.getTid()<<"] question for '"<<P.qdomain<<"|"<<P.qtype.getName()<<"' from "<<P.getRemote()<<endl;
169
170    sr.setId(MT.getTid());
171    if(!P.d.rd)
172      sr.setCacheOnly();
173
174    int res=sr.beginResolve(P.qdomain, P.qtype, ret);
175    if(res<0)
176      R->setRcode(RCode::ServFail);
177    else {
178      R->setRcode(res);
179      for(vector<DNSResourceRecord>::const_iterator i=ret.begin();i!=ret.end();++i)
180        R->addRecord(*i);
181    }
182
183    const char *buffer=R->getData();
184    sendto(d_serversock,buffer,R->len,0,(struct sockaddr *)(R->remote),R->d_socklen);
185    if(!quiet) {
186      L<<Logger::Error<<"["<<MT.getTid()<<"] answer to "<<(P.d.rd?"":"non-rd ")<<"question '"<<P.qdomain<<"|"<<P.qtype.getName();
187      L<<"': "<<ntohs(R->d.ancount)<<" answers, "<<ntohs(R->d.arcount)<<" additional, took "<<sr.d_outqueries<<" packets, "<<
188        sr.d_throttledqueries<<" throttled, rcode="<<res<<endl;
189    }
190   
191    sr.d_outqueries ? cacheMisses++ : cacheHits++;
192
193    delete R;
194  }
195  catch(AhuException &ae) {
196    L<<Logger::Error<<"startDoResolve problem: "<<ae.reason<<endl;
197  }
198  catch(...) {
199    L<<Logger::Error<<"Any other exception in a resolver context"<<endl;
200  }
201}
202
203void makeClientSocket()
204{
205  d_clientsock=socket(AF_INET, SOCK_DGRAM,0);
206  if(d_clientsock<0) 
207    throw AhuException("Making a socket for resolver: "+stringerror());
208 
209  struct sockaddr_in sin;
210  memset((char *)&sin,0, sizeof(sin));
211 
212  sin.sin_family = AF_INET;
213  sin.sin_addr.s_addr = INADDR_ANY;
214 
215  int tries=10;
216  while(--tries) {
217    u_int16_t port=10000+Utility::random()%10000;
218    sin.sin_port = htons(port); 
219   
220    if (bind(d_clientsock, (struct sockaddr *)&sin, sizeof(sin)) >= 0) 
221      break;
222   
223  }
224  if(!tries)
225    throw AhuException("Resolver binding to local socket: "+stringerror());
226}
227
228void makeServerSocket()
229{
230  d_serversock=socket(AF_INET, SOCK_DGRAM,0);
231  if(d_serversock<0) 
232    throw AhuException("Making a server socket for resolver: "+stringerror());
233 
234  struct sockaddr_in sin;
235  memset((char *)&sin,0, sizeof(sin));
236 
237  sin.sin_family = AF_INET;
238
239  if(arg()["local-address"]=="0.0.0.0") {
240    L<<Logger::Warning<<"It is advised to bind to explicit addresses with the --local-address option"<<endl;
241    sin.sin_addr.s_addr = INADDR_ANY;
242  }
243  else {
244    struct hostent *h=0;
245    h=gethostbyname(arg()["local-address"].c_str());
246    if(!h)
247      throw AhuException("Unable to resolve local address"); 
248   
249    sin.sin_addr.s_addr=*(int*)h->h_addr;
250  }
251
252  sin.sin_port = htons(arg().asNum("local-port")); 
253   
254  if (bind(d_serversock, (struct sockaddr *)&sin, sizeof(sin))<0) 
255    throw AhuException("Resolver binding to server socket: "+stringerror());
256  L<<Logger::Error<<"Incoming query source port: "<<arg().asNum("local-port")<<endl;
257}
258
259#ifndef WIN32
260void daemonize(void)
261{
262  if(fork())
263    exit(0); // bye bye
264 
265  setsid(); 
266
267  // cleanup open fds, but skip sockets
268  close(0);
269  close(1);
270  close(2);
271
272}
273#endif
274
275int counter, qcounter;
276bool statsWanted;
277
278void usr1Handler(int)
279{
280  statsWanted=true;
281}
282void doStats(void)
283{
284  if(qcounter) {
285    L<<Logger::Error<<"stats: "<<qcounter<<" questions, "<<cache.size()<<" cache entries, "<<SyncRes::s_negcache.size()<<" negative entries, "
286     <<(int)((cacheHits*100.0)/(cacheHits+cacheMisses))<<"% cache hits";
287    L<<Logger::Error<<", outpacket/query ratio "<<(int)(SyncRes::s_outqueries*100.0/SyncRes::s_queries)<<"%";
288    L<<Logger::Error<<", "<<(int)(SyncRes::s_throttledqueries*100.0/(SyncRes::s_outqueries+SyncRes::s_throttledqueries))<<"% throttled"<<endl;
289  }
290  statsWanted=false;
291}
292
293void houseKeeping(void *)
294{
295  static time_t last_stat, last_rootupdate;
296
297  if(time(0)-last_stat>1800) { 
298    doStats();
299    last_stat=time(0);
300  }
301  if(time(0)-last_rootupdate>7200) {
302    SyncRes sr;
303    vector<DNSResourceRecord>ret;
304
305    sr.setNoCache();
306    int res=sr.beginResolve("", QType(QType::NS), ret);
307    if(!res) {
308      L<<Logger::Error<<"Refreshed . records"<<endl;
309      last_rootupdate=time(0);
310    }
311    else
312      L<<Logger::Error<<"Failed to update . records, RCODE="<<res<<endl;
313  }
314}
315
316int main(int argc, char **argv) 
317{
318#ifdef WIN32
319    WSADATA wsaData;
320    WSAStartup( MAKEWORD( 2, 0 ), &wsaData );
321#endif // WIN32
322
323  try {
324    Utility::srandom(time(0));
325    arg().set("soa-minimum-ttl","Don't change")="0";
326    arg().set("soa-serial-offset","Don't change")="0";
327    arg().set("aaaa-additional-processing","turn on to do AAAA additional processing (slow)")="off";
328    arg().set("local-port","port to listen on")="53";
329    arg().set("local-address","port to listen on")="0.0.0.0";
330    arg().set("trace","if we should output heaps of logging")="off";
331    arg().set("daemon","Operate as a daemon")="yes";
332    arg().set("quiet","Suppress logging of questions and answers")="off";
333    arg().set("config-dir","Location of configuration directory (recursor.conf)")=SYSCONFDIR;
334    arg().setCmd("help","Provide a helpful message");
335    L.toConsole(Logger::Warning);
336    arg().laxParse(argc,argv); // do a lax parse
337
338    string configname=arg()["config-dir"]+"/recursor.conf";
339    cleanSlashes(configname);
340
341    if(!arg().file(configname.c_str())) 
342      L<<Logger::Warning<<"Unable to parse configuration file '"<<configname<<"'"<<endl;
343
344    arg().parse(argc,argv);
345
346    if(arg().mustDo("help")) {
347      cerr<<"syntax:"<<endl<<endl;
348      cerr<<arg().helpstring(arg()["help"])<<endl;
349      exit(99);
350    }
351
352    L.setName("pdns_recursor");
353
354    if(arg().mustDo("trace"))
355      SyncRes::setLog(true);
356   
357    makeClientSocket();
358    makeServerSocket();
359       
360    char data[1500];
361    struct sockaddr_in fromaddr;
362   
363    PacketID pident;
364    init();   
365    L<<Logger::Warning<<"Done priming cache with root hints"<<endl;
366#ifndef WIN32
367    if(arg().mustDo("daemon")) {
368      L.toConsole(Logger::Critical);
369      daemonize();
370    }
371    signal(SIGUSR1,usr1Handler);
372#endif
373
374    for(;;) {
375      while(MT.schedule()); // housekeeping, let threads do their thing
376     
377      if(!((counter++)%100)) 
378        MT.makeThread(houseKeeping,0);
379      if(statsWanted)
380        doStats();
381
382      Utility::socklen_t addrlen=sizeof(fromaddr);
383      int d_len;
384      DNSPacket P;
385     
386      struct timeval tv;
387      tv.tv_sec=0;
388      tv.tv_usec=500000;
389     
390      fd_set readfds;
391      FD_ZERO( &readfds );
392      FD_SET( d_clientsock, &readfds );
393      FD_SET( d_serversock, &readfds );
394
395
396      /* this should listen on a TCP port as well for new connections,  */
397      int selret = select( max(d_clientsock,d_serversock) + 1, &readfds, NULL, NULL, &tv );
398      if(selret<=0) 
399        if (selret == -1 && errno!=EINTR) 
400          throw AhuException("Select returned: "+stringerror());
401        else
402          continue;
403
404      if(FD_ISSET(d_clientsock,&readfds)) { // do we have a question response?
405        d_len=recvfrom(d_clientsock, data, sizeof(data), 0, (sockaddr *)&fromaddr, &addrlen);   
406        if(d_len<0) 
407          continue;
408       
409        P.setRemote((struct sockaddr *)&fromaddr, addrlen);
410        if(P.parse(data,d_len)<0) {
411          L<<Logger::Error<<"Unparseable packet from remote server "<<P.getRemote()<<endl;
412        }
413        else { 
414          if(P.d.qr) {
415
416            pident.remote=fromaddr;
417            pident.id=P.d.id;
418            string packet;
419            packet.assign(data,d_len);
420            MT.sendEvent(pident,&packet);
421          }
422          else 
423            L<<Logger::Warning<<"Ignoring question on outgoing socket from "<<P.getRemote()<<endl;
424        }
425      }
426     
427      if(FD_ISSET(d_serversock,&readfds)) { // do we have a new question?
428        d_len=recvfrom(d_serversock, data, sizeof(data), 0, (sockaddr *)&fromaddr, &addrlen);   
429        if(d_len<0) 
430          continue;
431        P.setRemote((struct sockaddr *)&fromaddr, addrlen);
432        if(P.parse(data,d_len)<0) {
433          L<<Logger::Error<<"Unparseable packet from remote client "<<P.getRemote()<<endl;
434        }
435        else { 
436          if(P.d.qr)
437            L<<Logger::Error<<"Ignoring answer on server socket!"<<endl;
438          else {
439            ++qcounter;
440            MT.makeThread(startDoResolve,(void*)new DNSPacket(P));
441
442          }
443        }
444      }
445    }
446  }
447  catch(AhuException &ae) {
448    L<<Logger::Error<<"Exception: "<<ae.reason<<endl;
449  }
450  catch(exception &e) {
451    L<<Logger::Error<<"STL Exception: "<<e.what()<<endl;
452  }
453  catch(...) {
454    L<<Logger::Error<<"any other exception in main: "<<endl;
455  }
456 
457#ifdef WIN32
458  WSACleanup();
459#endif // WIN32
460
461  return 0;
462}
Note: See TracBrowser for help on using the browser.