Home | History | Annotate | Line # | Download | only in nss
nss_mdnsd.c revision 1.3.6.1
      1  1.3.6.1    yamt /*	$NetBSD: nss_mdnsd.c,v 1.3.6.1 2014/05/22 13:58:04 yamt Exp $	*/
      2      1.1  tsarna 
      3      1.1  tsarna /*-
      4      1.1  tsarna  * Copyright (c) 2009 The NetBSD Foundation, Inc.
      5      1.1  tsarna  * All rights reserved.
      6      1.1  tsarna  *
      7      1.1  tsarna  * This code is derived from software contributed to The NetBSD Foundation
      8      1.1  tsarna  * by Tyler C. Sarna
      9      1.1  tsarna  *
     10      1.1  tsarna  * Redistribution and use in source and binary forms, with or without
     11      1.1  tsarna  * modification, are permitted provided that the following conditions
     12      1.1  tsarna  * are met:
     13      1.1  tsarna  * 1. Redistributions of source code must retain the above copyright
     14      1.1  tsarna  *    notice, this list of conditions and the following disclaimer.
     15      1.1  tsarna  * 2. Redistributions in binary form must reproduce the above copyright
     16      1.1  tsarna  *    notice, this list of conditions and the following disclaimer in the
     17      1.1  tsarna  *    documentation and/or other materials provided with the distribution.
     18      1.1  tsarna  *
     19      1.1  tsarna  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20      1.1  tsarna  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21      1.1  tsarna  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22      1.1  tsarna  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23      1.1  tsarna  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24      1.1  tsarna  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25      1.1  tsarna  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26      1.1  tsarna  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27      1.1  tsarna  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28      1.1  tsarna  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29      1.1  tsarna  * POSSIBILITY OF SUCH DAMAGE.
     30      1.1  tsarna  */
     31      1.1  tsarna 
     32      1.1  tsarna /*
     33      1.1  tsarna  * Multicast DNS ("Bonjour") hosts name service switch
     34      1.1  tsarna  *
     35      1.1  tsarna  * Documentation links:
     36      1.1  tsarna  *
     37      1.1  tsarna  * http://developer.apple.com/bonjour/
     38      1.1  tsarna  * http://www.multicastdns.org/
     39      1.1  tsarna  * http://www.dns-sd.org/
     40      1.1  tsarna  */
     41      1.1  tsarna 
     42      1.1  tsarna #include <errno.h>
     43      1.1  tsarna #include <nsswitch.h>
     44      1.1  tsarna #include <stdarg.h>
     45      1.1  tsarna #include <stdlib.h>
     46      1.1  tsarna #include <sys/socket.h>
     47      1.1  tsarna #include <sys/param.h>
     48      1.3  tsarna #include <sys/queue.h>
     49      1.1  tsarna #include <netdb.h>
     50      1.1  tsarna #include <netinet/in.h>
     51      1.1  tsarna #include <arpa/nameser.h>
     52      1.1  tsarna #include <resolv.h>
     53      1.1  tsarna #include <dns_sd.h>
     54      1.1  tsarna #include <poll.h>
     55      1.1  tsarna #include <string.h>
     56      1.1  tsarna #include <stdio.h>
     57      1.1  tsarna #include <stdbool.h>
     58      1.3  tsarna #include <pthread.h>
     59      1.3  tsarna #include <fcntl.h>
     60      1.3  tsarna #include <unistd.h>
     61      1.3  tsarna #include <time.h>
     62      1.3  tsarna 
     63      1.3  tsarna 
     64  1.3.6.1    yamt #include "hostent.h"
     65  1.3.6.1    yamt 
     66      1.3  tsarna /*
     67      1.3  tsarna  * Pool of mdnsd connections
     68      1.3  tsarna  */
     69      1.3  tsarna static SLIST_HEAD(, svc_ref) conn_list = LIST_HEAD_INITIALIZER(&conn_list);
     70      1.3  tsarna static unsigned int conn_count = 0;
     71      1.3  tsarna static pid_t my_pid;
     72      1.3  tsarna struct timespec last_config;
     73      1.3  tsarna 
     74      1.3  tsarna typedef struct svc_ref {
     75      1.3  tsarna     SLIST_ENTRY(svc_ref)    entries;
     76      1.3  tsarna     DNSServiceRef           sdRef;
     77      1.3  tsarna     unsigned int            uses;
     78      1.3  tsarna } svc_ref;
     79      1.3  tsarna 
     80      1.3  tsarna /*
     81      1.3  tsarna  * There is a large class of programs that do a few lookups at startup
     82      1.3  tsarna  * and then never again (ping, telnet, etc). Keeping a persistent connection
     83      1.3  tsarna  * for these would be a waste, so there is a kind of slow start mechanism.
     84      1.3  tsarna  * The first SLOWSTART_LOOKUPS times, dispose of the connection after use.
     85      1.3  tsarna  * After that we assume the program is a serious consumer of host lookup
     86      1.3  tsarna  * services and start keeping connections.
     87      1.3  tsarna  */
     88      1.3  tsarna #define SLOWSTART_LOOKUPS 5
     89      1.3  tsarna static unsigned int svc_puts = 0;
     90      1.3  tsarna 
     91      1.3  tsarna /*
     92      1.3  tsarna  * Age out connections. Free connection instead of putting on the list
     93      1.3  tsarna  * if used more than REUSE_TIMES and there are others on the list.
     94      1.3  tsarna  */
     95      1.3  tsarna #define REUSE_TIMES          32
     96      1.3  tsarna 
     97      1.3  tsarna /* protects above data */
     98      1.3  tsarna static pthread_mutex_t conn_list_lock = PTHREAD_MUTEX_INITIALIZER;
     99      1.3  tsarna 
    100      1.3  tsarna extern int __isthreaded; /* libc private -- wish there was a better way */
    101      1.3  tsarna 
    102      1.3  tsarna #define LOCK(x) do { if (__isthreaded) pthread_mutex_lock(x); } while (0)
    103      1.3  tsarna #define UNLOCK(x) do { if (__isthreaded) pthread_mutex_unlock(x); } while (0)
    104      1.3  tsarna 
    105      1.1  tsarna 
    106      1.1  tsarna #ifndef lint
    107      1.1  tsarna #define UNUSED(a)       (void)&a
    108      1.1  tsarna #else
    109      1.1  tsarna #define UNUSED(a)       a = a
    110      1.1  tsarna #endif
    111      1.1  tsarna 
    112      1.1  tsarna #define MAXALIASES      35
    113      1.1  tsarna #define MAXADDRS        35
    114      1.1  tsarna 
    115      1.1  tsarna typedef struct callback_ctx {
    116      1.1  tsarna     bool done;
    117      1.1  tsarna } callback_ctx;
    118      1.1  tsarna 
    119      1.1  tsarna typedef struct hostent_ctx {
    120      1.1  tsarna     callback_ctx cb_ctx;    /* must come first */
    121      1.1  tsarna     struct hostent host;
    122      1.1  tsarna     char *h_addr_ptrs[MAXADDRS + 1];
    123      1.1  tsarna     char *host_aliases[MAXALIASES];
    124      1.1  tsarna     char addrs[MAXADDRS * 16];
    125      1.1  tsarna     char buf[8192], *next;
    126      1.1  tsarna     int naliases, naddrs;
    127      1.1  tsarna } hostent_ctx;
    128      1.1  tsarna 
    129      1.1  tsarna typedef struct addrinfo_ctx {
    130      1.1  tsarna     callback_ctx cb_ctx;    /* must come first */
    131      1.1  tsarna     struct addrinfo start, *last;
    132      1.1  tsarna } addrinfo_ctx;
    133      1.1  tsarna 
    134      1.1  tsarna #define HCTX_BUFLEFT(c) (sizeof((c)->buf) - ((c)->next - (c)->buf))
    135      1.1  tsarna 
    136      1.3  tsarna typedef struct res_conf {
    137      1.3  tsarna     unsigned int            refcount;
    138      1.3  tsarna     char                  **search_domains;
    139      1.3  tsarna     char                  **no_search;
    140      1.3  tsarna     short                   ndots;
    141      1.3  tsarna     short                   timeout;
    142      1.3  tsarna } res_conf;
    143      1.3  tsarna 
    144      1.3  tsarna static res_conf *cur_res_conf;
    145      1.3  tsarna 
    146      1.3  tsarna /* protects above data */
    147      1.3  tsarna static pthread_mutex_t res_conf_lock = PTHREAD_MUTEX_INITIALIZER;
    148      1.3  tsarna 
    149      1.1  tsarna typedef struct search_iter {
    150      1.3  tsarna     res_conf       *conf;
    151      1.1  tsarna     const char     *name;
    152      1.3  tsarna     char          **next_search;
    153      1.1  tsarna     size_t          baselen;
    154      1.1  tsarna     bool            abs_first;
    155      1.1  tsarna     bool            abs_last;
    156      1.1  tsarna     char            buf[MAXHOSTNAMELEN];
    157      1.1  tsarna } search_iter;
    158      1.1  tsarna 
    159      1.1  tsarna static DNSServiceFlags svc_flags = 0;
    160      1.1  tsarna 
    161      1.1  tsarna ns_mtab *nss_module_register(const char *, u_int *, nss_module_unregister_fn *);
    162      1.1  tsarna static int load_config(res_state);
    163      1.1  tsarna 
    164      1.1  tsarna static int _mdns_getaddrinfo(void *, void *, va_list);
    165      1.1  tsarna static int _mdns_gethtbyaddr(void *, void *, va_list);
    166      1.1  tsarna static int _mdns_gethtbyname(void *, void *, va_list);
    167      1.1  tsarna 
    168      1.1  tsarna static int _mdns_getaddrinfo_abs(const char *, DNSServiceProtocol,
    169      1.3  tsarna     svc_ref **, addrinfo_ctx *, short);
    170      1.1  tsarna static void _mdns_addrinfo_init(addrinfo_ctx *, const struct addrinfo *);
    171      1.1  tsarna static void _mdns_addrinfo_add_ai(addrinfo_ctx *, struct addrinfo *);
    172      1.1  tsarna static struct addrinfo *_mdns_addrinfo_done(addrinfo_ctx *);
    173      1.1  tsarna 
    174  1.3.6.1    yamt static int _mdns_gethtbyname_abs(struct getnamaddr *, struct hostent_ctx *,
    175  1.3.6.1    yamt     const char *, int, svc_ref **, short);
    176      1.1  tsarna static void _mdns_hostent_init(hostent_ctx *, int, int);
    177      1.1  tsarna static void _mdns_hostent_add_host(hostent_ctx *, const char *);
    178      1.1  tsarna static void _mdns_hostent_add_addr(hostent_ctx *, const void *, uint16_t);
    179  1.3.6.1    yamt static int _mdns_hostent_done(struct getnamaddr *, hostent_ctx *);
    180      1.1  tsarna 
    181      1.1  tsarna static void _mdns_addrinfo_cb(DNSServiceRef, DNSServiceFlags,
    182      1.1  tsarna     uint32_t, DNSServiceErrorType, const char *, const struct sockaddr *,
    183      1.1  tsarna     uint32_t, void *);
    184      1.1  tsarna static void _mdns_hostent_cb(DNSServiceRef, DNSServiceFlags,
    185      1.1  tsarna     uint32_t, DNSServiceErrorType, const char *, uint16_t, uint16_t, uint16_t,
    186      1.1  tsarna     const void *, uint32_t, void *);
    187      1.3  tsarna static void _mdns_eventloop(svc_ref *, callback_ctx *, short);
    188      1.1  tsarna 
    189      1.1  tsarna static char *_mdns_rdata2name(const unsigned char *, uint16_t,
    190      1.1  tsarna     char *, size_t);
    191      1.1  tsarna 
    192      1.3  tsarna static int search_init(search_iter *, const char *, const char **);
    193      1.3  tsarna static void search_done(search_iter *);
    194      1.3  tsarna static const char *search_next(search_iter *);
    195      1.3  tsarna static bool searchable_domain(char *);
    196      1.3  tsarna 
    197      1.3  tsarna static void destroy_svc_ref(svc_ref *);
    198      1.3  tsarna static svc_ref *get_svc_ref(void);
    199      1.3  tsarna static void put_svc_ref(svc_ref *);
    200      1.3  tsarna static bool retry_query(svc_ref **, DNSServiceErrorType);
    201      1.3  tsarna 
    202      1.3  tsarna static void decref_res_conf(res_conf *);
    203      1.3  tsarna static res_conf *get_res_conf(void);
    204      1.3  tsarna static void put_res_conf(res_conf *);
    205      1.3  tsarna static res_conf *new_res_conf(res_state);
    206      1.3  tsarna static short get_timeout(void);
    207      1.1  tsarna 
    208      1.1  tsarna static ns_mtab mtab[] = {
    209      1.1  tsarna     { NSDB_HOSTS, "getaddrinfo",    _mdns_getaddrinfo, NULL },
    210      1.1  tsarna     { NSDB_HOSTS, "gethostbyaddr",  _mdns_gethtbyaddr, NULL },
    211      1.1  tsarna     { NSDB_HOSTS, "gethostbyname",  _mdns_gethtbyname, NULL },
    212      1.1  tsarna };
    213      1.1  tsarna 
    214      1.1  tsarna 
    215      1.1  tsarna 
    216      1.1  tsarna ns_mtab *
    217      1.1  tsarna nss_module_register(const char *source, u_int *nelems,
    218      1.1  tsarna                     nss_module_unregister_fn *unreg)
    219      1.1  tsarna {
    220      1.1  tsarna     *nelems = sizeof(mtab) / sizeof(mtab[0]);
    221      1.1  tsarna     *unreg = NULL;
    222      1.1  tsarna 
    223      1.3  tsarna     my_pid = getpid();
    224      1.3  tsarna 
    225      1.1  tsarna     if (!strcmp(source, "multicast_dns")) {
    226      1.1  tsarna         svc_flags = kDNSServiceFlagsForceMulticast;
    227      1.1  tsarna     }
    228      1.1  tsarna 
    229      1.1  tsarna     return mtab;
    230      1.1  tsarna }
    231      1.1  tsarna 
    232      1.1  tsarna 
    233      1.1  tsarna 
    234      1.1  tsarna static int
    235      1.1  tsarna _mdns_getaddrinfo(void *cbrv, void *cbdata, va_list ap)
    236      1.1  tsarna {
    237      1.1  tsarna     const struct addrinfo *pai;
    238      1.1  tsarna     const char *name, *sname;
    239      1.1  tsarna     DNSServiceProtocol proto;
    240      1.1  tsarna     DNSServiceRef sdRef;
    241      1.1  tsarna     addrinfo_ctx ctx;
    242      1.1  tsarna     search_iter iter;
    243      1.3  tsarna     res_conf *rc;
    244      1.3  tsarna     svc_ref *sr;
    245      1.3  tsarna     int err;
    246      1.1  tsarna 
    247      1.1  tsarna     UNUSED(cbdata);
    248      1.1  tsarna 
    249      1.1  tsarna     name = va_arg(ap, char *);
    250      1.1  tsarna     pai = va_arg(ap, struct addrinfo *);
    251      1.1  tsarna 
    252      1.1  tsarna     switch (pai->ai_family) {
    253      1.1  tsarna     case AF_UNSPEC:
    254      1.1  tsarna         proto = kDNSServiceProtocol_IPv6 | kDNSServiceProtocol_IPv4;
    255      1.1  tsarna         break;
    256      1.1  tsarna 
    257      1.1  tsarna     case AF_INET6:
    258      1.1  tsarna         proto = kDNSServiceProtocol_IPv6;
    259      1.1  tsarna         break;
    260      1.1  tsarna 
    261      1.1  tsarna     case AF_INET:
    262      1.1  tsarna         proto = kDNSServiceProtocol_IPv4;
    263      1.1  tsarna         break;
    264      1.1  tsarna 
    265      1.1  tsarna     default:
    266      1.1  tsarna         h_errno = NO_RECOVERY;
    267      1.1  tsarna         return NS_UNAVAIL;
    268      1.1  tsarna     }
    269      1.1  tsarna 
    270      1.3  tsarna     sr = get_svc_ref();
    271      1.3  tsarna     if (!sr) {
    272      1.1  tsarna         h_errno = NETDB_INTERNAL;
    273      1.1  tsarna         return NS_UNAVAIL;
    274      1.1  tsarna     }
    275      1.1  tsarna 
    276      1.3  tsarna     if ((err = search_init(&iter, name, &sname)) != NS_SUCCESS) {
    277      1.3  tsarna         put_svc_ref(sr);
    278      1.3  tsarna         return err;
    279      1.3  tsarna     }
    280      1.3  tsarna 
    281      1.1  tsarna     _mdns_addrinfo_init(&ctx, pai);
    282      1.3  tsarna 
    283      1.3  tsarna     err = NS_NOTFOUND;
    284      1.3  tsarna     while (sr && sname && (err != NS_SUCCESS)) {
    285      1.3  tsarna         err = _mdns_getaddrinfo_abs(sname, proto, &sr, &ctx, iter.conf->timeout);
    286      1.1  tsarna         if (err != NS_SUCCESS) {
    287      1.1  tsarna             sname = search_next(&iter);
    288      1.1  tsarna         }
    289      1.1  tsarna     };
    290      1.1  tsarna 
    291      1.3  tsarna     search_done(&iter);
    292      1.3  tsarna     put_svc_ref(sr);
    293      1.1  tsarna 
    294      1.1  tsarna     if (err == NS_SUCCESS) {
    295      1.1  tsarna         *(struct addrinfo **)cbrv = _mdns_addrinfo_done(&ctx);
    296      1.1  tsarna     }
    297      1.1  tsarna 
    298      1.1  tsarna     return err;
    299      1.1  tsarna }
    300      1.1  tsarna 
    301      1.1  tsarna 
    302      1.1  tsarna 
    303      1.1  tsarna static int
    304      1.1  tsarna _mdns_getaddrinfo_abs(const char *name, DNSServiceProtocol proto,
    305      1.3  tsarna     svc_ref **sr, addrinfo_ctx *ctx, short timeout)
    306      1.1  tsarna {
    307      1.3  tsarna     DNSServiceErrorType err = kDNSServiceErr_ServiceNotRunning;
    308      1.3  tsarna     DNSServiceRef sdRef;
    309      1.3  tsarna     bool retry = true;
    310      1.3  tsarna 
    311      1.3  tsarna     while (*sr && retry) {
    312      1.3  tsarna         /* We must always use a copy of the ref when using a shared
    313      1.3  tsarna            connection, per kDNSServiceFlagsShareConnection docs */
    314      1.3  tsarna 
    315      1.3  tsarna         sdRef = (*sr)->sdRef;
    316      1.3  tsarna 
    317      1.3  tsarna         err = DNSServiceGetAddrInfo(
    318      1.3  tsarna             &sdRef,
    319      1.3  tsarna             svc_flags
    320      1.3  tsarna                 | kDNSServiceFlagsShareConnection
    321      1.3  tsarna                 | kDNSServiceFlagsReturnIntermediates,
    322      1.3  tsarna             kDNSServiceInterfaceIndexAny,
    323      1.3  tsarna             proto,
    324      1.3  tsarna             name,
    325      1.3  tsarna             _mdns_addrinfo_cb,
    326      1.3  tsarna             ctx
    327      1.3  tsarna         );
    328      1.3  tsarna 
    329      1.3  tsarna         retry = retry_query(sr, err);
    330      1.3  tsarna     }
    331      1.1  tsarna 
    332      1.1  tsarna     if (err) {
    333      1.1  tsarna         h_errno = NETDB_INTERNAL;
    334      1.1  tsarna         return NS_UNAVAIL;
    335      1.1  tsarna     }
    336      1.3  tsarna 
    337      1.3  tsarna     _mdns_eventloop(*sr, (void *)ctx, timeout);
    338      1.1  tsarna 
    339      1.1  tsarna     DNSServiceRefDeallocate(sdRef);
    340      1.1  tsarna 
    341      1.1  tsarna     if (ctx->start.ai_next) {
    342      1.1  tsarna         return NS_SUCCESS;
    343      1.1  tsarna     } else {
    344      1.1  tsarna         h_errno = HOST_NOT_FOUND;
    345      1.1  tsarna         return NS_NOTFOUND;
    346      1.1  tsarna     }
    347      1.1  tsarna }
    348      1.1  tsarna 
    349      1.1  tsarna 
    350      1.1  tsarna 
    351      1.1  tsarna static int
    352      1.1  tsarna _mdns_gethtbyaddr(void *cbrv, void *cbdata, va_list ap)
    353      1.1  tsarna {
    354      1.1  tsarna     const unsigned char *addr;
    355      1.1  tsarna     int addrlen, af;
    356      1.1  tsarna     char qbuf[NS_MAXDNAME + 1], *qp, *ep;
    357      1.1  tsarna     int advance, n;
    358      1.1  tsarna     DNSServiceErrorType err;
    359      1.1  tsarna     DNSServiceRef sdRef;
    360      1.3  tsarna     svc_ref *sr;
    361      1.3  tsarna     bool retry = true;
    362  1.3.6.1    yamt     struct getnamaddr *info = cbrv;
    363      1.1  tsarna 
    364      1.1  tsarna     UNUSED(cbdata);
    365      1.1  tsarna 
    366      1.1  tsarna     addr = va_arg(ap, unsigned char *);
    367      1.1  tsarna     addrlen = va_arg(ap, int);
    368      1.1  tsarna     af = va_arg(ap, int);
    369      1.1  tsarna 
    370      1.1  tsarna     switch (af) {
    371      1.1  tsarna     case AF_INET:
    372      1.2  tsarna         /* if mcast-only don't bother for non-LinkLocal addrs) */
    373      1.2  tsarna         if (svc_flags & kDNSServiceFlagsForceMulticast) {
    374      1.2  tsarna             if ((addr[0] != 169) || (addr[1] != 254)) {
    375  1.3.6.1    yamt                 *info->he = HOST_NOT_FOUND;
    376      1.2  tsarna                 return NS_NOTFOUND;
    377      1.2  tsarna             }
    378      1.2  tsarna         }
    379      1.2  tsarna 
    380      1.1  tsarna         (void)snprintf(qbuf, sizeof(qbuf), "%u.%u.%u.%u.in-addr.arpa",
    381      1.1  tsarna             (addr[3] & 0xff), (addr[2] & 0xff),
    382      1.1  tsarna             (addr[1] & 0xff), (addr[0] & 0xff));
    383      1.1  tsarna         break;
    384      1.1  tsarna 
    385      1.1  tsarna     case AF_INET6:
    386      1.2  tsarna         /* if mcast-only don't bother for non-LinkLocal addrs) */
    387      1.2  tsarna         if (svc_flags & kDNSServiceFlagsForceMulticast) {
    388      1.2  tsarna             if ((addr[0] != 0xfe) || ((addr[1] & 0xc0) != 0x80)) {
    389  1.3.6.1    yamt                 *info->he = HOST_NOT_FOUND;
    390      1.2  tsarna                 return NS_NOTFOUND;
    391      1.2  tsarna             }
    392      1.2  tsarna         }
    393      1.2  tsarna 
    394      1.1  tsarna         qp = qbuf;
    395      1.1  tsarna         ep = qbuf + sizeof(qbuf) - 1;
    396      1.1  tsarna         for (n = IN6ADDRSZ - 1; n >= 0; n--) {
    397      1.1  tsarna             advance = snprintf(qp, (size_t)(ep - qp), "%x.%x.",
    398      1.1  tsarna                 addr[n] & 0xf,
    399      1.1  tsarna                 ((unsigned int)addr[n] >> 4) & 0xf);
    400      1.1  tsarna             if (advance > 0 && qp + advance < ep)
    401      1.1  tsarna                 qp += advance;
    402      1.1  tsarna             else {
    403  1.3.6.1    yamt                 *info->he = NETDB_INTERNAL;
    404      1.1  tsarna                 return NS_NOTFOUND;
    405      1.1  tsarna             }
    406      1.1  tsarna         }
    407      1.1  tsarna         if (strlcat(qbuf, "ip6.arpa", sizeof(qbuf)) >= sizeof(qbuf)) {
    408  1.3.6.1    yamt             *info->he = NETDB_INTERNAL;
    409      1.1  tsarna             return NS_NOTFOUND;
    410      1.1  tsarna         }
    411      1.1  tsarna         break;
    412      1.1  tsarna 
    413      1.1  tsarna     default:
    414  1.3.6.1    yamt         *info->he = NO_RECOVERY;
    415      1.1  tsarna         return NS_UNAVAIL;
    416      1.1  tsarna     }
    417      1.1  tsarna 
    418  1.3.6.1    yamt     hostent_ctx h_ctx;
    419      1.1  tsarna     _mdns_hostent_init(&h_ctx, af, addrlen);
    420      1.1  tsarna     _mdns_hostent_add_addr(&h_ctx, addr, addrlen);
    421      1.1  tsarna 
    422      1.3  tsarna     sr = get_svc_ref();
    423      1.3  tsarna     if (!sr) {
    424  1.3.6.1    yamt         *info->he = NETDB_INTERNAL;
    425      1.3  tsarna         return NS_UNAVAIL;
    426      1.3  tsarna     }
    427      1.3  tsarna 
    428      1.3  tsarna     while (sr && retry) {
    429      1.3  tsarna         /* We must always use a copy of the ref when using a shared
    430      1.3  tsarna            connection, per kDNSServiceFlagsShareConnection docs */
    431      1.3  tsarna         sdRef = sr->sdRef;
    432      1.3  tsarna 
    433      1.3  tsarna         err = DNSServiceQueryRecord(
    434      1.3  tsarna             &sdRef,
    435      1.3  tsarna             svc_flags
    436      1.3  tsarna                 | kDNSServiceFlagsShareConnection
    437      1.3  tsarna                 | kDNSServiceFlagsReturnIntermediates,
    438      1.3  tsarna             kDNSServiceInterfaceIndexAny,
    439      1.3  tsarna             qbuf,
    440      1.3  tsarna             kDNSServiceType_PTR,
    441      1.3  tsarna             kDNSServiceClass_IN,
    442      1.3  tsarna             _mdns_hostent_cb,
    443      1.3  tsarna             &h_ctx
    444      1.3  tsarna         );
    445      1.3  tsarna 
    446      1.3  tsarna         retry = retry_query(&sr, err);
    447      1.3  tsarna     }
    448      1.1  tsarna 
    449      1.1  tsarna     if (err) {
    450      1.3  tsarna         put_svc_ref(sr);
    451  1.3.6.1    yamt         *info->he = NETDB_INTERNAL;
    452      1.1  tsarna         return NS_UNAVAIL;
    453      1.1  tsarna     }
    454      1.3  tsarna 
    455      1.3  tsarna     _mdns_eventloop(sr, (void *)&h_ctx, get_timeout());
    456      1.1  tsarna 
    457      1.1  tsarna     DNSServiceRefDeallocate(sdRef);
    458      1.3  tsarna     put_svc_ref(sr);
    459      1.1  tsarna 
    460      1.1  tsarna     if (h_ctx.naliases) {
    461  1.3.6.1    yamt         return _mdns_hostent_done(info, &h_ctx);
    462      1.1  tsarna     } else {
    463  1.3.6.1    yamt         *info->he = HOST_NOT_FOUND;
    464      1.1  tsarna         return NS_NOTFOUND;
    465      1.1  tsarna     }
    466      1.1  tsarna }
    467      1.1  tsarna 
    468      1.1  tsarna 
    469      1.1  tsarna 
    470      1.1  tsarna static int
    471      1.1  tsarna _mdns_gethtbyname(void *cbrv, void *cbdata, va_list ap)
    472      1.1  tsarna {
    473      1.3  tsarna     int namelen, af, addrlen, rrtype, err;
    474      1.1  tsarna     const char *name, *sname;
    475      1.1  tsarna     DNSServiceRef sdRef;
    476      1.1  tsarna     search_iter iter;
    477      1.3  tsarna     svc_ref *sr;
    478  1.3.6.1    yamt     struct getnamaddr *info = cbrv;
    479      1.1  tsarna 
    480      1.1  tsarna     UNUSED(cbdata);
    481      1.1  tsarna 
    482      1.1  tsarna     name = va_arg(ap, char *);
    483      1.1  tsarna     namelen = va_arg(ap, int);
    484      1.1  tsarna     af = va_arg(ap, int);
    485      1.1  tsarna 
    486      1.1  tsarna     UNUSED(namelen);
    487      1.1  tsarna 
    488      1.1  tsarna     switch (af) {
    489      1.1  tsarna     case AF_INET:
    490      1.1  tsarna         rrtype = kDNSServiceType_A;
    491      1.1  tsarna         addrlen = 4;
    492      1.1  tsarna         break;
    493      1.1  tsarna 
    494      1.1  tsarna     case AF_INET6:
    495      1.1  tsarna         rrtype = kDNSServiceType_AAAA;
    496      1.1  tsarna         addrlen = 16;
    497      1.1  tsarna         break;
    498      1.1  tsarna 
    499      1.1  tsarna     default:
    500  1.3.6.1    yamt         *info->he = NO_RECOVERY;
    501      1.2  tsarna         return NS_UNAVAIL;
    502      1.2  tsarna     }
    503      1.2  tsarna 
    504      1.3  tsarna     sr = get_svc_ref();
    505      1.3  tsarna     if (!sr) {
    506  1.3.6.1    yamt         *info->he = NETDB_INTERNAL;
    507      1.1  tsarna         return NS_UNAVAIL;
    508      1.1  tsarna     }
    509      1.1  tsarna 
    510      1.3  tsarna     if ((err = search_init(&iter, name, &sname)) != NS_SUCCESS) {
    511      1.3  tsarna         put_svc_ref(sr);
    512      1.3  tsarna         return err;
    513      1.3  tsarna     }
    514      1.3  tsarna 
    515  1.3.6.1    yamt     hostent_ctx h_ctx;
    516      1.1  tsarna     _mdns_hostent_init(&h_ctx, af, addrlen);
    517      1.2  tsarna 
    518      1.3  tsarna     err = NS_NOTFOUND;
    519      1.3  tsarna     while (sr && sname && (err != NS_SUCCESS)) {
    520  1.3.6.1    yamt         err = _mdns_gethtbyname_abs(info, &h_ctx, sname, rrtype, &sr,
    521  1.3.6.1    yamt 	    iter.conf->timeout);
    522      1.1  tsarna         if (err != NS_SUCCESS) {
    523      1.1  tsarna             sname = search_next(&iter);
    524      1.1  tsarna         }
    525      1.1  tsarna     };
    526      1.1  tsarna 
    527      1.3  tsarna     search_done(&iter);
    528      1.3  tsarna     put_svc_ref(sr);
    529      1.3  tsarna 
    530  1.3.6.1    yamt     if (err != NS_SUCCESS)
    531  1.3.6.1    yamt 	return err;
    532  1.3.6.1    yamt     _mdns_hostent_add_host(&h_ctx, sname);
    533  1.3.6.1    yamt     _mdns_hostent_add_host(&h_ctx, name);
    534  1.3.6.1    yamt     return _mdns_hostent_done(info, &h_ctx);
    535      1.1  tsarna }
    536      1.1  tsarna 
    537      1.1  tsarna 
    538      1.1  tsarna 
    539      1.1  tsarna static int
    540  1.3.6.1    yamt _mdns_gethtbyname_abs(struct getnamaddr *info, struct hostent_ctx *ctx,
    541  1.3.6.1    yamt     const char *name, int rrtype, svc_ref **sr, short timeout)
    542      1.1  tsarna {
    543      1.3  tsarna     DNSServiceErrorType err = kDNSServiceErr_ServiceNotRunning;
    544      1.3  tsarna     DNSServiceRef sdRef;
    545      1.3  tsarna     bool retry = true;
    546      1.1  tsarna 
    547      1.3  tsarna     while (*sr && retry) {
    548      1.3  tsarna         /* We must always use a copy of the ref when using a shared
    549      1.3  tsarna            connection, per kDNSServiceFlagsShareConnection docs */
    550      1.3  tsarna         sdRef = (*sr)->sdRef;
    551      1.3  tsarna 
    552      1.3  tsarna         err = DNSServiceQueryRecord(
    553      1.3  tsarna             &sdRef,
    554      1.3  tsarna             svc_flags
    555      1.3  tsarna                 | kDNSServiceFlagsShareConnection
    556      1.3  tsarna                 | kDNSServiceFlagsReturnIntermediates,
    557      1.3  tsarna             kDNSServiceInterfaceIndexAny,
    558      1.3  tsarna             name,
    559      1.3  tsarna             rrtype,
    560      1.3  tsarna             kDNSServiceClass_IN,
    561      1.3  tsarna             _mdns_hostent_cb,
    562  1.3.6.1    yamt             ctx
    563      1.3  tsarna         );
    564      1.3  tsarna 
    565      1.3  tsarna         retry = retry_query(sr, err);
    566      1.3  tsarna     }
    567      1.1  tsarna 
    568      1.1  tsarna     if (err) {
    569  1.3.6.1    yamt         *info->he = NETDB_INTERNAL;
    570      1.1  tsarna         return NS_UNAVAIL;
    571      1.1  tsarna     }
    572      1.3  tsarna 
    573  1.3.6.1    yamt     _mdns_eventloop(*sr, (void *)ctx, timeout);
    574      1.1  tsarna 
    575      1.1  tsarna     DNSServiceRefDeallocate(sdRef);
    576      1.1  tsarna 
    577  1.3.6.1    yamt     if (ctx->naddrs) {
    578      1.1  tsarna         return NS_SUCCESS;
    579      1.1  tsarna     } else {
    580  1.3.6.1    yamt         *info->he = HOST_NOT_FOUND;
    581      1.1  tsarna         return NS_NOTFOUND;
    582      1.1  tsarna     }
    583      1.1  tsarna }
    584      1.1  tsarna 
    585      1.1  tsarna 
    586      1.1  tsarna 
    587      1.1  tsarna static void
    588      1.1  tsarna _mdns_addrinfo_init(addrinfo_ctx *ctx, const struct addrinfo *ai)
    589      1.1  tsarna {
    590      1.1  tsarna     ctx->cb_ctx.done = false;
    591      1.1  tsarna     ctx->start = *ai;
    592      1.1  tsarna     ctx->start.ai_next = NULL;
    593      1.1  tsarna     ctx->start.ai_canonname = NULL;
    594      1.1  tsarna     ctx->last = &(ctx->start);
    595      1.1  tsarna }
    596      1.1  tsarna 
    597      1.1  tsarna 
    598      1.1  tsarna 
    599      1.1  tsarna static void
    600      1.1  tsarna _mdns_addrinfo_add_ai(addrinfo_ctx *ctx, struct addrinfo *ai)
    601      1.1  tsarna {
    602      1.1  tsarna     ctx->last->ai_next = ai;
    603      1.1  tsarna     while (ctx->last->ai_next)
    604      1.1  tsarna         ctx->last = ctx->last->ai_next;
    605      1.1  tsarna }
    606      1.1  tsarna 
    607      1.1  tsarna 
    608      1.1  tsarna 
    609      1.1  tsarna static struct addrinfo *
    610      1.1  tsarna _mdns_addrinfo_done(addrinfo_ctx *ctx)
    611      1.1  tsarna {
    612      1.1  tsarna     struct addrinfo head, *t, *p;
    613      1.1  tsarna 
    614      1.1  tsarna     /* sort v6 up */
    615      1.1  tsarna 
    616      1.1  tsarna     t = &head;
    617      1.1  tsarna     p = ctx->start.ai_next;
    618      1.1  tsarna 
    619      1.1  tsarna     while (p->ai_next) {
    620      1.1  tsarna         if (p->ai_next->ai_family == AF_INET6) {
    621      1.1  tsarna             t->ai_next = p->ai_next;
    622      1.1  tsarna             t = t->ai_next;
    623      1.1  tsarna             p->ai_next = p->ai_next->ai_next;
    624      1.1  tsarna         } else {
    625      1.1  tsarna             p = p->ai_next;
    626      1.1  tsarna         }
    627      1.1  tsarna     }
    628      1.1  tsarna 
    629      1.1  tsarna     /* add rest of list and reset start to the new list */
    630      1.1  tsarna 
    631      1.1  tsarna     t->ai_next = ctx->start.ai_next;
    632      1.1  tsarna     ctx->start.ai_next = head.ai_next;
    633      1.1  tsarna 
    634      1.1  tsarna     return ctx->start.ai_next;
    635      1.1  tsarna }
    636      1.1  tsarna 
    637      1.1  tsarna 
    638      1.1  tsarna 
    639      1.1  tsarna static void
    640      1.1  tsarna _mdns_hostent_init(hostent_ctx *ctx, int af, int addrlen)
    641      1.1  tsarna {
    642      1.1  tsarna     int i;
    643      1.1  tsarna 
    644      1.1  tsarna     ctx->cb_ctx.done = false;
    645      1.1  tsarna     ctx->naliases = ctx->naddrs = 0;
    646      1.1  tsarna     ctx->next = ctx->buf;
    647      1.1  tsarna 
    648      1.1  tsarna     ctx->host.h_aliases = ctx->host_aliases;
    649      1.1  tsarna     ctx->host.h_addr_list = ctx->h_addr_ptrs;
    650      1.1  tsarna     ctx->host.h_name = ctx->host.h_aliases[0] = NULL;
    651      1.1  tsarna     ctx->host.h_addrtype = af;
    652      1.1  tsarna     ctx->host.h_length = addrlen;
    653      1.1  tsarna 
    654      1.1  tsarna     for (i = 0; i < MAXADDRS; i++) {
    655      1.1  tsarna         ctx->host.h_addr_list[i] = &(ctx->addrs[i * 16]);
    656      1.1  tsarna     }
    657      1.1  tsarna }
    658      1.1  tsarna 
    659      1.1  tsarna 
    660      1.1  tsarna 
    661      1.1  tsarna static void
    662      1.1  tsarna _mdns_hostent_add_host(hostent_ctx *ctx, const char *name)
    663      1.1  tsarna {
    664      1.1  tsarna     size_t len;
    665      1.1  tsarna     int i;
    666      1.1  tsarna 
    667      1.1  tsarna     if (name && (len = strlen(name))
    668      1.1  tsarna     && (HCTX_BUFLEFT(ctx) > len) && (ctx->naliases < MAXALIASES)) {
    669      1.1  tsarna         if (len && (name[len - 1] == '.')) {
    670      1.1  tsarna             len--;
    671      1.1  tsarna         }
    672      1.1  tsarna 
    673      1.1  tsarna         /* skip dupe names */
    674      1.1  tsarna 
    675      1.1  tsarna         if ((ctx->host.h_name) && !strncmp(ctx->host.h_name, name, len)
    676      1.1  tsarna         && (strlen(ctx->host.h_name) == len)) {
    677      1.1  tsarna             return;
    678      1.1  tsarna         }
    679      1.1  tsarna 
    680      1.1  tsarna         for (i = 0; i < ctx->naliases - 1; i++) {
    681      1.1  tsarna             if (!strncmp(ctx->host.h_aliases[i], name, len)
    682      1.1  tsarna             && (strlen(ctx->host.h_aliases[i]) == len)) {
    683      1.1  tsarna                 return;
    684      1.1  tsarna             }
    685      1.1  tsarna         }
    686      1.1  tsarna 
    687      1.1  tsarna         strncpy(ctx->next, name, len);
    688      1.1  tsarna         ctx->next[len] = 0;
    689      1.1  tsarna 
    690      1.1  tsarna         if (ctx->naliases == 0) {
    691      1.1  tsarna             ctx->host.h_name = ctx->next;
    692      1.1  tsarna         } else {
    693      1.1  tsarna             ctx->host.h_aliases[ctx->naliases - 1] = ctx->next;
    694      1.1  tsarna         }
    695      1.1  tsarna 
    696      1.1  tsarna         ctx->next += (len + 1);
    697      1.1  tsarna         ctx->naliases++;
    698      1.1  tsarna     } /* else silently ignore */
    699      1.1  tsarna }
    700      1.1  tsarna 
    701      1.1  tsarna 
    702      1.1  tsarna 
    703      1.1  tsarna static void
    704      1.1  tsarna _mdns_hostent_add_addr(hostent_ctx *ctx, const void *addr, uint16_t len)
    705      1.1  tsarna {
    706      1.1  tsarna     if ((len == ctx->host.h_length) && (ctx->naddrs < MAXADDRS)) {
    707      1.1  tsarna         memcpy(ctx->host.h_addr_list[ctx->naddrs++], addr, (size_t)len);
    708      1.1  tsarna     } /* else wrong address type or out of room... silently skip */
    709      1.1  tsarna }
    710      1.1  tsarna 
    711      1.1  tsarna 
    712      1.1  tsarna 
    713  1.3.6.1    yamt static int
    714  1.3.6.1    yamt _mdns_hostent_done(struct getnamaddr *info, hostent_ctx *ctx)
    715      1.1  tsarna {
    716  1.3.6.1    yamt     int i;
    717  1.3.6.1    yamt     char *ptr = info->buf;
    718  1.3.6.1    yamt     size_t len = info->buflen;
    719  1.3.6.1    yamt     struct hostent *hp = info->hp;
    720  1.3.6.1    yamt     struct hostent *chp = &ctx->host;
    721  1.3.6.1    yamt 
    722  1.3.6.1    yamt     hp->h_length = ctx->host.h_length;
    723  1.3.6.1    yamt     hp->h_addrtype = ctx->host.h_addrtype;
    724  1.3.6.1    yamt     HENT_ARRAY(hp->h_addr_list, ctx->naddrs, ptr, len);
    725  1.3.6.1    yamt     HENT_ARRAY(hp->h_aliases, ctx->naliases - 1, ptr, len);
    726  1.3.6.1    yamt 
    727  1.3.6.1    yamt     for (i = 0; i < ctx->naddrs; i++)
    728  1.3.6.1    yamt 	HENT_COPY(hp->h_addr_list[i], chp->h_addr_list[i],
    729  1.3.6.1    yamt 	    hp->h_length, ptr, len);
    730  1.3.6.1    yamt 
    731  1.3.6.1    yamt     hp->h_addr_list[ctx->naddrs] = NULL;
    732  1.3.6.1    yamt 
    733  1.3.6.1    yamt     HENT_SCOPY(hp->h_name, chp->h_name, ptr, len);
    734  1.3.6.1    yamt 
    735  1.3.6.1    yamt     for (i = 0; i < ctx->naliases - 1; i++)
    736  1.3.6.1    yamt 	    HENT_SCOPY(hp->h_aliases[i], chp->h_aliases[i], ptr, len);
    737  1.3.6.1    yamt     hp->h_aliases[ctx->naliases - 1] = NULL;
    738  1.3.6.1    yamt     *info->he = 0;
    739  1.3.6.1    yamt     return NS_SUCCESS;
    740  1.3.6.1    yamt nospc:
    741  1.3.6.1    yamt     *info->he = NETDB_INTERNAL;
    742  1.3.6.1    yamt     errno = ENOSPC;
    743  1.3.6.1    yamt     return NS_UNAVAIL;
    744      1.1  tsarna }
    745      1.1  tsarna 
    746      1.1  tsarna 
    747      1.1  tsarna 
    748      1.1  tsarna static void
    749      1.1  tsarna _mdns_addrinfo_cb(
    750      1.1  tsarna     DNSServiceRef           sdRef,
    751      1.1  tsarna     DNSServiceFlags         flags,
    752      1.1  tsarna     uint32_t                interfaceIndex,
    753      1.1  tsarna     DNSServiceErrorType     errorCode,
    754      1.1  tsarna     const char             *hostname,
    755      1.1  tsarna     const struct sockaddr  *address,
    756      1.1  tsarna     uint32_t                ttl,
    757      1.1  tsarna     void                   *context
    758      1.1  tsarna ) {
    759      1.1  tsarna     addrinfo_ctx *ctx = context;
    760      1.1  tsarna     struct addrinfo *ai;
    761      1.1  tsarna 
    762      1.1  tsarna     UNUSED(sdRef);
    763      1.1  tsarna     UNUSED(interfaceIndex);
    764      1.1  tsarna     UNUSED(ttl);
    765      1.1  tsarna 
    766      1.1  tsarna     if (errorCode == kDNSServiceErr_NoError) {
    767      1.1  tsarna         if (! (flags & kDNSServiceFlagsMoreComing)) {
    768      1.1  tsarna             ctx->cb_ctx.done = true;
    769      1.1  tsarna         }
    770      1.1  tsarna 
    771      1.1  tsarna         ai = allocaddrinfo((socklen_t)(address->sa_len));
    772      1.1  tsarna         if (ai) {
    773      1.1  tsarna             ai->ai_flags = ctx->start.ai_flags;
    774      1.1  tsarna             ai->ai_family = address->sa_family;
    775      1.1  tsarna             ai->ai_socktype = ctx->start.ai_socktype;
    776      1.1  tsarna             ai->ai_protocol = ctx->start.ai_protocol;
    777      1.1  tsarna             memcpy(ai->ai_addr, address, (size_t)(address->sa_len));
    778      1.1  tsarna 
    779      1.1  tsarna             if ((ctx->start.ai_flags & AI_CANONNAME) && hostname) {
    780      1.1  tsarna                 ai->ai_canonname = strdup(hostname);
    781      1.1  tsarna                 if (ai->ai_canonname[strlen(ai->ai_canonname) - 1] == '.') {
    782      1.1  tsarna                     ai->ai_canonname[strlen(ai->ai_canonname) - 1] = '\0';
    783      1.1  tsarna                 }
    784      1.1  tsarna             }
    785      1.1  tsarna 
    786      1.1  tsarna             _mdns_addrinfo_add_ai(ctx, ai);
    787      1.1  tsarna         }
    788      1.1  tsarna     }
    789      1.1  tsarna }
    790      1.1  tsarna 
    791      1.1  tsarna 
    792      1.1  tsarna 
    793      1.1  tsarna static void
    794      1.1  tsarna _mdns_hostent_cb(
    795      1.1  tsarna     DNSServiceRef           sdRef,
    796      1.1  tsarna     DNSServiceFlags         flags,
    797      1.1  tsarna     uint32_t                interfaceIndex,
    798      1.1  tsarna     DNSServiceErrorType     errorCode,
    799      1.1  tsarna     const char             *fullname,
    800      1.1  tsarna     uint16_t                rrtype,
    801      1.1  tsarna     uint16_t                rrclass,
    802      1.1  tsarna     uint16_t                rdlen,
    803      1.1  tsarna     const void             *rdata,
    804      1.1  tsarna     uint32_t                ttl,
    805      1.1  tsarna     void                   *context
    806      1.1  tsarna ) {
    807      1.1  tsarna     hostent_ctx *ctx = (hostent_ctx *)context;
    808      1.1  tsarna     char buf[NS_MAXDNAME+1];
    809      1.1  tsarna 
    810      1.1  tsarna     UNUSED(sdRef);
    811      1.1  tsarna     UNUSED(interfaceIndex);
    812      1.1  tsarna     UNUSED(rrclass);
    813      1.1  tsarna     UNUSED(ttl);
    814      1.1  tsarna 
    815      1.1  tsarna     if (! (flags & kDNSServiceFlagsMoreComing)) {
    816      1.1  tsarna         ctx->cb_ctx.done = true;
    817      1.1  tsarna     }
    818      1.1  tsarna 
    819      1.1  tsarna     if (errorCode == kDNSServiceErr_NoError) {
    820      1.1  tsarna         switch (rrtype) {
    821      1.1  tsarna         case kDNSServiceType_PTR:
    822      1.1  tsarna             if (!_mdns_rdata2name(rdata, rdlen, buf, sizeof(buf))) {
    823      1.1  tsarna                 /* corrupt response -- skip */
    824      1.1  tsarna                 return;
    825      1.1  tsarna             }
    826      1.1  tsarna 
    827      1.1  tsarna             _mdns_hostent_add_host(ctx, buf);
    828      1.1  tsarna             break;
    829      1.1  tsarna 
    830      1.1  tsarna         case kDNSServiceType_A:
    831      1.1  tsarna             if (ctx->host.h_addrtype == AF_INET) {
    832      1.1  tsarna                 _mdns_hostent_add_host(ctx, fullname);
    833      1.1  tsarna                 _mdns_hostent_add_addr(ctx, rdata, rdlen);
    834      1.1  tsarna             }
    835      1.1  tsarna             break;
    836      1.1  tsarna 
    837      1.1  tsarna         case kDNSServiceType_AAAA:
    838      1.1  tsarna             if (ctx->host.h_addrtype == AF_INET6) {
    839      1.1  tsarna                 _mdns_hostent_add_host(ctx, fullname);
    840      1.1  tsarna                 _mdns_hostent_add_addr(ctx, rdata, rdlen);
    841      1.1  tsarna             }
    842      1.1  tsarna             break;
    843      1.1  tsarna         }
    844      1.2  tsarna     } else if (errorCode == kDNSServiceErr_NoSuchRecord) {
    845      1.2  tsarna         ctx->cb_ctx.done = true;
    846      1.1  tsarna     }
    847      1.1  tsarna }
    848      1.1  tsarna 
    849      1.1  tsarna 
    850      1.1  tsarna 
    851      1.1  tsarna static void
    852      1.3  tsarna _mdns_eventloop(svc_ref *sr, callback_ctx *ctx, short timeout)
    853      1.1  tsarna {
    854      1.1  tsarna     struct pollfd fds;
    855      1.1  tsarna     int fd, ret;
    856      1.1  tsarna 
    857      1.3  tsarna     fd = DNSServiceRefSockFD(sr->sdRef);
    858      1.1  tsarna     fds.fd = fd;
    859      1.1  tsarna     fds.events = POLLRDNORM;
    860      1.1  tsarna 
    861      1.1  tsarna     while (!ctx->done) {
    862      1.3  tsarna         ret = poll(&fds, 1, timeout * 1000);
    863      1.1  tsarna         if (ret > 0) {
    864      1.3  tsarna             DNSServiceProcessResult(sr->sdRef);
    865      1.1  tsarna         } else {
    866      1.1  tsarna             break;
    867      1.1  tsarna         }
    868      1.1  tsarna     }
    869      1.1  tsarna }
    870      1.1  tsarna 
    871      1.1  tsarna 
    872      1.1  tsarna 
    873      1.1  tsarna static char *
    874      1.1  tsarna _mdns_rdata2name(const unsigned char *rdata, uint16_t rdlen, char *buf, size_t buflen)
    875      1.1  tsarna {
    876      1.1  tsarna     unsigned char l;
    877      1.1  tsarna     char *r = buf;
    878      1.1  tsarna 
    879      1.1  tsarna     /* illegal 0-size answer or not enough room for even "." */
    880      1.1  tsarna     if ((!rdlen) || (rdlen < 2)) {
    881      1.1  tsarna         return NULL;
    882      1.1  tsarna     }
    883      1.1  tsarna 
    884      1.1  tsarna     buflen--; /* reserve space for terminating NUL now */
    885      1.1  tsarna 
    886      1.1  tsarna     /* special case empty as "." */
    887      1.1  tsarna     if ((rdlen == 1) && (!*rdata)) {
    888      1.1  tsarna         strcpy(buf, ".");
    889      1.1  tsarna 
    890      1.1  tsarna         return r;
    891      1.1  tsarna     }
    892      1.1  tsarna 
    893      1.1  tsarna     while (rdlen && *rdata) {
    894      1.1  tsarna         /* label length byte */
    895      1.1  tsarna         l = *rdata++; rdlen--;
    896      1.1  tsarna 
    897      1.1  tsarna         if (l > 63) {
    898      1.1  tsarna             /* compression or bitstrings -- shouldn't happen */
    899      1.1  tsarna             return NULL;
    900      1.1  tsarna         } else if (l > buflen) {
    901      1.1  tsarna             /* not enough space */
    902      1.1  tsarna             return NULL;
    903      1.1  tsarna         } else if (l > rdlen) {
    904      1.1  tsarna             /* label shouldn't be longer than remaining rdata */
    905      1.1  tsarna             return NULL;
    906      1.1  tsarna         } else if (!l) {
    907      1.1  tsarna             /* empty label -- should be done */
    908      1.1  tsarna             if (rdlen) {
    909      1.1  tsarna                 /* but more left!? */
    910      1.1  tsarna                 return NULL;
    911      1.1  tsarna             } else {
    912      1.1  tsarna                 break;
    913      1.1  tsarna             }
    914      1.1  tsarna         }
    915      1.1  tsarna 
    916      1.1  tsarna         memcpy(buf, rdata, (size_t)l);
    917      1.1  tsarna         rdata += l; buf += l;
    918      1.1  tsarna         rdlen -= l; buflen -= l;
    919      1.1  tsarna 
    920      1.1  tsarna         /* Another label to come? add a separator */
    921      1.1  tsarna         if (rdlen && *rdata) {
    922      1.1  tsarna             if (!buflen) {
    923      1.1  tsarna                 return NULL;
    924      1.1  tsarna             }
    925      1.1  tsarna 
    926      1.1  tsarna             *buf++ = '.'; buflen--;
    927      1.1  tsarna         }
    928      1.1  tsarna     }
    929      1.1  tsarna 
    930      1.1  tsarna     /* we reserved space above, so we know we have space
    931      1.1  tsarna        to add this termination */
    932      1.1  tsarna 
    933      1.1  tsarna     *buf = '\0';
    934      1.1  tsarna 
    935      1.1  tsarna     return r;
    936      1.1  tsarna }
    937      1.1  tsarna 
    938      1.1  tsarna 
    939      1.1  tsarna 
    940      1.3  tsarna int
    941      1.3  tsarna search_init(search_iter *iter, const char *name, const char **first)
    942      1.1  tsarna {
    943      1.2  tsarna     const char *c = name, *cmp;
    944      1.2  tsarna     int dots = 0, enddot = 0;
    945      1.2  tsarna     size_t len, cl;
    946      1.3  tsarna 
    947      1.3  tsarna     iter->conf = get_res_conf();
    948      1.3  tsarna     if (!iter->conf) {
    949      1.3  tsarna         h_errno = NETDB_INTERNAL;
    950      1.3  tsarna         return NS_UNAVAIL;
    951      1.3  tsarna     }
    952      1.1  tsarna 
    953      1.1  tsarna     iter->name = name;
    954      1.1  tsarna     iter->baselen = 0;
    955      1.2  tsarna     iter->abs_first = iter->abs_last = false;
    956      1.3  tsarna     iter->next_search = iter->conf->search_domains;
    957      1.1  tsarna 
    958      1.1  tsarna     while (*c) {
    959      1.1  tsarna         if (*c == '.') {
    960      1.1  tsarna             dots++;
    961      1.2  tsarna             enddot = 1;
    962      1.1  tsarna         } else {
    963      1.2  tsarna             enddot = 0;
    964      1.1  tsarna         }
    965      1.1  tsarna         c++;
    966      1.1  tsarna     }
    967      1.1  tsarna 
    968      1.2  tsarna     if (svc_flags & kDNSServiceFlagsForceMulticast) {
    969      1.2  tsarna         if (dots) {
    970      1.3  tsarna             iter->next_search = iter->conf->no_search;
    971      1.2  tsarna             if ((dots - enddot) == 1) {
    972      1.2  tsarna                 len = strlen(iter->name);
    973      1.2  tsarna                 cl = strlen(".local") + enddot;
    974      1.2  tsarna                 if (len > cl) {
    975      1.2  tsarna                     cmp = enddot ? ".local." : ".local";
    976      1.2  tsarna                     c = iter->name + len - cl;
    977      1.2  tsarna 
    978      1.2  tsarna                     if (!strcasecmp(c, cmp)) {
    979      1.2  tsarna                         iter->abs_first = true;
    980      1.2  tsarna                     }
    981      1.2  tsarna                 }
    982      1.2  tsarna             }
    983      1.2  tsarna         }
    984      1.1  tsarna     } else {
    985      1.3  tsarna         if (dots >= iter->conf->ndots) {
    986      1.2  tsarna             iter->abs_first = true;
    987      1.2  tsarna         } else {
    988      1.2  tsarna             iter->abs_last = true;
    989      1.2  tsarna         }
    990      1.1  tsarna 
    991      1.2  tsarna         if (enddot) {
    992      1.2  tsarna             /* absolute; don't search */
    993      1.3  tsarna             iter->next_search = iter->conf->no_search;
    994      1.2  tsarna         }
    995      1.1  tsarna     }
    996      1.3  tsarna 
    997      1.3  tsarna     *first = search_next(iter);
    998      1.3  tsarna     if (!first) {
    999      1.3  tsarna         search_done(iter);
   1000      1.3  tsarna         h_errno = HOST_NOT_FOUND;
   1001      1.3  tsarna         return NS_NOTFOUND;
   1002      1.3  tsarna     }
   1003      1.3  tsarna 
   1004      1.3  tsarna     return NS_SUCCESS;
   1005      1.1  tsarna }
   1006      1.1  tsarna 
   1007      1.1  tsarna 
   1008      1.1  tsarna 
   1009      1.1  tsarna const char *
   1010      1.1  tsarna search_next(search_iter *iter)
   1011      1.1  tsarna {
   1012      1.1  tsarna     const char *a = NULL;
   1013      1.1  tsarna     res_state res;
   1014      1.1  tsarna     size_t len;
   1015      1.1  tsarna 
   1016      1.1  tsarna     if (iter->abs_first) {
   1017      1.1  tsarna         iter->abs_first = false;
   1018      1.1  tsarna         return iter->name;
   1019      1.1  tsarna     }
   1020      1.1  tsarna 
   1021      1.2  tsarna     while (*(iter->next_search)) {
   1022      1.2  tsarna         if (!iter->baselen) {
   1023      1.2  tsarna             iter->baselen = strlcpy(iter->buf, iter->name, sizeof(iter->buf));
   1024      1.2  tsarna             if (iter->baselen >= sizeof(iter->buf) - 1) {
   1025      1.2  tsarna                 /* original is too long, don't try any search domains */
   1026      1.3  tsarna                 iter->next_search = iter->conf->no_search;
   1027      1.2  tsarna                 break;
   1028      1.1  tsarna             }
   1029      1.2  tsarna 
   1030      1.2  tsarna             iter->buf[iter->baselen++] = '.';
   1031      1.2  tsarna         }
   1032      1.2  tsarna 
   1033      1.2  tsarna         len = strlcpy(&(iter->buf[iter->baselen]),
   1034      1.2  tsarna             *(iter->next_search),
   1035      1.2  tsarna             sizeof(iter->buf) - iter->baselen);
   1036      1.2  tsarna 
   1037      1.2  tsarna         iter->next_search++;
   1038      1.2  tsarna 
   1039      1.2  tsarna         if (len >= sizeof(iter->buf) - iter->baselen) {
   1040      1.2  tsarna             /* result was too long */
   1041      1.2  tsarna             continue;
   1042      1.1  tsarna         }
   1043      1.2  tsarna 
   1044      1.2  tsarna         return iter->buf;
   1045      1.1  tsarna     }
   1046      1.1  tsarna 
   1047      1.1  tsarna     if (iter->abs_last) {
   1048      1.1  tsarna         iter->abs_last = false;
   1049      1.1  tsarna         return iter->name;
   1050      1.1  tsarna     }
   1051      1.1  tsarna 
   1052      1.1  tsarna     return NULL;
   1053      1.1  tsarna }
   1054      1.2  tsarna 
   1055      1.2  tsarna 
   1056      1.2  tsarna 
   1057      1.3  tsarna void
   1058      1.3  tsarna search_done(search_iter *iter)
   1059      1.3  tsarna {
   1060      1.3  tsarna     if (iter->conf) {
   1061      1.3  tsarna         put_res_conf(iter->conf);
   1062      1.3  tsarna         iter->conf = NULL;
   1063      1.3  tsarna     }
   1064      1.3  tsarna }
   1065      1.3  tsarna 
   1066      1.3  tsarna 
   1067      1.3  tsarna 
   1068      1.2  tsarna /*
   1069      1.2  tsarna  * Is domain appropriate to be in the domain search list?
   1070      1.2  tsarna  * For mdnsd, take everything. For multicast_dns, only "local"
   1071      1.2  tsarna  * if present.
   1072      1.2  tsarna  */
   1073      1.2  tsarna bool
   1074      1.2  tsarna searchable_domain(char *d)
   1075      1.2  tsarna {
   1076      1.2  tsarna     if (!(svc_flags & kDNSServiceFlagsForceMulticast)) {
   1077      1.2  tsarna         return true;
   1078      1.2  tsarna     }
   1079      1.2  tsarna 
   1080      1.2  tsarna     if (!strcasecmp(d, "local") || !strcasecmp(d, "local.")) {
   1081      1.2  tsarna         return true;
   1082      1.2  tsarna     }
   1083      1.2  tsarna 
   1084      1.2  tsarna     return false;
   1085      1.2  tsarna }
   1086      1.3  tsarna 
   1087      1.3  tsarna 
   1088      1.3  tsarna 
   1089      1.3  tsarna static void
   1090      1.3  tsarna destroy_svc_ref(svc_ref *sr)
   1091      1.3  tsarna {
   1092      1.3  tsarna     /* assumes not on conn list */
   1093      1.3  tsarna 
   1094      1.3  tsarna     if (sr) {
   1095      1.3  tsarna         DNSServiceRefDeallocate(sr->sdRef);
   1096      1.3  tsarna         free(sr);
   1097      1.3  tsarna     }
   1098      1.3  tsarna }
   1099      1.3  tsarna 
   1100      1.3  tsarna 
   1101      1.3  tsarna 
   1102      1.3  tsarna static svc_ref *
   1103      1.3  tsarna get_svc_ref(void)
   1104      1.3  tsarna {
   1105      1.3  tsarna     svc_ref *sr;
   1106      1.3  tsarna 
   1107      1.3  tsarna     LOCK(&conn_list_lock);
   1108      1.3  tsarna 
   1109      1.3  tsarna     if (getpid() != my_pid) {
   1110      1.3  tsarna         my_pid = getpid();
   1111      1.3  tsarna 
   1112      1.3  tsarna         /*
   1113      1.3  tsarna          * We forked and kept running. We don't want to share
   1114      1.3  tsarna          * connections with the parent or we'll garble each others
   1115      1.3  tsarna          * comms, so throw away the parent's list and start over
   1116      1.3  tsarna          */
   1117      1.3  tsarna         while ((sr = SLIST_FIRST(&conn_list))) {
   1118      1.3  tsarna             SLIST_REMOVE_HEAD(&conn_list, entries);
   1119      1.3  tsarna             destroy_svc_ref(sr);
   1120      1.3  tsarna         }
   1121      1.3  tsarna 
   1122      1.3  tsarna         sr = NULL;
   1123      1.3  tsarna     } else {
   1124      1.3  tsarna         /* try to recycle a connection */
   1125      1.3  tsarna         sr = SLIST_FIRST(&conn_list);
   1126      1.3  tsarna         if (sr) {
   1127      1.3  tsarna             SLIST_REMOVE_HEAD(&conn_list, entries);
   1128      1.3  tsarna             conn_count--;
   1129      1.3  tsarna         }
   1130      1.3  tsarna     }
   1131      1.3  tsarna 
   1132      1.3  tsarna     UNLOCK(&conn_list_lock);
   1133      1.3  tsarna 
   1134      1.3  tsarna     if (!sr) {
   1135      1.3  tsarna         /* none available, we need a new one */
   1136      1.3  tsarna 
   1137      1.3  tsarna         sr = calloc(sizeof(svc_ref), 1);
   1138      1.3  tsarna         if (sr) {
   1139      1.3  tsarna             if (DNSServiceCreateConnection(&(sr->sdRef))) {
   1140      1.3  tsarna                 free(sr);
   1141      1.3  tsarna                 return NULL;
   1142      1.3  tsarna             }
   1143      1.3  tsarna 
   1144      1.3  tsarna             if (fcntl(DNSServiceRefSockFD(sr->sdRef), F_SETFD, FD_CLOEXEC) < 0) {
   1145      1.3  tsarna                 destroy_svc_ref(sr);
   1146      1.3  tsarna                 sr = NULL;
   1147      1.3  tsarna             }
   1148      1.3  tsarna         }
   1149      1.3  tsarna     }
   1150      1.3  tsarna 
   1151      1.3  tsarna     return sr;
   1152      1.3  tsarna }
   1153      1.3  tsarna 
   1154      1.3  tsarna 
   1155      1.3  tsarna 
   1156      1.3  tsarna static void
   1157      1.3  tsarna put_svc_ref(svc_ref *sr)
   1158      1.3  tsarna {
   1159      1.3  tsarna     if (sr) {
   1160      1.3  tsarna         LOCK(&conn_list_lock);
   1161      1.3  tsarna 
   1162      1.3  tsarna         sr->uses++;
   1163      1.3  tsarna 
   1164      1.3  tsarna         /* if slow start or aged out, destroy */
   1165      1.3  tsarna         if ((svc_puts++ < SLOWSTART_LOOKUPS)
   1166      1.3  tsarna         ||  (conn_count && (sr->uses > REUSE_TIMES))) {
   1167      1.3  tsarna             UNLOCK(&conn_list_lock);
   1168      1.3  tsarna             destroy_svc_ref(sr);
   1169      1.3  tsarna             return;
   1170      1.3  tsarna         }
   1171      1.3  tsarna 
   1172      1.3  tsarna         conn_count++;
   1173      1.3  tsarna 
   1174      1.3  tsarna         SLIST_INSERT_HEAD(&conn_list, sr, entries);
   1175      1.3  tsarna 
   1176      1.3  tsarna         UNLOCK(&conn_list_lock);
   1177      1.3  tsarna     }
   1178      1.3  tsarna }
   1179      1.3  tsarna 
   1180      1.3  tsarna 
   1181      1.3  tsarna 
   1182      1.3  tsarna /*
   1183      1.3  tsarna  * determine if this is a call we should retry with a fresh
   1184      1.3  tsarna  * connection, for example if mdnsd went away and came back.
   1185      1.3  tsarna  */
   1186      1.3  tsarna static bool
   1187      1.3  tsarna retry_query(svc_ref **sr, DNSServiceErrorType err)
   1188      1.3  tsarna {
   1189      1.3  tsarna     if ((err == kDNSServiceErr_Unknown)
   1190      1.3  tsarna     ||  (err == kDNSServiceErr_ServiceNotRunning)) {
   1191      1.3  tsarna         /* these errors might indicate a stale socket */
   1192      1.3  tsarna         if ((*sr)->uses) {
   1193      1.3  tsarna             /* this was an old socket, so kill it and get another */
   1194      1.3  tsarna             destroy_svc_ref(*sr);
   1195      1.3  tsarna             *sr = get_svc_ref();
   1196      1.3  tsarna             if (*sr) {
   1197      1.3  tsarna                 /* we can retry */
   1198      1.3  tsarna                 return true;
   1199      1.3  tsarna             }
   1200      1.3  tsarna         }
   1201      1.3  tsarna     }
   1202      1.3  tsarna 
   1203      1.3  tsarna     return false;
   1204      1.3  tsarna }
   1205      1.3  tsarna 
   1206      1.3  tsarna 
   1207      1.3  tsarna 
   1208      1.3  tsarna static void
   1209      1.3  tsarna decref_res_conf(res_conf *rc)
   1210      1.3  tsarna {
   1211      1.3  tsarna     rc->refcount--;
   1212      1.3  tsarna 
   1213      1.3  tsarna     if (rc->refcount < 1) {
   1214      1.3  tsarna         if ((rc->no_search = rc->search_domains)) {
   1215      1.3  tsarna             for (; *(rc->no_search); rc->no_search++) {
   1216      1.3  tsarna                 free(*(rc->no_search));
   1217      1.3  tsarna             }
   1218      1.3  tsarna 
   1219      1.3  tsarna             free(rc->search_domains);
   1220      1.3  tsarna         }
   1221      1.3  tsarna 
   1222      1.3  tsarna         free(rc);
   1223      1.3  tsarna     }
   1224      1.3  tsarna }
   1225      1.3  tsarna 
   1226      1.3  tsarna 
   1227      1.3  tsarna 
   1228      1.3  tsarna res_conf *
   1229      1.3  tsarna get_res_conf(void)
   1230      1.3  tsarna {
   1231      1.3  tsarna     struct timespec last_change;
   1232      1.3  tsarna     res_state res;
   1233      1.3  tsarna     res_conf *rc;
   1234      1.3  tsarna 
   1235      1.3  tsarna     LOCK(&res_conf_lock);
   1236      1.3  tsarna 
   1237      1.3  tsarna     /* check if resolver config changed */
   1238      1.3  tsarna 
   1239      1.3  tsarna     res = __res_get_state();
   1240      1.3  tsarna     if (res) {
   1241      1.3  tsarna         res_check(res, &last_change);
   1242      1.3  tsarna         if (timespeccmp(&last_config, &last_change, <)) {
   1243      1.3  tsarna             if (cur_res_conf) {
   1244      1.3  tsarna                 decref_res_conf(cur_res_conf);
   1245      1.3  tsarna             }
   1246      1.3  tsarna             cur_res_conf = new_res_conf(res);
   1247      1.3  tsarna             if (cur_res_conf) {
   1248      1.3  tsarna                 last_config = last_change;
   1249      1.3  tsarna             }
   1250      1.3  tsarna         }
   1251      1.3  tsarna         __res_put_state(res);
   1252      1.3  tsarna     }
   1253      1.3  tsarna 
   1254      1.3  tsarna     rc = cur_res_conf;
   1255      1.3  tsarna     if (rc) {
   1256      1.3  tsarna         rc->refcount++;
   1257      1.3  tsarna     }
   1258      1.3  tsarna 
   1259      1.3  tsarna     UNLOCK(&res_conf_lock);
   1260      1.3  tsarna 
   1261      1.3  tsarna     return rc;
   1262      1.3  tsarna }
   1263      1.3  tsarna 
   1264      1.3  tsarna 
   1265      1.3  tsarna 
   1266      1.3  tsarna static void
   1267      1.3  tsarna put_res_conf(res_conf *rc)
   1268      1.3  tsarna {
   1269      1.3  tsarna     LOCK(&res_conf_lock);
   1270      1.3  tsarna 
   1271      1.3  tsarna     decref_res_conf(rc);
   1272      1.3  tsarna 
   1273      1.3  tsarna     UNLOCK(&res_conf_lock);
   1274      1.3  tsarna }
   1275      1.3  tsarna 
   1276      1.3  tsarna 
   1277      1.3  tsarna 
   1278      1.3  tsarna static res_conf *
   1279      1.3  tsarna new_res_conf(res_state res)
   1280      1.3  tsarna {
   1281      1.3  tsarna     res_conf *rc;
   1282      1.3  tsarna     int count = 0;
   1283      1.3  tsarna     char **sd, *p;
   1284      1.3  tsarna 
   1285      1.3  tsarna     rc = calloc(sizeof(res_conf), 1);
   1286      1.3  tsarna     if (rc) {
   1287      1.3  tsarna         rc->refcount = 1;
   1288      1.3  tsarna 
   1289      1.3  tsarna         sd = res->dnsrch;
   1290      1.3  tsarna         while (*sd) {
   1291      1.3  tsarna             if (searchable_domain(*sd)) {
   1292      1.3  tsarna                 count++;
   1293      1.3  tsarna             }
   1294      1.3  tsarna             sd++;
   1295      1.3  tsarna         }
   1296      1.3  tsarna 
   1297      1.3  tsarna         rc->search_domains = calloc(sizeof(char *), count + 1);
   1298      1.3  tsarna         if (!(rc->search_domains)) {
   1299      1.3  tsarna             decref_res_conf(rc);
   1300      1.3  tsarna             return NULL;
   1301      1.3  tsarna         }
   1302      1.3  tsarna 
   1303      1.3  tsarna         sd = res->dnsrch;
   1304      1.3  tsarna         rc->no_search = rc->search_domains;
   1305      1.3  tsarna         while (*sd) {
   1306      1.3  tsarna             if (searchable_domain(*sd)) {
   1307      1.3  tsarna                 *(rc->no_search) = p = strdup(*sd);
   1308      1.3  tsarna                 if (!p) {
   1309      1.3  tsarna                     decref_res_conf(rc);
   1310      1.3  tsarna                     return NULL;
   1311      1.3  tsarna                 }
   1312      1.3  tsarna 
   1313      1.3  tsarna                 rc->no_search++;
   1314      1.3  tsarna             }
   1315      1.3  tsarna             sd++;
   1316      1.3  tsarna         }
   1317      1.3  tsarna 
   1318      1.3  tsarna         rc->timeout = res->retrans;
   1319      1.3  tsarna 
   1320      1.3  tsarna         if (svc_flags & kDNSServiceFlagsForceMulticast) {
   1321      1.3  tsarna             rc->ndots = 1;
   1322      1.3  tsarna             if (rc->timeout > 2) {
   1323      1.3  tsarna                 rc->timeout = 2;
   1324      1.3  tsarna             }
   1325      1.3  tsarna         } else {
   1326      1.3  tsarna             rc->ndots = res->ndots;
   1327      1.3  tsarna         }
   1328      1.3  tsarna     }
   1329      1.3  tsarna 
   1330      1.3  tsarna     return rc;
   1331      1.3  tsarna }
   1332      1.3  tsarna 
   1333      1.3  tsarna 
   1334      1.3  tsarna 
   1335      1.3  tsarna static short
   1336      1.3  tsarna get_timeout(void)
   1337      1.3  tsarna {
   1338      1.3  tsarna     short timeout = 5;
   1339      1.3  tsarna     res_conf *rc;
   1340      1.3  tsarna 
   1341      1.3  tsarna     rc = get_res_conf();
   1342      1.3  tsarna     if (rc) {
   1343      1.3  tsarna         timeout = rc->timeout;
   1344      1.3  tsarna         put_res_conf(rc);
   1345      1.3  tsarna     }
   1346      1.3  tsarna 
   1347      1.3  tsarna     return timeout;
   1348      1.3  tsarna }
   1349