]>
Commit | Line | Data |
---|---|---|
12c86877 BH |
1 | /* |
2 | PowerDNS Versatile Database Driven Nameserver | |
b5d7e593 | 3 | Copyright (C) 2002-2009 PowerDNS.COM BV |
12c86877 BH |
4 | |
5 | This program is free software; you can redistribute it and/or modify | |
22dc646a BH |
6 | it under the terms of the GNU General Public License version 2 |
7 | as published by the Free Software Foundation | |
8 | ||
12c86877 BH |
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 |
12c86877 | 17 | */ |
379ab445 | 18 | #include "packetcache.hh" |
1258abe0 | 19 | #include "utility.hh" |
12c86877 BH |
20 | #include <cstdio> |
21 | #include <cstring> | |
22 | #include <cstdlib> | |
23 | #include <sys/types.h> | |
24 | #include <iostream> | |
25 | #include <string> | |
26 | #include "tcpreceiver.hh" | |
67d74e49 | 27 | #include "sstuff.hh" |
8edfedf1 | 28 | #include <boost/foreach.hpp> |
12c86877 BH |
29 | #include <errno.h> |
30 | #include <signal.h> | |
31 | ||
32 | #include "ueberbackend.hh" | |
33 | #include "dnspacket.hh" | |
34 | #include "nameserver.hh" | |
35 | #include "distributor.hh" | |
36 | #include "lock.hh" | |
37 | #include "logger.hh" | |
38 | #include "arguments.hh" | |
379ab445 | 39 | |
12c86877 BH |
40 | #include "packethandler.hh" |
41 | #include "statbag.hh" | |
42 | #include "resolver.hh" | |
43 | #include "communicator.hh" | |
61b26744 | 44 | #include "namespaces.hh" |
12c86877 BH |
45 | |
46 | extern PacketCache PC; | |
47 | extern StatBag S; | |
48 | ||
49 | /** | |
50 | \file tcpreceiver.cc | |
51 | \brief This file implements the tcpreceiver that receives and answers questions over TCP/IP | |
52 | */ | |
53 | ||
ac2bb9e7 | 54 | pthread_mutex_t TCPNameserver::s_plock = PTHREAD_MUTEX_INITIALIZER; |
12c86877 BH |
55 | Semaphore *TCPNameserver::d_connectionroom_sem; |
56 | PacketHandler *TCPNameserver::s_P; | |
57 | int TCPNameserver::s_timeout; | |
9f1d5826 | 58 | NetmaskGroup TCPNameserver::d_ng; |
12c86877 | 59 | |
12c86877 BH |
60 | void TCPNameserver::go() |
61 | { | |
62 | L<<Logger::Error<<"Creating backend connection for TCP"<<endl; | |
63 | s_P=0; | |
64 | try { | |
65 | s_P=new PacketHandler; | |
66 | } | |
67 | catch(AhuException &ae) { | |
68 | L<<Logger::Error<<Logger::NTLog<<"TCP server is unable to launch backends - will try again when questions come in"<<endl; | |
fd8bc993 | 69 | L<<Logger::Error<<"TCP server is unable to launch backends - will try again when questions come in: "<<ae.reason<<endl; |
12c86877 BH |
70 | } |
71 | pthread_create(&d_tid, 0, launcher, static_cast<void *>(this)); | |
72 | } | |
73 | ||
74 | void *TCPNameserver::launcher(void *data) | |
75 | { | |
76 | static_cast<TCPNameserver *>(data)->thread(); | |
77 | return 0; | |
78 | } | |
79 | ||
6a3e5d1a BH |
80 | // throws AhuException if things didn't go according to plan, returns 0 if really 0 bytes were read |
81 | int readnWithTimeout(int fd, void* buffer, unsigned int n, bool throwOnEOF=true) | |
12c86877 | 82 | { |
6a3e5d1a BH |
83 | unsigned int bytes=n; |
84 | char *ptr = (char*)buffer; | |
85 | int ret; | |
86 | while(bytes) { | |
87 | ret=read(fd, ptr, bytes); | |
88 | if(ret < 0) { | |
89 | if(errno==EAGAIN) { | |
4957a608 BH |
90 | ret=waitForData(fd, 5); |
91 | if(ret < 0) | |
92 | throw NetworkError("Waiting for data read"); | |
93 | if(!ret) | |
94 | throw NetworkError("Timeout reading data"); | |
95 | continue; | |
6a3e5d1a BH |
96 | } |
97 | else | |
4957a608 | 98 | throw NetworkError("Reading data: "+stringerror()); |
6a3e5d1a BH |
99 | } |
100 | if(!ret) { | |
101 | if(!throwOnEOF && n == bytes) | |
4957a608 | 102 | return 0; |
6a3e5d1a | 103 | else |
4957a608 | 104 | throw NetworkError("Did not fulfill read from TCP due to EOF"); |
6a3e5d1a BH |
105 | } |
106 | ||
107 | ptr += ret; | |
108 | bytes -= ret; | |
109 | } | |
110 | return n; | |
111 | } | |
12c86877 | 112 | |
6a3e5d1a BH |
113 | // ditto |
114 | void writenWithTimeout(int fd, const void *buffer, unsigned int n) | |
115 | { | |
116 | unsigned int bytes=n; | |
117 | const char *ptr = (char*)buffer; | |
118 | int ret; | |
119 | while(bytes) { | |
120 | ret=write(fd, ptr, bytes); | |
121 | if(ret < 0) { | |
122 | if(errno==EAGAIN) { | |
4957a608 BH |
123 | ret=waitForRWData(fd, false, 5, 0); |
124 | if(ret < 0) | |
125 | throw NetworkError("Waiting for data write"); | |
126 | if(!ret) | |
127 | throw NetworkError("Timeout writing data"); | |
128 | continue; | |
6a3e5d1a BH |
129 | } |
130 | else | |
4957a608 | 131 | throw NetworkError("Writing data: "+stringerror()); |
6a3e5d1a | 132 | } |
12c86877 | 133 | if(!ret) { |
67d74e49 | 134 | throw NetworkError("Did not fulfill TCP write due to EOF"); |
12c86877 | 135 | } |
6a3e5d1a BH |
136 | |
137 | ptr += ret; | |
138 | bytes -= ret; | |
12c86877 | 139 | } |
12c86877 BH |
140 | } |
141 | ||
6a3e5d1a | 142 | void connectWithTimeout(int fd, struct sockaddr* remote, size_t socklen) |
12c86877 | 143 | { |
6a3e5d1a BH |
144 | int err; |
145 | Utility::socklen_t len=sizeof(err); | |
146 | ||
147 | #ifndef WIN32 | |
148 | if((err=connect(fd, remote, socklen))<0 && errno!=EINPROGRESS) | |
149 | #else | |
150 | if((err=connect(clisock, remote, socklen))<0 && WSAGetLastError() != WSAEWOULDBLOCK ) | |
151 | #endif // WIN32 | |
67d74e49 | 152 | throw NetworkError("connect: "+stringerror()); |
6a3e5d1a BH |
153 | |
154 | if(!err) | |
155 | goto done; | |
156 | ||
157 | err=waitForRWData(fd, false, 5, 0); | |
158 | if(err == 0) | |
67d74e49 | 159 | throw NetworkError("Timeout connecting to remote"); |
6a3e5d1a | 160 | if(err < 0) |
67d74e49 | 161 | throw NetworkError("Error connecting to remote"); |
12c86877 | 162 | |
6a3e5d1a | 163 | if(getsockopt(fd, SOL_SOCKET,SO_ERROR,(char *)&err,&len)<0) |
67d74e49 | 164 | throw NetworkError("Error connecting to remote: "+stringerror()); // Solaris |
6a3e5d1a BH |
165 | |
166 | if(err) | |
67d74e49 | 167 | throw NetworkError("Error connecting to remote: "+string(strerror(err))); |
6a3e5d1a BH |
168 | |
169 | done: | |
170 | ; | |
171 | } | |
12c86877 | 172 | |
6a3e5d1a BH |
173 | void TCPNameserver::sendPacket(shared_ptr<DNSPacket> p, int outsock) |
174 | { | |
175 | const char *buf=p->getData(); | |
176 | uint16_t len=htons(p->len); | |
177 | writenWithTimeout(outsock, &len, 2); | |
178 | writenWithTimeout(outsock, buf, p->len); | |
179 | } | |
180 | ||
181 | ||
182 | void TCPNameserver::getQuestion(int fd, char *mesg, int pktlen, const ComboAddress &remote) | |
183 | try | |
184 | { | |
185 | readnWithTimeout(fd, mesg, pktlen); | |
186 | } | |
67d74e49 BH |
187 | catch(NetworkError& ae) { |
188 | throw NetworkError("Error reading DNS data from TCP client "+remote.toString()+": "+ae.what()); | |
12c86877 BH |
189 | } |
190 | ||
ff76e8b4 BH |
191 | static void proxyQuestion(shared_ptr<DNSPacket> packet) |
192 | { | |
193 | int sock=socket(AF_INET, SOCK_STREAM, 0); | |
194 | if(sock < 0) | |
67d74e49 | 195 | throw NetworkError("Error making TCP connection socket to recursor: "+stringerror()); |
ff76e8b4 | 196 | |
6a3e5d1a BH |
197 | Utility::setNonBlocking(sock); |
198 | ServiceTuple st; | |
199 | st.port=53; | |
379ab445 | 200 | parseService(::arg()["recursor"],st); |
6a3e5d1a | 201 | |
ff76e8b4 | 202 | try { |
ff76e8b4 | 203 | ComboAddress recursor(st.host, st.port); |
6a3e5d1a | 204 | connectWithTimeout(sock, (struct sockaddr*)&recursor, recursor.getSocklen()); |
ff76e8b4 BH |
205 | const string &buffer=packet->getString(); |
206 | ||
207 | uint16_t len=htons(buffer.length()), slen; | |
208 | ||
6a3e5d1a BH |
209 | writenWithTimeout(sock, &len, 2); |
210 | writenWithTimeout(sock, buffer.c_str(), buffer.length()); | |
ff76e8b4 BH |
211 | |
212 | int ret; | |
213 | ||
6a3e5d1a | 214 | ret=readnWithTimeout(sock, &len, 2); |
ff76e8b4 BH |
215 | len=ntohs(len); |
216 | ||
217 | char answer[len]; | |
6a3e5d1a | 218 | ret=readnWithTimeout(sock, answer, len); |
ff76e8b4 BH |
219 | |
220 | slen=htons(len); | |
6a3e5d1a | 221 | writenWithTimeout(packet->getSocket(), &slen, 2); |
ff76e8b4 | 222 | |
6a3e5d1a | 223 | writenWithTimeout(packet->getSocket(), answer, len); |
ff76e8b4 | 224 | } |
67d74e49 | 225 | catch(NetworkError& ae) { |
ff76e8b4 | 226 | close(sock); |
67d74e49 | 227 | throw NetworkError("While proxying a question to recursor "+st.host+": " +ae.what()); |
ff76e8b4 BH |
228 | } |
229 | close(sock); | |
230 | return; | |
231 | } | |
232 | ||
12c86877 BH |
233 | void *TCPNameserver::doConnection(void *data) |
234 | { | |
ff76e8b4 | 235 | shared_ptr<DNSPacket> packet; |
b014ad98 BH |
236 | // Fix gcc-4.0 error (on AMD64) |
237 | int fd=(int)(long)data; // gotta love C (generates a harmless warning on opteron) | |
12c86877 | 238 | pthread_detach(pthread_self()); |
027ffd26 | 239 | Utility::setNonBlocking(fd); |
12c86877 | 240 | try { |
12c86877 BH |
241 | char mesg[512]; |
242 | ||
243 | DLOG(L<<"TCP Connection accepted on fd "<<fd<<endl); | |
244 | ||
245 | for(;;) { | |
809fe23f | 246 | ComboAddress remote; |
38a9b470 BH |
247 | socklen_t remotelen=sizeof(remote); |
248 | if(getpeername(fd, (struct sockaddr *)&remote, &remotelen) < 0) { | |
4957a608 BH |
249 | L<<Logger::Error<<"Received question from socket which had no remote address, dropping ("<<stringerror()<<")"<<endl; |
250 | break; | |
38a9b470 | 251 | } |
6a3e5d1a BH |
252 | |
253 | uint16_t pktlen; | |
254 | if(!readnWithTimeout(fd, &pktlen, 2, false)) | |
4957a608 | 255 | break; |
6a3e5d1a | 256 | else |
4957a608 | 257 | pktlen=ntohs(pktlen); |
12c86877 BH |
258 | |
259 | if(pktlen>511) { | |
4957a608 BH |
260 | L<<Logger::Error<<"Received an overly large question from "<<remote.toString()<<", dropping"<<endl; |
261 | break; | |
12c86877 BH |
262 | } |
263 | ||
264 | getQuestion(fd,mesg,pktlen,remote); | |
265 | S.inc("tcp-queries"); | |
3e579e91 | 266 | |
ff76e8b4 | 267 | packet=shared_ptr<DNSPacket>(new DNSPacket); |
809fe23f | 268 | packet->setRemote(&remote); |
e9dd48f9 | 269 | packet->d_tcp=true; |
ff76e8b4 | 270 | packet->setSocket(fd); |
c1663439 | 271 | if(packet->parse(mesg, pktlen)<0) |
4957a608 | 272 | break; |
c1663439 | 273 | |
af1311e4 | 274 | if(packet->qtype.getCode()==QType::AXFR || packet->qtype.getCode()==QType::IXFR ) { |
4957a608 BH |
275 | if(doAXFR(packet->qdomain, packet, fd)) |
276 | S.inc("tcp-answers"); | |
277 | continue; | |
12c86877 BH |
278 | } |
279 | ||
ff76e8b4 | 280 | shared_ptr<DNSPacket> reply; |
ff76e8b4 BH |
281 | shared_ptr<DNSPacket> cached= shared_ptr<DNSPacket>(new DNSPacket); |
282 | ||
283 | if(!packet->d.rd && (PC.get(packet.get(), cached.get()))) { // short circuit - does the PacketCache recognize this question? | |
4957a608 BH |
284 | cached->setRemote(&packet->remote); |
285 | cached->d.id=packet->d.id; | |
286 | cached->d.rd=packet->d.rd; // copy in recursion desired bit | |
287 | cached->commitD(); // commit d to the packet inlined | |
288 | ||
289 | sendPacket(cached, fd); | |
290 | S.inc("tcp-answers"); | |
291 | continue; | |
12c86877 | 292 | } |
4957a608 | 293 | |
12c86877 | 294 | { |
4957a608 BH |
295 | Lock l(&s_plock); |
296 | if(!s_P) { | |
297 | L<<Logger::Error<<"TCP server is without backend connections, launching"<<endl; | |
298 | s_P=new PacketHandler; | |
299 | } | |
300 | bool shouldRecurse; | |
301 | ||
302 | reply=shared_ptr<DNSPacket>(s_P->questionOrRecurse(packet.get(), &shouldRecurse)); // we really need to ask the backend :-) | |
303 | ||
304 | if(shouldRecurse) { | |
305 | proxyQuestion(packet); | |
306 | continue; | |
307 | } | |
12c86877 BH |
308 | } |
309 | ||
12c86877 | 310 | if(!reply) // unable to write an answer? |
4957a608 BH |
311 | break; |
312 | ||
12c86877 | 313 | S.inc("tcp-answers"); |
ff76e8b4 | 314 | sendPacket(reply, fd); |
12c86877 | 315 | } |
12c86877 | 316 | } |
cc3afe25 | 317 | catch(DBException &e) { |
556252ea BH |
318 | Lock l(&s_plock); |
319 | delete s_P; | |
320 | s_P = 0; | |
321 | ||
322 | L<<Logger::Error<<"TCP Connection Thread unable to answer a question because of a backend error, cycling"<<endl; | |
12c86877 | 323 | } |
ef1d2f44 | 324 | catch(AhuException &ae) { |
556252ea BH |
325 | Lock l(&s_plock); |
326 | delete s_P; | |
327 | s_P = 0; // on next call, backend will be recycled | |
027ffd26 | 328 | L<<Logger::Error<<"TCP nameserver had error, cycling backend: "<<ae.reason<<endl; |
ef1d2f44 | 329 | } |
0afa9049 BH |
330 | catch(NetworkError &e) { |
331 | L<<Logger::Info<<"TCP Connection Thread died because of STL error: "<<e.what()<<endl; | |
332 | } | |
333 | ||
adc10f99 | 334 | catch(std::exception &e) { |
12c86877 BH |
335 | L<<Logger::Error<<"TCP Connection Thread died because of STL error: "<<e.what()<<endl; |
336 | } | |
337 | catch( ... ) | |
338 | { | |
339 | L << Logger::Error << "TCP Connection Thread caught unknown exception." << endl; | |
340 | } | |
12c86877 | 341 | d_connectionroom_sem->post(); |
c38f6509 | 342 | Utility::closesocket(fd); |
12c86877 BH |
343 | |
344 | return 0; | |
345 | } | |
346 | ||
ff76e8b4 | 347 | bool TCPNameserver::canDoAXFR(shared_ptr<DNSPacket> q) |
12c86877 | 348 | { |
379ab445 | 349 | if(::arg().mustDo("disable-axfr")) |
318c3ec6 BH |
350 | return false; |
351 | ||
ab5edd12 | 352 | if(!::arg().mustDo("per-zone-axfr-acls") && (::arg()["allow-axfr-ips"].empty() || d_ng.match( (ComboAddress *) &q->remote ))) |
12c86877 BH |
353 | return true; |
354 | ||
ab5edd12 BH |
355 | if(::arg().mustDo("per-zone-axfr-acls")) { |
356 | SOAData sd; | |
357 | sd.db=(DNSBackend *)-1; | |
358 | if(s_P->getBackend()->getSOA(q->qdomain,sd)) { | |
359 | DNSBackend *B=sd.db; | |
360 | if (B->checkACL(string("allow-axfr"), q->qdomain, q->getRemote())) { | |
4957a608 | 361 | return true; |
ab5edd12 BH |
362 | } |
363 | } | |
364 | } | |
365 | ||
12c86877 BH |
366 | extern CommunicatorClass Communicator; |
367 | ||
368 | if(Communicator.justNotified(q->qdomain, q->getRemote())) { // we just notified this ip | |
369 | L<<Logger::Warning<<"Approved AXFR of '"<<q->qdomain<<"' from recently notified slave "<<q->getRemote()<<endl; | |
370 | return true; | |
371 | } | |
372 | ||
373 | return false; | |
374 | } | |
375 | ||
376 | /** do the actual zone transfer. Return 0 in case of error, 1 in case of success */ | |
ff76e8b4 | 377 | int TCPNameserver::doAXFR(const string &target, shared_ptr<DNSPacket> q, int outsock) |
12c86877 | 378 | { |
ff76e8b4 | 379 | shared_ptr<DNSPacket> outpacket; |
12c86877 | 380 | if(!canDoAXFR(q)) { |
20ca8e7d | 381 | L<<Logger::Error<<"AXFR of domain '"<<target<<"' denied to "<<q->getRemote()<<endl; |
12c86877 | 382 | |
ff76e8b4 | 383 | outpacket=shared_ptr<DNSPacket>(q->replyPacket()); |
12c86877 BH |
384 | outpacket->setRcode(RCode::Refused); |
385 | // FIXME: should actually figure out if we are auth over a zone, and send out 9 if we aren't | |
ff76e8b4 | 386 | sendPacket(outpacket,outsock); |
12c86877 BH |
387 | return 0; |
388 | } | |
20ca8e7d | 389 | L<<Logger::Error<<"AXFR of domain '"<<target<<"' initiated by "<<q->getRemote()<<endl; |
ff76e8b4 | 390 | outpacket=shared_ptr<DNSPacket>(q->replyPacket()); |
12c86877 BH |
391 | |
392 | DNSResourceRecord soa; | |
393 | DNSResourceRecord rr; | |
394 | ||
395 | SOAData sd; | |
3de83124 | 396 | sd.db=(DNSBackend *)-1; // force uncached answer |
12c86877 BH |
397 | { |
398 | Lock l(&s_plock); | |
399 | ||
400 | // find domain_id via SOA and list complete domain. No SOA, no AXFR | |
401 | ||
402 | DLOG(L<<"Looking for SOA"<<endl); | |
12a965c5 BH |
403 | if(!s_P) { |
404 | L<<Logger::Error<<"TCP server is without backend connections in doAXFR, launching"<<endl; | |
405 | s_P=new PacketHandler; | |
406 | } | |
12c86877 BH |
407 | |
408 | if(!s_P->getBackend()->getSOA(target,sd)) { | |
d10f9034 | 409 | L<<Logger::Error<<"AXFR of domain '"<<target<<"' failed: not authoritative"<<endl; |
12c86877 | 410 | outpacket->setRcode(9); // 'NOTAUTH' |
ff76e8b4 | 411 | sendPacket(outpacket,outsock); |
12c86877 BH |
412 | return 0; |
413 | } | |
7b308b7e | 414 | |
3de83124 BH |
415 | } |
416 | PacketHandler P; // now open up a database connection, we'll need it | |
7b308b7e | 417 | |
3de83124 | 418 | sd.db=(DNSBackend *)-1; // force uncached answer |
232f8a72 | 419 | if(!P.getBackend()->getSOA(target, sd)) { |
d10f9034 | 420 | L<<Logger::Error<<"AXFR of domain '"<<target<<"' failed: not authoritative in second instance"<<endl; |
3de83124 | 421 | outpacket->setRcode(9); // 'NOTAUTH' |
ff76e8b4 | 422 | sendPacket(outpacket,outsock); |
3de83124 BH |
423 | return 0; |
424 | } | |
7b308b7e | 425 | |
3de83124 BH |
426 | soa.qname=target; |
427 | soa.qtype=QType::SOA; | |
34b37bbb | 428 | soa.content=serializeSOAData(sd); |
232f8a72 | 429 | soa.ttl=sd.ttl; |
3de83124 BH |
430 | soa.domain_id=sd.domain_id; |
431 | soa.d_place=DNSResourceRecord::ANSWER; | |
25b1d5d7 | 432 | |
3de83124 BH |
433 | if(!sd.db || sd.db==(DNSBackend *)-1) { |
434 | L<<Logger::Error<<"Error determining backend for domain '"<<target<<"' trying to serve an AXFR"<<endl; | |
435 | outpacket->setRcode(RCode::ServFail); | |
ff76e8b4 | 436 | sendPacket(outpacket,outsock); |
3de83124 | 437 | return 0; |
12c86877 BH |
438 | } |
439 | ||
440 | DLOG(L<<"Issuing list command - opening dedicated database connection"<<endl); | |
3de83124 | 441 | |
36d772ab | 442 | DNSBackend *B=sd.db; // get the RIGHT backend |
12c86877 BH |
443 | |
444 | // now list zone | |
88def049 | 445 | if(!(B->list(target, sd.domain_id))) { |
12c86877 BH |
446 | L<<Logger::Error<<"Backend signals error condition"<<endl; |
447 | outpacket->setRcode(2); // 'SERVFAIL' | |
ff76e8b4 | 448 | sendPacket(outpacket,outsock); |
12c86877 BH |
449 | return 0; |
450 | } | |
12c86877 BH |
451 | /* write first part of answer */ |
452 | ||
453 | DLOG(L<<"Sending out SOA"<<endl); | |
454 | outpacket->addRecord(soa); // AXFR format begins and ends with a SOA record, so we add one | |
ff76e8b4 | 455 | sendPacket(outpacket, outsock); |
12c86877 BH |
456 | |
457 | /* now write all other records */ | |
458 | ||
459 | int count=0; | |
460 | int chunk=100; // FIXME: this should probably be autosizing | |
379ab445 | 461 | if(::arg().mustDo("strict-rfc-axfrs")) |
12c86877 BH |
462 | chunk=1; |
463 | ||
ff76e8b4 | 464 | outpacket=shared_ptr<DNSPacket>(q->replyPacket()); |
12c86877 BH |
465 | outpacket->setCompress(false); |
466 | ||
467 | while(B->get(rr)) { | |
468 | if(rr.qtype.getCode()==6) | |
469 | continue; // skip SOA - would indicate end of AXFR | |
b5d7e593 BH |
470 | else if(rr.qtype.getCode() == QType::URL && ::arg().mustDo("fancy-records")) { |
471 | rr.qtype = QType::A; | |
472 | rr.content = ::arg()["urlredirector"]; | |
473 | } | |
474 | else if(rr.qtype.getCode() == QType::MBOXFW && ::arg().mustDo("fancy-records")) { | |
475 | rr.qtype = QType::MX; | |
476 | rr.content = ::arg()["smtpredirector"]; | |
477 | rr.priority = 25; | |
478 | string::size_type pos = rr.qname.find('@'); | |
479 | if(pos != string::npos) | |
4957a608 | 480 | rr.qname = rr.qname.substr(pos + 1); // trim off p to and including @ |
b5d7e593 | 481 | } |
12c86877 BH |
482 | outpacket->addRecord(rr); |
483 | ||
484 | if(!((++count)%chunk)) { | |
485 | count=0; | |
486 | ||
6a3e5d1a | 487 | sendPacket(outpacket, outsock); |
12c86877 | 488 | |
ff76e8b4 | 489 | outpacket=shared_ptr<DNSPacket>(q->replyPacket()); |
12c86877 BH |
490 | outpacket->setCompress(false); |
491 | // FIXME: Subsequent messages SHOULD NOT have a question section, though the final message MAY. | |
492 | } | |
493 | } | |
494 | if(count) { | |
ff76e8b4 | 495 | sendPacket(outpacket, outsock); |
12c86877 BH |
496 | } |
497 | ||
498 | DLOG(L<<"Done writing out records"<<endl); | |
499 | /* and terminate with yet again the SOA record */ | |
ff76e8b4 | 500 | outpacket=shared_ptr<DNSPacket>(q->replyPacket()); |
12c86877 | 501 | outpacket->addRecord(soa); |
ff76e8b4 | 502 | sendPacket(outpacket, outsock); |
12c86877 | 503 | DLOG(L<<"last packet - close"<<endl); |
20ca8e7d | 504 | L<<Logger::Error<<"AXFR of domain '"<<target<<"' to "<<q->getRemote()<<" finished"<<endl; |
12c86877 BH |
505 | |
506 | return 1; | |
507 | } | |
508 | ||
509 | TCPNameserver::~TCPNameserver() | |
510 | { | |
511 | delete d_connectionroom_sem; | |
512 | } | |
513 | ||
514 | TCPNameserver::TCPNameserver() | |
515 | { | |
379ab445 BH |
516 | // sem_init(&d_connectionroom_sem,0,::arg().asNum("max-tcp-connections")); |
517 | d_connectionroom_sem = new Semaphore( ::arg().asNum( "max-tcp-connections" )); | |
12c86877 BH |
518 | |
519 | s_timeout=10; | |
520 | vector<string>locals; | |
379ab445 | 521 | stringtok(locals,::arg()["local-address"]," ,"); |
12c86877 BH |
522 | |
523 | vector<string>locals6; | |
379ab445 | 524 | stringtok(locals6,::arg()["local-ipv6"]," ,"); |
12c86877 | 525 | |
12c86877 BH |
526 | if(locals.empty() && locals6.empty()) |
527 | throw AhuException("No local address specified"); | |
528 | ||
529 | d_highfd=0; | |
530 | ||
9f1d5826 | 531 | vector<string> parts; |
379ab445 | 532 | stringtok( parts, ::arg()["allow-axfr-ips"], ", \t" ); // is this IP on the guestlist? |
9f1d5826 BH |
533 | for( vector<string>::const_iterator i = parts.begin(); i != parts.end(); ++i ) { |
534 | d_ng.addMask( *i ); | |
535 | } | |
536 | ||
12c86877 BH |
537 | #ifndef WIN32 |
538 | signal(SIGPIPE,SIG_IGN); | |
539 | #endif // WIN32 | |
12c86877 BH |
540 | |
541 | for(vector<string>::const_iterator laddr=locals.begin();laddr!=locals.end();++laddr) { | |
12c86877 BH |
542 | int s=socket(AF_INET,SOCK_STREAM,0); |
543 | ||
544 | if(s<0) | |
545 | throw AhuException("Unable to acquire TCP socket: "+stringerror()); | |
12c86877 | 546 | |
379ab445 | 547 | ComboAddress local(*laddr, ::arg().asNum("local-port")); |
12c86877 BH |
548 | |
549 | int tmp=1; | |
550 | if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0) { | |
551 | L<<Logger::Error<<"Setsockopt failed"<<endl; | |
552 | exit(1); | |
553 | } | |
554 | ||
379ab445 | 555 | if(::bind(s, (sockaddr*)&local, local.getSocklen())<0) { |
12c86877 BH |
556 | L<<Logger::Error<<"binding to TCP socket: "<<strerror(errno)<<endl; |
557 | throw AhuException("Unable to bind to TCP socket"); | |
558 | } | |
559 | ||
560 | listen(s,128); | |
ff76e8b4 | 561 | L<<Logger::Error<<"TCP server bound to "<<local.toStringWithPort()<<endl; |
12c86877 | 562 | d_sockets.push_back(s); |
8edfedf1 BH |
563 | struct pollfd pfd; |
564 | memset(&pfd, 0, sizeof(pfd)); | |
565 | pfd.fd = s; | |
566 | pfd.events = POLLIN; | |
567 | ||
568 | d_prfds.push_back(pfd); | |
569 | ||
12c86877 BH |
570 | d_highfd=max(s,d_highfd); |
571 | } | |
572 | ||
1258abe0 | 573 | #if !WIN32 && HAVE_IPV6 |
12c86877 | 574 | for(vector<string>::const_iterator laddr=locals6.begin();laddr!=locals6.end();++laddr) { |
12c86877 BH |
575 | int s=socket(AF_INET6,SOCK_STREAM,0); |
576 | ||
577 | if(s<0) | |
578 | throw AhuException("Unable to acquire TCPv6 socket: "+stringerror()); | |
178d5134 | 579 | |
379ab445 | 580 | ComboAddress local(*laddr, ::arg().asNum("local-port")); |
12c86877 BH |
581 | |
582 | int tmp=1; | |
583 | if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0) { | |
584 | L<<Logger::Error<<"Setsockopt failed"<<endl; | |
585 | exit(1); | |
586 | } | |
587 | ||
ff76e8b4 | 588 | if(bind(s, (const sockaddr*)&local, local.getSocklen())<0) { |
12c86877 BH |
589 | L<<Logger::Error<<"binding to TCP socket: "<<strerror(errno)<<endl; |
590 | throw AhuException("Unable to bind to TCPv6 socket"); | |
591 | } | |
592 | ||
593 | listen(s,128); | |
ff76e8b4 | 594 | L<<Logger::Error<<"TCPv6 server bound to "<<local.toStringWithPort()<<endl; |
12c86877 | 595 | d_sockets.push_back(s); |
8edfedf1 BH |
596 | |
597 | struct pollfd pfd; | |
598 | memset(&pfd, 0, sizeof(pfd)); | |
599 | pfd.fd = s; | |
600 | pfd.events = POLLIN; | |
601 | ||
602 | d_prfds.push_back(pfd); | |
603 | d_highfd=max(s, d_highfd); | |
12c86877 BH |
604 | } |
605 | #endif // WIN32 | |
606 | } | |
607 | ||
608 | ||
ff76e8b4 | 609 | //! Start of TCP operations thread, we launch a new thread for each incoming TCP question |
12c86877 BH |
610 | void TCPNameserver::thread() |
611 | { | |
612 | struct timeval tv; | |
613 | tv.tv_sec=1; | |
614 | tv.tv_usec=0; | |
615 | try { | |
616 | for(;;) { | |
617 | int fd; | |
618 | struct sockaddr_in remote; | |
619 | Utility::socklen_t addrlen=sizeof(remote); | |
620 | ||
8edfedf1 | 621 | int ret=poll(&d_prfds[0], d_prfds.size(), -1); // blocks, forever if need be |
8a63d3ce | 622 | if(ret <= 0) |
4957a608 | 623 | continue; |
8a63d3ce | 624 | |
12c86877 | 625 | int sock=-1; |
8edfedf1 | 626 | BOOST_FOREACH(const struct pollfd& pfd, d_prfds) { |
4957a608 BH |
627 | if(pfd.revents == POLLIN) { |
628 | sock = pfd.fd; | |
629 | addrlen=sizeof(remote); | |
630 | ||
631 | if((fd=accept(sock, (sockaddr*)&remote, &addrlen))<0) { | |
632 | L<<Logger::Error<<"TCP question accept error: "<<strerror(errno)<<endl; | |
633 | ||
634 | if(errno==EMFILE) { | |
635 | L<<Logger::Error<<Logger::NTLog<<"TCP handler out of filedescriptors, exiting, won't recover from this"<<endl; | |
636 | exit(1); | |
637 | } | |
638 | } | |
639 | else { | |
640 | pthread_t tid; | |
641 | d_connectionroom_sem->wait(); // blocks if no connections are available | |
642 | ||
643 | int room; | |
644 | d_connectionroom_sem->getValue( &room); | |
645 | if(room<1) | |
646 | L<<Logger::Warning<<Logger::NTLog<<"Limit of simultaneous TCP connections reached - raise max-tcp-connections"<<endl; | |
647 | ||
648 | if(pthread_create(&tid, 0, &doConnection, (void *)fd)) { | |
649 | L<<Logger::Error<<"Error creating thread: "<<stringerror()<<endl; | |
650 | d_connectionroom_sem->post(); | |
651 | } | |
652 | } | |
653 | } | |
12c86877 BH |
654 | } |
655 | } | |
656 | } | |
657 | catch(AhuException &AE) { | |
658 | L<<Logger::Error<<"TCP Namerserver thread dying because of fatal error: "<<AE.reason<<endl; | |
659 | } | |
660 | catch(...) { | |
661 | L<<Logger::Error<<"TCPNameserver dying because of an unexpected fatal error"<<endl; | |
662 | } | |
663 | exit(1); // take rest of server with us | |
664 | } | |
665 | ||
666 |