Module Name:    src
Committed By:   tsarna
Date:           Wed Nov  4 23:34:59 UTC 2009

Modified Files:
        src/external/apache2/mDNSResponder/nss: nss_mdnsd.c

Log Message:
Two new features:

- Detect and adapt to resolv.conf changes through the use of the new
  res_check() API, so that for example changing the search list doesn't
  require restarting clients.

- A persistent mdnsd connection pool, with slow start (so that
  programs like ping, ssh, etc don't keep connections open) and age-out.
  NOTE: Tuning parameter values are just SWAGs.


To generate a diff of this commit:
cvs rdiff -u -r1.2 -r1.3 src/external/apache2/mDNSResponder/nss/nss_mdnsd.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/external/apache2/mDNSResponder/nss/nss_mdnsd.c
diff -u src/external/apache2/mDNSResponder/nss/nss_mdnsd.c:1.2 src/external/apache2/mDNSResponder/nss/nss_mdnsd.c:1.3
--- src/external/apache2/mDNSResponder/nss/nss_mdnsd.c:1.2	Mon Oct 26 00:46:19 2009
+++ src/external/apache2/mDNSResponder/nss/nss_mdnsd.c	Wed Nov  4 23:34:59 2009
@@ -1,4 +1,4 @@
-/*	$NetBSD: nss_mdnsd.c,v 1.2 2009/10/26 00:46:19 tsarna Exp $	*/
+/*	$NetBSD: nss_mdnsd.c,v 1.3 2009/11/04 23:34:59 tsarna Exp $	*/
 
 /*-
  * Copyright (c) 2009 The NetBSD Foundation, Inc.
@@ -45,6 +45,7 @@
 #include <stdlib.h>  
 #include <sys/socket.h>
 #include <sys/param.h>
+#include <sys/queue.h>
 #include <netdb.h>
 #include <netinet/in.h>
 #include <arpa/nameser.h>
@@ -54,6 +55,51 @@
 #include <string.h>
 #include <stdio.h>
 #include <stdbool.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+
+
+/*
+ * Pool of mdnsd connections
+ */
+static SLIST_HEAD(, svc_ref) conn_list = LIST_HEAD_INITIALIZER(&conn_list);
+static unsigned int conn_count = 0;
+static pid_t my_pid;
+struct timespec last_config;
+
+typedef struct svc_ref {
+    SLIST_ENTRY(svc_ref)    entries;
+    DNSServiceRef           sdRef;
+    unsigned int            uses;
+} svc_ref;
+
+/*
+ * There is a large class of programs that do a few lookups at startup
+ * and then never again (ping, telnet, etc). Keeping a persistent connection
+ * for these would be a waste, so there is a kind of slow start mechanism.
+ * The first SLOWSTART_LOOKUPS times, dispose of the connection after use.
+ * After that we assume the program is a serious consumer of host lookup
+ * services and start keeping connections.
+ */
+#define SLOWSTART_LOOKUPS 5
+static unsigned int svc_puts = 0;
+
+/*
+ * Age out connections. Free connection instead of putting on the list
+ * if used more than REUSE_TIMES and there are others on the list.
+ */
+#define REUSE_TIMES          32
+ 
+/* protects above data */
+static pthread_mutex_t conn_list_lock = PTHREAD_MUTEX_INITIALIZER;
+
+extern int __isthreaded; /* libc private -- wish there was a better way */
+
+#define LOCK(x) do { if (__isthreaded) pthread_mutex_lock(x); } while (0)
+#define UNLOCK(x) do { if (__isthreaded) pthread_mutex_unlock(x); } while (0)
+
 
 #ifndef lint
 #define UNUSED(a)       (void)&a
@@ -85,9 +131,23 @@
 
 #define HCTX_BUFLEFT(c) (sizeof((c)->buf) - ((c)->next - (c)->buf))
 
+typedef struct res_conf {
+    unsigned int            refcount;
+    char                  **search_domains;
+    char                  **no_search;
+    short                   ndots;
+    short                   timeout;
+} res_conf;
+
+static res_conf *cur_res_conf;
+
+/* protects above data */
+static pthread_mutex_t res_conf_lock = PTHREAD_MUTEX_INITIALIZER;
+
 typedef struct search_iter {
+    res_conf       *conf;
     const char     *name;
-    char           **next_search;
+    char          **next_search;    
     size_t          baselen;
     bool            abs_first;
     bool            abs_last;
@@ -96,8 +156,6 @@
 
 static hostent_ctx h_ctx;
 static DNSServiceFlags svc_flags = 0;
-static int ndots = 1, timeout = 1000;
-static char **search_domains, **no_search;
 
 ns_mtab *nss_module_register(const char *, u_int *, nss_module_unregister_fn *);
 static int load_config(res_state);
@@ -107,12 +165,12 @@
 static int _mdns_gethtbyname(void *, void *, va_list);
 
 static int _mdns_getaddrinfo_abs(const char *, DNSServiceProtocol,
-    DNSServiceRef, addrinfo_ctx *);
+    svc_ref **, addrinfo_ctx *, short);
 static void _mdns_addrinfo_init(addrinfo_ctx *, const struct addrinfo *);
 static void _mdns_addrinfo_add_ai(addrinfo_ctx *, struct addrinfo *);
 static struct addrinfo *_mdns_addrinfo_done(addrinfo_ctx *);
 
-static int _mdns_gethtbyname_abs(const char *, int, DNSServiceRef);
+static int _mdns_gethtbyname_abs(const char *, int, svc_ref **, short);
 static void _mdns_hostent_init(hostent_ctx *, int, int);
 static void _mdns_hostent_add_host(hostent_ctx *, const char *);
 static void _mdns_hostent_add_addr(hostent_ctx *, const void *, uint16_t);
@@ -124,16 +182,26 @@
 static void _mdns_hostent_cb(DNSServiceRef, DNSServiceFlags,
     uint32_t, DNSServiceErrorType, const char *, uint16_t, uint16_t, uint16_t,
     const void *, uint32_t, void *);
-static void _mdns_eventloop(DNSServiceRef, callback_ctx *);
+static void _mdns_eventloop(svc_ref *, callback_ctx *, short);
 
 static char *_mdns_rdata2name(const unsigned char *, uint16_t,
     char *, size_t);
 
-void search_init(search_iter *, const char *);
-const char *search_next(search_iter *);
-bool searchable_domain(char *);
-
-
+static int search_init(search_iter *, const char *, const char **);
+static void search_done(search_iter *);
+static const char *search_next(search_iter *);
+static bool searchable_domain(char *);
+
+static void destroy_svc_ref(svc_ref *);
+static svc_ref *get_svc_ref(void);
+static void put_svc_ref(svc_ref *);
+static bool retry_query(svc_ref **, DNSServiceErrorType);
+
+static void decref_res_conf(res_conf *);
+static res_conf *get_res_conf(void);
+static void put_res_conf(res_conf *);
+static res_conf *new_res_conf(res_state);
+static short get_timeout(void);
 
 static ns_mtab mtab[] = {
     { NSDB_HOSTS, "getaddrinfo",    _mdns_getaddrinfo, NULL },
@@ -147,92 +215,32 @@
 nss_module_register(const char *source, u_int *nelems,
                     nss_module_unregister_fn *unreg)
 {
-    res_state res;
-
     *nelems = sizeof(mtab) / sizeof(mtab[0]);
     *unreg = NULL;
 
+    my_pid = getpid();
+
     if (!strcmp(source, "multicast_dns")) {
         svc_flags = kDNSServiceFlagsForceMulticast;
     }
 
-    res = __res_get_state();
-    if (res) {
-        load_config(res);
-        __res_put_state(res);
-    }
-
     return mtab;
 }
 
 
 
 static int
-load_config(res_state res)
-{
-    int count = 0;
-    char **sd;
-    
-    /* free old search list, if any */
-
-    if ((no_search = search_domains)) {
-        for (; *no_search; no_search++) {
-            free(*no_search);
-        }
-        
-        free(search_domains);
-    }
-
-    /* new search list */
-    
-    sd = res->dnsrch;
-    while (*sd) {
-        if (searchable_domain(*sd)) {
-            count++;
-        }
-        sd++;
-    }
-    
-    search_domains = calloc(sizeof(char *), count + 1);
-    if (!search_domains) {
-        return -1;
-    }
-    
-    sd = res->dnsrch;
-    no_search = search_domains;
-    while (*sd) {
-        if (searchable_domain(*sd)) {
-            *no_search = strdup(*sd);
-            no_search++;
-        }
-        sd++;
-    }
-
-    /* retrans in sec to timeout in msec */
-    timeout = res->retrans * 1000;
-
-    if (svc_flags & kDNSServiceFlagsForceMulticast) {
-        ndots = 1;
-        if (timeout > 2000) {
-            timeout = 2000;
-        }
-    } else {
-        ndots = res->ndots;
-    }
-}
-
-
-
-static int
 _mdns_getaddrinfo(void *cbrv, void *cbdata, va_list ap)
 {
     const struct addrinfo *pai;
     const char *name, *sname;
     DNSServiceProtocol proto;
-    int err = NS_UNAVAIL;
     DNSServiceRef sdRef;
     addrinfo_ctx ctx;
     search_iter iter;
+    res_conf *rc;
+    svc_ref *sr;
+    int err;
     
     UNUSED(cbdata);
     
@@ -257,30 +265,29 @@
         return NS_UNAVAIL;
     }
 
-    search_init(&iter, name);
-    sname = search_next(&iter);
-
-    if (!sname) {
-        h_errno = HOST_NOT_FOUND;
-        return NS_NOTFOUND;
-    }
-
-    /* use one connection for all searches */
-    if (DNSServiceCreateConnection(&sdRef)) {
+    sr = get_svc_ref();
+    if (!sr) {
         h_errno = NETDB_INTERNAL;
         return NS_UNAVAIL;
     }
     
+    if ((err = search_init(&iter, name, &sname)) != NS_SUCCESS) {
+        put_svc_ref(sr);
+        return err;
+    }
+    
     _mdns_addrinfo_init(&ctx, pai);
-
-    while (sname && (err != NS_SUCCESS)) {
-        err = _mdns_getaddrinfo_abs(sname, proto, sdRef, &ctx);
+    
+    err = NS_NOTFOUND;
+    while (sr && sname && (err != NS_SUCCESS)) {
+        err = _mdns_getaddrinfo_abs(sname, proto, &sr, &ctx, iter.conf->timeout);
         if (err != NS_SUCCESS) {
             sname = search_next(&iter);
         }
     };
 
-    DNSServiceRefDeallocate(sdRef);
+    search_done(&iter);
+    put_svc_ref(sr);
 
     if (err == NS_SUCCESS) {
         *(struct addrinfo **)cbrv = _mdns_addrinfo_done(&ctx);
@@ -293,25 +300,39 @@
 
 static int
 _mdns_getaddrinfo_abs(const char *name, DNSServiceProtocol proto,
-    DNSServiceRef sdRef, addrinfo_ctx *ctx)
+    svc_ref **sr, addrinfo_ctx *ctx, short timeout)
 {
-    DNSServiceErrorType err;
-
-    err = DNSServiceGetAddrInfo(
-        &sdRef,
-        svc_flags | kDNSServiceFlagsReturnIntermediates,
-        kDNSServiceInterfaceIndexAny,
-        proto,
-        name,
-        _mdns_addrinfo_cb,
-        ctx
-    );
+    DNSServiceErrorType err = kDNSServiceErr_ServiceNotRunning;
+    DNSServiceRef sdRef;
+    bool retry = true;
+    
+    while (*sr && retry) {
+        /* We must always use a copy of the ref when using a shared
+           connection, per kDNSServiceFlagsShareConnection docs */
+
+        sdRef = (*sr)->sdRef;
+
+        err = DNSServiceGetAddrInfo(
+            &sdRef,
+            svc_flags
+                | kDNSServiceFlagsShareConnection
+                | kDNSServiceFlagsReturnIntermediates,
+            kDNSServiceInterfaceIndexAny,
+            proto,
+            name,
+            _mdns_addrinfo_cb,
+            ctx
+        );
+        
+        retry = retry_query(sr, err);
+    }
 
     if (err) {
         h_errno = NETDB_INTERNAL;
         return NS_UNAVAIL;
     }
-    _mdns_eventloop(sdRef, (void *)ctx);
+    
+    _mdns_eventloop(*sr, (void *)ctx, timeout);
 
     DNSServiceRefDeallocate(sdRef);
 
@@ -334,6 +355,8 @@
     int advance, n;
     DNSServiceErrorType err;
     DNSServiceRef sdRef;
+    svc_ref *sr;
+    bool retry = true;
 
     UNUSED(cbdata);
     
@@ -392,25 +415,43 @@
     _mdns_hostent_init(&h_ctx, af, addrlen);
     _mdns_hostent_add_addr(&h_ctx, addr, addrlen);
 
-    err = DNSServiceQueryRecord(
-        &sdRef,
-        svc_flags,
-        kDNSServiceInterfaceIndexAny,
-        qbuf,
-        kDNSServiceType_PTR,
-        kDNSServiceClass_IN,
-        _mdns_hostent_cb,
-        &h_ctx
-    );
+    sr = get_svc_ref();
+    if (!sr) {
+        h_errno = NETDB_INTERNAL;
+        return NS_UNAVAIL;
+    }
+    
+    while (sr && retry) {
+        /* We must always use a copy of the ref when using a shared
+           connection, per kDNSServiceFlagsShareConnection docs */
+        sdRef = sr->sdRef;
+
+        err = DNSServiceQueryRecord(
+            &sdRef,
+            svc_flags
+                | kDNSServiceFlagsShareConnection
+                | kDNSServiceFlagsReturnIntermediates,
+            kDNSServiceInterfaceIndexAny,
+            qbuf,
+            kDNSServiceType_PTR,
+            kDNSServiceClass_IN,
+            _mdns_hostent_cb,
+            &h_ctx
+        );
+        
+        retry = retry_query(&sr, err);
+    }
 
     if (err) {
+        put_svc_ref(sr);
         h_errno = NETDB_INTERNAL;
         return NS_UNAVAIL;
     }
-    
-    _mdns_eventloop(sdRef, (void *)&h_ctx);
+
+    _mdns_eventloop(sr, (void *)&h_ctx, get_timeout());
 
     DNSServiceRefDeallocate(sdRef);
+    put_svc_ref(sr);
 
     if (h_ctx.naliases) {
         *(struct hostent **)cbrv = _mdns_hostent_done(&h_ctx);
@@ -427,10 +468,11 @@
 static int
 _mdns_gethtbyname(void *cbrv, void *cbdata, va_list ap)
 {
-    int namelen, af, addrlen, rrtype, err = NS_UNAVAIL;
+    int namelen, af, addrlen, rrtype, err;
     const char *name, *sname;
     DNSServiceRef sdRef;
     search_iter iter;
+    svc_ref *sr;
 
     UNUSED(cbdata);
     
@@ -456,31 +498,30 @@
         return NS_UNAVAIL;
     }
 
-    search_init(&iter, name);
-    sname = search_next(&iter);
-
-    if (!sname) {
-        h_errno = HOST_NOT_FOUND;
-        return NS_NOTFOUND;
-    }
-
-    /* use one connection for all searches */
-    if (DNSServiceCreateConnection(&sdRef)) {
+    sr = get_svc_ref();
+    if (!sr) {
         h_errno = NETDB_INTERNAL;
         return NS_UNAVAIL;
     }
     
+    if ((err = search_init(&iter, name, &sname)) != NS_SUCCESS) {
+        put_svc_ref(sr);
+        return err;
+    }
+
     _mdns_hostent_init(&h_ctx, af, addrlen);
 
-    while (sname && (err != NS_SUCCESS)) {
-        err = _mdns_gethtbyname_abs(sname, rrtype, sdRef);
+    err = NS_NOTFOUND;
+    while (sr && sname && (err != NS_SUCCESS)) {
+        err = _mdns_gethtbyname_abs(sname, rrtype, &sr, iter.conf->timeout);
         if (err != NS_SUCCESS) {
             sname = search_next(&iter);
         }
     };
 
-    DNSServiceRefDeallocate(sdRef);
-
+    search_done(&iter);
+    put_svc_ref(sr);
+    
     if (err == NS_SUCCESS) {
         _mdns_hostent_add_host(&h_ctx, sname);
         _mdns_hostent_add_host(&h_ctx, name);
@@ -493,27 +534,39 @@
 
 
 static int
-_mdns_gethtbyname_abs(const char *name, int rrtype, DNSServiceRef sdRef)
+_mdns_gethtbyname_abs(const char *name, int rrtype, svc_ref **sr, short timeout)
 {
-    DNSServiceErrorType err;
+    DNSServiceErrorType err = kDNSServiceErr_ServiceNotRunning;
+    DNSServiceRef sdRef;
+    bool retry = true;    
 
-    err = DNSServiceQueryRecord(
-        &sdRef,
-        svc_flags | kDNSServiceFlagsReturnIntermediates,
-        kDNSServiceInterfaceIndexAny,
-        name,
-        rrtype,
-        kDNSServiceClass_IN,
-        _mdns_hostent_cb,
-        &h_ctx
-    );
+    while (*sr && retry) {
+        /* We must always use a copy of the ref when using a shared
+           connection, per kDNSServiceFlagsShareConnection docs */
+        sdRef = (*sr)->sdRef;
+
+        err = DNSServiceQueryRecord(
+            &sdRef,
+            svc_flags
+                | kDNSServiceFlagsShareConnection
+                | kDNSServiceFlagsReturnIntermediates,
+            kDNSServiceInterfaceIndexAny,
+            name,
+            rrtype,
+            kDNSServiceClass_IN,
+            _mdns_hostent_cb,
+            &h_ctx
+        );
+        
+        retry = retry_query(sr, err);
+    }
 
     if (err) {
         h_errno = NETDB_INTERNAL;
         return NS_UNAVAIL;
     }
-    
-    _mdns_eventloop(sdRef, (void *)&h_ctx);
+
+    _mdns_eventloop(*sr, (void *)&h_ctx, timeout);
 
     DNSServiceRefDeallocate(sdRef);
 
@@ -771,19 +824,19 @@
 
 
 static void
-_mdns_eventloop(DNSServiceRef sdRef, callback_ctx *ctx)
+_mdns_eventloop(svc_ref *sr, callback_ctx *ctx, short timeout)
 {
     struct pollfd fds;
     int fd, ret;
 
-    fd = DNSServiceRefSockFD(sdRef);
+    fd = DNSServiceRefSockFD(sr->sdRef);
     fds.fd = fd;
     fds.events = POLLRDNORM;
 
     while (!ctx->done) {
-        ret = poll(&fds, 1, timeout);
+        ret = poll(&fds, 1, timeout * 1000);
         if (ret > 0) {
-            DNSServiceProcessResult(sdRef);
+            DNSServiceProcessResult(sr->sdRef);
         } else {
             break;
         }
@@ -859,17 +912,23 @@
 
 
 
-void
-search_init(search_iter *iter, const char *name)
+int
+search_init(search_iter *iter, const char *name, const char **first)
 {
     const char *c = name, *cmp;
     int dots = 0, enddot = 0;
     size_t len, cl;
+    
+    iter->conf = get_res_conf();
+    if (!iter->conf) {
+        h_errno = NETDB_INTERNAL;
+        return NS_UNAVAIL;
+    }
 
     iter->name = name;
     iter->baselen = 0;
     iter->abs_first = iter->abs_last = false;
-    iter->next_search = search_domains;
+    iter->next_search = iter->conf->search_domains;
         
     while (*c) {
         if (*c == '.') {
@@ -883,7 +942,7 @@
     
     if (svc_flags & kDNSServiceFlagsForceMulticast) {
         if (dots) {
-            iter->next_search = no_search;
+            iter->next_search = iter->conf->no_search;
             if ((dots - enddot) == 1) {
                 len = strlen(iter->name);
                 cl = strlen(".local") + enddot;
@@ -898,7 +957,7 @@
             }
         }
     } else {
-        if (dots >= ndots) {
+        if (dots >= iter->conf->ndots) {
             iter->abs_first = true;
         } else {
             iter->abs_last = true;
@@ -906,9 +965,18 @@
     
         if (enddot) {
             /* absolute; don't search */
-            iter->next_search = no_search;
+            iter->next_search = iter->conf->no_search;
         }
     }
+    
+    *first = search_next(iter);
+    if (!first) {
+        search_done(iter);
+        h_errno = HOST_NOT_FOUND;
+        return NS_NOTFOUND;
+    }
+    
+    return NS_SUCCESS;
 }
 
 
@@ -930,7 +998,7 @@
             iter->baselen = strlcpy(iter->buf, iter->name, sizeof(iter->buf));
             if (iter->baselen >= sizeof(iter->buf) - 1) {
                 /* original is too long, don't try any search domains */
-                iter->next_search = no_search;
+                iter->next_search = iter->conf->no_search;
                 break;
             }
 
@@ -961,6 +1029,17 @@
 
 
 
+void
+search_done(search_iter *iter)
+{
+    if (iter->conf) {
+        put_res_conf(iter->conf);
+        iter->conf = NULL;
+    }
+}
+
+
+
 /*
  * Is domain appropriate to be in the domain search list?
  * For mdnsd, take everything. For multicast_dns, only "local"
@@ -979,3 +1058,266 @@
     
     return false;
 }
+
+
+
+static void
+destroy_svc_ref(svc_ref *sr)
+{
+    /* assumes not on conn list */
+    
+    if (sr) {
+        DNSServiceRefDeallocate(sr->sdRef);
+        free(sr);
+    }
+}
+
+
+
+static svc_ref *
+get_svc_ref(void)
+{
+    svc_ref *sr;
+    
+    LOCK(&conn_list_lock);
+    
+    if (getpid() != my_pid) {
+        my_pid = getpid();
+
+        /*
+         * We forked and kept running. We don't want to share
+         * connections with the parent or we'll garble each others
+         * comms, so throw away the parent's list and start over
+         */
+        while ((sr = SLIST_FIRST(&conn_list))) {
+            SLIST_REMOVE_HEAD(&conn_list, entries);
+            destroy_svc_ref(sr);
+        }
+
+        sr = NULL;
+    } else {
+        /* try to recycle a connection */
+        sr = SLIST_FIRST(&conn_list);
+        if (sr) {
+            SLIST_REMOVE_HEAD(&conn_list, entries);
+            conn_count--;
+        }
+    }
+    
+    UNLOCK(&conn_list_lock);
+    
+    if (!sr) {
+        /* none available, we need a new one */
+
+        sr = calloc(sizeof(svc_ref), 1);
+        if (sr) {
+            if (DNSServiceCreateConnection(&(sr->sdRef))) {
+                free(sr);
+                return NULL;
+            }
+            
+            if (fcntl(DNSServiceRefSockFD(sr->sdRef), F_SETFD, FD_CLOEXEC) < 0) {
+                destroy_svc_ref(sr);
+                sr = NULL;
+            }
+        }
+    }
+
+    return sr;
+}
+
+
+
+static void
+put_svc_ref(svc_ref *sr)
+{
+    if (sr) {
+        LOCK(&conn_list_lock);
+
+        sr->uses++;
+
+        /* if slow start or aged out, destroy */
+        if ((svc_puts++ < SLOWSTART_LOOKUPS)
+        ||  (conn_count && (sr->uses > REUSE_TIMES))) {
+            UNLOCK(&conn_list_lock);
+            destroy_svc_ref(sr);
+            return;
+        }
+
+        conn_count++;
+        
+        SLIST_INSERT_HEAD(&conn_list, sr, entries);
+        
+        UNLOCK(&conn_list_lock);
+    }
+}
+
+
+
+/*
+ * determine if this is a call we should retry with a fresh
+ * connection, for example if mdnsd went away and came back.
+ */
+static bool
+retry_query(svc_ref **sr, DNSServiceErrorType err)
+{
+    if ((err == kDNSServiceErr_Unknown)
+    ||  (err == kDNSServiceErr_ServiceNotRunning)) {
+        /* these errors might indicate a stale socket */
+        if ((*sr)->uses) {
+            /* this was an old socket, so kill it and get another */
+            destroy_svc_ref(*sr);
+            *sr = get_svc_ref();
+            if (*sr) {
+                /* we can retry */
+                return true;
+            }
+        }
+    }
+    
+    return false;
+}
+
+
+
+static void
+decref_res_conf(res_conf *rc)
+{
+    rc->refcount--;
+    
+    if (rc->refcount < 1) {
+        if ((rc->no_search = rc->search_domains)) {
+            for (; *(rc->no_search); rc->no_search++) {
+                free(*(rc->no_search));
+            }
+        
+            free(rc->search_domains);
+        }
+        
+        free(rc);
+    }
+}
+
+
+
+res_conf *
+get_res_conf(void)
+{
+    struct timespec last_change;
+    res_state res;
+    res_conf *rc;
+
+    LOCK(&res_conf_lock);
+    
+    /* check if resolver config changed */
+
+    res = __res_get_state();
+    if (res) {
+        res_check(res, &last_change);
+        if (timespeccmp(&last_config, &last_change, <)) {
+            if (cur_res_conf) {
+                decref_res_conf(cur_res_conf);
+            }
+            cur_res_conf = new_res_conf(res);
+            if (cur_res_conf) {
+                last_config = last_change;
+            }
+        }
+        __res_put_state(res);
+    }
+
+    rc = cur_res_conf;
+    if (rc) {
+        rc->refcount++;
+    }
+
+    UNLOCK(&res_conf_lock);
+    
+    return rc;
+}
+
+
+
+static void
+put_res_conf(res_conf *rc)
+{
+    LOCK(&res_conf_lock);
+    
+    decref_res_conf(rc);
+    
+    UNLOCK(&res_conf_lock);
+}
+
+
+
+static res_conf *
+new_res_conf(res_state res)
+{
+    res_conf *rc;
+    int count = 0;
+    char **sd, *p;
+    
+    rc = calloc(sizeof(res_conf), 1);
+    if (rc) {
+        rc->refcount = 1;
+        
+        sd = res->dnsrch;
+        while (*sd) {
+            if (searchable_domain(*sd)) {
+                count++;
+            }
+            sd++;
+        }
+    
+        rc->search_domains = calloc(sizeof(char *), count + 1);
+        if (!(rc->search_domains)) {
+            decref_res_conf(rc);
+            return NULL;
+        }
+    
+        sd = res->dnsrch;
+        rc->no_search = rc->search_domains;
+        while (*sd) {
+            if (searchable_domain(*sd)) {
+                *(rc->no_search) = p = strdup(*sd);
+                if (!p) {
+                    decref_res_conf(rc);
+                    return NULL;
+                }
+                    
+                rc->no_search++;
+            }
+            sd++;
+        }
+
+        rc->timeout = res->retrans;
+
+        if (svc_flags & kDNSServiceFlagsForceMulticast) {
+            rc->ndots = 1;
+            if (rc->timeout > 2) {
+                rc->timeout = 2;
+            }
+        } else {
+            rc->ndots = res->ndots;
+        }
+    }
+    
+    return rc;
+}
+
+
+
+static short
+get_timeout(void)
+{
+    short timeout = 5;
+    res_conf *rc;
+    
+    rc = get_res_conf();
+    if (rc) {
+        timeout = rc->timeout;
+        put_res_conf(rc);
+    }
+
+    return timeout;
+}

Reply via email to