]> granicus.if.org Git - pdns/commitdiff
finally gave up. You can now bind to 0.0.0.0 or :: and we'll Do The Right Thing at...
authorBert Hubert <bert.hubert@netherlabs.nl>
Fri, 5 Oct 2012 11:39:27 +0000 (11:39 +0000)
committerBert Hubert <bert.hubert@netherlabs.nl>
Fri, 5 Oct 2012 11:39:27 +0000 (11:39 +0000)
git-svn-id: svn://svn.powerdns.com/pdns/trunk/pdns@2763 d19b8d6e-7fed-0310-83ef-9ca221ded41b

pdns/common_startup.cc
pdns/dnspacket.cc
pdns/dnspacket.hh
pdns/nameserver.cc
pdns/nameserver.hh

index bb289f1cc1edd4244f16ba3dfd6c1b00584dfa8d..0f0dd0bac16bc8b0f20a2810c6ff833fcd09050a 100644 (file)
@@ -269,6 +269,7 @@ void *qthread(void *number)
         L<<"packetcache HIT"<<endl;
       cached.setRemote(&P->d_remote);  // inlined
       cached.setSocket(P->getSocket());                               // inlined
+      cached.d_anyLocal = P->d_anyLocal;
       cached.setMaxReplyLen(P->getMaxReplyLen());
       cached.d.rd=P->d.rd; // copy in recursion desired bit 
       cached.d.id=P->d.id;
index 5c18e9a3f5e3110bebe1e7ca16dbce12ba673e72..44fde37fe7642dabc77ceb3cced80d201cf2ba5b 100644 (file)
@@ -89,7 +89,7 @@ DNSPacket::DNSPacket(const DNSPacket &orig)
   d_maxreplylen = orig.d_maxreplylen;
   d_ednsping = orig.d_ednsping;
   d_wantsnsid = orig.d_wantsnsid;
-  
+  d_anyLocal = orig.d_anyLocal;  
   d_eso = orig.d_eso;
   d_haveednssubnet = orig.d_haveednssubnet;
   d_haveednssection = orig.d_haveednssection;
@@ -357,7 +357,7 @@ DNSPacket *DNSPacket::replyPacket() const
 {
   DNSPacket *r=new DNSPacket;
   r->setSocket(d_socket);
-
+  r->d_anyLocal=d_anyLocal;
   r->setRemote(&d_remote);
   r->setAnswer(true);  // this implies the allocation of the header
   r->setA(true); // and we are authoritative
index 5d9a74873fce82e3fd08f5bea504952c08d5de43..231fe8d2224091d0e7911404b65120125b3cb1c3 100644 (file)
@@ -1,6 +1,6 @@
 /*
     PowerDNS Versatile Database Driven Nameserver
-    Copyright (C) 2002 - 2011  PowerDNS.COM BV
+    Copyright (C) 2002 - 2012  PowerDNS.COM BV
 
     This program is free software; you can redistribute it and/or modify it
     under the terms of the GNU General Public License version 2 as published
@@ -93,6 +93,8 @@ public:
   }
   uint16_t getRemotePort() const;
 
+  boost::optional<ComboAddress> d_anyLocal;
+
   Utility::sock_t getSocket() const
   {
     return d_socket;
index 34ea30df8baa0c4c96a43018e2da255698e3f54d..edcfc763628f495a679886b345fdeb717fd6675c 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2002 - 2007  PowerDNS.COM BV
+    Copyright (C) 2002 - 2012  PowerDNS.COM BV
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License version 2 as 
@@ -76,6 +76,14 @@ extern StatBag S;
     The main() of PowerDNS can be found in receiver.cc - start reading there for further insights into the operation of the nameserver
 */
 
+#ifdef IP_PKTINFO
+  #define GEN_IP_PKTINFO IP_PKTINFO
+#endif
+#ifdef IP_RECVDSTADDR
+  #define GEN_IP_PKTINFO IP_RECVDSTADDR 
+#endif
+
+
 void UDPNameserver::bindIPv4()
 {
   vector<string>locals;
@@ -90,11 +98,12 @@ void UDPNameserver::bindIPv4()
     struct sockaddr_in locala;
 
     s=socket(AF_INET,SOCK_DGRAM,0);
-    Utility::setCloseOnExec(s);
 
     if(s<0)
       throw AhuException("Unable to acquire a UDP socket: "+string(strerror(errno)));
   
+    Utility::setCloseOnExec(s);
+  
     if(locals.size() > 1 && !Utility::setNonBlocking(s))
       throw AhuException("Unable to set UDP socket to non-blocking: "+stringerror());
   
@@ -102,8 +111,8 @@ void UDPNameserver::bindIPv4()
     locala.sin_family=AF_INET;
 
     if(localname=="0.0.0.0") {
-      L<<Logger::Warning<<"It is advised to bind to explicit addresses with the --local-address option"<<endl;
-
+      int val=1;
+      setsockopt(s, IPPROTO_IP, GEN_IP_PKTINFO, &val, sizeof(val));
       locala.sin_addr.s_addr = INADDR_ANY;
     }
     else
@@ -133,10 +142,20 @@ void UDPNameserver::bindIPv4()
   }
 }
 
