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