2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2002 PowerDNS.COM BV
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
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.
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
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include <sys/types.h>
26 #include "tcpreceiver.hh"
31 #include "ueberbackend.hh"
32 #include "dnspacket.hh"
33 #include "nameserver.hh"
34 #include "distributor.hh"
37 #include "arguments.hh"
38 #include "packetcache.hh"
39 #include "packethandler.hh"
41 #include "resolver.hh"
42 #include "communicator.hh"
44 extern PacketCache PC
;
49 \brief This file implements the tcpreceiver that receives and answers questions over TCP/IP
52 pthread_mutex_t
TCPNameserver::s_plock
= PTHREAD_MUTEX_INITIALIZER
;
53 Semaphore
*TCPNameserver::d_connectionroom_sem
;
54 PacketHandler
*TCPNameserver::s_P
;
55 int TCPNameserver::s_timeout
;
58 int TCPNameserver::sendDelPacket(DNSPacket
*p
, int outsock
)
60 const char *buf
=p
->getData();
61 int res
=sendData(buf
, p
->len
, outsock
);
67 void TCPNameserver::go()
69 L
<<Logger::Error
<<"Creating backend connection for TCP"<<endl
;
72 s_P
=new PacketHandler
;
74 catch(AhuException
&ae
) {
75 L
<<Logger::Error
<<Logger::NTLog
<<"TCP server is unable to launch backends - will try again when questions come in"<<endl
;
77 pthread_create(&d_tid
, 0, launcher
, static_cast<void *>(this));
80 void *TCPNameserver::launcher(void *data
)
82 static_cast<TCPNameserver
*>(data
)->thread();
87 int TCPNameserver::readLength(int fd
, struct sockaddr_in
*remote
)
92 Utility::socklen_t remotelen
=sizeof(*remote
);
93 getpeername(fd
, (struct sockaddr
*)remote
, &remotelen
);
96 int ret
=waitForData(fd
, s_timeout
);
98 throw AhuException("Waiting on data from remote TCP client "+string(inet_ntoa(remote
->sin_addr
))+": "+stringerror());
100 ret
=recv(fd
, reinterpret_cast< char * >( buf
) +2-bytesLeft
, bytesLeft
,0);
102 throw AhuException("Trying to read data from remote TCP client "+string(inet_ntoa(remote
->sin_addr
))+": "+stringerror());
104 DLOG(L
<<"Remote TCP client "+string(inet_ntoa(remote
->sin_addr
))+" closed connection");
109 return buf
[0]*256+buf
[1];
112 void TCPNameserver::getQuestion(int fd
, char *mesg
, int pktlen
, const struct sockaddr_in
&remote
)
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)
125 throw AhuException("Error reading DNS data from TCP client "+string(inet_ntoa(remote
.sin_addr
))+": "+stringerror());
127 throw AhuException("Remote TCP client "+string(inet_ntoa(remote
.sin_addr
))+" closed connection");
130 void *TCPNameserver::doConnection(void *data
)
132 int fd
=(int)data
; // gotta love C
133 pthread_detach(pthread_self());
137 L
<<Logger::Error
<<"TCP server is without backend connections, launching"<<endl
;
138 s_P
=new PacketHandler
;
142 DLOG(L
<<"TCP Connection accepted on fd "<<fd
<<endl
);
145 struct sockaddr_in remote
;
147 int pktlen
=readLength(fd
, &remote
);
152 L
<<Logger::Error
<<"Received an overly large question from "<<inet_ntoa(remote
.sin_addr
)<<", dropping"<<endl
;
156 getQuestion(fd
,mesg
,pktlen
,remote
);
157 S
.inc("tcp-queries");
158 DNSPacket
*packet
=new DNSPacket
;
160 if(packet
->parse(mesg
, pktlen
)<0)
163 packet
->setRemote((struct sockaddr
*)&remote
,sizeof(remote
));
165 if(packet
->qtype
.getCode()==QType::AXFR
) {
166 if(doAXFR(packet
->qdomain
, packet
, fd
))
167 S
.inc("tcp-answers");
171 if(packet
->d
.rd
&& arg().mustDo("recursor")) {
173 // this is a pretty rare event all in all, so we can afford to be slow
174 S
.inc("recursing-questions");
177 DLOG(L
<<"About to hand query to recursor"<<endl
);
180 parseService(arg()["recursor"],st
);
182 char *buffer
=res
.sendReceive(st
.host
,st
.port
,packet
->getRaw(),packet
->len
,&len
);
183 DLOG(L
<<"got an answer from recursor: "<<len
<<" bytes, "<<(int)buffer
<<endl
);
185 sendData(buffer
,len
,fd
);
186 DLOG(L
<<"sent out to customer: "<<len
<<" bytes"<<endl
);
188 S
.inc("recursing-answers");
189 S
.inc("tcp-answers");
194 DNSPacket
* cached
=new DNSPacket
;
195 if(!packet
->d
.rd
&& (PC
.get(packet
, cached
))) { // short circuit - does the PacketCache recognize this question?
196 cached
->setRemote((struct sockaddr
*)(packet
->remote
), sizeof(struct sockaddr_in
));
197 cached
->spoofID(packet
->d
.id
);
198 if(sendDelPacket(cached
, fd
)<0)
201 S
.inc("tcp-answers");
210 reply
=s_P
->question(packet
); // we really need to ask the backend :-)
215 if(!reply
) // unable to write an answer?
218 S
.inc("tcp-answers");
219 sendDelPacket(reply
, fd
);
224 catch(DBException
&e
) {
225 L
<<Logger::Error
<<"TCP Connection Thread unable to answer a question because of a backend error"<<endl
;
227 catch(AhuException
&ae
) {
228 L
<<Logger::Error
<<"TCP nameserver: "<<ae
.reason
<<endl
;
230 catch(exception
&e
) {
231 L
<<Logger::Error
<<"TCP Connection Thread died because of STL error: "<<e
.what()<<endl
;
235 L
<< Logger::Error
<< "TCP Connection Thread caught unknown exception." << endl
;
237 Utility::closesocket(fd
);
238 d_connectionroom_sem
->post();
243 static bool canDoAXFR(DNSPacket
*q
)
245 if(!arg().mustDo("disable-axfr")) // default is 'everybody can do axfr'
249 stringtok(parts
,arg()["allow-axfr-ips"],", "); // is this IP on the guestlist?
250 for(vector
<string
>::const_iterator i
=parts
.begin();i
!=parts
.end();++i
) {
251 if(matchNetmask(q
->getRemote().c_str(),i
->c_str())==1)
255 extern CommunicatorClass Communicator
;
257 if(Communicator
.justNotified(q
->qdomain
, q
->getRemote())) { // we just notified this ip
258 L
<<Logger::Warning
<<"Approved AXFR of '"<<q
->qdomain
<<"' from recently notified slave "<<q
->getRemote()<<endl
;
265 /** do the actual zone transfer. Return 0 in case of error, 1 in case of success */
266 int TCPNameserver::doAXFR(const string
&target
, DNSPacket
*q
, int outsock
)
268 DNSPacket
*outpacket
=0;
270 L
<<Logger::Error
<<"AXFR of domain '"<<target
<<"' denied to "<<q
->getRemote()<<endl
;
272 outpacket
=q
->replyPacket();
273 outpacket
->setRcode(RCode::Refused
);
274 // FIXME: should actually figure out if we are auth over a zone, and send out 9 if we aren't
275 sendDelPacket(outpacket
,outsock
);
278 L
<<Logger::Error
<<"AXFR of domain '"<<target
<<"' initiated by "<<q
->getRemote()<<endl
;
279 outpacket
=q
->replyPacket();
281 DNSResourceRecord soa
;
282 DNSResourceRecord rr
;
288 // find domain_id via SOA and list complete domain. No SOA, no AXFR
290 DLOG(L
<<"Looking for SOA"<<endl
);
292 if(!s_P
->getBackend()->getSOA(target
,sd
)) {
293 outpacket
->setRcode(9); // 'NOTAUTH'
294 sendDelPacket(outpacket
,outsock
);
298 soa
.qtype
=QType::SOA
;
299 soa
.content
=DNSPacket::serializeSOAData(sd
);
300 soa
.ttl
=sd
.default_ttl
;
301 soa
.domain_id
=sd
.domain_id
;
302 soa
.d_place
=DNSResourceRecord::ANSWER
;
305 DLOG(L
<<"Issuing list command - opening dedicated database connection"<<endl
);
307 DNSBackend
*B
=P
.getBackend();
310 if(!(B
->list(sd
.domain_id
))) {
311 L
<<Logger::Error
<<"Backend signals error condition"<<endl
;
312 outpacket
->setRcode(2); // 'SERVFAIL'
313 sendDelPacket(outpacket
,outsock
);
317 /* write first part of answer */
319 DLOG(L
<<"Sending out SOA"<<endl
);
320 outpacket
->addRecord(soa
); // AXFR format begins and ends with a SOA record, so we add one
321 sendDelPacket(outpacket
, outsock
);
323 /* now write all other records */
326 int chunk
=100; // FIXME: this should probably be autosizing
327 if(arg().mustDo("strict-rfc-axfrs"))
330 outpacket
=q
->replyPacket();
331 outpacket
->setCompress(false);
334 if(rr
.qtype
.getCode()==6)
335 continue; // skip SOA - would indicate end of AXFR
337 outpacket
->addRecord(rr
);
339 if(!((++count
)%chunk
)) {
342 if(sendDelPacket(outpacket
, outsock
)<0) // FIXME: this leaks memory!
345 outpacket
=q
->replyPacket();
346 outpacket
->setCompress(false);
347 // FIXME: Subsequent messages SHOULD NOT have a question section, though the final message MAY.
351 sendDelPacket(outpacket
, outsock
);
354 DLOG(L
<<"Done writing out records"<<endl
);
355 /* and terminate with yet again the SOA record */
356 outpacket
=q
->replyPacket();
357 outpacket
->addRecord(soa
);
358 sendDelPacket(outpacket
, outsock
);
359 DLOG(L
<<"last packet - close"<<endl
);
360 L
<<Logger::Error
<<"AXFR of domain '"<<target
<<"' to "<<q
->getRemote()<<" finished"<<endl
;
365 TCPNameserver::~TCPNameserver()
367 delete d_connectionroom_sem
;
370 TCPNameserver::TCPNameserver()
372 // sem_init(&d_connectionroom_sem,0,arg().asNum("max-tcp-connections"));
373 d_connectionroom_sem
= new Semaphore( arg().asNum( "max-tcp-connections" ));
376 vector
<string
>locals
;
377 stringtok(locals
,arg()["local-address"]," ,");
379 vector
<string
>locals6
;
380 stringtok(locals6
,arg()["local-ipv6"]," ,");
383 if(locals
.empty() && locals6
.empty())
384 throw AhuException("No local address specified");
389 signal(SIGPIPE
,SIG_IGN
);
393 for(vector
<string
>::const_iterator laddr
=locals
.begin();laddr
!=locals
.end();++laddr
) {
394 struct sockaddr_in local
;
395 int s
=socket(AF_INET
,SOCK_STREAM
,0);
398 throw AhuException("Unable to acquire TCP socket: "+stringerror());
400 memset(&local
,0,sizeof(local
));
401 local
.sin_family
=AF_INET
;
405 if ( *laddr
== "0.0.0.0" )
407 local
.sin_addr
.s_addr
= INADDR_ANY
;
411 h
=gethostbyname(laddr
->c_str());
414 throw AhuException("Unable to resolve local address '"+*laddr
+"'");
416 local
.sin_addr
.s_addr
=*(int*)h
->h_addr
;
420 if(setsockopt(s
,SOL_SOCKET
,SO_REUSEADDR
,(char*)&tmp
,sizeof tmp
)<0) {
421 L
<<Logger::Error
<<"Setsockopt failed"<<endl
;
425 local
.sin_port
=htons(arg().asNum("local-port"));
427 if(bind(s
, (sockaddr
*)&local
,sizeof(local
))<0) {
428 L
<<Logger::Error
<<"binding to TCP socket: "<<strerror(errno
)<<endl
;
429 throw AhuException("Unable to bind to TCP socket");
433 L
<<Logger::Error
<<"TCP server bound to "<<*laddr
<<":"<<arg()["local-port"]<<endl
;
434 d_sockets
.push_back(s
);
436 d_highfd
=max(s
,d_highfd
);
439 // TODO: Implement ipv6
440 #if !WIN32 && HAVE_IPV6
441 for(vector
<string
>::const_iterator laddr
=locals6
.begin();laddr
!=locals6
.end();++laddr
) {
442 int s
=socket(AF_INET6
,SOCK_STREAM
,0);
445 throw AhuException("Unable to acquire TCPv6 socket: "+stringerror());
448 locala
.sin6_port
=ntohs(arg().asNum("local-port"));
449 locala
.sin6_family
=AF_INET6
;
450 locala
.sin6_flowinfo
=0;
453 if(!inet_pton(AF_INET6
, laddr
->c_str(), (void *)&locala
.sin6_addr
)) {
456 memset(&hints
,0,sizeof(hints
));
457 hints
.ai_socktype
=SOCK_STREAM
;
458 hints
.ai_family
=AF_INET6
;
460 if(getaddrinfo(laddr
->c_str(),arg()["local-port"].c_str(),&hints
,&addrinfos
))
461 throw AhuException("Unable to resolve local IPv6 address '"+*laddr
+"'");
463 memcpy(&locala
,addrinfos
->ai_addr
,addrinfos
->ai_addrlen
);
467 if(setsockopt(s
,SOL_SOCKET
,SO_REUSEADDR
,(char*)&tmp
,sizeof tmp
)<0) {
468 L
<<Logger::Error
<<"Setsockopt failed"<<endl
;
473 if(bind(s
, (const sockaddr
*)&locala
, sizeof(locala
))<0) {
474 L
<<Logger::Error
<<"binding to TCP socket: "<<strerror(errno
)<<endl
;
475 throw AhuException("Unable to bind to TCPv6 socket");
479 L
<<Logger::Error
<<"TCPv6 server bound to "<<*laddr
<<":"<<arg()["local-port"]<<endl
;
480 d_sockets
.push_back(s
);
482 d_highfd
=max(s
,d_highfd
);
488 //! Start of TCP operations thread
489 void TCPNameserver::thread()
497 struct sockaddr_in remote
;
498 Utility::socklen_t addrlen
=sizeof(remote
);
502 select(d_highfd
+1, &rfds
, 0, 0, 0); // blocks
504 for(vector
<int>::const_iterator i
=d_sockets
.begin();i
!=d_sockets
.end();++i
) {
505 if(FD_ISSET(*i
, &rfds
)) {
507 addrlen
=sizeof(remote
);
509 if((fd
=accept(sock
, (sockaddr
*)&remote
, &addrlen
))<0) {
510 L
<<Logger::Error
<<"TCP question accept error: "<<strerror(errno
)<<endl
;
513 L
<<Logger::Error
<<Logger::NTLog
<<"TCP handler out of filedescriptors, exiting, won't recover from this"<<endl
;
519 d_connectionroom_sem
->wait(); // blocks if no connections are available
522 d_connectionroom_sem
->getValue( &room
);
524 L
<<Logger::Warning
<<Logger::NTLog
<<"Limit of simultaneous TCP connections reached - raise max-tcp-connections"<<endl
;
526 if(pthread_create(&tid
, 0, &doConnection
, (void *)fd
)) {
527 L
<<Logger::Error
<<"Error creating thread: "<<stringerror()<<endl
;
528 d_connectionroom_sem
->post();
535 catch(AhuException
&AE
) {
536 L
<<Logger::Error
<<"TCP Namerserver thread dying because of fatal error: "<<AE
.reason
<<endl
;
539 L
<<Logger::Error
<<"TCPNameserver dying because of an unexpected fatal error"<<endl
;
541 exit(1); // take rest of server with us