+static bool IsAnyAddress(const ComboAddress& addr)
+{
+  if(addr.sin4.sin_family == AF_INET)
+    return addr.sin4.sin_addr.s_addr == 0;
+  else if(addr.sin4.sin_family == AF_INET6)
+    return !memcmp(&addr.sin6.sin6_addr, &in6addr_any, sizeof(addr.sin6.sin6_addr));
+  
+  return false;
+}
+
 void UDPNameserver::bindIPv6()
 {
 #if !WIN32 && HAVE_IPV6
-  vector<string>locals;
+  vector<string> locals;
   stringtok(locals,::arg()["local-ipv6"]," ,");
 
   if(locals.empty())
@@ -150,13 +169,15 @@ void UDPNameserver::bindIPv6()
     Utility::setCloseOnExec(s);
     if(s<0)
       throw AhuException("Unable to acquire a UDPv6 socket: "+string(strerror(errno)));
-  
-    if(localname=="::0") {
-      L<<Logger::Warning<<"It is advised to bind to explicit addresses with the --local-ipv6 option"<<endl;
+
+    ComboAddress locala(localname, ::arg().asNum("local-port"));
+    
+    if(IsAnyAddress(locala)) {
+      int val=1;
+      setsockopt(s, IPPROTO_IP, GEN_IP_PKTINFO, &val, sizeof(val));
+      setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val));
     }
     
-    ComboAddress locala(localname, ::arg().asNum("local-port"));
-
     if(::bind(s, (sockaddr*)&locala, sizeof(locala))<0) {
       L<<Logger::Error<<"binding to UDP ipv6 socket: "<<strerror(errno)<<endl;
       throw AhuException("Unable to bind to UDP ipv6 socket");
@@ -183,18 +204,205 @@ UDPNameserver::UDPNameserver()
     bindIPv6();
 
   if(::arg()["local-address"].empty() && ::arg()["local-ipv6"].empty()) 
-    L<<Logger::Critical<<"PDNS is deaf and mute! Not listening on any interfaces"<<endl;
-    
+    L<<Logger::Critical<<"PDNS is deaf and mute! Not listening on any interfaces"<<endl;    
 }
-
 void UDPNameserver::send(DNSPacket *p)
 {
   const string& buffer=p->getString();
+  
+  struct msghdr msgh;
+  struct cmsghdr *cmsg;
+  struct iovec iov;
+  char cbuf[256];
+
+  /* Set up iov and msgh structures. */
+  memset(&msgh, 0, sizeof(struct msghdr));
+  iov.iov_base = (void*)buffer.c_str();
+  iov.iov_len = buffer.length();
+  msgh.msg_iov = &iov;
+  msgh.msg_iovlen = 1;
+  msgh.msg_name = (struct sockaddr*)&p->d_remote;
+  msgh.msg_namelen = p->d_remote.getSocklen();
+
+  if(p->d_anyLocal) {
+    if(p->d_anyLocal->sin4.sin_family == AF_INET6) {
+      struct in6_pktinfo *pkt;
+          
+      msgh.msg_control = cbuf;
+      msgh.msg_controllen = CMSG_SPACE(sizeof(*pkt));
+                  
+      cmsg = CMSG_FIRSTHDR(&msgh);
+      cmsg->cmsg_level = IPPROTO_IPV6;
+      cmsg->cmsg_type = IPV6_PKTINFO;
+      cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
+                                  
+      pkt = (struct in6_pktinfo *) CMSG_DATA(cmsg);
+      memset(pkt, 0, sizeof(*pkt));
+      pkt->ipi6_addr = p->d_anyLocal->sin6.sin6_addr;
+//      cerr<<"Setting local ipv6 address"<<endl;
+    }
+    else {
+#ifdef IP_PKTINFO
+      struct in_pktinfo *pkt;
+      msgh.msg_control = cbuf;
+      msgh.msg_controllen = CMSG_SPACE(sizeof(*pkt));
+
+      cmsg = CMSG_FIRSTHDR(&msgh);
+      cmsg->cmsg_level = SOL_IP;
+      cmsg->cmsg_type = IP_PKTINFO;
+      cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
+
+      pkt = (struct in_pktinfo *) CMSG_DATA(cmsg);
+      memset(pkt, 0, sizeof(*pkt));
+      pkt->ipi_spec_dst = p->d_anyLocal->sin4.sin_addr;
+//      cerr<<"Setting local ipv4 address Linux way"<<endl;
+#endif
+#ifdef IP_SENDSRCADDR
+      struct in_addr *in;
+    
+      msgh.msg_control = cbuf;
+      msgh.msg_controllen = CMSG_SPACE(sizeof(*in));
+            
+      cmsg = CMSG_FIRSTHDR(&msgh);
+      cmsg->cmsg_level = IPPROTO_IP;
+      cmsg->cmsg_type = IP_SENDSRCADDR;
+      cmsg->cmsg_len = CMSG_LEN(sizeof(*in));
+                            
+      in = (struct in_addr *) CMSG_DATA(cmsg);
+      *in = p->d_anyLocal->sin4.sin_addr;
+//      cerr<<"Setting local ipv4 address FreeBSD way"<<endl;
+#endif
+    }
+  }
   DLOG(L<<Logger::Notice<<"Sending a packet to "<< p->getRemote() <<" ("<< buffer.length()<<" octets)"<<endl);
   if(buffer.length() > p->getMaxReplyLen()) {
     cerr<<"Weird, trying to send a message that needs truncation, "<< buffer.length()<<" > "<<p->getMaxReplyLen()<<endl;
   }
-  if(sendto(p->getSocket(),buffer.c_str(), buffer.length(), 0, (struct sockaddr *)(&p->d_remote), p->d_remote.getSocklen()) < 0)
+  if(sendmsg(p->getSocket(), &msgh, 0) < 0)
     L<<Logger::Error<<"Error sending reply with sendto (socket="<<p->getSocket()<<"): "<<strerror(errno)<<endl;
 }
 
