]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/pdns_recursor.cc
dist and windows makefile fixes
[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);
f28307ad 829 if(fd<0)
37d3f960
BH
830 throw AhuException("Making a UDP server socket for resolver: "+stringerror());
831
a19fb8e8 832 setReceiveBuffer(fd, 200000);
37d3f960
BH
833 sin.sin4.sin_port = htons(::arg().asNum("local-port"));
834
835 int socklen=sin.sin4.sin_family==AF_INET ? sizeof(sin.sin4) : sizeof(sin.sin6);
836 if (::bind(fd, (struct sockaddr *)&sin, socklen)<0)
98d0ee4a 837 throw AhuException("Resolver binding to server socket on port "+::arg()["local-port"]+" for "+*i+": "+stringerror());
f28307ad
BH
838
839 Utility::setNonBlocking(fd);
5db529f8
BH
840 // g_fdm->addReadFD(fd, handleNewUDPQuestion);
841 deferredAdd.push_back(make_pair(fd, handleNewUDPQuestion));
37d3f960 842 g_tcpListenSockets.push_back(fd);
aa136564
BH
843 if(sin.sin4.sin_family == AF_INET)
844 L<<Logger::Error<<"Listening for UDP queries on "<< sin.toString() <<":"<<::arg().asNum("local-port")<<endl;
845 else
846 L<<Logger::Error<<"Listening for UDP queries on ["<< sin.toString() <<"]:"<<::arg().asNum("local-port")<<endl;
f28307ad 847 }
c836dc19 848}
caa6eefa 849
9c495589 850
caa6eefa 851#ifndef WIN32
c836dc19
BH
852void daemonize(void)
853{
854 if(fork())
855 exit(0); // bye bye
856
857 setsid();
858
27a5ead5
BH
859 int i=open("/dev/null",O_RDWR); /* open stdin */
860 if(i < 0)
861 L<<Logger::Critical<<"Unable to open /dev/null: "<<stringerror()<<endl;
862 else {
863 dup2(i,0); /* stdin */
864 dup2(i,1); /* stderr */
865 dup2(i,2); /* stderr */
866 close(i);
867 }
288f4aa9 868}
caa6eefa
BH
869#endif
870
aaacf7f2 871uint64_t counter;
c75a6a9e
BH
872bool statsWanted;
873
1d5b3ce6 874
c75a6a9e
BH
875void usr1Handler(int)
876{
877 statsWanted=true;
878}
ae1b2e98 879
c9e9e5e0
BH
880
881
9170fbaf
BH
882void usr2Handler(int)
883{
884 SyncRes::setLog(true);
1d5b3ce6
BH
885 g_quiet=false;
886 ::arg().set("quiet")="no";
c9e9e5e0 887
9170fbaf
BH
888}
889
c75a6a9e
BH
890void doStats(void)
891{
aaacf7f2
BH
892 if(g_stats.qcounter) {
893 L<<Logger::Error<<"stats: "<<g_stats.qcounter<<" questions, "<<RC.size()<<" cache entries, "<<SyncRes::s_negcache.size()<<" negative entries, "
8a5602d4 894 <<(int)((RC.cacheHits*100.0)/(RC.cacheHits+RC.cacheMisses))<<"% cache hits"<<endl;
2e3d8a19 895 L<<Logger::Error<<"stats: throttle map: "<<SyncRes::s_throttle.size()<<", ns speeds: "
8cd5b55e 896 <<SyncRes::s_nsSpeeds.size()<<endl; // ", bytes: "<<RC.bytes()<<endl;
8a5602d4 897 L<<Logger::Error<<"stats: outpacket/query ratio "<<(int)(SyncRes::s_outqueries*100.0/SyncRes::s_queries)<<"%";
525b8a7c
BH
898 L<<Logger::Error<<", "<<(int)(SyncRes::s_throttledqueries*100.0/(SyncRes::s_outqueries+SyncRes::s_throttledqueries))<<"% throttled, "
899 <<SyncRes::s_nodelegated<<" no-delegation drops"<<endl;
5c633640 900 L<<Logger::Error<<"stats: "<<SyncRes::s_tcpoutqueries<<" outgoing tcp connections, "<<MT->numProcesses()<<" queries running, "<<SyncRes::s_outgoingtimeouts<<" outgoing timeouts"<<endl;
c75a6a9e 901 }
7becf07f
BH
902 else if(statsWanted)
903 L<<Logger::Error<<"stats: no stats yet!"<<endl;
904
c75a6a9e
BH
905 statsWanted=false;
906}
c836dc19 907
29f0b1ce 908static void houseKeeping(void *)
779828c4 909try
c836dc19 910{
ae1b2e98 911 static time_t last_stat, last_rootupdate, last_prune;
c9e9e5e0 912 struct timeval now;
c038218b 913 Utility::gettimeofday(&now, 0);
c9e9e5e0 914
255e0a07 915 if(now.tv_sec - last_prune > 300) {
5e4a2466
BH
916 DTime dt;
917 dt.setTimeval(now);
eefd15f9 918 RC.doPrune();
33988bfb
BH
919
920 typedef SyncRes::negcache_t::nth_index<1>::type negcache_by_ttd_index_t;
921 negcache_by_ttd_index_t& ttdindex=boost::multi_index::get<1>(SyncRes::s_negcache);
922
923 negcache_by_ttd_index_t::iterator i=ttdindex.lower_bound(now.tv_sec);
924 ttdindex.erase(ttdindex.begin(), i);
2e3d8a19 925
c9e9e5e0 926 time_t limit=now.tv_sec-300;
2e3d8a19
BH
927 for(SyncRes::nsspeeds_t::iterator i = SyncRes::s_nsSpeeds.begin() ; i!= SyncRes::s_nsSpeeds.end(); )
928 if(i->second.stale(limit))
929 SyncRes::s_nsSpeeds.erase(i++);
930 else
931 ++i;
932
255e0a07 933 // cerr<<"Pruned "<<pruned<<" records, left "<<SyncRes::s_negcache.size()<<"\n";
5e4a2466 934// cout<<"Prune took "<<dt.udiff()<<"usec\n";
ae1b2e98
BH
935 last_prune=time(0);
936 }
c9e9e5e0 937 if(now.tv_sec - last_stat>1800) {
c75a6a9e 938 doStats();
c836dc19
BH
939 last_stat=time(0);
940 }
c038218b 941 if(now.tv_sec - last_rootupdate > 7200) {
c9e9e5e0 942 SyncRes sr(now);
ea634573 943 vector<DNSResourceRecord> ret;
c836dc19
BH
944
945 sr.setNoCache();
a9af3782 946 int res=sr.beginResolve(".", QType(QType::NS), 1, ret);
c836dc19
BH
947 if(!res) {
948 L<<Logger::Error<<"Refreshed . records"<<endl;
c9e9e5e0 949 last_rootupdate=now.tv_sec;
c836dc19
BH
950 }
951 else
952 L<<Logger::Error<<"Failed to update . records, RCODE="<<res<<endl;
953 }
954}
779828c4
BH
955catch(AhuException& ae)
956{
957 L<<Logger::Error<<"Fatal error: "<<ae.reason<<endl;
958 throw;
959}
705f31ae 960;
d6d5dea7 961
0d5f0a9f
BH
962string questionExpand(const char* packet, uint16_t len)
963{
964 const char* end=packet+len;
965 const char* pos=packet+12;
966 unsigned char labellen;
967 string ret;
4ef015cd 968 ret.reserve(len-12);
0d5f0a9f
BH
969 while((labellen=*pos++)) {
970 if(pos+labellen > end)
971 break;
972 ret.append(pos, labellen);
973 ret.append(1,'.');
974 pos+=labellen;
975 }
976 if(ret.empty())
977 ret=".";
978 return ret;
979}
980
09e6702a
BH
981
982void handleRCC(int fd, boost::any& var)
983{
984 string remote;
985 string msg=s_rcc.recv(&remote);
986 RecursorControlParser rcp;
987 RecursorControlParser::func_t* command;
988 string answer=rcp.getAnswer(msg, &command);
989 s_rcc.send(answer, &remote);
990 command();
991}
992
993void handleTCPClientReadable(int fd, boost::any& var)
994{
0b18b22e 995 PacketID* pident=any_cast<PacketID>(&var);
667f7e60 996 // cerr<<"handleTCPClientReadable called for fd "<<fd<<", pident->inNeeded: "<<pident->inNeeded<<", "<<pident->sock->getHandle()<<endl;
09e6702a 997
667f7e60 998 shared_array<char> buffer(new char[pident->inNeeded]);
09e6702a 999
705f31ae 1000 int ret=recv(fd, buffer.get(), pident->inNeeded,0);
09e6702a 1001 if(ret > 0) {
667f7e60
BH
1002 pident->inMSG.append(&buffer[0], &buffer[ret]);
1003 pident->inNeeded-=ret;
1004 if(!pident->inNeeded) {
1005 // cerr<<"Got entire load of "<<pident->inMSG.size()<<" bytes"<<endl;
1006 PacketID pid=*pident;
1007 string msg=pident->inMSG;
09e6702a
BH
1008
1009 g_fdm->removeReadFD(fd);
1010 MT->sendEvent(pid, &msg);
1011 }
1012 else {
667f7e60 1013 // cerr<<"Still have "<<pident->inNeeded<<" left to go"<<endl;
09e6702a
BH
1014 }
1015 }
1016 else {
667f7e60 1017 PacketID tmp=*pident;
09e6702a
BH
1018 g_fdm->removeReadFD(fd); // pident might now be invalid (it isn't, but still)
1019 string empty;
1020 MT->sendEvent(tmp, &empty); // this conveys error status
1021 }
1022}
1023
1024void handleTCPClientWritable(int fd, boost::any& var)
1025{
0b18b22e 1026 PacketID* pid=any_cast<PacketID>(&var);
09e6702a 1027
705f31ae 1028 int ret=send(fd, pid->outMSG.c_str(), pid->outMSG.size() - pid->outPos,0);
09e6702a 1029 if(ret > 0) {
667f7e60
BH
1030 pid->outPos+=ret;
1031 if(pid->outPos==pid->outMSG.size()) {
1032 PacketID tmp=*pid;
09e6702a
BH
1033 g_fdm->removeWriteFD(fd);
1034 MT->sendEvent(tmp, &tmp.outMSG); // send back what we sent to convey everything is ok
1035 }
1036 }
1037 else { // error or EOF
667f7e60 1038 PacketID tmp(*pid);
09e6702a
BH
1039 g_fdm->removeWriteFD(fd);
1040 string sent;
998a4334 1041 MT->sendEvent(tmp, &sent); // we convey error status by sending empty string
09e6702a
BH
1042 }
1043}
1044
998a4334 1045void handleUDPServerResponse(int fd, boost::any& var)
09e6702a 1046{
600fc20b 1047 PacketID pid=any_cast<PacketID>(var);
998a4334 1048 int len;
09e6702a 1049 char data[1500];
996c89cc 1050 ComboAddress fromaddr;
09e6702a
BH
1051 socklen_t addrlen=sizeof(fromaddr);
1052
998a4334 1053 len=recvfrom(fd, data, sizeof(data), 0, (sockaddr *)&fromaddr, &addrlen);
600fc20b 1054
998a4334
BH
1055 if(len < (int)sizeof(dnsheader)) {
1056 if(len < 0)
996c89cc 1057 ; // cerr<<"Error on fd "<<fd<<": "<<stringerror()<<"\n";
09e6702a
BH
1058 else {
1059 g_stats.serverParseError++;
1060 if(g_logCommonErrors)
37d3f960 1061 L<<Logger::Error<<"Unable to parse packet from remote UDP server "<< sockAddrToString((struct sockaddr_in*) &fromaddr) <<
998a4334
BH
1062 ": packet smalller than DNS header"<<endl;
1063 }
1064 string empty;
600fc20b 1065 g_udpclientsocks.returnSocket(fd);
998a4334
BH
1066 MT->sendEvent(pid, &empty); // this denotes error
1067 return;
1068 }
1069
1070 dnsheader dh;
1071 memcpy(&dh, data, sizeof(dh));
1072
1073 if(!dh.qdcount) // UPC, Nominum?
1074 return;
1075
1076 if(dh.qr) {
1077 PacketID pident;
1078 pident.remote=fromaddr;
1079 pident.id=dh.id;
1080 pident.fd=fd;
1081 pident.domain=questionExpand(data, len); // don't copy this from above - we need to do the actual read
1082 string packet;
1083 packet.assign(data, len);
1084 if(!MT->sendEvent(pident, &packet)) {
1085 // if(g_logCommonErrors)
1086 // L<<Logger::Warning<<"Discarding unexpected packet from "<<sockAddrToString((struct sockaddr_in*) &fromaddr, addrlen)<<endl;
1087 g_stats.unexpectedCount++;
1088
1089 for(MT_t::waiters_t::iterator mthread=MT->d_waiters.begin(); mthread!=MT->d_waiters.end(); ++mthread) {
996c89cc 1090 if(pident.fd==mthread->key.fd && mthread->key.remote==pident.remote &&
705f31ae 1091 !Utility::strcasecmp(pident.domain.c_str(), mthread->key.domain.c_str())) {
998a4334
BH
1092 mthread->key.nearMisses++;
1093 }
1094 }
09e6702a 1095 }
2a5e6212
BH
1096 else
1097 g_udpclientsocks.returnSocket(fd);
09e6702a 1098 }
998a4334 1099 else
37d3f960 1100 L<<Logger::Warning<<"Ignoring question on outgoing socket from "<< sockAddrToString((struct sockaddr_in*) &fromaddr) <<endl;
09e6702a
BH
1101}
1102
1f4abb20
BH
1103FDMultiplexer* getMultiplexer()
1104{
1105 FDMultiplexer* ret;
1106 for(FDMultiplexer::FDMultiplexermap_t::const_iterator i = FDMultiplexer::getMultiplexerMap().begin();
1107 i != FDMultiplexer::getMultiplexerMap().end(); ++i) {
1108 try {
1109 ret=i->second();
1110 L<<Logger::Error<<"Enabled '"<<ret->getName()<<"' multiplexer"<<endl;
1111 return ret;
1112 }
98d0ee4a 1113 catch(FDMultiplexerException &fe) {
0a7f24cb 1114 L<<Logger::Error<<"Non-fatal error initializing possible multiplexer ("<<fe.what()<<"), falling back"<<endl;
98d0ee4a
BH
1115 }
1116 catch(...) {
1117 L<<Logger::Error<<"Non-fatal error initializing possible multiplexer"<<endl;
1118 }
1f4abb20
BH
1119 }
1120 L<<Logger::Error<<"No working multiplexer found!"<<endl;
1121 exit(1);
1122}
1123
5605c067
BH
1124static void makeNameToIPZone(const string& hostname, const string& ip)
1125{
1126 SyncRes::AuthDomain ad;
1127 DNSResourceRecord rr;
1128 rr.qname=toCanonic("", hostname);
1129 rr.d_place=DNSResourceRecord::ANSWER;
1130 rr.ttl=86400;
1131 rr.qtype=QType::SOA;
c0247d0e 1132 rr.content="localhost. root 1 604800 86400 2419200 604800";
5605c067
BH
1133
1134 ad.d_records.insert(rr);
1135
1136 rr.qtype=QType::NS;
1137 rr.content="localhost.";
1138
1139 ad.d_records.insert(rr);
1140
1141 rr.qtype=QType::A;
1142 rr.content=ip;
1143 ad.d_records.insert(rr);
1144
1145 if(SyncRes::s_domainmap.count(rr.qname)) {
1146 L<<Logger::Warning<<"Hosts file will not overwrite zone '"<<rr.qname<<"' already loaded"<<endl;
1147 }
1148 else {
1149 L<<Logger::Warning<<"Inserting forward zone '"<<rr.qname<<"' based on hosts file"<<endl;
1150 SyncRes::s_domainmap[rr.qname]=ad;
1151 }
1152}
1153
9bc8c14c 1154//! parts[0] must be an IP address, the rest must be host names
5605c067
BH
1155static void makeIPToNamesZone(const vector<string>& parts)
1156{
1157 string address=parts[0];
1158 vector<string> ipparts;
1159 stringtok(ipparts, address,".");
5605c067 1160
5605c067
BH
1161 SyncRes::AuthDomain ad;
1162 DNSResourceRecord rr;
9bc8c14c 1163 for(int n=ipparts.size()-1; n>=0 ; --n) {
5605c067
BH
1164 rr.qname.append(ipparts[n]);
1165 rr.qname.append(1,'.');
1166 }
1167 rr.qname.append("in-addr.arpa.");
1168
1169 rr.d_place=DNSResourceRecord::ANSWER;
1170 rr.ttl=86400;
1171 rr.qtype=QType::SOA;
9bc8c14c 1172 rr.content="localhost. root. 1 604800 86400 2419200 604800";
5605c067
BH
1173
1174 ad.d_records.insert(rr);
1175
1176 rr.qtype=QType::NS;
1177 rr.content="localhost.";
1178
1179 ad.d_records.insert(rr);
1180 rr.qtype=QType::PTR;
1181
9bc8c14c
BH
1182 if(ipparts.size()==4) // otherwise this is a partial zone
1183 for(unsigned int n=1; n < parts.size(); ++n) {
1184 rr.content=toCanonic("", parts[n]);
1185 ad.d_records.insert(rr);
1186 }
5605c067
BH
1187
1188 if(SyncRes::s_domainmap.count(rr.qname)) {
9bc8c14c 1189 L<<Logger::Warning<<"Will not overwrite zone '"<<rr.qname<<"' already loaded"<<endl;
5605c067
BH
1190 }
1191 else {
9bc8c14c
BH
1192 if(ipparts.size()==4)
1193 L<<Logger::Warning<<"Inserting reverse zone '"<<rr.qname<<"' based on hosts file"<<endl;
5605c067
BH
1194 SyncRes::s_domainmap[rr.qname]=ad;
1195 }
1196}
1197
1198void parseAuthAndForwards()
1199{
1200 SyncRes::s_domainmap.clear(); // this makes us idempotent
1201
1202 typedef vector<string> parts_t;
1203 parts_t parts;
1204 for(int n=0; n < 2 ; ++n ) {
1205 parts.clear();
1206 stringtok(parts, ::arg()[n ? "forward-zones" : "auth-zones"], ",\t\n\r");
1207 for(parts_t::const_iterator iter = parts.begin(); iter != parts.end(); ++iter) {
1208 SyncRes::AuthDomain ad;
1209 pair<string,string> headers=splitField(*iter, '=');
1210 trim(headers.first);
1211 trim(headers.second);
1212 headers.first=toCanonic("", headers.first);
1213 if(n==0) {
1214 L<<Logger::Error<<"Parsing authoritative data for zone '"<<headers.first<<"' from file '"<<headers.second<<"'"<<endl;
1215 ZoneParserTNG zpt(headers.second, headers.first);
1216 DNSResourceRecord rr;
1217 while(zpt.get(rr)) {
1218 ad.d_records.insert(rr);
1219 }
1220 }
1221 else {
1222 L<<Logger::Error<<"Redirecting queries for zone '"<<headers.first<<"' to IP '"<<headers.second<<"'"<<endl;
1223 ad.d_server=headers.second;
1224 }
1225
1226 SyncRes::s_domainmap[headers.first]=ad;
1227 }
1228 }
1229
9bc8c14c
BH
1230 if(::arg().mustDo("export-etc-hosts")) {
1231 string line;
1232 string fname;
5605c067 1233
9bc8c14c
BH
1234 ifstream ifs("/etc/hosts");
1235 if(!ifs) {
1236 L<<Logger::Warning<<"Could not open /etc/hosts for reading"<<endl;
1237 return;
1238 }
1239
1240 string::size_type pos;
1241 while(getline(ifs,line)) {
1242 pos=line.find('#');
1243 if(pos!=string::npos)
1244 line.resize(pos);
1245 trim(line);
1246 if(line.empty())
1247 continue;
1248 parts.clear();
1249 stringtok(parts, line, "\t\r\n ");
1250 if(parts[0].find(':')!=string::npos)
1251 continue;
1252
1253 for(unsigned int n=1; n < parts.size(); ++n)
1254 makeNameToIPZone(parts[n], parts[0]);
1255 makeIPToNamesZone(parts);
1256 }
1257 }
1258 if(::arg().mustDo("serve-rfc1918")) {
1259 L<<Logger::Warning<<"Inserting rfc 1918 private space zones"<<endl;
5605c067 1260 parts.clear();
9bc8c14c
BH
1261 parts.push_back("127");
1262 makeIPToNamesZone(parts);
1263 parts[0]="10";
1264 makeIPToNamesZone(parts);
5605c067 1265
9bc8c14c 1266 parts[0]="192.168";
5605c067 1267 makeIPToNamesZone(parts);
9bc8c14c
BH
1268 for(int n=16; n < 32; n++) {
1269 parts[0]="172."+lexical_cast<string>(n);
1270 makeIPToNamesZone(parts);
1271 }
5605c067
BH
1272 }
1273}
1274
f7c1d4e3
BH
1275int serviceMain(int argc, char**argv)
1276{
1277 L.setName("pdns_recursor");
1278
1279 L<<Logger::Warning<<"PowerDNS recursor "<<VERSION<<" (C) 2001-2006 PowerDNS.COM BV ("<<__DATE__", "__TIME__;
1280#ifdef __GNUC__
1281 L<<", gcc "__VERSION__;
1282#endif // add other compilers here
1283#ifdef _MSC_VER
1284 L<<", MSVC "<<_MSC_VER;
1285#endif
1286 L<<") starting up"<<endl;
1287
1288 L<<Logger::Warning<<"PowerDNS comes with ABSOLUTELY NO WARRANTY. "
1289 "This is free software, and you are welcome to redistribute it "
1290 "according to the terms of the GPL version 2."<<endl;
1291
1292 L<<Logger::Warning<<"Operating in "<<(sizeof(unsigned long)*8) <<" bits mode"<<endl;
1293
1294 if(!::arg()["allow-from"].empty()) {
1295 g_allowFrom=new NetmaskGroup;
1296 vector<string> ips;
1297 stringtok(ips, ::arg()["allow-from"], ", ");
1298 L<<Logger::Warning<<"Only allowing queries from: ";
1299 for(vector<string>::const_iterator i = ips.begin(); i!= ips.end(); ++i) {
1300 g_allowFrom->addMask(*i);
1301 if(i!=ips.begin())
1302 L<<Logger::Warning<<", ";
1303 L<<Logger::Warning<<*i;
1304 }
1305 L<<Logger::Warning<<endl;
1306 }
1307 else if(::arg()["local-address"]!="127.0.0.1" && ::arg().asNum("local-port")==53)
1308 L<<Logger::Error<<"WARNING: Allowing queries from all IP addresses - this can be a security risk!"<<endl;
1309
1310 g_quiet=::arg().mustDo("quiet");
1311 if(::arg().mustDo("trace")) {
1312 SyncRes::setLog(true);
1313 ::arg().set("quiet")="no";
1314 g_quiet=false;
1315 }
1316
1317
1318 if(!::arg()["query-local-address6"].empty()) {
1319 SyncRes::s_doIPv6=true;
1320 L<<Logger::Error<<"Enabling IPv6 transport for outgoing queries"<<endl;
1321 }
1322
1323 SyncRes::s_maxnegttl=::arg().asNum("max-negative-ttl");
1324 SyncRes::s_serverID=::arg()["server-id"];
1325 if(SyncRes::s_serverID.empty()) {
1326 char tmp[128];
1327 gethostname(tmp, sizeof(tmp)-1);
1328 SyncRes::s_serverID=tmp;
1329 }
1330
1331
1332 parseAuthAndForwards();
1333
1334 g_stats.remotes.resize(::arg().asNum("remotes-ringbuffer-entries"));
1335 if(!g_stats.remotes.empty())
1336 memset(&g_stats.remotes[0], 0, g_stats.remotes.size() * sizeof(RecursorStats::remotes_t::value_type));
1337 g_logCommonErrors=::arg().mustDo("log-common-errors");
1338
1339 makeUDPServerSockets();
1340 makeTCPServerSockets();
1341
1342#ifndef WIN32
1343 if(::arg().mustDo("fork")) {
1344 fork();
1345 L<<Logger::Warning<<"This is forked pid "<<getpid()<<endl;
1346 }
1347#endif
1348
1349 MT=new MTasker<PacketID,string>(100000);
1350 makeControlChannelSocket();
1351 PacketID pident;
1352 primeHints();
1353 L<<Logger::Warning<<"Done priming cache with root hints"<<endl;
1354#ifndef WIN32
1355 if(::arg().mustDo("daemon")) {
1356 L<<Logger::Warning<<"Calling daemonize, going to background"<<endl;
1357 L.toConsole(Logger::Critical);
1358 L.setLoglevel((Logger::Urgency)(4));
1359
1360 daemonize();
1361 }
1362 signal(SIGUSR1,usr1Handler);
1363 signal(SIGUSR2,usr2Handler);
1364 signal(SIGPIPE,SIG_IGN);
1365 writePid();
1366#endif
1367 g_fdm=getMultiplexer();
1368
1369 for(deferredAdd_t::const_iterator i=deferredAdd.begin(); i!=deferredAdd.end(); ++i)
1370 g_fdm->addReadFD(i->first, i->second);
1371
1372 int newgid=0;
1373 if(!::arg()["setgid"].empty())
1374 newgid=Utility::makeGidNumeric(::arg()["setgid"]);
1375 int newuid=0;
1376 if(!::arg()["setuid"].empty())
1377 newuid=Utility::makeUidNumeric(::arg()["setuid"]);
1378
1379#ifndef WIN32
1380 if (!::arg()["chroot"].empty()) {
1381 if (chroot(::arg()["chroot"].c_str())<0) {
1382 L<<Logger::Error<<"Unable to chroot to '"+::arg()["chroot"]+"': "<<strerror (errno)<<", exiting"<<endl;
1383 exit(1);
1384 }
1385 }
1386
1387 Utility::dropPrivs(newuid, newgid);
1388 g_fdm->addReadFD(s_rcc.d_fd, handleRCC); // control channel
1389#endif
1390
1391 counter=0;
1392 unsigned int maxTcpClients=::arg().asNum("max-tcp-clients");
1393 g_tcpTimeout=::arg().asNum("client-tcp-timeout");
1394
1395 g_maxTCPPerClient=::arg().asNum("max-tcp-per-client");
1396
1397
1398 bool listenOnTCP(true);
1399
1400 for(;;) {
1401 while(MT->schedule()); // housekeeping, let threads do their thing
1402
1403 if(!(counter%500)) {
1404 MT->makeThread(houseKeeping,0);
1405 }
1406
1407 if(!(counter%11)) {
1408 typedef vector<pair<int, boost::any> > expired_t;
1409 expired_t expired=g_fdm->getTimeouts(g_now);
1410
1411 for(expired_t::iterator i=expired.begin() ; i != expired.end(); ++i) {
1412 TCPConnection conn=any_cast<TCPConnection>(i->second);
1413 if(conn.state != TCPConnection::DONE) {
1414 if(g_logCommonErrors)
1415 L<<Logger::Warning<<"Timeout from remote TCP client "<< conn.remote.toString() <<endl;
1416 g_fdm->removeReadFD(i->first);
1417 conn.closeAndCleanup();
1418 }
1419 }
1420 }
1421
1422 counter++;
1423
1424 if(statsWanted) {
1425 doStats();
1426 }
1427
1428 Utility::gettimeofday(&g_now, 0);
1429 g_fdm->run(&g_now);
1430
1431 if(listenOnTCP) {
1432 if(TCPConnection::s_currentConnections > maxTcpClients) { // shutdown
1433 for(g_tcpListenSockets_t::iterator i=g_tcpListenSockets.begin(); i != g_tcpListenSockets.end(); ++i)
1434 g_fdm->removeReadFD(*i);
1435 listenOnTCP=false;
1436 }
1437 }
1438 else {
1439 if(TCPConnection::s_currentConnections <= maxTcpClients) { // reenable
1440 for(g_tcpListenSockets_t::iterator i=g_tcpListenSockets.begin(); i != g_tcpListenSockets.end(); ++i)
1441 g_fdm->addReadFD(*i, handleNewTCPQuestion);
1442 listenOnTCP=true;
1443 }
1444 }
1445 }
1446}
1447#ifdef WIN32
1448void doWindowsServiceArguments(RecursorService& recursor)
1449{
1450 if(::arg().mustDo( "register-service" )) {
1451 if ( !recursor.registerService( "The PowerDNS Recursor.", true )) {
1452 cerr << "Could not register service." << endl;
1453 exit( 99 );
1454 }
1455
1456 exit( 0 );
1457 }
1458
1459 if ( ::arg().mustDo( "unregister-service" )) {
1460 recursor.unregisterService();
1461 exit( 0 );
1462 }
1463}
1464#endif
1465
288f4aa9
BH
1466int main(int argc, char **argv)
1467{
8a63d3ce 1468 reportBasicTypes();
ea634573 1469
22030c37 1470 int ret = EXIT_SUCCESS;
caa6eefa 1471#ifdef WIN32
f7c1d4e3
BH
1472 RecursorService service;
1473 WSADATA wsaData;
1474 if(WSAStartup( MAKEWORD( 2, 2 ), &wsaData )) {
1475 cerr<<"Unable to initialize winsock\n";
1476 exit(1);
1477 }
caa6eefa
BH
1478#endif // WIN32
1479
288f4aa9 1480 try {
caa6eefa 1481 Utility::srandom(time(0));
2e3d8a19
BH
1482 ::arg().set("soa-minimum-ttl","Don't change")="0";
1483 ::arg().set("soa-serial-offset","Don't change")="0";
1484 ::arg().set("no-shuffle","Don't change")="off";
1485 ::arg().set("aaaa-additional-processing","turn on to do AAAA additional processing (slow)")="off";
1486 ::arg().set("local-port","port to listen on")="53";
01ed3112 1487 ::arg().set("local-address","IP addresses to listen on, separated by spaces or commas")="127.0.0.1";
2e3d8a19
BH
1488 ::arg().set("trace","if we should output heaps of logging")="off";
1489 ::arg().set("daemon","Operate as a daemon")="yes";
0e9d9ce2 1490 ::arg().set("log-common-errors","If we should log rather common errors")="yes";
2e3d8a19
BH
1491 ::arg().set("chroot","switch to chroot jail")="";
1492 ::arg().set("setgid","If set, change group id to this gid for more security")="";
1493 ::arg().set("setuid","If set, change user id to this uid for more security")="";
c038218b
BH
1494#ifdef WIN32
1495 ::arg().set("quiet","Suppress logging of questions and answers")="off";
f7c1d4e3
BH
1496 ::arg().setSwitch( "register-service", "Register the service" )= "no";
1497 ::arg().setSwitch( "unregister-service", "Unregister the service" )= "no";
1498 ::arg().setSwitch( "ntservice", "Run as service" )= "no";
1499 ::arg().setSwitch( "use-ntlog", "Use the NT logging facilities" )= "yes";
1500 ::arg().setSwitch( "use-logfile", "Use a log file" )= "no";
1501 ::arg().setSwitch( "logfile", "Filename of the log file" )= "recursor.log";
c038218b
BH
1502#else
1503 ::arg().set("quiet","Suppress logging of questions and answers")="";
1504#endif
2e3d8a19
BH
1505 ::arg().set("config-dir","Location of configuration directory (recursor.conf)")=SYSCONFDIR;
1506 ::arg().set("socket-dir","Where the controlsocket will live")=LOCALSTATEDIR;
1507 ::arg().set("delegation-only","Which domains we only accept delegations from")="";
1508 ::arg().set("query-local-address","Source IP address for sending queries")="0.0.0.0";
996c89cc 1509 ::arg().set("query-local-address6","Source IPv6 address for sending queries")="";
2e3d8a19
BH
1510 ::arg().set("client-tcp-timeout","Timeout in seconds when talking to TCP clients")="2";
1511 ::arg().set("max-tcp-clients","Maximum number of simultaneous TCP clients")="128";
1512 ::arg().set("hint-file", "If set, load root hints from this file")="";
bec87d21 1513 ::arg().set("max-cache-entries", "If set, maximum number of entries in the main cache")="0";
a9af3782
BH
1514 ::arg().set("max-negative-ttl", "maximum number of seconds to keep a negative cached entry in memory")="3600";
1515 ::arg().set("server-id", "Returned when queried for 'server.id' TXT, defaults to hostname")="";
1516 ::arg().set("remotes-ringbuffer-entries", "maximum number of packets to store statistics for")="0";
d5141417 1517 ::arg().set("version-string", "string reported on version.pdns or version.bind")="PowerDNS Recursor "VERSION" $Id$";
01ed3112 1518 ::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 1519 ::arg().set("max-tcp-per-client", "If set, maximum number of TCP sessions per client (IP address)")="0";
41f7a068 1520 ::arg().set("fork", "If set, fork the daemon for possible double performance")="no";
0d5f0a9f 1521 ::arg().set("spoof-nearmiss-max", "If non-zero, assume spoofing after this many near misses")="20";
4ef015cd 1522 ::arg().set("single-socket", "If set, only use a single socket for outgoing queries")="off";
5605c067
BH
1523 ::arg().set("auth-zones", "Zones for which we have authoritative data, comma separated domain=file pairs ")="";
1524 ::arg().set("forward-zones", "Zones for which we forward queries, comma separated domain=ip pairs")="";
1525 ::arg().set("export-etc-hosts", "If we should serve up contents from /etc/hosts")="off";
9bc8c14c 1526 ::arg().set("serve-rfc1918", "If we should be authoritative for RFC 1918 private IP space")="";
2e3d8a19
BH
1527
1528 ::arg().setCmd("help","Provide a helpful message");
d5141417 1529 ::arg().setCmd("config","Output blank configuration");
c75a6a9e 1530 L.toConsole(Logger::Warning);
2e3d8a19 1531 ::arg().laxParse(argc,argv); // do a lax parse
c75a6a9e 1532
2e3d8a19 1533 string configname=::arg()["config-dir"]+"/recursor.conf";
c75a6a9e
BH
1534 cleanSlashes(configname);
1535
2e3d8a19 1536 if(!::arg().file(configname.c_str()))
c75a6a9e
BH
1537 L<<Logger::Warning<<"Unable to parse configuration file '"<<configname<<"'"<<endl;
1538
2e3d8a19 1539 ::arg().parse(argc,argv);
c836dc19 1540
2e3d8a19 1541 ::arg().set("delegation-only")=toLower(::arg()["delegation-only"]);
562588a3 1542
2e3d8a19 1543 if(::arg().mustDo("help")) {
b636533b 1544 cerr<<"syntax:"<<endl<<endl;
2e3d8a19 1545 cerr<<::arg().helpstring(::arg()["help"])<<endl;
b636533b
BH
1546 exit(99);
1547 }
1548
d5141417
BH
1549 if(::arg().mustDo("config")) {
1550 cout<<::arg().configstring()<<endl;
1551 exit(0);
1552 }
1553
caa6eefa 1554#ifndef WIN32
f7c1d4e3
BH
1555 serviceMain(argc, argv);
1556#else
6a0bb0cf 1557 doWindowsServiceArguments(service);
f7c1d4e3 1558 RecursorService::instance()->start( argc, argv, ::arg().mustDo( "ntservice" ));
caa6eefa 1559#endif
998a4334 1560
288f4aa9
BH
1561 }
1562 catch(AhuException &ae) {
c836dc19 1563 L<<Logger::Error<<"Exception: "<<ae.reason<<endl;
22030c37 1564 ret=EXIT_FAILURE;
288f4aa9
BH
1565 }
1566 catch(exception &e) {
c836dc19 1567 L<<Logger::Error<<"STL Exception: "<<e.what()<<endl;
22030c37 1568 ret=EXIT_FAILURE;
288f4aa9
BH
1569 }
1570 catch(...) {
c836dc19 1571 L<<Logger::Error<<"any other exception in main: "<<endl;
22030c37 1572 ret=EXIT_FAILURE;
288f4aa9 1573 }
caa6eefa
BH
1574
1575#ifdef WIN32
1576 WSACleanup();
1577#endif // WIN32
1578
22030c37 1579 return ret;
288f4aa9 1580}