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