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