1/*
2 * Copyright (c) 1999-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include "dns.h"
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <unistd.h>
29#include <time.h>
30#include <netdb.h>
31#include <stdarg.h>
32#include <sys/stat.h>
33#include <sys/dir.h>
34#include <errno.h>
35#include <ifaddrs.h>
36#include <net/if.h>
37#include <pthread.h>
38#include <netinet/in.h>
39#include <arpa/nameser.h>
40#include <resolv.h>
41#include <fcntl.h>
42#include <notify.h>
43#include <dnsinfo.h>
44#include "dns_private.h"
45#include "res_private.h"
46
47#define INET_NTOP_AF_INET_OFFSET 4
48#define INET_NTOP_AF_INET6_OFFSET 8
49
50#define SEARCH_COUNT_INIT -1
51
52#define DNS_RESOLVER_DIR "/etc/resolver"
53
54#define NOTIFY_DNS_CONTROL_NAME "com.apple.system.dns"
55#define DNS_CONTROL_FLAG_DEBUG    0x0000000000000001LL
56#define DNS_CONTROL_FLAG_NO_MDNS  0x0000000000000002LL
57
58#define NOTIFY_DIR_NAME "com.apple.system.dns.resolver.dir"
59#define DNS_DELAY_NAME "com.apple.system.dns.delay"
60
61#define DNS_DELAY_INTERVAL 4
62
63#define DNS_PRIVATE_HANDLE_TYPE_SUPER 0
64#define DNS_PRIVATE_HANDLE_TYPE_PLAIN 1
65
66#define MDNS_MIN_TTL 2
67
68static int dns_control_token = -1;
69static int dns_control_mdns = 1;
70static int dns_control_debug = 0;
71static pthread_mutex_t dns_control_lock = PTHREAD_MUTEX_INITIALIZER;
72
73extern uint32_t notify_monitor_file(int token, const char *name, int flags);
74
75extern void res_client_close(res_state res);
76extern res_state res_state_new();
77extern int res_nquery_soa_min(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen, int *min);
78extern int res_nsearch_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, uint32_t *fromlen);
79extern int __res_nsearch_list_2(res_state statp, const char *name,	int class, int type,  u_char *answer, int anslen, struct sockaddr *from, uint32_t *fromlen, int nsearch, char **search);
80
81extern char *res_next_word(char **p);
82extern res_state res_build_start(res_state res);
83extern int res_build(res_state res, uint16_t port, uint32_t *nsrch, char *key, char *val);
84extern int res_build_sortlist(res_state res, struct in_addr addr, struct in_addr mask);
85
86static void
87_pdns_set_name(pdns_handle_t *pdns, const char *name)
88{
89	int n;
90
91	if (pdns == NULL) return;
92	if (name == NULL) return;
93
94	/* only set the name once */
95	if (pdns->name != NULL) return;
96
97	/* strip trailing dots */
98	n = strlen(name) - 1;
99	while ((n >= 0) && (name[n] == '.')) n--;
100
101	if (n < 0) return;
102
103	n++;
104	pdns->name = calloc(n + 1, sizeof(char));
105	if (pdns->name == NULL) return;
106	memcpy(pdns->name, name, n);
107}
108
109static pdns_handle_t *
110_pdns_build_start(char *name)
111{
112	pdns_handle_t *pdns;
113
114	pdns = (pdns_handle_t *)calloc(1, sizeof(pdns_handle_t));
115	if (pdns == NULL) return NULL;
116
117	pdns->res = res_build_start(NULL);
118	if (pdns->res == NULL)
119	{
120		free(pdns);
121		return NULL;
122	}
123
124	_pdns_set_name(pdns, name);
125	pdns->port = NS_DEFAULTPORT;
126
127	return pdns;
128}
129
130static int
131_pdns_build_finish(pdns_handle_t *pdns)
132{
133	uint32_t n;
134
135	if (pdns == NULL) return -1;
136
137	n = pdns->res->nscount;
138	if (n == 0) n = 1;
139
140	if (pdns->total_timeout == 0)
141	{
142		if (pdns->send_timeout == 0) pdns->total_timeout = RES_MAXRETRANS;
143		else pdns->total_timeout = pdns->send_timeout * pdns->res->retry * n;
144	}
145
146	if (pdns->total_timeout == 0) pdns->res->retrans = RES_MAXRETRANS;
147	else pdns->res->retrans = pdns->total_timeout;
148
149	pdns->res->options |= RES_INIT;
150
151	return 0;
152}
153
154static int
155_pdns_build_sortlist(pdns_handle_t *pdns, struct in_addr addr, struct in_addr mask)
156{
157	if (pdns == NULL) return -1;
158	return res_build_sortlist(pdns->res, addr, mask);
159}
160
161static void
162_pdns_free(pdns_handle_t *pdns)
163{
164	int i;
165
166	if (pdns == NULL) return;
167
168	if ((pdns->search_count != SEARCH_COUNT_INIT) && (pdns->search_count > 0) && (pdns->search_list != NULL))
169	{
170		for (i = 0; i < pdns->search_count; i++) free(pdns->search_list[i]);
171		free(pdns->search_list);
172	}
173
174	if (pdns->name != NULL) free(pdns->name);
175	if (pdns->res != NULL) res_client_close(pdns->res);
176
177	free(pdns);
178}
179
180static int
181_pdns_build(pdns_handle_t *pdns, char *key, char *val)
182{
183	struct in6_addr addr6;
184	int32_t status;
185	char *dupval;
186
187	if (pdns == NULL) return -1;
188
189	/*
190	 * Detect IPv6 server addresses.
191	 */
192	if (((pdns->flags & DNS_FLAG_HAVE_IPV6_SERVER) == 0) && (!strcmp(key, "nameserver")))
193	{
194		memset(&addr6, 0, sizeof(struct in6_addr));
195		status = inet_pton(AF_INET6, val, &addr6);
196		if (status == 1) pdns->flags |= DNS_FLAG_HAVE_IPV6_SERVER;
197	}
198
199	/*
200	 * We handle some keys here.
201	 * Other keys get passed on to res_build.
202	 */
203	if (!strcmp(key, "default"))
204	{
205		pdns->flags |= DNS_FLAG_DEFAULT_RESOLVER;
206		return 0;
207	}
208
209	if (!strcmp(key, "port"))
210	{
211		pdns->port = atoi(val);
212		return 0;
213	}
214
215	if (!strcmp(key, "search"))
216	{
217		dupval = strdup(val);
218		if (dupval == NULL) return -1;
219
220		if (pdns->search_count == SEARCH_COUNT_INIT) pdns->search_count = 0;
221		if (pdns->search_count == 0)
222		{
223			pdns->search_list = (char **)calloc(1, sizeof(char *));
224		}
225		else
226		{
227			pdns->search_list = (char **)reallocf(pdns->search_list, (pdns->search_count + 1) * sizeof(char *));
228		}
229
230		if (pdns->search_list == NULL)
231		{
232			free(dupval);
233			_pdns_free(pdns);
234			return -1;
235		}
236
237		pdns->search_list[pdns->search_count] = dupval;
238		pdns->search_count++;
239		return 0;
240	}
241
242	if (!strcmp(key, "total_timeout"))
243	{
244		pdns->total_timeout = atoi(val);
245		return 0;
246	}
247
248	if (!strcmp(key, "timeout"))
249	{
250		pdns->send_timeout = atoi(val);
251		return 0;
252	}
253
254	if (!strcmp(key, "search_order"))
255	{
256		pdns->search_order = atoi(val);
257		return 0;
258	}
259
260	if (!strcmp(key, "pdns"))
261	{
262		pdns->flags |= DNS_FLAG_FORWARD_TO_MDNSRESPONDER;
263		return 0;
264	}
265
266	if (!strcmp(key, "mdns"))
267	{
268		pdns->flags |= DNS_FLAG_FORWARD_TO_MDNSRESPONDER;
269		return 0;
270	}
271
272	/* pass on to res_build */
273	return res_build(pdns->res, pdns->port, &(pdns->search_count), key, val);
274}
275
276static pdns_handle_t *
277_pdns_convert_sc(dns_resolver_t *r)
278{
279	pdns_handle_t *pdns;
280	char *val, *p, *x;
281	int i;
282
283	pdns = _pdns_build_start(r->domain);
284	if (r->domain == NULL) _pdns_build(pdns, "default", NULL);
285
286	p = getenv("RES_RETRY_TIMEOUT");
287	if (p != NULL) pdns->send_timeout = atoi(p);
288
289	p = getenv("RES_RETRY");
290	if (p != NULL) pdns->res->retry= atoi(p);
291
292	if (r->port != 0)
293	{
294		val = NULL;
295		asprintf(&val, "%hu", r->port);
296		if (val == NULL)
297		{
298			_pdns_free(pdns);
299			return NULL;
300		}
301
302		_pdns_build(pdns, "port", val);
303		free(val);
304	}
305
306	if (r->n_nameserver > MAXNS) r->n_nameserver = MAXNS;
307	for (i = 0; i < r->n_nameserver; i++)
308	{
309		if (r->nameserver[i]->sa_family == AF_INET)
310		{
311			val = calloc(1, 256);
312			if (val == NULL)
313			{
314				_pdns_free(pdns);
315				return NULL;
316			}
317
318			inet_ntop(AF_INET, (char *)(r->nameserver[i]) + INET_NTOP_AF_INET_OFFSET, val, 256);
319			_pdns_build(pdns, "nameserver", val);
320			free(val);
321		}
322		else if (r->nameserver[i]->sa_family == AF_INET6)
323		{
324			pdns->flags |= DNS_FLAG_HAVE_IPV6_SERVER;
325			val = calloc(1, 256);
326			if (val == NULL)
327			{
328				_pdns_free(pdns);
329				return NULL;
330			}
331
332			inet_ntop(AF_INET6, (char *)(r->nameserver[i]) + INET_NTOP_AF_INET6_OFFSET, val, 256);
333			_pdns_build(pdns, "nameserver", val);
334			free(val);
335		}
336	}
337
338	if (r->n_search > MAXDNSRCH) r->n_search = MAXDNSRCH;
339	for (i = 0; i < r->n_search; i++)
340	{
341		val = NULL;
342		asprintf(&val, "%s", r->search[i]);
343		if (val == NULL)
344		{
345			_pdns_free(pdns);
346			return NULL;
347		}
348
349		_pdns_build(pdns, "search", val);
350		free(val);
351	}
352
353	if (r->timeout > 0)
354	{
355		val = NULL;
356		asprintf(&val, "%d", r->timeout);
357		if (val == NULL)
358		{
359			_pdns_free(pdns);
360			return NULL;
361		}
362
363		_pdns_build(pdns, "total_timeout", val);
364		free(val);
365	}
366
367	val = NULL;
368	asprintf(&val, "%d", r->search_order);
369	if (val == NULL)
370	{
371		_pdns_free(pdns);
372		return NULL;
373	}
374
375	_pdns_build(pdns, "search_order", val);
376	free(val);
377
378	if (r->n_sortaddr > MAXRESOLVSORT) r->n_sortaddr = MAXRESOLVSORT;
379	for (i = 0; i < r->n_sortaddr; i++)
380	{
381		_pdns_build_sortlist(pdns, r->sortaddr[i]->address, r->sortaddr[i]->mask);
382	}
383
384	p = r->options;
385	while (NULL != (x = res_next_word(&p)))
386	{
387		/* search for and process individual options */
388		if (!strncmp(x, "ndots:", 6))
389		{
390			_pdns_build(pdns, "ndots", x+6);
391		}
392
393		else if (!strncmp(x, "nibble:", 7))
394		{
395			_pdns_build(pdns, "nibble", x+7);
396		}
397
398		else if (!strncmp(x, "nibble2:", 8))
399		{
400			_pdns_build(pdns, "nibble2", x+8);
401		}
402
403		else if (!strncmp(x, "timeout:", 8))
404		{
405			_pdns_build(pdns, "timeout", x+8);
406		}
407
408		else if (!strncmp(x, "attempts:", 9))
409		{
410			_pdns_build(pdns, "attempts", x+9);
411		}
412
413		else if (!strncmp(x, "bitstring:", 10))
414		{
415			_pdns_build(pdns, "bitstring", x+10);
416		}
417
418		else if (!strncmp(x, "v6revmode:", 10))
419		{
420			_pdns_build(pdns, "v6revmode", x+10);
421		}
422
423		else if (!strcmp(x, "debug"))
424		{
425			_pdns_build(pdns, "debug", NULL);
426		}
427
428		else if (!strcmp(x, "no_tld_query"))
429		{
430			_pdns_build(pdns, "no_tld_query", NULL);
431		}
432
433		else if (!strcmp(x, "inet6"))
434		{
435			_pdns_build(pdns, "inet6", NULL);
436		}
437
438		else if (!strcmp(x, "rotate"))
439		{
440			_pdns_build(pdns, "rotate", NULL);
441		}
442
443		else if (!strcmp(x, "no-check-names"))
444		{
445			_pdns_build(pdns, "no-check-names", NULL);
446		}
447
448#ifdef RES_USE_EDNS0
449		else if (!strcmp(x, "edns0"))
450		{
451			_pdns_build(pdns, "edns0", NULL);
452		}
453#endif
454		else if (!strcmp(x, "a6"))
455		{
456			_pdns_build(pdns, "a6", NULL);
457		}
458
459		else if (!strcmp(x, "dname"))
460		{
461			_pdns_build(pdns, "dname", NULL);
462		}
463
464		else if (!strcmp(x, "default"))
465		{
466			_pdns_build(pdns, "default", NULL);
467		}
468
469		else if (!strcmp(x, "pdns"))
470		{
471			_pdns_build(pdns, "pdns", NULL);
472		}
473
474		else if (!strcmp(x, "mdns"))
475		{
476			_pdns_build(pdns, "mdns", NULL);
477		}
478	}
479
480	_pdns_build_finish(pdns);
481	return pdns;
482}
483
484static pdns_handle_t *
485_mdns_primary(dns_resolver_t *r)
486{
487	pdns_handle_t *pdns;
488	char *val, *p;
489	int i;
490
491	pdns = _pdns_build_start(r->domain);
492	if (r->domain == NULL) _pdns_build(pdns, "default", NULL);
493
494	p = getenv("RES_RETRY_TIMEOUT");
495	if (p != NULL) pdns->send_timeout = atoi(p);
496
497	p = getenv("RES_RETRY");
498	if (p != NULL) pdns->res->retry= atoi(p);
499
500	if (r->n_search > MAXDNSRCH) r->n_search = MAXDNSRCH;
501	for (i = 0; i < r->n_search; i++)
502	{
503		val = NULL;
504		asprintf(&val, "%s", r->search[i]);
505		if (val == NULL)
506		{
507			_pdns_free(pdns);
508			return NULL;
509		}
510
511		_pdns_build(pdns, "search", val);
512		free(val);
513	}
514
515	_pdns_build(pdns, "mdns", NULL);
516
517	_pdns_build_finish(pdns);
518	return pdns;
519}
520
521/*
522 * Open a named resolver client from the system config data.
523 */
524static pdns_handle_t *
525_pdns_sc_open(const char *name)
526{
527	pdns_handle_t *pdns;
528	int i;
529	dns_config_t *sc_dns;
530	dns_resolver_t *sc_res;
531
532	sc_dns = dns_configuration_copy();
533	if (sc_dns == NULL) return NULL;
534
535	sc_res = NULL;
536
537	if (name == NULL)
538	{
539		if (sc_dns->n_resolver != 0) sc_res = sc_dns->resolver[0];
540	}
541	else
542	{
543		for (i = 0; (sc_res == NULL) && (i < sc_dns->n_resolver); i++)
544		{
545			if (sc_dns->resolver[i] == NULL) continue;
546			if (sc_dns->resolver[i]->domain == NULL) continue;
547			if (!strcasecmp(name, sc_dns->resolver[i]->domain)) sc_res = sc_dns->resolver[i];
548		}
549	}
550
551	if (sc_res == NULL)
552	{
553		dns_configuration_free(sc_dns);
554		return NULL;
555	}
556
557	pdns = (pdns_handle_t *)calloc(1, sizeof(pdns_handle_t));
558	if (pdns == NULL)
559	{
560		dns_configuration_free(sc_dns);
561		return NULL;
562	}
563
564	pdns = _pdns_convert_sc(sc_res);
565
566	dns_configuration_free(sc_dns);
567
568	if (pdns == NULL) return NULL;
569
570	if (pdns->res == NULL)
571	{
572		free(pdns);
573		return NULL;
574	}
575
576	pdns->name = NULL;
577	if (pdns->res->defdname[0] != '\0') _pdns_set_name(pdns, pdns->res->defdname);
578	else if (name != NULL) _pdns_set_name(pdns, name);
579
580	if (name != NULL) pdns->search_count = SEARCH_COUNT_INIT;
581
582	return pdns;
583}
584
585/*
586 * Open a named resolver client from file.
587 */
588static pdns_handle_t *
589_pdns_file_open(const char *name)
590{
591	pdns_handle_t *pdns;
592	char *path, buf[1024];
593	char *p, *x, *y;
594	FILE *fp;
595
596	path = NULL;
597	if (name == NULL)
598	{
599		asprintf(&path, "%s", _PATH_RESCONF);
600	}
601	else if ((name[0] == '.') || (name[0] == '/'))
602	{
603		asprintf(&path, "%s", name);
604	}
605	else
606	{
607		asprintf(&path, "%s/%s", DNS_RESOLVER_DIR, name);
608	}
609
610	if (path == NULL) return NULL;
611
612	fp = fopen(path, "r");
613	free(path);
614	if (fp == NULL) return NULL;
615
616	pdns = _pdns_build_start(NULL);
617	if (pdns == NULL)
618	{
619		fclose(fp);
620		return NULL;
621	}
622
623	p = getenv("RES_RETRY_TIMEOUT");
624	if (p != NULL) pdns->send_timeout = atoi(p);
625
626	p = getenv("RES_RETRY");
627	if (p != NULL) pdns->res->retry= atoi(p);
628
629	while (fgets(buf, sizeof(buf), fp) != NULL)
630	{
631		/* skip comments */
632		if ((buf[0] == ';') || (buf[0] == '#')) continue;
633		p = buf;
634		x = res_next_word(&p);
635		if (x == NULL) continue;
636		if (!strcmp(x, "sortlist"))
637		{
638			while (NULL != (x = res_next_word(&p)))
639			{
640				_pdns_build(pdns, "sortlist", x);
641			}
642		}
643		else if (!strcmp(x, "timeout"))
644		{
645			x = res_next_word(&p);
646			if (x != NULL) _pdns_build(pdns, "total_timeout", x);
647		}
648		else if (!strcmp(x, "options"))
649		{
650			while (NULL != (x = res_next_word(&p)))
651			{
652				y = strchr(x, ':');
653				if (y != NULL)
654				{
655					*y = '\0';
656					y++;
657				}
658				_pdns_build(pdns, x, y);
659			}
660		}
661		else
662		{
663			y = res_next_word(&p);
664			_pdns_build(pdns, x, y);
665
666			if ((!strcmp(x, "domain")) && (pdns->name == NULL)) _pdns_set_name(pdns, y);
667		}
668	}
669
670	fclose(fp);
671
672	if (pdns->name == NULL) _pdns_set_name(pdns, name);
673
674	_pdns_build_finish(pdns);
675
676	return pdns;
677}
678
679/*
680 * If there was no search list, use domain name and parent domain components.
681 *
682 * N.B. This code deals with multiple trailing dots, but does not deal with
683 * multiple internal dots, e.g. "foo.....com".
684 */
685static void
686_pdns_check_search_list(pdns_handle_t *pdns)
687{
688	int n;
689	char *p;
690
691	if (pdns == NULL) return;
692	if (pdns->name == NULL) return;
693	if (pdns->search_count > 0) return;
694
695	/* Count dots */
696	n = 0;
697	for (p = pdns->name; *p != '\0'; p++)
698	{
699		if (*p == '.') n++;
700	}
701
702	/* Back up over any trailing dots and cut them out of the name */
703	for (p--; (p >= pdns->name) && (*p == '.'); p--)
704	{
705		*p = '\0';
706		n--;
707	}
708
709	/* This will be true if name was all dots */
710	if (p < pdns->name) return;
711
712	/* dots are separators, so number of components is one larger */
713	n++;
714
715	_pdns_build(pdns, "search", pdns->name);
716
717	/* Include parent domains with at least LOCALDOMAINPARTS components */
718	p = pdns->name;
719	while (n > LOCALDOMAINPARTS)
720	{
721		/* Find next component */
722		while ((*p != '.') && (*p != '\0')) p++;
723		if (*p == '\0') break;
724		p++;
725
726		n--;
727		_pdns_build(pdns, "search", p);
728	}
729}
730
731__private_extern__ void
732_check_cache(sdns_handle_t *sdns)
733{
734	int i, n, status, refresh, sc_dns_count;
735	DIR *dp;
736	struct direct *d;
737	pdns_handle_t *c;
738	dns_config_t *sc_dns;
739
740	if (sdns == NULL) return;
741
742	refresh = 0;
743
744	if (sdns->stattime == 0) refresh = 1;
745
746	if (refresh == 0)
747	{
748		if (sdns->notify_sys_config_token == -1) refresh = 1;
749		else
750		{
751			n = 1;
752			status = notify_check(sdns->notify_sys_config_token, &n);
753			if ((status != NOTIFY_STATUS_OK) || (n == 1)) refresh = 1;
754		}
755	}
756
757	if (refresh == 0)
758	{
759		if (sdns->notify_dir_token == -1) refresh = 1;
760		else
761		{
762			n = 1;
763			status = notify_check(sdns->notify_dir_token, &n);
764			if ((status != NOTIFY_STATUS_OK) || (n == 1)) refresh = 1;
765		}
766	}
767
768	if (refresh == 0) return;
769
770	/* Free old clients */
771	sdns->pdns_primary = NULL;
772
773	for (i = 0; i < sdns->client_count; i++)
774	{
775		_pdns_free(sdns->client[i]);
776	}
777
778	sdns->client_count = 0;
779	if (sdns->client != NULL) free(sdns->client);
780	sdns->client = NULL;
781
782	/* Fetch clients from System Configuration */
783	sc_dns = dns_configuration_copy();
784
785	/* Set up Primary resolver.  It's the one we consult for a search list */
786	sc_dns_count = 0;
787	if ((sc_dns != NULL) && (sc_dns->n_resolver > 0))
788	{
789		if (sdns->flags & DNS_FLAG_FORWARD_TO_MDNSRESPONDER)
790		{
791			sc_dns_count = 1;
792			sdns->pdns_primary = _mdns_primary(sc_dns->resolver[0]);
793		}
794		else
795		{
796			sc_dns_count = sc_dns->n_resolver;
797			sdns->pdns_primary = _pdns_convert_sc(sc_dns->resolver[0]);
798		}
799
800		_pdns_check_search_list(sdns->pdns_primary);
801	}
802	else
803	{
804		sdns->pdns_primary = _pdns_file_open(_PATH_RESCONF);
805	}
806
807	if (sdns->pdns_primary != NULL)
808	{
809		if ((sdns->flags & DNS_FLAG_DEBUG) && (sdns->pdns_primary->res != NULL)) sdns->pdns_primary->res->options |= RES_DEBUG;
810		if (sdns->flags & DNS_FLAG_OK_TO_SKIP_AAAA) sdns->pdns_primary->flags |= DNS_FLAG_OK_TO_SKIP_AAAA;
811		sdns->pdns_primary->flags |= DNS_FLAG_DEFAULT_RESOLVER;
812
813		sdns->client = (pdns_handle_t **)calloc(1, sizeof(pdns_handle_t *));
814		if (sdns->client == NULL)
815		{
816			if (sc_dns != NULL) dns_configuration_free(sc_dns);
817			return;
818		}
819
820		sdns->client[sdns->client_count] = sdns->pdns_primary;
821		sdns->client_count++;
822	}
823
824	/* Convert System Configuration resolvers */
825	for (i = 1; i < sc_dns_count; i++)
826	{
827		c = _pdns_convert_sc(sc_dns->resolver[i]);
828		if (c == NULL) continue;
829
830		if (sdns->flags & DNS_FLAG_DEBUG) c->res->options |= RES_DEBUG;
831		if (sdns->flags & DNS_FLAG_OK_TO_SKIP_AAAA) c->flags |= DNS_FLAG_OK_TO_SKIP_AAAA;
832
833		if (sdns->client_count == 0)
834		{
835			sdns->client = (pdns_handle_t **)calloc(1, sizeof(pdns_handle_t *));
836		}
837		else
838		{
839			sdns->client = (pdns_handle_t **)reallocf(sdns->client, (sdns->client_count + 1) * sizeof(pdns_handle_t *));
840		}
841
842		if (sdns->client == NULL)
843		{
844			sdns->client_count = 0;
845			dns_configuration_free(sc_dns);
846			return;
847		}
848
849		sdns->client[sdns->client_count] = c;
850		sdns->client_count++;
851	}
852
853	if (sc_dns != NULL) dns_configuration_free(sc_dns);
854
855	if (sdns->flags & DNS_FLAG_CHECK_RESOLVER_DIR)
856	{
857		/* Read /etc/resolvers clients */
858		dp = opendir(DNS_RESOLVER_DIR);
859		if (dp == NULL)
860		{
861			sdns->flags &= ~DNS_FLAG_CHECK_RESOLVER_DIR;
862		}
863		else
864		{
865			while (NULL != (d = readdir(dp)))
866			{
867				if (d->d_name[0] == '.') continue;
868
869				c = _pdns_file_open(d->d_name);
870				if (c == NULL) continue;
871				if (sdns->flags & DNS_FLAG_DEBUG) c->res->options |= RES_DEBUG;
872				if (sdns->flags & DNS_FLAG_OK_TO_SKIP_AAAA) c->flags |= DNS_FLAG_OK_TO_SKIP_AAAA;
873
874				if (sdns->client_count == 0)
875				{
876					sdns->client = (pdns_handle_t **)calloc(1, sizeof(pdns_handle_t *));
877				}
878				else
879				{
880					sdns->client = (pdns_handle_t **)reallocf(sdns->client, (sdns->client_count + 1) * sizeof(pdns_handle_t *));
881				}
882
883				if (sdns->client == NULL)
884				{
885					sdns->client_count = 0;
886					return;
887				}
888
889				sdns->client[sdns->client_count] = c;
890				sdns->client_count++;
891			}
892			closedir(dp);
893		}
894	}
895
896	sdns->stattime = 1;
897}
898
899static uint32_t
900_pdns_get_default_handles(sdns_handle_t *sdns, pdns_handle_t ***pdns)
901{
902	int i, j, k, count;
903
904	if (sdns == NULL) return 0;
905	if (pdns == NULL) return 0;
906
907	count = 0;
908
909	for (i = 0; i < sdns->client_count; i++)
910	{
911		if (sdns->client[i]->flags & DNS_FLAG_DEFAULT_RESOLVER)
912		{
913			if (count == 0)
914			{
915				*pdns = (pdns_handle_t **)calloc(1, sizeof(pdns_handle_t *));
916			}
917			else
918			{
919				*pdns = (pdns_handle_t **)reallocf((*pdns), (count + 1) * sizeof(pdns_handle_t *));
920			}
921
922			if (*pdns == NULL) return 0;
923
924			/* Insert sorted by search_order */
925			for (j = 0; j < count; j++)
926			{
927				if (sdns->client[i]->search_order < (*pdns)[j]->search_order) break;
928			}
929
930			for (k = count; k > j; k--) (*pdns)[k] = (*pdns)[k-1];
931			(*pdns)[j] = sdns->client[i];
932			count++;
933		}
934	}
935
936	return count;
937}
938
939static uint32_t
940_pdns_get_handles_for_name(sdns_handle_t *sdns, const char *name, pdns_handle_t ***pdns)
941{
942	char *p, *vname;
943	int i, j, k, count;
944
945	if (sdns == NULL) return 0;
946	if (pdns == NULL) return 0;
947
948	if (name == NULL) return _pdns_get_default_handles(sdns, pdns);
949	else if (name[0] == '\0') return _pdns_get_default_handles(sdns, pdns);
950
951	count = 0;
952
953	vname = strdup(name);
954	i = strlen(vname) - 1;
955	if ((i >= 0) && (vname[i] == '.')) vname[i] = '\0';
956
957	p = vname;
958	while (p != NULL)
959	{
960		for (i = 0; i < sdns->client_count; i++)
961		{
962			if (sdns->client[i]->name == NULL) continue;
963
964			if (!strcasecmp(sdns->client[i]->name, p))
965			{
966				if (count == 0)
967				{
968					*pdns = (pdns_handle_t **)calloc(1, sizeof(pdns_handle_t *));
969				}
970				else
971				{
972					*pdns = (pdns_handle_t **)reallocf((*pdns), (count + 1) * sizeof(pdns_handle_t *));
973				}
974
975				if (*pdns == NULL) return 0;
976
977				/* Insert sorted by search_order */
978				for (j = 0; j < count; j++)
979				{
980					if (sdns->client[i]->search_order < (*pdns)[j]->search_order) break;
981				}
982
983				for (k = count; k > j; k--) (*pdns)[k] = (*pdns)[k-1];
984				(*pdns)[j] = sdns->client[i];
985				count++;
986			}
987		}
988
989		p = strchr(p, '.');
990		if (p != NULL) p++;
991	}
992
993	free(vname);
994
995	if (count != 0) return count;
996
997	return _pdns_get_default_handles(sdns, pdns);
998}
999
1000static void
1001_pdns_process_res_search_list(pdns_handle_t *pdns)
1002{
1003	if (pdns->search_count != SEARCH_COUNT_INIT) return;
1004	for (pdns->search_count = 0; (pdns->res->dnsrch[pdns->search_count] != NULL) && (pdns->res->dnsrch[pdns->search_count][0] != '\0'); pdns->search_count++);
1005}
1006
1007static char *
1008_pdns_search_list_domain(pdns_handle_t *pdns, uint32_t i)
1009{
1010	char *s;
1011
1012	if (pdns == NULL) return NULL;
1013	if (pdns->search_count == SEARCH_COUNT_INIT) _pdns_process_res_search_list(pdns);
1014	if (i >= pdns->search_count) return NULL;
1015
1016	s = pdns->search_list[i];
1017	if (s == NULL) return NULL;
1018	return strdup(s);
1019}
1020
1021static void
1022_dns_open_notify(sdns_handle_t *sdns)
1023{
1024	int status, n;
1025
1026	if (sdns == NULL) return;
1027
1028	if (sdns->notify_delay_token == -1)
1029	{
1030		status = notify_register_check(DNS_DELAY_NAME, &(sdns->notify_delay_token));
1031		if (status != NOTIFY_STATUS_OK) sdns->notify_delay_token = -1;
1032		else status = notify_check(sdns->notify_delay_token, &n);
1033	}
1034
1035	if (sdns->notify_sys_config_token == -1)
1036	{
1037		status = notify_register_check(dns_configuration_notify_key(), &(sdns->notify_sys_config_token));
1038		if (status != NOTIFY_STATUS_OK) sdns->notify_sys_config_token = -1;
1039	}
1040
1041	if (sdns->notify_dir_token == -1)
1042	{
1043		status = notify_register_check(NOTIFY_DIR_NAME, &(sdns->notify_dir_token));
1044		if (status == NOTIFY_STATUS_OK)
1045		{
1046			status = notify_monitor_file(sdns->notify_dir_token, "/private/etc/resolver", 0);
1047			if (status != NOTIFY_STATUS_OK)
1048			{
1049				notify_cancel(sdns->notify_dir_token);
1050				sdns->notify_dir_token = -1;
1051			}
1052		}
1053		else
1054		{
1055			sdns->notify_dir_token = -1;
1056		}
1057	}
1058}
1059
1060static void
1061_dns_close_notify(sdns_handle_t *sdns)
1062{
1063	if (sdns == NULL) return;
1064
1065	if (sdns->notify_delay_token != -1) notify_cancel(sdns->notify_delay_token);
1066	sdns->notify_delay_token = -1;
1067
1068	if (sdns->notify_sys_config_token != -1) notify_cancel(sdns->notify_sys_config_token);
1069	sdns->notify_sys_config_token = -1;
1070
1071	if (sdns->notify_dir_token != -1) notify_cancel(sdns->notify_dir_token);
1072	sdns->notify_dir_token = -1;
1073}
1074
1075dns_handle_t
1076dns_open(const char *name)
1077{
1078	dns_private_handle_t *dns;
1079	struct stat sb;
1080	int check, status, local_control;
1081	uint64_t control;
1082
1083	dns = (dns_private_handle_t *)calloc(1, sizeof(dns_private_handle_t));
1084	if (dns == NULL) return NULL;
1085
1086	/* set up control notification if necessary */
1087	if (dns_control_token == -1)
1088	{
1089		pthread_mutex_lock(&dns_control_lock);
1090		if (dns_control_token == -1) status = notify_register_check(NOTIFY_DNS_CONTROL_NAME, &dns_control_token);
1091		pthread_mutex_unlock(&dns_control_lock);
1092	}
1093
1094	/* check for dns flags */
1095	if (dns_control_token != -1)
1096	{
1097		pthread_mutex_lock(&dns_control_lock);
1098		status = notify_check(dns_control_token, &check);
1099		if ((status == 0) && (check == 1))
1100		{
1101			/* notification was triggered */
1102			status = notify_get_state(dns_control_token, &control);
1103			if (status == 0)
1104			{
1105				if (control & DNS_CONTROL_FLAG_NO_MDNS) dns_control_mdns = 0;
1106				if (control & DNS_CONTROL_FLAG_DEBUG) dns_control_debug = 1;
1107			}
1108		}
1109
1110		pthread_mutex_unlock(&dns_control_lock);
1111	}
1112
1113	if (name == NULL) local_control = dns_control_mdns;
1114	else if (!strcmp(name, MDNS_HANDLE_NAME)) local_control = 2;
1115	else local_control = 0;
1116
1117	if ((name == NULL) && (local_control == 0))
1118	{
1119		dns->handle_type = DNS_PRIVATE_HANDLE_TYPE_SUPER;
1120		dns->sdns = (sdns_handle_t *)calloc(1, sizeof(sdns_handle_t));
1121		if (dns->sdns == NULL)
1122		{
1123			free(dns);
1124			return NULL;
1125		}
1126
1127		dns->sdns->flags |= DNS_FLAG_CHECK_RESOLVER_DIR;
1128		dns->sdns->notify_sys_config_token = -1;
1129		dns->sdns->notify_dir_token = -1;
1130		dns->sdns->notify_delay_token = -1;
1131		_dns_open_notify(dns->sdns);
1132
1133		memset(&sb, 0, sizeof(struct stat));
1134		dns_set_debug((dns_handle_t)dns, dns_control_debug);
1135
1136		return (dns_handle_t)dns;
1137	}
1138
1139	if (local_control != 0)
1140	{
1141		dns->handle_type = DNS_PRIVATE_HANDLE_TYPE_SUPER;
1142		dns->sdns = (sdns_handle_t *)calloc(1, sizeof(sdns_handle_t));
1143		if (dns->sdns == NULL)
1144		{
1145			free(dns);
1146			return NULL;
1147		}
1148
1149		dns->sdns->flags = DNS_FLAG_FORWARD_TO_MDNSRESPONDER;
1150		dns->sdns->notify_sys_config_token = -1;
1151		dns->sdns->notify_dir_token = -1;
1152		dns->sdns->notify_delay_token = -1;
1153		if (local_control == 1) _dns_open_notify(dns->sdns);
1154
1155		memset(&sb, 0, sizeof(struct stat));
1156		dns_set_debug((dns_handle_t)dns, dns_control_debug);
1157
1158		return (dns_handle_t)dns;
1159	}
1160
1161	dns->handle_type = DNS_PRIVATE_HANDLE_TYPE_PLAIN;
1162
1163	/*  Look for name in System Configuration first */
1164	dns->pdns = _pdns_sc_open(name);
1165	if (dns->pdns == NULL) dns->pdns = _pdns_file_open(name);
1166
1167	if (dns->pdns == NULL)
1168	{
1169		free(dns);
1170		return NULL;
1171	}
1172
1173	dns_set_debug((dns_handle_t)dns, dns_control_debug);
1174	return (dns_handle_t)dns;
1175}
1176
1177/*
1178 * Release a DNS client handle
1179 */
1180void
1181dns_free(dns_handle_t d)
1182{
1183	dns_private_handle_t *dns;
1184	int i;
1185
1186	if (d == NULL) return;
1187
1188	dns = (dns_private_handle_t *)d;
1189
1190	if (dns->recvbuf != NULL) free(dns->recvbuf);
1191
1192	if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER)
1193	{
1194		if (dns->sdns == NULL) return;
1195
1196		_dns_close_notify(dns->sdns);
1197
1198		for (i = 0; i < dns->sdns->client_count; i++)
1199		{
1200			_pdns_free(dns->sdns->client[i]);
1201		}
1202
1203		dns->sdns->client_count = 0;
1204		if (dns->sdns->client != NULL) free(dns->sdns->client);
1205
1206		free(dns->sdns);
1207	}
1208	else
1209	{
1210		_pdns_free(dns->pdns);
1211	}
1212
1213	free(dns);
1214}
1215
1216static void
1217_pdns_debug(pdns_handle_t *pdns, uint32_t flag)
1218{
1219	if (pdns == NULL) return;
1220
1221	if (flag == 0)
1222	{
1223		pdns->res->options &= ~RES_DEBUG;
1224	}
1225	else
1226	{
1227		pdns->res->options |= RES_DEBUG;
1228	}
1229}
1230
1231static void
1232_sdns_debug(sdns_handle_t *sdns, uint32_t flag)
1233{
1234	int i;
1235
1236	if (sdns == NULL) return;
1237
1238	if (flag == 0)
1239	{
1240		sdns->flags &= ~ DNS_FLAG_DEBUG;
1241
1242		for (i = 0; i < sdns->client_count; i++)
1243		{
1244			sdns->client[i]->res->options &= ~RES_DEBUG;
1245		}
1246	}
1247	else
1248	{
1249		sdns->flags |= DNS_FLAG_DEBUG;
1250
1251		for (i = 0; i < sdns->client_count; i++)
1252		{
1253			sdns->client[i]->res->options |= RES_DEBUG;
1254		}
1255	}
1256}
1257
1258/*
1259 * Enable / Disable debugging
1260 */
1261void
1262dns_set_debug(dns_handle_t d, uint32_t flag)
1263{
1264	dns_private_handle_t *dns;
1265
1266	if (d == NULL) return;
1267
1268	dns = (dns_private_handle_t *)d;
1269
1270	if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER)
1271	{
1272		_sdns_debug(dns->sdns, flag);
1273	}
1274	else
1275	{
1276		_pdns_debug(dns->pdns, flag);
1277	}
1278}
1279
1280/*
1281 * Returns the number of names in the search list
1282 */
1283uint32_t
1284dns_search_list_count(dns_handle_t d)
1285{
1286	dns_private_handle_t *dns;
1287	pdns_handle_t *pdns;
1288
1289	if (d == NULL) return 0;
1290
1291	dns = (dns_private_handle_t *)d;
1292
1293	if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER)
1294	{
1295		_check_cache(dns->sdns);
1296		pdns = dns->sdns->pdns_primary;
1297	}
1298	else
1299	{
1300		pdns = dns->pdns;
1301	}
1302
1303	if (pdns->search_count == SEARCH_COUNT_INIT) _pdns_process_res_search_list(pdns);
1304	return pdns->search_count;
1305}
1306
1307/*
1308 * Returns the domain name at index i in the search list.
1309 * Returns NULL if there are no names in the search list.
1310 * Caller must free the returned name.
1311 */
1312char *
1313dns_search_list_domain(dns_handle_t d, uint32_t i)
1314{
1315	dns_private_handle_t *dns;
1316	pdns_handle_t *pdns;
1317
1318	if (d == NULL) return NULL;
1319
1320	dns = (dns_private_handle_t *)d;
1321
1322	if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER)
1323	{
1324		_check_cache(dns->sdns);
1325		pdns = dns->sdns->pdns_primary;
1326	}
1327	else
1328	{
1329		pdns = dns->pdns;
1330	}
1331
1332	return _pdns_search_list_domain(pdns, i);
1333}
1334
1335static int
1336_pdns_delay(sdns_handle_t *sdns)
1337{
1338	int status, n, snooze;
1339	time_t tick;
1340
1341	if (sdns == NULL) return 0;
1342
1343	snooze = 0;
1344	n = 0;
1345
1346	/* No delay if we are not receiving notifications */
1347	if (sdns->notify_delay_token == -1) return 0;
1348
1349	if (sdns->dns_delay == 0)
1350	{
1351		status = notify_check(sdns->notify_delay_token, &n);
1352		if ((status == NOTIFY_STATUS_OK) && (n == 1))
1353		{
1354			/*
1355			 * First thread to hit this condition sleeps for DNS_DELAY_INTERVAL seconds
1356			 */
1357			sdns->dns_delay = time(NULL) + DNS_DELAY_INTERVAL;
1358			snooze = DNS_DELAY_INTERVAL;
1359		}
1360	}
1361	else
1362	{
1363		tick = time(NULL);
1364		/*
1365		 * Subsequent threads sleep for the remaining duration.
1366		 * We add one to round up the interval since our granularity is coarse.
1367		 */
1368		snooze = 1 + (sdns->dns_delay - tick);
1369		if (snooze < 0) snooze = 0;
1370	}
1371
1372	if (snooze == 0) return 0;
1373
1374	sleep(snooze);
1375
1376	/* When exiting, first thread in resets the delay condition */
1377	if (n == 1) sdns->dns_delay = 0;
1378
1379	return 0;
1380}
1381
1382static int
1383_pdns_query(sdns_handle_t *sdns, pdns_handle_t *pdns, const char *name, uint32_t class, uint32_t type, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen, int *min)
1384{
1385	int n;
1386
1387	if (name == NULL) return -1;
1388	if (pdns == NULL) return -1;
1389
1390	if (pdns->flags & DNS_FLAG_FORWARD_TO_MDNSRESPONDER)
1391	{
1392		n = res_query_mDNSResponder(pdns->res, name, class, type, (u_char *)buf, len, from, fromlen);
1393		if ((n < 0) && (min != NULL)) *min = MDNS_MIN_TTL;
1394		return n;
1395	}
1396
1397	if (pdns->res == NULL) return -1;
1398	if (pdns->res->nscount == 0) return -1;
1399
1400	if ((type == ns_t_aaaa) && ((pdns->flags & DNS_FLAG_HAVE_IPV6_SERVER) == 0) && (pdns->flags & DNS_FLAG_OK_TO_SKIP_AAAA)) return -1;
1401
1402	_pdns_delay(sdns);
1403
1404	/* BIND_9 API */
1405	return res_nquery_soa_min(pdns->res, name, class, type, (u_char *)buf, len, from, (int32_t *)fromlen, min);
1406}
1407
1408__private_extern__ int
1409_pdns_search(sdns_handle_t *sdns, pdns_handle_t *pdns, const char *name, uint32_t class, uint32_t type, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen)
1410{
1411	char *dot, *qname;
1412	int append, status;
1413
1414	if (name == NULL) return -1;
1415	if (pdns == NULL) return -1;
1416	if (pdns->res == NULL) return -1;
1417	if (pdns->res->nscount == 0) return -1;
1418
1419	if ((type == ns_t_aaaa) && ((pdns->flags & DNS_FLAG_HAVE_IPV6_SERVER) == 0) && (pdns->flags & DNS_FLAG_OK_TO_SKIP_AAAA)) return -1;
1420
1421	qname = NULL;
1422	append = 1;
1423
1424	/*
1425	 * don't append my name if:
1426	 * - my name is NULL
1427	 * - input name is qualified (i.e. not single component)
1428	 * - there is a search list
1429	 * - there is a domain name
1430	 */
1431
1432	if (pdns->name == NULL) append = 0;
1433
1434	if (append == 1)
1435	{
1436		dot = strrchr(name, '.');
1437		if (dot != NULL) append = 0;
1438	}
1439
1440	if (append == 1)
1441	{
1442		if (pdns->search_count == SEARCH_COUNT_INIT) _pdns_process_res_search_list(pdns);
1443		if (pdns->search_count > 0) append = 0;
1444	}
1445
1446	if ((append == 1) && (pdns->res->defdname != NULL) && (pdns->res->defdname[0] != '\0')) append = 0;
1447
1448	status = -1;
1449	if (append == 0)
1450	{
1451		/* BIND_9 API */
1452		_pdns_delay(sdns);
1453
1454		status = __res_nsearch_list_2(pdns->res, name, class, type, (u_char *)buf, len, from, fromlen, pdns->search_count, pdns->search_list);
1455	}
1456	else
1457	{
1458		_pdns_delay(sdns);
1459
1460		qname = NULL;
1461		asprintf(&qname, "%s.%s.", name, pdns->name);
1462		if (qname == NULL) return -1;
1463
1464		/* BIND_9 API */
1465		status = res_nsearch_2(pdns->res, qname, class, type, (u_char *)buf, len, from, fromlen);
1466		free(qname);
1467	}
1468
1469	return status;
1470}
1471
1472static int
1473_sdns_send(sdns_handle_t *sdns, const char *name, uint32_t class, uint32_t type, uint32_t fqdn, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen, int *min)
1474{
1475	char *qname;
1476	pdns_handle_t **pdns;
1477	uint32_t pdns_count;
1478	int i, n;
1479	int m, tmin, minstate;
1480
1481	pdns = NULL;
1482	pdns_count = 0;
1483	n = -1;
1484	minstate = 0;
1485	*min = -1;
1486	m = -1;
1487
1488	pdns_count = _pdns_get_handles_for_name(sdns, name, &pdns);
1489
1490	if (pdns_count == 0) return -1;
1491
1492	qname = NULL;
1493	asprintf(&qname, "%s%s", name, (fqdn == 0) ? "." : "");
1494	if (qname == NULL) return -1;
1495
1496	for (i = 0; i < pdns_count; i++)
1497	{
1498		tmin = -1;
1499		n = _pdns_query(sdns, pdns[i], qname, class, type, buf, len, from, fromlen, &tmin);
1500		if (n <= 0)
1501		{
1502			if (tmin < 0)
1503			{
1504				minstate = -1;
1505			}
1506			else if (minstate == 0)
1507			{
1508				if (m == -1) m = tmin;
1509				else if (tmin < m) m = tmin;
1510			}
1511		}
1512
1513		if (n > 0) break;
1514	}
1515
1516	if (minstate == 0) *min = m;
1517
1518	free(pdns);
1519	free(qname);
1520	return n;
1521}
1522
1523__private_extern__ int
1524_sdns_search(sdns_handle_t *sdns, const char *name, uint32_t class, uint32_t type, uint32_t fqdn, uint32_t recurse, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen, int *min)
1525{
1526	pdns_handle_t *primary, **pdns;
1527	int i, n, ndots, status;
1528	int m, tmin, minstate;
1529	char *dot, *qname;
1530	uint32_t pdns_count;
1531
1532	if (sdns == NULL) return -1;
1533	if (name == NULL) return -1;
1534
1535	/*
1536	 * A minimum TTL derived from the minimim of all SOA records
1537	 * that are received with NXDOMAIN or no data is returned to
1538	 * the caller if every call returns an NXDOMAIN or no data
1539	 * and a SOA min ttl.  If any call times out or returns some
1540	 * other error, we return "-1" in the "min" out parameter.
1541	 * The minstate variable is set to -1 if we must return -1.
1542	 */
1543	minstate = 0;
1544	*min = -1;
1545
1546	/* m is the lowest of all minima.  -1 is unset */
1547	m = -1;
1548
1549	/* ndots is the threshold for trying a qualified name "as is" */
1550	ndots = 1;
1551	primary = sdns->pdns_primary;
1552	if ((primary != NULL) && (primary->res != NULL)) ndots = primary->res->ndots;
1553
1554	/* count dots in input name, and keep track of the location of the last dot */
1555	n = 0;
1556	dot = NULL;
1557
1558	for (i = 0; name[i] != '\0'; i++)
1559	{
1560		if (name[i] == '.')
1561		{
1562			n++;
1563			dot = (char *)(name + i);
1564		}
1565	}
1566
1567	/* the last dot is the last character, name is fully qualified */
1568	if ((fqdn == 0) && (dot != NULL) && (*(dot + 1) == '\0')) fqdn = 1;
1569
1570	/*
1571	 * If n >= ndots, or it's a FQDN, or if it's a PTR query,
1572	 * we try a query with the name "as is".
1573	 */
1574	if ((n >= ndots) || (fqdn == 1) || (type == ns_t_ptr))
1575	{
1576		tmin = -1;
1577		status = _sdns_send(sdns, name, class, type, fqdn, buf, len, from, fromlen, &tmin);
1578		if (status > 0) return status;
1579
1580		if (tmin < 0) minstate = -1;
1581		else m = tmin;
1582	}
1583
1584	/* end of the line for FQDNs or PTR queries */
1585	if ((fqdn == 1) || (type == ns_t_ptr) || (recurse == 0) || (primary == NULL))
1586	{
1587		if (minstate == 0) *min = m;
1588		return -1;
1589	}
1590
1591	/* Try appending names from the search list */
1592	if (primary->search_count == SEARCH_COUNT_INIT) _pdns_process_res_search_list(primary);
1593	n = primary->search_count;
1594	if (n > 0)
1595	{
1596		/* Try qualifying with each name in the search list */
1597		for (i = 0; i < n ; i++)
1598		{
1599			qname = NULL;
1600			asprintf(&qname, "%s.%s", name, primary->search_list[i]);
1601			if (qname == NULL) return -1;
1602
1603			tmin = -1;
1604			status = _sdns_search(sdns, qname, class, type, fqdn, 0, buf, len, from, fromlen, &tmin);
1605			if (status <= 0)
1606			{
1607				if (tmin < 0)
1608				{
1609					minstate = -1;
1610				}
1611				else if (minstate == 0)
1612				{
1613					if (m == -1) m = tmin;
1614					else if (tmin < m) m = tmin;
1615				}
1616			}
1617
1618			free(qname);
1619			if (status > 0) return status;
1620		}
1621
1622		if (minstate == 0) *min = m;
1623		return -1;
1624	}
1625
1626	/*
1627	 * We get here if the name is not fully qualified (no trailing dot), and there is no search list.
1628	 * Try each default client, qualifying with that client's name.
1629	 */
1630	pdns = NULL;
1631	pdns_count = _pdns_get_default_handles(sdns, &pdns);
1632	status = -1;
1633
1634	if (pdns_count == 0)
1635	{
1636		if (minstate == 0) *min = m;
1637		return -1;
1638	}
1639
1640	for (i = 0; i < pdns_count; i++)
1641	{
1642		qname = NULL;
1643		if (pdns[i]->name == NULL) asprintf(&qname, "%s", name);
1644		else asprintf(&qname, "%s.%s", name, pdns[i]->name);
1645
1646		/* leave *min at -1 in case of a malloc failure */
1647		if (qname == NULL) return -1;
1648
1649		tmin = -1;
1650		status = _pdns_query(sdns, pdns[i], qname, class, type, buf, len, from, fromlen, &tmin);
1651		if (status <= 0)
1652		{
1653			if (tmin < 0)
1654			{
1655				minstate = -1;
1656			}
1657			else if (minstate == 0)
1658			{
1659				if (m == -1) m = tmin;
1660				else if (tmin < m) m = tmin;
1661			}
1662		}
1663
1664		free(qname);
1665		if (status > 0) break;
1666	}
1667
1668	free(pdns);
1669
1670	if (minstate == 0) *min = m;
1671	return status;
1672}
1673
1674int
1675dns_query(dns_handle_t d, const char *name, uint32_t class, uint32_t type, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen)
1676{
1677	dns_private_handle_t *dns;
1678	int status, unused;
1679
1680	if (d == NULL) return -1;
1681	if (name == NULL) return -1;
1682	dns = (dns_private_handle_t *)d;
1683
1684	status = -1;
1685	unused = 0;
1686
1687	if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER)
1688	{
1689		_check_cache(dns->sdns);
1690		status = _sdns_search(dns->sdns, name, class, type, 1, 1, buf, len, from, fromlen, &unused);
1691	}
1692	else
1693	{
1694		status = _pdns_query(dns->sdns, dns->pdns, name, class, type, buf, len, from, fromlen, &unused);
1695	}
1696
1697	return status;
1698}
1699
1700
1701int
1702dns_search(dns_handle_t d, const char *name, uint32_t class, uint32_t type, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen)
1703{
1704	dns_private_handle_t *dns;
1705	int status, unused;
1706
1707	if (d == NULL) return -1;
1708	if (name == NULL) return -1;
1709	dns = (dns_private_handle_t *)d;
1710
1711	status = -1;
1712	unused = 0;
1713
1714	if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER)
1715	{
1716		_check_cache(dns->sdns);
1717		status = _sdns_search(dns->sdns, name, class, type, 0, 1, buf, len, from, fromlen, &unused);
1718	}
1719	else
1720	{
1721		status = _pdns_search(dns->sdns, dns->pdns, name, class, type, buf, len, from, fromlen);
1722	}
1723
1724	return status;
1725}
1726
1727/*
1728 * PRIVATE
1729 */
1730
1731uint32_t
1732dns_server_list_count(dns_handle_t d)
1733{
1734	dns_private_handle_t *dns;
1735	res_state r;
1736
1737	if (d == NULL) return 0;
1738	dns = (dns_private_handle_t *)d;
1739
1740	if (dns->handle_type != DNS_PRIVATE_HANDLE_TYPE_PLAIN) return 0;
1741
1742	if (dns->pdns == NULL) return 0;
1743
1744	r = dns->pdns->res;
1745	if (r == NULL) return 0;
1746
1747	return r->nscount;
1748}
1749
1750struct sockaddr *
1751dns_server_list_address(dns_handle_t d, uint32_t i)
1752{
1753	dns_private_handle_t *dns;
1754	res_state r;
1755	struct sockaddr_storage *s;
1756	struct sockaddr *sa;
1757
1758	if (d == NULL) return NULL;
1759	dns = (dns_private_handle_t *)d;
1760
1761	if (dns->handle_type != DNS_PRIVATE_HANDLE_TYPE_PLAIN) return NULL;
1762
1763	if (dns->pdns == NULL) return NULL;
1764
1765	r = dns->pdns->res;
1766	if (r == NULL) return NULL;
1767
1768	if (i >= r->nscount) return NULL;
1769	sa = get_nsaddr(r, i);
1770	if (sa == NULL) return NULL;
1771
1772	s = (struct sockaddr_storage *)calloc(1, sizeof(struct sockaddr_storage));
1773	if (s == NULL) return NULL;
1774
1775	memcpy(s, sa, sizeof(struct sockaddr_storage));
1776	return (struct sockaddr *)s;
1777}
1778