[Pdns-dev] Better IPv6 support [was: Slaving from IPv6 host]

Lionel Elie Mamane lionel at mamane.lu
Sat Jan 7 12:02:41 CET 2006


On Tue, Oct 12, 2004 at 03:37:26PM +0200, Lionel Elie Mamane wrote:
> On Sat, Jul 17, 2004 at 05:12:13PM +0200, Lionel Elie Mamane wrote:

>> pdsn 2.9.16 cannot slave from an IPv6 host. Here's a patch that
>> implements this.

> In case anyone is interested, a new version of the patch, that makes
> IPv6 literals in masters (masters from which to slave from) work. In
> this case, a "." is used as IP address-to-port separator.

And a new version, that additionally corrects the following bug in the
recursor: When AAAA-additional processing is activated, e.g. in an MX
query that answers "example.com IN MX 10 a.mx.example.com", it tries
to lookup a AAAA RR for "10 a.mx.example.com" instead of
"a.mx.example.com".

I've also added the "AI_NUMERICHOST" flag to when I call getaddrinfo.

It also adds an query-local-ipv6 option to choose which IPv6 address
queries will come from (like query-local-address for IPv4).

-- 
Lionel
-------------- next part --------------
diff --recursive -u pdns-2.9.19/pdns/common_startup.cc pdns-2.9.19.ipv6/pdns/common_startup.cc
--- pdns-2.9.19/pdns/common_startup.cc	2005-10-16 17:26:00.000000000 +0200
+++ pdns-2.9.19.ipv6/pdns/common_startup.cc	2006-01-07 11:06:13.777357810 +0100
@@ -48,6 +48,7 @@
   arg().set("local-address","Local IP addresses to which we bind")="0.0.0.0";
   arg().set("local-ipv6","Local IP address to which we bind")="";
   arg().set("query-local-address","Source IP address for sending queries")="";
+  arg().set("query-local-ipv6","Source IP address for sending queries")="";
   arg().set("max-queue-length","Maximum queuelength before considering situation lost")="5000";
   arg().set("soa-serial-offset","Make sure that no SOA serial is less than this number")="0";
 
diff --recursive -u pdns-2.9.19/pdns/communicator.cc pdns-2.9.19.ipv6/pdns/communicator.cc
--- pdns-2.9.19/pdns/communicator.cc	2005-10-20 23:04:07.000000000 +0200
+++ pdns-2.9.19.ipv6/pdns/communicator.cc	2006-01-07 11:06:13.777357810 +0100
@@ -2,6 +2,9 @@
     PowerDNS Versatile Database Driven Nameserver
     Copyright (C) 2002-2005  PowerDNS.COM BV
 
+    Modified 10-17 Jul 2004 for slaving from IPv6 host by
+    Lionel Elie Mamane <lionel at mamane.lu>
+
     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 by the Free Software Foundation; 
