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