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