From 2b6f1436930d84fef86d2eb7cab33e1f2e22eb06 Mon Sep 17 00:00:00 2001 From: Bert Hubert Date: Fri, 5 Oct 2012 11:39:27 +0000 Subject: [PATCH] finally gave up. You can now bind to 0.0.0.0 or :: and we'll Do The Right Thing at least on Linux and BSD. Untested on BSD, unknown if this even will work on Solaris. git-svn-id: svn://svn.powerdns.com/pdns/trunk/pdns@2763 d19b8d6e-7fed-0310-83ef-9ca221ded41b --- pdns/common_startup.cc | 1 + pdns/dnspacket.cc | 4 +- pdns/dnspacket.hh | 4 +- pdns/nameserver.cc | 236 ++++++++++++++++++++++++++++++++++++++--- pdns/nameserver.hh | 74 +------------ 5 files changed, 229 insertions(+), 90 deletions(-) diff --git a/pdns/common_startup.cc b/pdns/common_startup.cc index bb289f1cc..0f0dd0bac 100644 --- a/pdns/common_startup.cc +++ b/pdns/common_startup.cc @@ -269,6 +269,7 @@ void *qthread(void *number) L<<"packetcache HIT"<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; diff --git a/pdns/dnspacket.cc b/pdns/dnspacket.cc index 5c18e9a3f..44fde37fe 100644 --- a/pdns/dnspacket.cc +++ b/pdns/dnspacket.cc @@ -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 diff --git a/pdns/dnspacket.hh b/pdns/dnspacket.hh index 5d9a74873..231fe8d22 100644 --- a/pdns/dnspacket.hh +++ b/pdns/dnspacket.hh @@ -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 d_anyLocal; + Utility::sock_t getSocket() const { return d_socket; diff --git a/pdns/nameserver.cc b/pdns/nameserver.cc index 34ea30df8..edcfc7636 100644 --- a/pdns/nameserver.cc +++ b/pdns/nameserver.cc @@ -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() { vectorlocals; @@ -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<locals; + vector 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<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"<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"<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"<getRemote() <<" ("<< buffer.length()<<" octets)"< p->getMaxReplyLen()) { cerr<<"Weird, trying to send a message that needs truncation, "<< buffer.length()<<" > "<getMaxReplyLen()<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<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<d_dt.set(); // timing + packet->setSocket(sock); + packet->setRemote(&remote); + + ComboAddress dest; + if(HarvestDestinationAddress(&msgh, &dest)) { +// cerr<<"Setting d_anyLocal to '"<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; +} diff --git a/pdns/nameserver.hh b/pdns/nameserver.hh index ae6b113ff..8986e69c0 100644 --- a/pdns/nameserver.hh +++ b/pdns/nameserver.hh @@ -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<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; -} -- 2.40.0