root/trunk/pdns/pdns/tcpreceiver.cc @ 902

Revision 902, 16.1 KB (checked in by ahu, 7 years ago)

Christian Kuehn <christian.kuehn at mcs.de> discovered we send out the wrong TTL for SOA records during an AXFR, we previously used the 'default ttl'.

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