]> git.saurik.com Git - apple/libinfo.git/blob - lookup.subproj/mdns_module.c
Libinfo-392.tar.gz
[apple/libinfo.git] / lookup.subproj / mdns_module.c
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 static int _mdns_debug = 0;
137
138 // mutex protects DNSServiceProcessResult and DNSServiceRefDeallocate
139 static pthread_mutex_t _mdns_mutex = PTHREAD_MUTEX_INITIALIZER;
140
141 typedef struct {
142 uint16_t priority;
143 uint16_t weight;
144 uint16_t port;
145 uint8_t target[0];
146 } mdns_rr_srv_t;
147
148 typedef struct mdns_srv_t mdns_srv_t;
149 struct mdns_srv_t {
150 si_srv_t srv;
151 mdns_srv_t *next;
152 };
153
154 typedef struct {
155 struct hostent host;
156 int alias_count;
157 int addr_count;
158 } mdns_hostent_t;
159
160 typedef 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
168 static uint32_t _mdns_generation = 0;
169 static DNSServiceRef _mdns_sdref;
170 static DNSServiceRef _mdns_old_sdref;
171
172 static void _mdns_hostent_clear(mdns_hostent_t *h);
173 static void _mdns_reply_clear(mdns_reply_t *r);
174 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);
175
176 static 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
189 const 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
200 static 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
218 static 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 */
249 static 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 */
268 static 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 */
305 static 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
327 static 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
352 static 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
369 static si_item_t *
370 mdns_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
432 static si_item_t *
433 mdns_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
497 static si_list_t *
498 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)
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
594 static si_list_t *
595 mdns_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 */
625 static si_item_t *
626 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)
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
681 static int
682 mdns_is_valid(si_mod_t *si, si_item_t *item)
683 {
684 return 0;
685 }
686
687 static void
688 mdns_close(si_mod_t *si)
689 {
690 }
691
692 static 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
699 static void
700 _mdns_atfork_parent(void)
701 {
702 // parent can simply resume
703 pthread_mutex_unlock(&_mdns_mutex);
704 }
705
706 static 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
715 static 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
723 si_mod_t *
724 si_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 */
762 static 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 */
804 static 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
836 static 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 */
873 static 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
921 static 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
952 typedef 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
967 static 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 */
973 static 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) abort();
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 */
1034 static 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 abort();
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 */
1067 static 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
1092 static 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
1270 wakeup_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
1280 static 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
1288 static 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
1300 static 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
1308 static 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
1320 static 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
1328 int
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 really small, we probably got a result from mDNSResponder's cache
1519 if ((delta.tv_sec == 0) && (delta.tv_nsec <= 200000000)) {
1520 extra.tv_sec = LONG_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 }