+static bool HarvestDestinationAddress(struct msghdr* msgh, ComboAddress* destination)
+{
+  memset(destination, 0, sizeof(*destination));
+  struct cmsghdr *cmsg;
+  for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(msgh,cmsg)) {
+#ifdef IP_PKTINFO
+     if ((cmsg->cmsg_level == SOL_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
+        struct in_pktinfo *i = (struct in_pktinfo *) CMSG_DATA(cmsg);
+        destination->sin4.sin_addr = i->ipi_addr;
+        destination->sin4.sin_family = AF_INET;
+        return true;
+    }
+#endif
+#ifdef IP_RECVDSTADDR
+    if ((cmsg->cmsg_level == SOL_IP) && (cmsg->cmsg_type == IP_RECVDSTADDR)) {
+      struct in_addr *i = (struct in_addr *) CMSG_DATA(cmsg);
+      destination->sin4.sin_addr = *i;
+      destination->sin4.sin_family = AF_INET;      
+      return true;
+    }
+#endif
+
+    if ((cmsg->cmsg_level == IPPROTO_IPV6) && (cmsg->cmsg_type == IPV6_PKTINFO)) {
+        struct in6_pktinfo *i = (struct in6_pktinfo *) CMSG_DATA(cmsg);
+        destination->sin6.sin6_addr = i->ipi6_addr;
+        destination->sin4.sin_family = AF_INET6;
+        return true;
+    }
+  }
+  return false;
+}
+
+DNSPacket *UDPNameserver::receive(DNSPacket *prefilled)
+{
+  ComboAddress remote;
+  extern StatBag S;
+  int len=-1;
+  char mesg[512];
+  Utility::sock_t sock=-1;
+    
+  struct msghdr msgh;
+  struct iovec iov;
+  char cbuf[256];
+
+  iov.iov_base = mesg;
+  iov.iov_len  = sizeof(mesg);
+
+  memset(&msgh, 0, sizeof(struct msghdr));
+  
+  msgh.msg_control = cbuf;
+  msgh.msg_controllen = sizeof(cbuf);
+  msgh.msg_name = &remote;
+  msgh.msg_namelen = sizeof(remote);
+  msgh.msg_iov  = &iov;
+  msgh.msg_iovlen = 1;
+  msgh.msg_flags = 0;
+  
+  int err;
+  if(d_sockets.size()>1) {
+    BOOST_FOREACH(struct pollfd &pfd, d_rfds) {
+      pfd.events = POLL_IN;
+      pfd.revents = 0;
+    }
+    
+    err = poll(&d_rfds[0], d_rfds.size(), -1);
+    if(err < 0)
+      unixDie("Unable to poll for new UDP events");
+    
+    BOOST_FOREACH(struct pollfd &pfd, d_rfds) {
+      if(pfd.revents & POLL_IN) {
+        sock=pfd.fd;        
+        len=0;
+        
+        if((len=recvmsg(sock, &msgh, 0)) < 0 ) {
+           if(errno != EAGAIN)
+            L<<Logger::Error<<"recvfrom gave error, ignoring: "<<strerror(errno)<<endl;
+          return 0;
+        }
+        break;
+      }
+    }
+    if(sock==-1)
+      throw AhuException("select betrayed us! (should not happen)");
+  }
+  else {
+    sock=d_sockets[0];
+
+    if((len=recvmsg(sock, &msgh, 0)) < 0 ) {
+      if(errno != EAGAIN)
+        L<<Logger::Error<<"recvfrom gave error, ignoring: "<<strerror(errno)<<endl;
+      return 0;
+    }
+  }
+  
+  
+  DLOG(L<<"Received a packet " << len <<" bytes long from "<< remote.toString()<<endl);
+  
+  DNSPacket *packet;
+  if(prefilled)  // they gave us a preallocated packet
+    packet=prefilled;
+  else
+    packet=new DNSPacket; // don't forget to free it!
+  packet->d_dt.set(); // timing
+  packet->setSocket(sock);
+  packet->setRemote(&remote);
+
+  ComboAddress dest;
+  if(HarvestDestinationAddress(&msgh, &dest)) {
+//    cerr<<"Setting d_anyLocal to '"<<dest.toString()<<"'"<<endl;
+    packet->d_anyLocal = dest;
+  }      
+
+
+  if(packet->parse(mesg, len)<0) {
+    S.inc("corrupt-packets");
+    S.ringAccount("remotes-corrupt", packet->getRemote());
+
+    if(!prefilled)
+      delete packet;
+    return 0; // unable to parse
+  }
+  
+  return packet;
+}
index ae6b113ffbebdf282d7df7014f916669917f01ce..8986e69c092ea3c82cd4ebf9b0d79e0e12fa26b0 100644 (file)
@@ -73,7 +73,7 @@ class UDPNameserver
 {
 public:
   UDPNameserver();  //!< Opens the socket
-  inline DNSPacket *receive(DNSPacket *prefilled=0); //!< call this in a while or for(;;) loop to get packets
+  DNSPacket *receive(DNSPacket *prefilled=0); //!< call this in a while or for(;;) loop to get packets
   static void send(DNSPacket *); //!< send a DNSPacket. Will call DNSPacket::truncate() if over 512 bytes
   
 private:
@@ -84,78 +84,6 @@ private:
   int d_highfd;
 };
 
