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