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

Revision 2, 14.3 KB (checked in by ahu, 11 years ago)

Initial revision

  • 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  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
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
52pthread_mutex_t TCPNameserver::s_plock;
53Semaphore *TCPNameserver::d_connectionroom_sem;
54PacketHandler *TCPNameserver::s_P; 
55int TCPNameserver::s_timeout;
56
57
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
66
67void 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
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{
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(AhuException &ae) {
225    L<<Logger::Error<<"TCP nameserver: "<<ae.reason<<endl;
226  }
227  catch(PacketHandler::DBException &e) {
228    L<<Logger::Error<<"TCP Connection Thread unable to answer a question because of a backend error"<<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
243static 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 */
266int TCPNameserver::doAXFR(const string &target, DNSPacket *q, int outsock)
267{
268  DNSPacket *outpacket=0;
269  if(!canDoAXFR(q)) {
270    DLOG(L<<"AXFR denied for "<<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
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
361  return 1;
362}
363
364TCPNameserver::~TCPNameserver()
365{
366  delete d_connectionroom_sem;
367}
368
369TCPNameserver::TCPNameserver()
370{
371//  sem_init(&d_connectionroom_sem,0,arg().asNum("max-tcp-connections"));
372  d_connectionroom_sem = new Semaphore( arg().asNum( "max-tcp-connections" ));
373
374  s_timeout=10;
375  vector<string>locals;
376  stringtok(locals,arg()["local-address"]," ,");
377
378  vector<string>locals6;
379  stringtok(locals6,arg()["local-ipv6"]," ,");
380
381
382  if(locals.empty() && locals6.empty())
383    throw AhuException("No local address specified");
384
385  d_highfd=0;
386
387#ifndef WIN32
388  signal(SIGPIPE,SIG_IGN);
389#endif // WIN32
390  FD_ZERO(&d_rfds); 
391
392  for(vector<string>::const_iterator laddr=locals.begin();laddr!=locals.end();++laddr) {
393    struct sockaddr_in local;
394    int s=socket(AF_INET,SOCK_STREAM,0); 
395
396    if(s<0) 
397      throw AhuException("Unable to acquire TCP socket: "+stringerror());
398   
399    memset(&local,0,sizeof(local));
400    local.sin_family=AF_INET;
401
402    struct hostent *h;
403   
404    if ( *laddr == "0.0.0.0" )
405    {
406      local.sin_addr.s_addr = INADDR_ANY;
407    }
408    else 
409    {
410      h=gethostbyname(laddr->c_str());
411 
412      if(!h)
413        throw AhuException("Unable to resolve local address '"+*laddr+"'");
414
415      local.sin_addr.s_addr=*(int*)h->h_addr;
416    }
417     
418    int tmp=1;
419    if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0) {
420      L<<Logger::Error<<"Setsockopt failed"<<endl;
421      exit(1); 
422    }
423
424    local.sin_port=htons(arg().asNum("local-port"));
425   
426    if(bind(s, (sockaddr*)&local,sizeof(local))<0) {
427      L<<Logger::Error<<"binding to TCP socket: "<<strerror(errno)<<endl;
428      throw AhuException("Unable to bind to TCP socket");
429    }
430   
431    listen(s,128);
432    L<<Logger::Error<<"TCP server bound to "<<*laddr<<":"<<arg()["local-port"]<<endl;
433    d_sockets.push_back(s);
434    FD_SET(s, &d_rfds);
435    d_highfd=max(s,d_highfd);
436  }
437
438  // TODO: Implement ipv6
439#ifndef WIN32
440  for(vector<string>::const_iterator laddr=locals6.begin();laddr!=locals6.end();++laddr) {
441    struct sockaddr_in6 local;
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    memset(&local,0,sizeof(local));
448    local.sin6_family=AF_INET6;
449    struct hostent *h;
450    h=gethostbyname2(laddr->c_str(),AF_INET6);
451 
452    if(!h)
453      throw AhuException("Unable to resolve local address '"+*laddr+"'");
454     
455    memcpy(&local.sin6_addr.s6_addr,h->h_addr,16);
456    local.sin6_port=htons(arg().asNum("local-port"));
457
458    int tmp=1;
459    if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0) {
460      L<<Logger::Error<<"Setsockopt failed"<<endl;
461      exit(1); 
462    }
463
464    if(bind(s, (sockaddr*)&local,sizeof(local))<0) {
465      L<<Logger::Error<<"binding to TCP socket: "<<strerror(errno)<<endl;
466      throw AhuException("Unable to bind to TCPv6 socket");
467    }
468   
469    listen(s,128);
470    L<<Logger::Error<<"TCPv6 server bound to "<<*laddr<<":"<<arg()["local-port"]<<endl;
471    d_sockets.push_back(s);
472    FD_SET(s, &d_rfds);
473    d_highfd=max(s,d_highfd);
474  }
475#endif // WIN32
476}
477
478
479//! Start of TCP operations thread
480void TCPNameserver::thread()
481{
482  struct timeval tv;
483  tv.tv_sec=1;
484  tv.tv_usec=0;
485  try {
486    for(;;) {
487      int fd;
488      struct sockaddr_in remote;
489      Utility::socklen_t addrlen=sizeof(remote);
490
491      fd_set rfds=d_rfds; 
492
493      select(d_highfd+1, &rfds, 0, 0,  0); // blocks
494      int sock=-1;
495      for(vector<int>::const_iterator i=d_sockets.begin();i!=d_sockets.end();++i) {
496        if(FD_ISSET(*i, &rfds)) {
497          sock=*i;
498          addrlen=sizeof(remote);
499
500          if((fd=accept(sock, (sockaddr*)&remote, &addrlen))<0) {
501            L<<Logger::Error<<"TCP question accept error: "<<strerror(errno)<<endl;
502           
503            if(errno==EMFILE) {
504              L<<Logger::Error<<Logger::NTLog<<"TCP handler out of filedescriptors, exiting, won't recover from this"<<endl;
505              exit(1);
506            }
507          }
508          else {
509            pthread_t tid;
510            d_connectionroom_sem->wait(); // blocks if no connections are available
511
512            int room;
513            d_connectionroom_sem->getValue( &room);
514            if(room<1)
515              L<<Logger::Warning<<Logger::NTLog<<"Limit of simultaneous TCP connections reached - raise max-tcp-connections"<<endl;
516
517            if(pthread_create(&tid, 0, &doConnection, (void *)fd)) {
518              L<<Logger::Error<<"Error creating thread: "<<stringerror()<<endl;
519              d_connectionroom_sem->post();
520            }
521          }
522        }
523      }
524    }
525  }
526  catch(AhuException &AE) {
527    L<<Logger::Error<<"TCP Namerserver thread dying because of fatal error: "<<AE.reason<<endl;
528  }
529  catch(...) {
530    L<<Logger::Error<<"TCPNameserver dying because of an unexpected fatal error"<<endl;
531  }
532  exit(1); // take rest of server with us
533}
534
535
Note: See TracBrowser for help on using the browser.