]> granicus.if.org Git - curl/commitdiff
Added IPv6 name servers support
authorYang Tse <yangsita@gmail.com>
Fri, 5 Mar 2010 20:01:47 +0000 (20:01 +0000)
committerYang Tse <yangsita@gmail.com>
Fri, 5 Mar 2010 20:01:47 +0000 (20:01 +0000)
27 files changed:
ares/CHANGES
ares/Makefile.inc
ares/RELEASE-NOTES
ares/adig.c
ares/ahost.c
ares/ares.h
ares/ares__get_hostent.c
ares/ares_data.c
ares/ares_data.h
ares/ares_destroy.c
ares/ares_free_data.3
ares/ares_get_servers.3 [new file with mode: 0644]
ares/ares_gethostbyaddr.c
ares/ares_gethostbyname.c
ares/ares_getnameinfo.c
ares/ares_init.3
ares/ares_init.c
ares/ares_ipv6.h
ares/ares_options.c [new file with mode: 0644]
ares/ares_parse_aaaa_reply.c
ares/ares_private.h
ares/ares_process.c
ares/ares_save_options.3
ares/ares_set_servers.3 [new file with mode: 0644]
ares/inet_net_pton.c
ares/inet_ntop.c
ares/vc/cares/vc6cares.dsp

index c3961c4c556b3ed05c45c02c54b5beead4714ebf..bb9d9385efc9662f0af697665d42aebbc29441d3 100644 (file)
@@ -1,5 +1,26 @@
   Changelog for the c-ares project
 
+* March 5, 2010 (Yang Tse)
+- Added IPv6 name servers support. Implementation has been based on code,
+  comments and feedback provided November and December of 2008 by Daniel
+  Stenberg, Gregor Jasny, Phil Blundell and myself, December 2009 by Cedric
+  Bail, and February 2010 by Jakub Hrozek on the c-ares mailing list. On
+  March I reviewed all that, selected the best of each, and adjusted or
+  extended parts of it to make the best fit.
+
+  The external and visible result of all this is that two new functions are
+  added to the external API, ares_get_servers() and ares_set_servers(), which
+  becomes now the preferred way of getting and setting name servers for any
+  ares channel as these support both IPv4 and IPv6 name servers.
+
+  In order to not break ABI compatibility, ares_init_options() with option
+  mask ARES_OPT_SERVERS and ares_save_options() may still be used in code
+  which is intended to run on IPv4-only stacks. But remember that these
+  functions do not support IPv6 name servers. This implies that if the user
+  is capable of defining or providing an IPv6 name server, and the app is
+  using ares_init_options() or ares_save_options() at some point to handle
+  the name servers, the app will likely loose IPv6 name servers.
+
 * January 28, 2010 (Daniel Stenberg)
 - Tommie Gannert pointed out a silly bug in ares_process_fd() since it didn't
   check for broken connections like ares_process() did. Based on that, I
index a0d030035843d54051b3612acc9e82ae14ae7464..cef828a7cff2b5d87d59dae848e56c4bc3a14cb1 100644 (file)
@@ -20,6 +20,7 @@ CSOURCES = ares__close_sockets.c      \
   ares_llist.c                         \
   ares_mkquery.c                       \
   ares_nowarn.c                                \
+  ares_options.c                       \
   ares_parse_a_reply.c                 \
   ares_parse_aaaa_reply.c              \
   ares_parse_ns_reply.c                        \
@@ -48,8 +49,8 @@ HHEADERS = ares.h                     \
   ares_ipv6.h                          \
   ares_library_init.h                  \
   ares_llist.h                         \
-  ares_private.h                       \
   ares_nowarn.h                                \
+  ares_private.h                       \
   ares_rules.h                         \
   ares_strcasecmp.h                    \
   ares_strdup.h                                \
@@ -72,6 +73,7 @@ MANPAGES = ares_cancel.3              \
   ares_free_data.3                     \
   ares_free_hostent.3                  \
   ares_free_string.3                   \
+  ares_get_servers.3                   \
   ares_gethostbyaddr.3                 \
   ares_gethostbyname.3                 \
   ares_gethostbyname_file.3            \
@@ -93,6 +95,7 @@ MANPAGES = ares_cancel.3              \
   ares_save_options.3                  \
   ares_search.3                                \
   ares_send.3                          \
+  ares_set_servers.3                   \
   ares_set_socket_callback.3           \
   ares_strerror.3                      \
   ares_timeout.3                       \
@@ -108,6 +111,7 @@ HTMLPAGES = ares_cancel.html                \
   ares_free_data.html                  \
   ares_free_hostent.html               \
   ares_free_string.html                        \
+  ares_get_servers.html                        \
   ares_gethostbyaddr.html              \
   ares_gethostbyname.html              \
   ares_gethostbyname_file.html         \
@@ -129,6 +133,7 @@ HTMLPAGES = ares_cancel.html                \
   ares_save_options.html               \
   ares_search.html                     \
   ares_send.html                       \
+  ares_set_servers.html                        \
   ares_set_socket_callback.html                \
   ares_strerror.html                   \
   ares_timeout.html                    \
@@ -144,6 +149,7 @@ PDFPAGES = ares_cancel.pdf          \
   ares_free_data.pdf                   \
   ares_free_hostent.pdf                        \
   ares_free_string.pdf                 \
+  ares_get_servers.pdf                 \
   ares_gethostbyaddr.pdf               \
   ares_gethostbyname.pdf               \
   ares_gethostbyname_file.pdf          \
@@ -165,6 +171,7 @@ PDFPAGES = ares_cancel.pdf          \
   ares_save_options.pdf                        \
   ares_search.pdf                      \
   ares_send.pdf                                \
+  ares_set_servers.pdf                 \
   ares_set_socket_callback.pdf         \
   ares_strerror.pdf                    \
   ares_timeout.pdf                     \
index 120ac2908f65783381c42556c20e1936fb20b7d6..0e1fefb4c8bc997513d55248d85c1cc3bb8ffe90 100644 (file)
@@ -2,7 +2,7 @@ This is what's new and changed in the c-ares 1.7.1 release:
 
 Changed:
 
- o 
+ o added IPv6 name servers support
 
 Fixed:
 
@@ -12,6 +12,7 @@ Fixed:
 
 Thanks go to these friendly people for their efforts and contributions:
 
- Ingmar Runge, Laszlo Tamas Szabo, Yang Tse, Tommie Gannert
+ Ingmar Runge, Laszlo Tamas Szabo, Yang Tse, Tommie Gannert, Gregor Jasny,
+ Phil Blundell, Cedric Bail, Jakub Hrozek
 
 Have fun!
index e796a2c0db1b587d3556321f93d624cafd45b263..4e33f6c02b3ebe520b7be32d5addad8b9c1ee995 100644 (file)
@@ -164,8 +164,6 @@ static const char *rcodes[] = {
   "(unknown)", "(unknown)", "(unknown)", "(unknown)", "NOCHANGE"
 };
 
