Subject: [PATCH] save ares channel options for caching purposes

[PATCH] save ares channel options for caching purposes

From: Brad House <brad_at_mainstreetsoftworks.com>
Date: Mon, 14 May 2007 17:54:22 -0400

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.

        Basic usage:
        {
                ares_channel ch;
                struct ares_options opt;
                int optmask;

                /* Save config options */
                ares_init(&ch);
                ares_save_options(ch, &opt, &optmask);
                ares_destroy(ch);

                /* Reload saved configs */
                ares_init_options(&ch, &opt, optmask);
                ...
                ares_destroy(ch);

                /* Cleanup */
                ares_destroy_options(&opt);
        }
                

Patch Notes:
  * Had to add an ARES_CONFIG_CHECK() macro which gets called within
    init_by_resolv_conf() to force Unix systems to not re-read the
    resolv.conf and friends.
  * Tagged the 'options' parameter for init_by_options() and
    ares_init_options() as const since they are not modified by
    those functions.
  * If ares_save_options() returns anything other than ARES_SUCCESS,
    you need to call ares_destroy_options() to ensure any associated
    memory is free()'d.
  * Sortlist support was added to the options structure and
    init_by_options() function.

Patch is attached. Questions, Concerns, Thoughts for improvement are
all appreciated.

Thanks.
-Brad

--- ares_destroy.c (revision 10830)
+++ ares_destroy.c (revision 10908)
@@ -20,4 +20,17 @@
 #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)
--- ares.h (revision 10830)
+++ ares.h (revision 10908)
@@ -95,4 +95,5 @@
 #define ARES_OPT_LOOKUPS (1 << 8)
 #define ARES_OPT_SOCK_STATE_CB (1 << 9)
+#define ARES_OPT_SORTLIST (1 << 10)
 
 /* Nameinfo flag values */
@@ -149,4 +150,6 @@
 #endif
 
+struct apattern;
+
 struct ares_options {
   int flags;
@@ -163,4 +166,6 @@
   ares_sock_state_cb sock_state_cb;
   void *sock_state_cb_data;
+ struct apattern *sortlist;
+ int nsort;
 };
 
@@ -178,6 +183,8 @@
 
 int ares_init(ares_channel *channelptr);
-int ares_init_options(ares_channel *channelptr, struct ares_options *options,
+int ares_init_options(ares_channel *channelptr, const 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);
--- ares_init.c (revision 10830)
+++ ares_init.c (revision 10908)
@@ -60,5 +60,5 @@
 #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);
@@ -83,4 +83,9 @@
 #endif
 
+#define ARES_CONFIG_CHECK() (channel->lookups && channel->nsort > -1 && \
+ channel->nservers > -1 && channel->ndomains > -1 && \
+ channel->ndots > -1 && channel->timeout > -1 && \
+ channel->tries > -1)
+
 int ares_init(ares_channel *channelptr)
 {
@@ -88,5 +93,5 @@
 }
 
-int ares_init_options(ares_channel *channelptr, struct ares_options *options,
+int ares_init_options(ares_channel *channelptr, const struct ares_options *options,
                       int optmask)
 {
@@ -188,5 +193,70 @@
 }
 
-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())
+ 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)
 {
@@ -248,4 +318,17 @@
       if (!channel->lookups)
         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;
     }
 
@@ -578,4 +661,8 @@
     int linesize;
 
+ /* Don't read resolv.conf and friends if we don't have to */
+ if (ARES_CONFIG_CHECK())
+ return ARES_SUCCESS;
+
     fp = fopen(PATH_RESOLV_CONF, "r");
     if (!fp)
Received on 2007-05-14