root/trunk/pdns/pdns/nameserver.cc

Revision 3067, 12.8 KB (checked in by peter, 20 months ago)

only setCloseOnExec on valid sockets

  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1/*
2    Copyright (C) 2002 - 2012  PowerDNS.COM BV
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License version 2 as
6    published by the Free Software Foundation.
7
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
16*/
17
18#include "utility.hh"
19#include <cstdio>
20#include <cstring>
21#include <cstdlib>
22#include <cerrno>
23#include <iostream>
24#include <string>
25#include <sys/types.h>
26#include <boost/shared_ptr.hpp>
27#include "dns.hh"
28#include "dnsbackend.hh"
29#include "dnspacket.hh"
30#include "nameserver.hh"
31#include "distributor.hh"
32#include "logger.hh"
33#include "arguments.hh"
34#include "statbag.hh"
35
36#include "namespaces.hh"
37
38extern StatBag S;
39
40/** \mainpage
41    PowerDNS is a very versatile nameserver that can answer questions from different backends. To implement your
42    own backend, see the documentation for the DNSBackend class.
43
44    \section copyright Copyright and License
45    PowerDNS is (C) 2001-2008 PowerDNS.COM BV. It is distributed according to the terms of the General Public License version 2.
46
47    \section overview High level overview
48
49    The Distributor contains a configurable number of PacketHandler instances, each in its own thread, for connection pooling.
50    PacketHandler instances are recycled of they let escape an AhuException.
51
52    The PacketHandler implements the RFC1034 algorithm and converts question packets into DNSBackend queries.
53
54    A DNSBackend is an entity that returns DNSResourceRecord objects in return to explicit questions for domains with a specified QType
55
56    PowerDNS uses the UeberBackend as its DNSBackend. The UeberBackend by default has no DNSBackends within itself, those are loaded
57    using the pdns_control tool. This way DNSBackend implementations can be kept completely separate (but they often aren't).s
58
59    If one or more DNSBackends are loaded, the UeberBackend fields the queries to all of them until one answers.
60
61    \section TCP TCP Operations
62
63    The TCP operation runs within a single thread called tcpreceiver(), that also queries the PacketHandler.
64
65    \section Cache Caching
66 
67    On its own, this setup is not suitable for high performance operations. A single DNS query can turn into many DNSBackend questions,
68    each taking many milliseconds to complete. This is why the qthread() first checks the PacketCache to see if an answer is known to a packet
69    asking this question. If so, the entire Distributor is shunted, and the answer is sent back *directly*, within a few microseconds.
70
71    \section misc Miscellaneous
72    Configuration details are available via the ArgvMap instance arg. Statistics are created by making calls to the StatBag object called S.
73    These statistics are made available via the UeberBackend on the same socket that is used for dynamic module commands.
74
75    \section Main Main
76    The main() of PowerDNS can be found in receiver.cc - start reading there for further insights into the operation of the nameserver
77*/
78
79#ifdef IP_PKTINFO
80  #define GEN_IP_PKTINFO IP_PKTINFO
81#endif
82#ifdef IP_RECVDSTADDR
83  #define GEN_IP_PKTINFO IP_RECVDSTADDR
84#endif
85
86
87void UDPNameserver::bindIPv4()
88{
89  vector<string>locals;
90  stringtok(locals,::arg()["local-address"]," ,");
91
92  if(locals.empty())
93    throw AhuException("No local address specified");
94
95  int s;
96  for(vector<string>::const_iterator i=locals.begin();i!=locals.end();++i) {
97    string localname(*i);
98    struct sockaddr_in locala;
99
100    s=socket(AF_INET,SOCK_DGRAM,0);
101
102    if(s<0)
103      throw AhuException("Unable to acquire a UDP socket: "+string(strerror(errno)));
104 
105    Utility::setCloseOnExec(s);
106 
107    if(locals.size() > 1 && !Utility::setNonBlocking(s))
108      throw AhuException("Unable to set UDP socket to non-blocking: "+stringerror());
109 
110    memset(&locala,0,sizeof(locala));
111    locala.sin_family=AF_INET;
112
113    if(localname=="0.0.0.0") {
114      int val=1;
115      setsockopt(s, IPPROTO_IP, GEN_IP_PKTINFO, &val, sizeof(val));
116      locala.sin_addr.s_addr = INADDR_ANY;
117    }
118    else
119    {
120      struct hostent *h=0;
121      h=gethostbyname(localname.c_str());
122      if(!h)
123        throw AhuException("Unable to resolve local address"); 
124
125      locala.sin_addr.s_addr=*(int*)h->h_addr;
126    }
127
128    locala.sin_port=htons(::arg().asNum("local-port"));
129   
130    if(::bind(s, (sockaddr*)&locala,sizeof(locala))<0) {
131      L<<Logger::Error<<"binding UDP socket to '"+localname+"' port "+lexical_cast<string>(ntohs(locala.sin_port))+": "<<strerror(errno)<<endl;
132      throw AhuException("Unable to bind to UDP socket");
133    }
134    d_sockets.push_back(s);
135    L<<Logger::Error<<"UDP server bound to "<<inet_ntoa(locala.sin_addr)<<":"<<::arg().asNum("local-port")<<endl;
136    struct pollfd pfd;
137    pfd.fd = s;
138    pfd.events = POLL_IN;
139    pfd.revents = 0;
140    d_rfds.push_back(pfd);
141  }
142}
143
144static bool IsAnyAddress(const ComboAddress& addr)
145{
146  if(addr.sin4.sin_family == AF_INET)
147    return addr.sin4.sin_addr.s_addr == 0;
148  else if(addr.sin4.sin_family == AF_INET6)
149    return !memcmp(&addr.sin6.sin6_addr, &in6addr_any, sizeof(addr.sin6.sin6_addr));
150 
151  return false;
152}
153
154void UDPNameserver::bindIPv6()
155{
156#if !WIN32 && HAVE_IPV6
157  vector<string> locals;
158  stringtok(locals,::arg()["local-ipv6"]," ,");
159
160  if(locals.empty())
161    return;
162
163  int s;
164  for(vector<string>::const_iterator i=locals.begin();i!=locals.end();++i) {
165    string localname(*i);
166
167    s=socket(AF_INET6,SOCK_DGRAM,0);
168    if(s<0)
169      throw AhuException("Unable to acquire a UDPv6 socket: "+string(strerror(errno)));
170
171    Utility::setCloseOnExec(s);
172
173    ComboAddress locala(localname, ::arg().asNum("local-port"));
174   
175    if(IsAnyAddress(locala)) {
176      int val=1;
177      setsockopt(s, IPPROTO_IP, GEN_IP_PKTINFO, &val, sizeof(val));     // linux supports this, so why not - might fail on other systems
178      setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val)); 
179      setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));      // if this fails, we report an error in tcpreceiver too
180    }
181   
182    if(::bind(s, (sockaddr*)&locala, sizeof(locala))<0) {
183      L<<Logger::Error<<"binding to UDP ipv6 socket: "<<strerror(errno)<<endl;
184      throw AhuException("Unable to bind to UDP ipv6 socket");
185    }
186    d_sockets.push_back(s);
187    struct pollfd pfd;
188    pfd.fd = s;
189    pfd.events = POLL_IN;
190    pfd.revents = 0;
191    d_rfds.push_back(pfd);
192    L<<Logger::Error<<"UDPv6 server bound to "<<locala.toStringWithPort()<<endl;
193   
194  }
195#endif // WIN32
196}
197
198UDPNameserver::UDPNameserver()
199{
200  if(!::arg()["local-address"].empty())
201    bindIPv4();
202  if(!::arg()["local-ipv6"].empty())
203    bindIPv6();
204
205  if(::arg()["local-address"].empty() && ::arg()["local-ipv6"].empty()) 
206    L<<Logger::Critical<<"PDNS is deaf and mute! Not listening on any interfaces"<<endl;   
207}
208void UDPNameserver::send(DNSPacket *p)
209{
210  const string& buffer=p->getString();
211 
212  struct msghdr msgh;
213  struct cmsghdr *cmsg;
214  struct iovec iov;
215  char cbuf[256];
216 
217  /* Set up iov and msgh structures. */
218  memset(&msgh, 0, sizeof(struct msghdr));
219  iov.iov_base = (void*)buffer.c_str();
220  iov.iov_len = buffer.length();
221  msgh.msg_iov = &iov;
222  msgh.msg_iovlen = 1;
223  msgh.msg_name = (struct sockaddr*)&p->d_remote;
224  msgh.msg_namelen = p->d_remote.getSocklen();
225
226  if(p->d_anyLocal) {
227    if(p->d_anyLocal->sin4.sin_family == AF_INET6) {
228      struct in6_pktinfo *pkt;
229         
230      msgh.msg_control = cbuf;
231      msgh.msg_controllen = CMSG_SPACE(sizeof(*pkt));
232                 
233      cmsg = CMSG_FIRSTHDR(&msgh);
234      cmsg->cmsg_level = IPPROTO_IPV6;
235      cmsg->cmsg_type = IPV6_PKTINFO;
236      cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
237                                 
238      pkt = (struct in6_pktinfo *) CMSG_DATA(cmsg);
239      memset(pkt, 0, sizeof(*pkt));
240      pkt->ipi6_addr = p->d_anyLocal->sin6.sin6_addr;
241      msgh.msg_controllen = cmsg->cmsg_len; // makes valgrind happy and is slightly better style
242    }
243    else {
244#ifdef IP_PKTINFO
245      struct in_pktinfo *pkt;
246      msgh.msg_control = cbuf;
247      msgh.msg_controllen = CMSG_SPACE(sizeof(*pkt));
248
249      cmsg = CMSG_FIRSTHDR(&msgh);
250      cmsg->cmsg_level = IPPROTO_IP;
251      cmsg->cmsg_type = IP_PKTINFO;
252      cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
253
254      pkt = (struct in_pktinfo *) CMSG_DATA(cmsg);
255      memset(pkt, 0, sizeof(*pkt));
256      pkt->ipi_spec_dst = p->d_anyLocal->sin4.sin_addr;
257#endif
258#ifdef IP_SENDSRCADDR
259      struct in_addr *in;
260   
261      msgh.msg_control = cbuf;
262      msgh.msg_controllen = CMSG_SPACE(sizeof(*in));
263           
264      cmsg = CMSG_FIRSTHDR(&msgh);
265      cmsg->cmsg_level = IPPROTO_IP;
266      cmsg->cmsg_type = IP_SENDSRCADDR;
267      cmsg->cmsg_len = CMSG_LEN(sizeof(*in));
268                           
269      in = (struct in_addr *) CMSG_DATA(cmsg);
270      *in = p->d_anyLocal->sin4.sin_addr;
271#endif
272      msgh.msg_controllen = cmsg->cmsg_len;
273    }
274  }
275  DLOG(L<<Logger::Notice<<"Sending a packet to "<< p->getRemote() <<" ("<< buffer.length()<<" octets)"<<endl);
276  if(buffer.length() > p->getMaxReplyLen()) {
277    cerr<<"Weird, trying to send a message that needs truncation, "<< buffer.length()<<" > "<<p->getMaxReplyLen()<<endl;
278  }
279  if(sendmsg(p->getSocket(), &msgh, 0) < 0)
280    L<<Logger::Error<<"Error sending reply with sendto (socket="<<p->getSocket()<<"): "<<strerror(errno)<<endl;
281}
282
283static bool HarvestDestinationAddress(struct msghdr* msgh, ComboAddress* destination)
284{
285  memset(destination, 0, sizeof(*destination));
286  struct cmsghdr *cmsg;
287  for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(msgh,cmsg)) {
288#ifdef IP_PKTINFO
289     if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
290        struct in_pktinfo *i = (struct in_pktinfo *) CMSG_DATA(cmsg);
291        destination->sin4.sin_addr = i->ipi_addr;
292        destination->sin4.sin_family = AF_INET;
293        return true;
294    }
295#endif
296#ifdef IP_RECVDSTADDR
297    if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_RECVDSTADDR)) {
298      struct in_addr *i = (struct in_addr *) CMSG_DATA(cmsg);
299      destination->sin4.sin_addr = *i;
300      destination->sin4.sin_family = AF_INET;     
301      return true;
302    }
303#endif
304
305    if ((cmsg->cmsg_level == IPPROTO_IPV6) && (cmsg->cmsg_type == IPV6_PKTINFO)) {
306        struct in6_pktinfo *i = (struct in6_pktinfo *) CMSG_DATA(cmsg);
307        destination->sin6.sin6_addr = i->ipi6_addr;
308        destination->sin4.sin_family = AF_INET6;
309        return true;
310    }
311  }
312  return false;
313}
314
315DNSPacket *UDPNameserver::receive(DNSPacket *prefilled)
316{
317  ComboAddress remote;
318  extern StatBag S;
319  int len=-1;
320  char mesg[512];
321  Utility::sock_t sock=-1;
322   
323  struct msghdr msgh;
324  struct iovec iov;
325  char cbuf[256];
326
327  iov.iov_base = mesg;
328  iov.iov_len  = sizeof(mesg);
329
330  memset(&msgh, 0, sizeof(struct msghdr));
331 
332  msgh.msg_control = cbuf;
333  msgh.msg_controllen = sizeof(cbuf);
334  msgh.msg_name = &remote;
335  msgh.msg_namelen = sizeof(remote);
336  msgh.msg_iov  = &iov;
337  msgh.msg_iovlen = 1;
338  msgh.msg_flags = 0;
339 
340  int err;
341  vector<struct pollfd> rfds= d_rfds;
342  if(d_sockets.size()>1) {
343    BOOST_FOREACH(struct pollfd &pfd, rfds) {
344      pfd.events = POLL_IN;
345      pfd.revents = 0;
346    }
347   
348    err = poll(&rfds[0], rfds.size(), -1);
349    if(err < 0)
350      unixDie("Unable to poll for new UDP events");
351   
352    BOOST_FOREACH(struct pollfd &pfd, rfds) {
353      if(pfd.revents & POLL_IN) {
354        sock=pfd.fd;       
355        len=0;
356       
357        if((len=recvmsg(sock, &msgh, 0)) < 0 ) {
358           if(errno != EAGAIN)
359            L<<Logger::Error<<"recvfrom gave error, ignoring: "<<strerror(errno)<<endl;
360          return 0;
361        }
362        break;
363      }
364    }
365    if(sock==-1)
366      throw AhuException("poll betrayed us! (should not happen)");
367  }
368  else {
369    sock=d_sockets[0];
370
371    if((len=recvmsg(sock, &msgh, 0)) < 0 ) {
372      if(errno != EAGAIN)
373        L<<Logger::Error<<"recvfrom gave error, ignoring: "<<strerror(errno)<<endl;
374      return 0;
375    }
376  }
377 
378 
379  DLOG(L<<"Received a packet " << len <<" bytes long from "<< remote.toString()<<endl);
380 
381  DNSPacket *packet;
382  if(prefilled)  // they gave us a preallocated packet
383    packet=prefilled;
384  else
385    packet=new DNSPacket; // don't forget to free it!
386  packet->d_dt.set(); // timing
387  packet->setSocket(sock);
388  packet->setRemote(&remote);
389
390  ComboAddress dest;
391  if(HarvestDestinationAddress(&msgh, &dest)) {
392//    cerr<<"Setting d_anyLocal to '"<<dest.toString()<<"'"<<endl;
393    packet->d_anyLocal = dest;
394  }       
395
396
397  if(packet->parse(mesg, len)<0) {
398    S.inc("corrupt-packets");
399    S.ringAccount("remotes-corrupt", packet->getRemote());
400
401    if(!prefilled)
402      delete packet;
403    return 0; // unable to parse
404  }
405 
406  return packet;
407}
Note: See TracBrowser for help on using the browser.