-inline DNSPacket *UDPNameserver::receive(DNSPacket *prefilled)
-{
-  ComboAddress remote;
-  extern StatBag S;
-
-  Utility::socklen_t addrlen;
-  int len=-1;
-  char mesg[513];
-  Utility::sock_t sock=-1;
-
-  memset( &remote, 0, sizeof( remote ));
-  addrlen=sizeof(remote);  
-  if(d_sockets.size()>1) {
-    BOOST_FOREACH(struct pollfd &pfd, d_rfds) {
-      pfd.events = POLL_IN;
-      pfd.revents = 0;
-    }
-    
-    poll(&d_rfds[0], d_rfds.size(), -1);
-  
-    BOOST_FOREACH(struct pollfd &pfd, d_rfds) {
-      if(pfd.revents & POLL_IN) {
-        sock=pfd.fd;
-        addrlen=sizeof(remote);
-        
-        len=0;
-
-        // XXX FIXME this code could be using recvmsg + ip_pktinfo on platforms that support it
-        
-        if((len=recvfrom(sock,mesg,sizeof(mesg)-1,0,(sockaddr*) &remote, &addrlen))<0) {
-          if(errno != EAGAIN)
-            L<<Logger::Error<<"recvfrom gave error, ignoring: "<<strerror(errno)<<endl;
-          return 0;
-        }
-        break;
-      }
-    }
-    if(sock==-1)
-      throw AhuException("select betrayed us! (should not happen)");
-  }
-  else {
-    sock=d_sockets[0];
-
-    len=0;
-    if((len=recvfrom(sock,mesg,512,0,(sockaddr*) &remote, &addrlen))<0) {
-      if(errno != EAGAIN)
-        L<<Logger::Error<<"recvfrom gave error, ignoring: "<<strerror(errno)<<endl;
-      return 0;
-    }
-  }
-  
-  DLOG(L<<"Received a packet " << len <<" bytes long from "<< remote.toString()<<endl);
-  
-  DNSPacket *packet;
-  if(prefilled)  // they gave us a preallocated packet
-    packet=prefilled;
-  else
-    packet=new DNSPacket; // don't forget to free it!
-  packet->d_dt.set(); // timing
-  packet->setSocket(sock);
-  packet->setRemote(&remote);
-  if(packet->parse(mesg, len)<0) {
-    S.inc("corrupt-packets");
-    S.ringAccount("remotes-corrupt", packet->getRemote());
-
-    if(!prefilled)
-      delete packet;
-    return 0; // unable to parse
-  }
-  
-  return packet;
-}