-static struct in_addr inaddr;
-
 static void callback(void *arg, int status, int timeouts,
                      unsigned char *abuf, int alen);
 static const unsigned char *display_question(const unsigned char *aptr,
@@ -176,6 +174,9 @@ static const unsigned char *display_rr(const unsigned char *aptr,
 static const char *type_name(int type);
 static const char *class_name(int dnsclass);
 static void usage(void);
+static void destroy_addr_list(struct ares_addr_node *head);
+static void append_addr_list(struct ares_addr_node **head,
+                             struct ares_addr_node *node);
 
 int main(int argc, char **argv)
 {
@@ -186,6 +187,7 @@ int main(int argc, char **argv)
   struct hostent *hostent;
   fd_set read_fds, write_fds;
   struct timeval *tvp, tv;
+  struct ares_addr_node *srvr, *servers = NULL;
 
 #ifdef USE_WINSOCK
   WORD wVersionRequested = MAKEWORD(USE_WINSOCK,USE_WINSOCK);
@@ -227,27 +229,56 @@ int main(int argc, char **argv)
           break;
 
         case 's':
-          /* Add a server, and specify servers in the option mask. */
-          if (ares_inet_pton(AF_INET, optarg, &inaddr) <= 0)
+          /* User specified name servers override default ones. */
+          srvr = malloc(sizeof(struct ares_addr_node));
+          if (!srvr)
+            {
+              fprintf(stderr, "Out of memory!\n");
+              destroy_addr_list(servers);
+              return 1;
+            }
+          append_addr_list(&servers, srvr);
+          if (ares_inet_pton(AF_INET, optarg, &srvr->addr.addr4) > 0)
+            srvr->family = AF_INET;
+          else if (ares_inet_pton(AF_INET6, optarg, &srvr->addr.addr6) > 0)
+            srvr->family = AF_INET6;
+          else
             {
               hostent = gethostbyname(optarg);
-              if (!hostent || hostent->h_addrtype != AF_INET)
+              if (!hostent)
                 {
                   fprintf(stderr, "adig: server %s not found.\n", optarg);
+                  destroy_addr_list(servers);
                   return 1;
                 }
-              memcpy(&inaddr, hostent->h_addr, sizeof(struct in_addr));
-            }
-          options.servers = realloc(options.servers, (options.nservers + 1)
-                                    * sizeof(struct in_addr));
-          if (!options.servers)
-            {
-              fprintf(stderr, "Out of memory!\n");
-              return 1;
+              switch (hostent->h_addrtype)
+                {
+                  case AF_INET:
+                    srvr->family = AF_INET;
+                    memcpy(&srvr->addr.addr4, hostent->h_addr,
+                           sizeof(srvr->addr.addr4));
+                    break;
+                  case AF_INET6:
+                    srvr->family = AF_INET6;
+                    memcpy(&srvr->addr.addr6, hostent->h_addr,
+                           sizeof(srvr->addr.addr6));
+                    break;
+                  default:
+                    fprintf(stderr,
+                      "adig: server %s unsupported address family.\n", optarg);
+                    destroy_addr_list(servers);
+                    return 1;
+                }
             }
-          memcpy(&options.servers[options.nservers], &inaddr,
-                 sizeof(struct in_addr));
-          options.nservers++;
+          /* Notice that calling ares_init_options() without servers in the
+           * options struct and with ARES_OPT_SERVERS set simultaneously in
+           * the options mask, results in an initialization with no servers.
+           * When alternative name servers have been specified these are set
+           * later calling ares_set_servers() overriding any existing server
+           * configuration. To prevent initial configuration with default
+           * servers that will be discarded later ARES_OPT_SERVERS is set.
+           * If this flag is not set here the result shall be the same but
+           * ares_init_options() will do needless work. */
           optmask |= ARES_OPT_SERVERS;
           break;
 
@@ -308,6 +339,18 @@ int main(int argc, char **argv)
       return 1;
     }
 
+  if(servers)
+    {
+      status = ares_set_servers(channel, servers);
+      destroy_addr_list(servers);
+      if (status != ARES_SUCCESS)
+        {
+          fprintf(stderr, "ares_init_options: %s\n",
+                  ares_strerror(status));
+          return 1;
+        }
+    }
+
   /* Initiate the queries, one per command-line argument.  If there is
    * only one query to do, supply NULL as the callback argument;
    * otherwise, supply the query name as an argument so we can
@@ -749,3 +792,29 @@ static void usage(void)
           "[-t type] [-p port] name ...\n");
   exit(1);
 }
+
+static void destroy_addr_list(struct ares_addr_node *head)
+{
+  while(head)
+    {
+      struct ares_addr_node *detached = head;
+      head = head->next;
+      free(detached);
+    }
+}
+
+static void append_addr_list(struct ares_addr_node **head,
+                             struct ares_addr_node *node)
+{
+  struct ares_addr_node *last;
+  node->next = NULL;
+  if(*head)
+    {
+      last = *head;
+      while(last->next)
+        last = last->next;
+      last->next = node;
+    }
+  else
+    *head = node;
+}
index ce5d35903cc2ca1a33d2c3605c98de2739b05bb4..f499306399681a65c97eb0beba8c98708f177a95 100644 (file)
@@ -69,7 +69,7 @@ int main(int argc, char **argv)
   fd_set read_fds, write_fds;
   struct timeval *tvp, tv;
   struct in_addr addr4;
-  struct in6_addr addr6;
+  struct ares_in6_addr addr6;
 
 #ifdef USE_WINSOCK
   WORD wVersionRequested = MAKEWORD(USE_WINSOCK,USE_WINSOCK);
index 8a9b1c0b2a339550b319ce0d9e719134b663e6b4..c514f2516d651d7f3d158ba179a4a5f5ccc3acbc 100644 (file)
@@ -1,7 +1,7 @@
 /* $Id$ */
 
 /* Copyright 1998, 2009 by the Massachusetts Institute of Technology.
- * Copyright (C) 2007-2009 by Daniel Stenberg
+ * Copyright (C) 2007-2010 by Daniel Stenberg
  *
  * Permission to use, copy, modify, and distribute this
  * software and its documentation for any purpose and without
@@ -487,6 +487,21 @@ CARES_EXTERN void ares_free_data(void *dataptr);
 
 CARES_EXTERN const char *ares_strerror(int code);
 
+struct ares_addr_node {
+  struct ares_addr_node *next;
+  int family;
+  union {
+    struct in_addr       addr4;
+    struct ares_in6_addr addr6;
+  } addr;
+};
+
+CARES_EXTERN int ares_set_servers(ares_channel channel,
+                                  struct ares_addr_node *servers);
+
+CARES_EXTERN int ares_get_servers(ares_channel channel,
+                                  struct ares_addr_node **servers);
+
 #ifdef  __cplusplus
 }
 #endif
index 18d300987f7acc93a4a22695e3349a95f45d119b..caa7f795bb0f31ec675dc92c1b8f49039ebbc6aa 100644 (file)
@@ -1,6 +1,6 @@
 /* $Id$ */
 
-/* Copyright 1998, 2009 by the Massachusetts Institute of Technology.
+/* Copyright 1998, 2010 by the Massachusetts Institute of Technology.
  *
  * Permission to use, copy, modify, and distribute this
  * software and its documentation for any purpose and without
@@ -146,7 +146,7 @@ int ares__get_hostent(FILE *fp, int family, struct hostent **host)
             {
               /* Actual network address family and length. */
               addr.family = AF_INET;
-              addrlen = sizeof(struct in_addr);
+              addrlen = sizeof(addr.addrV4);
             }
         }
       if ((family == AF_INET6) || ((family == AF_UNSPEC) && (!addrlen)))
@@ -155,7 +155,7 @@ int ares__get_hostent(FILE *fp, int family, struct hostent **host)
             {
               /* Actual network address family and length. */
               addr.family = AF_INET6;
-              addrlen = sizeof(struct in6_addr);
+              addrlen = sizeof(addr.addrV6);
             }
         }
       if (!addrlen)
@@ -189,9 +189,9 @@ int ares__get_hostent(FILE *fp, int family, struct hostent **host)
       if (!hostent->h_addr_list[0])
         break;
       if (addr.family == AF_INET)
-        memcpy(hostent->h_addr_list[0], &addr.addrV4, sizeof(struct in_addr));
+        memcpy(hostent->h_addr_list[0], &addr.addrV4, sizeof(addr.addrV4));
       else
-        memcpy(hostent->h_addr_list[0], &addr.addrV6, sizeof(struct in6_addr));
+        memcpy(hostent->h_addr_list[0], &addr.addrV6, sizeof(addr.addrV6));
 
       /* Copy aliases. */
       hostent->h_aliases = malloc((naliases + 1) * sizeof(char *));
index ee76018568c4e7aa2c616a9fa882f9bbe4833dd5..3a4c00c97d5fd2ce60bad01e5d741effdb129ce4 100644 (file)
@@ -1,6 +1,6 @@
 /* $Id$ */
 
-/* Copyright (C) 2009 by Daniel Stenberg
+/* Copyright (C) 2009-2010 by Daniel Stenberg
  *
  * Permission to use, copy, modify, and distribute this
  * software and its documentation for any purpose and without
@@ -34,6 +34,7 @@
 ** of c-ares functions returning pointers that must be free'ed using this
 ** function is:
 **
+**   ares_get_servers()
 **   ares_parse_srv_reply()
 **   ares_parse_txt_reply()
 */
@@ -78,6 +79,12 @@ void ares_free_data(void *dataptr)
           free(ptr->data.txt_reply.txt);
         break;
 
+      case ARES_DATATYPE_ADDR_NODE:
+
+        if (ptr->data.addr_node.next)
+          ares_free_data(ptr->data.addr_node.next);
+        break;
+
       default:
         return;
     }
@@ -121,6 +128,12 @@ void *ares_malloc_data(ares_datatype type)
         ptr->data.txt_reply.length  = 0;
         break;
 
+      case ARES_DATATYPE_ADDR_NODE:
+        ptr->data.addr_node.next = NULL;
+        ptr->data.addr_node.family = 0;
+        memset(&ptr->data.addr_node.addrV6, 0,
+          sizeof(ptr->data.addr_node.addrV6));
+
       default:
         free(ptr);
         return NULL;
