Brad House added ares_save_options() and ares_destroy_options() that can be
authorDaniel Stenberg <daniel@haxx.se>
Wed, 30 May 2007 20:49:14 +0000 (20:49 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 30 May 2007 20:49:14 +0000 (20:49 +0000)
used to keep options for later re-usal when ares_init_options() is used.

CHANGES
ares.h
ares_destroy.c
ares_init.c

diff --git a/CHANGES b/CHANGES
index 3f607b8..9f0a044 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,25 @@
 
 * May 30 2007
 
+- Brad House added ares_save_options() and ares_destroy_options() that can be
+  used to keep options for later re-usal when ares_init_options() is used.
+  
+  Problem: Calling ares_init() for each lookup can be unnecessarily resource
+         intensive.  On windows, it must LoadLibrary() or search the registry
+         on each call to ares_init().  On unix, it must read and parse
+         multiple files to obtain the necessary configuration information.  In
+         a single-threaded environment, it would make sense to only
+         ares_init() once, but in a heavily multi-threaded environment, it is
+         undesirable to ares_init() and ares_destroy() for each thread created
+         and track that.
+
+  Solution: Create ares_save_options() and ares_destroy_options() functions to
+         retrieve and free options obtained from an initialized channel.  The
+         options populated can be used to pass back into ares_init_options(),
+         it should populate all needed fields and not retrieve any information
+         from the system.  Probably wise to destroy the cache every minute or
+         so to prevent the data from becoming stale.
+
 - Daniel S added ares_process_fd() to allow applications to ask for processing
   on specific sockets and thus avoiding select() and associated
   functions/macros.  This function will be used by upcoming libcurl releases
diff --git a/ares.h b/ares.h
index 73fc3e6..6b94295 100644 (file)
--- a/ares.h
+++ b/ares.h
@@ -94,6 +94,7 @@ extern "C" {
 #define ARES_OPT_DOMAINS        (1 << 7)
 #define ARES_OPT_LOOKUPS        (1 << 8)
 #define ARES_OPT_SOCK_STATE_CB  (1 << 9)
+#define ARES_OPT_SORTLIST       (1 << 10)
 
 /* Nameinfo flag values */
 #define ARES_NI_NOFQDN                  (1 << 0)
@@ -164,6 +165,8 @@ typedef void (*ares_sock_state_cb)(void *data,
                                    int writable);
 #endif
 
+struct apattern;
+
 struct ares_options {
   int flags;
   int timeout;
@@ -178,6 +181,8 @@ struct ares_options {
   char *lookups;
   ares_sock_state_cb sock_state_cb;
   void *sock_state_cb_data;
+  struct apattern *sortlist;
+  int nsort;
 };
 
 struct hostent;
@@ -195,6 +200,8 @@ typedef void (*ares_nameinfo_callback)(void *arg, int status,
 int ares_init(ares_channel *channelptr);
 int ares_init_options(ares_channel *channelptr, struct ares_options *options,
                       int optmask);
+int ares_save_options(ares_channel channel, struct ares_options *options, int *optmask);
+void ares_destroy_options(struct ares_options *options);
 void ares_destroy(ares_channel channel);
 void ares_cancel(ares_channel channel);
 void ares_send(ares_channel channel, const unsigned char *qbuf, int qlen,
index fd243c3..8d9bdbc 100644 (file)
 #include "ares.h"
 #include "ares_private.h"
 
+void ares_destroy_options(struct ares_options *options)
+{
+  int i;
+
+  free(options->servers);
+  for (i = 0; i < options->ndomains; i++)
+    free(options->domains[i]);
+  free(options->domains);
+  if(options->sortlist)
+    free(options->sortlist);
+  free(options->lookups);
+}
+
 void ares_destroy(ares_channel channel)
 {
   int i;
index bfe6b90..d384f94 100644 (file)
@@ -61,7 +61,7 @@
 #undef WIN32  /* Redefined in MingW/MSVC headers */
 #endif
 
-static int init_by_options(ares_channel channel, struct ares_options *options,
+static int init_by_options(ares_channel channel, const struct ares_options *options,
                            int optmask);
 static int init_by_environment(ares_channel channel);
 static int init_by_resolv_conf(ares_channel channel);
@@ -84,6 +84,12 @@ static int config_sortlist(struct apattern **sortlist, int *nsort,
 static char *try_config(char *s, const char *opt);
 #endif
 
+#define ARES_CONFIG_CHECK(x) (x->lookups && x->nsort > -1 && \
+                            x->nservers > -1 && \
+                             x->ndomains > -1 && \
+                            x->ndots > -1 && x->timeout > -1 && \
+                            x->tries > -1)
+
 int ares_init(ares_channel *channelptr)
 {
   return ares_init_options(channelptr, NULL, 0);
@@ -212,7 +218,76 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options,
   return ARES_SUCCESS;
 }
 
-static int init_by_options(ares_channel channel, struct ares_options *options,
+/* Save options from initialized channel */
+int ares_save_options(ares_channel channel, struct ares_options *options,
+                      int *optmask)
+{
+  int i;
+
+  /* Zero everything out */
+  memset(options, 0, sizeof(struct ares_options));
+
+  if (!ARES_CONFIG_CHECK(channel))
+    return ARES_ENODATA;
+
+  (*optmask) = (ARES_OPT_FLAGS|ARES_OPT_TIMEOUT|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);
+
+  /* Copy easy stuff */
+  options->flags   = channel->flags;
+  options->timeout = channel->timeout;
+  options->tries   = channel->tries;
+  options->ndots   = channel->ndots;
+  options->udp_port = channel->udp_port;
+  options->tcp_port = channel->tcp_port;
+  options->sock_state_cb     = channel->sock_state_cb;
+  options->sock_state_cb_data = channel->sock_state_cb_data;
+
+  /* Copy servers */
+  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;
+  options->nservers = channel->nservers;
+
+  /* copy domains */
+  options->domains = malloc(channel->ndomains * sizeof(char *));
+  if (!options->domains)
+    return ARES_ENOMEM;
+  for (i = 0; i < channel->ndomains; i++)
+  {
+    options->ndomains = i;
+    options->domains[i] = strdup(channel->domains[i]);
+    if (!options->domains[i])
+      return ARES_ENOMEM;
+  }
+  options->ndomains = channel->ndomains;
+
+  /* copy lookups */
+  options->lookups = strdup(channel->lookups);
+  if (!options->lookups)
+    return ARES_ENOMEM;
+
+  /* copy sortlist */
+  options->sortlist = malloc(channel->nsort * sizeof(struct apattern));
+  if (!options->sortlist)
+    return ARES_ENOMEM;
+  for (i = 0; i < channel->nsort; i++)
+  {
+    memcpy(&(options->sortlist[i]), &(channel->sortlist[i]),
+           sizeof(struct apattern));
+  }
+  options->nsort = channel->nsort;
+
+  return ARES_SUCCESS;
+}
+
+static int init_by_options(ares_channel channel,
+                           const struct ares_options *options,
                            int optmask)
 {
   int i;
@@ -282,6 +357,19 @@ static int init_by_options(ares_channel channel, struct ares_options *options,
         return ARES_ENOMEM;
     }
 
+  /* copy sortlist */
+  if ((optmask & ARES_OPT_SORTLIST) && channel->nsort == -1)
+    {
+      channel->sortlist = malloc(options->nsort * sizeof(struct apattern));
+      if (!channel->sortlist)
+        return ARES_ENOMEM;
+      for (i = 0; i < options->nsort; i++)
+        {
+          memcpy(&(channel->sortlist[i]), &(options->sortlist[i]), sizeof(struct apattern));
+        }
+      channel->nsort = options->nsort;
+    }
+
   return ARES_SUCCESS;
 }
 
@@ -615,6 +703,10 @@ DhcpNameServer
     int linesize;
     int error;
 
+    /* Don't read resolv.conf and friends if we don't have to */
+    if (ARES_CONFIG_CHECK(channel))
+        return ARES_SUCCESS;
+
     fp = fopen(PATH_RESOLV_CONF, "r");
     if (fp) {
       while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS)