/*
- 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
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;
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());
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
}
}
+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())
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");
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;
+}
{
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:
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;
-}