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