index f211dd733a81fc6fc9be8fc97929dfabb0dea126..02ff1bca522cf35f69004a1f5b451bd6cfc81b7b 100644 (file)
@@ -1,6 +1,6 @@
 /* $Id$ */
 
-/* Copyright (C) 2009 by Daniel Stenberg
+/* Copyright (C) 2009-2010 by Daniel Stenberg
  *
  * Permission to use, copy, modify, and distribute this
  * software and its documentation for any purpose and without
@@ -19,6 +19,7 @@ typedef enum {
   ARES_DATATYPE_UNKNOWN = 1,  /* unknown data type     - introduced in 1.7.0 */
   ARES_DATATYPE_SRV_REPLY,    /* struct ares_srv_reply - introduced in 1.7.0 */
   ARES_DATATYPE_TXT_REPLY,    /* struct ares_txt_reply - introduced in 1.7.0 */
+  ARES_DATATYPE_ADDR_NODE,    /* struct ares_addr_node - introduced in 1.7.1 */
 #if 0
   ARES_DATATYPE_ADDR6TTL,     /* struct ares_addrttl   */
   ARES_DATATYPE_ADDRTTL,      /* struct ares_addr6ttl  */
@@ -54,6 +55,7 @@ struct ares_data {
   union {
     struct ares_txt_reply txt_reply;
     struct ares_srv_reply srv_reply;
+    struct ares_addr_node addr_node;
   } data;
 };
 
index 2dfb694459243ffb227ff041e285ba64ff0c5e63..d9216668fc421196a804d1bdc94ca6fa9f89f70f 100644 (file)
@@ -1,6 +1,7 @@
 /* $Id$ */
 
 /* Copyright 1998 by the Massachusetts Institute of Technology.
+ * Copyright (C) 2004-2010 by Daniel Stenberg
  *
  * Permission to use, copy, modify, and distribute this
  * software and its documentation for any purpose and without
@@ -25,7 +26,8 @@ void ares_destroy_options(struct ares_options *options)
 {
   int i;
 
-  free(options->servers);
+  if(options->servers)
+    free(options->servers);
   for (i = 0; i < options->ndomains; i++)
     free(options->domains[i]);
   free(options->domains);
@@ -67,15 +69,7 @@ void ares_destroy(ares_channel channel)
     }
 #endif
 
-  if (channel->servers) {
-    for (i = 0; i < channel->nservers; i++)
-      {
-        struct server_state *server = &channel->servers[i];
-        ares__close_sockets(channel, server);
-        assert(ares__is_list_empty(&(server->queries_to_server)));
-      }
-    free(channel->servers);
-  }
+  ares__destroy_servers_state(channel);
 
   if (channel->domains) {
     for (i = 0; i < channel->ndomains; i++)
@@ -91,3 +85,22 @@ void ares_destroy(ares_channel channel)
 
   free(channel);
 }
+
+void ares__destroy_servers_state(ares_channel channel)
+{
+  struct server_state *server;
+  int i;
+
+  if (channel->servers)
+    {
+      for (i = 0; i < channel->nservers; i++)
+        {
+          server = &channel->servers[i];
+          ares__close_sockets(channel, server);
+          assert(ares__is_list_empty(&server->queries_to_server));
+        }
+      free(channel->servers);
+      channel->servers = NULL;
+    }
+  channel->nservers = -1;
+}
index f6c5e33b9f1d6b3c84d930ad4d32ab2dd1a5cfb8..2cb1f14423ae0d065f7ef93663e399c1ac90711d 100644 (file)
@@ -1,7 +1,7 @@
 .\" $Id$
 .\"
 .\" Copyright 1998 by the Massachusetts Institute of Technology.
-.\" Copyright (C) 2004-2009 by Daniel Stenberg
+.\" Copyright (C) 2004-2010 by Daniel Stenberg
 .\"
 .\" Permission to use, copy, modify, and distribute this
 .\" software and its documentation for any purpose and without
@@ -15,7 +15,7 @@
 .\" this software for any purpose.  It is provided "as is"
 .\" without express or implied warranty.
 .\"
-.TH ARES_FREE_DATA 3 "23 Nov 2009"
+.TH ARES_FREE_DATA 3 "5 March 2010"
 .SH NAME
 ares_free_data \- Free data allocated by several c-ares functions
 .SH SYNOPSIS
@@ -34,6 +34,11 @@ function frees one or more data structures allocated and returned
 by several c-ares functions. Specifically the data returned by the
 following list of functions must be deallocated using this function.
 .TP 5
+.B ares_get_servers(3)
+When used to free the data returned by ares_get_servers(3) this
+will free the whole linked list of ares_addr_node structures returned
+by ares_get_servers(3).
+.TP
 .B ares_parse_srv_reply(3)
 When used to free the data returned by ares_parse_srv_reply(3) this
 will free the whole linked list of ares_srv_reply structures returned
@@ -50,6 +55,7 @@ The ares_free_data() function does not return a value.
 .SH AVAILABILITY
 This function was first introduced in c-ares version 1.7.0.
 .SH SEE ALSO
+.BR ares_get_servers(3),
 .BR ares_parse_srv_reply(3),
 .BR ares_parse_txt_reply(3)
 .SH AUTHOR
@@ -57,4 +63,4 @@ Yang Tse
 .PP
 Copyright 1998 by the Massachusetts Institute of Technology.
 .br
-Copyright (C) 2004-2009 by Daniel Stenberg.
+Copyright (C) 2004-2010 by Daniel Stenberg.
diff --git a/ares/ares_get_servers.3 b/ares/ares_get_servers.3
new file mode 100644 (file)
index 0000000..6bd1600
--- /dev/null
@@ -0,0 +1,78 @@
+.\" $Id$
+.\"
+.\" Copyright 1998 by the Massachusetts Institute of Technology.
+.\" Copyright (C) 2008-2010 by Daniel Stenberg
+.\"
+.\" Permission to use, copy, modify, and distribute this
+.\" software and its documentation for any purpose and without
+.\" fee is hereby granted, provided that the above copyright
+.\" notice appear in all copies and that both that copyright
+.\" notice and this permission notice appear in supporting
+.\" documentation, and that the name of M.I.T. not be used in
+.\" advertising or publicity pertaining to distribution of the
+.\" software without specific, written prior permission.
+.\" M.I.T. makes no representations about the suitability of
+.\" this software for any purpose.  It is provided "as is"
+.\" without express or implied warranty.
+.\"
+.TH ARES_GET_SERVERS 3 "5 March 2010"
+.SH NAME
+ares_get_servers \- Retrieve name servers from an initialized ares_channel
+.SH SYNOPSIS
+.nf
+.B #include <ares.h>
+.PP
+.B int ares_get_servers(ares_channel \fIchannel\fP, struct ares_addr_node **\fIservers\fP)
+.fi
+.SH DESCRIPTION
+The \fBares_get_servers(3)\fP function retrieves name servers configuration
+from the
+channel data identified by
+.IR channel ,
+as a linked list of ares_addr_node structs storing a pointer to the first
+node at the address specified by
+.IR servers .
+
+Function caller may traverse the returned name server linked list, or may use
+it directly as suitable input for the \fBares_set_servers(3)\fP function, but
+shall not shrink or extend the list on its own.
+
+Each node of the name server linked list is stored in memory dynamically
+allocated and managed by c-ares. It is the caller's responsibility to free
+the resulting linked list, using \fBares_free_data(3)\fP , once the caller
+does not need it any longer.
+
+This function is capable of handling IPv4 and IPv6 name server
+addresses simultaneously, rendering \fBares_save_options(3)\fP with
+optmask \fBARES_OPT_SERVERS\fP functionally obsolete except for
+IPv4-only name server usage.
+
+.SH RETURN VALUES
+.B ares_get_servers(3)
+may return any of the following values:
+.TP 15
+.B ARES_SUCCESS
+The name servers configuration was successfuly retrieved
+.TP 15
+.B ARES_ENOMEM
+The memory was exhausted
+.TP 15
+.B ARES_ENODATA
+The channel data identified by 
+.IR channel
+was invalid.
+.SH SEE ALSO
+.BR ares_set_servers (3),
+.BR ares_init_options (3),
+.BR ares_save_options(3)
+.SH AVAILABILITY
+ares_get_servers(3) was added in c-ares 1.7.1
+.SH AUTHOR
+Implementation of this function and associated library internals are based
+on code, comments and feedback provided November and December of 2008 by
+Daniel Stenberg, Gregor Jasny, Phil Blundell and Yang Tse, December 2009
+by Cedric Bail, February 2010 by Jakub Hrozek. On March 2010 Yang Tse
+shuffled all the bits and this function popped out.
+.br
+Copyright 1998 by the Massachusetts Institute of Technology.
+Copyright (C) 2008-2010 by Daniel Stenberg
index cafb15e1d58758d603d49cf8d9d4c6e0bab681f7..4b1dad36dc4a50db466f83135707970bf85660d2 100644 (file)
@@ -79,8 +79,8 @@ void ares_gethostbyaddr(ares_channel channel, const void *addr, int addrlen,
       return;
     }
 
-  if ((family == AF_INET && addrlen != sizeof(struct in_addr)) ||
-      (family == AF_INET6 && addrlen != sizeof(struct in6_addr)))
+  if ((family == AF_INET && addrlen != sizeof(aquery->addr.addrV4)) ||
+      (family == AF_INET6 && addrlen != sizeof(aquery->addr.addrV6)))
     {
       callback(arg, ARES_ENOTIMP, 0, NULL);
       return;
@@ -94,9 +94,9 @@ void ares_gethostbyaddr(ares_channel channel, const void *addr, int addrlen,
     }
   aquery->channel = channel;
   if (family == AF_INET)
-    memcpy(&aquery->addr.addrV4, addr, sizeof(struct in_addr));
+    memcpy(&aquery->addr.addrV4, addr, sizeof(aquery->addr.addrV4));
   else
-    memcpy(&aquery->addr.addrV6, addr, sizeof(struct in6_addr));
+    memcpy(&aquery->addr.addrV6, addr, sizeof(aquery->addr.addrV6));
   aquery->addr.family = family;
   aquery->callback = callback;
   aquery->arg = arg;
@@ -152,13 +152,13 @@ static void addr_callback(void *arg, int status, int timeouts,
     {
       if (aquery->addr.family == AF_INET)
         {
-          addrlen = sizeof(struct in_addr);
+          addrlen = sizeof(aquery->addr.addrV4);
           status = ares_parse_ptr_reply(abuf, alen, &aquery->addr.addrV4,
                                         (int)addrlen, AF_INET, &host);
         }
       else
         {
-          addrlen = sizeof(struct in6_addr);
+          addrlen = sizeof(aquery->addr.addrV6);
           status = ares_parse_ptr_reply(abuf, alen, &aquery->addr.addrV6,
                                         (int)addrlen, AF_INET6, &host);
         }
@@ -241,12 +241,12 @@ static int file_lookup(struct ares_addr *addr, struct hostent **host)
         }
       if (addr->family == AF_INET)
         {
-          if (memcmp((*host)->h_addr, &addr->addrV4, sizeof(struct in_addr)) == 0)
+          if (memcmp((*host)->h_addr, &addr->addrV4, sizeof(addr->addrV4)) == 0)
             break;
         }
       else if (addr->family == AF_INET6)
         {
-          if (memcmp((*host)->h_addr, &addr->addrV6, sizeof(struct in6_addr)) == 0)
+          if (memcmp((*host)->h_addr, &addr->addrV6, sizeof(addr->addrV6)) == 0)
             break;
         }
       ares_free_hostent(*host);
index acf1a44d92f060ab6c79c11b21abc57799b42d04..bf88309c196a6d38226f80ace3463c00bb8989c0 100644 (file)
@@ -81,7 +81,7 @@ static void sort6_addresses(struct hostent *host,
                             const struct apattern *sortlist, int nsort);
 static int get_address_index(const struct in_addr *addr,
                              const struct apattern *sortlist, int nsort);
-static int get6_address_index(const struct in6_addr *addr,
+static int get6_address_index(const struct ares_in6_addr *addr,
                               const struct apattern *sortlist, int nsort);
 
 void ares_gethostbyname(ares_channel channel, const char *name, int family,
@@ -243,7 +243,7 @@ static int fake_hostent(const char *name, int family, ares_host_callback callbac
   char *addrs[2];
   int result = 0;
   struct in_addr in;
-  struct in6_addr in6;
+  struct ares_in6_addr in6;
 
   if (family == AF_INET || family == AF_INET6)
     {
@@ -284,7 +284,7 @@ static int fake_hostent(const char *name, int family, ares_host_callback callbac
     }
   else if (family == AF_INET6)
     {
-      hostent.h_length = (int)sizeof(struct in6_addr);
+      hostent.h_length = (int)sizeof(struct ares_in6_addr);
       addrs[0] = (char *)&in6;
     }
   /* Duplicate the name, to avoid a constness violation. */
