Hello,

Here comes another feature patch for the reSIProcate transition from
ares to c-ares. The embedded copy supports querying IPv6 nameservers and
setting these up via the options structure.

The reSIProcate developers substituted the in_addr field by an union
contruct to hold the in6_addr. As this would break API compatibility, I
have added a second nservers and servers field and suffixed it with a '6'.

For the internal part I've declared the mf_address structure:
  struct mf_address {
    sa_family_t family;
    union
    {
      struct in_addr in4;
      struct in6_addr in6;
    } mf_addr;
  };

Another approach would be the usage of sockaddr_storage. But all the
casting to sockaddr_in and sockaddr_in6 made the code very ugly and hard
to read. Maybe some macros would help here. The other drawback of
sockaddr_storage is it's relativly young age, so that it's (probably)
not available on all target platforms.

Things that needs to be done:
* Guard the AF_INET6 specific part by the preprocessor. What
(config-)defines would be optimal for this task? A combination of
HAVE_AF_INET6, HAVE_STRUCT_SOCKADDR_STORAGE, HAVE_STRUCT_SOCKADDR_IN6?

* Test on all supported target platforms

* Documentation

But before I continue I'd like to get some feedback on this issue.

Thanks,
Gregor

Index: adig.c
===================================================================
RCS file: /cvsroot/curl/curl/ares/adig.c,v
retrieving revision 1.36
diff -u -3 -p -r1.36 adig.c
--- adig.c	17 Oct 2008 19:04:53 -0000	1.36
+++ adig.c	23 Nov 2008 17:29:36 -0000
@@ -152,7 +152,6 @@ static const char *opcodes[] = {
   "UPDATEA", "UPDATED", "UPDATEDA", "UPDATEM", "UPDATEMA",
   "ZONEINIT", "ZONEREF"
 };
