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