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