]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/tcpreceiver.cc
fix memory leak (Dan Bilik <dan@mail.neosystem.cz>) - thanks!
[thirdparty/pdns.git] / pdns / tcpreceiver.cc
CommitLineData
12c86877
BH
1/*
2 PowerDNS Versatile Database Driven Nameserver
7b308b7e 3 Copyright (C) 2002-2005 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
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
06bd9ccf 17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
12c86877 18*/
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"
1258abe0 27
12c86877
BH
28#include <errno.h>
29#include <signal.h>
30
31#include "ueberbackend.hh"
32#include "dnspacket.hh"
33#include "nameserver.hh"
34#include "distributor.hh"
35#include "lock.hh"
36#include "logger.hh"
37#include "arguments.hh"
38#include "packetcache.hh"
39#include "packethandler.hh"
40#include "statbag.hh"
41#include "resolver.hh"
42#include "communicator.hh"
43
44extern PacketCache PC;
45extern StatBag S;
46
47/**
48\file tcpreceiver.cc
49\brief This file implements the tcpreceiver that receives and answers questions over TCP/IP
50*/
51
ac2bb9e7 52pthread_mutex_t TCPNameserver::s_plock = PTHREAD_MUTEX_INITIALIZER;
12c86877
BH
53Semaphore *TCPNameserver::d_connectionroom_sem;
54PacketHandler *TCPNameserver::s_P;
55int TCPNameserver::s_timeout;
9f1d5826 56NetmaskGroup TCPNameserver::d_ng;
12c86877 57
12c86877
BH
58int TCPNameserver::sendDelPacket(DNSPacket *p, int outsock)
59{
60 const char *buf=p->getData();
61 int res=sendData(buf, p->len, outsock);
62 delete p;
63 return res;
64}
65
12c86877
BH
66void TCPNameserver::go()
67{
68 L<<Logger::Error<<"Creating backend connection for TCP"<<endl;
69 s_P=0;
70 try {
71 s_P=new PacketHandler;
72 }
73 catch(AhuException &ae) {
74 L<<Logger::Error<<Logger::NTLog<<"TCP server is unable to launch backends - will try again when questions come in"<<endl;
fd8bc993 75 L<<Logger::Error<<"TCP server is unable to launch backends - will try again when questions come in: "<<ae.reason<<endl;
12c86877
BH
76 }
77 pthread_create(&d_tid, 0, launcher, static_cast<void *>(this));
78}
79
80void *TCPNameserver::launcher(void *data)
81{
82 static_cast<TCPNameserver *>(data)->thread();
83 return 0;
84}
85
86
87int TCPNameserver::readLength(int fd, struct sockaddr_in *remote)
88{
89 int bytesLeft=2;
90 unsigned char buf[2];
91
92 Utility::socklen_t remotelen=sizeof(*remote);
93 getpeername(fd, (struct sockaddr *)remote, &remotelen);
94
95 while(bytesLeft) {
96 int ret=waitForData(fd, s_timeout);
97 if(ret<0)
98 throw AhuException("Waiting on data from remote TCP client "+string(inet_ntoa(remote->sin_addr))+": "+stringerror());
99
100 ret=recv(fd, reinterpret_cast< char * >( buf ) +2-bytesLeft, bytesLeft,0);
101 if(ret<0)
102 throw AhuException("Trying to read data from remote TCP client "+string(inet_ntoa(remote->sin_addr))+": "+stringerror());
103 if(!ret) {
104 DLOG(L<<"Remote TCP client "+string(inet_ntoa(remote->sin_addr))+" closed connection");
105 return -1;
106 }
107 bytesLeft-=ret;
108 }
109 return buf[0]*256+buf[1];
110}
111
112void TCPNameserver::getQuestion(int fd, char *mesg, int pktlen, const struct sockaddr_in &remote)
113{
114 int ret=0, bytesread=0;
115 while(bytesread<pktlen) {
116 if((ret=waitForData(fd,s_timeout))<0 || (ret=recv(fd,mesg+bytesread,pktlen-bytesread,0))<=0)
117 goto err;
118
119 bytesread+=ret;
120 }
121 return;
122
123 err:;
124 if(ret<0)
125 throw AhuException("Error reading DNS data from TCP client "+string(inet_ntoa(remote.sin_addr))+": "+stringerror());
126 else
127 throw AhuException("Remote TCP client "+string(inet_ntoa(remote.sin_addr))+" closed connection");
128}
129
130void *TCPNameserver::doConnection(void *data)
131{
b014ad98
BH
132 // Fix gcc-4.0 error (on AMD64)
133 int fd=(int)(long)data; // gotta love C (generates a harmless warning on opteron)
12c86877
BH
134 pthread_detach(pthread_self());
135
136 try {
556252ea
BH
137 {
138 Lock l(&s_plock);
139 if(!s_P) {
140 L<<Logger::Error<<"TCP server is without backend connections, launching"<<endl;
141 s_P=new PacketHandler;
142 }
12c86877
BH
143 }
144 char mesg[512];
145
146 DLOG(L<<"TCP Connection accepted on fd "<<fd<<endl);
147
148 for(;;) {
149 struct sockaddr_in remote;
150
151 int pktlen=readLength(fd, &remote);
152 if(pktlen<0) // EOF
153 break;
154
155 if(pktlen>511) {
156 L<<Logger::Error<<"Received an overly large question from "<<inet_ntoa(remote.sin_addr)<<", dropping"<<endl;
157 break;
158 }
159
160 getQuestion(fd,mesg,pktlen,remote);
161 S.inc("tcp-queries");
162 DNSPacket *packet=new DNSPacket;
163
12c86877 164 packet->setRemote((struct sockaddr *)&remote,sizeof(remote));
e9dd48f9 165 packet->d_tcp=true;
c1663439
BH
166 if(packet->parse(mesg, pktlen)<0)
167 break;
168
12c86877
BH
169 if(packet->qtype.getCode()==QType::AXFR) {
170 if(doAXFR(packet->qdomain, packet, fd))
171 S.inc("tcp-answers");
172 continue;
173 }
174
dc45a198 175
12c86877
BH
176 if(packet->d.rd && arg().mustDo("recursor")) {
177 // now what
178 // this is a pretty rare event all in all, so we can afford to be slow
8d022964
BH
179
180 // this code SHOULD attempt to answer from the local cache first!
12c86877
BH
181 S.inc("recursing-questions");
182 Resolver res;
183 unsigned int len;
184 DLOG(L<<"About to hand query to recursor"<<endl);
185 ServiceTuple st;
186 st.port=53;
187 parseService(arg()["recursor"],st);
188
189 char *buffer=res.sendReceive(st.host,st.port,packet->getRaw(),packet->len,&len);
190 DLOG(L<<"got an answer from recursor: "<<len<<" bytes, "<<(int)buffer<<endl);
191 if(buffer) {
192 sendData(buffer,len,fd);
193 DLOG(L<<"sent out to customer: "<<len<<" bytes"<<endl);
194 delete buffer;
195 S.inc("recursing-answers");
196 S.inc("tcp-answers");
197 }
198 continue;
199 }
200
201 DNSPacket* cached=new DNSPacket;
202 if(!packet->d.rd && (PC.get(packet, cached))) { // short circuit - does the PacketCache recognize this question?
203 cached->setRemote((struct sockaddr *)(packet->remote), sizeof(struct sockaddr_in));
204 cached->spoofID(packet->d.id);
205 if(sendDelPacket(cached, fd)<0)
206 goto out;
207
208 S.inc("tcp-answers");
209 continue;
210 }
211 else
212 delete cached;
213
214 DNSPacket *reply;
215 {
216 Lock l(&s_plock);
217 reply=s_P->question(packet); // we really need to ask the backend :-)
218 }
219
220 delete packet;
221
222 if(!reply) // unable to write an answer?
223 break;
224
225 S.inc("tcp-answers");
226 sendDelPacket(reply, fd);
227 }
228
229 out:;
230 }
cc3afe25 231 catch(DBException &e) {
556252ea
BH
232 Lock l(&s_plock);
233 delete s_P;
234 s_P = 0;
235
236 L<<Logger::Error<<"TCP Connection Thread unable to answer a question because of a backend error, cycling"<<endl;
12c86877 237 }
ef1d2f44 238 catch(AhuException &ae) {
556252ea
BH
239 Lock l(&s_plock);
240 delete s_P;
241 s_P = 0; // on next call, backend will be recycled
242 L<<Logger::Error<<"TCP nameserver had error, cycling backend:"<<ae.reason<<endl;
ef1d2f44 243 }
12c86877
BH
244 catch(exception &e) {
245 L<<Logger::Error<<"TCP Connection Thread died because of STL error: "<<e.what()<<endl;
246 }
247 catch( ... )
248 {
249 L << Logger::Error << "TCP Connection Thread caught unknown exception." << endl;
250 }
251 Utility::closesocket(fd);
252 d_connectionroom_sem->post();
253
254 return 0;
255}
256
9f1d5826 257bool TCPNameserver::canDoAXFR(DNSPacket *q)
12c86877 258{
ae1b2e98 259 if(arg().mustDo("disable-axfr"))
318c3ec6
BH
260 return false;
261
9f1d5826 262 if( arg()["allow-axfr-ips"].empty() || d_ng.match( (struct sockaddr_in *) &q->remote ) )
12c86877
BH
263 return true;
264
12c86877
BH
265 extern CommunicatorClass Communicator;
266
267 if(Communicator.justNotified(q->qdomain, q->getRemote())) { // we just notified this ip
268 L<<Logger::Warning<<"Approved AXFR of '"<<q->qdomain<<"' from recently notified slave "<<q->getRemote()<<endl;
269 return true;
270 }
271
272 return false;
273}
274
275/** do the actual zone transfer. Return 0 in case of error, 1 in case of success */
276int TCPNameserver::doAXFR(const string &target, DNSPacket *q, int outsock)
277{
278 DNSPacket *outpacket=0;
279 if(!canDoAXFR(q)) {
20ca8e7d 280 L<<Logger::Error<<"AXFR of domain '"<<target<<"' denied to "<<q->getRemote()<<endl;
12c86877
BH
281
282 outpacket=q->replyPacket();
283 outpacket->setRcode(RCode::Refused);
284 // FIXME: should actually figure out if we are auth over a zone, and send out 9 if we aren't
285 sendDelPacket(outpacket,outsock);
286 return 0;
287 }
20ca8e7d 288 L<<Logger::Error<<"AXFR of domain '"<<target<<"' initiated by "<<q->getRemote()<<endl;
12c86877
BH
289 outpacket=q->replyPacket();
290
291 DNSResourceRecord soa;
292 DNSResourceRecord rr;
293
294 SOAData sd;
3de83124 295 sd.db=(DNSBackend *)-1; // force uncached answer
12c86877
BH
296 {
297 Lock l(&s_plock);
298
299 // find domain_id via SOA and list complete domain. No SOA, no AXFR
300
301 DLOG(L<<"Looking for SOA"<<endl);
302
303 if(!s_P->getBackend()->getSOA(target,sd)) {
d10f9034 304 L<<Logger::Error<<"AXFR of domain '"<<target<<"' failed: not authoritative"<<endl;
12c86877
BH
305 outpacket->setRcode(9); // 'NOTAUTH'
306 sendDelPacket(outpacket,outsock);
307 return 0;
308 }
7b308b7e 309
3de83124
BH
310 }
311 PacketHandler P; // now open up a database connection, we'll need it
7b308b7e 312
3de83124
BH
313 sd.db=(DNSBackend *)-1; // force uncached answer
314 if(!P.getBackend()->getSOA(target,sd)) {
d10f9034 315 L<<Logger::Error<<"AXFR of domain '"<<target<<"' failed: not authoritative in second instance"<<endl;
3de83124
BH
316 outpacket->setRcode(9); // 'NOTAUTH'
317 sendDelPacket(outpacket,outsock);
318 return 0;
319 }
7b308b7e 320
3de83124
BH
321 soa.qname=target;
322 soa.qtype=QType::SOA;
323 soa.content=DNSPacket::serializeSOAData(sd);
324 soa.ttl=sd.default_ttl;
325 soa.domain_id=sd.domain_id;
326 soa.d_place=DNSResourceRecord::ANSWER;
25b1d5d7 327
3de83124
BH
328 if(!sd.db || sd.db==(DNSBackend *)-1) {
329 L<<Logger::Error<<"Error determining backend for domain '"<<target<<"' trying to serve an AXFR"<<endl;
330 outpacket->setRcode(RCode::ServFail);
331 sendDelPacket(outpacket,outsock);
332 return 0;
12c86877
BH
333 }
334
335 DLOG(L<<"Issuing list command - opening dedicated database connection"<<endl);
3de83124 336
36d772ab 337 DNSBackend *B=sd.db; // get the RIGHT backend
12c86877
BH
338
339 // now list zone
88def049 340 if(!(B->list(target, sd.domain_id))) {
12c86877
BH
341 L<<Logger::Error<<"Backend signals error condition"<<endl;
342 outpacket->setRcode(2); // 'SERVFAIL'
343 sendDelPacket(outpacket,outsock);
344 return 0;
345 }
12c86877
BH
346 /* write first part of answer */
347
348 DLOG(L<<"Sending out SOA"<<endl);
349 outpacket->addRecord(soa); // AXFR format begins and ends with a SOA record, so we add one
350 sendDelPacket(outpacket, outsock);
351
352 /* now write all other records */
353
354 int count=0;
355 int chunk=100; // FIXME: this should probably be autosizing
356 if(arg().mustDo("strict-rfc-axfrs"))
357 chunk=1;
358
359 outpacket=q->replyPacket();
360 outpacket->setCompress(false);
361
362 while(B->get(rr)) {
363 if(rr.qtype.getCode()==6)
364 continue; // skip SOA - would indicate end of AXFR
365
366 outpacket->addRecord(rr);
367
368 if(!((++count)%chunk)) {
369 count=0;
370
371 if(sendDelPacket(outpacket, outsock)<0) // FIXME: this leaks memory!
372 return 0;
373
374 outpacket=q->replyPacket();
375 outpacket->setCompress(false);
376 // FIXME: Subsequent messages SHOULD NOT have a question section, though the final message MAY.
377 }
378 }
379 if(count) {
380 sendDelPacket(outpacket, outsock);
381 }
382
383 DLOG(L<<"Done writing out records"<<endl);
384 /* and terminate with yet again the SOA record */
385 outpacket=q->replyPacket();
386 outpacket->addRecord(soa);
387 sendDelPacket(outpacket, outsock);
388 DLOG(L<<"last packet - close"<<endl);
20ca8e7d 389 L<<Logger::Error<<"AXFR of domain '"<<target<<"' to "<<q->getRemote()<<" finished"<<endl;
12c86877
BH
390
391 return 1;
392}
393
394TCPNameserver::~TCPNameserver()
395{
396 delete d_connectionroom_sem;
397}
398
399TCPNameserver::TCPNameserver()
400{
401// sem_init(&d_connectionroom_sem,0,arg().asNum("max-tcp-connections"));
402 d_connectionroom_sem = new Semaphore( arg().asNum( "max-tcp-connections" ));
403
404 s_timeout=10;
405 vector<string>locals;
406 stringtok(locals,arg()["local-address"]," ,");
407
408 vector<string>locals6;
409 stringtok(locals6,arg()["local-ipv6"]," ,");
410
411
412 if(locals.empty() && locals6.empty())
413 throw AhuException("No local address specified");
414
415 d_highfd=0;
416
9f1d5826
BH
417 vector<string> parts;
418 stringtok( parts, arg()["allow-axfr-ips"], ", \t" ); // is this IP on the guestlist?
419 for( vector<string>::const_iterator i = parts.begin(); i != parts.end(); ++i ) {
420 d_ng.addMask( *i );
421 }
422
12c86877
BH
423#ifndef WIN32
424 signal(SIGPIPE,SIG_IGN);
425#endif // WIN32
426 FD_ZERO(&d_rfds);
427
428 for(vector<string>::const_iterator laddr=locals.begin();laddr!=locals.end();++laddr) {
429 struct sockaddr_in local;
430 int s=socket(AF_INET,SOCK_STREAM,0);
431
432 if(s<0)
433 throw AhuException("Unable to acquire TCP socket: "+stringerror());
434
435 memset(&local,0,sizeof(local));
436 local.sin_family=AF_INET;
437
438 struct hostent *h;
439
440 if ( *laddr == "0.0.0.0" )
441 {
442 local.sin_addr.s_addr = INADDR_ANY;
443 }
444 else
445 {
446 h=gethostbyname(laddr->c_str());
447
448 if(!h)
449 throw AhuException("Unable to resolve local address '"+*laddr+"'");
450
451 local.sin_addr.s_addr=*(int*)h->h_addr;
452 }
453
454 int tmp=1;
455 if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0) {
456 L<<Logger::Error<<"Setsockopt failed"<<endl;
457 exit(1);
458 }
459
460 local.sin_port=htons(arg().asNum("local-port"));
461
462 if(bind(s, (sockaddr*)&local,sizeof(local))<0) {
463 L<<Logger::Error<<"binding to TCP socket: "<<strerror(errno)<<endl;
464 throw AhuException("Unable to bind to TCP socket");
465 }
466
467 listen(s,128);
da44c9ea 468 L<<Logger::Error<<"TCP server bound to "<<*laddr<<":"<<arg().asNum("local-port")<<endl;
12c86877
BH
469 d_sockets.push_back(s);
470 FD_SET(s, &d_rfds);
471 d_highfd=max(s,d_highfd);
472 }
473
474 // TODO: Implement ipv6
1258abe0 475#if !WIN32 && HAVE_IPV6
12c86877 476 for(vector<string>::const_iterator laddr=locals6.begin();laddr!=locals6.end();++laddr) {
12c86877
BH
477 int s=socket(AF_INET6,SOCK_STREAM,0);
478
479 if(s<0)
480 throw AhuException("Unable to acquire TCPv6 socket: "+stringerror());
178d5134
BH
481
482 sockaddr_in6 locala;
483 locala.sin6_port=ntohs(arg().asNum("local-port"));
bdc9f8d2
BH
484 locala.sin6_family=AF_INET6;
485 locala.sin6_flowinfo=0;
486
178d5134
BH
487
488 if(!inet_pton(AF_INET6, laddr->c_str(), (void *)&locala.sin6_addr)) {
489 addrinfo *addrinfos;
490 addrinfo hints;
491 memset(&hints,0,sizeof(hints));
492 hints.ai_socktype=SOCK_STREAM;
493 hints.ai_family=AF_INET6;
12c86877 494
178d5134
BH
495 if(getaddrinfo(laddr->c_str(),arg()["local-port"].c_str(),&hints,&addrinfos))
496 throw AhuException("Unable to resolve local IPv6 address '"+*laddr+"'");
497
498 memcpy(&locala,addrinfos->ai_addr,addrinfos->ai_addrlen);
499 }
12c86877
BH
500
501 int tmp=1;
502 if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0) {
503 L<<Logger::Error<<"Setsockopt failed"<<endl;
504 exit(1);
505 }
506
bdc9f8d2 507
178d5134 508 if(bind(s, (const sockaddr*)&locala, sizeof(locala))<0) {
12c86877
BH
509 L<<Logger::Error<<"binding to TCP socket: "<<strerror(errno)<<endl;
510 throw AhuException("Unable to bind to TCPv6 socket");
511 }
512
513 listen(s,128);
bd852e59 514 L<<Logger::Error<<"TCPv6 server bound to ["<<*laddr<<"]:"<<arg()["local-port"]<<endl;
12c86877
BH
515 d_sockets.push_back(s);
516 FD_SET(s, &d_rfds);
517 d_highfd=max(s,d_highfd);
518 }
519#endif // WIN32
520}
521
522
523//! Start of TCP operations thread
524void TCPNameserver::thread()
525{
526 struct timeval tv;
527 tv.tv_sec=1;
528 tv.tv_usec=0;
529 try {
530 for(;;) {
531 int fd;
532 struct sockaddr_in remote;
533 Utility::socklen_t addrlen=sizeof(remote);
534
535 fd_set rfds=d_rfds;
536
8a63d3ce
BH
537 int ret=select(d_highfd+1, &rfds, 0, 0, 0); // blocks
538 if(ret <= 0)
539 continue;
540
12c86877
BH
541 int sock=-1;
542 for(vector<int>::const_iterator i=d_sockets.begin();i!=d_sockets.end();++i) {
543 if(FD_ISSET(*i, &rfds)) {
544 sock=*i;
545 addrlen=sizeof(remote);
546
547 if((fd=accept(sock, (sockaddr*)&remote, &addrlen))<0) {
548 L<<Logger::Error<<"TCP question accept error: "<<strerror(errno)<<endl;
549
550 if(errno==EMFILE) {
551 L<<Logger::Error<<Logger::NTLog<<"TCP handler out of filedescriptors, exiting, won't recover from this"<<endl;
552 exit(1);
553 }
554 }
555 else {
556 pthread_t tid;
557 d_connectionroom_sem->wait(); // blocks if no connections are available
558
559 int room;
560 d_connectionroom_sem->getValue( &room);
561 if(room<1)
562 L<<Logger::Warning<<Logger::NTLog<<"Limit of simultaneous TCP connections reached - raise max-tcp-connections"<<endl;
563
564 if(pthread_create(&tid, 0, &doConnection, (void *)fd)) {
565 L<<Logger::Error<<"Error creating thread: "<<stringerror()<<endl;
566 d_connectionroom_sem->post();
567 }
568 }
569 }
570 }
571 }
572 }
573 catch(AhuException &AE) {
574 L<<Logger::Error<<"TCP Namerserver thread dying because of fatal error: "<<AE.reason<<endl;
575 }
576 catch(...) {
577 L<<Logger::Error<<"TCPNameserver dying because of an unexpected fatal error"<<endl;
578 }
579 exit(1); // take rest of server with us
580}
581
582