1/*	$NetBSD: nss_mdnsd.c,v 1.3 2009/11/04 23:34:59 tsarna 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 */
69static SLIST_HEAD(, svc_ref) conn_list = LIST_HEAD_INITIALIZER(&conn_list);
70static unsigned int conn_count = 0;
71static pid_t my_pid;
72struct timespec last_config;
73
74typedef 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
89static 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 */
98static pthread_mutex_t conn_list_lock = PTHREAD_MUTEX_INITIALIZER;
99
100extern 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
115typedef struct callback_ctx {
116    bool done;
117} callback_ctx;
118
119typedef 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
129typedef 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
136typedef 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
144static res_conf *cur_res_conf;
145
146/* protects above data */
147static pthread_mutex_t res_conf_lock = PTHREAD_MUTEX_INITIALIZER;
148
149typedef 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
159static DNSServiceFlags svc_flags = 0;
160
161ns_mtab *nss_module_register(const char *, u_int *, nss_module_unregister_fn *);
162static int load_config(res_state);
163
164static int _mdns_getaddrinfo(void *, void *, va_list);
165static int _mdns_gethtbyaddr(void *, void *, va_list);
166static int _mdns_gethtbyname(void *, void *, va_list);
167
168static int _mdns_getaddrinfo_abs(const char *, DNSServiceProtocol,
169    svc_ref **, addrinfo_ctx *, short);
170static void _mdns_addrinfo_init(addrinfo_ctx *, const struct addrinfo *);
171static void _mdns_addrinfo_add_ai(addrinfo_ctx *, struct addrinfo *);
172static struct addrinfo *_mdns_addrinfo_done(addrinfo_ctx *);
173
174static int _mdns_gethtbyname_abs(struct getnamaddr *, struct hostent_ctx *,
175    const char *, int, svc_ref **, short);
176static void _mdns_hostent_init(hostent_ctx *, int, int);
177static void _mdns_hostent_add_host(hostent_ctx *, const char *);
178static void _mdns_hostent_add_addr(hostent_ctx *, const void *, uint16_t);
179static int _mdns_hostent_done(struct getnamaddr *, hostent_ctx *);
180
181static void _mdns_addrinfo_cb(DNSServiceRef, DNSServiceFlags,
182    uint32_t, DNSServiceErrorType, const char *, const struct sockaddr *,
183    uint32_t, void *);
184static void _mdns_hostent_cb(DNSServiceRef, DNSServiceFlags,
185    uint32_t, DNSServiceErrorType, const char *, uint16_t, uint16_t, uint16_t,
186    const void *, uint32_t, void *);
187static void _mdns_eventloop(svc_ref *, callback_ctx *, short);
188
189static char *_mdns_rdata2name(const unsigned char *, uint16_t,
190    char *, size_t);
191
192static int search_init(search_iter *, const char *, const char **);
193static void search_done(search_iter *);
194static const char *search_next(search_iter *);
195static bool searchable_domain(char *);
196
197static void destroy_svc_ref(svc_ref *);
198static svc_ref *get_svc_ref(void);
199static void put_svc_ref(svc_ref *);
200static bool retry_query(svc_ref **, DNSServiceErrorType);
201
202static void decref_res_conf(res_conf *);
203static res_conf *get_res_conf(void);
204static void put_res_conf(res_conf *);
205static res_conf *new_res_conf(res_state);
206static short get_timeout(void);
207
208static 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
216ns_mtab *
217nss_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
234static 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
303static 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
351static 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
470static 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
539static 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
587static 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
599static 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
609static 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
639static 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
661static 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
703static 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
713static 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;
740nospc:
741    *info->he = NETDB_INTERNAL;
742    errno = ENOSPC;
743    return NS_UNAVAIL;
744}
745
746
747
748static 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
793static 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
851static 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
873static 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
940int
941search_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
1009const char *
1010search_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
1057void
1058search_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 */
1073bool
1074searchable_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
1089static void
1090destroy_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
1102static svc_ref *
1103get_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
1156static void
1157put_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 */
1186static bool
1187retry_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
1208static void
1209decref_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
1228res_conf *
1229get_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
1266static void
1267put_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
1278static res_conf *
1279new_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
1335static short
1336get_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