nss_mdnsd.c revision 1.4 1 1.4 christos /* $NetBSD: nss_mdnsd.c,v 1.4 2014/03/31 02:03:38 christos Exp $ */
2 1.1 tsarna
3 1.1 tsarna /*-
4 1.1 tsarna * Copyright (c) 2009 The NetBSD Foundation, Inc.
5 1.1 tsarna * All rights reserved.
6 1.1 tsarna *
7 1.1 tsarna * This code is derived from software contributed to The NetBSD Foundation
8 1.1 tsarna * by Tyler C. Sarna
9 1.1 tsarna *
10 1.1 tsarna * Redistribution and use in source and binary forms, with or without
11 1.1 tsarna * modification, are permitted provided that the following conditions
12 1.1 tsarna * are met:
13 1.1 tsarna * 1. Redistributions of source code must retain the above copyright
14 1.1 tsarna * notice, this list of conditions and the following disclaimer.
15 1.1 tsarna * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 tsarna * notice, this list of conditions and the following disclaimer in the
17 1.1 tsarna * documentation and/or other materials provided with the distribution.
18 1.1 tsarna *
19 1.1 tsarna * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.1 tsarna * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.1 tsarna * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.1 tsarna * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.1 tsarna * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.1 tsarna * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.1 tsarna * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.1 tsarna * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.1 tsarna * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.1 tsarna * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.1 tsarna * POSSIBILITY OF SUCH DAMAGE.
30 1.1 tsarna */
31 1.1 tsarna
32 1.1 tsarna /*
33 1.1 tsarna * Multicast DNS ("Bonjour") hosts name service switch
34 1.1 tsarna *
35 1.1 tsarna * Documentation links:
36 1.1 tsarna *
37 1.1 tsarna * http://developer.apple.com/bonjour/
38 1.1 tsarna * http://www.multicastdns.org/
39 1.1 tsarna * http://www.dns-sd.org/
40 1.1 tsarna */
41 1.1 tsarna
42 1.1 tsarna #include <errno.h>
43 1.1 tsarna #include <nsswitch.h>
44 1.1 tsarna #include <stdarg.h>
45 1.1 tsarna #include <stdlib.h>
46 1.1 tsarna #include <sys/socket.h>
47 1.1 tsarna #include <sys/param.h>
48 1.3 tsarna #include <sys/queue.h>
49 1.1 tsarna #include <netdb.h>
50 1.1 tsarna #include <netinet/in.h>
51 1.1 tsarna #include <arpa/nameser.h>
52 1.1 tsarna #include <resolv.h>
53 1.1 tsarna #include <dns_sd.h>
54 1.1 tsarna #include <poll.h>
55 1.1 tsarna #include <string.h>
56 1.1 tsarna #include <stdio.h>
57 1.1 tsarna #include <stdbool.h>
58 1.3 tsarna #include <pthread.h>
59 1.3 tsarna #include <fcntl.h>
60 1.3 tsarna #include <unistd.h>
61 1.3 tsarna #include <time.h>
62 1.3 tsarna
63 1.3 tsarna
64 1.4 christos #include "hostent.h"
65 1.4 christos
66 1.3 tsarna /*
67 1.3 tsarna * Pool of mdnsd connections
68 1.3 tsarna */
69 1.3 tsarna static SLIST_HEAD(, svc_ref) conn_list = LIST_HEAD_INITIALIZER(&conn_list);
70 1.3 tsarna static unsigned int conn_count = 0;
71 1.3 tsarna static pid_t my_pid;
72 1.3 tsarna struct timespec last_config;
73 1.3 tsarna
74 1.3 tsarna typedef struct svc_ref {
75 1.3 tsarna SLIST_ENTRY(svc_ref) entries;
76 1.3 tsarna DNSServiceRef sdRef;
77 1.3 tsarna unsigned int uses;
78 1.3 tsarna } svc_ref;
79 1.3 tsarna
80 1.3 tsarna /*
81 1.3 tsarna * There is a large class of programs that do a few lookups at startup
82 1.3 tsarna * and then never again (ping, telnet, etc). Keeping a persistent connection
83 1.3 tsarna * for these would be a waste, so there is a kind of slow start mechanism.
84 1.3 tsarna * The first SLOWSTART_LOOKUPS times, dispose of the connection after use.
85 1.3 tsarna * After that we assume the program is a serious consumer of host lookup
86 1.3 tsarna * services and start keeping connections.
87 1.3 tsarna */
88 1.3 tsarna #define SLOWSTART_LOOKUPS 5
89 1.3 tsarna static unsigned int svc_puts = 0;
90 1.3 tsarna
91 1.3 tsarna /*
92 1.3 tsarna * Age out connections. Free connection instead of putting on the list
93 1.3 tsarna * if used more than REUSE_TIMES and there are others on the list.
94 1.3 tsarna */
95 1.3 tsarna #define REUSE_TIMES 32
96 1.3 tsarna
97 1.3 tsarna /* protects above data */
98 1.3 tsarna static pthread_mutex_t conn_list_lock = PTHREAD_MUTEX_INITIALIZER;
99 1.3 tsarna
100 1.3 tsarna extern int __isthreaded; /* libc private -- wish there was a better way */
101 1.3 tsarna
102 1.3 tsarna #define LOCK(x) do { if (__isthreaded) pthread_mutex_lock(x); } while (0)
103 1.3 tsarna #define UNLOCK(x) do { if (__isthreaded) pthread_mutex_unlock(x); } while (0)
104 1.3 tsarna
105 1.1 tsarna
106 1.1 tsarna #ifndef lint
107 1.1 tsarna #define UNUSED(a) (void)&a
108 1.1 tsarna #else
109 1.1 tsarna #define UNUSED(a) a = a
110 1.1 tsarna #endif
111 1.1 tsarna
112 1.1 tsarna #define MAXALIASES 35
113 1.1 tsarna #define MAXADDRS 35
114 1.1 tsarna
115 1.1 tsarna typedef struct callback_ctx {
116 1.1 tsarna bool done;
117 1.1 tsarna } callback_ctx;
118 1.1 tsarna
119 1.1 tsarna typedef struct hostent_ctx {
120 1.1 tsarna callback_ctx cb_ctx; /* must come first */
121 1.1 tsarna struct hostent host;
122 1.1 tsarna char *h_addr_ptrs[MAXADDRS + 1];
123 1.1 tsarna char *host_aliases[MAXALIASES];
124 1.1 tsarna char addrs[MAXADDRS * 16];
125 1.1 tsarna char buf[8192], *next;
126 1.1 tsarna int naliases, naddrs;
127 1.1 tsarna } hostent_ctx;
128 1.1 tsarna
129 1.1 tsarna typedef struct addrinfo_ctx {
130 1.1 tsarna callback_ctx cb_ctx; /* must come first */
131 1.1 tsarna struct addrinfo start, *last;
132 1.1 tsarna } addrinfo_ctx;
133 1.1 tsarna
134 1.1 tsarna #define HCTX_BUFLEFT(c) (sizeof((c)->buf) - ((c)->next - (c)->buf))
135 1.1 tsarna
136 1.3 tsarna typedef struct res_conf {
137 1.3 tsarna unsigned int refcount;
138 1.3 tsarna char **search_domains;
139 1.3 tsarna char **no_search;
140 1.3 tsarna short ndots;
141 1.3 tsarna short timeout;
142 1.3 tsarna } res_conf;
143 1.3 tsarna
144 1.3 tsarna static res_conf *cur_res_conf;
145 1.3 tsarna
146 1.3 tsarna /* protects above data */
147 1.3 tsarna static pthread_mutex_t res_conf_lock = PTHREAD_MUTEX_INITIALIZER;
148 1.3 tsarna
149 1.1 tsarna typedef struct search_iter {
150 1.3 tsarna res_conf *conf;
151 1.1 tsarna const char *name;
152 1.3 tsarna char **next_search;
153 1.1 tsarna size_t baselen;
154 1.1 tsarna bool abs_first;
155 1.1 tsarna bool abs_last;
156 1.1 tsarna char buf[MAXHOSTNAMELEN];
157 1.1 tsarna } search_iter;
158 1.1 tsarna
159 1.1 tsarna static DNSServiceFlags svc_flags = 0;
160 1.1 tsarna
161 1.1 tsarna ns_mtab *nss_module_register(const char *, u_int *, nss_module_unregister_fn *);
162 1.1 tsarna static int load_config(res_state);
163 1.1 tsarna
164 1.1 tsarna static int _mdns_getaddrinfo(void *, void *, va_list);
165 1.1 tsarna static int _mdns_gethtbyaddr(void *, void *, va_list);
166 1.1 tsarna static int _mdns_gethtbyname(void *, void *, va_list);
167 1.1 tsarna
168 1.1 tsarna static int _mdns_getaddrinfo_abs(const char *, DNSServiceProtocol,
169 1.3 tsarna svc_ref **, addrinfo_ctx *, short);
170 1.1 tsarna static void _mdns_addrinfo_init(addrinfo_ctx *, const struct addrinfo *);
171 1.1 tsarna static void _mdns_addrinfo_add_ai(addrinfo_ctx *, struct addrinfo *);
172 1.1 tsarna static struct addrinfo *_mdns_addrinfo_done(addrinfo_ctx *);
173 1.1 tsarna
174 1.4 christos static int _mdns_gethtbyname_abs(struct getnamaddr *, struct hostent_ctx *,
175 1.4 christos const char *, int, svc_ref **, short);
176 1.1 tsarna static void _mdns_hostent_init(hostent_ctx *, int, int);
177 1.1 tsarna static void _mdns_hostent_add_host(hostent_ctx *, const char *);
178 1.1 tsarna static void _mdns_hostent_add_addr(hostent_ctx *, const void *, uint16_t);
179 1.4 christos static int _mdns_hostent_done(struct getnamaddr *, hostent_ctx *);
180 1.1 tsarna
181 1.1 tsarna static void _mdns_addrinfo_cb(DNSServiceRef, DNSServiceFlags,
182 1.1 tsarna uint32_t, DNSServiceErrorType, const char *, const struct sockaddr *,
183 1.1 tsarna uint32_t, void *);
184 1.1 tsarna static void _mdns_hostent_cb(DNSServiceRef, DNSServiceFlags,
185 1.1 tsarna uint32_t, DNSServiceErrorType, const char *, uint16_t, uint16_t, uint16_t,
186 1.1 tsarna const void *, uint32_t, void *);
187 1.3 tsarna static void _mdns_eventloop(svc_ref *, callback_ctx *, short);
188 1.1 tsarna
189 1.1 tsarna static char *_mdns_rdata2name(const unsigned char *, uint16_t,
190 1.1 tsarna char *, size_t);
191 1.1 tsarna
192 1.3 tsarna static int search_init(search_iter *, const char *, const char **);
193 1.3 tsarna static void search_done(search_iter *);
194 1.3 tsarna static const char *search_next(search_iter *);
195 1.3 tsarna static bool searchable_domain(char *);
196 1.3 tsarna
197 1.3 tsarna static void destroy_svc_ref(svc_ref *);
198 1.3 tsarna static svc_ref *get_svc_ref(void);
199 1.3 tsarna static void put_svc_ref(svc_ref *);
200 1.3 tsarna static bool retry_query(svc_ref **, DNSServiceErrorType);
201 1.3 tsarna
202 1.3 tsarna static void decref_res_conf(res_conf *);
203 1.3 tsarna static res_conf *get_res_conf(void);
204 1.3 tsarna static void put_res_conf(res_conf *);
205 1.3 tsarna static res_conf *new_res_conf(res_state);
206 1.3 tsarna static short get_timeout(void);
207 1.1 tsarna
208 1.1 tsarna static ns_mtab mtab[] = {
209 1.1 tsarna { NSDB_HOSTS, "getaddrinfo", _mdns_getaddrinfo, NULL },
210 1.1 tsarna { NSDB_HOSTS, "gethostbyaddr", _mdns_gethtbyaddr, NULL },
211 1.1 tsarna { NSDB_HOSTS, "gethostbyname", _mdns_gethtbyname, NULL },
212 1.1 tsarna };
213 1.1 tsarna
214 1.1 tsarna
215 1.1 tsarna
216 1.1 tsarna ns_mtab *
217 1.1 tsarna nss_module_register(const char *source, u_int *nelems,
218 1.1 tsarna nss_module_unregister_fn *unreg)
219 1.1 tsarna {
220 1.1 tsarna *nelems = sizeof(mtab) / sizeof(mtab[0]);
221 1.1 tsarna *unreg = NULL;
222 1.1 tsarna
223 1.3 tsarna my_pid = getpid();
224 1.3 tsarna
225 1.1 tsarna if (!strcmp(source, "multicast_dns")) {
226 1.1 tsarna svc_flags = kDNSServiceFlagsForceMulticast;
227 1.1 tsarna }
228 1.1 tsarna
229 1.1 tsarna return mtab;
230 1.1 tsarna }
231 1.1 tsarna
232 1.1 tsarna
233 1.1 tsarna
234 1.1 tsarna static int
235 1.1 tsarna _mdns_getaddrinfo(void *cbrv, void *cbdata, va_list ap)
236 1.1 tsarna {
237 1.1 tsarna const struct addrinfo *pai;
238 1.1 tsarna const char *name, *sname;
239 1.1 tsarna DNSServiceProtocol proto;
240 1.1 tsarna DNSServiceRef sdRef;
241 1.1 tsarna addrinfo_ctx ctx;
242 1.1 tsarna search_iter iter;
243 1.3 tsarna res_conf *rc;
244 1.3 tsarna svc_ref *sr;
245 1.3 tsarna int err;
246 1.1 tsarna
247 1.1 tsarna UNUSED(cbdata);
248 1.1 tsarna
249 1.1 tsarna name = va_arg(ap, char *);
250 1.1 tsarna pai = va_arg(ap, struct addrinfo *);
251 1.1 tsarna
252 1.1 tsarna switch (pai->ai_family) {
253 1.1 tsarna case AF_UNSPEC:
254 1.1 tsarna proto = kDNSServiceProtocol_IPv6 | kDNSServiceProtocol_IPv4;
255 1.1 tsarna break;
256 1.1 tsarna
257 1.1 tsarna case AF_INET6:
258 1.1 tsarna proto = kDNSServiceProtocol_IPv6;
259 1.1 tsarna break;
260 1.1 tsarna
261 1.1 tsarna case AF_INET:
262 1.1 tsarna proto = kDNSServiceProtocol_IPv4;
263 1.1 tsarna break;
264 1.1 tsarna
265 1.1 tsarna default:
266 1.1 tsarna h_errno = NO_RECOVERY;
267 1.1 tsarna return NS_UNAVAIL;
268 1.1 tsarna }
269 1.1 tsarna
270 1.3 tsarna sr = get_svc_ref();
271 1.3 tsarna if (!sr) {
272 1.1 tsarna h_errno = NETDB_INTERNAL;
273 1.1 tsarna return NS_UNAVAIL;
274 1.1 tsarna }
275 1.1 tsarna
276 1.3 tsarna if ((err = search_init(&iter, name, &sname)) != NS_SUCCESS) {
277 1.3 tsarna put_svc_ref(sr);
278 1.3 tsarna return err;
279 1.3 tsarna }
280 1.3 tsarna
281 1.1 tsarna _mdns_addrinfo_init(&ctx, pai);
282 1.3 tsarna
283 1.3 tsarna err = NS_NOTFOUND;
284 1.3 tsarna while (sr && sname && (err != NS_SUCCESS)) {
285 1.3 tsarna err = _mdns_getaddrinfo_abs(sname, proto, &sr, &ctx, iter.conf->timeout);
286 1.1 tsarna if (err != NS_SUCCESS) {
287 1.1 tsarna sname = search_next(&iter);
288 1.1 tsarna }
289 1.1 tsarna };
290 1.1 tsarna
291 1.3 tsarna search_done(&iter);
292 1.3 tsarna put_svc_ref(sr);
293 1.1 tsarna
294 1.1 tsarna if (err == NS_SUCCESS) {
295 1.1 tsarna *(struct addrinfo **)cbrv = _mdns_addrinfo_done(&ctx);
296 1.1 tsarna }
297 1.1 tsarna
298 1.1 tsarna return err;
299 1.1 tsarna }
300 1.1 tsarna
301 1.1 tsarna
302 1.1 tsarna
303 1.1 tsarna static int
304 1.1 tsarna _mdns_getaddrinfo_abs(const char *name, DNSServiceProtocol proto,
305 1.3 tsarna svc_ref **sr, addrinfo_ctx *ctx, short timeout)
306 1.1 tsarna {
307 1.3 tsarna DNSServiceErrorType err = kDNSServiceErr_ServiceNotRunning;
308 1.3 tsarna DNSServiceRef sdRef;
309 1.3 tsarna bool retry = true;
310 1.3 tsarna
311 1.3 tsarna while (*sr && retry) {
312 1.3 tsarna /* We must always use a copy of the ref when using a shared
313 1.3 tsarna connection, per kDNSServiceFlagsShareConnection docs */
314 1.3 tsarna
315 1.3 tsarna sdRef = (*sr)->sdRef;
316 1.3 tsarna
317 1.3 tsarna err = DNSServiceGetAddrInfo(
318 1.3 tsarna &sdRef,
319 1.3 tsarna svc_flags
320 1.3 tsarna | kDNSServiceFlagsShareConnection
321 1.3 tsarna | kDNSServiceFlagsReturnIntermediates,
322 1.3 tsarna kDNSServiceInterfaceIndexAny,
323 1.3 tsarna proto,
324 1.3 tsarna name,
325 1.3 tsarna _mdns_addrinfo_cb,
326 1.3 tsarna ctx
327 1.3 tsarna );
328 1.3 tsarna
329 1.3 tsarna retry = retry_query(sr, err);
330 1.3 tsarna }
331 1.1 tsarna
332 1.1 tsarna if (err) {
333 1.1 tsarna h_errno = NETDB_INTERNAL;
334 1.1 tsarna return NS_UNAVAIL;
335 1.1 tsarna }
336 1.3 tsarna
337 1.3 tsarna _mdns_eventloop(*sr, (void *)ctx, timeout);
338 1.1 tsarna
339 1.1 tsarna DNSServiceRefDeallocate(sdRef);
340 1.1 tsarna
341 1.1 tsarna if (ctx->start.ai_next) {
342 1.1 tsarna return NS_SUCCESS;
343 1.1 tsarna } else {
344 1.1 tsarna h_errno = HOST_NOT_FOUND;
345 1.1 tsarna return NS_NOTFOUND;
346 1.1 tsarna }
347 1.1 tsarna }
348 1.1 tsarna
349 1.1 tsarna
350 1.1 tsarna
351 1.1 tsarna static int
352 1.1 tsarna _mdns_gethtbyaddr(void *cbrv, void *cbdata, va_list ap)
353 1.1 tsarna {
354 1.1 tsarna const unsigned char *addr;
355 1.1 tsarna int addrlen, af;
356 1.1 tsarna char qbuf[NS_MAXDNAME + 1], *qp, *ep;
357 1.1 tsarna int advance, n;
358 1.1 tsarna DNSServiceErrorType err;
359 1.1 tsarna DNSServiceRef sdRef;
360 1.3 tsarna svc_ref *sr;
361 1.3 tsarna bool retry = true;
362 1.4 christos struct getnamaddr *info = cbrv;
363 1.1 tsarna
364 1.1 tsarna UNUSED(cbdata);
365 1.1 tsarna
366 1.1 tsarna addr = va_arg(ap, unsigned char *);
367 1.1 tsarna addrlen = va_arg(ap, int);
368 1.1 tsarna af = va_arg(ap, int);
369 1.1 tsarna
370 1.1 tsarna switch (af) {
371 1.1 tsarna case AF_INET:
372 1.2 tsarna /* if mcast-only don't bother for non-LinkLocal addrs) */
373 1.2 tsarna if (svc_flags & kDNSServiceFlagsForceMulticast) {
374 1.2 tsarna if ((addr[0] != 169) || (addr[1] != 254)) {
375 1.4 christos *info->he = HOST_NOT_FOUND;
376 1.2 tsarna return NS_NOTFOUND;
377 1.2 tsarna }
378 1.2 tsarna }
379 1.2 tsarna
380 1.1 tsarna (void)snprintf(qbuf, sizeof(qbuf), "%u.%u.%u.%u.in-addr.arpa",
381 1.1 tsarna (addr[3] & 0xff), (addr[2] & 0xff),
382 1.1 tsarna (addr[1] & 0xff), (addr[0] & 0xff));
383 1.1 tsarna break;
384 1.1 tsarna
385 1.1 tsarna case AF_INET6:
386 1.2 tsarna /* if mcast-only don't bother for non-LinkLocal addrs) */
387 1.2 tsarna if (svc_flags & kDNSServiceFlagsForceMulticast) {
388 1.2 tsarna if ((addr[0] != 0xfe) || ((addr[1] & 0xc0) != 0x80)) {
389 1.4 christos *info->he = HOST_NOT_FOUND;
390 1.2 tsarna return NS_NOTFOUND;
391 1.2 tsarna }
392 1.2 tsarna }
393 1.2 tsarna
394 1.1 tsarna qp = qbuf;
395 1.1 tsarna ep = qbuf + sizeof(qbuf) - 1;
396 1.1 tsarna for (n = IN6ADDRSZ - 1; n >= 0; n--) {
397 1.1 tsarna advance = snprintf(qp, (size_t)(ep - qp), "%x.%x.",
398 1.1 tsarna addr[n] & 0xf,
399 1.1 tsarna ((unsigned int)addr[n] >> 4) & 0xf);
400 1.1 tsarna if (advance > 0 && qp + advance < ep)
401 1.1 tsarna qp += advance;
402 1.1 tsarna else {
403 1.4 christos *info->he = NETDB_INTERNAL;
404 1.1 tsarna return NS_NOTFOUND;
405 1.1 tsarna }
406 1.1 tsarna }
407 1.1 tsarna if (strlcat(qbuf, "ip6.arpa", sizeof(qbuf)) >= sizeof(qbuf)) {
408 1.4 christos *info->he = NETDB_INTERNAL;
409 1.1 tsarna return NS_NOTFOUND;
410 1.1 tsarna }
411 1.1 tsarna break;
412 1.1 tsarna
413 1.1 tsarna default:
414 1.4 christos *info->he = NO_RECOVERY;
415 1.1 tsarna return NS_UNAVAIL;
416 1.1 tsarna }
417 1.1 tsarna
418 1.4 christos hostent_ctx h_ctx;
419 1.1 tsarna _mdns_hostent_init(&h_ctx, af, addrlen);
420 1.1 tsarna _mdns_hostent_add_addr(&h_ctx, addr, addrlen);
421 1.1 tsarna
422 1.3 tsarna sr = get_svc_ref();
423 1.3 tsarna if (!sr) {
424 1.4 christos *info->he = NETDB_INTERNAL;
425 1.3 tsarna return NS_UNAVAIL;
426 1.3 tsarna }
427 1.3 tsarna
428 1.3 tsarna while (sr && retry) {
429 1.3 tsarna /* We must always use a copy of the ref when using a shared
430 1.3 tsarna connection, per kDNSServiceFlagsShareConnection docs */
431 1.3 tsarna sdRef = sr->sdRef;
432 1.3 tsarna
433 1.3 tsarna err = DNSServiceQueryRecord(
434 1.3 tsarna &sdRef,
435 1.3 tsarna svc_flags
436 1.3 tsarna | kDNSServiceFlagsShareConnection
437 1.3 tsarna | kDNSServiceFlagsReturnIntermediates,
438 1.3 tsarna kDNSServiceInterfaceIndexAny,
439 1.3 tsarna qbuf,
440 1.3 tsarna kDNSServiceType_PTR,
441 1.3 tsarna kDNSServiceClass_IN,
442 1.3 tsarna _mdns_hostent_cb,
443 1.3 tsarna &h_ctx
444 1.3 tsarna );
445 1.3 tsarna
446 1.3 tsarna retry = retry_query(&sr, err);
447 1.3 tsarna }
448 1.1 tsarna
449 1.1 tsarna if (err) {
450 1.3 tsarna put_svc_ref(sr);
451 1.4 christos *info->he = NETDB_INTERNAL;
452 1.1 tsarna return NS_UNAVAIL;
453 1.1 tsarna }
454 1.3 tsarna
455 1.3 tsarna _mdns_eventloop(sr, (void *)&h_ctx, get_timeout());
456 1.1 tsarna
457 1.1 tsarna DNSServiceRefDeallocate(sdRef);
458 1.3 tsarna put_svc_ref(sr);
459 1.1 tsarna
460 1.1 tsarna if (h_ctx.naliases) {
461 1.4 christos return _mdns_hostent_done(info, &h_ctx);
462 1.1 tsarna } else {
463 1.4 christos *info->he = HOST_NOT_FOUND;
464 1.1 tsarna return NS_NOTFOUND;
465 1.1 tsarna }
466 1.1 tsarna }
467 1.1 tsarna
468 1.1 tsarna
469 1.1 tsarna
470 1.1 tsarna static int
471 1.1 tsarna _mdns_gethtbyname(void *cbrv, void *cbdata, va_list ap)
472 1.1 tsarna {
473 1.3 tsarna int namelen, af, addrlen, rrtype, err;
474 1.1 tsarna const char *name, *sname;
475 1.1 tsarna DNSServiceRef sdRef;
476 1.1 tsarna search_iter iter;
477 1.3 tsarna svc_ref *sr;
478 1.4 christos struct getnamaddr *info = cbrv;
479 1.1 tsarna
480 1.1 tsarna UNUSED(cbdata);
481 1.1 tsarna
482 1.1 tsarna name = va_arg(ap, char *);
483 1.1 tsarna namelen = va_arg(ap, int);
484 1.1 tsarna af = va_arg(ap, int);
485 1.1 tsarna
486 1.1 tsarna UNUSED(namelen);
487 1.1 tsarna
488 1.1 tsarna switch (af) {
489 1.1 tsarna case AF_INET:
490 1.1 tsarna rrtype = kDNSServiceType_A;
491 1.1 tsarna addrlen = 4;
492 1.1 tsarna break;
493 1.1 tsarna
494 1.1 tsarna case AF_INET6:
495 1.1 tsarna rrtype = kDNSServiceType_AAAA;
496 1.1 tsarna addrlen = 16;
497 1.1 tsarna break;
498 1.1 tsarna
499 1.1 tsarna default:
500 1.4 christos *info->he = NO_RECOVERY;
501 1.2 tsarna return NS_UNAVAIL;
502 1.2 tsarna }
503 1.2 tsarna
504 1.3 tsarna sr = get_svc_ref();
505 1.3 tsarna if (!sr) {
506 1.4 christos *info->he = NETDB_INTERNAL;
507 1.1 tsarna return NS_UNAVAIL;
508 1.1 tsarna }
509 1.1 tsarna
510 1.3 tsarna if ((err = search_init(&iter, name, &sname)) != NS_SUCCESS) {
511 1.3 tsarna put_svc_ref(sr);
512 1.3 tsarna return err;
513 1.3 tsarna }
514 1.3 tsarna
515 1.4 christos hostent_ctx h_ctx;
516 1.1 tsarna _mdns_hostent_init(&h_ctx, af, addrlen);
517 1.2 tsarna
518 1.3 tsarna err = NS_NOTFOUND;
519 1.3 tsarna while (sr && sname && (err != NS_SUCCESS)) {
520 1.4 christos err = _mdns_gethtbyname_abs(info, &h_ctx, sname, rrtype, &sr,
521 1.4 christos iter.conf->timeout);
522 1.1 tsarna if (err != NS_SUCCESS) {
523 1.1 tsarna sname = search_next(&iter);
524 1.1 tsarna }
525 1.1 tsarna };
526 1.1 tsarna
527 1.3 tsarna search_done(&iter);
528 1.3 tsarna put_svc_ref(sr);
529 1.3 tsarna
530 1.4 christos if (err != NS_SUCCESS)
531 1.4 christos return err;
532 1.4 christos _mdns_hostent_add_host(&h_ctx, sname);
533 1.4 christos _mdns_hostent_add_host(&h_ctx, name);
534 1.4 christos return _mdns_hostent_done(info, &h_ctx);
535 1.1 tsarna }
536 1.1 tsarna
537 1.1 tsarna
538 1.1 tsarna
539 1.1 tsarna static int
540 1.4 christos _mdns_gethtbyname_abs(struct getnamaddr *info, struct hostent_ctx *ctx,
541 1.4 christos const char *name, int rrtype, svc_ref **sr, short timeout)
542 1.1 tsarna {
543 1.3 tsarna DNSServiceErrorType err = kDNSServiceErr_ServiceNotRunning;
544 1.3 tsarna DNSServiceRef sdRef;
545 1.3 tsarna bool retry = true;
546 1.1 tsarna
547 1.3 tsarna while (*sr && retry) {
548 1.3 tsarna /* We must always use a copy of the ref when using a shared
549 1.3 tsarna connection, per kDNSServiceFlagsShareConnection docs */
550 1.3 tsarna sdRef = (*sr)->sdRef;
551 1.3 tsarna
552 1.3 tsarna err = DNSServiceQueryRecord(
553 1.3 tsarna &sdRef,
554 1.3 tsarna svc_flags
555 1.3 tsarna | kDNSServiceFlagsShareConnection
556 1.3 tsarna | kDNSServiceFlagsReturnIntermediates,
557 1.3 tsarna kDNSServiceInterfaceIndexAny,
558 1.3 tsarna name,
559 1.3 tsarna rrtype,
560 1.3 tsarna kDNSServiceClass_IN,
561 1.3 tsarna _mdns_hostent_cb,
562 1.4 christos ctx
563 1.3 tsarna );
564 1.3 tsarna
565 1.3 tsarna retry = retry_query(sr, err);
566 1.3 tsarna }
567 1.1 tsarna
568 1.1 tsarna if (err) {
569 1.4 christos *info->he = NETDB_INTERNAL;
570 1.1 tsarna return NS_UNAVAIL;
571 1.1 tsarna }
572 1.3 tsarna
573 1.4 christos _mdns_eventloop(*sr, (void *)ctx, timeout);
574 1.1 tsarna
575 1.1 tsarna DNSServiceRefDeallocate(sdRef);
576 1.1 tsarna
577 1.4 christos if (ctx->naddrs) {
578 1.1 tsarna return NS_SUCCESS;
579 1.1 tsarna } else {
580 1.4 christos *info->he = HOST_NOT_FOUND;
581 1.1 tsarna return NS_NOTFOUND;
582 1.1 tsarna }
583 1.1 tsarna }
584 1.1 tsarna
585 1.1 tsarna
586 1.1 tsarna
587 1.1 tsarna static void
588 1.1 tsarna _mdns_addrinfo_init(addrinfo_ctx *ctx, const struct addrinfo *ai)
589 1.1 tsarna {
590 1.1 tsarna ctx->cb_ctx.done = false;
591 1.1 tsarna ctx->start = *ai;
592 1.1 tsarna ctx->start.ai_next = NULL;
593 1.1 tsarna ctx->start.ai_canonname = NULL;
594 1.1 tsarna ctx->last = &(ctx->start);
595 1.1 tsarna }
596 1.1 tsarna
597 1.1 tsarna
598 1.1 tsarna
599 1.1 tsarna static void
600 1.1 tsarna _mdns_addrinfo_add_ai(addrinfo_ctx *ctx, struct addrinfo *ai)
601 1.1 tsarna {
602 1.1 tsarna ctx->last->ai_next = ai;
603 1.1 tsarna while (ctx->last->ai_next)
604 1.1 tsarna ctx->last = ctx->last->ai_next;
605 1.1 tsarna }
606 1.1 tsarna
607 1.1 tsarna
608 1.1 tsarna
609 1.1 tsarna static struct addrinfo *
610 1.1 tsarna _mdns_addrinfo_done(addrinfo_ctx *ctx)
611 1.1 tsarna {
612 1.1 tsarna struct addrinfo head, *t, *p;
613 1.1 tsarna
614 1.1 tsarna /* sort v6 up */
615 1.1 tsarna
616 1.1 tsarna t = &head;
617 1.1 tsarna p = ctx->start.ai_next;
618 1.1 tsarna
619 1.1 tsarna while (p->ai_next) {
620 1.1 tsarna if (p->ai_next->ai_family == AF_INET6) {
621 1.1 tsarna t->ai_next = p->ai_next;
622 1.1 tsarna t = t->ai_next;
623 1.1 tsarna p->ai_next = p->ai_next->ai_next;
624 1.1 tsarna } else {
625 1.1 tsarna p = p->ai_next;
626 1.1 tsarna }
627 1.1 tsarna }
628 1.1 tsarna
629 1.1 tsarna /* add rest of list and reset start to the new list */
630 1.1 tsarna
631 1.1 tsarna t->ai_next = ctx->start.ai_next;
632 1.1 tsarna ctx->start.ai_next = head.ai_next;
633 1.1 tsarna
634 1.1 tsarna return ctx->start.ai_next;
635 1.1 tsarna }
636 1.1 tsarna
637 1.1 tsarna
638 1.1 tsarna
639 1.1 tsarna static void
640 1.1 tsarna _mdns_hostent_init(hostent_ctx *ctx, int af, int addrlen)
641 1.1 tsarna {
642 1.1 tsarna int i;
643 1.1 tsarna
644 1.1 tsarna ctx->cb_ctx.done = false;
645 1.1 tsarna ctx->naliases = ctx->naddrs = 0;
646 1.1 tsarna ctx->next = ctx->buf;
647 1.1 tsarna
648 1.1 tsarna ctx->host.h_aliases = ctx->host_aliases;
649 1.1 tsarna ctx->host.h_addr_list = ctx->h_addr_ptrs;
650 1.1 tsarna ctx->host.h_name = ctx->host.h_aliases[0] = NULL;
651 1.1 tsarna ctx->host.h_addrtype = af;
652 1.1 tsarna ctx->host.h_length = addrlen;
653 1.1 tsarna
654 1.1 tsarna for (i = 0; i < MAXADDRS; i++) {
655 1.1 tsarna ctx->host.h_addr_list[i] = &(ctx->addrs[i * 16]);
656 1.1 tsarna }
657 1.1 tsarna }
658 1.1 tsarna
659 1.1 tsarna
660 1.1 tsarna
661 1.1 tsarna static void
662 1.1 tsarna _mdns_hostent_add_host(hostent_ctx *ctx, const char *name)
663 1.1 tsarna {
664 1.1 tsarna size_t len;
665 1.1 tsarna int i;
666 1.1 tsarna
667 1.1 tsarna if (name && (len = strlen(name))
668 1.1 tsarna && (HCTX_BUFLEFT(ctx) > len) && (ctx->naliases < MAXALIASES)) {
669 1.1 tsarna if (len && (name[len - 1] == '.')) {
670 1.1 tsarna len--;
671 1.1 tsarna }
672 1.1 tsarna
673 1.1 tsarna /* skip dupe names */
674 1.1 tsarna
675 1.1 tsarna if ((ctx->host.h_name) && !strncmp(ctx->host.h_name, name, len)
676 1.1 tsarna && (strlen(ctx->host.h_name) == len)) {
677 1.1 tsarna return;
678 1.1 tsarna }
679 1.1 tsarna
680 1.1 tsarna for (i = 0; i < ctx->naliases - 1; i++) {
681 1.1 tsarna if (!strncmp(ctx->host.h_aliases[i], name, len)
682 1.1 tsarna && (strlen(ctx->host.h_aliases[i]) == len)) {
683 1.1 tsarna return;
684 1.1 tsarna }
685 1.1 tsarna }
686 1.1 tsarna
687 1.1 tsarna strncpy(ctx->next, name, len);
688 1.1 tsarna ctx->next[len] = 0;
689 1.1 tsarna
690 1.1 tsarna if (ctx->naliases == 0) {
691 1.1 tsarna ctx->host.h_name = ctx->next;
692 1.1 tsarna } else {
693 1.1 tsarna ctx->host.h_aliases[ctx->naliases - 1] = ctx->next;
694 1.1 tsarna }
695 1.1 tsarna
696 1.1 tsarna ctx->next += (len + 1);
697 1.1 tsarna ctx->naliases++;
698 1.1 tsarna } /* else silently ignore */
699 1.1 tsarna }
700 1.1 tsarna
701 1.1 tsarna
702 1.1 tsarna
703 1.1 tsarna static void
704 1.1 tsarna _mdns_hostent_add_addr(hostent_ctx *ctx, const void *addr, uint16_t len)
705 1.1 tsarna {
706 1.1 tsarna if ((len == ctx->host.h_length) && (ctx->naddrs < MAXADDRS)) {
707 1.1 tsarna memcpy(ctx->host.h_addr_list[ctx->naddrs++], addr, (size_t)len);
708 1.1 tsarna } /* else wrong address type or out of room... silently skip */
709 1.1 tsarna }
710 1.1 tsarna
711 1.1 tsarna
712 1.1 tsarna
713 1.4 christos static int
714 1.4 christos _mdns_hostent_done(struct getnamaddr *info, hostent_ctx *ctx)
715 1.1 tsarna {
716 1.4 christos int i;
717 1.4 christos char *ptr = info->buf;
718 1.4 christos size_t len = info->buflen;
719 1.4 christos struct hostent *hp = info->hp;
720 1.4 christos struct hostent *chp = &ctx->host;
721 1.4 christos
722 1.4 christos hp->h_length = ctx->host.h_length;
723 1.4 christos hp->h_addrtype = ctx->host.h_addrtype;
724 1.4 christos HENT_ARRAY(hp->h_addr_list, ctx->naddrs, ptr, len);
725 1.4 christos HENT_ARRAY(hp->h_aliases, ctx->naliases - 1, ptr, len);
726 1.4 christos
727 1.4 christos for (i = 0; i < ctx->naddrs; i++)
728 1.4 christos HENT_COPY(hp->h_addr_list[i], chp->h_addr_list[i],
729 1.4 christos hp->h_length, ptr, len);
730 1.4 christos
731 1.4 christos hp->h_addr_list[ctx->naddrs] = NULL;
732 1.4 christos
733 1.4 christos HENT_SCOPY(hp->h_name, chp->h_name, ptr, len);
734 1.4 christos
735 1.4 christos for (i = 0; i < ctx->naliases - 1; i++)
736 1.4 christos HENT_SCOPY(hp->h_aliases[i], chp->h_aliases[i], ptr, len);
737 1.4 christos hp->h_aliases[ctx->naliases - 1] = NULL;
738 1.4 christos *info->he = 0;
739 1.4 christos return NS_SUCCESS;
740 1.4 christos nospc:
741 1.4 christos *info->he = NETDB_INTERNAL;
742 1.4 christos errno = ENOSPC;
743 1.4 christos return NS_UNAVAIL;
744 1.1 tsarna }
745 1.1 tsarna
746 1.1 tsarna
747 1.1 tsarna
748 1.1 tsarna static void
749 1.1 tsarna _mdns_addrinfo_cb(
750 1.1 tsarna DNSServiceRef sdRef,
751 1.1 tsarna DNSServiceFlags flags,
752 1.1 tsarna uint32_t interfaceIndex,
753 1.1 tsarna DNSServiceErrorType errorCode,
754 1.1 tsarna const char *hostname,
755 1.1 tsarna const struct sockaddr *address,
756 1.1 tsarna uint32_t ttl,
757 1.1 tsarna void *context
758 1.1 tsarna ) {
759 1.1 tsarna addrinfo_ctx *ctx = context;
760 1.1 tsarna struct addrinfo *ai;
761 1.1 tsarna
762 1.1 tsarna UNUSED(sdRef);
763 1.1 tsarna UNUSED(interfaceIndex);
764 1.1 tsarna UNUSED(ttl);
765 1.1 tsarna
766 1.1 tsarna if (errorCode == kDNSServiceErr_NoError) {
767 1.1 tsarna if (! (flags & kDNSServiceFlagsMoreComing)) {
768 1.1 tsarna ctx->cb_ctx.done = true;
769 1.1 tsarna }
770 1.1 tsarna
771 1.1 tsarna ai = allocaddrinfo((socklen_t)(address->sa_len));
772 1.1 tsarna if (ai) {
773 1.1 tsarna ai->ai_flags = ctx->start.ai_flags;
774 1.1 tsarna ai->ai_family = address->sa_family;
775 1.1 tsarna ai->ai_socktype = ctx->start.ai_socktype;
776 1.1 tsarna ai->ai_protocol = ctx->start.ai_protocol;
777 1.1 tsarna memcpy(ai->ai_addr, address, (size_t)(address->sa_len));
778 1.1 tsarna
779 1.1 tsarna if ((ctx->start.ai_flags & AI_CANONNAME) && hostname) {
780 1.1 tsarna ai->ai_canonname = strdup(hostname);
781 1.1 tsarna if (ai->ai_canonname[strlen(ai->ai_canonname) - 1] == '.') {
782 1.1 tsarna ai->ai_canonname[strlen(ai->ai_canonname) - 1] = '\0';
783 1.1 tsarna }
784 1.1 tsarna }
785 1.1 tsarna
786 1.1 tsarna _mdns_addrinfo_add_ai(ctx, ai);
787 1.1 tsarna }
788 1.1 tsarna }
789 1.1 tsarna }
790 1.1 tsarna
791 1.1 tsarna
792 1.1 tsarna
793 1.1 tsarna static void
794 1.1 tsarna _mdns_hostent_cb(
795 1.1 tsarna DNSServiceRef sdRef,
796 1.1 tsarna DNSServiceFlags flags,
797 1.1 tsarna uint32_t interfaceIndex,
798 1.1 tsarna DNSServiceErrorType errorCode,
799 1.1 tsarna const char *fullname,
800 1.1 tsarna uint16_t rrtype,
801 1.1 tsarna uint16_t rrclass,
802 1.1 tsarna uint16_t rdlen,
803 1.1 tsarna const void *rdata,
804 1.1 tsarna uint32_t ttl,
805 1.1 tsarna void *context
806 1.1 tsarna ) {
807 1.1 tsarna hostent_ctx *ctx = (hostent_ctx *)context;
808 1.1 tsarna char buf[NS_MAXDNAME+1];
809 1.1 tsarna
810 1.1 tsarna UNUSED(sdRef);
811 1.1 tsarna UNUSED(interfaceIndex);
812 1.1 tsarna UNUSED(rrclass);
813 1.1 tsarna UNUSED(ttl);
814 1.1 tsarna
815 1.1 tsarna if (! (flags & kDNSServiceFlagsMoreComing)) {
816 1.1 tsarna ctx->cb_ctx.done = true;
817 1.1 tsarna }
818 1.1 tsarna
819 1.1 tsarna if (errorCode == kDNSServiceErr_NoError) {
820 1.1 tsarna switch (rrtype) {
821 1.1 tsarna case kDNSServiceType_PTR:
822 1.1 tsarna if (!_mdns_rdata2name(rdata, rdlen, buf, sizeof(buf))) {
823 1.1 tsarna /* corrupt response -- skip */
824 1.1 tsarna return;
825 1.1 tsarna }
826 1.1 tsarna
827 1.1 tsarna _mdns_hostent_add_host(ctx, buf);
828 1.1 tsarna break;
829 1.1 tsarna
830 1.1 tsarna case kDNSServiceType_A:
831 1.1 tsarna if (ctx->host.h_addrtype == AF_INET) {
832 1.1 tsarna _mdns_hostent_add_host(ctx, fullname);
833 1.1 tsarna _mdns_hostent_add_addr(ctx, rdata, rdlen);
834 1.1 tsarna }
835 1.1 tsarna break;
836 1.1 tsarna
837 1.1 tsarna case kDNSServiceType_AAAA:
838 1.1 tsarna if (ctx->host.h_addrtype == AF_INET6) {
839 1.1 tsarna _mdns_hostent_add_host(ctx, fullname);
840 1.1 tsarna _mdns_hostent_add_addr(ctx, rdata, rdlen);
841 1.1 tsarna }
842 1.1 tsarna break;
843 1.1 tsarna }
844 1.2 tsarna } else if (errorCode == kDNSServiceErr_NoSuchRecord) {
845 1.2 tsarna ctx->cb_ctx.done = true;
846 1.1 tsarna }
847 1.1 tsarna }
848 1.1 tsarna
849 1.1 tsarna
850 1.1 tsarna
851 1.1 tsarna static void
852 1.3 tsarna _mdns_eventloop(svc_ref *sr, callback_ctx *ctx, short timeout)
853 1.1 tsarna {
854 1.1 tsarna struct pollfd fds;
855 1.1 tsarna int fd, ret;
856 1.1 tsarna
857 1.3 tsarna fd = DNSServiceRefSockFD(sr->sdRef);
858 1.1 tsarna fds.fd = fd;
859 1.1 tsarna fds.events = POLLRDNORM;
860 1.1 tsarna
861 1.1 tsarna while (!ctx->done) {
862 1.3 tsarna ret = poll(&fds, 1, timeout * 1000);
863 1.1 tsarna if (ret > 0) {
864 1.3 tsarna DNSServiceProcessResult(sr->sdRef);
865 1.1 tsarna } else {
866 1.1 tsarna break;
867 1.1 tsarna }
868 1.1 tsarna }
869 1.1 tsarna }
870 1.1 tsarna
871 1.1 tsarna
872 1.1 tsarna
873 1.1 tsarna static char *
874 1.1 tsarna _mdns_rdata2name(const unsigned char *rdata, uint16_t rdlen, char *buf, size_t buflen)
875 1.1 tsarna {
876 1.1 tsarna unsigned char l;
877 1.1 tsarna char *r = buf;
878 1.1 tsarna
879 1.1 tsarna /* illegal 0-size answer or not enough room for even "." */
880 1.1 tsarna if ((!rdlen) || (rdlen < 2)) {
881 1.1 tsarna return NULL;
882 1.1 tsarna }
883 1.1 tsarna
884 1.1 tsarna buflen--; /* reserve space for terminating NUL now */
885 1.1 tsarna
886 1.1 tsarna /* special case empty as "." */
887 1.1 tsarna if ((rdlen == 1) && (!*rdata)) {
888 1.1 tsarna strcpy(buf, ".");
889 1.1 tsarna
890 1.1 tsarna return r;
891 1.1 tsarna }
892 1.1 tsarna
893 1.1 tsarna while (rdlen && *rdata) {
894 1.1 tsarna /* label length byte */
895 1.1 tsarna l = *rdata++; rdlen--;
896 1.1 tsarna
897 1.1 tsarna if (l > 63) {
898 1.1 tsarna /* compression or bitstrings -- shouldn't happen */
899 1.1 tsarna return NULL;
900 1.1 tsarna } else if (l > buflen) {
901 1.1 tsarna /* not enough space */
902 1.1 tsarna return NULL;
903 1.1 tsarna } else if (l > rdlen) {
904 1.1 tsarna /* label shouldn't be longer than remaining rdata */
905 1.1 tsarna return NULL;
906 1.1 tsarna } else if (!l) {
907 1.1 tsarna /* empty label -- should be done */
908 1.1 tsarna if (rdlen) {
909 1.1 tsarna /* but more left!? */
910 1.1 tsarna return NULL;
911 1.1 tsarna } else {
912 1.1 tsarna break;
913 1.1 tsarna }
914 1.1 tsarna }
915 1.1 tsarna
916 1.1 tsarna memcpy(buf, rdata, (size_t)l);
917 1.1 tsarna rdata += l; buf += l;
918 1.1 tsarna rdlen -= l; buflen -= l;
919 1.1 tsarna
920 1.1 tsarna /* Another label to come? add a separator */
921 1.1 tsarna if (rdlen && *rdata) {
922 1.1 tsarna if (!buflen) {
923 1.1 tsarna return NULL;
924 1.1 tsarna }
925 1.1 tsarna
926 1.1 tsarna *buf++ = '.'; buflen--;
927 1.1 tsarna }
928 1.1 tsarna }
929 1.1 tsarna
930 1.1 tsarna /* we reserved space above, so we know we have space
931 1.1 tsarna to add this termination */
932 1.1 tsarna
933 1.1 tsarna *buf = '\0';
934 1.1 tsarna
935 1.1 tsarna return r;
936 1.1 tsarna }
937 1.1 tsarna
938 1.1 tsarna
939 1.1 tsarna
940 1.3 tsarna int
941 1.3 tsarna search_init(search_iter *iter, const char *name, const char **first)
942 1.1 tsarna {
943 1.2 tsarna const char *c = name, *cmp;
944 1.2 tsarna int dots = 0, enddot = 0;
945 1.2 tsarna size_t len, cl;
946 1.3 tsarna
947 1.3 tsarna iter->conf = get_res_conf();
948 1.3 tsarna if (!iter->conf) {
949 1.3 tsarna h_errno = NETDB_INTERNAL;
950 1.3 tsarna return NS_UNAVAIL;
951 1.3 tsarna }
952 1.1 tsarna
953 1.1 tsarna iter->name = name;
954 1.1 tsarna iter->baselen = 0;
955 1.2 tsarna iter->abs_first = iter->abs_last = false;
956 1.3 tsarna iter->next_search = iter->conf->search_domains;
957 1.1 tsarna
958 1.1 tsarna while (*c) {
959 1.1 tsarna if (*c == '.') {
960 1.1 tsarna dots++;
961 1.2 tsarna enddot = 1;
962 1.1 tsarna } else {
963 1.2 tsarna enddot = 0;
964 1.1 tsarna }
965 1.1 tsarna c++;
966 1.1 tsarna }
967 1.1 tsarna
968 1.2 tsarna if (svc_flags & kDNSServiceFlagsForceMulticast) {
969 1.2 tsarna if (dots) {
970 1.3 tsarna iter->next_search = iter->conf->no_search;
971 1.2 tsarna if ((dots - enddot) == 1) {
972 1.2 tsarna len = strlen(iter->name);
973 1.2 tsarna cl = strlen(".local") + enddot;
974 1.2 tsarna if (len > cl) {
975 1.2 tsarna cmp = enddot ? ".local." : ".local";
976 1.2 tsarna c = iter->name + len - cl;
977 1.2 tsarna
978 1.2 tsarna if (!strcasecmp(c, cmp)) {
979 1.2 tsarna iter->abs_first = true;
980 1.2 tsarna }
981 1.2 tsarna }
982 1.2 tsarna }
983 1.2 tsarna }
984 1.1 tsarna } else {
985 1.3 tsarna if (dots >= iter->conf->ndots) {
986 1.2 tsarna iter->abs_first = true;
987 1.2 tsarna } else {
988 1.2 tsarna iter->abs_last = true;
989 1.2 tsarna }
990 1.1 tsarna
991 1.2 tsarna if (enddot) {
992 1.2 tsarna /* absolute; don't search */
993 1.3 tsarna iter->next_search = iter->conf->no_search;
994 1.2 tsarna }
995 1.1 tsarna }
996 1.3 tsarna
997 1.3 tsarna *first = search_next(iter);
998 1.3 tsarna if (!first) {
999 1.3 tsarna search_done(iter);
1000 1.3 tsarna h_errno = HOST_NOT_FOUND;
1001 1.3 tsarna return NS_NOTFOUND;
1002 1.3 tsarna }
1003 1.3 tsarna
1004 1.3 tsarna return NS_SUCCESS;
1005 1.1 tsarna }
1006 1.1 tsarna
1007 1.1 tsarna
1008 1.1 tsarna
1009 1.1 tsarna const char *
1010 1.1 tsarna search_next(search_iter *iter)
1011 1.1 tsarna {
1012 1.1 tsarna const char *a = NULL;
1013 1.1 tsarna res_state res;
1014 1.1 tsarna size_t len;
1015 1.1 tsarna
1016 1.1 tsarna if (iter->abs_first) {
1017 1.1 tsarna iter->abs_first = false;
1018 1.1 tsarna return iter->name;
1019 1.1 tsarna }
1020 1.1 tsarna
1021 1.2 tsarna while (*(iter->next_search)) {
1022 1.2 tsarna if (!iter->baselen) {
1023 1.2 tsarna iter->baselen = strlcpy(iter->buf, iter->name, sizeof(iter->buf));
1024 1.2 tsarna if (iter->baselen >= sizeof(iter->buf) - 1) {
1025 1.2 tsarna /* original is too long, don't try any search domains */
1026 1.3 tsarna iter->next_search = iter->conf->no_search;
1027 1.2 tsarna break;
1028 1.1 tsarna }
1029 1.2 tsarna
1030 1.2 tsarna iter->buf[iter->baselen++] = '.';
1031 1.2 tsarna }
1032 1.2 tsarna
1033 1.2 tsarna len = strlcpy(&(iter->buf[iter->baselen]),
1034 1.2 tsarna *(iter->next_search),
1035 1.2 tsarna sizeof(iter->buf) - iter->baselen);
1036 1.2 tsarna
1037 1.2 tsarna iter->next_search++;
1038 1.2 tsarna
1039 1.2 tsarna if (len >= sizeof(iter->buf) - iter->baselen) {
1040 1.2 tsarna /* result was too long */
1041 1.2 tsarna continue;
1042 1.1 tsarna }
1043 1.2 tsarna
1044 1.2 tsarna return iter->buf;
1045 1.1 tsarna }
1046 1.1 tsarna
1047 1.1 tsarna if (iter->abs_last) {
1048 1.1 tsarna iter->abs_last = false;
1049 1.1 tsarna return iter->name;
1050 1.1 tsarna }
1051 1.1 tsarna
1052 1.1 tsarna return NULL;
1053 1.1 tsarna }
1054 1.2 tsarna
1055 1.2 tsarna
1056 1.2 tsarna
1057 1.3 tsarna void
1058 1.3 tsarna search_done(search_iter *iter)
1059 1.3 tsarna {
1060 1.3 tsarna if (iter->conf) {
1061 1.3 tsarna put_res_conf(iter->conf);
1062 1.3 tsarna iter->conf = NULL;
1063 1.3 tsarna }
1064 1.3 tsarna }
1065 1.3 tsarna
1066 1.3 tsarna
1067 1.3 tsarna
1068 1.2 tsarna /*
1069 1.2 tsarna * Is domain appropriate to be in the domain search list?
1070 1.2 tsarna * For mdnsd, take everything. For multicast_dns, only "local"
1071 1.2 tsarna * if present.
1072 1.2 tsarna */
1073 1.2 tsarna bool
1074 1.2 tsarna searchable_domain(char *d)
1075 1.2 tsarna {
1076 1.2 tsarna if (!(svc_flags & kDNSServiceFlagsForceMulticast)) {
1077 1.2 tsarna return true;
1078 1.2 tsarna }
1079 1.2 tsarna
1080 1.2 tsarna if (!strcasecmp(d, "local") || !strcasecmp(d, "local.")) {
1081 1.2 tsarna return true;
1082 1.2 tsarna }
1083 1.2 tsarna
1084 1.2 tsarna return false;
1085 1.2 tsarna }
1086 1.3 tsarna
1087 1.3 tsarna
1088 1.3 tsarna
1089 1.3 tsarna static void
1090 1.3 tsarna destroy_svc_ref(svc_ref *sr)
1091 1.3 tsarna {
1092 1.3 tsarna /* assumes not on conn list */
1093 1.3 tsarna
1094 1.3 tsarna if (sr) {
1095 1.3 tsarna DNSServiceRefDeallocate(sr->sdRef);
1096 1.3 tsarna free(sr);
1097 1.3 tsarna }
1098 1.3 tsarna }
1099 1.3 tsarna
1100 1.3 tsarna
1101 1.3 tsarna
1102 1.3 tsarna static svc_ref *
1103 1.3 tsarna get_svc_ref(void)
1104 1.3 tsarna {
1105 1.3 tsarna svc_ref *sr;
1106 1.3 tsarna
1107 1.3 tsarna LOCK(&conn_list_lock);
1108 1.3 tsarna
1109 1.3 tsarna if (getpid() != my_pid) {
1110 1.3 tsarna my_pid = getpid();
1111 1.3 tsarna
1112 1.3 tsarna /*
1113 1.3 tsarna * We forked and kept running. We don't want to share
1114 1.3 tsarna * connections with the parent or we'll garble each others
1115 1.3 tsarna * comms, so throw away the parent's list and start over
1116 1.3 tsarna */
1117 1.3 tsarna while ((sr = SLIST_FIRST(&conn_list))) {
1118 1.3 tsarna SLIST_REMOVE_HEAD(&conn_list, entries);
1119 1.3 tsarna destroy_svc_ref(sr);
1120 1.3 tsarna }
1121 1.3 tsarna
1122 1.3 tsarna sr = NULL;
1123 1.3 tsarna } else {
1124 1.3 tsarna /* try to recycle a connection */
1125 1.3 tsarna sr = SLIST_FIRST(&conn_list);
1126 1.3 tsarna if (sr) {
1127 1.3 tsarna SLIST_REMOVE_HEAD(&conn_list, entries);
1128 1.3 tsarna conn_count--;
1129 1.3 tsarna }
1130 1.3 tsarna }
1131 1.3 tsarna
1132 1.3 tsarna UNLOCK(&conn_list_lock);
1133 1.3 tsarna
1134 1.3 tsarna if (!sr) {
1135 1.3 tsarna /* none available, we need a new one */
1136 1.3 tsarna
1137 1.3 tsarna sr = calloc(sizeof(svc_ref), 1);
1138 1.3 tsarna if (sr) {
1139 1.3 tsarna if (DNSServiceCreateConnection(&(sr->sdRef))) {
1140 1.3 tsarna free(sr);
1141 1.3 tsarna return NULL;
1142 1.3 tsarna }
1143 1.3 tsarna
1144 1.3 tsarna if (fcntl(DNSServiceRefSockFD(sr->sdRef), F_SETFD, FD_CLOEXEC) < 0) {
1145 1.3 tsarna destroy_svc_ref(sr);
1146 1.3 tsarna sr = NULL;
1147 1.3 tsarna }
1148 1.3 tsarna }
1149 1.3 tsarna }
1150 1.3 tsarna
1151 1.3 tsarna return sr;
1152 1.3 tsarna }
1153 1.3 tsarna
1154 1.3 tsarna
1155 1.3 tsarna
1156 1.3 tsarna static void
1157 1.3 tsarna put_svc_ref(svc_ref *sr)
1158 1.3 tsarna {
1159 1.3 tsarna if (sr) {
1160 1.3 tsarna LOCK(&conn_list_lock);
1161 1.3 tsarna
1162 1.3 tsarna sr->uses++;
1163 1.3 tsarna
1164 1.3 tsarna /* if slow start or aged out, destroy */
1165 1.3 tsarna if ((svc_puts++ < SLOWSTART_LOOKUPS)
1166 1.3 tsarna || (conn_count && (sr->uses > REUSE_TIMES))) {
1167 1.3 tsarna UNLOCK(&conn_list_lock);
1168 1.3 tsarna destroy_svc_ref(sr);
1169 1.3 tsarna return;
1170 1.3 tsarna }
1171 1.3 tsarna
1172 1.3 tsarna conn_count++;
1173 1.3 tsarna
1174 1.3 tsarna SLIST_INSERT_HEAD(&conn_list, sr, entries);
1175 1.3 tsarna
1176 1.3 tsarna UNLOCK(&conn_list_lock);
1177 1.3 tsarna }
1178 1.3 tsarna }
1179 1.3 tsarna
1180 1.3 tsarna
1181 1.3 tsarna
1182 1.3 tsarna /*
1183 1.3 tsarna * determine if this is a call we should retry with a fresh
1184 1.3 tsarna * connection, for example if mdnsd went away and came back.
1185 1.3 tsarna */
1186 1.3 tsarna static bool
1187 1.3 tsarna retry_query(svc_ref **sr, DNSServiceErrorType err)
1188 1.3 tsarna {
1189 1.3 tsarna if ((err == kDNSServiceErr_Unknown)
1190 1.3 tsarna || (err == kDNSServiceErr_ServiceNotRunning)) {
1191 1.3 tsarna /* these errors might indicate a stale socket */
1192 1.3 tsarna if ((*sr)->uses) {
1193 1.3 tsarna /* this was an old socket, so kill it and get another */
1194 1.3 tsarna destroy_svc_ref(*sr);
1195 1.3 tsarna *sr = get_svc_ref();
1196 1.3 tsarna if (*sr) {
1197 1.3 tsarna /* we can retry */
1198 1.3 tsarna return true;
1199 1.3 tsarna }
1200 1.3 tsarna }
1201 1.3 tsarna }
1202 1.3 tsarna
1203 1.3 tsarna return false;
1204 1.3 tsarna }
1205 1.3 tsarna
1206 1.3 tsarna
1207 1.3 tsarna
1208 1.3 tsarna static void
1209 1.3 tsarna decref_res_conf(res_conf *rc)
1210 1.3 tsarna {
1211 1.3 tsarna rc->refcount--;
1212 1.3 tsarna
1213 1.3 tsarna if (rc->refcount < 1) {
1214 1.3 tsarna if ((rc->no_search = rc->search_domains)) {
1215 1.3 tsarna for (; *(rc->no_search); rc->no_search++) {
1216 1.3 tsarna free(*(rc->no_search));
1217 1.3 tsarna }
1218 1.3 tsarna
1219 1.3 tsarna free(rc->search_domains);
1220 1.3 tsarna }
1221 1.3 tsarna
1222 1.3 tsarna free(rc);
1223 1.3 tsarna }
1224 1.3 tsarna }
1225 1.3 tsarna
1226 1.3 tsarna
1227 1.3 tsarna
1228 1.3 tsarna res_conf *
1229 1.3 tsarna get_res_conf(void)
1230 1.3 tsarna {
1231 1.3 tsarna struct timespec last_change;
1232 1.3 tsarna res_state res;
1233 1.3 tsarna res_conf *rc;
1234 1.3 tsarna
1235 1.3 tsarna LOCK(&res_conf_lock);
1236 1.3 tsarna
1237 1.3 tsarna /* check if resolver config changed */
1238 1.3 tsarna
1239 1.3 tsarna res = __res_get_state();
1240 1.3 tsarna if (res) {
1241 1.3 tsarna res_check(res, &last_change);
1242 1.3 tsarna if (timespeccmp(&last_config, &last_change, <)) {
1243 1.3 tsarna if (cur_res_conf) {
1244 1.3 tsarna decref_res_conf(cur_res_conf);
1245 1.3 tsarna }
1246 1.3 tsarna cur_res_conf = new_res_conf(res);
1247 1.3 tsarna if (cur_res_conf) {
1248 1.3 tsarna last_config = last_change;
1249 1.3 tsarna }
1250 1.3 tsarna }
1251 1.3 tsarna __res_put_state(res);
1252 1.3 tsarna }
1253 1.3 tsarna
1254 1.3 tsarna rc = cur_res_conf;
1255 1.3 tsarna if (rc) {
1256 1.3 tsarna rc->refcount++;
1257 1.3 tsarna }
1258 1.3 tsarna
1259 1.3 tsarna UNLOCK(&res_conf_lock);
1260 1.3 tsarna
1261 1.3 tsarna return rc;
1262 1.3 tsarna }
1263 1.3 tsarna
1264 1.3 tsarna
1265 1.3 tsarna
1266 1.3 tsarna static void
1267 1.3 tsarna put_res_conf(res_conf *rc)
1268 1.3 tsarna {
1269 1.3 tsarna LOCK(&res_conf_lock);
1270 1.3 tsarna
1271 1.3 tsarna decref_res_conf(rc);
1272 1.3 tsarna
1273 1.3 tsarna UNLOCK(&res_conf_lock);
1274 1.3 tsarna }
1275 1.3 tsarna
1276 1.3 tsarna
1277 1.3 tsarna
1278 1.3 tsarna static res_conf *
1279 1.3 tsarna new_res_conf(res_state res)
1280 1.3 tsarna {
1281 1.3 tsarna res_conf *rc;
1282 1.3 tsarna int count = 0;
1283 1.3 tsarna char **sd, *p;
1284 1.3 tsarna
1285 1.3 tsarna rc = calloc(sizeof(res_conf), 1);
1286 1.3 tsarna if (rc) {
1287 1.3 tsarna rc->refcount = 1;
1288 1.3 tsarna
1289 1.3 tsarna sd = res->dnsrch;
1290 1.3 tsarna while (*sd) {
1291 1.3 tsarna if (searchable_domain(*sd)) {
1292 1.3 tsarna count++;
1293 1.3 tsarna }
1294 1.3 tsarna sd++;
1295 1.3 tsarna }
1296 1.3 tsarna
1297 1.3 tsarna rc->search_domains = calloc(sizeof(char *), count + 1);
1298 1.3 tsarna if (!(rc->search_domains)) {
1299 1.3 tsarna decref_res_conf(rc);
1300 1.3 tsarna return NULL;
1301 1.3 tsarna }
1302 1.3 tsarna
1303 1.3 tsarna sd = res->dnsrch;
1304 1.3 tsarna rc->no_search = rc->search_domains;
1305 1.3 tsarna while (*sd) {
1306 1.3 tsarna if (searchable_domain(*sd)) {
1307 1.3 tsarna *(rc->no_search) = p = strdup(*sd);
1308 1.3 tsarna if (!p) {
1309 1.3 tsarna decref_res_conf(rc);
1310 1.3 tsarna return NULL;
1311 1.3 tsarna }
1312 1.3 tsarna
1313 1.3 tsarna rc->no_search++;
1314 1.3 tsarna }
1315 1.3 tsarna sd++;
1316 1.3 tsarna }
1317 1.3 tsarna
1318 1.3 tsarna rc->timeout = res->retrans;
1319 1.3 tsarna
1320 1.3 tsarna if (svc_flags & kDNSServiceFlagsForceMulticast) {
1321 1.3 tsarna rc->ndots = 1;
1322 1.3 tsarna if (rc->timeout > 2) {
1323 1.3 tsarna rc->timeout = 2;
1324 1.3 tsarna }
1325 1.3 tsarna } else {
1326 1.3 tsarna rc->ndots = res->ndots;
1327 1.3 tsarna }
1328 1.3 tsarna }
1329 1.3 tsarna
1330 1.3 tsarna return rc;
1331 1.3 tsarna }
1332 1.3 tsarna
1333 1.3 tsarna
1334 1.3 tsarna
1335 1.3 tsarna static short
1336 1.3 tsarna get_timeout(void)
1337 1.3 tsarna {
1338 1.3 tsarna short timeout = 5;
1339 1.3 tsarna res_conf *rc;
1340 1.3 tsarna
1341 1.3 tsarna rc = get_res_conf();
1342 1.3 tsarna if (rc) {
1343 1.3 tsarna timeout = rc->timeout;
1344 1.3 tsarna put_res_conf(rc);
1345 1.3 tsarna }
1346 1.3 tsarna
1347 1.3 tsarna return timeout;
1348 1.3 tsarna }
1349