]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/tcpreceiver.cc
removed some dead code
[thirdparty/pdns.git] / pdns / tcpreceiver.cc
1 /*
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2002 PowerDNS.COM BV
4
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.
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
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19 #include "utility.hh"
20 #include <cstdio>
21 #include <cstring>
22 #include <cstdlib>
23 #include <sys/types.h>
24 #include <iostream>
25 #include <string>
26 #include "tcpreceiver.hh"
27
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
44 extern PacketCache PC;
45 extern 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
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;
56
57
58 int 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
66
67 void TCPNameserver::go()
68 {
69 L<<Logger::Error<<"Creating backend connection for TCP"<<endl;
70 s_P=0;
71 try {
72 s_P=new PacketHandler;
73 }
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;
76 }
77 pthread_create(&d_tid, 0, launcher, static_cast<void *>(this));
78 }
79
80 void *TCPNameserver::launcher(void *data)
81 {
82 static_cast<TCPNameserver *>(data)->thread();
83 return 0;
84 }
85
86
87 int 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
112 void 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
130 void *TCPNameserver::doConnection(void *data)
131 {
132 int fd=(int)data; // gotta love C
133 pthread_detach(pthread_self());
134
135 try {
136 if(!s_P) {
137 L<<Logger::Error<<"TCP server is without backend connections, launching"<<endl;
138 s_P=new PacketHandler;
139 }
140 char mesg[512];
141
142 DLOG(L<<"TCP Connection accepted on fd "<<fd<<endl);
143
144 for(;;) {
145 struct sockaddr_in remote;
146
147 int pktlen=readLength(fd, &remote);
148 if(pktlen<0) // EOF
149 break;
150
151 if(pktlen>511) {
152 L<<Logger::Error<<"Received an overly large question from "<<inet_ntoa(remote.sin_addr)<<", dropping"<<endl;
153 break;
154 }
155
156 getQuestion(fd,mesg,pktlen,remote);
157 S.inc("tcp-queries");
158 DNSPacket *packet=new DNSPacket;
159
160 if(packet->parse(mesg, pktlen)<0)
161 break;
162
163 packet->setRemote((struct sockaddr *)&remote,sizeof(remote));
164
165 if(packet->qtype.getCode()==QType::AXFR) {
166 if(doAXFR(packet->qdomain, packet, fd))
167 S.inc("tcp-answers");
168 continue;
169 }
170
171 if(packet->d.rd && arg().mustDo("recursor")) {
172 // now what
173 // this is a pretty rare event all in all, so we can afford to be slow
174 S.inc("recursing-questions");
175 Resolver res;
176 unsigned int len;
177 DLOG(L<<"About to hand query to recursor"<<endl);
178 ServiceTuple st;
179 st.port=53;
180 parseService(arg()["recursor"],st);
181
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);
184 if(buffer) {
185 sendData(buffer,len,fd);
186 DLOG(L<<"sent out to customer: "<<len<<" bytes"<<endl);
187 delete buffer;
188 S.inc("recursing-answers");
189 S.inc("tcp-answers");
190 }
191 continue;
192 }
193
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)
199 goto out;
200
201 S.inc("tcp-answers");
202 continue;
203 }
204 else
205 delete cached;
206
207 DNSPacket *reply;
208 {
209 Lock l(&s_plock);
210 reply=s_P->question(packet); // we really need to ask the backend :-)
211 }
212
213 delete packet;
214
215 if(!reply) // unable to write an answer?
216 break;
217
218 S.inc("tcp-answers");
219 sendDelPacket(reply, fd);
220 }
221
222 out:;
223 }
224 catch(DBException &e) {
225 L<<Logger::Error<<"TCP Connection Thread unable to answer a question because of a backend error"<<endl;
226 }
227 catch(AhuException &ae) {
228 L<<Logger::Error<<"TCP nameserver: "<<ae.reason<<endl;
229 }
230 catch(exception &e) {
231 L<<Logger::Error<<"TCP Connection Thread died because of STL error: "<<e.what()<<endl;
232 }
233 catch( ... )
234 {
235 L << Logger::Error << "TCP Connection Thread caught unknown exception." << endl;
236 }
237 Utility::closesocket(fd);
238 d_connectionroom_sem->post();
239
240 return 0;
241 }
242
243 static bool canDoAXFR(DNSPacket *q)
244 {
245 if(!arg().mustDo("disable-axfr")) // default is 'everybody can do axfr'
246 return true;
247
248 vector<string>parts;
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)
252 return true;
253 }
254
255 extern CommunicatorClass Communicator;
256
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;
259 return true;
260 }
261
262 return false;
263 }
264
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)
267 {
268 DNSPacket *outpacket=0;
269 if(!canDoAXFR(q)) {
270 L<<Logger::Error<<"AXFR of domain '"<<target<<"' denied to "<<q->getRemote()<<endl;
271
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);
276 return 0;
277 }
278 L<<Logger::Error<<"AXFR of domain '"<<target<<"' initiated by "<<q->getRemote()<<endl;
279 outpacket=q->replyPacket();
280
281 DNSResourceRecord soa;
282 DNSResourceRecord rr;
283
284 SOAData sd;
285 {
286 Lock l(&s_plock);
287
288 // find domain_id via SOA and list complete domain. No SOA, no AXFR
289
290 DLOG(L<<"Looking for SOA"<<endl);
291
292 if(!s_P->getBackend()->getSOA(target,sd)) {
293 outpacket->setRcode(9); // 'NOTAUTH'
294 sendDelPacket(outpacket,outsock);
295 return 0;
296 }
297 soa.qname=target;
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;
303 }
304
305 DLOG(L<<"Issuing list command - opening dedicated database connection"<<endl);
306 PacketHandler P;
307 DNSBackend *B=P.getBackend();
308
309 // now list zone
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);
314 return 0;
315 }
316
317 /* write first part of answer */
318
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);
322
323 /* now write all other records */
324
325 int count=0;
326 int chunk=100; // FIXME: this should probably be autosizing
327 if(arg().mustDo("strict-rfc-axfrs"))
328 chunk=1;
329
330 outpacket=q->replyPacket();
331 outpacket->setCompress(false);
332
333 while(B->get(rr)) {
334 if(rr.qtype.getCode()==6)
335 continue; // skip SOA - would indicate end of AXFR
336
337 outpacket->addRecord(rr);
338
339 if(!((++count)%chunk)) {
340 count=0;
341
342 if(sendDelPacket(outpacket, outsock)<0) // FIXME: this leaks memory!
343 return 0;
344
345 outpacket=q->replyPacket();
346 outpacket->setCompress(false);
347 // FIXME: Subsequent messages SHOULD NOT have a question section, though the final message MAY.
348 }
349 }
350 if(count) {
351 sendDelPacket(outpacket, outsock);
352 }
353
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;
361
362 return 1;
363 }
364
365 TCPNameserver::~TCPNameserver()
366 {
367 delete d_connectionroom_sem;
368 }
369
370 TCPNameserver::TCPNameserver()
371 {
372 // sem_init(&d_connectionroom_sem,0,arg().asNum("max-tcp-connections"));
373 d_connectionroom_sem = new Semaphore( arg().asNum( "max-tcp-connections" ));
374
375 s_timeout=10;
376 vector<string>locals;
377 stringtok(locals,arg()["local-address"]," ,");
378
379 vector<string>locals6;
380 stringtok(locals6,arg()["local-ipv6"]," ,");
381
382
383 if(locals.empty() && locals6.empty())
384 throw AhuException("No local address specified");
385
386 d_highfd=0;
387
388 #ifndef WIN32
389 signal(SIGPIPE,SIG_IGN);
390 #endif // WIN32
391 FD_ZERO(&d_rfds);
392
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);
396
397 if(s<0)
398 throw AhuException("Unable to acquire TCP socket: "+stringerror());
399
400 memset(&local,0,sizeof(local));
401 local.sin_family=AF_INET;
402
403 struct hostent *h;
404
405 if ( *laddr == "0.0.0.0" )
406 {
407 local.sin_addr.s_addr = INADDR_ANY;
408 }
409 else
410 {
411 h=gethostbyname(laddr->c_str());
412
413 if(!h)
414 throw AhuException("Unable to resolve local address '"+*laddr+"'");
415
416 local.sin_addr.s_addr=*(int*)h->h_addr;
417 }
418
419 int tmp=1;
420 if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0) {
421 L<<Logger::Error<<"Setsockopt failed"<<endl;
422 exit(1);
423 }
424
425 local.sin_port=htons(arg().asNum("local-port"));
426
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");
430 }
431
432 listen(s,128);
433 L<<Logger::Error<<"TCP server bound to "<<*laddr<<":"<<arg()["local-port"]<<endl;
434 d_sockets.push_back(s);
435 FD_SET(s, &d_rfds);
436 d_highfd=max(s,d_highfd);
437 }
438
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);
443
444 if(s<0)
445 throw AhuException("Unable to acquire TCPv6 socket: "+stringerror());
446
447 sockaddr_in6 locala;
448 locala.sin6_port=ntohs(arg().asNum("local-port"));
449 locala.sin6_family=AF_INET6;
450 locala.sin6_flowinfo=0;
451
452
453 if(!inet_pton(AF_INET6, laddr->c_str(), (void *)&locala.sin6_addr)) {
454 addrinfo *addrinfos;
455 addrinfo hints;
456 memset(&hints,0,sizeof(hints));
457 hints.ai_socktype=SOCK_STREAM;
458 hints.ai_family=AF_INET6;
459
460 if(getaddrinfo(laddr->c_str(),arg()["local-port"].c_str(),&hints,&addrinfos))
461 throw AhuException("Unable to resolve local IPv6 address '"+*laddr+"'");
462
463 memcpy(&locala,addrinfos->ai_addr,addrinfos->ai_addrlen);
464 }
465
466 int tmp=1;
467 if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0) {
468 L<<Logger::Error<<"Setsockopt failed"<<endl;
469 exit(1);
470 }
471
472
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");
476 }
477
478 listen(s,128);
479 L<<Logger::Error<<"TCPv6 server bound to "<<*laddr<<":"<<arg()["local-port"]<<endl;
480 d_sockets.push_back(s);
481 FD_SET(s, &d_rfds);
482 d_highfd=max(s,d_highfd);
483 }
484 #endif // WIN32
485 }
486
487
488 //! Start of TCP operations thread
489 void TCPNameserver::thread()
490 {
491 struct timeval tv;
492 tv.tv_sec=1;
493 tv.tv_usec=0;
494 try {
495 for(;;) {
496 int fd;
497 struct sockaddr_in remote;
498 Utility::socklen_t addrlen=sizeof(remote);
499
500 fd_set rfds=d_rfds;
501
502 select(d_highfd+1, &rfds, 0, 0, 0); // blocks
503 int sock=-1;
504 for(vector<int>::const_iterator i=d_sockets.begin();i!=d_sockets.end();++i) {
505 if(FD_ISSET(*i, &rfds)) {
506 sock=*i;
507 addrlen=sizeof(remote);
508
509 if((fd=accept(sock, (sockaddr*)&remote, &addrlen))<0) {
510 L<<Logger::Error<<"TCP question accept error: "<<strerror(errno)<<endl;
511
512 if(errno==EMFILE) {
513 L<<Logger::Error<<Logger::NTLog<<"TCP handler out of filedescriptors, exiting, won't recover from this"<<endl;
514 exit(1);
515 }
516 }
517 else {
518 pthread_t tid;
519 d_connectionroom_sem->wait(); // blocks if no connections are available
520
521 int room;
522 d_connectionroom_sem->getValue( &room);
523 if(room<1)
524 L<<Logger::Warning<<Logger::NTLog<<"Limit of simultaneous TCP connections reached - raise max-tcp-connections"<<endl;
525
526 if(pthread_create(&tid, 0, &doConnection, (void *)fd)) {
527 L<<Logger::Error<<"Error creating thread: "<<stringerror()<<endl;
528 d_connectionroom_sem->post();
529 }
530 }
531 }
532 }
533 }
534 }
535 catch(AhuException &AE) {
536 L<<Logger::Error<<"TCP Namerserver thread dying because of fatal error: "<<AE.reason<<endl;
537 }
538 catch(...) {
539 L<<Logger::Error<<"TCPNameserver dying because of an unexpected fatal error"<<endl;
540 }
541 exit(1); // take rest of server with us
542 }
543
544