"Fossies" - the Fresh Open Source Software Archive

Member "pdns-auth-4.2.0/pdns/nameserver.cc" (27 Aug 2019, 13178 Bytes) of package /linux/misc/dns/pdns-auth-4.2.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "nameserver.cc" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 4.1.13_vs_4.2.0.

    1 /*
    2  * This file is part of PowerDNS or dnsdist.
    3  * Copyright -- PowerDNS.COM B.V. and its contributors
    4  *
    5  * This program is free software; you can redistribute it and/or modify
    6  * it under the terms of version 2 of the GNU General Public License as
    7  * published by the Free Software Foundation.
    8  *
    9  * In addition, for the avoidance of any doubt, permission is granted to
   10  * link this program with OpenSSL and to (re)distribute the binaries
   11  * produced as the result of such linking.
   12  *
   13  * This program is distributed in the hope that it will be useful,
   14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16  * GNU General Public License for more details.
   17  *
   18  * You should have received a copy of the GNU General Public License
   19  * along with this program; if not, write to the Free Software
   20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   21  */
   22 #ifdef HAVE_CONFIG_H
   23 #include "config.h"
   24 #endif
   25 #include "utility.hh"
   26 #include <cstdio>
   27 #include <cstring>
   28 #include <cstdlib>
   29 #include <cerrno>
   30 #include <iostream>
   31 #include <string>
   32 #include <sys/types.h>
   33 #include "responsestats.hh"
   34 
   35 #include "dns.hh"
   36 #include "dnsbackend.hh"
   37 #include "dnspacket.hh"
   38 #include "nameserver.hh"
   39 #include "distributor.hh"
   40 #include "logger.hh"
   41 #include "arguments.hh"
   42 #include "statbag.hh"
   43 
   44 #include "namespaces.hh"
   45 
   46 extern StatBag S;
   47 
   48 /** \mainpage 
   49     PowerDNS is a very versatile nameserver that can answer questions from different backends. To implement your
   50     own backend, see the documentation for the DNSBackend class.
   51 
   52     \section copyright Copyright and License
   53     PowerDNS is (C) 2001-2008 PowerDNS.COM BV. It is distributed according to the terms of the General Public License version 2.
   54 
   55     \section overview High level overview
   56 
   57     The Distributor contains a configurable number of PacketHandler instances, each in its own thread, for connection pooling. 
   58     PacketHandler instances are recycled of they let escape an PDNSException.
   59 
   60     The PacketHandler implements the RFC1034 algorithm and converts question packets into DNSBackend queries.
   61 
   62     A DNSBackend is an entity that returns DNSResourceRecord objects in return to explicit questions for domains with a specified QType
   63 
   64     PowerDNS uses the UeberBackend, which hosts DNSBackends. By default it has no DNSBackends within itself, those are loaded
   65     by setting --load=<list of backends>. This way DNSBackend implementations can be kept completely separate, but most aren't.
   66 
   67     If one or more DNSBackends are loaded, the UeberBackend fields the queries to all of them until one answers.
   68 
   69     \section TCP TCP Operations
   70 
   71     The TCP operation runs within a single thread called tcpreceiver(), that also queries the PacketHandler. 
   72 
   73     \section Cache Caching
   74  
   75     On its own, this setup is not suitable for high performance operations. A single DNS query can turn into many DNSBackend questions,
   76     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
   77     asking this question. If so, the entire Distributor is shunted, and the answer is sent back *directly*, within a few microseconds.
   78 
   79     \section misc Miscellaneous
   80     Configuration details are available via the ArgvMap instance arg. Statistics are created by making calls to the StatBag object called S. 
   81     These statistics are made available via the UeberBackend on the same socket that is used for dynamic module commands.
   82 
   83     \section Main Main 
   84     The main() of PowerDNS can be found in receiver.cc - start reading there for further insights into the operation of the nameserver
   85 */
   86 
   87 vector<ComboAddress> g_localaddresses; // not static, our unit tests need to poke this
   88 
   89 void UDPNameserver::bindIPv4()
   90 {
   91   vector<string>locals;
   92   stringtok(locals,::arg()["local-address"]," ,");
   93   int one = 1;
   94 
   95   if(locals.empty())
   96     throw PDNSException("No local address specified");
   97 
   98   int s;
   99   for(vector<string>::const_iterator i=locals.begin();i!=locals.end();++i) {
  100     string localname(*i);
  101     ComboAddress locala;
  102 
  103     s=socket(AF_INET,SOCK_DGRAM,0);
  104 
  105     if(s<0) {
  106       g_log<<Logger::Error<<"Unable to acquire UDP socket: "+string(strerror(errno)) << endl;
  107       throw PDNSException("Unable to acquire a UDP socket: "+string(strerror(errno)));
  108     }
  109   
  110     setCloseOnExec(s);
  111   
  112     if(!setNonBlocking(s))
  113       throw PDNSException("Unable to set UDP socket to non-blocking: "+stringerror());
  114 
  115     locala=ComboAddress(localname, ::arg().asNum("local-port"));
  116 
  117     if(locala.sin4.sin_family != AF_INET)
  118       throw PDNSException("Attempting to bind IPv4 socket to IPv6 address");
  119 
  120     if(IsAnyAddress(locala))
  121       setsockopt(s, IPPROTO_IP, GEN_IP_PKTINFO, &one, sizeof(one));
  122 
  123     if (!setSocketTimestamps(s))
  124       g_log<<Logger::Warning<<"Unable to enable timestamp reporting for socket"<<endl;
  125 
  126     if (locala.isIPv4()) {
  127       try {
  128         setSocketIgnorePMTU(s);
  129       }
  130       catch(const std::exception& e) {
  131         g_log<<Logger::Warning<<"Failed to set IP_MTU_DISCOVER on UDP server socket: "<<e.what()<<endl;
  132       }
  133     }
  134 
  135 #ifdef SO_REUSEPORT
  136     if( d_can_reuseport )
  137         if( setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) )
  138           d_can_reuseport = false;
  139 #endif
  140 
  141     if( ::arg().mustDo("non-local-bind") )
  142     Utility::setBindAny(AF_INET, s);
  143 
  144 
  145     if( !d_additional_socket )
  146         g_localaddresses.push_back(locala);
  147 
  148     if(::bind(s, (sockaddr*)&locala, locala.getSocklen()) < 0) {
  149       string binderror = strerror(errno);
  150       close(s);
  151       if( errno == EADDRNOTAVAIL && ! ::arg().mustDo("local-address-nonexist-fail") ) {
  152         g_log<<Logger::Error<<"IPv4 Address " << localname << " does not exist on this server - skipping UDP bind" << endl;
  153         continue;
  154       } else {
  155         g_log<<Logger::Error<<"Unable to bind UDP socket to '"+locala.toStringWithPort()+"': "<<binderror<<endl;
  156         throw PDNSException("Unable to bind to UDP socket");
  157       }
  158     }
  159     d_sockets.push_back(s);
  160     g_log<<Logger::Error<<"UDP server bound to "<<locala.toStringWithPort()<<endl;
  161     struct pollfd pfd;
  162     pfd.fd = s;
  163     pfd.events = POLLIN;
  164     pfd.revents = 0;
  165     d_rfds.push_back(pfd);
  166   }
  167 }
  168 
  169 bool AddressIsUs(const ComboAddress& remote)
  170 {
  171   for(const ComboAddress& us :  g_localaddresses) {
  172     if(remote == us)
  173       return true;
  174     if(IsAnyAddress(us)) {
  175       int s = socket(remote.sin4.sin_family, SOCK_DGRAM, 0);
  176       if(s < 0) 
  177         continue;
  178 
  179       if(connect(s, (struct sockaddr*)&remote, remote.getSocklen()) < 0) {
  180         close(s);
  181         continue;
  182       }
  183     
  184       ComboAddress actualLocal;
  185       actualLocal.sin4.sin_family = remote.sin4.sin_family;
  186       socklen_t socklen = actualLocal.getSocklen();
  187 
  188       if(getsockname(s, (struct sockaddr*) &actualLocal, &socklen) < 0) {
  189         close(s);
  190         continue;
  191       }
  192       close(s);
  193       actualLocal.sin4.sin_port = us.sin4.sin_port;
  194       if(actualLocal == remote)
  195         return true;
  196     }
  197   }
  198   return false;
  199 }
  200 
  201 
  202 void UDPNameserver::bindIPv6()
  203 {
  204   vector<string> locals;
  205   stringtok(locals,::arg()["local-ipv6"]," ,");
  206   int one=1;
  207 
  208   if(locals.empty())
  209     return;
  210 
  211   int s;
  212   for(vector<string>::const_iterator i=locals.begin();i!=locals.end();++i) {
  213     string localname(*i);
  214 
  215     s=socket(AF_INET6,SOCK_DGRAM,0);
  216     if(s<0) {
  217       if( errno == EAFNOSUPPORT ) {
  218         g_log<<Logger::Error<<"IPv6 Address Family is not supported - skipping UDPv6 bind" << endl;
  219         return;
  220       } else {
  221         g_log<<Logger::Error<<"Unable to acquire a UDPv6 socket: "+string(strerror(errno)) << endl;
  222         throw PDNSException("Unable to acquire a UDPv6 socket: "+string(strerror(errno)));
  223       }
  224     }
  225 
  226     setCloseOnExec(s);
  227     if(!setNonBlocking(s))
  228       throw PDNSException("Unable to set UDPv6 socket to non-blocking: "+stringerror());
  229 
  230     ComboAddress locala(localname, ::arg().asNum("local-port"));
  231     
  232     if(IsAnyAddress(locala)) {
  233       setsockopt(s, IPPROTO_IP, GEN_IP_PKTINFO, &one, sizeof(one));     // linux supports this, so why not - might fail on other systems
  234 #ifdef IPV6_RECVPKTINFO
  235       setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); 
  236 #endif
  237       setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));      // if this fails, we report an error in tcpreceiver too
  238     }
  239 
  240     if (!setSocketTimestamps(s))
  241       g_log<<Logger::Warning<<"Unable to enable timestamp reporting for socket"<<endl;
  242 
  243 #ifdef SO_REUSEPORT
  244     if( d_can_reuseport )
  245         if( setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) )
  246           d_can_reuseport = false;
  247 #endif
  248 
  249     if( ::arg().mustDo("non-local-bind") )
  250     Utility::setBindAny(AF_INET6, s);
  251 
  252     if( !d_additional_socket )
  253         g_localaddresses.push_back(locala);
  254     if(::bind(s, (sockaddr*)&locala, locala.getSocklen())<0) {
  255       close(s);
  256       if( errno == EADDRNOTAVAIL && ! ::arg().mustDo("local-ipv6-nonexist-fail") ) {
  257         g_log<<Logger::Error<<"IPv6 Address " << localname << " does not exist on this server - skipping UDP bind" << endl;
  258         continue;
  259       } else {
  260         g_log<<Logger::Error<<"Unable to bind to UDPv6 socket "<< localname <<": "<<strerror(errno)<<endl;
  261         throw PDNSException("Unable to bind to UDPv6 socket");
  262       }
  263     }
  264     d_sockets.push_back(s);
  265     struct pollfd pfd;
  266     pfd.fd = s;
  267     pfd.events = POLLIN;
  268     pfd.revents = 0;
  269     d_rfds.push_back(pfd);
  270     g_log<<Logger::Error<<"UDPv6 server bound to "<<locala.toStringWithPort()<<endl;
  271   }
  272 }
  273 
  274 UDPNameserver::UDPNameserver( bool additional_socket )
  275 {
  276 #ifdef SO_REUSEPORT
  277   d_can_reuseport = ::arg().mustDo("reuseport");
  278 #endif
  279   // Are we the main socket (false) or a rebinding using SO_REUSEPORT ?
  280   d_additional_socket = additional_socket;
  281 
  282   if(!::arg()["local-address"].empty())
  283     bindIPv4();
  284   if(!::arg()["local-ipv6"].empty())
  285     bindIPv6();
  286 
  287   if(::arg()["local-address"].empty() && ::arg()["local-ipv6"].empty()) 
  288     g_log<<Logger::Critical<<"PDNS is deaf and mute! Not listening on any interfaces"<<endl;    
  289 }
  290 
  291 void UDPNameserver::send(DNSPacket *p)
  292 {
  293   string buffer=p->getString();
  294   g_rs.submitResponse(*p, true);
  295 
  296   struct msghdr msgh;
  297   struct iovec iov;
  298   char cbuf[256];
  299 
  300   fillMSGHdr(&msgh, &iov, cbuf, 0, (char*)buffer.c_str(), buffer.length(), &p->d_remote);
  301 
  302   msgh.msg_control=NULL;
  303   if(p->d_anyLocal) {
  304     addCMsgSrcAddr(&msgh, cbuf, p->d_anyLocal.get_ptr(), 0);
  305   }
  306   DLOG(g_log<<Logger::Notice<<"Sending a packet to "<< p->getRemote() <<" ("<< buffer.length()<<" octets)"<<endl);
  307   if(buffer.length() > p->getMaxReplyLen()) {
  308     g_log<<Logger::Error<<"Weird, trying to send a message that needs truncation, "<< buffer.length()<<" > "<<p->getMaxReplyLen()<<". Question was for "<<p->qdomain<<"|"<<p->qtype.getName()<<endl;
  309   }
  310   if(sendmsg(p->getSocket(), &msgh, 0) < 0)
  311     g_log<<Logger::Error<<"Error sending reply with sendmsg (socket="<<p->getSocket()<<", dest="<<p->d_remote.toStringWithPort()<<"): "<<strerror(errno)<<endl;
  312 }
  313 
  314 DNSPacket *UDPNameserver::receive(DNSPacket *prefilled, std::string& buffer)
  315 {
  316   ComboAddress remote;
  317   extern StatBag S;
  318   ssize_t len=-1;
  319   Utility::sock_t sock=-1;
  320 
  321   struct msghdr msgh;
  322   struct iovec iov;
  323   char cbuf[256];
  324 
  325   remote.sin6.sin6_family=AF_INET6; // make sure it is big enough
  326   fillMSGHdr(&msgh, &iov, cbuf, sizeof(cbuf), &buffer.at(0), buffer.size(), &remote);
  327   
  328   int err;
  329   vector<struct pollfd> rfds= d_rfds;
  330 
  331   for(auto &pfd :  rfds) {
  332     pfd.events = POLLIN;
  333     pfd.revents = 0;
  334   }
  335     
  336   retry:;
  337   
  338   err = poll(&rfds[0], rfds.size(), -1);
  339   if(err < 0) {
  340     if(errno==EINTR)
  341       goto retry;
  342     unixDie("Unable to poll for new UDP events");
  343   }
  344     
  345   for(auto &pfd :  rfds) {
  346     if(pfd.revents & POLLIN) {
  347       sock=pfd.fd;        
  348       if((len=recvmsg(sock, &msgh, 0)) < 0 ) {
  349         if(errno != EAGAIN)
  350           g_log<<Logger::Error<<"recvfrom gave error, ignoring: "<<strerror(errno)<<endl;
  351         return 0;
  352       }
  353       break;
  354     }
  355   }
  356   if(sock==-1)
  357     throw PDNSException("poll betrayed us! (should not happen)");
  358   
  359   DLOG(g_log<<"Received a packet " << len <<" bytes long from "<< remote.toString()<<endl);
  360 
  361   BOOST_STATIC_ASSERT(offsetof(sockaddr_in, sin_port) == offsetof(sockaddr_in6, sin6_port));
  362 
  363   if(remote.sin4.sin_port == 0) // would generate error on responding. sin4 also works for ipv6
  364     return 0;
  365   
  366   DNSPacket *packet;
  367   if(prefilled)  // they gave us a preallocated packet
  368     packet=prefilled;
  369   else
  370     packet=new DNSPacket(true); // don't forget to free it!
  371 
  372   packet->setSocket(sock);
  373   packet->setRemote(&remote);
  374 
  375   ComboAddress dest;
  376   if(HarvestDestinationAddress(&msgh, &dest)) {
  377 //    cerr<<"Setting d_anyLocal to '"<<dest.toString()<<"'"<<endl;
  378     packet->d_anyLocal = dest;
  379   }            
  380 
  381   struct timeval recvtv;
  382   if(HarvestTimestamp(&msgh, &recvtv)) {
  383     packet->d_dt.setTimeval(recvtv);
  384   }
  385   else
  386     packet->d_dt.set(); // timing    
  387 
  388   if(packet->parse(&buffer.at(0), (size_t) len)<0) {
  389     S.inc("corrupt-packets");
  390     S.ringAccount("remotes-corrupt", packet->d_remote);
  391 
  392     if(!prefilled)
  393       delete packet;
  394     return 0; // unable to parse
  395   }
  396   
  397   return packet;
  398 }