]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/pdns_recursor.cc
implement RFC 2181 paragraph 5.2:
[thirdparty/pdns.git] / pdns / pdns_recursor.cc
CommitLineData
288f4aa9
BH
1/*
2 PowerDNS Versatile Database Driven Nameserver
22c012a8 3 Copyright (C) 2003 - 2006 PowerDNS.COM BV
288f4aa9
BH
4
5 This program is free software; you can redistribute it and/or modify
f28307ad
BH
6 it under the terms of the GNU General Public License version 2
7 as published by the Free Software Foundation
288f4aa9
BH
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
06bd9ccf 16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
288f4aa9 17*/
caa6eefa 18
705f31ae 19#ifndef WIN32
f7c1d4e3
BH
20# include <netdb.h>
21# include <unistd.h>
22#else
23 #include "ntservice.hh"
24 #include "recursorservice.hh"
705f31ae
BH
25#endif // WIN32
26
caa6eefa 27#include "utility.hh"
288f4aa9
BH
28#include <iostream>
29#include <errno.h>
30#include <map>
31#include <set>
97bb160b 32#include "recursor_cache.hh"
288f4aa9 33#include <stdio.h>
c75a6a9e 34#include <signal.h>
288f4aa9 35#include <stdlib.h>
c038218b 36
288f4aa9
BH
37#include "mtasker.hh"
38#include <utility>
288f4aa9
BH
39#include "arguments.hh"
40#include "syncres.hh"
88def049
BH
41#include <fcntl.h>
42#include <fstream>
5c633640
BH
43#include "sstuff.hh"
44#include <boost/tuple/tuple.hpp>
45#include <boost/tuple/tuple_comparison.hpp>
72df400f 46#include <boost/shared_array.hpp>
ea634573 47#include <boost/lexical_cast.hpp>
7f1fa77d 48#include <boost/function.hpp>
5605c067 49#include <boost/algorithm/string.hpp>
ea634573
BH
50#include "dnsparser.hh"
51#include "dnswriter.hh"
52#include "dnsrecords.hh"
f814d7c8 53#include "zoneparser-tng.hh"
1d5b3ce6 54#include "rec_channel.hh"
aaacf7f2 55#include "logger.hh"
c8ddb7c2 56#include "iputils.hh"
09e6702a 57#include "mplexer.hh"
c038218b 58#include "config.h"
1d5b3ce6 59
a2bfc3ff
BH
60#ifndef RECURSOR
61#include "statbag.hh"
62StatBag S;
63#endif
64
09e6702a
BH
65FDMultiplexer* g_fdm;
66unsigned int g_maxTCPPerClient;
67bool g_logCommonErrors;
33988bfb 68using namespace boost;
5c633640 69
27adc173 70#ifdef __FreeBSD__ // see cvstrac ticket #26
7f617eb9
BH
71#include <pthread.h>
72#include <semaphore.h>
73#endif
74
eefd15f9 75MemRecursorCache RC;
1d5b3ce6
BH
76RecursorStats g_stats;
77bool g_quiet;
c8ddb7c2 78NetmaskGroup* g_allowFrom;
88def049 79string s_programname="pdns_recursor";
0e7d73e9
BH
80typedef vector<int> g_tcpListenSockets_t;
81g_tcpListenSockets_t g_tcpListenSockets;
3159c9ef 82int g_tcpTimeout;
288f4aa9 83
ea634573 84struct DNSComboWriter {
c9e9e5e0 85 DNSComboWriter(const char* data, uint16_t len, const struct timeval& now) : d_mdp(data, len), d_now(now), d_tcp(false), d_socket(-1)
ea634573
BH
86 {}
87 MOADNSParser d_mdp;
37d3f960 88 void setRemote(ComboAddress* sa)
ea634573 89 {
37d3f960 90 d_remote=*sa;
ea634573
BH
91 }
92
93 void setSocket(int sock)
94 {
95 d_socket=sock;
96 }
a1754c6a
BH
97
98 string getRemote() const
99 {
37d3f960 100 return d_remote.toString();
a1754c6a
BH
101 }
102
c9e9e5e0 103 struct timeval d_now;
37d3f960 104 ComboAddress d_remote;
ea634573
BH
105 bool d_tcp;
106 int d_socket;
107};
108
109
27adc173
BH
110#ifndef WIN32
111#ifndef __FreeBSD__
288f4aa9
BH
112extern "C" {
113 int sem_init(sem_t*, int, unsigned int){return 0;}
114 int sem_wait(sem_t*){return 0;}
115 int sem_trywait(sem_t*){return 0;}
116 int sem_post(sem_t*){return 0;}
117 int sem_getvalue(sem_t*, int*){return 0;}
dcf9bd8f 118 pthread_t pthread_self(void){return (pthread_t) 0;}
98e05fce 119 int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr){ return 0; }
dcf9bd8f
BH
120 int pthread_mutex_lock(pthread_mutex_t *mutex){ return 0; }
121 int pthread_mutex_unlock(pthread_mutex_t *mutex) { return 0; }
df38dbe8 122 int pthread_mutex_destroy(pthread_mutex_t *mutex) { return 0; }
288f4aa9 123}
27adc173 124#endif // __FreeBSD__
caa6eefa 125#endif // WIN32
288f4aa9 126
288f4aa9
BH
127ArgvMap &arg()
128{
129 static ArgvMap theArg;
130 return theArg;
131}
4ef015cd 132
09e6702a 133struct timeval g_now;
cd50f30d 134typedef vector<int> tcpserversocks_t;
5c633640 135
35ce8576
BH
136typedef MTasker<PacketID,string> MT_t;
137MT_t* MT;
5c633640 138
09e6702a
BH
139
140void handleTCPClientWritable(int fd, boost::any& var);
141
50c81227 142// -1 is error, 0 is timeout, 1 is success
5c633640
BH
143int asendtcp(const string& data, Socket* sock)
144{
145 PacketID pident;
146 pident.sock=sock;
147 pident.outMSG=data;
5c633640 148
09e6702a 149 g_fdm->addWriteFD(sock->getHandle(), handleTCPClientWritable, pident);
50c81227 150 string packet;
5c633640 151
9170fbaf
BH
152 int ret=MT->waitEvent(pident,&packet,1);
153 if(!ret || ret==-1) { // timeout
09e6702a 154 g_fdm->removeWriteFD(sock->getHandle());
5c633640 155 }
50c81227
BH
156 else if(packet.size() !=data.size()) { // main loop tells us what it sent out, or empty in case of an error
157 return -1;
158 }
9170fbaf 159 return ret;
5c633640
BH
160}
161
09e6702a
BH
162void handleTCPClientReadable(int fd, boost::any& var);
163
9170fbaf 164// -1 is error, 0 is timeout, 1 is success
5c633640 165int arecvtcp(string& data, int len, Socket* sock)
288f4aa9 166{
50c81227 167 data.clear();
5c633640
BH
168 PacketID pident;
169 pident.sock=sock;
170 pident.inNeeded=len;
09e6702a 171 g_fdm->addReadFD(sock->getHandle(), handleTCPClientReadable, pident);
5c633640 172
9170fbaf
BH
173 int ret=MT->waitEvent(pident,&data,1);
174 if(!ret || ret==-1) { // timeout
09e6702a 175 g_fdm->removeReadFD(sock->getHandle());
288f4aa9 176 }
50c81227
BH
177 else if(data.empty()) {// error, EOF or other
178 return -1;
179 }
180
9170fbaf 181 return ret;
288f4aa9
BH
182}
183
998a4334 184// returns -1 for errors which might go away, throws for ones that won't
996c89cc 185int makeClientSocket(int family)
4ef015cd 186{
705f31ae 187 int ret=(int)socket(family, SOCK_DGRAM, 0);
998a4334
BH
188 if(ret < 0 && errno==EMFILE) // this is not a catastrophic error
189 return ret;
190
4ef015cd
BH
191 if(ret<0)
192 throw AhuException("Making a socket for resolver: "+stringerror());
193
996c89cc
BH
194 static optional<ComboAddress> sin4;
195 if(!sin4) {
196 sin4=ComboAddress(::arg()["query-local-address"]);
4ef015cd 197 }
996c89cc
BH
198 static optional<ComboAddress> sin6;
199 if(!sin6) {
200 if(!::arg()["query-local-address6"].empty())
201 sin6=ComboAddress(::arg()["query-local-address6"]);
202 }
203
998a4334 204 int tries=10;
4ef015cd 205 while(--tries) {
998a4334
BH
206 uint16_t port=1025+Utility::random()%64510;
207 if(tries==1) // fall back to kernel 'random'
208 port=0;
78983f94 209
996c89cc
BH
210 if(family==AF_INET) {
211 sin4->sin4.sin_port = htons(port);
212
779828c4 213 if (::bind(ret, (struct sockaddr *)&*sin4, sin4->getSocklen()) >= 0)
996c89cc
BH
214 break;
215 }
216 else {
217 sin6->sin6.sin6_port = htons(port);
218
779828c4 219 if (::bind(ret, (struct sockaddr *)&*sin6, sin6->getSocklen()) >= 0)
996c89cc
BH
220 break;
221 }
4ef015cd
BH
222 }
223 if(!tries)
224 throw AhuException("Resolver binding to local query client socket: "+stringerror());
225
226 Utility::setNonBlocking(ret);
227 return ret;
228}
229
09e6702a
BH
230void handleUDPServerResponse(int fd, boost::any&);
231
4ef015cd
BH
232// you can ask this class for a UDP socket to send a query from
233// this socket is not yours, don't even think about deleting it
234// but after you call 'returnSocket' on it, don't assume anything anymore
235class UDPClientSocks
236{
4ef015cd
BH
237 unsigned int d_numsocks;
238 unsigned int d_maxsocks;
998a4334 239
4ef015cd 240public:
998a4334 241 UDPClientSocks() : d_numsocks(0), d_maxsocks(5000)
4ef015cd
BH
242 {
243 }
244
996c89cc 245 typedef set<int> socks_t;
4ef015cd
BH
246 socks_t d_socks;
247
998a4334 248 // returning -1 means: temporary OS error (ie, out of files)
996c89cc 249 int getSocket(uint16_t family)
4ef015cd 250 {
996c89cc
BH
251 int fd=makeClientSocket(family);
252 if(fd < 0) // temporary error - receive exception otherwise
998a4334
BH
253 return -1;
254
996c89cc 255 d_socks.insert(fd);
998a4334 256 d_numsocks++;
996c89cc 257 return fd;
4ef015cd
BH
258 }
259
095c3045
BH
260 void returnSocket(int fd)
261 {
262 socks_t::iterator i=d_socks.find(fd);
263 returnSocket(i);
264 }
265
4ef015cd 266 // return a socket to the pool, or simply erase it
095c3045 267 void returnSocket(socks_t::iterator& i)
4ef015cd 268 {
600fc20b
BH
269 if(i==d_socks.end()) {
270 throw AhuException("Trying to return a socket not in the pool");
271 }
996c89cc 272 g_fdm->removeReadFD(*i);
c038218b 273 Utility::closesocket(*i);
998a4334
BH
274
275 d_socks.erase(i++);
276 --d_numsocks;
4ef015cd
BH
277 }
278}g_udpclientsocks;
279
288f4aa9
BH
280
281/* these two functions are used by LWRes */
998a4334 282// -2 is OS error, -1 is error that depends on the remote, > 1 is success
996c89cc 283int asendto(const char *data, int len, int flags, const ComboAddress& toaddr, int id, const string& domain, int* fd)
288f4aa9 284{
996c89cc 285 *fd=g_udpclientsocks.getSocket(toaddr.sin4.sin_family);
998a4334
BH
286 if(*fd < 0)
287 return -2;
288 PacketID pident;
289 pident.fd=*fd;
290 pident.id=id;
291 pident.domain=domain;
996c89cc 292 pident.remote=toaddr;
998a4334 293
996c89cc 294 int ret=connect(*fd, (struct sockaddr*)(&toaddr), toaddr.getSocklen());
998a4334
BH
295 if(ret < 0)
296 return ret;
297
298 g_fdm->addReadFD(*fd, handleUDPServerResponse, pident);
299 return send(*fd, data, len, 0);
288f4aa9
BH
300}
301
9170fbaf 302// -1 is error, 0 is timeout, 1 is success
996c89cc 303int arecvfrom(char *data, int len, int flags, const ComboAddress& fromaddr, int *d_len, int id, const string& domain, int fd)
288f4aa9 304{
0d5f0a9f
BH
305 static optional<unsigned int> nearMissLimit;
306 if(!nearMissLimit)
307 nearMissLimit=::arg().asNum("spoof-nearmiss-max");
308
288f4aa9 309 PacketID pident;
4ef015cd 310 pident.fd=fd;
288f4aa9 311 pident.id=id;
0d5f0a9f 312 pident.domain=domain;
996c89cc 313 pident.remote=fromaddr;
b636533b 314
288f4aa9 315 string packet;
29a14b24 316 int ret=MT->waitEvent(pident, &packet, 1);
9170fbaf 317 if(ret > 0) {
996c89cc 318 if(packet.empty()) // means "error"
998a4334 319 return -1;
998a4334 320
705f31ae 321 *d_len=(int)packet.size();
9170fbaf 322 memcpy(data,packet.c_str(),min(len,*d_len));
0d5f0a9f 323 if(*nearMissLimit && pident.nearMisses > *nearMissLimit) {
996c89cc 324 L<<Logger::Error<<"Too many ("<<pident.nearMisses<<" > "<<*nearMissLimit<<") bogus answers for '"<<domain<<"' from "<<fromaddr.toString()<<", assuming spoof attempt."<<endl;
0d5f0a9f 325 g_stats.spoofCount++;
35ce8576
BH
326 return -1;
327 }
288f4aa9 328 }
09e6702a 329 else {
095c3045 330 g_udpclientsocks.returnSocket(fd);
09e6702a 331 }
9170fbaf 332 return ret;
288f4aa9
BH
333}
334
aa4e4cbf 335void setBuffer(int fd, int optname, uint32_t size)
ce8deb27 336{
9b356afc 337 uint32_t psize=0;
91e4ecf3 338 socklen_t len=sizeof(psize);
9b356afc 339
aa4e4cbf 340 if(!getsockopt(fd, SOL_SOCKET, optname, (char*)&psize, &len) && psize > size) {
a19fb8e8 341 L<<Logger::Error<<"Not decreasing socket buffer size from "<<psize<<" to "<<size<<endl;
9b356afc
BH
342 return;
343 }
344
aa4e4cbf 345 if (setsockopt(fd, SOL_SOCKET, optname, (char*)&size, sizeof(size)) < 0 )
a19fb8e8 346 L<<Logger::Error<<"Warning: unable to raise socket buffer size to "<<size<<": "<<strerror(errno)<<endl;
ce8deb27
BH
347}
348
349
aa4e4cbf
BH
350static void setReceiveBuffer(int fd, uint32_t size)
351{
352 setBuffer(fd, SO_RCVBUF, size);
353}
354
355static void setSendBuffer(int fd, uint32_t size)
356{
357 setBuffer(fd, SO_SNDBUF, size);
358}
359
88def049
BH
360static void writePid(void)
361{
2e3d8a19 362 string fname=::arg()["socket-dir"]+"/"+s_programname+".pid";
88def049
BH
363 ofstream of(fname.c_str());
364 if(of)
705f31ae 365 of<< Utility::getpid() <<endl;
88def049 366 else
705f31ae 367 L<<Logger::Error<<"Requested to write pid for "<<Utility::getpid()<<" to "<<fname<<" failed: "<<strerror(errno)<<endl;
88def049
BH
368}
369
bdf40704 370void primeHints(void)
288f4aa9
BH
371{
372 // prime root cache
288f4aa9 373 set<DNSResourceRecord>nsset;
f814d7c8 374
2e3d8a19 375 if(::arg()["hint-file"].empty()) {
f814d7c8
BH
376 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",
377 "192.36.148.17","192.58.128.30", "193.0.14.129", "198.32.64.12", "202.12.27.33"};
378 DNSResourceRecord arr, nsrr;
379 arr.qtype=QType::A;
380 arr.ttl=time(0)+3600000;
381 nsrr.qtype=QType::NS;
382 nsrr.ttl=time(0)+3600000;
383
5456e605 384 for(char c='a';c<='m';++c) {
f814d7c8 385 static char templ[40];
7738a23f 386 strncpy(templ,"a.root-servers.net.", sizeof(templ) - 1);
f814d7c8
BH
387 *templ=c;
388 arr.qname=nsrr.content=templ;
5456e605 389 arr.content=ips[c-'a'];
f814d7c8
BH
390 set<DNSResourceRecord> aset;
391 aset.insert(arr);
392 RC.replace(string(templ), QType(QType::A), aset);
393
394 nsset.insert(nsrr);
395 }
396 }
397 else {
2e3d8a19 398 ZoneParserTNG zpt(::arg()["hint-file"]);
f814d7c8 399 DNSResourceRecord rr;
ea634573 400 set<DNSResourceRecord> aset;
288f4aa9 401
f814d7c8 402 while(zpt.get(rr)) {
f814d7c8
BH
403 rr.ttl+=time(0);
404 if(rr.qtype.getCode()==QType::A) {
405 set<DNSResourceRecord> aset;
406 aset.insert(rr);
407 RC.replace(rr.qname, QType(QType::A), aset);
408 }
409 if(rr.qtype.getCode()==QType::NS) {
e2e2c5d8 410 rr.content=toLower(rr.content);
f814d7c8
BH
411 nsset.insert(rr);
412 }
413 }
288f4aa9 414 }
7738a23f 415 RC.replace(".", QType(QType::NS), nsset); // and stuff in the cache
288f4aa9
BH
416}
417
37d3f960 418map<ComboAddress, uint32_t> g_tcpClientCounts;
0e9d9ce2
BH
419
420struct TCPConnection
421{
422 int fd;
7f1fa77d 423 enum stateenum {BYTE0, BYTE1, GETQUESTION, DONE} state;
0e9d9ce2
BH
424 int qlen;
425 int bytesread;
37d3f960 426 ComboAddress remote;
0e9d9ce2
BH
427 char data[65535];
428 time_t startTime;
429
430 void closeAndCleanup()
431 {
705f31ae 432 Utility::closesocket(fd);
37d3f960
BH
433 if(!g_tcpClientCounts[remote]--)
434 g_tcpClientCounts.erase(remote);
6dcd28c3 435 s_currentConnections--;
0e9d9ce2 436 }
6dcd28c3 437 static unsigned int s_currentConnections; //!< total number of current TCP connections
0e9d9ce2
BH
438};
439
6dcd28c3
BH
440unsigned int TCPConnection::s_currentConnections;
441
288f4aa9
BH
442void startDoResolve(void *p)
443{
7b1469bb 444 DNSComboWriter* dc=(DNSComboWriter *)p;
288f4aa9 445 try {
10321a98
BH
446 uint16_t maxudpsize=512;
447 MOADNSParser::EDNSOpts edo;
448 if(dc->d_mdp.getEDNSOpts(&edo)) {
449 maxudpsize=edo.d_packetsize;
450 }
09e6702a 451
ea634573 452 vector<DNSResourceRecord> ret;
9170fbaf 453
ea634573
BH
454 vector<uint8_t> packet;
455 DNSPacketWriter pw(packet, dc->d_mdp.d_qname, dc->d_mdp.d_qtype, dc->d_mdp.d_qclass);
456
457 pw.getHeader()->aa=0;
458 pw.getHeader()->ra=1;
c154c8a4 459 pw.getHeader()->qr=1;
ea634573 460 pw.getHeader()->id=dc->d_mdp.d_header.id;
10321a98 461 pw.getHeader()->rd=dc->d_mdp.d_header.rd;
ea634573 462
c9e9e5e0 463 SyncRes sr(dc->d_now);
1d5b3ce6 464 if(!g_quiet)
8a63d3ce
BH
465 L<<Logger::Error<<"["<<MT->getTid()<<"] " << (dc->d_tcp ? "TCP " : "") << "question for '"<<dc->d_mdp.d_qname<<"|"
466 <<DNSRecordContent::NumberToType(dc->d_mdp.d_qtype)<<"' from "<<dc->getRemote()<<endl;
c75a6a9e 467
fededf47 468 sr.setId(MT->getTid());
ea634573 469 if(!dc->d_mdp.d_header.rd)
c836dc19
BH
470 sr.setCacheOnly();
471
a9af3782 472 int res=sr.beginResolve(dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_mdp.d_qclass, ret);
1d5b3ce6 473 if(res<0) {
ea634573 474 pw.getHeader()->rcode=RCode::ServFail;
bec87d21 475 // no commit here, because no record
1d5b3ce6
BH
476 g_stats.servFails++;
477 }
288f4aa9 478 else {
ea634573 479 pw.getHeader()->rcode=res;
1d5b3ce6 480 switch(res) {
5e4a2466
BH
481 case RCode::ServFail:
482 g_stats.servFails++;
483 break;
1d5b3ce6
BH
484 case RCode::NXDomain:
485 g_stats.nxDomains++;
486 break;
487 case RCode::NoError:
488 g_stats.noErrors++;
489 break;
490 }
491
c154c8a4 492 if(ret.size()) {
e67e250f 493 shuffle(ret);
c154c8a4 494 for(vector<DNSResourceRecord>::const_iterator i=ret.begin();i!=ret.end();++i) {
a9af3782
BH
495 pw.startRecord(i->qname, i->qtype.getCode(), i->ttl, i->qclass, (DNSPacketWriter::Place)i->d_place);
496 shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(i->qtype.getCode(), i->qclass, i->content));
7b1469bb 497
c154c8a4 498 drc->toPacket(pw);
7b1469bb 499
10321a98
BH
500 if(!dc->d_tcp && pw.size() > maxudpsize) {
501 pw.rollback();
1791e3c4
BH
502 if(i->d_place==DNSResourceRecord::ANSWER) // only truncate if we actually omitted parts of the answer
503 pw.getHeader()->tc=1;
10321a98
BH
504 goto sendit; // need to jump over pw.commit
505 }
c154c8a4
BH
506 }
507 pw.commit();
ea634573 508 }
288f4aa9 509 }
10321a98 510 sendit:;
ea634573 511 if(!dc->d_tcp) {
c038218b 512 sendto(dc->d_socket, (const char*)&*packet.begin(), packet.size(), 0, (struct sockaddr *)(&dc->d_remote), dc->d_remote.getSocklen());
feccc9fc 513 }
9c495589
BH
514 else {
515 char buf[2];
ea634573
BH
516 buf[0]=packet.size()/256;
517 buf[1]=packet.size()%256;
feccc9fc 518
c038218b 519 Utility::iovec iov[2];
feccc9fc 520
ea634573
BH
521 iov[0].iov_base=(void*)buf; iov[0].iov_len=2;
522 iov[1].iov_base=(void*)&*packet.begin(); iov[1].iov_len = packet.size();
feccc9fc 523
c038218b 524 int ret=Utility::writev(dc->d_socket, iov, 2);
0e9d9ce2 525 bool hadError=true;
feccc9fc 526
0e9d9ce2
BH
527 if(ret == 0)
528 L<<Logger::Error<<"EOF writing TCP answer to "<<dc->getRemote()<<endl;
529 else if(ret < 0 )
530 L<<Logger::Error<<"Error writing TCP answer to "<<dc->getRemote()<<": "<< strerror(errno) <<endl;
ea634573 531 else if((unsigned int)ret != 2 + packet.size())
aa4e4cbf 532 L<<Logger::Error<<"Oops, partial answer sent to "<<dc->getRemote()<<" for "<<dc->d_mdp.d_qname<<" (size="<< (2 + packet.size()) <<", sent "<<ret<<")"<<endl;
0e9d9ce2
BH
533 else
534 hadError=false;
09e6702a
BH
535
536 // update tcp connection status, either by closing or moving to 'BYTE0'
537
538 if(hadError) {
539 g_fdm->removeReadFD(dc->d_socket);
705f31ae 540 Utility::closesocket(dc->d_socket);
09e6702a 541 }
a6ae6414 542 else {
0b18b22e 543 any_cast<TCPConnection>(&g_fdm->getReadParameter(dc->d_socket))->state=TCPConnection::BYTE0;
a6ae6414 544 struct timeval now;
c038218b 545 Utility::gettimeofday(&now, 0); // needs to be updated
a6ae6414 546 g_fdm->setReadTTD(dc->d_socket, now, g_tcpTimeout);
0e9d9ce2 547 }
9c495589
BH
548 }
549
1d5b3ce6 550 if(!g_quiet) {
8a63d3ce 551 L<<Logger::Error<<"["<<MT->getTid()<<"] answer to "<<(dc->d_mdp.d_header.rd?"":"non-rd ")<<"question '"<<dc->d_mdp.d_qname<<"|"<<DNSRecordContent::NumberToType(dc->d_mdp.d_qtype);
ea634573 552 L<<"': "<<ntohs(pw.getHeader()->ancount)<<" answers, "<<ntohs(pw.getHeader()->arcount)<<" additional, took "<<sr.d_outqueries<<" packets, "<<
5c633640 553 sr.d_throttledqueries<<" throttled, "<<sr.d_timeouts<<" timeouts, "<<sr.d_tcpoutqueries<<" tcp connections, rcode="<<res<<endl;
c75a6a9e
BH
554 }
555
eefd15f9 556 sr.d_outqueries ? RC.cacheMisses++ : RC.cacheHits++;
fe213470
BH
557 float spent=makeFloat(sr.d_now-dc->d_now);
558 if(spent < 0.001)
559 g_stats.answers0_1++;
560 else if(spent < 0.010)
561 g_stats.answers1_10++;
562 else if(spent < 0.1)
563 g_stats.answers10_100++;
564 else if(spent < 1.0)
565 g_stats.answers100_1000++;
566 else
567 g_stats.answersSlow++;
568
574af7ea 569 uint64_t newLat=(uint64_t)(spent*1000000);
87b8e43a
BH
570 if(newLat < 1000000) // outliers of several minutes exist..
571 g_stats.avgLatencyUsec=(uint64_t)((1-0.0001)*g_stats.avgLatencyUsec + 0.0001*newLat);
ea634573 572 delete dc;
288f4aa9
BH
573 }
574 catch(AhuException &ae) {
c836dc19 575 L<<Logger::Error<<"startDoResolve problem: "<<ae.reason<<endl;
288f4aa9 576 }
7b1469bb
BH
577 catch(MOADNSException& e) {
578 L<<Logger::Error<<"DNS parser error: "<<dc->d_mdp.d_qname<<", "<<e.what()<<endl;
579 }
c154c8a4
BH
580 catch(exception& e) {
581 L<<Logger::Error<<"STL error: "<<e.what()<<endl;
582 }
288f4aa9 583 catch(...) {
c836dc19 584 L<<Logger::Error<<"Any other exception in a resolver context"<<endl;
288f4aa9
BH
585 }
586}
587
1d5b3ce6
BH
588RecursorControlChannel s_rcc;
589
590void makeControlChannelSocket()
591{
41f7a068
BH
592 string sockname=::arg()["socket-dir"]+"/pdns_recursor.controlsocket";
593 if(::arg().mustDo("fork")) {
705f31ae 594 sockname+="."+lexical_cast<string>(Utility::getpid());
41f7a068
BH
595 L<<Logger::Warning<<"Forked control socket name: "<<sockname<<endl;
596 }
597 s_rcc.listen(sockname);
1d5b3ce6
BH
598}
599
09e6702a
BH
600void handleRunningTCPQuestion(int fd, boost::any& var)
601{
0b18b22e 602 TCPConnection* conn=any_cast<TCPConnection>(&var);
667f7e60 603 if(conn->state==TCPConnection::BYTE0) {
c038218b
BH
604
605 int bytes=recv(conn->fd, conn->data, 2, 0);
09e6702a 606 if(bytes==1)
667f7e60 607 conn->state=TCPConnection::BYTE1;
09e6702a 608 if(bytes==2) {
667f7e60
BH
609 conn->qlen=(conn->data[0]<<8)+conn->data[1];
610 conn->bytesread=0;
611 conn->state=TCPConnection::GETQUESTION;
09e6702a
BH
612 }
613 if(!bytes || bytes < 0) {
667f7e60 614 TCPConnection tmp(*conn);
09e6702a 615 g_fdm->removeReadFD(fd);
6dcd28c3 616 tmp.closeAndCleanup();
09e6702a
BH
617 return;
618 }
619 }
667f7e60 620 else if(conn->state==TCPConnection::BYTE1) {
c038218b 621 int bytes=recv(conn->fd,conn->data+1,1,0);
09e6702a 622 if(bytes==1) {
667f7e60
BH
623 conn->state=TCPConnection::GETQUESTION;
624 conn->qlen=(conn->data[0]<<8)+conn->data[1];
625 conn->bytesread=0;
09e6702a
BH
626 }
627 if(!bytes || bytes < 0) {
628 if(g_logCommonErrors)
667f7e60
BH
629 L<<Logger::Error<<"TCP client "<< conn->remote.toString() <<" disconnected after first byte"<<endl;
630 TCPConnection tmp(*conn);
09e6702a 631 g_fdm->removeReadFD(fd);
6dcd28c3 632 tmp.closeAndCleanup(); // conn loses validity here..
09e6702a
BH
633 return;
634 }
635 }
667f7e60 636 else if(conn->state==TCPConnection::GETQUESTION) {
c038218b 637 int bytes=recv(conn->fd,conn->data + conn->bytesread,conn->qlen - conn->bytesread, 0);
09e6702a 638 if(!bytes || bytes < 0) {
667f7e60
BH
639 L<<Logger::Error<<"TCP client "<< conn->remote.toString() <<" disconnected while reading question body"<<endl;
640 TCPConnection tmp(*conn);
09e6702a 641 g_fdm->removeReadFD(fd);
6dcd28c3 642 tmp.closeAndCleanup(); // conn loses validity here..
09e6702a
BH
643
644 return;
645 }
667f7e60
BH
646 conn->bytesread+=bytes;
647 if(conn->bytesread==conn->qlen) {
648 conn->state=TCPConnection::DONE; // this makes us immune from timeouts, from now on *we* are responsible
09e6702a
BH
649 DNSComboWriter* dc=0;
650 try {
667f7e60 651 dc=new DNSComboWriter(conn->data, conn->qlen, g_now);
09e6702a
BH
652 }
653 catch(MOADNSException &mde) {
654 g_stats.clientParseError++;
667f7e60
BH
655 L<<Logger::Error<<"Unable to parse packet from TCP client "<< conn->remote.toString() <<endl;
656 TCPConnection tmp(*conn);
09e6702a 657 g_fdm->removeReadFD(fd);
6dcd28c3 658 tmp.closeAndCleanup();
09e6702a
BH
659 return;
660 }
661
667f7e60 662 dc->setSocket(conn->fd);
09e6702a 663 dc->d_tcp=true;
667f7e60 664 dc->setRemote(&conn->remote);
09e6702a
BH
665 if(dc->d_mdp.d_header.qr)
666 L<<Logger::Error<<"Ignoring answer on server socket!"<<endl;
667 else {
668 ++g_stats.qcounter;
669 ++g_stats.tcpqcounter;
670 MT->makeThread(startDoResolve, dc);
671 return;
672 }
673 }
674 }
675}
676
6dcd28c3 677//! Handle new incoming TCP connection
09e6702a
BH
678void handleNewTCPQuestion(int fd, boost::any& )
679{
37d3f960 680 ComboAddress addr;
09e6702a 681 socklen_t addrlen=sizeof(addr);
705f31ae 682 int newsock=(int)accept(fd, (struct sockaddr*)&addr, &addrlen);
09e6702a 683 if(newsock>0) {
a9af3782 684 g_stats.addRemote(addr);
09e6702a
BH
685 if(g_allowFrom && !g_allowFrom->match(&addr)) {
686 g_stats.unauthorizedTCP++;
705f31ae 687 Utility::closesocket(newsock);
09e6702a
BH
688 return;
689 }
690
37d3f960 691 if(g_maxTCPPerClient && g_tcpClientCounts.count(addr) && g_tcpClientCounts[addr] >= g_maxTCPPerClient) {
09e6702a 692 g_stats.tcpClientOverflow++;
705f31ae 693 Utility::closesocket(newsock); // don't call TCPConnection::closeAndCleanup here - did not enter it in the counts yet!
09e6702a
BH
694 return;
695 }
37d3f960 696 g_tcpClientCounts[addr]++;
09e6702a
BH
697 Utility::setNonBlocking(newsock);
698 TCPConnection tc;
699 tc.fd=newsock;
700 tc.state=TCPConnection::BYTE0;
701 tc.remote=addr;
702 tc.startTime=g_now.tv_sec;
6dcd28c3 703 TCPConnection::s_currentConnections++;
09e6702a 704 g_fdm->addReadFD(tc.fd, handleRunningTCPQuestion, tc);
c038218b 705
0bff046b 706 struct timeval now;
c038218b 707 Utility::gettimeofday(&now, 0);
3159c9ef 708 g_fdm->setReadTTD(tc.fd, now, g_tcpTimeout);
09e6702a
BH
709 }
710}
711
5db529f8
BH
712void handleNewUDPQuestion(int fd, boost::any& var)
713{
a9af3782 714 int len;
5db529f8
BH
715 char data[1500];
716 ComboAddress fromaddr;
717 socklen_t addrlen=sizeof(fromaddr);
718
a9af3782
BH
719 if((len=recvfrom(fd, data, sizeof(data), 0, (sockaddr *)&fromaddr, &addrlen)) >= 0) {
720 g_stats.addRemote(fromaddr);
5db529f8
BH
721 if(g_allowFrom && !g_allowFrom->match(&fromaddr)) {
722 g_stats.unauthorizedUDP++;
a9af3782 723 return;
5db529f8
BH
724 }
725
726 try {
a9af3782 727 DNSComboWriter* dc = new DNSComboWriter(data, len, g_now);
5db529f8
BH
728
729 dc->setRemote(&fromaddr);
730
731 if(dc->d_mdp.d_header.qr) {
732 if(g_logCommonErrors)
733 L<<Logger::Error<<"Ignoring answer from "<<dc->getRemote()<<" on server socket!"<<endl;
734 }
735 else {
736 ++g_stats.qcounter;
737 dc->setSocket(fd);
738 dc->d_tcp=false;
739 MT->makeThread(startDoResolve, (void*) dc);
740 }
741 }
742 catch(MOADNSException& mde) {
743 g_stats.clientParseError++;
744 L<<Logger::Error<<"Unable to parse packet from remote UDP client "<<fromaddr.toString() <<": "<<mde.what()<<endl;
745 }
746 }
747}
748
749typedef vector<pair<int, function< void(int, any&) > > > deferredAdd_t;
750deferredAdd_t deferredAdd;
751
f28307ad 752void makeTCPServerSockets()
9c495589 753{
37d3f960 754 int fd;
f28307ad 755 vector<string>locals;
2e3d8a19 756 stringtok(locals,::arg()["local-address"]," ,");
9c495589 757
f28307ad
BH
758 if(locals.empty())
759 throw AhuException("No local address specified");
760
37d3f960 761 ComboAddress sin;
f28307ad 762 for(vector<string>::const_iterator i=locals.begin();i!=locals.end();++i) {
f28307ad 763 memset((char *)&sin,0, sizeof(sin));
37d3f960 764 sin.sin4.sin_family = AF_INET;
c038218b 765 if(!IpToU32(*i, (uint32_t*)&sin.sin4.sin_addr.s_addr)) {
37d3f960 766 sin.sin6.sin6_family = AF_INET6;
c038218b 767 if(Utility::inet_pton(AF_INET6, i->c_str(), &sin.sin6.sin6_addr) <= 0)
37d3f960
BH
768 throw AhuException("Unable to resolve local address '"+ *i +"'");
769 }
770
771 fd=socket(sin.sin6.sin6_family, SOCK_STREAM, 0);
772 if(fd<0)
773 throw AhuException("Making a TCP server socket for resolver: "+stringerror());
f28307ad
BH
774
775 int tmp=1;
37d3f960 776 if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0) {
f28307ad 777 L<<Logger::Error<<"Setsockopt failed for TCP listening socket"<<endl;
c8ddb7c2 778 exit(1);
f28307ad
BH
779 }
780
c8ddb7c2 781#ifdef TCP_DEFER_ACCEPT
37d3f960
BH
782 if(setsockopt(fd, SOL_TCP,TCP_DEFER_ACCEPT,(char*)&tmp,sizeof tmp) >= 0) {
783 if(i==locals.begin())
784 L<<Logger::Error<<"Enabled TCP data-ready filter for (slight) DoS protection"<<endl;
c8ddb7c2
BH
785 }
786#endif
787
37d3f960
BH
788 sin.sin4.sin_port = htons(::arg().asNum("local-port"));
789 int socklen=sin.sin4.sin_family==AF_INET ? sizeof(sin.sin4) : sizeof(sin.sin6);
790 if (::bind(fd, (struct sockaddr *)&sin, socklen )<0)
f28307ad
BH
791 throw AhuException("Binding TCP server socket for "+*i+": "+stringerror());
792
37d3f960
BH
793 Utility::setNonBlocking(fd);
794 setSendBuffer(fd, 65000);
795 listen(fd, 128);
5db529f8
BH
796 deferredAdd.push_back(make_pair(fd, handleNewTCPQuestion));
797 // g_fdm->addReadFD(fd, handleNewTCPQuestion);
aa136564
BH
798 if(sin.sin4.sin_family == AF_INET)
799 L<<Logger::Error<<"Listening for TCP queries on "<< sin.toString() <<":"<<::arg().asNum("local-port")<<endl;
800 else
801 L<<Logger::Error<<"Listening for TCP queries on ["<< sin.toString() <<"]:"<<::arg().asNum("local-port")<<endl;
f28307ad 802 }
9c495589
BH
803}
804
f28307ad 805void makeUDPServerSockets()
288f4aa9 806{
f28307ad 807 vector<string>locals;
2e3d8a19 808 stringtok(locals,::arg()["local-address"]," ,");
288f4aa9 809
f28307ad
BH
810 if(locals.empty())
811 throw AhuException("No local address specified");
812
2e3d8a19 813 if(::arg()["local-address"]=="0.0.0.0") {
c836dc19 814 L<<Logger::Warning<<"It is advised to bind to explicit addresses with the --local-address option"<<endl;
288f4aa9 815 }
525b8a7c 816
f28307ad 817 for(vector<string>::const_iterator i=locals.begin();i!=locals.end();++i) {
37d3f960 818 ComboAddress sin;
996c89cc 819
37d3f960
BH
820 memset(&sin, 0, sizeof(sin));
821 sin.sin4.sin_family = AF_INET;
c038218b 822 if(!IpToU32(*i, (uint32_t*)&sin.sin4.sin_addr.s_addr)) {
37d3f960 823 sin.sin6.sin6_family = AF_INET6;
c038218b 824 if(Utility::inet_pton(AF_INET6, i->c_str(), &sin.sin6.sin6_addr) <= 0)
37d3f960
BH
825 throw AhuException("Unable to resolve local address '"+ *i +"'");
826 }
827
828 int fd=socket(sin.sin4.sin_family, SOCK_DGRAM,0);
d3b4137e
BH
829 if(fd < 0) {
830 throw AhuException("Making a UDP server socket for resolver: "+netstringerror());
831 }
37d3f960 832
a19fb8e8 833 setReceiveBuffer(fd, 200000);
37d3f960
BH
834 sin.sin4.sin_port = htons(::arg().asNum("local-port"));
835
836 int socklen=sin.sin4.sin_family==AF_INET ? sizeof(sin.sin4) : sizeof(sin.sin6);
837 if (::bind(fd, (struct sockaddr *)&sin, socklen)<0)
98d0ee4a 838 throw AhuException("Resolver binding to server socket on port "+::arg()["local-port"]+" for "+*i+": "+stringerror());
f28307ad
BH
839
840 Utility::setNonBlocking(fd);
5db529f8
BH
841 // g_fdm->addReadFD(fd, handleNewUDPQuestion);
842 deferredAdd.push_back(make_pair(fd, handleNewUDPQuestion));
37d3f960 843 g_tcpListenSockets.push_back(fd);
aa136564
BH
844 if(sin.sin4.sin_family == AF_INET)
845 L<<Logger::Error<<"Listening for UDP queries on "<< sin.toString() <<":"<<::arg().asNum("local-port")<<endl;
846 else
847 L<<Logger::Error<<"Listening for UDP queries on ["<< sin.toString() <<"]:"<<::arg().asNum("local-port")<<endl;
f28307ad 848 }
c836dc19 849}
caa6eefa 850
9c495589 851
caa6eefa 852#ifndef WIN32
c836dc19
BH
853void daemonize(void)
854{
855 if(fork())
856 exit(0); // bye bye
857
858 setsid();
859
27a5ead5
BH
860 int i=open("/dev/null",O_RDWR); /* open stdin */
861 if(i < 0)
862 L<<Logger::Critical<<"Unable to open /dev/null: "<<stringerror()<<endl;
863 else {
864 dup2(i,0); /* stdin */
865 dup2(i,1); /* stderr */
866 dup2(i,2); /* stderr */
867 close(i);
868 }
288f4aa9 869}
caa6eefa
BH
870#endif
871
aaacf7f2 872uint64_t counter;
c75a6a9e
BH
873bool statsWanted;
874
1d5b3ce6 875
c75a6a9e
BH
876void usr1Handler(int)
877{
878 statsWanted=true;
879}
ae1b2e98 880
c9e9e5e0
BH
881
882
9170fbaf
BH
883void usr2Handler(int)
884{
885 SyncRes::setLog(true);
1d5b3ce6
BH
886 g_quiet=false;
887 ::arg().set("quiet")="no";
c9e9e5e0 888
9170fbaf
BH
889}
890
c75a6a9e
BH
891void doStats(void)
892{
aaacf7f2
BH
893 if(g_stats.qcounter) {
894 L<<Logger::Error<<"stats: "<<g_stats.qcounter<<" questions, "<<RC.size()<<" cache entries, "<<SyncRes::s_negcache.size()<<" negative entries, "
8a5602d4 895 <<(int)((RC.cacheHits*100.0)/(RC.cacheHits+RC.cacheMisses))<<"% cache hits"<<endl;
2e3d8a19 896 L<<Logger::Error<<"stats: throttle map: "<<SyncRes::s_throttle.size()<<", ns speeds: "
8cd5b55e 897 <<SyncRes::s_nsSpeeds.size()<<endl; // ", bytes: "<<RC.bytes()<<endl;
8a5602d4 898 L<<Logger::Error<<"stats: outpacket/query ratio "<<(int)(SyncRes::s_outqueries*100.0/SyncRes::s_queries)<<"%";
525b8a7c
BH
899 L<<Logger::Error<<", "<<(int)(SyncRes::s_throttledqueries*100.0/(SyncRes::s_outqueries+SyncRes::s_throttledqueries))<<"% throttled, "
900 <<SyncRes::s_nodelegated<<" no-delegation drops"<<endl;
5c633640 901 L<<Logger::Error<<"stats: "<<SyncRes::s_tcpoutqueries<<" outgoing tcp connections, "<<MT->numProcesses()<<" queries running, "<<SyncRes::s_outgoingtimeouts<<" outgoing timeouts"<<endl;
c75a6a9e 902 }
7becf07f
BH
903 else if(statsWanted)
904 L<<Logger::Error<<"stats: no stats yet!"<<endl;
905
c75a6a9e
BH
906 statsWanted=false;
907}
c836dc19 908
29f0b1ce 909static void houseKeeping(void *)
779828c4 910try
c836dc19 911{
ae1b2e98 912 static time_t last_stat, last_rootupdate, last_prune;
c9e9e5e0 913 struct timeval now;
c038218b 914 Utility::gettimeofday(&now, 0);
c9e9e5e0 915
255e0a07 916 if(now.tv_sec - last_prune > 300) {
5e4a2466
BH
917 DTime dt;
918 dt.setTimeval(now);
eefd15f9 919 RC.doPrune();
33988bfb
BH
920
921 typedef SyncRes::negcache_t::nth_index<1>::type negcache_by_ttd_index_t;
922 negcache_by_ttd_index_t& ttdindex=boost::multi_index::get<1>(SyncRes::s_negcache);
923
924 negcache_by_ttd_index_t::iterator i=ttdindex.lower_bound(now.tv_sec);
925 ttdindex.erase(ttdindex.begin(), i);
2e3d8a19 926
c9e9e5e0 927 time_t limit=now.tv_sec-300;
2e3d8a19
BH
928 for(SyncRes::nsspeeds_t::iterator i = SyncRes::s_nsSpeeds.begin() ; i!= SyncRes::s_nsSpeeds.end(); )
929 if(i->second.stale(limit))
930 SyncRes::s_nsSpeeds.erase(i++);
931 else
932 ++i;
933
255e0a07 934 // cerr<<"Pruned "<<pruned<<" records, left "<<SyncRes::s_negcache.size()<<"\n";
5e4a2466 935// cout<<"Prune took "<<dt.udiff()<<"usec\n";
ae1b2e98
BH
936 last_prune=time(0);
937 }
c9e9e5e0 938 if(now.tv_sec - last_stat>1800) {
c75a6a9e 939 doStats();
c836dc19
BH
940 last_stat=time(0);
941 }
c038218b 942 if(now.tv_sec - last_rootupdate > 7200) {
c9e9e5e0 943 SyncRes sr(now);
ea634573 944 vector<DNSResourceRecord> ret;
c836dc19
BH
945
946 sr.setNoCache();
a9af3782 947 int res=sr.beginResolve(".", QType(QType::NS), 1, ret);
c836dc19
BH
948 if(!res) {
949 L<<Logger::Error<<"Refreshed . records"<<endl;
c9e9e5e0 950 last_rootupdate=now.tv_sec;
c836dc19
BH
951 }
952 else
953 L<<Logger::Error<<"Failed to update . records, RCODE="<<res<<endl;
954 }
955}
779828c4
BH
956catch(AhuException& ae)
957{
958 L<<Logger::Error<<"Fatal error: "<<ae.reason<<endl;
959 throw;
960}
705f31ae 961;
d6d5dea7 962
0d5f0a9f
BH
963string questionExpand(const char* packet, uint16_t len)
964{
965 const char* end=packet+len;
966 const char* pos=packet+12;
967 unsigned char labellen;
968 string ret;
4ef015cd 969 ret.reserve(len-12);
0d5f0a9f
BH
970 while((labellen=*pos++)) {
971 if(pos+labellen > end)
972 break;
973 ret.append(pos, labellen);
974 ret.append(1,'.');
975 pos+=labellen;
976 }
977 if(ret.empty())
978 ret=".";
979 return ret;
980}
981
09e6702a
BH
982
983void handleRCC(int fd, boost::any& var)
984{
985 string remote;
986 string msg=s_rcc.recv(&remote);
987 RecursorControlParser rcp;
988 RecursorControlParser::func_t* command;
989 string answer=rcp.getAnswer(msg, &command);
990 s_rcc.send(answer, &remote);
991 command();
992}
993
994void handleTCPClientReadable(int fd, boost::any& var)
995{
0b18b22e 996 PacketID* pident=any_cast<PacketID>(&var);
667f7e60 997 // cerr<<"handleTCPClientReadable called for fd "<<fd<<", pident->inNeeded: "<<pident->inNeeded<<", "<<pident->sock->getHandle()<<endl;
09e6702a 998
667f7e60 999 shared_array<char> buffer(new char[pident->inNeeded]);
09e6702a 1000
705f31ae 1001 int ret=recv(fd, buffer.get(), pident->inNeeded,0);
09e6702a 1002 if(ret > 0) {
667f7e60
BH
1003 pident->inMSG.append(&buffer[0], &buffer[ret]);
1004 pident->inNeeded-=ret;
1005 if(!pident->inNeeded) {
1006 // cerr<<"Got entire load of "<<pident->inMSG.size()<<" bytes"<<endl;
1007 PacketID pid=*pident;
1008 string msg=pident->inMSG;
09e6702a
BH
1009
1010 g_fdm->removeReadFD(fd);
1011 MT->sendEvent(pid, &msg);
1012 }
1013 else {
667f7e60 1014 // cerr<<"Still have "<<pident->inNeeded<<" left to go"<<endl;
09e6702a
BH
1015 }
1016 }
1017 else {
667f7e60 1018 PacketID tmp=*pident;
09e6702a
BH
1019 g_fdm->removeReadFD(fd); // pident might now be invalid (it isn't, but still)
1020 string empty;
1021 MT->sendEvent(tmp, &empty); // this conveys error status
1022 }
1023}
1024
1025void handleTCPClientWritable(int fd, boost::any& var)
1026{
0b18b22e 1027 PacketID* pid=any_cast<PacketID>(&var);
09e6702a 1028
705f31ae 1029 int ret=send(fd, pid->outMSG.c_str(), pid->outMSG.size() - pid->outPos,0);
09e6702a 1030 if(ret > 0) {
667f7e60
BH
1031 pid->outPos+=ret;
1032 if(pid->outPos==pid->outMSG.size()) {
1033 PacketID tmp=*pid;
09e6702a
BH
1034 g_fdm->removeWriteFD(fd);
1035 MT->sendEvent(tmp, &tmp.outMSG); // send back what we sent to convey everything is ok
1036 }
1037 }
1038 else { // error or EOF
667f7e60 1039 PacketID tmp(*pid);
09e6702a
BH
1040 g_fdm->removeWriteFD(fd);
1041 string sent;
998a4334 1042 MT->sendEvent(tmp, &sent); // we convey error status by sending empty string
09e6702a
BH
1043 }
1044}
1045
998a4334 1046void handleUDPServerResponse(int fd, boost::any& var)
09e6702a 1047{
600fc20b 1048 PacketID pid=any_cast<PacketID>(var);
998a4334 1049 int len;
09e6702a 1050 char data[1500];
996c89cc 1051 ComboAddress fromaddr;
09e6702a
BH
1052 socklen_t addrlen=sizeof(fromaddr);
1053
998a4334 1054 len=recvfrom(fd, data, sizeof(data), 0, (sockaddr *)&fromaddr, &addrlen);
600fc20b 1055
998a4334
BH
1056 if(len < (int)sizeof(dnsheader)) {
1057 if(len < 0)
996c89cc 1058 ; // cerr<<"Error on fd "<<fd<<": "<<stringerror()<<"\n";
09e6702a
BH
1059 else {
1060 g_stats.serverParseError++;
1061 if(g_logCommonErrors)
37d3f960 1062 L<<Logger::Error<<"Unable to parse packet from remote UDP server "<< sockAddrToString((struct sockaddr_in*) &fromaddr) <<
998a4334
BH
1063 ": packet smalller than DNS header"<<endl;
1064 }
1065 string empty;
600fc20b 1066 g_udpclientsocks.returnSocket(fd);
998a4334
BH
1067 MT->sendEvent(pid, &empty); // this denotes error
1068 return;
1069 }
1070
1071 dnsheader dh;
1072 memcpy(&dh, data, sizeof(dh));
1073
1074 if(!dh.qdcount) // UPC, Nominum?
1075 return;
1076
1077 if(dh.qr) {
1078 PacketID pident;
1079 pident.remote=fromaddr;
1080 pident.id=dh.id;
1081 pident.fd=fd;
1082 pident.domain=questionExpand(data, len); // don't copy this from above - we need to do the actual read
1083 string packet;
1084 packet.assign(data, len);
1085 if(!MT->sendEvent(pident, &packet)) {
1086 // if(g_logCommonErrors)
1087 // L<<Logger::Warning<<"Discarding unexpected packet from "<<sockAddrToString((struct sockaddr_in*) &fromaddr, addrlen)<<endl;
1088 g_stats.unexpectedCount++;
1089
1090 for(MT_t::waiters_t::iterator mthread=MT->d_waiters.begin(); mthread!=MT->d_waiters.end(); ++mthread) {
996c89cc 1091 if(pident.fd==mthread->key.fd && mthread->key.remote==pident.remote &&
705f31ae 1092 !Utility::strcasecmp(pident.domain.c_str(), mthread->key.domain.c_str())) {
998a4334
BH
1093 mthread->key.nearMisses++;
1094 }
1095 }
09e6702a 1096 }
2a5e6212
BH
1097 else
1098 g_udpclientsocks.returnSocket(fd);
09e6702a 1099 }
998a4334 1100 else
37d3f960 1101 L<<Logger::Warning<<"Ignoring question on outgoing socket from "<< sockAddrToString((struct sockaddr_in*) &fromaddr) <<endl;
09e6702a
BH
1102}
1103
1f4abb20
BH
1104FDMultiplexer* getMultiplexer()
1105{
1106 FDMultiplexer* ret;
1107 for(FDMultiplexer::FDMultiplexermap_t::const_iterator i = FDMultiplexer::getMultiplexerMap().begin();
1108 i != FDMultiplexer::getMultiplexerMap().end(); ++i) {
1109 try {
1110 ret=i->second();
1111 L<<Logger::Error<<"Enabled '"<<ret->getName()<<"' multiplexer"<<endl;
1112 return ret;
1113 }
98d0ee4a 1114 catch(FDMultiplexerException &fe) {
0a7f24cb 1115 L<<Logger::Error<<"Non-fatal error initializing possible multiplexer ("<<fe.what()<<"), falling back"<<endl;
98d0ee4a
BH
1116 }
1117 catch(...) {
1118 L<<Logger::Error<<"Non-fatal error initializing possible multiplexer"<<endl;
1119 }
1f4abb20
BH
1120 }
1121 L<<Logger::Error<<"No working multiplexer found!"<<endl;
1122 exit(1);
1123}
1124
5605c067
BH
1125static void makeNameToIPZone(const string& hostname, const string& ip)
1126{
1127 SyncRes::AuthDomain ad;
1128 DNSResourceRecord rr;
1129 rr.qname=toCanonic("", hostname);
1130 rr.d_place=DNSResourceRecord::ANSWER;
1131 rr.ttl=86400;
1132 rr.qtype=QType::SOA;
c0247d0e 1133 rr.content="localhost. root 1 604800 86400 2419200 604800";
5605c067
BH
1134
1135 ad.d_records.insert(rr);
1136
1137 rr.qtype=QType::NS;
1138 rr.content="localhost.";
1139
1140 ad.d_records.insert(rr);
1141
1142 rr.qtype=QType::A;
1143 rr.content=ip;
1144 ad.d_records.insert(rr);
1145
1146 if(SyncRes::s_domainmap.count(rr.qname)) {
1147 L<<Logger::Warning<<"Hosts file will not overwrite zone '"<<rr.qname<<"' already loaded"<<endl;
1148 }
1149 else {
1150 L<<Logger::Warning<<"Inserting forward zone '"<<rr.qname<<"' based on hosts file"<<endl;
1151 SyncRes::s_domainmap[rr.qname]=ad;
1152 }
1153}
1154
9bc8c14c 1155//! parts[0] must be an IP address, the rest must be host names
5605c067
BH
1156static void makeIPToNamesZone(const vector<string>& parts)
1157{
1158 string address=parts[0];
1159 vector<string> ipparts;
1160 stringtok(ipparts, address,".");
5605c067 1161
5605c067
BH
1162 SyncRes::AuthDomain ad;
1163 DNSResourceRecord rr;
9bc8c14c 1164 for(int n=ipparts.size()-1; n>=0 ; --n) {
5605c067
BH
1165 rr.qname.append(ipparts[n]);
1166 rr.qname.append(1,'.');
1167 }
1168 rr.qname.append("in-addr.arpa.");
1169
1170 rr.d_place=DNSResourceRecord::ANSWER;
1171 rr.ttl=86400;
1172 rr.qtype=QType::SOA;
9bc8c14c 1173 rr.content="localhost. root. 1 604800 86400 2419200 604800";
5605c067
BH
1174
1175 ad.d_records.insert(rr);
1176
1177 rr.qtype=QType::NS;
1178 rr.content="localhost.";
1179
1180 ad.d_records.insert(rr);
1181 rr.qtype=QType::PTR;
1182
9bc8c14c
BH
1183 if(ipparts.size()==4) // otherwise this is a partial zone
1184 for(unsigned int n=1; n < parts.size(); ++n) {
1185 rr.content=toCanonic("", parts[n]);
1186 ad.d_records.insert(rr);
1187 }
5605c067
BH
1188
1189 if(SyncRes::s_domainmap.count(rr.qname)) {
9bc8c14c 1190 L<<Logger::Warning<<"Will not overwrite zone '"<<rr.qname<<"' already loaded"<<endl;
5605c067
BH
1191 }
1192 else {
9bc8c14c
BH
1193 if(ipparts.size()==4)
1194 L<<Logger::Warning<<"Inserting reverse zone '"<<rr.qname<<"' based on hosts file"<<endl;
5605c067
BH
1195 SyncRes::s_domainmap[rr.qname]=ad;
1196 }
1197}
1198
1199void parseAuthAndForwards()
1200{
1201 SyncRes::s_domainmap.clear(); // this makes us idempotent
1202
1203 typedef vector<string> parts_t;
1204 parts_t parts;
1205 for(int n=0; n < 2 ; ++n ) {
1206 parts.clear();
1207 stringtok(parts, ::arg()[n ? "forward-zones" : "auth-zones"], ",\t\n\r");
1208 for(parts_t::const_iterator iter = parts.begin(); iter != parts.end(); ++iter) {
1209 SyncRes::AuthDomain ad;
1210 pair<string,string> headers=splitField(*iter, '=');
1211 trim(headers.first);
1212 trim(headers.second);
1213 headers.first=toCanonic("", headers.first);
1214 if(n==0) {
1215 L<<Logger::Error<<"Parsing authoritative data for zone '"<<headers.first<<"' from file '"<<headers.second<<"'"<<endl;
1216 ZoneParserTNG zpt(headers.second, headers.first);
1217 DNSResourceRecord rr;
1218 while(zpt.get(rr)) {
1219 ad.d_records.insert(rr);
1220 }
1221 }
1222 else {
1223 L<<Logger::Error<<"Redirecting queries for zone '"<<headers.first<<"' to IP '"<<headers.second<<"'"<<endl;
1224 ad.d_server=headers.second;
1225 }
1226
1227 SyncRes::s_domainmap[headers.first]=ad;
1228 }
1229 }
1230
9bc8c14c
BH
1231 if(::arg().mustDo("export-etc-hosts")) {
1232 string line;
1233 string fname;
5605c067 1234
9bc8c14c
BH
1235 ifstream ifs("/etc/hosts");
1236 if(!ifs) {
1237 L<<Logger::Warning<<"Could not open /etc/hosts for reading"<<endl;
1238 return;
1239 }
1240
1241 string::size_type pos;
1242 while(getline(ifs,line)) {
1243 pos=line.find('#');
1244 if(pos!=string::npos)
1245 line.resize(pos);
1246 trim(line);
1247 if(line.empty())
1248 continue;
1249 parts.clear();
1250 stringtok(parts, line, "\t\r\n ");
1251 if(parts[0].find(':')!=string::npos)
1252 continue;
1253
1254 for(unsigned int n=1; n < parts.size(); ++n)
1255 makeNameToIPZone(parts[n], parts[0]);
1256 makeIPToNamesZone(parts);
1257 }
1258 }
1259 if(::arg().mustDo("serve-rfc1918")) {
1260 L<<Logger::Warning<<"Inserting rfc 1918 private space zones"<<endl;
5605c067 1261 parts.clear();
9bc8c14c
BH
1262 parts.push_back("127");
1263 makeIPToNamesZone(parts);
1264 parts[0]="10";
1265 makeIPToNamesZone(parts);
5605c067 1266
9bc8c14c 1267 parts[0]="192.168";
5605c067 1268 makeIPToNamesZone(parts);
9bc8c14c
BH
1269 for(int n=16; n < 32; n++) {
1270 parts[0]="172."+lexical_cast<string>(n);
1271 makeIPToNamesZone(parts);
1272 }
5605c067
BH
1273 }
1274}
1275
6424093e 1276int serviceMain(int argc, char*argv[])
f7c1d4e3
BH
1277{
1278 L.setName("pdns_recursor");
1279
1280 L<<Logger::Warning<<"PowerDNS recursor "<<VERSION<<" (C) 2001-2006 PowerDNS.COM BV ("<<__DATE__", "__TIME__;
1281#ifdef __GNUC__
1282 L<<", gcc "__VERSION__;
1283#endif // add other compilers here
1284#ifdef _MSC_VER
1285 L<<", MSVC "<<_MSC_VER;
1286#endif
1287 L<<") starting up"<<endl;
1288
1289 L<<Logger::Warning<<"PowerDNS comes with ABSOLUTELY NO WARRANTY. "
1290 "This is free software, and you are welcome to redistribute it "
1291 "according to the terms of the GPL version 2."<<endl;
1292
1293 L<<Logger::Warning<<"Operating in "<<(sizeof(unsigned long)*8) <<" bits mode"<<endl;
1294
1295 if(!::arg()["allow-from"].empty()) {
1296 g_allowFrom=new NetmaskGroup;
1297 vector<string> ips;
1298 stringtok(ips, ::arg()["allow-from"], ", ");
1299 L<<Logger::Warning<<"Only allowing queries from: ";
1300 for(vector<string>::const_iterator i = ips.begin(); i!= ips.end(); ++i) {
1301 g_allowFrom->addMask(*i);
1302 if(i!=ips.begin())
1303 L<<Logger::Warning<<", ";
1304 L<<Logger::Warning<<*i;
1305 }
1306 L<<Logger::Warning<<endl;
1307 }
1308 else if(::arg()["local-address"]!="127.0.0.1" && ::arg().asNum("local-port")==53)
1309 L<<Logger::Error<<"WARNING: Allowing queries from all IP addresses - this can be a security risk!"<<endl;
1310
1311 g_quiet=::arg().mustDo("quiet");
1312 if(::arg().mustDo("trace")) {
1313 SyncRes::setLog(true);
1314 ::arg().set("quiet")="no";
1315 g_quiet=false;
1316 }
1317
1318
1319 if(!::arg()["query-local-address6"].empty()) {
1320 SyncRes::s_doIPv6=true;
1321 L<<Logger::Error<<"Enabling IPv6 transport for outgoing queries"<<endl;
1322 }
1323
1324 SyncRes::s_maxnegttl=::arg().asNum("max-negative-ttl");
1325 SyncRes::s_serverID=::arg()["server-id"];
1326 if(SyncRes::s_serverID.empty()) {
1327 char tmp[128];
1328 gethostname(tmp, sizeof(tmp)-1);
1329 SyncRes::s_serverID=tmp;
1330 }
1331
1332
1333 parseAuthAndForwards();
1334
1335 g_stats.remotes.resize(::arg().asNum("remotes-ringbuffer-entries"));
1336 if(!g_stats.remotes.empty())
1337 memset(&g_stats.remotes[0], 0, g_stats.remotes.size() * sizeof(RecursorStats::remotes_t::value_type));
1338 g_logCommonErrors=::arg().mustDo("log-common-errors");
1339
1340 makeUDPServerSockets();
1341 makeTCPServerSockets();
1342
1343#ifndef WIN32
1344 if(::arg().mustDo("fork")) {
1345 fork();
1346 L<<Logger::Warning<<"This is forked pid "<<getpid()<<endl;
1347 }
1348#endif
1349
1350 MT=new MTasker<PacketID,string>(100000);
1351 makeControlChannelSocket();
1352 PacketID pident;
1353 primeHints();
1354 L<<Logger::Warning<<"Done priming cache with root hints"<<endl;
1355#ifndef WIN32
1356 if(::arg().mustDo("daemon")) {
1357 L<<Logger::Warning<<"Calling daemonize, going to background"<<endl;
1358 L.toConsole(Logger::Critical);
1359 L.setLoglevel((Logger::Urgency)(4));
1360
1361 daemonize();
1362 }
1363 signal(SIGUSR1,usr1Handler);
1364 signal(SIGUSR2,usr2Handler);
1365 signal(SIGPIPE,SIG_IGN);
1366 writePid();
1367#endif
1368 g_fdm=getMultiplexer();
1369
1370 for(deferredAdd_t::const_iterator i=deferredAdd.begin(); i!=deferredAdd.end(); ++i)
1371 g_fdm->addReadFD(i->first, i->second);
1372
1373 int newgid=0;
1374 if(!::arg()["setgid"].empty())
1375 newgid=Utility::makeGidNumeric(::arg()["setgid"]);
1376 int newuid=0;
1377 if(!::arg()["setuid"].empty())
1378 newuid=Utility::makeUidNumeric(::arg()["setuid"]);
1379
1380#ifndef WIN32
1381 if (!::arg()["chroot"].empty()) {
1382 if (chroot(::arg()["chroot"].c_str())<0) {
1383 L<<Logger::Error<<"Unable to chroot to '"+::arg()["chroot"]+"': "<<strerror (errno)<<", exiting"<<endl;
1384 exit(1);
1385 }
1386 }
1387
1388 Utility::dropPrivs(newuid, newgid);
1389 g_fdm->addReadFD(s_rcc.d_fd, handleRCC); // control channel
1390#endif
1391
1392 counter=0;
1393 unsigned int maxTcpClients=::arg().asNum("max-tcp-clients");
1394 g_tcpTimeout=::arg().asNum("client-tcp-timeout");
1395
1396 g_maxTCPPerClient=::arg().asNum("max-tcp-per-client");
1397
1398
1399 bool listenOnTCP(true);
1400
1401 for(;;) {
1402 while(MT->schedule()); // housekeeping, let threads do their thing
1403
1404 if(!(counter%500)) {
1405 MT->makeThread(houseKeeping,0);
1406 }
1407
1408 if(!(counter%11)) {
1409 typedef vector<pair<int, boost::any> > expired_t;
1410 expired_t expired=g_fdm->getTimeouts(g_now);
1411
1412 for(expired_t::iterator i=expired.begin() ; i != expired.end(); ++i) {
1413 TCPConnection conn=any_cast<TCPConnection>(i->second);
1414 if(conn.state != TCPConnection::DONE) {
1415 if(g_logCommonErrors)
1416 L<<Logger::Warning<<"Timeout from remote TCP client "<< conn.remote.toString() <<endl;
1417 g_fdm->removeReadFD(i->first);
1418 conn.closeAndCleanup();
1419 }
1420 }
1421 }
1422
1423 counter++;
1424
1425 if(statsWanted) {
1426 doStats();
1427 }
1428
1429 Utility::gettimeofday(&g_now, 0);
1430 g_fdm->run(&g_now);
1431
1432 if(listenOnTCP) {
1433 if(TCPConnection::s_currentConnections > maxTcpClients) { // shutdown
1434 for(g_tcpListenSockets_t::iterator i=g_tcpListenSockets.begin(); i != g_tcpListenSockets.end(); ++i)
1435 g_fdm->removeReadFD(*i);
1436 listenOnTCP=false;
1437 }
1438 }
1439 else {
1440 if(TCPConnection::s_currentConnections <= maxTcpClients) { // reenable
1441 for(g_tcpListenSockets_t::iterator i=g_tcpListenSockets.begin(); i != g_tcpListenSockets.end(); ++i)
1442 g_fdm->addReadFD(*i, handleNewTCPQuestion);
1443 listenOnTCP=true;
1444 }
1445 }
1446 }
1447}
1448#ifdef WIN32
1449void doWindowsServiceArguments(RecursorService& recursor)
1450{
1451 if(::arg().mustDo( "register-service" )) {
1452 if ( !recursor.registerService( "The PowerDNS Recursor.", true )) {
1453 cerr << "Could not register service." << endl;
1454 exit( 99 );
1455 }
1456
1457 exit( 0 );
1458 }
1459
1460 if ( ::arg().mustDo( "unregister-service" )) {
1461 recursor.unregisterService();
1462 exit( 0 );
1463 }
1464}
1465#endif
1466
288f4aa9
BH
1467int main(int argc, char **argv)
1468{
8a63d3ce 1469 reportBasicTypes();
ea634573 1470
22030c37 1471 int ret = EXIT_SUCCESS;
caa6eefa 1472#ifdef WIN32
f7c1d4e3
BH
1473 RecursorService service;
1474 WSADATA wsaData;
1475 if(WSAStartup( MAKEWORD( 2, 2 ), &wsaData )) {
1476 cerr<<"Unable to initialize winsock\n";
1477 exit(1);
1478 }
caa6eefa
BH
1479#endif // WIN32
1480
288f4aa9 1481 try {
caa6eefa 1482 Utility::srandom(time(0));
2e3d8a19
BH
1483 ::arg().set("soa-minimum-ttl","Don't change")="0";
1484 ::arg().set("soa-serial-offset","Don't change")="0";
1485 ::arg().set("no-shuffle","Don't change")="off";
1486 ::arg().set("aaaa-additional-processing","turn on to do AAAA additional processing (slow)")="off";
1487 ::arg().set("local-port","port to listen on")="53";
01ed3112 1488 ::arg().set("local-address","IP addresses to listen on, separated by spaces or commas")="127.0.0.1";
2e3d8a19
BH
1489 ::arg().set("trace","if we should output heaps of logging")="off";
1490 ::arg().set("daemon","Operate as a daemon")="yes";
0e9d9ce2 1491 ::arg().set("log-common-errors","If we should log rather common errors")="yes";
2e3d8a19
BH
1492 ::arg().set("chroot","switch to chroot jail")="";
1493 ::arg().set("setgid","If set, change group id to this gid for more security")="";
1494 ::arg().set("setuid","If set, change user id to this uid for more security")="";
c038218b
BH
1495#ifdef WIN32
1496 ::arg().set("quiet","Suppress logging of questions and answers")="off";
f7c1d4e3
BH
1497 ::arg().setSwitch( "register-service", "Register the service" )= "no";
1498 ::arg().setSwitch( "unregister-service", "Unregister the service" )= "no";
1499 ::arg().setSwitch( "ntservice", "Run as service" )= "no";
1500 ::arg().setSwitch( "use-ntlog", "Use the NT logging facilities" )= "yes";
1501 ::arg().setSwitch( "use-logfile", "Use a log file" )= "no";
1502 ::arg().setSwitch( "logfile", "Filename of the log file" )= "recursor.log";
c038218b
BH
1503#else
1504 ::arg().set("quiet","Suppress logging of questions and answers")="";
1505#endif
2e3d8a19
BH
1506 ::arg().set("config-dir","Location of configuration directory (recursor.conf)")=SYSCONFDIR;
1507 ::arg().set("socket-dir","Where the controlsocket will live")=LOCALSTATEDIR;
1508 ::arg().set("delegation-only","Which domains we only accept delegations from")="";
1509 ::arg().set("query-local-address","Source IP address for sending queries")="0.0.0.0";
996c89cc 1510 ::arg().set("query-local-address6","Source IPv6 address for sending queries")="";
2e3d8a19
BH
1511 ::arg().set("client-tcp-timeout","Timeout in seconds when talking to TCP clients")="2";
1512 ::arg().set("max-tcp-clients","Maximum number of simultaneous TCP clients")="128";
1513 ::arg().set("hint-file", "If set, load root hints from this file")="";
bec87d21 1514 ::arg().set("max-cache-entries", "If set, maximum number of entries in the main cache")="0";
a9af3782
BH
1515 ::arg().set("max-negative-ttl", "maximum number of seconds to keep a negative cached entry in memory")="3600";
1516 ::arg().set("server-id", "Returned when queried for 'server.id' TXT, defaults to hostname")="";
1517 ::arg().set("remotes-ringbuffer-entries", "maximum number of packets to store statistics for")="0";
d5141417 1518 ::arg().set("version-string", "string reported on version.pdns or version.bind")="PowerDNS Recursor "VERSION" $Id$";
01ed3112 1519 ::arg().set("allow-from", "If set, only allow these comma separated netmasks to recurse")="127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12";
4e120339 1520 ::arg().set("max-tcp-per-client", "If set, maximum number of TCP sessions per client (IP address)")="0";
41f7a068 1521 ::arg().set("fork", "If set, fork the daemon for possible double performance")="no";
0d5f0a9f 1522 ::arg().set("spoof-nearmiss-max", "If non-zero, assume spoofing after this many near misses")="20";
4ef015cd 1523 ::arg().set("single-socket", "If set, only use a single socket for outgoing queries")="off";
5605c067
BH
1524 ::arg().set("auth-zones", "Zones for which we have authoritative data, comma separated domain=file pairs ")="";
1525 ::arg().set("forward-zones", "Zones for which we forward queries, comma separated domain=ip pairs")="";
1526 ::arg().set("export-etc-hosts", "If we should serve up contents from /etc/hosts")="off";
9bc8c14c 1527 ::arg().set("serve-rfc1918", "If we should be authoritative for RFC 1918 private IP space")="";
2e3d8a19
BH
1528
1529 ::arg().setCmd("help","Provide a helpful message");
d5141417 1530 ::arg().setCmd("config","Output blank configuration");
c75a6a9e 1531 L.toConsole(Logger::Warning);
2e3d8a19 1532 ::arg().laxParse(argc,argv); // do a lax parse
c75a6a9e 1533
2e3d8a19 1534 string configname=::arg()["config-dir"]+"/recursor.conf";
c75a6a9e
BH
1535 cleanSlashes(configname);
1536
2e3d8a19 1537 if(!::arg().file(configname.c_str()))
c75a6a9e
BH
1538 L<<Logger::Warning<<"Unable to parse configuration file '"<<configname<<"'"<<endl;
1539
2e3d8a19 1540 ::arg().parse(argc,argv);
c836dc19 1541
2e3d8a19 1542 ::arg().set("delegation-only")=toLower(::arg()["delegation-only"]);
562588a3 1543
2e3d8a19 1544 if(::arg().mustDo("help")) {
b636533b 1545 cerr<<"syntax:"<<endl<<endl;
2e3d8a19 1546 cerr<<::arg().helpstring(::arg()["help"])<<endl;
b636533b
BH
1547 exit(99);
1548 }
1549
d5141417
BH
1550 if(::arg().mustDo("config")) {
1551 cout<<::arg().configstring()<<endl;
1552 exit(0);
1553 }
1554
caa6eefa 1555#ifndef WIN32
f7c1d4e3
BH
1556 serviceMain(argc, argv);
1557#else
6a0bb0cf 1558 doWindowsServiceArguments(service);
6424093e 1559 L.toNTLog();
f7c1d4e3 1560 RecursorService::instance()->start( argc, argv, ::arg().mustDo( "ntservice" ));
caa6eefa 1561#endif
998a4334 1562
288f4aa9
BH
1563 }
1564 catch(AhuException &ae) {
c836dc19 1565 L<<Logger::Error<<"Exception: "<<ae.reason<<endl;
22030c37 1566 ret=EXIT_FAILURE;
288f4aa9
BH
1567 }
1568 catch(exception &e) {
c836dc19 1569 L<<Logger::Error<<"STL Exception: "<<e.what()<<endl;
22030c37 1570 ret=EXIT_FAILURE;
288f4aa9
BH
1571 }
1572 catch(...) {
c836dc19 1573 L<<Logger::Error<<"any other exception in main: "<<endl;
22030c37 1574 ret=EXIT_FAILURE;
288f4aa9 1575 }
caa6eefa
BH
1576
1577#ifdef WIN32
1578 WSACleanup();
1579#endif // WIN32
1580
22030c37 1581 return ret;
288f4aa9 1582}