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