xref: /trueos/lib/libosxsupport/mdns_module.c (revision a7f9c1ab18631ff83a01ef6877231d2b57c29d00)
1 /*
2  * Copyright (c) 2008-2011 Apple Inc. All rights reserved.
3  *
4  * @APPLE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. Please obtain a copy of the License at
10  * http://www.opensource.apple.com/apsl/ and read it before using this
11  * file.
12  *
13  * The Original Code and all software distributed under the License are
14  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18  * Please see the License for the specific language governing rights and
19  * limitations under the License.
20  *
21  * @APPLE_LICENSE_HEADER_END@
22  */
23 /*
24  * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
25  *
26  * Permission to use, copy, modify, and distribute this software for any
27  * purpose with or without fee is hereby granted, provided that the above
28  * copyright notice and this permission notice appear in all copies.
29  *
30  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
31  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
32  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
33  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
34  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
35  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
36  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
37  * SOFTWARE.
38  */
39 /*
40  * Copyright (c) 1988, 1993
41  *    The Regents of the University of California.  All rights reserved.
42  *
43  * Redistribution and use in source and binary forms, with or without
44  * modification, are permitted provided that the following conditions
45  * are met:
46  * 1. Redistributions of source code must retain the above copyright
47  *    notice, this list of conditions and the following disclaimer.
48  * 2. Redistributions in binary form must reproduce the above copyright
49  *    notice, this list of conditions and the following disclaimer in the
50  *    documentation and/or other materials provided with the distribution.
51  * 4. Neither the name of the University nor the names of its contributors
52  *    may be used to endorse or promote products derived from this software
53  *    without specific prior written permission.
54  *
55  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65  * SUCH DAMAGE.
66  */
67 /*
68  * Portions Copyright (c) 1993 by Digital Equipment Corporation.
69  *
70  * Permission to use, copy, modify, and distribute this software for any
71  * purpose with or without fee is hereby granted, provided that the above
72  * copyright notice and this permission notice appear in all copies, and that
73  * the name of Digital Equipment Corporation not be used in advertising or
74  * publicity pertaining to distribution of the document or software without
75  * specific, written prior permission.
76  *
77  * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
78  * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
79  * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
80  * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
81  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
82  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
83  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
84  * SOFTWARE.
85  */
86 
87 #include "ils.h"
88 #include "netdb.h"
89 #include "si_module.h"
90 
91 #include <assert.h>
92 #include <arpa/inet.h>
93 #include <arpa/nameser.h>
94 #include <arpa/nameser_compat.h>
95 #include <libkern/OSAtomic.h>
96 #include <netinet/in.h>
97 #include <ctype.h>
98 #include <dns_sd.h>
99 #include <dnsinfo.h>
100 #include <errno.h>
101 #include <nameser.h>
102 #include <notify.h>
103 #include <pthread.h>
104 #include <resolv.h>
105 #include <stdio.h>
106 #include <stdlib.h>
107 #include <string.h>
108 #include <sys/event.h>
109 #include <sys/param.h>
110 #include <sys/time.h>
111 #include <sys/types.h>
112 #include <sys/socket.h>
113 #include <net/if.h>
114 #include <time.h>
115 #include <unistd.h>
116 #include <asl.h>
117 #include <dns.h>
118 #include <dns_util.h>
119 #include <TargetConditionals.h>
120 #include <dispatch/dispatch.h>
121 
122 /* from dns_util.c */
123 #define DNS_MAX_RECEIVE_SIZE 65536
124 
125 #define INET_NTOP_AF_INET_OFFSET 4
126 #define INET_NTOP_AF_INET6_OFFSET 8
127 
128 #define IPPROTO_UNSPEC 0
129 
130 #define GOT_DATA 1
131 #define GOT_ERROR 2
132 #define SHORT_AAAA_EXTRA 2
133 #define MEDIUM_AAAA_EXTRA 5
134 #define LONG_AAAA_EXTRA 10
135 
136 #define MDNS_DEBUG_FILE "/etc/.mdns_debug"
137 #define MDNS_DEBUG_STDOUT 0x00000001
138 #define MDNS_DEBUG_STDERR 0x00000002
139 #define MDNS_DEBUG_ASL    0x00000004
140 #define MDNS_DEBUG_OUT    0x00000007
141 #define MDNS_DEBUG_MORE   0x00000010
142 
143 static int _mdns_debug = 0;
144 
145 // mutex protects DNSServiceProcessResult and DNSServiceRefDeallocate
146 static pthread_mutex_t _mdns_mutex = PTHREAD_MUTEX_INITIALIZER;
147 
148 typedef struct {
149 	uint16_t priority;
150 	uint16_t weight;
151 	uint16_t port;
152 	uint8_t target[0];
153 } mdns_rr_srv_t;
154 
155 typedef struct mdns_srv_t mdns_srv_t;
156 struct mdns_srv_t {
157 	si_srv_t srv;
158 	mdns_srv_t *next;
159 };
160 
161 typedef struct {
162 	struct hostent host;
163 	int alias_count;
164 	int addr_count;
165 } mdns_hostent_t;
166 
167 typedef struct {
168 	mdns_hostent_t *h4;
169 	mdns_hostent_t *h6;
170 	mdns_srv_t *srv;
171 	uint64_t ttl;
172 	uint32_t ifnum;
173 } mdns_reply_t;
174 
175 static uint32_t _mdns_generation = 0;
176 static DNSServiceRef _mdns_sdref;
177 static DNSServiceRef _mdns_old_sdref;
178 
179 static void _mdns_hostent_clear(mdns_hostent_t *h);
180 static void _mdns_reply_clear(mdns_reply_t *r);
181 static int _mdns_search(const char *name, int class, int type, const char *interface, DNSServiceFlags flags, uint8_t *answer, uint32_t *anslen, mdns_reply_t *reply);
182 
183 static const char hexchar[] = "0123456789abcdef";
184 
185 #define BILLION 1000000000
186 
187 /* length of a reverse DNS IPv6 address query name, e.g. "9.4.a.f.c.e.e.f.e.e.1.5.4.1.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.e.f.ip6.arpa" */
188 #define IPv6_REVERSE_LEN 72
189 
190 /* index of the trailing char that must be "8", "9", "A", "a", "b", or "B" */
191 #define IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR 58
192 
193 /* index of low-order nibble of embedded scope id */
194 #define IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW 48
195 
196 const static uint8_t hexval[128] = {
197 	0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,		/*  0 - 15 */
198 	0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,		/* 16 - 31 */
199 	0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,		/* 32 - 47 */
200 	0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  0,  0,  0,  0,  0,  0,		/* 48 - 63 */
201 	0, 10, 11, 12, 13, 14, 15,  0,  0,  0,  0,  0,  0,  0,  0,  0,		/* 64 - 79 */
202 	0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,		/* 80 - 95 */
203 	0, 10, 11, 12, 13, 14, 15,  0,  0,  0,  0,  0,  0,  0,  0,  0,		/* 96 - 111 */
204 	0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0		/* 112 - 127 */
205 };
206 
207 static void
_mdns_debug_message(const char * str,...)208 _mdns_debug_message(const char *str, ...)
209 {
210 	va_list v;
211 	char *out = NULL;
212 	if ((_mdns_debug & MDNS_DEBUG_OUT) == 0) return;
213 
214 	va_start(v, str);
215 	vasprintf(&out, str, v);
216 	if (out == NULL) return;
217 
218 	if (_mdns_debug & MDNS_DEBUG_STDOUT) fprintf(stdout, "%s", out);
219 	if (_mdns_debug & MDNS_DEBUG_STDERR) fprintf(stderr, "%s", out);
220 	if (_mdns_debug & MDNS_DEBUG_ASL) asl_log_message(ASL_LEVEL_NOTICE, "%s", out);
221 	free(out);
222 
223 	va_end(v);
224 }
225 
226 static char *
_mdns_reverse_ipv4(const char * addr)227 _mdns_reverse_ipv4(const char *addr)
228 {
229 	union
230 	{
231 		uint32_t a;
232 		unsigned char b[4];
233 	} ab;
234 	char *p;
235 
236 	if (addr == NULL) return NULL;
237 
238 	memcpy(&(ab.a), addr, 4);
239 
240 	asprintf(&p, "%u.%u.%u.%u.in-addr.arpa.", ab.b[3], ab.b[2], ab.b[1], ab.b[0]);
241 	return p;
242 }
243 
244 static char *
_mdns_reverse_ipv6(const char * addr)245 _mdns_reverse_ipv6(const char *addr)
246 {
247 	char x[65], *p;
248 	int i, j;
249 	u_int8_t d, hi, lo;
250 
251 	if (addr == NULL) return NULL;
252 
253 	x[64] = '\0';
254 	j = 63;
255 	for (i = 0; i < 16; i++)
256 	{
257 		d = addr[i];
258 		lo = d & 0x0f;
259 		hi = d >> 4;
260 		x[j--] = '.';
261 		x[j--] = hexchar[hi];
262 		x[j--] = '.';
263 		x[j--] = hexchar[lo];
264 	}
265 
266 	asprintf(&p, "%sip6.arpa.", x);
267 
268 	return p;
269 }
270 
271 /* _mdns_canonicalize
272  * Canonicalize the domain name by converting to lower case and removing the
273  * trailing '.' if present.
274  */
275 static char *
_mdns_canonicalize(const char * s)276 _mdns_canonicalize(const char *s)
277 {
278 	int i;
279 	char *t;
280 	if (s == NULL) return NULL;
281 	t = strdup(s);
282 	if (t == NULL) return NULL;
283 	if (t[0] == '\0') return t;
284 	for (i = 0; t[i] != '\0'; i++) {
285 		if (t[i] >= 'A' && t[i] <= 'Z') t[i] += 32;
286 	}
287 	if (t[i-1] == '.') t[i-1] = '\0';
288 	return t;
289 }
290 
291 /* _mdns_hostent_append_alias
292  * Appends an alias to the mdns_hostent_t structure.
293  */
294 static int
_mdns_hostent_append_alias(mdns_hostent_t * h,const char * alias)295 _mdns_hostent_append_alias(mdns_hostent_t *h, const char *alias)
296 {
297 	int i;
298 	char *name;
299 	if (h == NULL || alias == NULL) return 0;
300 	name = _mdns_canonicalize(alias);
301 	if (name == NULL) return -1;
302 
303 	// don't add the name if it matches an existing name
304 	if (h->host.h_name && string_equal(h->host.h_name, name)) {
305 		free(name);
306 		return 0;
307 	}
308 	for (i = 0; i < h->alias_count; ++i) {
309 		if (string_equal(h->host.h_aliases[i], name)) {
310 			free(name);
311 			return 0;
312 		}
313 	}
314 
315 	// add the alias and NULL terminate the list
316 	h->host.h_aliases = (char **)reallocf(h->host.h_aliases, (h->alias_count+2) * sizeof(char *));
317 	if (h->host.h_aliases == NULL) {
318 		h->alias_count = 0;
319 		free(name);
320 		return -1;
321 	}
322 	h->host.h_aliases[h->alias_count] = name;
323 	++h->alias_count;
324 	h->host.h_aliases[h->alias_count] = NULL;
325 	return 0;
326 }
327 
328 /* _mdns_hostent_append_addr
329  * Appends an alias to the mdns_hostent_t structure.
330  */
331 static int
_mdns_hostent_append_addr(mdns_hostent_t * h,const uint8_t * addr,uint32_t len)332 _mdns_hostent_append_addr(mdns_hostent_t *h, const uint8_t *addr, uint32_t len)
333 {
334 	if (h == NULL || addr == NULL || len == 0) return 0;
335 
336 	// copy the address buffer
337 	uint8_t *buf = malloc(len);
338 	if (buf == NULL) return -1;
339 	memcpy(buf, addr, len);
340 
341 	// add the address and NULL terminate the list
342 	h->host.h_addr_list = (char **)reallocf(h->host.h_addr_list, (h->addr_count+2) * sizeof(char *));
343 	if (h->host.h_addr_list == NULL) {
344 		h->addr_count = 0;
345 		return -1;
346 	}
347 	h->host.h_addr_list[h->addr_count] = (char*)buf;
348 	h->addr_count++;
349 	h->host.h_addr_list[h->addr_count] = NULL;
350 	return 0;
351 }
352 
353 static void
_mdns_hostent_clear(mdns_hostent_t * h)354 _mdns_hostent_clear(mdns_hostent_t *h)
355 {
356 	if (h == NULL) return;
357 	free(h->host.h_name);
358 	h->host.h_name = NULL;
359 
360 	char **aliases = h->host.h_aliases;
361 	while (aliases && *aliases) {
362 		free(*aliases++);
363 	}
364 	free(h->host.h_aliases);
365 	h->host.h_aliases = NULL;
366 	h->alias_count = 0;
367 
368 	char **addrs = h->host.h_addr_list;
369 	while (addrs && *addrs) {
370 		free(*addrs++);
371 	}
372 	free(h->host.h_addr_list);
373 	h->host.h_addr_list = NULL;
374 	h->addr_count = 0;
375 
376 }
377 
378 static void
_mdns_reply_clear(mdns_reply_t * r)379 _mdns_reply_clear(mdns_reply_t *r)
380 {
381 	if (r == NULL) return;
382 	r->ifnum = 0;
383 	_mdns_hostent_clear(r->h4);
384 	_mdns_hostent_clear(r->h6);
385 	mdns_srv_t *srv = r->srv;
386 	r->srv = NULL;
387 	while (srv) {
388 		mdns_srv_t *next = srv->next;
389 		free(srv->srv.target);
390 		free(srv);
391 		srv = next;
392 	}
393 }
394 
395 static si_item_t *
mdns_hostbyname(si_mod_t * si,const char * name,int af,const char * interface,uint32_t * err)396 mdns_hostbyname(si_mod_t *si, const char *name, int af, const char *interface, uint32_t *err)
397 {
398 	uint32_t type;
399 	mdns_hostent_t h;
400 	mdns_reply_t reply;
401 	si_item_t *out = NULL;
402 	uint64_t bb;
403 	int status;
404 	DNSServiceFlags flags = 0;
405 
406 	if (err != NULL) *err = SI_STATUS_NO_ERROR;
407 
408 	if (name == NULL) {
409 		if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
410 		return NULL;
411 	}
412 
413 	memset(&h, 0, sizeof(h));
414 	memset(&reply, 0, sizeof(reply));
415 
416 	switch (af) {
417 		case AF_INET:
418 			type = ns_t_a;
419 			h.host.h_length = 4;
420 			reply.h4 = &h;
421 			break;
422 		case AF_INET6:
423 			type = ns_t_aaaa;
424 			h.host.h_length = 16;
425 			reply.h6 = &h;
426 			break;
427 		default:
428 			if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
429 			return NULL;
430 	}
431 	h.host.h_addrtype = af;
432 
433 	status = _mdns_search(name, ns_c_in, type, interface, flags, NULL, NULL, &reply);
434 	if (status != 0 || h.addr_count == 0) {
435 		_mdns_reply_clear(&reply);
436 		if (err != NULL) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
437 		return NULL;
438 	}
439 
440 	bb = reply.ttl + time(NULL);
441 
442 	switch (af) {
443 		case AF_INET:
444 			out = (si_item_t *)LI_ils_create("L4488s*44a", (unsigned long)si, CATEGORY_HOST_IPV4, 1, bb, 0LL, h.host.h_name, h.host.h_aliases, h.host.h_addrtype, h.host.h_length, h.host.h_addr_list);
445 			break;
446 		case AF_INET6:
447 			out = (si_item_t *)LI_ils_create("L4488s*44c", (unsigned long)si, CATEGORY_HOST_IPV6, 1, bb, 0LL, h.host.h_name, h.host.h_aliases, h.host.h_addrtype, h.host.h_length, h.host.h_addr_list);
448 			break;
449 	}
450 
451 	_mdns_reply_clear(&reply);
452 
453 	if (out == NULL && err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
454 
455 	return out;
456 }
457 
458 static si_item_t *
mdns_hostbyaddr(si_mod_t * si,const void * addr,int af,const char * interface,uint32_t * err)459 mdns_hostbyaddr(si_mod_t *si, const void *addr, int af, const char *interface, uint32_t *err)
460 {
461 	mdns_hostent_t h;
462 	mdns_reply_t reply;
463 	char *name;
464 	si_item_t *out;
465 	uint64_t bb;
466 	int cat;
467 	int status;
468 	DNSServiceFlags flags = 0;
469 
470 	if (err != NULL) *err = SI_STATUS_NO_ERROR;
471 
472 	if (addr == NULL || si == NULL) {
473 		if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
474 		return NULL;
475 	}
476 
477 	memset(&h, 0, sizeof(h));
478 	memset(&reply, 0, sizeof(reply));
479 
480 	switch (af) {
481 		case AF_INET:
482 			h.host.h_length = 4;
483 			reply.h4 = &h;
484 			name = _mdns_reverse_ipv4(addr);
485 			cat = CATEGORY_HOST_IPV4;
486 			break;
487 		case AF_INET6:
488 			h.host.h_length = 16;
489 			reply.h6 = &h;
490 			name = _mdns_reverse_ipv6(addr);
491 			cat = CATEGORY_HOST_IPV6;
492 			break;
493 		default:
494 			if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
495 			return NULL;
496 	}
497 	h.host.h_addrtype = af;
498 
499 	status = _mdns_search(name, ns_c_in, ns_t_ptr, interface, flags, NULL, NULL, &reply);
500 	free(name);
501 	if (status != 0) {
502 		_mdns_reply_clear(&reply);
503 		if (err != NULL) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
504 		return NULL;
505 	}
506 
507 	status = _mdns_hostent_append_addr(&h, addr, h.host.h_length);
508 	if (status != 0) {
509 		_mdns_hostent_clear(&h);
510 		if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
511 		return NULL;
512 	}
513 
514 	bb = reply.ttl + time(NULL);
515 	out = (si_item_t *)LI_ils_create("L4488s*44a", (unsigned long)si, cat, 1, bb, 0LL, h.host.h_name, h.host.h_aliases, h.host.h_addrtype, h.host.h_length, h.host.h_addr_list);
516 
517 	_mdns_hostent_clear(&h);
518 
519 	if (out == NULL && err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
520 	return out;
521 }
522 
523 static si_list_t *
mdns_addrinfo(si_mod_t * si,const void * node,const void * serv,uint32_t family,uint32_t socktype,uint32_t proto,uint32_t flags,const char * interface,uint32_t * err)524 mdns_addrinfo(si_mod_t *si, const void *node, const void *serv, uint32_t family, uint32_t socktype, uint32_t proto, uint32_t flags, const char *interface, uint32_t *err)
525 {
526 	int wantv4 = 1;
527 	int wantv6 = 1;
528 	struct in_addr a4;
529 	struct in6_addr a6;
530 	mdns_hostent_t h4;
531 	mdns_hostent_t h6;
532 	mdns_reply_t reply;
533 	uint32_t type;
534 	uint16_t port;
535 
536 	if (family == AF_INET6)
537 	{
538 		if ((flags & AI_V4MAPPED) == 0) wantv4 = 0;
539 	}
540 	else if (family == AF_INET)
541 	{
542 		wantv6 = 0;
543 	}
544 	else if (family != AF_UNSPEC)
545 	{
546 		return NULL;
547 	}
548 
549 	if (err != NULL) *err = SI_STATUS_NO_ERROR;
550 
551 	si_list_t *out = NULL;
552 
553 	memset(&h4, 0, sizeof(h4));
554 	memset(&h6, 0, sizeof(h6));
555 	memset(&reply, 0, sizeof(reply));
556 
557 	h4.host.h_addrtype = AF_INET;
558 	h4.host.h_length = 4;
559 	h6.host.h_addrtype = AF_INET6;
560 	h6.host.h_length = 16;
561 
562 	if (wantv4 && wantv6) {
563 		type = 0;
564 		reply.h4 = &h4;
565 		reply.h6 = &h6;
566 	} else if (wantv4) {
567 		reply.h4 = &h4;
568 		type = ns_t_a;
569 	} else if (wantv6) {
570 		type = ns_t_aaaa;
571 		reply.h6 = &h6;
572 	} else {
573 		if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
574 		return NULL;
575 	}
576 
577 	// service lookup
578 	if ((flags & AI_NUMERICSERV) != 0) {
579 		port = *(uint16_t *)serv;
580 	} else {
581 		if (_gai_serv_to_port(serv, proto, &port) != 0) {
582 			if (err) *err = SI_STATUS_EAI_NONAME;
583 			return NULL;
584 		}
585 	}
586 
587 	// host lookup
588 	if ((flags & AI_NUMERICHOST) != 0) {
589 		char *cname = NULL;
590 		struct in_addr *p4 = NULL;
591 		struct in6_addr *p6 = NULL;
592 		if (family == AF_INET) {
593 			p4 = &a4;
594 			memcpy(p4, node, sizeof(a4));
595 		} else if (family == AF_INET6) {
596 			p6 = &a6;
597 			memcpy(p6, node, sizeof(a6));
598 		}
599 		out = si_addrinfo_list(si, flags, socktype, proto, p4, p6, port, 0, cname, cname);
600 	} else {
601 		DNSServiceFlags dns_flags = 0;
602 		if (flags & AI_ADDRCONFIG) {
603 			dns_flags |= kDNSServiceFlagsSuppressUnusable;
604 		}
605 		int res;
606 		res = _mdns_search(node, ns_c_in, type, interface, dns_flags, NULL, NULL, &reply);
607 		if (res == 0 && (h4.addr_count > 0 || h6.addr_count > 0)) {
608 			out = si_addrinfo_list_from_hostent(si, flags, socktype, proto,
609 												port, 0,
610 												(wantv4 ? &h4.host : NULL),
611 												(wantv6 ? &h6.host : NULL));
612 		} else if (err != NULL) {
613 			*err = SI_STATUS_EAI_NONAME;
614 		}
615 		_mdns_reply_clear(&reply);
616 	}
617 	return out;
618 }
619 
620 static si_list_t *
mdns_srv_byname(si_mod_t * si,const char * qname,const char * interface,uint32_t * err)621 mdns_srv_byname(si_mod_t* si, const char *qname, const char *interface, uint32_t *err)
622 {
623 	si_list_t *out = NULL;
624 	mdns_reply_t reply;
625 	mdns_srv_t *srv;
626 	int res;
627 	const uint64_t unused = 0;
628 	DNSServiceFlags flags = 0;
629 
630 	if (err != NULL) *err = SI_STATUS_NO_ERROR;
631 
632 	memset(&reply, 0, sizeof(reply));
633 	res = _mdns_search(qname, ns_c_in, ns_t_srv, interface, flags, NULL, NULL, &reply);
634 	if (res == 0) {
635 		srv = reply.srv;
636 		while (srv) {
637 			si_item_t *item;
638 			item = (si_item_t *)LI_ils_create("L4488222s", (unsigned long)si, CATEGORY_SRV, 1, unused, unused, srv->srv.priority, srv->srv.weight, srv->srv.port, srv->srv.target);
639 			out = si_list_add(out, item);
640 			si_item_release(item);
641 			srv = srv->next;
642 		}
643 	}
644 	_mdns_reply_clear(&reply);
645 	return out;
646 }
647 
648 /*
649  * We support dns_async_start / cancel / handle_reply using dns_item_call
650  */
651 static si_item_t *
mdns_item_call(si_mod_t * si,int call,const char * name,const char * ignored,const char * interface,uint32_t class,uint32_t type,uint32_t * err)652 mdns_item_call(si_mod_t *si, int call, const char *name, const char *ignored, const char *interface, uint32_t class, uint32_t type, uint32_t *err)
653 {
654 	int res;
655 	uint8_t buf[DNS_MAX_RECEIVE_SIZE];
656 	uint32_t len = sizeof(buf);
657 	mdns_reply_t reply;
658 	mdns_hostent_t h4;
659 	mdns_hostent_t h6;
660 	si_item_t *out;
661 	DNSServiceFlags flags = 0;
662 
663 	if (err != NULL) *err = SI_STATUS_NO_ERROR;
664 
665 	if (name == NULL) {
666 		if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
667 		return NULL;
668 	}
669 
670 	memset(&h4, 0, sizeof(h4));
671 	memset(&h6, 0, sizeof(h6));
672 	memset(&reply, 0, sizeof(reply));
673 
674 	h4.host.h_addrtype = AF_INET;
675 	h4.host.h_length = 4;
676 	h6.host.h_addrtype = AF_INET6;
677 	h6.host.h_length = 16;
678 	reply.h4 = &h4;
679 	reply.h6 = &h6;
680 
681 	res = _mdns_search(name, class, type, interface, flags, buf, &len, &reply);
682 	if (res != 0 || len <= 0 || len > DNS_MAX_RECEIVE_SIZE) {
683 		_mdns_reply_clear(&reply);
684 		if (err != NULL) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
685 		return NULL;
686 	}
687 
688 	struct sockaddr_in6 from;
689 	uint32_t fromlen = sizeof(from);
690 	memset(&from, 0, fromlen);
691 	from.sin6_len = fromlen;
692 	from.sin6_family = AF_INET6;
693 	from.sin6_addr.__u6_addr.__u6_addr8[15] = 1;
694 	if (reply.ifnum != 0) {
695 		from.sin6_addr.__u6_addr.__u6_addr16[0] = htons(0xfe80);
696 		from.sin6_scope_id = reply.ifnum;
697 	}
698 
699 	out = (si_item_t *)LI_ils_create("L4488@@", (unsigned long)si, CATEGORY_DNSPACKET, 1, 0LL, 0LL, len, buf, fromlen, &from);
700 	if (out == NULL && err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
701 
702 	_mdns_reply_clear(&reply);
703 
704 	return out;
705 }
706 
707 static int
mdns_is_valid(si_mod_t * si,si_item_t * item)708 mdns_is_valid(si_mod_t *si, si_item_t *item)
709 {
710 	return 0;
711 }
712 
713 static void
mdns_close(si_mod_t * si)714 mdns_close(si_mod_t *si)
715 {
716 }
717 
718 static void
_mdns_atfork_prepare(void)719 _mdns_atfork_prepare(void)
720 {
721 	// acquire our lock so that we know all other threads have "drained"
722 	pthread_mutex_lock(&_mdns_mutex);
723 }
724 
725 static void
_mdns_atfork_parent(void)726 _mdns_atfork_parent(void)
727 {
728 	// parent can simply resume
729 	pthread_mutex_unlock(&_mdns_mutex);
730 }
731 
732 static void
_mdns_atfork_child(void)733 _mdns_atfork_child(void)
734 {
735 	// child needs to force re-initialization
736 	_mdns_old_sdref = _mdns_sdref; // for later deallocation
737 	_mdns_sdref = NULL;
738 	pthread_mutex_unlock(&_mdns_mutex);
739 }
740 
741 static void
_mdns_init(void)742 _mdns_init(void)
743 {
744 	pthread_atfork(_mdns_atfork_prepare, _mdns_atfork_parent, _mdns_atfork_child);
745 
746 	if (getenv("RES_DEBUG") != NULL) _mdns_debug |= MDNS_DEBUG_STDOUT;
747 	int fd = open(MDNS_DEBUG_FILE, O_RDONLY, 0);
748 	errno = 0;
749 	if (fd >= 0)
750 	{
751 		int i, n;
752 		char c[5];
753 		memset(c, 0, sizeof(c));
754 		n = read(fd, c, 4);
755 
756 		for (i = 0; i < n; i++)
757 		{
758 			if ((c[i] == 'o') || (c[i] == 'O')) _mdns_debug |= MDNS_DEBUG_STDOUT;
759 			if ((c[i] == 'e') || (c[i] == 'E')) _mdns_debug |= MDNS_DEBUG_STDERR;
760 			if ((c[i] == 'a') || (c[i] == 'A')) _mdns_debug |= MDNS_DEBUG_ASL;
761 			if ((c[i] == 'm') || (c[i] == 'M')) _mdns_debug |= MDNS_DEBUG_MORE;
762 		}
763 	}
764 }
765 
766 si_mod_t *
si_module_static_mdns(void)767 si_module_static_mdns(void)
768 {
769 	static const struct si_mod_vtable_s mdns_vtable =
770 	{
771 		.sim_close = &mdns_close,
772 		.sim_is_valid = &mdns_is_valid,
773 		.sim_host_byname = &mdns_hostbyname,
774 		.sim_host_byaddr = &mdns_hostbyaddr,
775 		.sim_item_call = &mdns_item_call,
776 		.sim_addrinfo = &mdns_addrinfo,
777 		.sim_srv_byname = &mdns_srv_byname,
778 	};
779 
780 	static si_mod_t si =
781 	{
782 		.vers = 1,
783 		.refcount = 1,
784 		.flags = SI_MOD_FLAG_STATIC,
785 
786 		.private = NULL,
787 		.vtable = &mdns_vtable,
788 	};
789 
790 	static dispatch_once_t once;
791 
792 	dispatch_once(&once, ^{
793 		si.name = strdup("mdns");
794 		_mdns_init();
795 	});
796 
797 	return (si_mod_t*)&si;
798 }
799 
800 /*
801  * _mdns_parse_domain_name
802  * Combine DNS labels to form a string.
803  * DNSService API does not return compressed names.
804  */
805 static char *
_mdns_parse_domain_name(const uint8_t * data,uint32_t datalen)806 _mdns_parse_domain_name(const uint8_t *data, uint32_t datalen)
807 {
808 	int i = 0, j = 0;
809 	uint32_t len;
810 	uint32_t domainlen = 0;
811 	char *domain = NULL;
812 
813 	if ((data == NULL) || (datalen == 0)) return NULL;
814 
815 	// i: index into input data
816 	// j: index into output string
817 	while (datalen-- > 0) {
818 		len = data[i++];
819 		domainlen += (len + 1);
820 		domain = reallocf(domain, domainlen);
821 		if (domain == NULL) return NULL;
822 		if (len == 0) break;	// DNS root (NUL)
823 		if (j > 0) {
824 			domain[j++] = datalen ? '.' : '\0';
825 		}
826 
827 		while ((len-- > 0) && (datalen--)) {
828 			if (data[i] == '.') {
829 				// special case: escape the '.' with a '\'
830 				domain = reallocf(domain, ++domainlen);
831 				if (domain == NULL) return NULL;
832 				domain[j++] = '\\';
833 			}
834 			domain[j++] = data[i++];
835 		}
836 	}
837 	domain[j] = '\0';
838 
839 	return domain;
840 }
841 
842 /*
843  * _mdns_pack_domain_name
844  * Format the string as packed DNS labels.
845  * Only used for one string at a time, therefore no need for compression.
846  */
847 static int
_mdns_pack_domain_name(const char * str,uint8_t * buf,size_t buflen)848 _mdns_pack_domain_name(const char* str, uint8_t *buf, size_t buflen) {
849 	int i = 0;
850 	uintptr_t len = 0;
851 
852 	while (i < buflen) {
853 		// calculate length to next '.' or '\0'
854 		char *dot = strchr(str, '.');
855 		if (dot == NULL) dot = strchr(str, '\0');
856 		len = (dot - str);
857 		if (len > NS_MAXLABEL) return -1;
858 		// copy data for label
859 		buf[i++] = len;
860 		while (str < dot && i < buflen) {
861 			buf[i++] = *str++;
862 		}
863 		// skip past '.', break if '\0'
864 		if (*str++ == '\0') break;
865 	}
866 
867 	if (i >= buflen) return -1;
868 
869 	if (len > 0) {
870 		// no trailing dot - add a null label
871 		buf[i++] = 0;
872 		if (i >= buflen) return -1;
873 	}
874 
875 	buf[i] = '\0';
876 	return i;
877 }
878 
879 static int
_is_rev_link_local(const char * name)880 _is_rev_link_local(const char *name)
881 {
882 	int len, i;
883 
884 	if (name == NULL) return 0;
885 
886 	len = strlen(name);
887 	if (len == 0) return 0;
888 
889 	/* check for trailing '.' */
890 	if (name[len - 1] == '.') len--;
891 
892 	if (len != IPv6_REVERSE_LEN) return 0;
893 
894 	i = IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR;
895 	if ((name[i] != '8') && (name[i] != '9') && (name[i] != 'A') && (name[i] != 'a') && (name[i] != 'B') && (name[i] != 'b')) return 0;
896 
897 	i = IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR + 1;
898 	if (strncasecmp(name + i, ".e.f.ip6.arpa", 13)) return 0;
899 
900 	for (i = 0; i < IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR; i += 2)
901 	{
902 		if (name[i] < '0') return 0;
903 		if ((name[i] > '9') && (name[i] < 'A')) return 0;
904 		if ((name[i] > 'F') && (name[i] < 'a')) return 0;
905 		if (name[i] > 'f') return 0;
906 		if (name[i + 1] != '.') return 0;
907 	}
908 
909 	return 1;
910 }
911 
912 /* _mdns_ipv6_extract_scope_id
913  * If the input string is a link local IPv6 address with an encoded scope id,
914  * the scope id is extracted and a new string is constructed with the scope id removed.
915  */
916 static char *
_mdns_ipv6_extract_scope_id(const char * name,uint32_t * out_ifnum)917 _mdns_ipv6_extract_scope_id(const char *name, uint32_t *out_ifnum)
918 {
919 	char *qname = NULL;
920 	uint16_t nibble;
921 	uint32_t iface;
922 	int i;
923 
924 	if (out_ifnum != NULL) *out_ifnum = 0;
925 
926 	/* examine the address, extract the scope id if present */
927 	if ((name != NULL) && (_is_rev_link_local(name)))
928 	{
929 		/* _is_rev_link_local rejects chars > 127 so it's safe to index into hexval */
930 		i = IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW;
931 		nibble = hexval[(uint32_t)name[i]];
932 		iface = nibble;
933 
934 		i += 2;
935 		nibble = hexval[(uint32_t)name[i]];
936 		iface += (nibble << 4);
937 
938 		i += 2;
939 		nibble = hexval[(uint32_t)name[i]];
940 		iface += (nibble << 8);
941 
942 		i += 2;
943 		nibble = hexval[(uint32_t)name[i]];
944 		iface += (nibble << 12);
945 
946 		if (iface != 0)
947 		{
948 			qname = strdup(name);
949 			if (qname == NULL) return NULL;
950 
951 			i = IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW;
952 			qname[i] = '0';
953 			qname[i + 2] = '0';
954 			qname[i + 4] = '0';
955 			qname[i + 6] = '0';
956 
957 			if (out_ifnum) *out_ifnum = iface;
958 		}
959 	}
960 
961 	return qname;
962 }
963 
964 static int
_mdns_make_query(const char * name,int class,int type,uint8_t * buf,uint32_t buflen)965 _mdns_make_query(const char* name, int class, int type, uint8_t *buf, uint32_t buflen)
966 {
967 	uint32_t len = 0;
968 
969 	if (buf == NULL || buflen < (NS_HFIXEDSZ + NS_QFIXEDSZ)) return -1;
970 	memset(buf, 0, NS_HFIXEDSZ);
971 	HEADER *hp = (HEADER *)buf;
972 
973 	len += NS_HFIXEDSZ;
974 	hp->id = arc4random();
975 	hp->qr = 1;
976 	hp->opcode = ns_o_query;
977 	hp->rd = 1;
978 	hp->rcode = ns_r_noerror;
979 	hp->qdcount = htons(1);
980 
981 	int n = _mdns_pack_domain_name(name, &buf[len], buflen - len);
982 	if (n < 0) return -1;
983 
984 	len += n;
985 	uint16_t word;
986 	word = htons(type);
987 	memcpy(&buf[len], &word, sizeof(word));
988 	len += sizeof(word);
989 	word = htons(class);
990 	memcpy(&buf[len], &word, sizeof(word));
991 	len += sizeof(word);
992 	return len;
993 }
994 
995 typedef struct {
996 	mdns_reply_t *reply;
997 	mdns_hostent_t *host;
998 	uint8_t *answer; // DNS packet buffer
999 	size_t anslen; // DNS packet buffer current length
1000 	size_t ansmaxlen; // DNS packet buffer maximum length
1001 	int type; // type of query: A, AAAA, PTR, SRV...
1002 	uint16_t last_type; // last type received
1003 	uint32_t sd_gen;
1004 	DNSServiceRef sd;
1005 	DNSServiceFlags flags;
1006 	DNSServiceErrorType error;
1007 	int kq; // kqueue to notify when callback received
1008 } mdns_query_context_t;
1009 
1010 static void
1011 _mdns_query_callback(DNSServiceRef, DNSServiceFlags, uint32_t, DNSServiceErrorType, const char *, uint16_t, uint16_t, uint16_t, const void *, uint32_t, void *);
1012 
1013 /* _mdns_query_start
1014  * initializes the context and starts a DNS-SD query.
1015  */
1016 static DNSServiceErrorType
_mdns_query_start(mdns_query_context_t * ctx,mdns_reply_t * reply,uint8_t * answer,uint32_t * anslen,const char * name,int class,int type,const char * interface,DNSServiceFlags flags,int kq)1017 _mdns_query_start(mdns_query_context_t *ctx, mdns_reply_t *reply, uint8_t *answer, uint32_t *anslen, const char* name, int class, int type, const char *interface, DNSServiceFlags flags, int kq)
1018 {
1019 	DNSServiceErrorType status;
1020 
1021 	flags |= kDNSServiceFlagsShareConnection;
1022 	flags |= kDNSServiceFlagsReturnIntermediates;
1023 
1024 	/* <rdar://problem/7428439> mDNSResponder is now responsible for timeouts */
1025 	flags |= kDNSServiceFlagsTimeout;
1026 
1027 	memset(ctx, 0, sizeof(mdns_query_context_t));
1028 
1029 	if (answer && anslen) {
1030 		// build a dummy DNS header to return to the caller
1031 		ctx->answer = answer;
1032 		ctx->ansmaxlen = *anslen;
1033 		ctx->anslen = _mdns_make_query(name, class, type, answer, ctx->ansmaxlen);
1034 		if (ctx->anslen <= 0) return -1;
1035 	}
1036 
1037 	ctx->type = type;
1038 	ctx->sd = _mdns_sdref;
1039 	ctx->sd_gen = _mdns_generation;
1040 	ctx->kq = kq;
1041 	if (reply) {
1042 		ctx->reply = reply;
1043 		if (type == ns_t_a) ctx->host = reply->h4;
1044 		else if (type == ns_t_aaaa) ctx->host = reply->h6;
1045 		else if (type == ns_t_ptr && reply->h4) ctx->host = reply->h4;
1046 		else if (type == ns_t_ptr && reply->h6) ctx->host = reply->h6;
1047 		else if (type != ns_t_srv && type != ns_t_cname) return -1;
1048 	}
1049 
1050 	uint32_t iface = 0;
1051 	char *qname = _mdns_ipv6_extract_scope_id(name, &iface);
1052 	if (qname == NULL) qname = (char *)name;
1053 
1054 	if (interface != NULL)
1055 	{
1056 		/* get interface number from name */
1057 		int iface2 = if_nametoindex(interface);
1058 
1059 		/* balk if interface name lookup failed */
1060 		if (iface2 == 0) return -1;
1061 
1062 		/* balk if scope id is set AND interface is given AND they don't match */
1063 		if ((iface != 0) && (iface2 != 0) && (iface != iface2)) return -1;
1064 		if (iface2 != 0) iface = iface2;
1065 	}
1066 
1067 	_mdns_debug_message(";; mdns query %s %d %d [ctx %p]\n", qname, type, class, ctx);
1068 
1069 	status = DNSServiceQueryRecord(&ctx->sd, flags, iface, qname, type, class, _mdns_query_callback, ctx);
1070 	if (qname != name) free(qname);
1071 	return status;
1072 }
1073 
1074 /* _mdns_query_is_complete
1075  * Determines whether the specified query has sufficient information to be
1076  * considered complete.
1077  */
1078 static int
_mdns_query_is_complete(mdns_query_context_t * ctx)1079 _mdns_query_is_complete(mdns_query_context_t *ctx)
1080 {
1081 	int complete = 0;
1082 
1083 	/* NULL context is an error, but we call it complete */
1084 	if (ctx == NULL) return 1;
1085 
1086 	/*
1087 	 * The default is to ignore kDNSServiceFlagsMoreComing, since it has either
1088 	 * never been supported or worked correctly.  MDNS_DEBUG_MORE makes us honor it.
1089 	 */
1090 	if (ctx->flags & kDNSServiceFlagsMoreComing) {
1091 		if (_mdns_debug & MDNS_DEBUG_MORE) {
1092 			_mdns_debug_message(";; mdns is_complete type %d ctx %p more coming - incomplete\n", ctx->type, ctx);
1093 			return 0;
1094 		}
1095 	}
1096 
1097 	if (ctx->last_type != ctx->type) {
1098 		_mdns_debug_message(";; mdns is_complete ctx %p type mismatch (%d != %d) - incomplete\n", ctx, ctx->last_type, ctx->type);
1099 		return 0;
1100 	}
1101 
1102 	switch (ctx->type) {
1103 		case ns_t_a:
1104 		case ns_t_aaaa:
1105 			if (ctx->host != NULL && ctx->host->addr_count > 0) complete = 1;
1106 			break;
1107 		case ns_t_ptr:
1108 			if (ctx->host != NULL && ctx->host->host.h_name != NULL) complete = 1;
1109 			break;
1110 		case ns_t_srv:
1111 			if (ctx->reply != NULL && ctx->reply->srv != NULL) complete = 1;
1112 			break;
1113 		default:
1114 			_mdns_debug_message(";; mdns is_complete unexpected type %d ctx %p\n", ctx->type, ctx);
1115 	}
1116 
1117 	_mdns_debug_message(";; mdns is_complete type %d ctx %p %s%scomplete\n", ctx->type, ctx, (ctx->flags & kDNSServiceFlagsMoreComing) ? "(more coming flag ignored)" : "", (complete == 0) ? " - in" : " - ");
1118 
1119 	return complete;
1120 }
1121 
1122 /* _mdns_query_clear
1123  * Clear out the temporary fields of the context, and clear any result
1124  * structures that are incomplete.  Retrns 1 if the query was complete.
1125  */
1126 static int
_mdns_query_clear(mdns_query_context_t * ctx)1127 _mdns_query_clear(mdns_query_context_t *ctx)
1128 {
1129 	int complete = _mdns_query_is_complete(ctx);
1130 	if (ctx == NULL) return complete;
1131 
1132 	if (ctx->sd != NULL) {
1133 		/* only dealloc this DNSServiceRef if the "main" _mdns_sdref has not been deallocated */
1134 		if (ctx->sd != NULL && ctx->sd_gen == _mdns_generation) {
1135 			DNSServiceRefDeallocate(ctx->sd);
1136 		}
1137 	}
1138 
1139 	ctx->sd = NULL;
1140 	ctx->sd_gen = 0;
1141 	ctx->flags = 0;
1142 	ctx->kq = -1;
1143 
1144 	if (complete == 0) {
1145 		_mdns_hostent_clear(ctx->host);
1146 		ctx->anslen = -1;
1147 	}
1148 
1149 	return complete;
1150 }
1151 
1152 static void
_mdns_query_callback(DNSServiceRef sdRef,DNSServiceFlags flags,uint32_t ifIndex,DNSServiceErrorType errorCode,const char * fullname,uint16_t rrtype,uint16_t rrclass,uint16_t rdlen,const void * rdata,uint32_t ttl,void * ctx)1153 _mdns_query_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *ctx)
1154 {
1155 	mdns_query_context_t *context;
1156 	struct in6_addr a6;
1157 
1158 	context = (mdns_query_context_t *)ctx;
1159 
1160 	context->flags = flags;
1161 	context->error = errorCode;
1162 	context->last_type = rrtype;
1163 
1164 	if (errorCode != kDNSServiceErr_NoError) {
1165 		_mdns_debug_message(";; [%s %hu %hu]: error %d [ctx %p]\n", fullname, rrtype, rrclass, errorCode, context);
1166 		goto wakeup_kevent;
1167 	}
1168 
1169 	// embed the scope ID into link-local IPv6 addresses
1170 	if (rrtype == ns_t_aaaa && rdlen == sizeof(struct in6_addr) &&
1171 	    IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)rdata)) {
1172 		memcpy(&a6, rdata, rdlen);
1173 		a6.__u6_addr.__u6_addr16[1] = htons(ifIndex);
1174 		rdata = &a6;
1175 	}
1176 
1177 	if (context->reply) {
1178 		char *name;
1179 		int malformed = 0;
1180 		mdns_reply_t *reply = context->reply;
1181 
1182 		if (reply->ifnum == 0) {
1183 			reply->ifnum = ifIndex;
1184 		}
1185 
1186 		_mdns_hostent_append_alias(context->host, fullname);
1187 		if (reply->ttl == 0 || ttl < reply->ttl) reply->ttl = ttl;
1188 
1189 		switch (rrtype) {
1190 			case ns_t_a:
1191 			case ns_t_aaaa:
1192 				if (((rrtype == ns_t_a && context->host->host.h_addrtype == AF_INET) ||
1193 					 (rrtype == ns_t_aaaa && context->host->host.h_addrtype == AF_INET6)) &&
1194 					rdlen >= context->host->host.h_length) {
1195 					if (context->host->host.h_name == NULL) {
1196 						int i;
1197 						mdns_hostent_t *h = context->host;
1198 						char *h_name = _mdns_canonicalize(fullname);
1199 						context->host->host.h_name = h_name;
1200 
1201 						// 6863416 remove h_name from h_aliases
1202 						for (i = 0; i < h->alias_count; ++i) {
1203 							if (h_name == NULL) break;
1204 							if (string_equal(h->host.h_aliases[i], h_name)) {
1205 								// includes trailing NULL pointer
1206 								int sz = sizeof(char *) * (h->alias_count - i);
1207 								free(h->host.h_aliases[i]);
1208 								memmove(&h->host.h_aliases[i], &h->host.h_aliases[i+1], sz);
1209 								h->alias_count -= 1;
1210 								break;
1211 							}
1212 						}
1213 					}
1214 					_mdns_hostent_append_addr(context->host, rdata, context->host->host.h_length);
1215 				} else {
1216 					malformed = 1;
1217 				}
1218 				break;
1219 			case ns_t_cname:
1220 				name = _mdns_parse_domain_name(rdata, rdlen);
1221 				if (!name) malformed = 1;
1222 				_mdns_hostent_append_alias(context->host, name);
1223 				_mdns_debug_message(";; [%s %hu %hu] cname %s [ctx %p]\n", fullname, rrtype, rrclass, name, context);
1224 				free(name);
1225 				break;
1226 			case ns_t_ptr:
1227 				name = _mdns_parse_domain_name(rdata, rdlen);
1228 				if (!name) malformed = 1;
1229 				if (context->host && context->host->host.h_name == NULL) {
1230 					context->host->host.h_name = _mdns_canonicalize(name);
1231 				}
1232 				_mdns_hostent_append_alias(context->host, name);
1233 				free(name);
1234 				break;
1235 			case ns_t_srv: {
1236 				mdns_rr_srv_t *p = (mdns_rr_srv_t*)rdata;
1237 				mdns_srv_t *srv = calloc(1, sizeof(mdns_srv_t));
1238 				if (srv == NULL) break;
1239 				if (rdlen < sizeof(mdns_rr_srv_t)) {
1240 					malformed = 1;
1241 					break;
1242 				}
1243 				srv->srv.priority = ntohs(p->priority);
1244 				srv->srv.weight = ntohs(p->weight);
1245 				srv->srv.port = ntohs(p->port);
1246 				srv->srv.target = _mdns_parse_domain_name(&p->target[0], rdlen - 3*sizeof(uint16_t));
1247 				if (srv->srv.target == NULL) {
1248 					malformed = 1;
1249 					break;
1250 				}
1251 				// append to the end of the list
1252 				if (reply->srv == NULL) {
1253 					reply->srv = srv;
1254 				} else {
1255 					mdns_srv_t *iter = reply->srv;
1256 					while (iter->next) iter = iter->next;
1257 					iter->next = srv;
1258 				}
1259 				break;
1260 			}
1261 			default:
1262 				malformed = _mdns_debug;
1263 				break;
1264 		}
1265 		if (malformed != 0) {
1266 			_mdns_debug_message(";; [%s %hu %hu]: malformed reply [ctx %p]\n", fullname, rrtype, rrclass, context);
1267 			goto wakeup_kevent;
1268 		}
1269 	}
1270 
1271 	if (context->answer) {
1272 		int n;
1273 		uint8_t *cp;
1274 		HEADER *ans;
1275 		size_t buflen = context->ansmaxlen - context->anslen;
1276 		if (buflen < NS_HFIXEDSZ) {
1277 			_mdns_debug_message(";; [%s %hu %hu]: malformed reply (too small) [ctx %p]\n", fullname, rrtype, rrclass, context);
1278 			goto wakeup_kevent;
1279 		}
1280 
1281 		cp = context->answer + context->anslen;
1282 
1283 		n = _mdns_pack_domain_name(fullname, cp, buflen);
1284 		if (n < 0) {
1285 			_mdns_debug_message(";; [%s %hu %hu]: name mismatch [ctx %p]\n", fullname, rrtype, rrclass, context);
1286 			goto wakeup_kevent;
1287 		}
1288 
1289 		// check that there is enough space in the buffer for the
1290 		// resource name (n), the resource record data (rdlen) and
1291 		// the resource record header (10).
1292 		if (buflen < n + rdlen + 10) {
1293 			_mdns_debug_message(";; [%s %hu %hu]: insufficient buffer space for reply [ctx %p]\n", fullname, rrtype, rrclass, context);
1294 			goto wakeup_kevent;
1295 		}
1296 
1297 		cp += n;
1298 		buflen -= n;
1299 
1300 		uint16_t word;
1301 		uint32_t longword;
1302 
1303 		word = htons(rrtype);
1304 		memcpy(cp, &word, sizeof(word));
1305 		cp += sizeof(word);
1306 
1307 		word = htons(rrclass);
1308 		memcpy(cp, &word, sizeof(word));
1309 		cp += sizeof(word);
1310 
1311 		longword = htonl(ttl);
1312 		memcpy(cp, &longword, sizeof(longword));
1313 		cp += sizeof(longword);
1314 
1315 		word = htons(rdlen);
1316 		memcpy(cp, &word, sizeof(word));
1317 		cp += sizeof(word);
1318 
1319 		memcpy(cp, rdata, rdlen);
1320 		cp += rdlen;
1321 
1322 		ans = (HEADER *)context->answer;
1323 		ans->ancount = htons(ntohs(ans->ancount) + 1);
1324 
1325 		context->anslen = (size_t)(cp - context->answer);
1326 	}
1327 
1328 	_mdns_debug_message(";; [%s %hu %hu] reply [ctx %p]\n", fullname, rrtype, rrclass, context);
1329 
1330 wakeup_kevent:
1331 	// Ping the waiting thread in case this callback was invoked on another
1332 	if (context->kq != -1) {
1333 		struct kevent ev;
1334 		EV_SET(&ev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, 0);
1335 		int res = kevent(context->kq, &ev, 1, NULL, 0, NULL);
1336 		if (res != 0) _mdns_debug_message(";; kevent EV_TRIGGER: %s [ctx %p]\n", strerror(errno), context);
1337 	}
1338 }
1339 
1340 static void
_mdns_now(struct timespec * now)1341 _mdns_now(struct timespec *now) {
1342 	struct timeval tv;
1343 	gettimeofday(&tv, NULL);
1344 	now->tv_sec = tv.tv_sec;
1345 	now->tv_nsec = tv.tv_usec * 1000;
1346 }
1347 
1348 static void
_mdns_add_time(struct timespec * sum,const struct timespec * a,const struct timespec * b)1349 _mdns_add_time(struct timespec *sum, const struct timespec *a, const struct timespec *b)
1350 {
1351 	sum->tv_sec = a->tv_sec + b->tv_sec;
1352 	sum->tv_nsec = a->tv_nsec + b->tv_nsec;
1353 	if (sum->tv_nsec > 1000000000) {
1354 		sum->tv_sec += (sum->tv_nsec / 1000000000);
1355 		sum->tv_nsec %= 1000000000;
1356 	}
1357 }
1358 
1359 // calculate a deadline from the current time based on the desired timeout
1360 static void
_mdns_deadline(struct timespec * deadline,const struct timespec * delta)1361 _mdns_deadline(struct timespec *deadline, const struct timespec *delta)
1362 {
1363 	struct timespec now;
1364 	_mdns_now(&now);
1365 	_mdns_add_time(deadline, &now, delta);
1366 }
1367 
1368 static void
_mdns_sub_time(struct timespec * delta,const struct timespec * a,const struct timespec * b)1369 _mdns_sub_time(struct timespec *delta, const struct timespec *a, const struct timespec *b)
1370 {
1371 	delta->tv_sec = a->tv_sec - b->tv_sec;
1372 	delta->tv_nsec = a->tv_nsec - b->tv_nsec;
1373 	if (delta->tv_nsec < 0) {
1374 		delta->tv_nsec += 1000000000;
1375 		delta->tv_sec -= 1;
1376 	}
1377 }
1378 
1379 // calculate a timeout remaining before the given deadline
1380 static void
_mdns_timeout(struct timespec * timeout,const struct timespec * deadline)1381 _mdns_timeout(struct timespec *timeout, const struct timespec *deadline)
1382 {
1383 	struct timespec now;
1384 	_mdns_now(&now);
1385 	_mdns_sub_time(timeout, deadline, &now);
1386 }
1387 
1388 int
_mdns_search(const char * name,int class,int type,const char * interface,DNSServiceFlags flags,uint8_t * answer,uint32_t * anslen,mdns_reply_t * reply)1389 _mdns_search(const char *name, int class, int type, const char *interface, DNSServiceFlags flags, uint8_t *answer, uint32_t *anslen, mdns_reply_t *reply)
1390 {
1391 	DNSServiceErrorType err = 0;
1392 	int kq, n, wait = 1;
1393 	struct kevent ev;
1394 	struct timespec start, finish, delta, timeout;
1395 	int res = 0;
1396 	int i, complete, got_a_response = 0;
1397 	int initialize = 1;
1398 	uint32_t n_iface_4 = 0;
1399 
1400 	// determine number of IPv4 interfaces (ignore loopback)
1401 	si_inet_config(&n_iface_4, NULL);
1402 	if (n_iface_4 > 0) n_iface_4--;
1403 
1404 	// <rdar://problem/7732497> limit the number of initialization retries
1405 	int initialize_retries = 3;
1406 
1407 	// 2 for A and AAAA parallel queries
1408 	int n_ctx = 0;
1409 	mdns_query_context_t ctx[2];
1410 
1411 	if (name == NULL) return -1;
1412 
1413 #if TARGET_OS_EMBEDDED
1414 	// log a warning for queries from the main thread
1415 	if (pthread_is_threaded_np() && pthread_main_np()) asl_log(NULL, NULL, ASL_LEVEL_WARNING, "Warning: Libinfo call to mDNSResponder on main thread");
1416 #endif // TARGET_OS_EMBEDDED
1417 
1418 	// Timeout Logic
1419 	// The kevent(2) API timeout parameter is used to enforce the total
1420 	// timeout of the DNS query.  Each iteraion recalculates the relative
1421 	// timeout based on the desired end time (total timeout from origin).
1422 	//
1423 	// In order to workaround some DNS configurations that do not return
1424 	// responses for AAAA queries, parallel queries modify the total
1425 	// timeout upon receipt of the first response.  The new total timeout is
1426 	// set to an effective value of 2N where N is the time taken to receive
1427 	// the A response (the original total timeout is preserved if 2N would
1428 	// have exceeded it).  However, since mDNSResponder caches values, a
1429 	// minimum value of 50ms for N is enforced in order to give some time
1430 	// for the receipt of a AAAA response.
1431 
1432 	// determine the maximum time to wait for a result
1433 	delta.tv_sec = RES_MAXRETRANS + 5;
1434 	delta.tv_nsec = 0;
1435 	_mdns_deadline(&finish, &delta);
1436 	timeout = delta;
1437 	_mdns_now(&start);
1438 
1439 	for (i = 0; i < 2; ++i) {
1440 		memset(&ctx[i], 0 , sizeof(mdns_query_context_t));
1441 	}
1442 
1443 	// set up the kqueue
1444 	kq = kqueue();
1445 	EV_SET(&ev, 1, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, 0);
1446 	n = kevent(kq, &ev, 1, NULL, 0, NULL);
1447 	if (n != 0) wait = 0;
1448 
1449 	while (wait == 1) {
1450 		if (initialize) {
1451 			initialize = 0;
1452 			pthread_mutex_lock(&_mdns_mutex);
1453 			// clear any stale contexts
1454 			for (i = 0; i < n_ctx; ++i) {
1455 				_mdns_query_clear(&ctx[i]);
1456 			}
1457 			n_ctx = 0;
1458 
1459 			if (_mdns_sdref == NULL) {
1460 				if (_mdns_old_sdref != NULL) {
1461 					_mdns_generation++;
1462 					DNSServiceRefDeallocate(_mdns_old_sdref);
1463 					_mdns_old_sdref = NULL;
1464 				}
1465 				// (re)initialize the shared connection
1466 				err = DNSServiceCreateConnection(&_mdns_sdref);
1467 
1468 				// limit the number of retries
1469 				if (initialize_retries-- <= 0 && err == 0) {
1470 					err = kDNSServiceErr_Unknown;
1471 				}
1472 				if (err != 0) {
1473 					wait = 0;
1474 					pthread_mutex_unlock(&_mdns_mutex);
1475 					break;
1476 				}
1477 			}
1478 
1479 			// issue (or reissue) the queries
1480 			// unspecified type: do parallel A and AAAA
1481 			if (err == 0) {
1482 				err = _mdns_query_start(&ctx[n_ctx++], reply,
1483 										answer, anslen,
1484 										name, class,
1485 										(type == 0) ? ns_t_a : type, interface, flags, kq);
1486 			}
1487 
1488 			if (err == 0 && type == 0) {
1489 				err = _mdns_query_start(&ctx[n_ctx++], reply,
1490 										answer, anslen,
1491 										name, class, ns_t_aaaa, interface, flags, kq);
1492 			}
1493 
1494 			if (err != 0) {
1495 				_mdns_debug_message(";; initialization error %d\n", err);
1496 			}
1497 
1498 			// try to reinitialize
1499 			if (err == kDNSServiceErr_Unknown ||
1500 				err == kDNSServiceErr_ServiceNotRunning ||
1501 				err == kDNSServiceErr_BadReference) {
1502 				if (_mdns_sdref) {
1503 					_mdns_generation++;
1504 					DNSServiceRefDeallocate(_mdns_sdref);
1505 					_mdns_sdref = NULL;
1506 				}
1507 				err = 0;
1508 				initialize = 1;
1509 				pthread_mutex_unlock(&_mdns_mutex);
1510 				continue;
1511 			} else if (err != 0) {
1512 				pthread_mutex_unlock(&_mdns_mutex);
1513 				break;
1514 			}
1515 
1516 			// (re)register the fd with kqueue
1517 			int fd = DNSServiceRefSockFD(_mdns_sdref);
1518 			EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
1519 			n = kevent(kq, &ev, 1, NULL, 0, NULL);
1520 			pthread_mutex_unlock(&_mdns_mutex);
1521 			if (err != 0 || n != 0) break;
1522 		}
1523 
1524 		_mdns_debug_message(";; set kevent timeout %ld.%ld [ctx %p %p]\n", timeout.tv_sec, timeout.tv_nsec, (n_ctx > 0) ? &(ctx[0]) : NULL, (n_ctx > 1) ? &(ctx[1]) : NULL);
1525 
1526 		n = kevent(kq, NULL, 0, &ev, 1, &timeout);
1527 		if (n < 0 && errno != EINTR) {
1528 			res = -1;
1529 			break;
1530 		}
1531 
1532 		pthread_mutex_lock(&_mdns_mutex);
1533 		// DNSServiceProcessResult() is a blocking API
1534 		// confirm that there is still data on the socket
1535 		const struct timespec notimeout = { 0, 0 };
1536 		int m = kevent(kq, NULL, 0, &ev, 1, &notimeout);
1537 		if (_mdns_sdref == NULL) {
1538 			initialize = 1;
1539 		} else if (m > 0 && ev.filter == EVFILT_READ) {
1540 			err = DNSServiceProcessResult(_mdns_sdref);
1541 			if (err == kDNSServiceErr_ServiceNotRunning ||
1542 			    err == kDNSServiceErr_BadReference) {
1543 				_mdns_debug_message(";; DNSServiceProcessResult status %d [ctx %p %p]\n", err, (n_ctx > 0) ? &(ctx[0]) : NULL, (n_ctx > 1) ? &(ctx[1]) : NULL);
1544 				err = 0;
1545 				// re-initialize the shared connection
1546 				_mdns_generation++;
1547 				DNSServiceRefDeallocate(_mdns_sdref);
1548 				_mdns_sdref = NULL;
1549 				initialize = 1;
1550 			}
1551 		}
1552 
1553 		// Check if all queries are complete (including errors)
1554 		complete = 1;
1555 		for (i = 0; i < n_ctx; ++i) {
1556 			if ((ctx[i].error != 0) || _mdns_query_is_complete(&ctx[i])) {
1557 				if (ctx[i].type == ns_t_a) {
1558 					got_a_response = GOT_DATA;
1559 					if (ctx[i].error != 0) got_a_response = GOT_ERROR;
1560 				}
1561 				_mdns_debug_message(";; [%s %d %d] finished processing ctx %p\n", name, class, type, &(ctx[i]));
1562 
1563 			} else {
1564 				_mdns_debug_message(";; [%s %d %d] continuing ctx %p\n", name, class, type, &(ctx[i]));
1565 				complete = 0;
1566 			}
1567 		}
1568 		pthread_mutex_unlock(&_mdns_mutex);
1569 
1570 		if (err != 0) {
1571 			_mdns_debug_message(";; DNSServiceProcessResult error status %d [ctx %p %p]\n", err, (n_ctx > 0) ? &(ctx[0]) : NULL, (n_ctx > 1) ? &(ctx[1]) : NULL);
1572 			break;
1573 		} else if (complete == 1) {
1574 			_mdns_debug_message(";; [%s %d %d] done [ctx %p %p]\n", name, class, type, (n_ctx > 0) ? &(ctx[0]) : NULL, (n_ctx > 1) ? &(ctx[1]) : NULL);
1575 			break;
1576 		} else if (got_a_response != 0) {
1577 			// got A, adjust deadline for AAAA
1578 			struct timespec now, tn, extra;
1579 
1580 			// delta = now - start
1581 			_mdns_now(&now);
1582 			_mdns_sub_time(&delta, &now, &start);
1583 
1584 			extra.tv_sec = SHORT_AAAA_EXTRA;
1585 			extra.tv_nsec = 0;
1586 
1587 			// if delta is small (<= 20 milliseconds), we probably got a result from mDNSResponder's cache
1588 			if ((delta.tv_sec == 0) && (delta.tv_nsec <= 20000000)) {
1589 				extra.tv_sec = MEDIUM_AAAA_EXTRA;
1590 			}
1591 			else if (n_iface_4 == 0) {
1592 				extra.tv_sec = LONG_AAAA_EXTRA;
1593 			} else if (got_a_response == GOT_ERROR) {
1594 				extra.tv_sec = MEDIUM_AAAA_EXTRA;
1595 			}
1596 
1597 			// tn = 2 * delta
1598 			_mdns_add_time(&tn, &delta, &delta);
1599 
1600 			// delta = tn + extra
1601 			_mdns_add_time(&delta, &tn, &extra);
1602 
1603 			// check that delta doesn't exceed our total timeout
1604 			_mdns_sub_time(&tn, &timeout, &delta);
1605 			if (tn.tv_sec >= 0) {
1606 				_mdns_debug_message(";; new timeout [%s %d %d] (waiting for AAAA) %ld.%ld [ctx %p %p]\n", name, class, type, delta.tv_sec, delta.tv_nsec, (n_ctx > 0) ? &(ctx[0]) : NULL, (n_ctx > 1) ? &(ctx[1]) : NULL);
1607 				_mdns_deadline(&finish, &delta);
1608 			}
1609 		}
1610 
1611 		// calculate remaining timeout
1612 		_mdns_timeout(&timeout, &finish);
1613 
1614 		// check for time remaining
1615 		if (timeout.tv_sec < 0) {
1616 			_mdns_debug_message(";; [%s %d %d] timeout [ctx %p %p]\n", name, class, type, (n_ctx > 0) ? &(ctx[0]) : NULL, (n_ctx > 1) ? &(ctx[1]) : NULL);
1617 			break;
1618 		}
1619 	}
1620 
1621 	complete = 0;
1622 	pthread_mutex_lock(&_mdns_mutex);
1623 	for (i = 0; i < n_ctx; ++i) {
1624 		if (err == 0) err = ctx[i].error;
1625 		// Only clears hostents if result is incomplete.
1626 		complete = _mdns_query_clear(&ctx[i]) || complete;
1627 	}
1628 	pthread_mutex_unlock(&_mdns_mutex);
1629 	// Everything should be done with the kq by now.
1630 	close(kq);
1631 
1632 	// Return error if everything is incomplete
1633 	if (complete == 0) {
1634 		res = -1;
1635 	}
1636 
1637 	if (anslen) *anslen = ctx[0].anslen;
1638 	return res;
1639 }
1640