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