-  struct in_addr inaddr;
 
 static const char *rcodes[] = {
   "NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", "NOTIMP", "REFUSED",
@@ -180,6 +179,8 @@ int main(int argc, char **argv)
   struct hostent *hostent;
   fd_set read_fds, write_fds;
   struct timeval *tvp, tv;
+  struct in_addr inaddr;
+  struct in6_addr in6addr;
 
 #ifdef USE_WINSOCK
   WORD wVersionRequested = MAKEWORD(USE_WINSOCK,USE_WINSOCK);
@@ -190,7 +191,9 @@ int main(int argc, char **argv)
   options.flags = ARES_FLAG_NOCHECKRESP;
   options.servers = NULL;
   options.nservers = 0;
-  while ((c = ares_getopt(argc, argv, "df:s:c:t:T:U:")) != -1)
+  options.servers6 = NULL;
+  options.nservers6 = 0;
+  while ((c = ares_getopt(argc, argv, "df:s:S:c:t:T:U:")) != -1)
     {
       switch (c)
         {
@@ -237,6 +240,26 @@ int main(int argc, char **argv)
           optmask |= ARES_OPT_SERVERS;
           break;
 
+        case 'S':
+          /* Add a server, and specify servers in the option mask. */
+          if (ares_inet_pton(AF_INET6, optarg, &in6addr) <= 0)
+            {
+              fprintf(stderr, "adig: inet_pton returned an error on server %s.\n", optarg);
+              return 1;
+            }
+          options.servers6 = realloc(options.servers6, (options.nservers6 + 1)
+                                    * sizeof(struct in6_addr));
+          if (!options.servers6)
+            {
+              fprintf(stderr, "Out of memory!\n");
+              return 1;
+            }
+          memcpy(&options.servers6[options.nservers6], &in6addr,
+                 sizeof(struct in_addr));
+          options.nservers6++;
+          optmask |= ARES_OPT_SERVERS6;
+          break;
+
         case 'c':
           /* Set the query class. */
           for (i = 0; i < nclasses; i++)
@@ -715,7 +738,7 @@ static const char *class_name(int dnscla
 
 static void usage(void)
 {
-  fprintf(stderr, "usage: adig [-f flag] [-s server] [-c class] "
+  fprintf(stderr, "usage: adig [-f flag] [-s IPv4 server] [-S IPv6 server] [-c class] "
           "[-t type] [-p port] name ...\n");
   exit(1);
 }
Index: ares.h
===================================================================
RCS file: /cvsroot/curl/curl/ares/ares.h,v
retrieving revision 1.42
diff -u -3 -p -r1.42 ares.h
--- ares.h	19 Nov 2008 15:16:16 -0000	1.42
+++ ares.h	23 Nov 2008 17:29:36 -0000
@@ -115,6 +115,7 @@ extern "C" {
 #define ARES_OPT_SOCK_RCVBUF    (1 << 12)
 #define ARES_OPT_TIMEOUTMS      (1 << 13)
 #define ARES_OPT_ROTATE         (1 << 14)
+#define ARES_OPT_SERVERS6       (1 << 15)
 
 /* Nameinfo flag values */
 #define ARES_NI_NOFQDN                  (1 << 0)
@@ -180,6 +181,15 @@ typedef void (*ares_sock_state_cb)(void 
 
 struct apattern;
 
+#if !defined(HAVE_STRUCT_IN6_ADDR) && !defined(s6_addr)
+struct in6_addr {
+  union {
+    unsigned char _S6_u8[16];
+  } _S6_un;
+};
+#define s6_addr _S6_un._S6_u8
+#endif
+
 struct ares_options {
   int flags;
   int timeout; /* in seconds or milliseconds, depending on options */
@@ -199,6 +209,8 @@ struct ares_options {
   void *sock_state_cb_data;
   struct apattern *sortlist;
   int nsort;
+  struct in6_addr *servers6;
+  int nservers6;
 };
 
 struct hostent;
@@ -251,14 +263,6 @@ int ares_expand_name(const unsigned char
 int ares_expand_string(const unsigned char *encoded, const unsigned char *abuf,
                      int alen, unsigned char **s, long *enclen);
 
-#if !defined(HAVE_STRUCT_IN6_ADDR) && !defined(s6_addr)
-struct in6_addr {
-  union {
-    unsigned char _S6_u8[16];
-  } _S6_un;
-};
-#define s6_addr _S6_un._S6_u8
-#endif
 
 struct addrttl {
   struct in_addr ipaddr;
Index: ares_init.c
===================================================================
RCS file: /cvsroot/curl/curl/ares/ares_init.c,v
retrieving revision 1.81
diff -u -3 -p -r1.81 ares_init.c
--- ares_init.c	20 Nov 2008 07:50:48 -0000	1.81
+++ ares_init.c	23 Nov 2008 17:29:36 -0000
@@ -261,7 +261,7 @@ int ares_init_options(ares_channel *chan
 int ares_save_options(ares_channel channel, struct ares_options *options,
                       int *optmask)
 {
-  int i;
+  int i, s;
 
   /* Zero everything out */
   memset(options, 0, sizeof(struct ares_options));
@@ -272,7 +272,7 @@ int ares_save_options(ares_channel chann
   (*optmask) = (ARES_OPT_FLAGS|ARES_OPT_TRIES|ARES_OPT_NDOTS|
                 ARES_OPT_UDP_PORT|ARES_OPT_TCP_PORT|ARES_OPT_SOCK_STATE_CB|
                 ARES_OPT_SERVERS|ARES_OPT_DOMAINS|ARES_OPT_LOOKUPS|
-                ARES_OPT_SORTLIST|ARES_OPT_TIMEOUTMS);
+                ARES_OPT_SORTLIST|ARES_OPT_TIMEOUTMS|ARES_OPT_SERVERS6);
 
   /* Copy easy stuff */
   options->flags   = channel->flags;
@@ -287,16 +287,38 @@ int ares_save_options(ares_channel chann
   options->sock_state_cb     = channel->sock_state_cb;
   options->sock_state_cb_data = channel->sock_state_cb_data;
 
-  /* Copy servers */
-  if (channel->nservers) {
+  /* Count servers */
+  options->nservers = options->nservers6 = 0;
+  for (i = 0; i < channel->nservers; i++)
+    switch (channel->servers[i].address.family)
+      {
+        case AF_INET: options->nservers++; break;
+        case AF_INET6: options->nservers6++; break;
+      }
+
+  /* Copy IPv4 servers */
+  if (options->nservers) {
     options->servers =
-      malloc(channel->nservers * sizeof(struct server_state));
-    if (!options->servers && channel->nservers != 0)
+      malloc(options->nservers * sizeof(struct in_addr));
+    if (!options->servers)
+      return ARES_ENOMEM;
+    for (i = s = 0; i < channel->nservers; i++)
+      if (channel->servers[i].address.family != AF_INET)
+        continue;
+      options->servers[s++] = channel->servers[i].address.mf_addr.in4;
+  }
+
+  /* Copy IPv6 servers */
+  if (options->nservers6) {
+    options->servers6 =
+      malloc(options->nservers6 * sizeof(struct in6_addr));
+    if (!options->servers6)
       return ARES_ENOMEM;
-    for (i = 0; i < channel->nservers; i++)
-      options->servers[i] = channel->servers[i].addr;
+    for (i = s = 0; i < channel->nservers; i++)
+      if (channel->servers[i].address.family != AF_INET6)
+        continue;
+      options->servers6[s++] = channel->servers[i].address.mf_addr.in6;
   }
-  options->nservers = channel->nservers;
 
   /* copy domains */
   if (channel->ndomains) {
@@ -373,19 +395,41 @@ static int init_by_options(ares_channel 
     channel->socket_receive_buffer_size = options->socket_receive_buffer_size;
 
   /* Copy the servers, if given. */
-  if ((optmask & ARES_OPT_SERVERS) && channel->nservers == -1)
+  if ((optmask & (ARES_OPT_SERVERS|ARES_OPT_SERVERS6)) && channel->nservers == -1)
     {
+      int n = 0;
+      if ((optmask & ARES_OPT_SERVERS) && options->nservers > 0)
+        n += options->nservers;
+      if ((optmask & ARES_OPT_SERVERS6) && options->nservers6 > 0)
+        n += options->nservers6;
+
       /* Avoid zero size allocations at any cost */
-      if (options->nservers > 0)
+      if (n > 0)
         {
           channel->servers =
-            malloc(options->nservers * sizeof(struct server_state));
+            calloc(n, sizeof(struct server_state));
           if (!channel->servers)
             return ARES_ENOMEM;
-          for (i = 0; i < options->nservers; i++)
-            channel->servers[i].addr = options->servers[i];
+
+          n = 0;
+
+          /* copy IPv4 */
+          if ((optmask & ARES_OPT_SERVERS) && options->nservers > 0)
+            for (i = 0; i < options->nservers; i++, n++)
+              {
+                channel->servers[n].address.family = AF_INET;
+                channel->servers[n].address.mf_addr.in4 = options->servers[i];
+              }
+
+          /* copy IPv6 */
+          if ((optmask & ARES_OPT_SERVERS6) && options->nservers6 > 0)
+            for (i = 0; i < options->nservers6; i++, n++)
+              {
+                channel->servers[n].address.family = AF_INET6;
+                channel->servers[n].address.mf_addr.in6 = options->servers6[i];
+              }
         }
-      channel->nservers = options->nservers;
+      channel->nservers = n;
     }
 
   /* Copy the domains, if given.  Keep channel->ndomains consistent so
@@ -950,7 +994,8 @@ static int init_by_defaults(ares_channel
       rc = ARES_ENOMEM;
       goto error;
     }
-    channel->servers[0].addr.s_addr = htonl(INADDR_LOOPBACK);
+    channel->servers[0].address.family = AF_INET;
+    channel->servers[0].address.mf_addr.in4.s_addr = htonl(INADDR_LOOPBACK);
     channel->nservers = 1;
   }
 
@@ -1139,7 +1184,8 @@ static int config_nameserver(struct serv
   newserv = realloc(*servers, (*nservers + 1) * sizeof(struct server_state));
   if (!newserv)
     return ARES_ENOMEM;
-  newserv[*nservers].addr = addr;
+  newserv[*nservers].address.family = AF_INET;
+  newserv[*nservers].address.mf_addr.in4 = addr;
   *servers = newserv;
   (*nservers)++;
 #endif
Index: ares_private.h
===================================================================
RCS file: /cvsroot/curl/curl/ares/ares_private.h,v
retrieving revision 1.39
diff -u -3 -p -r1.39 ares_private.h
--- ares_private.h	20 Nov 2008 07:50:48 -0000	1.39
+++ ares_private.h	23 Nov 2008 17:29:36 -0000
@@ -131,8 +131,17 @@ struct send_request {
   struct send_request *next;
 };
 
+struct mf_address {
+  sa_family_t family;
+  union
+  {
+    struct in_addr in4;
+    struct in6_addr in6;
+  } mf_addr;
+};
+
 struct server_state {
-  struct in_addr addr;
+  struct mf_address address;
   ares_socket_t udp_socket;
   ares_socket_t tcp_socket;
 
Index: ares_process.c
===================================================================
RCS file: /cvsroot/curl/curl/ares/ares_process.c,v
retrieving revision 1.71
diff -u -3 -p -r1.71 ares_process.c
--- ares_process.c	13 Nov 2008 18:56:56 -0000	1.71
+++ ares_process.c	23 Nov 2008 17:29:37 -0000
@@ -97,6 +97,7 @@ static int open_tcp_socket(ares_channel 
 static int open_udp_socket(ares_channel channel, struct server_state *server);
 static int same_questions(const unsigned char *qbuf, int qlen,
                           const unsigned char *abuf, int alen);
+static int same_address(const struct sockaddr_storage *from, const struct mf_address *address);
 static void end_query(ares_channel channel, struct query *query, int status,
                       unsigned char *abuf, int alen);
 
@@ -428,7 +429,7 @@ static void read_udp_packets(ares_channe
   ssize_t count;
   unsigned char buf[PACKETSZ + 1];
 #ifdef HAVE_RECVFROM
-  struct sockaddr_in from;
+  struct sockaddr_storage from;
   socklen_t fromlen;
 #endif
 
@@ -466,6 +467,7 @@ static void read_udp_packets(ares_channe
       do {
 #ifdef HAVE_RECVFROM
         fromlen = sizeof(from);
+        fprintf(stderr, "Size: %u\n", fromlen);
         count = (ssize_t)recvfrom(server->udp_socket, (void *)buf, sizeof(buf),
                                   0, (struct sockaddr *)&from, &fromlen);
 #else
@@ -476,7 +478,7 @@ static void read_udp_packets(ares_channe
         else if (count <= 0)
           handle_error(channel, i, now);
 #ifdef HAVE_RECVFROM
-        else if (from.sin_addr.s_addr != server->addr.s_addr)
+        else if (!same_address(&from, &server->address))
           /* Address response came from did not match the address
            * we sent the request to.  Someone may be attempting
            * to perform a cache poisoning attack */
@@ -883,10 +885,33 @@ static int open_tcp_socket(ares_channel 
 {
   ares_socket_t s;
   int opt;
-  struct sockaddr_in sockin;
+  struct sockaddr_in sin;
+  struct sockaddr_in6 sin6;
+  const struct sockaddr *sa;
+  socklen_t sock_len;
+
+  switch (server->address.family)
+    {
+      case AF_INET:
+        memset(&sin, 0, sizeof(sin));
+        sin.sin_addr = server->address.mf_addr.in4;
+        sin.sin_port = (unsigned short)(channel->tcp_port & 0xffff);
+        sock_len = sizeof(sin);
+        sa = (const struct sockaddr *)&sin;
+        break;
+      case AF_INET6:
+        memset(&sin6, 0, sizeof(sin6));
+        sin6.sin6_addr = server->address.mf_addr.in6;
+        sin6.sin6_port = (unsigned short)(channel->tcp_port & 0xffff);
+        sock_len = sizeof(sin6);
+        sa = (const struct sockaddr *)&sin6;
+        break;
+      default:
+        return -1;
+    }
 
   /* Acquire a socket. */
-  s = socket(AF_INET, SOCK_STREAM, 0);
+  s = socket(server->address.family, SOCK_STREAM, 0);
   if (s == ARES_SOCKET_BAD)
     return -1;
 
@@ -912,11 +937,7 @@ static int open_tcp_socket(ares_channel 
     }
 
   /* Connect to the server. */
-  memset(&sockin, 0, sizeof(sockin));
-  sockin.sin_family = AF_INET;
-  sockin.sin_addr = server->addr;
-  sockin.sin_port = (unsigned short)(channel->tcp_port & 0xffff);
-  if (connect(s, (struct sockaddr *) &sockin, sizeof(sockin)) == -1)
+  if (connect(s, sa, sock_len) == -1)
     {
       int err = SOCKERRNO;
 
@@ -937,10 +958,35 @@ static int open_tcp_socket(ares_channel 
 static int open_udp_socket(ares_channel channel, struct server_state *server)
 {
   ares_socket_t s;
-  struct sockaddr_in sockin;
+  struct sockaddr_in sin;
+  struct sockaddr_in6 sin6;
+  const struct sockaddr *sa;
+  socklen_t sock_len;
+
+  switch (server->address.family)
+    {
+      case AF_INET:
+        memset(&sin, 0, sizeof(sin));
+        sin.sin_family = AF_INET;
+        sin.sin_addr = server->address.mf_addr.in4;
+        sin.sin_port = (unsigned short)(channel->tcp_port & 0xffff);
+        sock_len = sizeof(sin);
+        sa = (const struct sockaddr *)&sin;
+        break;
+      case AF_INET6:
+        memset(&sin6, 0, sizeof(sin6));
+        sin6.sin6_family = AF_INET6;
+        sin6.sin6_addr = server->address.mf_addr.in6;
+        sin6.sin6_port = (unsigned short)(channel->tcp_port & 0xffff);
+        sock_len = sizeof(sin6);
+        sa = (const struct sockaddr *)&sin6;
+        break;
+      default:
+        return -1;
+    }
 
   /* Acquire a socket. */
-  s = socket(AF_INET, SOCK_DGRAM, 0);
+  s = socket(server->address.family, SOCK_DGRAM, 0);
   if (s == ARES_SOCKET_BAD)
     return -1;
 
@@ -952,11 +998,7 @@ static int open_udp_socket(ares_channel 
     }
 
   /* Connect to the server. */
-  memset(&sockin, 0, sizeof(sockin));
-  sockin.sin_family = AF_INET;
-  sockin.sin_addr = server->addr;
-  sockin.sin_port = (unsigned short)(channel->udp_port & 0xffff);
-  if (connect(s, (struct sockaddr *) &sockin, sizeof(sockin)) == -1)
+  if (connect(s, sa, sock_len) == -1)
     {
       int err = SOCKERRNO;
 
@@ -1052,6 +1094,22 @@ static int same_questions(const unsigned
   return 1;
 }
 
+static int same_address(const struct sockaddr_storage *from, const struct mf_address *address)
+{
+  if (from->ss_family == AF_INET && address->family == AF_INET)
+  {
+    const struct sockaddr_in *from_in = (const struct sockaddr_in*)from;
+    return from_in->sin_addr.s_addr == address->mf_addr.in4.s_addr;
+  }
+  else if (from->ss_family == AF_INET6 && address->family == AF_INET6)
+  {
+    const struct sockaddr_in6 *from_in6 = (const struct sockaddr_in6*)from;
+    return from_in6->sin6_addr.s6_addr == address->mf_addr.in6.s6_addr;
+  }
+
+  return 0;
+}
+
 static void end_query (ares_channel channel, struct query *query, int status,
                        unsigned char *abuf, int alen)
 {

Reply via email to