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

Revision 135, 10.6 KB (checked in by ahu, 10 years ago)

heap of works

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