@@ -467,7 +467,7 @@ static int get_address_index(const struct in_addr *addr,
 static void sort6_addresses(struct hostent *host, const struct apattern *sortlist,
                            int nsort)
 {
-  struct in6_addr a1, a2;
+  struct ares_in6_addr a1, a2;
   int i1, i2, ind1, ind2;
 
   /* This is a simple insertion sort, not optimized at all.  i1 walks
@@ -477,24 +477,24 @@ static void sort6_addresses(struct hostent *host, const struct apattern *sortlis
    */
   for (i1 = 0; host->h_addr_list[i1]; i1++)
     {
-      memcpy(&a1, host->h_addr_list[i1], sizeof(struct in6_addr));
+      memcpy(&a1, host->h_addr_list[i1], sizeof(struct ares_in6_addr));
       ind1 = get6_address_index(&a1, sortlist, nsort);
       for (i2 = i1 - 1; i2 >= 0; i2--)
         {
-          memcpy(&a2, host->h_addr_list[i2], sizeof(struct in6_addr));
+          memcpy(&a2, host->h_addr_list[i2], sizeof(struct ares_in6_addr));
           ind2 = get6_address_index(&a2, sortlist, nsort);
           if (ind2 <= ind1)
             break;
-          memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct in6_addr));
+          memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct ares_in6_addr));
         }
-      memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct in6_addr));
+      memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct ares_in6_addr));
     }
 }
 
 /* Find the first entry in sortlist which matches addr.  Return nsort
  * if none of them match.
  */