@@ -242,7 +245,6 @@
 
   for(vector<DomainInfo>::const_iterator i=sdomains.begin();i!=sdomains.end();++i) {
     Resolver resolver;   
-    resolver.makeUDPSocket();  
     d_slaveschanged=true;
     uint32_t ourserial=i->serial,theirserial=0;
 
diff --recursive -u pdns-2.9.19/pdns/misc.cc pdns-2.9.19.ipv6/pdns/misc.cc
--- pdns-2.9.19/pdns/misc.cc	2005-10-09 11:57:17.000000000 +0200
+++ pdns-2.9.19.ipv6/pdns/misc.cc	2006-01-07 11:06:13.777357810 +0100
@@ -2,6 +2,9 @@
     PowerDNS Versatile Database Driven Nameserver
     Copyright (C) 2002  PowerDNS.COM BV
 
+    Modified 11 Oct 2004 for slaving from IPv6 host by
+    Lionel Elie Mamane <lionel at mamane.lu>
+
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 2 of the License, or
@@ -139,9 +142,21 @@
 
 void parseService(const string &descr, ServiceTuple &st)
 {
-
+  // If descr contains at least two colons (:), it is an IPv6 address litteral.
+  // If not, it cannot be.
+  const string colon = ":";
+  string delimiter = colon;
+  {
+    string::size_type i;
+    i = descr.find_first_of(colon);
+    if (i != string::npos) {
+      if (descr.find_first_of(colon,i+1) != string::npos) {
+	delimiter = ".";
+      }
+    }
+  }
   vector<string>parts;
-  stringtok(parts,descr,":");
+  stringtok(parts,descr,delimiter.c_str());
   if(parts.empty())
     throw AhuException("Unable to parse '"+descr+"' as a service");
   st.host=parts[0];
diff --recursive -u pdns-2.9.19/pdns/resolver.cc pdns-2.9.19.ipv6/pdns/resolver.cc
--- pdns-2.9.19/pdns/resolver.cc	2005-10-23 18:46:57.000000000 +0200
+++ pdns-2.9.19.ipv6/pdns/resolver.cc	2006-01-07 11:06:13.781358007 +0100
@@ -2,6 +2,9 @@
     PowerDNS Versatile Database Driven Nameserver
     Copyright (C) 2002 - 2005 PowerDNS.COM BV
 
+    Modified 10-17 Jul 2004 for slaving from IPv6 host by
+    Lionel Elie Mamane <lionel at mamane.lu>
+
     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 by the Free Software Foundation.
@@ -34,35 +37,93 @@
 #include "ahuexception.hh"
 #include "statbag.hh"
 #include "arguments.hh"
+#include <assert.h>
 
-void Resolver::makeUDPSocket()
+void Resolver::makeUDPSocket(int domain)
 {
-  makeSocket(SOCK_DGRAM);
+  makeSocket(domain, SOCK_DGRAM);
 }
 
-void Resolver::makeSocket(int type)
+void Resolver::makeSocket(int domain, int type)
 {
   static uint16_t port_counter=5000;
   port_counter++; // this makes us use a new port for each query, fixes ticket #2
-  if(d_sock>0)
-    return;
+  struct sockaddr_storage ss;
+  socklen_t ss_size = sizeof(ss);
+  struct sockaddr &sa = *(struct sockaddr*)&ss;
+
+  memset((char *)&ss,0, sizeof(ss));
+
+  assert(AF_INET == PF_INET && AF_INET6 == PF_INET6);
 
-  d_sock=socket(AF_INET, type,0);
+  if(d_sock > 0) {
+    int result;
+
+    result = getsockname(d_sock, &sa, &ss_size);
+    if (result != 0 || sa.sa_family != domain) {
+      memset((char *)&ss,0, sizeof(ss));
+      Utility::closesocket(d_sock);
+    }
+    else {
+      return;
+    }
+  }
+
+  d_sock=socket(domain, type,0);
   if(d_sock<0) 
     throw AhuException("Making a socket for resolver: "+stringerror());
 
-  struct sockaddr_in sin;
-  memset((char *)&sin,0, sizeof(sin));
-  
-  sin.sin_family = AF_INET;
-  if(!IpToU32(arg()["query-local-address"], &sin.sin_addr.s_addr))
-    throw AhuException("Unable to resolve local address '"+ arg()["query-local-address"] +"'"); 
+  sa.sa_family = domain;
+
+  if (domain == PF_INET) {
+    struct sockaddr_in &sin = *(struct sockaddr_in*)&ss;
+
+    if(!IpToU32(arg()["query-local-address"], &sin.sin_addr.s_addr))
+      throw AhuException("Unable to resolve local address '"+ arg()["query-local-address"] +"'"); 
+    ss_size = sizeof(sin);
+  }
+  else if(domain == PF_INET6) {
+    struct addrinfo *toaddrs;
+    struct sockaddr_in6 &sin = *(struct sockaddr_in6*)&ss;
+    sin.sin6_addr = in6addr_any;
+    ss_size = sizeof(sin);
+    
+    toaddrs = Utility::resolve_hostname_to_ip(arg()["query-local-ipv6"],"",AF_INET6);
+
+    for(struct addrinfo *toaddr = toaddrs;toaddr!=NULL;toaddr=toaddr->ai_next) {
+      if (toaddr == NULL) {
+	freeaddrinfo(toaddrs);
+	throw AhuException("Unable to resolve local address '"+ arg()["query-local-ipv6"] +"'");
+      }
+      else if (toaddr->ai_addr == NULL || toaddr->ai_family != PF_INET6) {
+	continue;
+      }
+      else {
+	ss_size = toaddr->ai_addrlen;
+	memcpy(&(sin.sin6_addr), toaddr->ai_addr, ss_size);
+	freeaddrinfo(toaddrs);
+	break;
+      }
+    }
+  }
+  else {
+    throw AhuException("Unkown socket domain: " + domain);
+  }
 
   int tries=10;
   while(--tries) {
-    sin.sin_port = htons(10000+(random()%10000));
-  
-    if (bind(d_sock, (struct sockaddr *)&sin, sizeof(sin)) >= 0) 
+    if (domain == PF_INET) {
+      struct sockaddr_in &sin = *(struct sockaddr_in*)&ss;
+
+      sin.sin_port = htons(10000+(port_counter++)%10000); // should be random!
+    }
+    else if(domain == PF_INET6) {
+      struct sockaddr_in6 &sin = *(struct sockaddr_in6*)&ss;
+
+      sin.sin6_port = htons(10000+(port_counter++)%10000); // should be random!
+    }
+
+    if (bind(d_sock, &sa, ss_size) >= 0) 
       break;
 
   }
@@ -163,8 +224,7 @@
   d_type=type;
   d_inaxfr=false;
 
-  struct sockaddr_in toaddr;
-  struct in_addr inp;
+  struct addrinfo *toaddrs;
   ServiceTuple st;
   st.port=53;
   try {
@@ -174,15 +234,26 @@
     throw ResolverException("Sending a dns question to '"+ip+"': "+ae.reason);
   }
 
-  Utility::inet_aton(st.host.c_str(),&inp);
-  toaddr.sin_addr.s_addr=inp.s_addr;
-
-  toaddr.sin_port=htons(st.port);
-  toaddr.sin_family=AF_INET;
+  {
+    ostringstream ss;
+    ss << st.port;
+    toaddrs = Utility::resolve_hostname_to_ip(st.host.c_str(),ss.str(),AF_UNSPEC);
+  }
 
-  if(sendto(d_sock, p.getData(), p.len, 0, (struct sockaddr*)(&toaddr), sizeof(toaddr))<0) {
-    throw ResolverException("Unable to ask query of "+st.host+":"+itoa(st.port)+": "+stringerror());
+  for(struct addrinfo *toaddr = toaddrs;toaddr!=NULL;toaddr=toaddr->ai_next) {
+    makeUDPSocket(toaddr->ai_addr->sa_family);
+    if(sendto(d_sock, p.getData(), p.len, 0, toaddr->ai_addr, toaddr->ai_addrlen)<0) {
+      if (toaddr->ai_next == NULL) {
+	freeaddrinfo(toaddrs);
+	throw ResolverException("Unable to ask query of "+st.host+":"+itoa(st.port)+": "+stringerror());
+      }
+    }
+    else
+      break;
   }
+
+  freeaddrinfo(toaddrs);
+
 }
 
 int Resolver::receiveResolve(struct sockaddr* fromaddr, Utility::socklen_t addrlen)
@@ -211,7 +282,6 @@
   
 int Resolver::resolve(const string &ip, const char *domain, int type)
 {
-  makeUDPSocket();
   sendResolve(ip,domain,type);
   try {
     struct sockaddr_in from;
@@ -224,25 +294,16 @@
   
 }
 
-void Resolver::makeTCPSocket(const string &ip, uint16_t port)
+void Resolver::makeTCPSocket_one_IP(const struct sockaddr *toaddr, const size_t toaddr_len)
 {
-  if(d_sock>=0)
-    return;
-
-  struct in_addr inp;
-  Utility::inet_aton(ip.c_str(),&inp);
-  d_toaddr.sin_addr.s_addr=inp.s_addr;
-
-  d_toaddr.sin_port=htons(port);
-  d_toaddr.sin_family=AF_INET;
-
-  d_sock=socket(AF_INET,SOCK_STREAM,0);
+  d_sock=socket(toaddr->sa_family,SOCK_STREAM,0);
   if(d_sock<0)
     throw ResolverException("Unable to make a TCP socket for resolver: "+stringerror());
 
+  // TODO: Handle IPv6 here
   // Use query-local-address as source IP for queries, if specified.
   string querylocaladdress(arg()["query-local-address"]);
-  if (querylocaladdress!="") {
+  if (querylocaladdress!="" && toaddr->sa_family == AF_INET) {
     struct sockaddr_in fromaddr;
     struct hostent *h=0;
 
@@ -268,9 +329,9 @@
 
   int err;
 #ifndef WIN32
-  if((err=connect(d_sock,(struct sockaddr*)&d_toaddr,sizeof(d_toaddr)))<0 && errno!=EINPROGRESS) {
+  if((err=connect(d_sock,toaddr,toaddr_len))<0 && errno!=EINPROGRESS) {
 #else
-  if((err=connect(d_sock,(struct sockaddr*)&d_toaddr,sizeof(d_toaddr)))<0 && WSAGetLastError() != WSAEWOULDBLOCK ) {
+  if((err=connect(d_sock,toaddr,toaddr_len))<0 && WSAGetLastError() != WSAEWOULDBLOCK ) {
 #endif // WIN32
     Utility::closesocket(d_sock);
     d_sock=-1;
@@ -315,6 +376,33 @@
   // d_sock now connected
 }
 
+void Resolver::makeTCPSocket(const string &ip, u_int16_t port)
+{
+  if(d_sock>=0)
+    return;
+  struct addrinfo *toaddrs;
+  {
+    ostringstream ss;
+    ss << port;
+    toaddrs = Utility::resolve_hostname_to_ip(ip,ss.str(),AF_UNSPEC);
+  }
+
+  if (toaddrs==NULL)
+    throw ResolverException("unable to resolve "+ip);
+
+  for(struct addrinfo *toaddr = toaddrs;toaddr!=NULL;toaddr=toaddr->ai_next) {
+    try {
+      makeTCPSocket_one_IP(toaddr->ai_addr, toaddr->ai_addrlen);
+    }
+    catch(...){
+      if (toaddr->ai_next==NULL) {
+	freeaddrinfo(toaddrs);
+	throw;
+      }
+    }
+  }
+  freeaddrinfo(toaddrs);
+}
 
 //! returns -1 for permanent error, 0 for timeout, 1 for success
 int Resolver::axfr(const string &ip, const char *domain)
diff --recursive -u pdns-2.9.19/pdns/resolver.hh pdns-2.9.19.ipv6/pdns/resolver.hh
--- pdns-2.9.19/pdns/resolver.hh	2005-10-23 18:39:46.000000000 +0200
+++ pdns-2.9.19.ipv6/pdns/resolver.hh	2006-01-07 11:06:13.781358007 +0100
@@ -2,6 +2,9 @@
     PowerDNS Versatile Database Driven Nameserver
     Copyright (C) 2002  PowerDNS.COM BV
 
+    Modified 10-17 Jul 2004 for slaving from IPv6 host by
+    Lionel Elie Mamane <lionel at mamane.lu>
+
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 2 of the License, or
@@ -55,8 +58,8 @@
   string i;
 
   typedef vector<DNSResourceRecord> res_t;
-  void makeSocket(int type);
-  void makeUDPSocket();
+  void makeSocket(int domain, int type);
+  void makeUDPSocket(int domain);
   void makeTCPSocket(const string &ip, uint16_t port=53);
   int notify(int sock, const string &domain, const string &ip, uint16_t id);
   int resolve(const string &ip, const char *domain, int type);
@@ -74,6 +77,7 @@
   int axfr(const string &ip, const char *domain);
   
 private:
+  void Resolver::makeTCPSocket_one_IP(const struct sockaddr *toaddr, const size_t toaddr_len);
   void timeoutReadn(char *buffer, int bytes);
   int d_sock;
   unsigned char *d_buf;
diff --recursive -u pdns-2.9.19/pdns/syncres.cc pdns-2.9.19.ipv6/pdns/syncres.cc
--- pdns-2.9.19/pdns/syncres.cc	2005-10-23 13:32:01.000000000 +0200
+++ pdns-2.9.19.ipv6/pdns/syncres.cc	2006-01-07 11:31:26.199843526 +0100
@@ -2,6 +2,11 @@
     PowerDNS Versatile Database Driven Nameserver
     Copyright (C) 2003 - 2005  PowerDNS.COM BV
 
+    Copyright (C) 2006 Lionel Elie Mamane <lionel at mamane.lu>
+
+    Modified 7 Jan 2006 by Lionel Elie Mamane <lionel at mamane.lu>
+    Chop off priority of and canonicalise AAAA additional processing RRs, too.
+
     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 
     by the Free Software Foundation
@@ -585,18 +590,20 @@
        ((k->d_place==DNSResourceRecord::AUTHORITY || k->d_place==DNSResourceRecord::ANSWER) && k->qtype==QType(QType::NS))) {
       LOG<<d_prefix<<qname<<": record '"<<k->content<<"|"<<k->qtype.getName()<<"' needs IP for additional processing"<<endl;
       set<GetBestNSAnswer>beenthere;
+      string RR_to_lookup;
       if(k->qtype==QType(QType::MX)) {
 	string::size_type pos=k->content.find_first_not_of(" \t0123456789"); // chop off the priority
 	if(pos!=string::npos)
-	  doResolve(toLowerCanonic(k->content.substr(pos)), QType(QType::A),addit,1,beenthere);
+	  RR_to_lookup = toLowerCanonic(k->content.substr(pos));
 	else
-	  doResolve(toLowerCanonic(k->content), QType(QType::A),addit,1,beenthere);
+	  RR_to_lookup = toLowerCanonic(k->content);
       }
       else
-	doResolve(k->content,QType(QType::A),addit,1,beenthere);
+	RR_to_lookup = k->content;
 
+      doResolve(RR_to_lookup,QType(QType::A),addit,1,beenthere);
       if(arg().mustDo("aaaa-additional-processing"))
-	doResolve(k->content,QType(QType::AAAA),addit,1,beenthere);
+	doResolve(RR_to_lookup,QType(QType::AAAA),addit,1,beenthere);
     }
   
   for(vector<DNSResourceRecord>::iterator k=addit.begin();k!=addit.end();++k) {
diff --recursive -u pdns-2.9.19/pdns/unix_utility.cc pdns-2.9.19.ipv6/pdns/unix_utility.cc
--- pdns-2.9.19/pdns/unix_utility.cc	2005-10-20 23:45:04.000000000 +0200
+++ pdns-2.9.19.ipv6/pdns/unix_utility.cc	2006-01-07 11:28:21.910767814 +0100
@@ -2,6 +2,9 @@
     PowerDNS Versatile Database Driven Nameserver
     Copyright (C) 2002 - 2005 PowerDNS.COM BV
 
+    Modified 10-17 Jul 2004, 2005 by Lionel Elie Mamane <lionel at mamane.lu>
+    Added resolve_hostname_to_ip function.
+
     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 by the Free Software Foundation
@@ -27,6 +30,7 @@
 #include <pwd.h>
 #include <grp.h>
 #include <sys/types.h>
+#include <netdb.h>
 
 #ifdef NEED_INET_NTOP_PROTO
 extern "C" {
@@ -123,6 +127,28 @@
 
 }
 
+struct addrinfo *Utility::resolve_hostname_to_ip( const string &host, const string &port, int pf )
+{
+   struct addrinfo hints, *res0;
+   int result;
+   memset(&hints, 0, sizeof(hints));
+   hints.ai_family = pf;
+   hints.ai_socktype = SOCK_STREAM;
+   hints.ai_flags = AI_NUMERICHOST;
+
+   result = getaddrinfo(host.c_str(), port.c_str(), &hints, &res0);
+   if ( result != 0 ) {
+     theL()<<Logger::Error<<"Unable to resolve hostname '"<<host<<"': "<<gai_strerror(result)<<endl;
+     if (result == EAI_SYSTEM)
+       theL()<<Logger::Error<<"The system error is: "<<strerror(errno)<<endl;
+     return NULL;
+   }
+   else
+     if (res0==0)
+       theL()<<Logger::Error<<"Unable to resolve hostname '"<<host<<"': "<<"Success, but no information returned"<<endl;
+
+   return res0;
+}
 
 // Converts an address from presentation format to network format.
 int Utility::inet_pton( int af, const char *src, void *dst )
diff --recursive -u pdns-2.9.19/pdns/utility.hh pdns-2.9.19.ipv6/pdns/utility.hh
--- pdns-2.9.19/pdns/utility.hh	2005-09-03 20:12:43.000000000 +0200
+++ pdns-2.9.19.ipv6/pdns/utility.hh	2006-01-07 11:06:13.781358007 +0100
@@ -2,6 +2,9 @@
     PowerDNS Versatile Database Driven Nameserver
     Copyright (C) 2002  PowerDNS.COM BV
 
+    Modified 10-17 Jul 2004 by Lionel Elie Mamane <lionel at mamane.lu>
+    Added resolve_hostname_to_ip function.
+
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 2 of the License, or
@@ -188,6 +191,9 @@
   //! Gets the current time.
   static int gettimeofday( struct timeval *tv, void *tz = NULL );
 
+  //! Converts an address from any format to binary data.
+  static struct addrinfo *resolve_hostname_to_ip( const string &host, const string &port, int pf );
+
   //! Converts an address from dot and numbers format to binary data.
   static int inet_aton( const char *cp, struct in_addr *inp );
 


More information about the Pdns-dev mailing list