1 /* $NetBSD: nss_mdnsd.c,v 1.4 2014/03/31 02:03:38 christos 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 *
nss_module_register(const char * source,u_int * nelems,nss_module_unregister_fn * unreg)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
_mdns_getaddrinfo(void * cbrv,void * cbdata,va_list ap)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
_mdns_getaddrinfo_abs(const char * name,DNSServiceProtocol proto,svc_ref ** sr,addrinfo_ctx * ctx,short timeout)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
_mdns_gethtbyaddr(void * cbrv,void * cbdata,va_list ap)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
_mdns_gethtbyname(void * cbrv,void * cbdata,va_list ap)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
_mdns_gethtbyname_abs(struct getnamaddr * info,struct hostent_ctx * ctx,const char * name,int rrtype,svc_ref ** sr,short timeout)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
_mdns_addrinfo_init(addrinfo_ctx * ctx,const struct addrinfo * ai)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
_mdns_addrinfo_add_ai(addrinfo_ctx * ctx,struct addrinfo * ai)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 *
_mdns_addrinfo_done(addrinfo_ctx * ctx)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
_mdns_hostent_init(hostent_ctx * ctx,int af,int addrlen)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
_mdns_hostent_add_host(hostent_ctx * ctx,const char * name)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
_mdns_hostent_add_addr(hostent_ctx * ctx,const void * addr,uint16_t len)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
_mdns_hostent_done(struct getnamaddr * info,hostent_ctx * ctx)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
_mdns_addrinfo_cb(DNSServiceRef sdRef,DNSServiceFlags flags,uint32_t interfaceIndex,DNSServiceErrorType errorCode,const char * hostname,const struct sockaddr * address,uint32_t ttl,void * context)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
_mdns_hostent_cb(DNSServiceRef sdRef,DNSServiceFlags flags,uint32_t interfaceIndex,DNSServiceErrorType errorCode,const char * fullname,uint16_t rrtype,uint16_t rrclass,uint16_t rdlen,const void * rdata,uint32_t ttl,void * context)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
_mdns_eventloop(svc_ref * sr,callback_ctx * ctx,short timeout)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 *
_mdns_rdata2name(const unsigned char * rdata,uint16_t rdlen,char * buf,size_t buflen)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
search_init(search_iter * iter,const char * name,const char ** first)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 *
search_next(search_iter * iter)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
search_done(search_iter * iter)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
searchable_domain(char * d)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
destroy_svc_ref(svc_ref * sr)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 *
get_svc_ref(void)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
put_svc_ref(svc_ref * sr)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
retry_query(svc_ref ** sr,DNSServiceErrorType err)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
decref_res_conf(res_conf * rc)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 *
get_res_conf(void)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
put_res_conf(res_conf * rc)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 *
new_res_conf(res_state res)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
get_timeout(void)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