-static int get6_address_index(const struct in6_addr *addr,
+static int get6_address_index(const struct ares_in6_addr *addr,
                               const struct apattern *sortlist,
                               int nsort)
 {
index bb5ee08d70b496911cb87e360678ad21c17883ac..77752df025817bf020207a3c59b7a1cd466bfc95 100644 (file)
@@ -74,9 +74,11 @@ struct nameinfo_query {
 };
 
 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
-#define IPBUFSIZ 40+IF_NAMESIZE
+#define IPBUFSIZ \
+        (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") + IF_NAMESIZE)
 #else
-#define IPBUFSIZ 40
+#define IPBUFSIZ \
+        (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"))
 #endif
 
 static void nameinfo_callback(void *arg, int status, int timeouts, struct hostent *host);
@@ -184,14 +186,16 @@ void ares_getnameinfo(ares_channel channel, const struct sockaddr *sa,
           {
             niquery->family = AF_INET;
             memcpy(&niquery->addr.addr4, addr, sizeof(addr));
-            ares_gethostbyaddr(channel, &addr->sin_addr, sizeof(struct in_addr), AF_INET,
+            ares_gethostbyaddr(channel, &addr->sin_addr,
+                               sizeof(struct in_addr), AF_INET,
                                nameinfo_callback, niquery);
           }
         else
           {
             niquery->family = AF_INET6;
             memcpy(&niquery->addr.addr6, addr6, sizeof(addr6));
-            ares_gethostbyaddr(channel, &addr6->sin6_addr, sizeof(struct in6_addr), AF_INET6,
+            ares_gethostbyaddr(channel, &addr6->sin6_addr,
+                               sizeof(struct ares_in6_addr), AF_INET6,
                                nameinfo_callback, niquery);
           }
       }
index 596e485968cf9278a1a234c3b2f894449a7457b2..e24065154e12918931a51a2e36446d2cfd58054a 100644 (file)
@@ -1,7 +1,7 @@
 .\" $Id$
 .\"
 .\" Copyright 1998 by the Massachusetts Institute of Technology.
-.\" Copyright (C) 2004-2009 by Daniel Stenberg
+.\" Copyright (C) 2004-2010 by Daniel Stenberg
 .\"
 .\" Permission to use, copy, modify, and distribute this
 .\" software and its documentation for any purpose and without
@@ -15,7 +15,7 @@
 .\" this software for any purpose.  It is provided "as is"
 .\" without express or implied warranty.
 .\"
-.TH ARES_INIT 3 "26 May 2009"
+.TH ARES_INIT 3 "5 March 2010"
 .SH NAME
 ares_init, ares_init_options \- Initialize a resolver channel
 .SH SYNOPSIS
@@ -93,8 +93,11 @@ service port.
 .br
 .B int \fInservers\fP;
 .br
-The list of servers to contact, instead of the servers specified in
-resolv.conf or the local named.
+The list of IPv4 servers to contact, instead of the servers specified in
+resolv.conf or the local named. In order to allow specification of either
+IPv4 or IPv6 name servers, function
+.BR ares_set_servers(3)
+must be used instead.
 .TP 18
 .B ARES_OPT_DOMAINS
 .B char **\fIdomains\fP;
@@ -190,10 +193,11 @@ c-ares library initialization not yet performed.
 .SH SEE ALSO
 .BR ares_destroy(3),
 .BR ares_dup(3),
-.BR ares_library_init(3)
+.BR ares_library_init(3),
+.BR ares_set_servers(3)
 .SH AUTHOR
 Greg Hudson, MIT Information Systems
 .br
 Copyright 1998 by the Massachusetts Institute of Technology.
 .br
-Copyright (C) 2004-2009 by Daniel Stenberg.
+Copyright (C) 2004-2010 by Daniel Stenberg.
index 028b1453ee3a13cc66456fae236d1552479fc0fc..668dc9f65bd4450458a8c7400f5caa1505c05b8f 100644 (file)
@@ -1,7 +1,7 @@
 /* $Id$ */
 
 /* Copyright 1998 by the Massachusetts Institute of Technology.
- * Copyright (C) 2007-2009 by Daniel Stenberg
+ * Copyright (C) 2007-2010 by Daniel Stenberg
  *
  * Permission to use, copy, modify, and distribute this
  * software and its documentation for any purpose and without
@@ -118,7 +118,6 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options,
   ares_channel channel;
   int i;
   int status = ARES_SUCCESS;
-  struct server_state *server;
   struct timeval now;
 
 #ifdef CURLDEBUG
@@ -247,21 +246,7 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options,
   if ((channel->flags & ARES_FLAG_PRIMARY) && channel->nservers > 1)
     channel->nservers = 1;
 
-  /* Initialize server states. */
-  for (i = 0; i < channel->nservers; i++)
-    {
-      server = &channel->servers[i];
-      server->udp_socket = ARES_SOCKET_BAD;
-      server->tcp_socket = ARES_SOCKET_BAD;
-      server->tcp_connection_generation = ++channel->tcp_connection_generation;
-      server->tcp_lenbuf_pos = 0;
-      server->tcp_buffer = NULL;
-      server->qhead = NULL;
-      server->qtail = NULL;
-      ares__init_list_head(&(server->queries_to_server));
-      server->channel = channel;
-      server->is_broken = 0;
-    }
+  ares__init_servers_state(channel);
 
   *channelptr = channel;
   return ARES_SUCCESS;
@@ -272,7 +257,9 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options,
 int ares_dup(ares_channel *dest, ares_channel src)
 {
   struct ares_options opts;
-  int rc;
+  struct ares_addr_node *servers;
+  int ipv6_nservers = 0;
+  int i, rc;
   int optmask;
 
   *dest = NULL; /* in case of failure return NULL explicitly */
@@ -296,16 +283,33 @@ int ares_dup(ares_channel *dest, ares_channel src)
   (*dest)->sock_create_cb      = src->sock_create_cb;
   (*dest)->sock_create_cb_data = src->sock_create_cb_data;
 
+  /* Full name server cloning required when not all are IPv4 */
+  for (i = 0; i < src->nservers; i++)
+    {
+      if (src->servers[i].addr.family != AF_INET) {
+        ipv6_nservers++;
+        break;
+      }
+    }
+  if (ipv6_nservers) {
+    rc = ares_get_servers(src, &servers);
+    if (rc != ARES_SUCCESS)
+      return rc;
+    rc = ares_set_servers(*dest, servers);
+    ares_free_data(servers);
+    if (rc != ARES_SUCCESS)
+      return rc;
+  }
 
   return ARES_SUCCESS; /* everything went fine */
-
 }
 
 /* Save options from initialized channel */
 int ares_save_options(ares_channel channel, struct ares_options *options,
                       int *optmask)
 {
-  int i;
+  int i, j;
+  int ipv4_nservers = 0;
 
   /* Zero everything out */
   memset(options, 0, sizeof(struct ares_options));
@@ -335,16 +339,27 @@ int ares_save_options(ares_channel channel, struct ares_options *options,
   options->sock_state_cb     = channel->sock_state_cb;
   options->sock_state_cb_data = channel->sock_state_cb_data;
 
-  /* Copy servers */
+  /* Copy IPv4 servers */
   if (channel->nservers) {
-    options->servers =
-      malloc(channel->nservers * sizeof(struct server_state));
-    if (!options->servers && channel->nservers != 0)
-      return ARES_ENOMEM;
     for (i = 0; i < channel->nservers; i++)
-      options->servers[i] = channel->servers[i].addr;
+    {
+      if (channel->servers[i].addr.family == AF_INET)
+        ipv4_nservers++;
+    }
+    if (ipv4_nservers) {
+      options->servers = malloc(ipv4_nservers * sizeof(struct server_state));
+      if (!options->servers)
+        return ARES_ENOMEM;
+      for (i = j = 0; i < channel->nservers; i++)
+      {
+        if (channel->servers[i].addr.family == AF_INET)
+          memcpy(&options->servers[j++],
+                 &channel->servers[i].addr.addrV4,
+                 sizeof(channel->servers[i].addr.addrV4));
+      }
+    }
   }
-  options->nservers = channel->nservers;
+  options->nservers = ipv4_nservers;
 
   /* copy domains */
   if (channel->ndomains) {
@@ -420,7 +435,7 @@ static int init_by_options(ares_channel channel,
       && channel->socket_receive_buffer_size == -1)
     channel->socket_receive_buffer_size = options->socket_receive_buffer_size;
 
-  /* Copy the servers, if given. */
+  /* Copy the IPv4 servers, if given. */
   if ((optmask & ARES_OPT_SERVERS) && channel->nservers == -1)
     {
       /* Avoid zero size allocations at any cost */
@@ -431,7 +446,12 @@ static int init_by_options(ares_channel channel,
           if (!channel->servers)
             return ARES_ENOMEM;
           for (i = 0; i < options->nservers; i++)
-            channel->servers[i].addr = options->servers[i];
+            {
+              channel->servers[i].addr.family = AF_INET;
+              memcpy(&channel->servers[i].addr.addrV4,
+                     &options->servers[i],
+                     sizeof(channel->servers[i].addr.addrV4));
+            }
         }
       channel->nservers = options->nservers;
     }
@@ -1001,7 +1021,8 @@ static int init_by_defaults(ares_channel channel)
       rc = ARES_ENOMEM;
       goto error;
     }
-    channel->servers[0].addr.s_addr = htonl(INADDR_LOOPBACK);
+    channel->servers[0].addr.family = AF_INET;
+    channel->servers[0].addr.addrV4.s_addr = htonl(INADDR_LOOPBACK);
     channel->nservers = 1;
   }
 
@@ -1149,61 +1170,62 @@ static int config_lookup(ares_channel channel, const char *str,
 static int config_nameserver(struct server_state **servers, int *nservers,
                              char *str)
 {
-  struct in_addr addr;
+  struct ares_addr host;
   struct server_state *newserv;
+  char *p, *txtaddr;
   /* On Windows, there may be more than one nameserver specified in the same
-   * registry key, so we parse it as a space or comma seperated list.
+   * registry key, so we parse input as a space or comma seperated list.
    */
-#ifdef WIN32
-  char *p = str;
-  char *begin = str;
-  int more = 1;
-  while (more)
-  {
-    more = 0;
-    while (*p && !ISSPACE(*p) && *p != ',')
-      p++;
-
-    if (*p)
+  for (p = str; p;)
     {
-      *p = '\0';
-      more = 1;
-    }
+      /* Skip whitespace and commas. */
+      while (*p && (ISSPACE(*p) || (*p == ',')))
+        p++;
+      if (!*p)
+        /* No more input, done. */
+        break;
 
-    /* Skip multiple spaces or trailing spaces */
-    if (!*begin)
-    {
-      begin = ++p;
-      continue;
-    }
+      /* Pointer to start of IPv4 or IPv6 address part. */
+      txtaddr = p;
 
-    /* This is the part that actually sets the nameserver */
-    addr.s_addr = inet_addr(begin);
-    if (addr.s_addr == INADDR_NONE)
-      continue;
-    newserv = realloc(*servers, (*nservers + 1) * sizeof(struct server_state));
-    if (!newserv)
-      return ARES_ENOMEM;
-    newserv[*nservers].addr = addr;
-    *servers = newserv;
-    (*nservers)++;
+      /* Advance past this address. */
+      while (*p && !ISSPACE(*p) && (*p != ','))
+        p++;
+      if (*p)
+        /* Null terminate this address. */
+        *p++ = '\0';
+      else
+        /* Reached end of input, done when this address is processed. */
+        p = NULL;
+
+      /* Convert textual address to binary format. */
+      if (ares_inet_pton(AF_INET, txtaddr, &host.addrV4) == 1)
+        host.family = AF_INET;
+      else if (ares_inet_pton(AF_INET6, txtaddr, &host.addrV6) == 1)
+        host.family = AF_INET6;
+      else
+        continue;
+
+      /* Resize servers state array. */
+      newserv = realloc(*servers, (*nservers + 1) *
+                        sizeof(struct server_state));
+      if (!newserv)
+        return ARES_ENOMEM;
+
+      /* Store address data. */
+      newserv[*nservers].addr.family = host.family;
+      if (host.family == AF_INET)
+        memcpy(&newserv[*nservers].addr.addrV4, &host.addrV4,
+               sizeof(host.addrV4));
+      else
+        memcpy(&newserv[*nservers].addr.addrV6, &host.addrV6,
+               sizeof(host.addrV6));
+
+      /* Update arguments. */
+      *servers = newserv;
+      *nservers += 1;
+    }
 
-    if (!more)
-      break;
-    begin = ++p;
-  }
-#else
-  /* Add a nameserver entry, if this is a valid address. */
-  addr.s_addr = inet_addr(str);
-  if (addr.s_addr == INADDR_NONE)
-    return ARES_SUCCESS;
-  newserv = realloc(*servers, (*nservers + 1) * sizeof(struct server_state));
-  if (!newserv)
-    return ARES_ENOMEM;
-  newserv[*nservers].addr = addr;
-  *servers = newserv;
-  (*nservers)++;
-#endif
   return ARES_SUCCESS;
 }
 
@@ -1580,3 +1602,26 @@ void ares_set_socket_callback(ares_channel channel,
   channel->sock_create_cb = cb;
   channel->sock_create_cb_data = data;
 }
+
+void ares__init_servers_state(ares_channel channel)
+{
+  struct server_state *server;
+  int i;
+
+  for (i = 0; i < channel->nservers; i++)
+    {
+      server = &channel->servers[i];
+      server->udp_socket = ARES_SOCKET_BAD;
+      server->tcp_socket = ARES_SOCKET_BAD;
+      server->tcp_connection_generation = ++channel->tcp_connection_generation;
+      server->tcp_lenbuf_pos = 0;
+      server->tcp_buffer_pos = 0;
+      server->tcp_buffer = NULL;
+      server->tcp_length = 0;
+      server->qhead = NULL;
+      server->qtail = NULL;
+      ares__init_list_head(&server->queries_to_server);
+      server->channel = channel;
+      server->is_broken = 0;
+    }
+}
index e8229dfcada14a7226ab331580463d15052e0369..5bb7b8cf7b5b1ca45a974ed249f86ce8e845382a 100644 (file)
 #endif
 
 #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
 
 #ifndef HAVE_STRUCT_SOCKADDR_IN6
 struct sockaddr_in6
 {
-  unsigned short  sin6_family;
-  unsigned short  sin6_port;
-  unsigned long   sin6_flowinfo;
-  struct in6_addr sin6_addr;
-  unsigned int    sin6_scope_id;
+  unsigned short       sin6_family;
+  unsigned short       sin6_port;
+  unsigned long        sin6_flowinfo;
+  struct ares_in6_addr sin6_addr;
+  unsigned int         sin6_scope_id;
 };
 #endif
 
diff --git a/ares/ares_options.c b/ares/ares_options.c
new file mode 100644 (file)
index 0000000..0c87974
--- /dev/null
@@ -0,0 +1,128 @@
+/* $Id$ */
+
+/* Copyright 1998 by the Massachusetts Institute of Technology.
+ * Copyright (C) 2008-2010 by Daniel Stenberg
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ */
+
+
+#include "ares_setup.h"
+
+#include "ares.h"
+#include "ares_data.h"
+#include "ares_private.h"
+
+
+int ares_get_servers(ares_channel channel,
+                     struct ares_addr_node **servers)
+{
+  struct ares_addr_node *srvr_head = NULL;
+  struct ares_addr_node *srvr_last = NULL;
+  struct ares_addr_node *srvr_curr;
+  int status = ARES_SUCCESS;
+  int i;
+
+  if (!channel)
+    return ARES_ENODATA;
+
+  for (i = 0; i < channel->nservers; i++)
+    {
+      /* Allocate storage for this server node appending it to the list */
+      srvr_curr = ares_malloc_data(ARES_DATATYPE_ADDR_NODE);
+      if (!srvr_curr)
+        {
+          status = ARES_ENOMEM;
+          break;
+        }
+      if (srvr_last)
+        {
+          srvr_last->next = srvr_curr;
+        }
+      else
+        {
+          srvr_head = srvr_curr;
+        }
+      srvr_last = srvr_curr;
+
+      /* Fill this server node data */
+      srvr_curr->family = channel->servers[i].addr.family;
+      if (srvr_curr->family == AF_INET)
+        memcpy(&srvr_curr->addrV4, &channel->servers[i].addr.addrV4,
+               sizeof(srvr_curr->addrV4));
+      else
+        memcpy(&srvr_curr->addrV6, &channel->servers[i].addr.addrV6,
+               sizeof(srvr_curr->addrV6));
+    }
+
+  if (status != ARES_SUCCESS)
+    {
+      if (srvr_head)
+        {
+          ares_free_data(srvr_head);
+          srvr_head = NULL;
+        }
+    }
+
+  *servers = srvr_head;
+
+  return status;
+}
+
+
+int ares_set_servers(ares_channel channel,
+                     struct ares_addr_node *servers)
+{
+  struct ares_addr_node *srvr;
+  int num_srvrs = 0;
+  int i;
+
+  if (ares_library_initialized() != ARES_SUCCESS)
+    return ARES_ENOTINITIALIZED;
+
+  if (!channel)
+    return ARES_ENODATA;
+
+  ares__destroy_servers_state(channel);
+
+  for (srvr = servers; srvr; srvr = srvr->next)
+    {
+      num_srvrs++;
+    }
+
+  if (num_srvrs > 0)
+    {
+      /* Allocate storage for servers state */
+      channel->servers = malloc(num_srvrs * sizeof(struct server_state));
+      if (!channel->servers)
+        {
+          return ARES_ENOMEM;
+        }
+      channel->nservers = num_srvrs;
+      /* Fill servers state address data */
+      for (i = 0, srvr = servers; srvr; i++, srvr = srvr->next)
+        {
+          channel->servers[i].addr.family = srvr->family;
+          if (srvr->family == AF_INET)
+            memcpy(&channel->servers[i].addr.addrV4, &srvr->addrV4,
+                   sizeof(srvr->addrV4));
+          else
+            memcpy(&channel->servers[i].addr.addrV6, &srvr->addrV6,
+                   sizeof(srvr->addrV6));
+        }
+      /* Initialize servers state remaining data */
+      ares__init_servers_state(channel);
+    }
+
+  return ARES_SUCCESS;
+}
index 6d54667554e965215e825918fd4251c2d5bf6cf5..8c2843f472ee81a5a94f2f33b8e8bf989a00da8e 100644 (file)
@@ -65,7 +65,7 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen,
   long len;
   const unsigned char *aptr;
   char *hostname, *rr_name, *rr_data, **aliases;
-  struct in6_addr *addrs;
+  struct ares_in6_addr *addrs;
   struct hostent *hostent;
   const int max_addr_ttls = (addrttls && naddrttls) ? *naddrttls : 0;
 
@@ -101,7 +101,7 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen,
   /* Allocate addresses and aliases; ancount gives an upper bound for both. */
   if (host)
     {
-      addrs = malloc(ancount * sizeof(struct in6_addr));
+      addrs = malloc(ancount * sizeof(struct ares_in6_addr));
       if (!addrs)
         {
           free(hostname);
@@ -143,27 +143,27 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen,
       aptr += RRFIXEDSZ;
 
       if (rr_class == C_IN && rr_type == T_AAAA
-          && rr_len == sizeof(struct in6_addr)
+          && rr_len == sizeof(struct ares_in6_addr)
           && strcasecmp(rr_name, hostname) == 0)
         {
           if (addrs)
             {
-              if (aptr + sizeof(struct in6_addr) > abuf + alen)
+              if (aptr + sizeof(struct ares_in6_addr) > abuf + alen)
               {
                 status = ARES_EBADRESP;
                 break;
               }
-              memcpy(&addrs[naddrs], aptr, sizeof(struct in6_addr));
+              memcpy(&addrs[naddrs], aptr, sizeof(struct ares_in6_addr));
             }
           if (naddrs < max_addr_ttls)
             {
               struct ares_addr6ttl * const at = &addrttls[naddrs];
-              if (aptr + sizeof(struct in6_addr) > abuf + alen)
+              if (aptr + sizeof(struct ares_in6_addr) > abuf + alen)
               {
                 status = ARES_EBADRESP;
                 break;
               }
-              memcpy(&at->ip6addr, aptr,  sizeof(struct in6_addr));
+              memcpy(&at->ip6addr, aptr,  sizeof(struct ares_in6_addr));
               at->ttl = rr_ttl;
             }
           naddrs++;
@@ -233,7 +233,7 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen,
                   hostent->h_name = hostname;
                   hostent->h_aliases = aliases;
                   hostent->h_addrtype = AF_INET6;
-                  hostent->h_length = sizeof(struct in6_addr);
+                  hostent->h_length = sizeof(struct ares_in6_addr);
                   for (i = 0; i < naddrs; i++)
                     hostent->h_addr_list[i] = (char *) &addrs[i];
                   hostent->h_addr_list[naddrs] = NULL;
index 37f3967e8ded7bb6dc6464dda65746552087ef22..2766d191646628b7a29da902a7afaae1212d7ba1 100644 (file)
@@ -4,7 +4,7 @@
 /* $Id$ */
 
 /* Copyright 1998 by the Massachusetts Institute of Technology.
- * Copyright (C) 2004-2009 by Daniel Stenberg
+ * Copyright (C) 2004-2010 by Daniel Stenberg
  *
  * Permission to use, copy, modify, and distribute this
  * software and its documentation for any purpose and without
 struct ares_addr {
   int family;
   union {
-    struct in_addr  addr4;
-    struct in6_addr addr6;
+    struct in_addr       addr4;
+    struct ares_in6_addr addr6;
   } addr;
 };
 #define addrV4 addr.addr4
@@ -137,7 +137,7 @@ struct send_request {
 };
 
 struct server_state {
-  struct in_addr addr;
+  struct ares_addr addr;
   ares_socket_t udp_socket;
   ares_socket_t tcp_socket;
 
@@ -221,14 +221,14 @@ struct query_server_info {
 struct apattern {
   union
   {
-    struct in_addr  addr4;
-    struct in6_addr addr6;
+    struct in_addr       addr4;
+    struct ares_in6_addr addr6;
   } addr;
   union
   {
-    struct in_addr  addr4;
-    struct in6_addr addr6;
-    unsigned short  bits;
+    struct in_addr       addr4;
+    struct ares_in6_addr addr6;
+    unsigned short       bits;
   } mask;
   int family;
   unsigned short type;
@@ -319,6 +319,8 @@ struct timeval ares__tvnow(void);
 int ares__expand_name_for_response(const unsigned char *encoded,
                                    const unsigned char *abuf, int alen,
                                    char **s, long *enclen);
+void ares__init_servers_state(ares_channel channel);
+void ares__destroy_servers_state(ares_channel channel);
 #if 0 /* Not used */
 long ares__tvdiff(struct timeval t1, struct timeval t2);
 #endif
index 01036f5beabece6b99711fc265ffeadf20f38b2e..ab0b79dc6893140ade8242ef6f8f951a6153e668 100644 (file)
@@ -97,6 +97,7 @@ static int open_tcp_socket(ares_channel channel, struct server_state *server);
 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(struct sockaddr *sa, struct ares_addr *aa);
 static void end_query(ares_channel channel, struct query *query, int status,
                       unsigned char *abuf, int alen);
 
@@ -434,8 +435,11 @@ static void read_udp_packets(ares_channel channel, fd_set *read_fds,
   ssize_t count;
   unsigned char buf[PACKETSZ + 1];
 #ifdef HAVE_RECVFROM
-  struct sockaddr_in from;
   ares_socklen_t fromlen;
+  union {
+    struct sockaddr_in  sa4;
+    struct sockaddr_in6 sa6;
+  } from;
 #endif
 
   if(!read_fds && (read_fd == ARES_SOCKET_BAD))
@@ -471,7 +475,10 @@ static void read_udp_packets(ares_channel channel, fd_set *read_fds,
        * packets as we can. */
       do {
 #ifdef HAVE_RECVFROM
-        fromlen = sizeof(from);
+        if (server->addr.family == AF_INET)
+          fromlen = sizeof(from.sa4);
+        else
+          fromlen = sizeof(from.sa6);
         count = (ssize_t)recvfrom(server->udp_socket, (void *)buf, sizeof(buf),
                                   0, (struct sockaddr *)&from, &fromlen);
 #else
@@ -482,10 +489,10 @@ static void read_udp_packets(ares_channel channel, fd_set *read_fds,
         else if (count <= 0)
           handle_error(channel, i, now);
 #ifdef HAVE_RECVFROM
-        else if (from.sin_addr.s_addr != server->addr.s_addr)
-          /* Address response came from did not match the address
-           * we sent the request to.  Someone may be attempting
-           * to perform a cache poisoning attack */
+        else if (!same_address((struct sockaddr *)&from, &server->addr))
+          /* The address the response comes from does not match
+           * the address we sent the request to. Someone may be
+           * attempting to perform a cache poisoning attack. */
           break;
 #endif
         else
@@ -893,10 +900,39 @@ static int open_tcp_socket(ares_channel channel, struct server_state *server)
 {
   ares_socket_t s;
   int opt;
-  struct sockaddr_in sockin;
+  ares_socklen_t salen;
+  union {
+    struct sockaddr_in  sa4;
+    struct sockaddr_in6 sa6;
+  } saddr;
+  struct sockaddr *sa;
+
+  switch (server->addr.family)
+    {
+      case AF_INET:
+        sa = (void *)&saddr.sa4;
+        salen = sizeof(saddr.sa4);
+        memset(sa, 0, salen);
+        saddr.sa4.sin_family = AF_INET;
+        saddr.sa4.sin_port = (unsigned short)(channel->tcp_port & 0xffff);
+        memcpy(&saddr.sa4.sin_addr, &server->addr.addrV4,
+               sizeof(server->addr.addrV4));
+        break;
+      case AF_INET6:
+        sa = (void *)&saddr.sa6;
+        salen = sizeof(saddr.sa6);
+        memset(sa, 0, salen);
+        saddr.sa6.sin6_family = AF_INET6;
+        saddr.sa6.sin6_port = (unsigned short)(channel->tcp_port & 0xffff);
+        memcpy(&saddr.sa6.sin6_addr, &server->addr.addrV6,
+               sizeof(server->addr.addrV6));
+        break;
+      default:
+        return -1;
+    }
 
   /* Acquire a socket. */
-  s = socket(AF_INET, SOCK_STREAM, 0);
+  s = socket(server->addr.family, SOCK_STREAM, 0);
   if (s == ARES_SOCKET_BAD)
     return -1;
 
@@ -924,11 +960,7 @@ static int open_tcp_socket(ares_channel channel, struct server_state *server)
 #endif
 
   /* 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, salen) == -1)
     {
       int err = SOCKERRNO;
 
@@ -960,10 +992,39 @@ static int open_tcp_socket(ares_channel channel, struct server_state *server)
 static int open_udp_socket(ares_channel channel, struct server_state *server)
 {
   ares_socket_t s;
-  struct sockaddr_in sockin;
+  ares_socklen_t salen;
+  union {
+    struct sockaddr_in  sa4;
+    struct sockaddr_in6 sa6;
+  } saddr;
+  struct sockaddr *sa;
+
+  switch (server->addr.family)
+    {
+      case AF_INET:
+        sa = (void *)&saddr.sa4;
+        salen = sizeof(saddr.sa4);
+        memset(sa, 0, salen);
+        saddr.sa4.sin_family = AF_INET;
+        saddr.sa4.sin_port = (unsigned short)(channel->udp_port & 0xffff);
+        memcpy(&saddr.sa4.sin_addr, &server->addr.addrV4,
+               sizeof(server->addr.addrV4));
+        break;
+      case AF_INET6:
+        sa = (void *)&saddr.sa6;
+        salen = sizeof(saddr.sa6);
+        memset(sa, 0, salen);
+        saddr.sa6.sin6_family = AF_INET6;
+        saddr.sa6.sin6_port = (unsigned short)(channel->udp_port & 0xffff);
+        memcpy(&saddr.sa6.sin6_addr, &server->addr.addrV6,
+               sizeof(server->addr.addrV6));
+        break;
+      default:
+        return -1;
+    }
 
   /* Acquire a socket. */
-  s = socket(AF_INET, SOCK_DGRAM, 0);
+  s = socket(server->addr.family, SOCK_DGRAM, 0);
   if (s == ARES_SOCKET_BAD)
     return -1;
 
@@ -975,11 +1036,7 @@ static int open_udp_socket(ares_channel channel, struct server_state *server)
     }
 
   /* 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, salen) == -1)
     {
       int err = SOCKERRNO;
 
@@ -1086,6 +1143,34 @@ static int same_questions(const unsigned char *qbuf, int qlen,
   return 1;
 }
 
+static int same_address(struct sockaddr *sa, struct ares_addr *aa)
+{
+  void *addr1;
+  void *addr2;
+
+  if (sa->sa_family == aa->family)
+    {
+      switch (aa->family)
+        {
+          case AF_INET:
+            addr1 = &aa->addrV4;
+            addr2 = &((struct sockaddr_in *)sa)->sin_addr;
+            if (memcmp(addr1, addr2, sizeof(aa->addrV4)) == 0)
+              return 1; /* match */
+            break;
+          case AF_INET6:
+            addr1 = &aa->addrV6;
+            addr2 = &((struct sockaddr_in6 *)sa)->sin6_addr;
+            if (memcmp(addr1, addr2, sizeof(aa->addrV6)) == 0)
+              return 1; /* match */
+            break;
+          default:
+            break;
+        }
+    }
+  return 0; /* different */
+}
+
 static void end_query (ares_channel channel, struct query *query, int status,
                        unsigned char *abuf, int alen)
 {
index 3434fc98b188cc9c5fa556081d075c095630ec4d..0dd4b4ecbfdf3e9ab1e75d726f2068fdbbb2f874 100644 (file)
@@ -14,7 +14,7 @@
 .\" this software for any purpose.  It is provided "as is"
 .\" without express or implied warranty.
 .\"
-.TH ARES_SAVE_OPTIONS 3 "1 June 2007"
+.TH ARES_SAVE_OPTIONS 3 "5 March 2010"
 .SH NAME
 ares_save_options \- Save configuration values obtained from initialized ares_channel
 .SH SYNOPSIS
@@ -52,13 +52,20 @@ The channel data identified by
 were invalid.
 .SH NOTE
 Since c-ares 1.6.0 the ares_options struct has been "locked" meaning that it
-won't be extended to cover new funtions. This function will remain
+won't be extended to cover new functions. This function will remain
 functioning, but it can only return config data that can be represented in
 this config struct, which may no longer be the complete set of config
 options. \fBares_dup(3)\fP will not have that restriction.
+
+The ares_options struct can not handle potential IPv6 name servers the
+ares_channel might be configured to use. Function \fBares_save_options(3)\fP
+will only return IPv4 servers if any. In order to retrieve all name servers
+an ares_channel might be using, function \fBares_get_servers(3)\fP must be
+used instead.
 .SH SEE ALSO
 .BR ares_destroy_options (3),
 .BR ares_init_options (3),
+.BR ares_get_servers (3),
 .BR ares_dup (3)
 .SH AVAILABILITY
 ares_save_options(3) was added in c-ares 1.4.0
diff --git a/ares/ares_set_servers.3 b/ares/ares_set_servers.3
new file mode 100644 (file)
index 0000000..98c30b8
--- /dev/null
@@ -0,0 +1,84 @@
+.\" $Id$
+.\"
+.\" Copyright 1998 by the Massachusetts Institute of Technology.
+.\" Copyright (C) 2008-2010 by Daniel Stenberg
+.\"
+.\" Permission to use, copy, modify, and distribute this
+.\" software and its documentation for any purpose and without
+.\" fee is hereby granted, provided that the above copyright
+.\" notice appear in all copies and that both that copyright
+.\" notice and this permission notice appear in supporting
+.\" documentation, and that the name of M.I.T. not be used in
+.\" advertising or publicity pertaining to distribution of the
+.\" software without specific, written prior permission.
+.\" M.I.T. makes no representations about the suitability of
+.\" this software for any purpose.  It is provided "as is"
+.\" without express or implied warranty.
+.\"
+.TH ARES_SET_SERVERS 3 "5 March 2010"
+.SH NAME
+ares_set_servers \- Initialize an ares_channel name servers configuration
+.SH SYNOPSIS
+.nf
+.B #include <ares.h>
+.PP
+.B int ares_set_servers(ares_channel \fIchannel\fP, struct ares_addr_node *\fIservers\fP)
+.fi
+.SH DESCRIPTION
+The \fBares_set_servers(3)\fP function initializes name servers configuration
+for the channel data identified by
+.IR channel ,
+from a
+.IR servers
+pointer to a linked list of ares_addr_node structs holding name servers
+address data.
+
+The name server linked list pointer argument may be the result of a previous
+call to \fBares_get_servers(3)\fP or a linked list of ares_addr_node structs
+setup by other means.
+
+This function replaces any potentially previously configured name servers
+with the ones given in the linked list. So, in order to configure a channel
+with more than one name server all the desired ones must be specified in a 
+single list.
+
+\fBares_set_servers(3)\fP does not take ownership of the linked list argument.
+The caller is responsible to free the linked list when no longer needed.
+
+This function is capable of handling IPv4 and IPv6 name server
+addresses simultaneously, rendering \fBares_init_options(3)\fP with
+optmask \fBARES_OPT_SERVERS\fP functionally obsolete except for
+IPv4-only name server usage.
+
+.SH RETURN VALUES
+.B ares_set_servers(3)
+may return any of the following values:
+.TP 15
+.B ARES_SUCCESS
+The name servers configuration was successfuly initialized.
+.TP 15
+.B ARES_ENOMEM
+The process's available memory was exhausted.
+.TP 15
+.B ARES_ENODATA
+The channel data identified by 
+.IR channel
+was invalid.
+.TP 15
+.B ARES_ENOTINITIALIZED
+c-ares library initialization not yet performed.
+.SH SEE ALSO
+.BR ares_get_servers (3),
+.BR ares_init_options (3),
+.BR ares_dup(3)
+.SH AVAILABILITY
+ares_set_servers(3) was added in c-ares 1.7.1
+.SH AUTHOR
+Implementation of this function and associated library internals are based
+on code, comments and feedback provided November and December of 2008 by
+Daniel Stenberg, Gregor Jasny, Phil Blundell and Yang Tse, December 2009
+by Cedric Bail, February 2010 by Jakub Hrozek. On March 2010 Yang Tse
+shuffled all the bits and this function popped out.
+.br
+Copyright 1998 by the Massachusetts Institute of Technology.
+Copyright (C) 2008-2010 by Daniel Stenberg
index 1e278a7930cabbb2d1742ad62e4b05b163e99787..9c4717ab1aa2a0f009dbe2a81c0d59781f1186cf 100644 (file)
@@ -43,6 +43,7 @@
 #include <string.h>
 #include <stdlib.h>
 
+#include "ares.h"
 #include "ares_ipv6.h"
 #include "inet_net_pton.h"
 
@@ -432,7 +433,7 @@ int ares_inet_pton(int af, const char *src, void *dst)
   if (af == AF_INET)
     size = sizeof(struct in_addr);
   else if (af == AF_INET6)
-    size = sizeof(struct in6_addr);
+    size = sizeof(struct ares_in6_addr);
   else
   {
     SET_ERRNO(EAFNOSUPPORT);
index b3061774eb00128651e4fc25efa6e07d5357d7e1..3b35397804f0c4b7a0e23619ade2b7e690dc5e1a 100644 (file)
@@ -42,6 +42,7 @@
 #include <string.h>
 #include <stdlib.h>
 
+#include "ares.h"
 #include "ares_ipv6.h"
 #include "inet_ntop.h"
 
index 696254334f126cd6be0814d76a0e3acb6d578b54..ca84f8090b5484c818d1e3b362b230b5a86dcbd0 100644 (file)
@@ -230,6 +230,10 @@ SOURCE=..\..\ares_nowarn.c
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=..\..\ares_options.c\r
+# End Source File\r
+# Begin Source File\r
+\r
 SOURCE=..\..\ares_parse_a_reply.c\r
 # End Source File\r
 # Begin Source File\r