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