1/*
2 * Copyright (c) 2004-2014 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/*
25 * Modification History
26 *
27 * March 22, 2004	Allan Nathanson <ajn@apple.com>
28 * - initial revision
29 */
30
31#include <TargetConditionals.h>
32#include <fcntl.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <unistd.h>
36#include <sys/types.h>
37#include <sys/socket.h>
38#include <sys/stat.h>
39#include <net/if.h>
40#include <netinet/in.h>
41#include <arpa/inet.h>
42#include <arpa/nameser.h>
43#include <resolv.h>
44#if	!TARGET_OS_IPHONE
45#include <notify.h>
46extern uint32_t notify_monitor_file(int token, const char *name, int flags);
47#endif	// !TARGET_OS_IPHONE
48#include <CommonCrypto/CommonDigest.h>
49
50#include <CoreFoundation/CoreFoundation.h>
51#include <SystemConfiguration/SystemConfiguration.h>
52#include <SystemConfiguration/SCPrivate.h>
53#include <SystemConfiguration/SCValidation.h>
54#include "ip_plugin.h"
55
56#include "dns-configuration.h"
57
58#include <dnsinfo.h>
59#include "dnsinfo_create.h"
60#include "dnsinfo_server.h"
61
62#ifdef	MAIN
63#undef	MAIN
64#include "dnsinfo_copy.c"
65#include "dnsinfo_internal.h"
66#define	MAIN
67#define	DNS_CONFIGURATION_DEBUG
68#endif	// MAIN
69
70#include <dns_sd.h>
71#ifndef	kDNSServiceCompMulticastDNS
72#define	kDNSServiceCompMulticastDNS	"MulticastDNS"
73#endif
74#ifndef	kDNSServiceCompPrivateDNS
75#define	kDNSServiceCompPrivateDNS	"PrivateDNS"
76#endif
77
78#define DNS_CONFIGURATION_FLAGS_KEY	CFSTR("__FLAGS__")
79#define DNS_CONFIGURATION_IF_INDEX_KEY	CFSTR("__IF_INDEX__")
80#define DNS_CONFIGURATION_ORDER_KEY	CFSTR("__ORDER__")
81
82/* multicast DNS resolver configurations */
83static	CFNumberRef	S_mdns_timeout	= NULL;
84
85/* private DNS resolver configurations */
86static	CFNumberRef	S_pdns_timeout	= NULL;
87
88
89#pragma mark -
90#pragma mark DNS resolver flags
91
92
93
94
95static void
96add_dns_query_flags(const void *key, const void *value, void *context)
97{
98	CFDictionaryRef	service		= value;
99	uint32_t	*query_flags	= context;
100
101
102	// check if the service has v4 or v6 configured
103
104	if ((*query_flags & DNS_RESOLVER_FLAGS_REQUEST_A_RECORDS) == 0
105	    && service_contains_protocol(service, AF_INET)) {
106		*query_flags |= DNS_RESOLVER_FLAGS_REQUEST_A_RECORDS;
107	}
108	if ((*query_flags & DNS_RESOLVER_FLAGS_REQUEST_AAAA_RECORDS) == 0
109	    && service_contains_protocol(service, AF_INET6)) {
110		*query_flags |= DNS_RESOLVER_FLAGS_REQUEST_AAAA_RECORDS;
111	}
112
113	return;
114}
115
116
117#pragma mark -
118#pragma mark DNS resolver configuration
119
120
121static void
122add_resolver(CFMutableArrayRef resolvers, CFMutableDictionaryRef resolver)
123{
124	CFIndex		i;
125	CFStringRef	interface;
126	CFIndex		n_resolvers;
127	CFNumberRef	order;
128	uint32_t	order_val	= 0;
129
130	order = CFDictionaryGetValue(resolver, kSCPropNetDNSSearchOrder);
131	if (!isA_CFNumber(order) ||
132	    !CFNumberGetValue(order, kCFNumberSInt32Type, &order_val)) {
133		order     = NULL;
134		order_val = 0;
135	}
136
137	n_resolvers = CFArrayGetCount(resolvers);
138	for (i = 0; i < n_resolvers; i++) {
139		CFDictionaryRef		match_resolver;
140
141		match_resolver = CFArrayGetValueAtIndex(resolvers, i);
142		if (CFEqual(resolver, match_resolver)) {
143			// a real duplicate
144			return;
145		}
146
147		if (order != NULL) {
148			CFMutableDictionaryRef	compare;
149			Boolean			match;
150
151			compare = CFDictionaryCreateMutableCopy(NULL, 0, match_resolver);
152			CFDictionarySetValue(compare, kSCPropNetDNSSearchOrder, order);
153			match = CFEqual(resolver, compare);
154			CFRelease(compare);
155			if (match) {
156				CFNumberRef	match_order;
157				uint32_t	match_order_val	= 0;
158
159				// if only the search order's are different
160				match_order = CFDictionaryGetValue(match_resolver, kSCPropNetDNSSearchOrder);
161				if (!isA_CFNumber(match_order) ||
162				    !CFNumberGetValue(match_order, kCFNumberSInt32Type, &match_order_val)) {
163					match_order_val = 0;
164				}
165
166				if (order_val < match_order_val ) {
167					// if we should prefer this match resolver, else just skip it
168					CFArraySetValueAtIndex(resolvers, i, resolver);
169				}
170
171				return;
172			}
173		}
174	}
175
176	order = CFNumberCreate(NULL, kCFNumberCFIndexType, &n_resolvers);
177	CFDictionarySetValue(resolver, DNS_CONFIGURATION_ORDER_KEY, order);
178	CFRelease(order);
179
180	interface = CFDictionaryGetValue(resolver, kSCPropInterfaceName);
181	if ((interface != NULL) && !CFEqual(interface, CFSTR("*"))) {
182		uint32_t	flags;
183		unsigned int	if_index		= 0;
184		char		if_name[IF_NAMESIZE];
185		CFNumberRef	num;
186		CFBooleanRef	val;
187
188		if (_SC_cfstring_to_cstring(interface,
189					    if_name,
190					    sizeof(if_name),
191					    kCFStringEncodingASCII) != NULL) {
192			if_index = my_if_nametoindex(if_name);
193		}
194
195		if ((if_index != 0) &&
196		    (
197		     // check if this is a "scoped" configuration
198		     (CFDictionaryGetValueIfPresent(resolver, DNS_CONFIGURATION_FLAGS_KEY, (const void **)&num) &&
199		      isA_CFNumber(num) &&
200		      CFNumberGetValue(num, kCFNumberSInt32Type, &flags) &&
201		      (flags & DNS_RESOLVER_FLAGS_SCOPED) != 0)
202		     ||
203		     // check if we should scope all queries with this configuration
204		     (CFDictionaryGetValueIfPresent(resolver, DNS_CONFIGURATION_SCOPED_QUERY_KEY, (const void **)&val) &&
205		      isA_CFBoolean(val) &&
206		      CFBooleanGetValue(val))
207		    )
208		   ) {
209			// if interface index available and it should be used
210			num = CFNumberCreate(NULL, kCFNumberIntType, &if_index);
211			CFDictionarySetValue(resolver, DNS_CONFIGURATION_IF_INDEX_KEY, num);
212			CFRelease(num);
213		}
214	}
215
216	CFArrayAppendValue(resolvers, resolver);
217	return;
218}
219
220
221static void
222add_supplemental(CFMutableArrayRef resolvers, CFDictionaryRef dns, uint32_t defaultOrder)
223{
224	CFArrayRef	domains;
225	CFIndex		i;
226	CFIndex		n_domains;
227	CFArrayRef	orders;
228
229	domains = CFDictionaryGetValue(dns, kSCPropNetDNSSupplementalMatchDomains);
230	n_domains = isA_CFArray(domains) ? CFArrayGetCount(domains) : 0;
231	if (n_domains == 0) {
232		return;
233	}
234
235	orders = CFDictionaryGetValue(dns, kSCPropNetDNSSupplementalMatchOrders);
236	if (orders != NULL) {
237		if (!isA_CFArray(orders) || (n_domains != CFArrayGetCount(orders))) {
238			return;
239		}
240	}
241
242	/*
243	 * yes, this is a "supplemental" resolver configuration, expand
244	 * the match domains and add each to the resolvers list.
245	 */
246	for (i = 0; i < n_domains; i++) {
247		CFStringRef		match_domain;
248		CFNumberRef		match_order;
249		CFMutableDictionaryRef	match_resolver;
250
251		match_domain = CFArrayGetValueAtIndex(domains, i);
252		if (!isA_CFString(match_domain)) {
253			continue;
254		}
255
256		match_resolver = CFDictionaryCreateMutableCopy(NULL, 0, dns);
257
258		// set supplemental resolver "domain"
259		if (CFStringGetLength(match_domain) > 0) {
260			CFDictionarySetValue(match_resolver, kSCPropNetDNSDomainName, match_domain);
261		} else {
262			CFDictionaryRemoveValue(match_resolver, kSCPropNetDNSDomainName);
263		}
264
265		// set supplemental resolver "search_order"
266		match_order = (orders != NULL) ? CFArrayGetValueAtIndex(orders, i) : NULL;
267		if (isA_CFNumber(match_order)) {
268			CFDictionarySetValue(match_resolver, kSCPropNetDNSSearchOrder, match_order);
269		} else if (!CFDictionaryContainsKey(match_resolver, kSCPropNetDNSSearchOrder)) {
270			CFNumberRef     num;
271
272			num = CFNumberCreate(NULL, kCFNumberIntType, &defaultOrder);
273			CFDictionarySetValue(match_resolver, kSCPropNetDNSSearchOrder, num);
274			CFRelease(num);
275
276			defaultOrder++;		// if multiple domains, maintain ordering
277		}
278
279		// remove keys we don't want in a supplemental resolver
280		CFDictionaryRemoveValue(match_resolver, kSCPropNetDNSSupplementalMatchDomains);
281		CFDictionaryRemoveValue(match_resolver, kSCPropNetDNSSupplementalMatchOrders);
282		CFDictionaryRemoveValue(match_resolver, kSCPropNetDNSSearchDomains);
283		CFDictionaryRemoveValue(match_resolver, kSCPropNetDNSSortList);
284
285		add_resolver(resolvers, match_resolver);
286		CFRelease(match_resolver);
287	}
288
289	return;
290}
291
292
293#define	N_QUICK	32
294
295
296static void
297add_supplemental_resolvers(CFMutableArrayRef	resolvers,
298			   CFDictionaryRef	services,
299			   CFArrayRef		service_order,
300			   CFStringRef		scoped_interface,
301			   CFDictionaryRef	scoped_service)
302{
303	const void *		keys_q[N_QUICK];
304	const void **		keys	= keys_q;
305	CFIndex			i;
306	CFIndex			n_order;
307	CFIndex			n_services;
308	const void *		vals_q[N_QUICK];
309	const void **		vals	= vals_q;
310
311	n_services = isA_CFDictionary(services) ? CFDictionaryGetCount(services) : 0;
312	if (n_services == 0) {
313		return;		// if no services
314	}
315
316	if (n_services > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
317		keys = CFAllocatorAllocate(NULL, n_services * sizeof(CFTypeRef), 0);
318		vals = CFAllocatorAllocate(NULL, n_services * sizeof(CFTypeRef), 0);
319	}
320
321	n_order = isA_CFArray(service_order) ? CFArrayGetCount(service_order) : 0;
322
323	CFDictionaryGetKeysAndValues(services, keys, vals);
324	for (i = 0; i < n_services; i++) {
325		uint32_t		defaultOrder;
326		CFDictionaryRef		dns;
327		CFStringRef		interface;
328		uint32_t		interface_flags;
329		CFMutableDictionaryRef	newDNS		= NULL;
330		CFDictionaryRef		service		= (CFDictionaryRef)vals[i];
331
332		if (!isA_CFDictionary(service)) {
333			continue;
334		}
335
336		dns = CFDictionaryGetValue(service, kSCEntNetDNS);
337		dns = isA_CFDictionary(dns);
338		if (dns == NULL) {
339			continue;
340		}
341
342		interface = CFDictionaryGetValue(dns, kSCPropInterfaceName);
343
344		if (scoped_interface != NULL) {
345			//
346			// we only want to add split/supplemental configurations
347			// for queries scoped to an interface if they are NOT
348			// associated with a "real" service
349			//
350			if (CFDictionaryContainsKey(service, kSCEntNetIPv4) ||
351			    CFDictionaryContainsKey(service, kSCEntNetIPv6)) {
352				continue;
353			}
354
355			//
356			// in addition, we don't want to add split/supplemental
357			// configurations for queries scoped to an interface if
358			// the configuration does not apply to all interfaces and
359			// the configuration is explicitly NOT for this interface
360			//
361			if (!_SC_CFEqual(interface, CFSTR("*")) &&
362			    !_SC_CFEqual(interface, scoped_interface)) {
363				continue;
364			}
365
366			//
367			// lastly, check if A/AAAA queries should be issued (based
368			// on the IP[v6] addresses).  If we would not be issuing a
369			// query then don't bother adding the configuration.
370			//
371			interface_flags = 0;
372			add_dns_query_flags(NULL, scoped_service, &interface_flags);
373			if (interface_flags == 0) {
374				continue;
375			}
376		}
377
378		defaultOrder = DEFAULT_SEARCH_ORDER
379			       - (DEFAULT_SEARCH_ORDER / 2)
380			       + ((DEFAULT_SEARCH_ORDER / 1000) * (uint32_t)i);
381		if ((n_order > 0) &&
382		    !CFArrayContainsValue(service_order, CFRangeMake(0, n_order), keys[i])) {
383			// push out services not specified in service order
384			defaultOrder += (DEFAULT_SEARCH_ORDER / 1000) * n_services;
385		}
386
387		/*
388		 * Ensure that we have the correct InterfaceName in the DNS configuration
389		 *
390		 * scoped_interface	[supplemental] interface	DNS interface
391		 * ================	========================	=================
392		 * NULL			NULL				NULL (No change)
393		 * NULL			en0				NULL
394		 * NULL			*				NULL
395		 * en0			NULL				"en0"
396		 * en0			en0				"en0" (now mutable)
397		 * en0			*				"en0"
398		 */
399		if ((scoped_interface == NULL) && (interface == NULL)) {
400			newDNS = (CFMutableDictionaryRef)CFRetain(dns);
401		} else {
402			newDNS = CFDictionaryCreateMutableCopy(NULL, 0, dns);
403			if (scoped_interface != NULL) {
404				CFDictionarySetValue(newDNS, kSCPropInterfaceName, scoped_interface);
405			} else {
406				CFDictionaryRemoveValue(newDNS, kSCPropInterfaceName);
407			}
408		}
409
410		if (scoped_interface != NULL) {
411			uint32_t	flags;
412			CFNumberRef	num;
413
414			// set "scoped" configuration flag(s)
415			if (!CFDictionaryGetValueIfPresent(newDNS, DNS_CONFIGURATION_FLAGS_KEY, (const void **)&num) ||
416			    !isA_CFNumber(num) ||
417			    !CFNumberGetValue(num, kCFNumberSInt32Type, &flags)) {
418				flags = 0;
419			}
420			flags |= DNS_RESOLVER_FLAGS_SCOPED;
421
422			// add A/AAAA query flag(s)
423			flags |= interface_flags;
424
425			num = CFNumberCreate(NULL, kCFNumberSInt32Type, &flags);
426			CFDictionarySetValue(newDNS, DNS_CONFIGURATION_FLAGS_KEY, num);
427			CFRelease(num);
428		}
429
430		// add [scoped] resolver entry
431		add_supplemental(resolvers, newDNS, defaultOrder);
432		CFRelease(newDNS);
433	}
434
435	if (keys != keys_q) {
436		CFAllocatorDeallocate(NULL, keys);
437		CFAllocatorDeallocate(NULL, vals);
438	}
439
440	return;
441}
442
443
444static void
445add_multicast_resolvers(CFMutableArrayRef resolvers, CFArrayRef multicastResolvers)
446{
447	CFIndex	i;
448	CFIndex	n;
449
450	n = isA_CFArray(multicastResolvers) ? CFArrayGetCount(multicastResolvers) : 0;
451	for (i = 0; i < n; i++) {
452		uint32_t		defaultOrder;
453		CFStringRef		domain;
454		CFNumberRef		num;
455		CFMutableDictionaryRef	resolver;
456
457		domain = CFArrayGetValueAtIndex(multicastResolvers, i);
458		domain = _SC_trimDomain(domain);
459		if (domain == NULL) {
460			continue;
461		}
462
463		defaultOrder = DEFAULT_SEARCH_ORDER
464		+ (DEFAULT_SEARCH_ORDER / 2)
465		+ ((DEFAULT_SEARCH_ORDER / 1000) * (uint32_t)i);
466
467		resolver = CFDictionaryCreateMutable(NULL,
468						     0,
469						     &kCFTypeDictionaryKeyCallBacks,
470						     &kCFTypeDictionaryValueCallBacks);
471		CFDictionarySetValue(resolver, kSCPropNetDNSDomainName, domain);
472		CFDictionarySetValue(resolver, kSCPropNetDNSOptions, CFSTR("mdns"));
473		num = CFNumberCreate(NULL, kCFNumberIntType, &defaultOrder);
474		CFDictionarySetValue(resolver, kSCPropNetDNSSearchOrder, num);
475		CFRelease(num);
476		if (S_mdns_timeout != NULL) {
477			CFDictionarySetValue(resolver, kSCPropNetDNSServerTimeout, S_mdns_timeout);
478		}
479		add_resolver(resolvers, resolver);
480		CFRelease(resolver);
481		CFRelease(domain);
482	}
483
484	return;
485}
486
487
488static void
489add_private_resolvers(CFMutableArrayRef resolvers, CFArrayRef privateResolvers)
490{
491	CFIndex	i;
492	CFIndex	n;
493
494	n = isA_CFArray(privateResolvers) ? CFArrayGetCount(privateResolvers) : 0;
495	for (i = 0; i < n; i++) {
496		uint32_t		defaultOrder;
497		CFStringRef		domain;
498		CFNumberRef		num;
499		CFMutableDictionaryRef	resolver;
500
501		domain = CFArrayGetValueAtIndex(privateResolvers, i);
502		domain = _SC_trimDomain(domain);
503		if (domain == NULL) {
504			continue;
505		}
506
507		defaultOrder = DEFAULT_SEARCH_ORDER
508			       - (DEFAULT_SEARCH_ORDER / 4)
509			       + ((DEFAULT_SEARCH_ORDER / 1000) * (uint32_t)i);
510
511		resolver = CFDictionaryCreateMutable(NULL,
512						     0,
513						     &kCFTypeDictionaryKeyCallBacks,
514						     &kCFTypeDictionaryValueCallBacks);
515		CFDictionarySetValue(resolver, kSCPropNetDNSDomainName, domain);
516		CFDictionarySetValue(resolver, kSCPropNetDNSOptions, CFSTR("pdns"));
517		num = CFNumberCreate(NULL, kCFNumberIntType, &defaultOrder);
518		CFDictionarySetValue(resolver, kSCPropNetDNSSearchOrder, num);
519		CFRelease(num);
520		if (S_pdns_timeout != NULL) {
521			CFDictionarySetValue(resolver, kSCPropNetDNSServerTimeout, S_pdns_timeout);
522		}
523		add_resolver(resolvers, resolver);
524		CFRelease(resolver);
525		CFRelease(domain);
526	}
527
528	return;
529}
530
531
532static CFComparisonResult
533compareBySearchOrder(const void *val1, const void *val2, void *context)
534{
535	CFDictionaryRef	dns1	= (CFDictionaryRef)val1;
536	CFDictionaryRef	dns2	= (CFDictionaryRef)val2;
537	CFNumberRef	num1;
538	CFNumberRef	num2;
539	uint32_t	order1	= DEFAULT_SEARCH_ORDER;
540	uint32_t	order2	= DEFAULT_SEARCH_ORDER;
541
542	num1 = CFDictionaryGetValue(dns1, kSCPropNetDNSSearchOrder);
543	if (!isA_CFNumber(num1) ||
544	    !CFNumberGetValue(num1, kCFNumberSInt32Type, &order1)) {
545		order1 = DEFAULT_SEARCH_ORDER;
546	}
547
548	num2 = CFDictionaryGetValue(dns2, kSCPropNetDNSSearchOrder);
549	if (!isA_CFNumber(num2) ||
550	    !CFNumberGetValue(num2, kCFNumberSInt32Type, &order2)) {
551		order2 = DEFAULT_SEARCH_ORDER;
552	}
553
554	if (order1 == order2) {
555		// if same "SearchOrder", retain original orderring for configurations
556		if (CFDictionaryGetValueIfPresent(dns1, DNS_CONFIGURATION_ORDER_KEY, (const void **)&num1) &&
557		    CFDictionaryGetValueIfPresent(dns2, DNS_CONFIGURATION_ORDER_KEY, (const void **)&num2) &&
558		    isA_CFNumber(num1) &&
559		    isA_CFNumber(num2) &&
560		    CFNumberGetValue(num1, kCFNumberSInt32Type, &order1) &&
561		    CFNumberGetValue(num2, kCFNumberSInt32Type, &order2)) {
562			if (order1 == order2) {
563				return kCFCompareEqualTo;
564			} else {
565				return (order1 < order2) ? kCFCompareLessThan : kCFCompareGreaterThan;
566			}
567		}
568
569		return kCFCompareEqualTo;
570	}
571
572	return (order1 < order2) ? kCFCompareLessThan : kCFCompareGreaterThan;
573}
574
575
576static CF_RETURNS_RETAINED CFArrayRef
577extract_search_domains(CFMutableDictionaryRef defaultDomain, CFArrayRef supplemental)
578{
579	CFStringRef		defaultDomainName	= NULL;
580	uint32_t		defaultOrder		= DEFAULT_SEARCH_ORDER;
581	CFArrayRef		defaultSearchDomains	= NULL;
582	CFIndex			defaultSearchIndex	= 0;
583	CFIndex			i;
584	CFMutableArrayRef	mySearchDomains;
585	CFMutableArrayRef	mySupplemental		= NULL;
586	CFIndex			n_supplemental;
587
588	mySearchDomains = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
589
590	if (defaultDomain != NULL) {
591		CFNumberRef	num;
592
593		num = CFDictionaryGetValue(defaultDomain, kSCPropNetDNSSearchOrder);
594		if (!isA_CFNumber(num) ||
595		    !CFNumberGetValue(num, kCFNumberSInt32Type, &defaultOrder)) {
596			defaultOrder = DEFAULT_SEARCH_ORDER;
597		}
598
599		defaultDomainName    = CFDictionaryGetValue(defaultDomain, kSCPropNetDNSDomainName);
600		defaultSearchDomains = CFDictionaryGetValue(defaultDomain, kSCPropNetDNSSearchDomains);
601	}
602
603	// validate the provided "search" domains or move/expand/promote the "domain" name
604	if (isA_CFArray(defaultSearchDomains)) {
605		CFIndex	n_search;
606
607		n_search = CFArrayGetCount(defaultSearchDomains);
608		for (i = 0; i < n_search; i++) {
609			CFStringRef	search;
610
611			search = CFArrayGetValueAtIndex(defaultSearchDomains, i);
612			search = _SC_trimDomain(search);
613			if (search != NULL) {
614				CFArrayAppendValue(mySearchDomains, search);
615				CFRelease(search);
616			}
617		}
618	} else {
619		defaultDomainName = _SC_trimDomain(defaultDomainName);
620		if (defaultDomainName != NULL) {
621			char		*domain;
622			int		domain_parts	= 1;
623			char		*dp;
624			const int	ndots		= 1;
625
626			domain = _SC_cfstring_to_cstring(defaultDomainName,
627							 NULL,
628							 0,
629							 kCFStringEncodingUTF8);
630			CFRelease(defaultDomainName);
631
632			// count domain parts
633			for (dp = domain; *dp != '\0'; dp++) {
634				if (*dp == '.') {
635					domain_parts++;
636				}
637			}
638
639			// move "domain" to "search" list (and expand as needed)
640			i = LOCALDOMAINPARTS;
641			dp = domain;
642			do {
643				CFStringRef	search;
644				CFStringRef	str;
645
646				str = CFStringCreateWithCString(NULL,
647								dp,
648								kCFStringEncodingUTF8);
649				search = _SC_trimDomain(str);
650				CFRelease(str);
651				if (search != NULL) {
652					CFArrayAppendValue(mySearchDomains, search);
653					CFRelease(search);
654				}
655
656				dp = strchr(dp, '.') + 1;
657			} while (++i <= (domain_parts - ndots));
658			CFAllocatorDeallocate(NULL, domain);
659		}
660	}
661
662	// add any supplemental "domain" names to the search list
663	n_supplemental = (supplemental != NULL) ? CFArrayGetCount(supplemental) : 0;
664	if (n_supplemental > 1) {
665		mySupplemental = CFArrayCreateMutableCopy(NULL, 0, supplemental);
666		CFArraySortValues(mySupplemental,
667				  CFRangeMake(0, n_supplemental),
668				  compareBySearchOrder,
669				  NULL);
670		supplemental = mySupplemental;
671	}
672	for (i = 0; i < n_supplemental; i++) {
673		CFDictionaryRef dns;
674		CFIndex		domainIndex;
675		int		noSearch;
676		CFNumberRef	num;
677		CFStringRef	options;
678		CFStringRef	supplementalDomain;
679		uint32_t	supplementalOrder;
680
681		dns = CFArrayGetValueAtIndex(supplemental, i);
682
683		options = CFDictionaryGetValue(dns, kSCPropNetDNSOptions);
684		if (isA_CFString(options)) {
685			CFRange	range;
686
687			if (CFEqual(options, CFSTR("pdns"))) {
688				// don't add private resolver domains to the search list
689				continue;
690			}
691
692			range = CFStringFind(options, CFSTR("interface="), 0);
693			if (range.location != kCFNotFound) {
694				// don't add scoped resolver domains to the search list
695				continue;
696			}
697		}
698
699		supplementalDomain = CFDictionaryGetValue(dns, kSCPropNetDNSDomainName);
700		supplementalDomain = _SC_trimDomain(supplementalDomain);
701		if (supplementalDomain == NULL) {
702			continue;
703		}
704
705		num = CFDictionaryGetValue(dns, kSCPropNetDNSSupplementalMatchDomainsNoSearch);
706		if (isA_CFNumber(num) &&
707		    CFNumberGetValue(num, kCFNumberIntType, &noSearch) &&
708		    (noSearch != 0)) {
709			CFRelease(supplementalDomain);
710			continue;
711		}
712
713		if (CFStringHasSuffix(supplementalDomain, CFSTR(".in-addr.arpa")) ||
714		    CFStringHasSuffix(supplementalDomain, CFSTR(".ip6.arpa"    ))) {
715			CFRelease(supplementalDomain);
716			continue;
717		}
718
719		domainIndex = CFArrayGetFirstIndexOfValue(mySearchDomains,
720							  CFRangeMake(0, CFArrayGetCount(mySearchDomains)),
721							  supplementalDomain);
722
723		num = CFDictionaryGetValue(dns, kSCPropNetDNSSearchOrder);
724		if (!isA_CFNumber(num) ||
725		    !CFNumberGetValue(num, kCFNumberSInt32Type, &supplementalOrder)) {
726			supplementalOrder = DEFAULT_SEARCH_ORDER;
727		}
728
729		if (supplementalOrder < defaultOrder) {
730			if (domainIndex != kCFNotFound) {
731				// if supplemental domain is already in the search list
732				CFArrayRemoveValueAtIndex(mySearchDomains, domainIndex);
733				if (domainIndex < defaultSearchIndex) {
734					defaultSearchIndex--;
735				}
736			}
737			CFArrayInsertValueAtIndex(mySearchDomains,
738						  defaultSearchIndex,
739						  supplementalDomain);
740			defaultSearchIndex++;
741		} else {
742			if (domainIndex == kCFNotFound) {
743				// add to the (end of the) search list
744				CFArrayAppendValue(mySearchDomains, supplementalDomain);
745			}
746		}
747
748		CFRelease(supplementalDomain);
749	}
750	if (mySupplemental != NULL) CFRelease(mySupplemental);
751
752	// update the "search" domains
753	if (CFArrayGetCount(mySearchDomains) == 0) {
754		CFRelease(mySearchDomains);
755		mySearchDomains = NULL;
756	}
757
758	// remove the "domain" name and "search" list
759	CFDictionaryRemoveValue(defaultDomain, kSCPropNetDNSDomainName);
760	CFDictionaryRemoveValue(defaultDomain, kSCPropNetDNSSearchDomains);
761
762	return mySearchDomains;
763}
764
765
766static void
767add_scoped_resolvers(CFMutableArrayRef scoped, CFDictionaryRef services, CFArrayRef service_order)
768{
769	const void *		keys_q[N_QUICK];
770	const void **		keys	= keys_q;
771	CFIndex			i;
772	CFIndex			n_order;
773	CFIndex			n_services;
774	CFMutableArrayRef	order;
775	CFMutableSetRef		seen;
776
777	n_services = isA_CFDictionary(services) ? CFDictionaryGetCount(services) : 0;
778	if (n_services == 0) {
779		return;		// if no services
780	}
781
782	// ensure that we process all services in order
783
784	n_order = isA_CFArray(service_order) ? CFArrayGetCount(service_order) : 0;
785	if (n_order > 0) {
786		order = CFArrayCreateMutableCopy(NULL, 0, service_order);
787	} else {
788		order = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
789	}
790
791	if (n_services > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
792		keys = CFAllocatorAllocate(NULL, n_services * sizeof(CFTypeRef), 0);
793	}
794	CFDictionaryGetKeysAndValues(services, keys, NULL);
795	for (i = 0; i < n_services; i++) {
796		CFStringRef	serviceID = (CFStringRef)keys[i];
797
798		if (!CFArrayContainsValue(order, CFRangeMake(0, n_order), serviceID)) {
799			CFArrayAppendValue(order, serviceID);
800			n_order++;
801		}
802	}
803	if (keys != keys_q) {
804		CFAllocatorDeallocate(NULL, keys);
805	}
806
807	// iterate over services
808
809	seen = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
810	for (i = 0; i < n_order; i++) {
811		CFDictionaryRef		dns;
812		uint32_t		flags;
813		char			if_name[IF_NAMESIZE];
814		CFStringRef		interface;
815		CFMutableDictionaryRef	newDNS;
816		CFNumberRef		num;
817		CFArrayRef		searchDomains;
818		CFDictionaryRef		service;
819		CFStringRef		serviceID;
820		uint32_t		these_flags;
821
822		serviceID = CFArrayGetValueAtIndex(order, i);
823		service = CFDictionaryGetValue(services, serviceID);
824		if (!isA_CFDictionary(service)) {
825			// if no service
826			continue;
827		}
828
829		dns = CFDictionaryGetValue(service, kSCEntNetDNS);
830		if (!isA_CFDictionary(dns)) {
831			// if no DNS
832			continue;
833		}
834
835		interface = CFDictionaryGetValue(dns, kSCPropInterfaceName);
836		if ((interface == NULL) || CFEqual(interface, CFSTR("*"))) {
837			// if no [scoped] interface or supplemental configuration w/match-all
838			continue;
839		}
840
841		if (CFDictionaryContainsKey(dns, kSCPropNetDNSServiceIdentifier)) {
842			// if this is a service-specific resolver
843			continue;
844		}
845
846		if (CFSetContainsValue(seen, interface)) {
847			// if we've already processed this [scoped] interface
848			continue;
849		}
850		CFSetSetValue(seen, interface);
851
852		if ((_SC_cfstring_to_cstring(interface,
853					     if_name,
854					     sizeof(if_name),
855					     kCFStringEncodingASCII) == NULL) ||
856		    (my_if_nametoindex(if_name) == 0)) {
857			// if interface index not available
858			continue;
859		}
860
861		// add [scoped] resolver entry
862		newDNS = CFDictionaryCreateMutableCopy(NULL, 0, dns);
863
864		// set search list
865		searchDomains = extract_search_domains(newDNS, NULL);
866		if (searchDomains != NULL) {
867			CFDictionarySetValue(newDNS, kSCPropNetDNSSearchDomains, searchDomains);
868			CFRelease(searchDomains);
869		}
870
871		// set "scoped" configuration flag(s)
872		if (!CFDictionaryGetValueIfPresent(newDNS, DNS_CONFIGURATION_FLAGS_KEY, (const void **)&num) ||
873		    !isA_CFNumber(num) ||
874		    !CFNumberGetValue(num, kCFNumberSInt32Type, &flags)) {
875			flags = 0;
876		}
877		flags |= DNS_RESOLVER_FLAGS_SCOPED;
878
879		these_flags = 0;
880		add_dns_query_flags(serviceID, service, &these_flags);
881		if (these_flags == 0) {
882		    goto skip;
883		}
884		flags |= these_flags;
885
886		num = CFNumberCreate(NULL, kCFNumberSInt32Type, &flags);
887		CFDictionarySetValue(newDNS, DNS_CONFIGURATION_FLAGS_KEY, num);
888		CFRelease(num);
889
890		// remove keys we don't want in a [scoped] resolver
891		CFDictionaryRemoveValue(newDNS, kSCPropNetDNSSupplementalMatchDomains);
892		CFDictionaryRemoveValue(newDNS, kSCPropNetDNSSupplementalMatchOrders);
893
894		// add the [scoped] resolver
895		add_resolver(scoped, newDNS);
896
897		// add any supplemental resolver configurations for this interface
898		add_supplemental_resolvers(scoped, services, service_order, interface, service);
899
900	skip:
901		CFRelease(newDNS);
902	}
903
904	CFRelease(seen);
905	CFRelease(order);
906	return;
907}
908
909
910static void
911add_service_specific_resolvers(CFMutableArrayRef resolvers, CFDictionaryRef services)
912{
913	CFIndex	services_count	= (isA_CFDictionary(services) ? CFDictionaryGetCount(services) : 0);
914
915	if (services_count > 0) {
916		CFIndex			key_idx;
917		CFStringRef		keys_q[N_QUICK];
918		CFStringRef		*keys	= keys_q;
919		CFMutableSetRef		seen	= CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
920
921		if (services_count > (CFIndex)(sizeof(keys_q) / sizeof(keys_q[0]))) {
922			keys = CFAllocatorAllocate(kCFAllocatorDefault, services_count * sizeof(keys[0]), 0);
923		}
924
925		CFDictionaryGetKeysAndValues(services, (const void **)keys, NULL);
926
927		for (key_idx = 0; key_idx < services_count; key_idx++) {
928			CFDictionaryRef	service	= CFDictionaryGetValue(services, keys[key_idx]);
929			CFDictionaryRef	dns	= CFDictionaryGetValue(service, kSCEntNetDNS);
930
931			if (isA_CFDictionary(dns)) {
932				CFNumberRef	service_identifier	= CFDictionaryGetValue(dns, kSCPropNetDNSServiceIdentifier);
933
934				if (isA_CFNumber(service_identifier)) {
935					if (!CFSetContainsValue(seen, service_identifier)) {
936						CFMutableDictionaryRef	new_resolver	= CFDictionaryCreateMutableCopy(NULL, 0, dns);
937						CFNumberRef		flags_num;
938						int32_t			flags		= 0;
939
940						CFSetSetValue(seen, service_identifier);
941
942						if (!CFDictionaryGetValueIfPresent(new_resolver, DNS_CONFIGURATION_FLAGS_KEY, (const void **)&flags_num) ||
943						    !isA_CFNumber(flags_num) ||
944						    !CFNumberGetValue(flags_num, kCFNumberSInt32Type, &flags)) {
945							flags = 0;
946						}
947
948						flags |= DNS_RESOLVER_FLAGS_SERVICE_SPECIFIC | DNS_RESOLVER_FLAGS_REQUEST_ALL_RECORDS;
949
950						flags_num = CFNumberCreate(NULL, kCFNumberSInt32Type, &flags);
951						CFDictionarySetValue(new_resolver, DNS_CONFIGURATION_FLAGS_KEY, flags_num);
952						CFRelease(flags_num);
953
954						if (CFDictionaryContainsKey(new_resolver, kSCPropInterfaceName)) {
955							CFDictionarySetValue(new_resolver, DNS_CONFIGURATION_SCOPED_QUERY_KEY, kCFBooleanTrue);
956						}
957
958						CFDictionaryRemoveValue(new_resolver, kSCPropNetDNSSupplementalMatchDomains);
959						CFDictionaryRemoveValue(new_resolver, kSCPropNetDNSSupplementalMatchOrders);
960
961						add_resolver(resolvers, new_resolver);
962						CFRelease(new_resolver);
963					} else {
964						my_log(LOG_ERR, "add_service_specific_resolvers: got a resolver with a duplicate service identifier, skipping");
965					}
966				}
967			}
968		}
969
970		if (keys != keys_q) {
971			CFAllocatorDeallocate(kCFAllocatorDefault, keys);
972		}
973		CFRelease(seen);
974	}
975}
976
977
978static void
979add_default_resolver(CFMutableArrayRef	resolvers,
980		     CFDictionaryRef	defaultResolver,
981		     Boolean		*orderAdded,
982		     CFArrayRef		*searchDomains)
983{
984	CFMutableDictionaryRef	myDefault;
985	uint32_t		myOrder	= DEFAULT_SEARCH_ORDER;
986	CFNumberRef		order;
987
988	if (defaultResolver == NULL) {
989		myDefault = CFDictionaryCreateMutable(NULL,
990						      0,
991						      &kCFTypeDictionaryKeyCallBacks,
992						      &kCFTypeDictionaryValueCallBacks);
993	} else {
994		myDefault = CFDictionaryCreateMutableCopy(NULL, 0, defaultResolver);
995	}
996	assert(myDefault != NULL);
997
998	// ensure that the default resolver has a search order
999
1000	order = CFDictionaryGetValue(myDefault, kSCPropNetDNSSearchOrder);
1001	if (!isA_CFNumber(order) ||
1002	    !CFNumberGetValue(order, kCFNumberSInt32Type, &myOrder)) {
1003		myOrder = DEFAULT_SEARCH_ORDER;
1004		order = CFNumberCreate(NULL, kCFNumberIntType, &myOrder);
1005		CFDictionarySetValue(myDefault, kSCPropNetDNSSearchOrder, order);
1006		CFRelease(order);
1007		*orderAdded = TRUE;
1008	}
1009
1010	// extract the "search" domain list for the default resolver (and
1011	// any supplemental resolvers)
1012
1013	*searchDomains = extract_search_domains(myDefault, resolvers);
1014
1015	// add the default resolver
1016
1017	add_resolver(resolvers, myDefault);
1018	CFRelease(myDefault);
1019	return;
1020}
1021
1022
1023static dns_create_resolver_t
1024create_resolver(CFDictionaryRef dns)
1025{
1026	CFArrayRef		list;
1027	CFNumberRef		num;
1028	dns_create_resolver_t	_resolver;
1029	CFStringRef		str;
1030	CFStringRef		targetInterface		= NULL;
1031	unsigned int		targetInterfaceIndex	= 0;
1032
1033	_resolver = _dns_resolver_create();
1034
1035	// process domain
1036	str = CFDictionaryGetValue(dns, kSCPropNetDNSDomainName);
1037	if (isA_CFString(str) && (CFStringGetLength(str) > 0)) {
1038		char	domain[NS_MAXDNAME];
1039
1040		if (_SC_cfstring_to_cstring(str, domain, sizeof(domain), kCFStringEncodingUTF8) != NULL) {
1041			_dns_resolver_set_domain(&_resolver, domain);
1042		}
1043	}
1044
1045	// process search domains
1046	list = CFDictionaryGetValue(dns, kSCPropNetDNSSearchDomains);
1047	if (isA_CFArray(list)) {
1048		CFIndex	i;
1049		CFIndex n	= CFArrayGetCount(list);
1050
1051		// add "search" domains
1052		for (i = 0; i < n; i++) {
1053			str = CFArrayGetValueAtIndex(list, i);
1054			if (isA_CFString(str) && (CFStringGetLength(str) > 0)) {
1055				char	search[NS_MAXDNAME];
1056
1057				if (_SC_cfstring_to_cstring(str, search, sizeof(search), kCFStringEncodingUTF8) != NULL) {
1058					_dns_resolver_add_search(&_resolver, search);
1059				}
1060			}
1061		}
1062	}
1063
1064	// process interface index
1065	num = CFDictionaryGetValue(dns, DNS_CONFIGURATION_IF_INDEX_KEY);
1066	if (isA_CFNumber(num)) {
1067		int	if_index;
1068
1069		if (CFNumberGetValue(num, kCFNumberIntType, &if_index)) {
1070			char	if_name[IFNAMSIZ];
1071
1072			_dns_resolver_set_if_index(&_resolver, if_index);
1073
1074			if ((if_index != 0) &&
1075			    (my_if_indextoname(if_index, if_name) != NULL)) {
1076				targetInterface = CFStringCreateWithCString(NULL,
1077									    if_name,
1078									    kCFStringEncodingASCII);
1079				targetInterfaceIndex = if_index;
1080			}
1081		}
1082	}
1083
1084	// process flags
1085	num = CFDictionaryGetValue(dns, DNS_CONFIGURATION_FLAGS_KEY);
1086	if (isA_CFNumber(num)) {
1087		uint32_t	flags;
1088
1089		if (CFNumberGetValue(num, kCFNumberSInt32Type, &flags)) {
1090			_dns_resolver_set_flags(&_resolver, flags);
1091		}
1092	}
1093
1094	// process nameserver addresses
1095	// Note: the flags may be overwritten if the resolver has LOOPBACK addresses
1096	list = CFDictionaryGetValue(dns, kSCPropNetDNSServerAddresses);
1097	if (isA_CFArray(list)) {
1098		CFIndex	i;
1099		CFIndex	n	= CFArrayGetCount(list);
1100
1101		for (i = 0; i < n; i++) {
1102			union {
1103				struct sockaddr         sa;
1104				struct sockaddr_in      sin;
1105				struct sockaddr_in6     sin6;
1106			} addr;
1107			char				buf[64];
1108
1109			str = CFArrayGetValueAtIndex(list, i);
1110			if (!isA_CFString(str)) {
1111				continue;
1112			}
1113
1114			if (_SC_cfstring_to_cstring(str, buf, sizeof(buf), kCFStringEncodingASCII) == NULL) {
1115				continue;
1116			}
1117
1118			if (_SC_string_to_sockaddr(buf, AF_UNSPEC, (void *)&addr, sizeof(addr)) == NULL) {
1119				continue;
1120			}
1121
1122			if ((addr.sa.sa_family == AF_INET6) &&
1123			    IN6_IS_ADDR_LINKLOCAL(&addr.sin6.sin6_addr) &&
1124			    (addr.sin6.sin6_scope_id == 0) &&
1125			    (targetInterfaceIndex != 0)) {
1126				// for link local [IPv6] addresses, if the scope id is not
1127				// set then we should use the interface associated with the
1128				// resolver configuration
1129				addr.sin6.sin6_scope_id = targetInterfaceIndex;
1130			}
1131
1132			_dns_resolver_add_nameserver(&_resolver, &addr.sa);
1133		}
1134	}
1135
1136	// process search order
1137	num = CFDictionaryGetValue(dns, kSCPropNetDNSSearchOrder);
1138	if (isA_CFNumber(num)) {
1139		uint32_t	order;
1140
1141		if (CFNumberGetValue(num, kCFNumberSInt32Type, &order)) {
1142			_dns_resolver_set_order(&_resolver, order);
1143		}
1144	}
1145
1146	// process sortlist
1147	list = CFDictionaryGetValue(dns, kSCPropNetDNSSortList);
1148	if (isA_CFArray(list)) {
1149		CFIndex	i;
1150		CFIndex n	= CFArrayGetCount(list);
1151
1152		for (i = 0; i < n; i++) {
1153			char		buf[128];
1154			char		*slash;
1155			dns_sortaddr_t	sortaddr;
1156
1157			str = CFArrayGetValueAtIndex(list, i);
1158			if (!isA_CFString(str)) {
1159				continue;
1160			}
1161
1162			if (_SC_cfstring_to_cstring(str, buf, sizeof(buf), kCFStringEncodingASCII) == NULL) {
1163				continue;
1164			}
1165
1166			slash = strchr(buf, '/');
1167			if (slash != NULL) {
1168				*slash = '\0';
1169			}
1170
1171			bzero(&sortaddr, sizeof(sortaddr));
1172			if (inet_aton(buf, &sortaddr.address) != 1) {
1173				/* if address not valid */
1174				continue;
1175			}
1176
1177			if (slash != NULL) {
1178				if (inet_aton(slash + 1, &sortaddr.mask) != 1) {
1179					/* if subnet mask not valid */
1180					continue;
1181				}
1182			} else {
1183				in_addr_t	a;
1184				in_addr_t	m;
1185
1186				a = ntohl(sortaddr.address.s_addr);
1187				if (IN_CLASSA(a)) {
1188					m = IN_CLASSA_NET;
1189				} else if (IN_CLASSB(a)) {
1190					m = IN_CLASSB_NET;
1191				} else if (IN_CLASSC(a)) {
1192					m = IN_CLASSC_NET;
1193				} else {
1194					continue;
1195				}
1196
1197				sortaddr.mask.s_addr = htonl(m);
1198			}
1199
1200			_dns_resolver_add_sortaddr(&_resolver, &sortaddr);
1201		}
1202	}
1203
1204	// process port
1205	num = CFDictionaryGetValue(dns, kSCPropNetDNSServerPort);
1206	if (isA_CFNumber(num)) {
1207		int	port;
1208
1209		if (CFNumberGetValue(num, kCFNumberIntType, &port)) {
1210			_dns_resolver_set_port(&_resolver, (uint16_t)port);
1211		}
1212	}
1213
1214	// process timeout
1215	num = CFDictionaryGetValue(dns, kSCPropNetDNSServerTimeout);
1216	if (isA_CFNumber(num)) {
1217		int	timeout;
1218
1219		if (CFNumberGetValue(num, kCFNumberIntType, &timeout)) {
1220			_dns_resolver_set_timeout(&_resolver, (uint32_t)timeout);
1221		}
1222	}
1223
1224	// process options
1225	str = CFDictionaryGetValue(dns, kSCPropNetDNSOptions);
1226	if (isA_CFString(str)) {
1227		char	*options;
1228
1229		options = _SC_cfstring_to_cstring(str, NULL, 0, kCFStringEncodingUTF8);
1230		if (options != NULL) {
1231			_dns_resolver_set_options(&_resolver, options);
1232			CFAllocatorDeallocate(NULL, options);
1233		}
1234	}
1235
1236	num = CFDictionaryGetValue(dns, kSCPropNetDNSServiceIdentifier);
1237	if (isA_CFNumber(num)) {
1238		int	service_identifier;
1239
1240		if (CFNumberGetValue(num, kCFNumberIntType, &service_identifier)) {
1241			_dns_resolver_set_service_identifier(&_resolver, (uint32_t)service_identifier);
1242		}
1243	}
1244
1245	if (targetInterface != NULL) {
1246		CFRelease(targetInterface);
1247	}
1248
1249	return _resolver;
1250}
1251
1252
1253static __inline__ Boolean
1254isScopedConfiguration(CFDictionaryRef dns)
1255{
1256	uint32_t	flags;
1257	CFNumberRef	num;
1258
1259	if ((dns != NULL) &&
1260	    CFDictionaryGetValueIfPresent(dns, DNS_CONFIGURATION_FLAGS_KEY, (const void **)&num) &&
1261	    (num != NULL) &&
1262	    CFNumberGetValue(num, kCFNumberSInt32Type, &flags) &&
1263	    ((flags & DNS_RESOLVER_FLAGS_SCOPED) != 0)) {
1264		return TRUE;
1265	}
1266
1267	return FALSE;
1268}
1269
1270
1271static __inline__ Boolean
1272isServiceSpecificConfiguration(CFDictionaryRef dns)
1273{
1274	uint32_t	flags;
1275	CFNumberRef	num;
1276
1277	if (dns != NULL &&
1278	    CFDictionaryGetValueIfPresent(dns, DNS_CONFIGURATION_FLAGS_KEY, (const void **)&num) &&
1279	    num != NULL &&
1280	    CFNumberGetValue(num, kCFNumberSInt32Type, &flags) &&
1281	    (flags & DNS_RESOLVER_FLAGS_SERVICE_SPECIFIC))
1282	{
1283		return TRUE;
1284	}
1285
1286	return FALSE;
1287}
1288
1289
1290static CFComparisonResult
1291compareDomain(const void *val1, const void *val2, void *context)
1292{
1293	CFDictionaryRef		dns1	= (CFDictionaryRef)val1;
1294	CFDictionaryRef		dns2	= (CFDictionaryRef)val2;
1295	CFStringRef		domain1;
1296	CFStringRef		domain2;
1297	CFArrayRef		labels1	= NULL;
1298	CFArrayRef		labels2	= NULL;
1299	CFIndex			n1;
1300	CFIndex			n2;
1301	CFComparisonResult	result;
1302	Boolean			rev1;
1303	Boolean			rev2;
1304	Boolean			scoped1;
1305	Boolean			scoped2;
1306
1307	// "default" domains sort before "supplemental" domains
1308	domain1 = CFDictionaryGetValue(dns1, kSCPropNetDNSDomainName);
1309	domain2 = CFDictionaryGetValue(dns2, kSCPropNetDNSDomainName);
1310	if (domain1 == NULL) {
1311		return kCFCompareLessThan;
1312	} else if (domain2 == NULL) {
1313		return kCFCompareGreaterThan;
1314	}
1315
1316	// sort non-scoped before scoped
1317	scoped1 = isScopedConfiguration(dns1);
1318	scoped2 = isScopedConfiguration(dns2);
1319	if (scoped1 != scoped2) {
1320		if (!scoped1) {
1321			return kCFCompareLessThan;
1322		} else {
1323			return kCFCompareGreaterThan;
1324		}
1325	}
1326
1327	// must have domain names for any further comparisons
1328	if ((domain1 == NULL) || (domain2 == NULL)) {
1329		return kCFCompareEqualTo;
1330	}
1331
1332	// forward (A, AAAA) domains sort before reverse (PTR) domains
1333	rev1 = CFStringHasSuffix(domain1, CFSTR(".arpa"));
1334	rev2 = CFStringHasSuffix(domain2, CFSTR(".arpa"));
1335	if (rev1 != rev2) {
1336		if (rev1) {
1337			return kCFCompareGreaterThan;
1338		} else {
1339			return kCFCompareLessThan;
1340		}
1341	}
1342
1343	labels1 = CFStringCreateArrayBySeparatingStrings(NULL, domain1, CFSTR("."));
1344	n1 = CFArrayGetCount(labels1);
1345
1346	labels2 = CFStringCreateArrayBySeparatingStrings(NULL, domain2, CFSTR("."));
1347	n2 = CFArrayGetCount(labels2);
1348
1349	while ((n1 > 0) && (n2 > 0)) {
1350		CFStringRef	label1	= CFArrayGetValueAtIndex(labels1, --n1);
1351		CFStringRef	label2	= CFArrayGetValueAtIndex(labels2, --n2);
1352
1353		// compare domain labels
1354		result = CFStringCompare(label1, label2, kCFCompareCaseInsensitive);
1355		if (result != kCFCompareEqualTo) {
1356			goto done;
1357		}
1358	}
1359
1360	// longer labels (corp.apple.com) sort before shorter labels (apple.com)
1361	if (n1 > n2) {
1362		result = kCFCompareLessThan;
1363		goto done;
1364	} else if (n1 < n2) {
1365		result = kCFCompareGreaterThan;
1366		goto done;
1367	}
1368
1369	// sort by search order
1370	result = compareBySearchOrder(val1, val2, context);
1371
1372    done :
1373
1374	if (labels1 != NULL) CFRelease(labels1);
1375	if (labels2 != NULL) CFRelease(labels2);
1376	return result;
1377}
1378
1379
1380__private_extern__
1381Boolean
1382dns_configuration_set(CFDictionaryRef   defaultResolver,
1383		      CFDictionaryRef   services,
1384		      CFArrayRef	serviceOrder,
1385		      CFArrayRef	multicastResolvers,
1386		      CFArrayRef	privateResolvers)
1387{
1388	dns_create_config_t	_config;
1389	Boolean			changed			= FALSE;
1390	uint32_t		dns_resolver_flags	= 0;
1391	CFIndex			i;
1392	CFMutableDictionaryRef	myDefault;
1393	Boolean			myOrderAdded		= FALSE;
1394	CFArrayRef		mySearchDomains		= NULL;
1395	CFIndex			n_resolvers;
1396	CFMutableArrayRef	resolvers;
1397	unsigned char		signature[CC_SHA1_DIGEST_LENGTH];
1398	static unsigned char	signature_last[CC_SHA1_DIGEST_LENGTH];
1399
1400	// establish list of resolvers
1401
1402	resolvers = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1403	assert(resolvers != NULL);
1404
1405	// collect (and add) any "supplemental" resolver configurations
1406
1407	add_supplemental_resolvers(resolvers, services, serviceOrder, NULL, NULL);
1408
1409	// collect (and add) any "private" resolver configurations
1410
1411	add_private_resolvers(resolvers, privateResolvers);
1412
1413	// add the "default" resolver
1414
1415	add_default_resolver(resolvers, defaultResolver, &myOrderAdded, &mySearchDomains);
1416
1417	// collect (and add) any "multicast" resolver configurations
1418
1419	add_multicast_resolvers(resolvers, multicastResolvers);
1420
1421	// collect (and add) any "scoped" resolver configurations
1422
1423	add_scoped_resolvers(resolvers, services, serviceOrder);
1424
1425	// collect (and add) any "service-specific" resolver configurations
1426
1427	add_service_specific_resolvers(resolvers, services);
1428
1429	// sort resolvers
1430
1431	n_resolvers = CFArrayGetCount(resolvers);
1432	if (n_resolvers > 1) {
1433		CFArraySortValues(resolvers, CFRangeMake(0, n_resolvers), compareDomain, NULL);
1434	}
1435
1436	// cleanup
1437
1438	for (i = n_resolvers; --i > 0; ) {
1439		CFDictionaryRef	resolver;
1440
1441		resolver = CFArrayGetValueAtIndex(resolvers, i);
1442		if (!CFDictionaryContainsKey(resolver, kSCPropNetDNSDomainName) &&
1443		    !CFDictionaryContainsKey(resolver, kSCPropNetDNSSearchDomains) &&
1444		    !CFDictionaryContainsKey(resolver, kSCPropNetDNSServerAddresses)) {
1445			// remove empty resolver
1446			CFArrayRemoveValueAtIndex(resolvers, i);
1447			n_resolvers--;
1448		}
1449	}
1450
1451	// update the default resolver
1452
1453	myDefault = CFDictionaryCreateMutableCopy(NULL,
1454						  0,
1455						  CFArrayGetValueAtIndex(resolvers, 0));
1456	if (mySearchDomains != NULL) {
1457		// add search domains to the default resolver
1458		CFDictionarySetValue(myDefault, kSCPropNetDNSSearchDomains, mySearchDomains);
1459		CFRelease(mySearchDomains);
1460	}
1461	if (myOrderAdded && (n_resolvers > 1)) {
1462		CFDictionaryRef	resolver;
1463
1464		resolver = CFArrayGetValueAtIndex(resolvers, 1);
1465		if (CFDictionaryContainsKey(resolver, kSCPropNetDNSDomainName) ||
1466		    isScopedConfiguration(resolver)) {
1467			// if not a supplemental "default" resolver (a domain name is
1468			// present) or if it's a scoped configuration
1469			CFDictionaryRemoveValue(myDefault, kSCPropNetDNSSearchOrder);
1470		}
1471	}
1472	CFArraySetValueAtIndex(resolvers, 0, myDefault);
1473	CFRelease(myDefault);
1474
1475	// establish resolver configuration
1476
1477	if ((defaultResolver == NULL) && (n_resolvers <= 1)) {
1478		/*
1479		 * if no default and no supplemental/scoped resolvers
1480		 */
1481		_config = NULL;
1482	} else {
1483		/*
1484		 * if default and/or supplemental/scoped resolvers are defined
1485		 */
1486		_config = _dns_configuration_create();
1487
1488		CFDictionaryApplyFunction(services, add_dns_query_flags , &dns_resolver_flags);
1489
1490		for (i = 0; i < n_resolvers; i++) {
1491			boolean_t		is_default_resolver;
1492			CFDictionaryRef		resolver;
1493			dns_create_resolver_t	_resolver;
1494
1495			resolver = CFArrayGetValueAtIndex(resolvers, i);
1496
1497			is_default_resolver = (!isScopedConfiguration(resolver) && !isServiceSpecificConfiguration(resolver));
1498			if (is_default_resolver) {
1499				CFMutableDictionaryRef	new_resolver;
1500				CFNumberRef		num;
1501
1502				new_resolver = CFDictionaryCreateMutableCopy(NULL, 0, resolver);
1503
1504				num = CFNumberCreate(NULL, kCFNumberSInt32Type, &dns_resolver_flags);
1505				CFDictionarySetValue(new_resolver, DNS_CONFIGURATION_FLAGS_KEY, num);
1506				CFRelease(num);
1507
1508				resolver = new_resolver;
1509			}
1510
1511			_resolver = create_resolver(resolver);
1512			_dns_configuration_add_resolver(&_config, _resolver);
1513			_dns_resolver_free(&_resolver);
1514
1515			if (is_default_resolver) {
1516				CFRelease(resolver);
1517			}
1518		}
1519
1520#if	!TARGET_OS_IPHONE
1521		// add flatfile resolvers
1522
1523		_dnsinfo_flatfile_set_flags(dns_resolver_flags);
1524		_dnsinfo_flatfile_add_resolvers(&_config);
1525#endif	// !TARGET_OS_IPHONE
1526	}
1527
1528#ifdef	DNS_CONFIGURATION_DEBUG
1529	{
1530		uint8_t			*buf;
1531		dns_config_t		*config;
1532		_dns_config_buf_t	*config_buf;
1533		uint32_t		n_config;
1534		uint32_t		n_padding;
1535
1536		config_buf = (_dns_config_buf_t *)_config;
1537		n_config  = sizeof(_dns_config_buf_t) + ntohl(config_buf->n_attribute);
1538		n_padding = ntohl(config_buf->n_padding);
1539		buf = malloc(n_config + n_padding);
1540		bcopy((void *)config_buf, buf, n_config);
1541		bzero(&buf[n_config], n_padding);
1542		config = expand_config((_dns_config_buf_t *)buf);
1543		_dns_configuration_print(config);
1544		free(buf);
1545	}
1546#endif	// DNS_CONFIGURATION_DEBUG
1547
1548	// check if the configuration changed
1549	_dns_configuration_signature(&_config, signature, sizeof(signature));
1550	if (bcmp(signature, signature_last, sizeof(signature)) != 0) {
1551		// save [new] signature
1552		bcopy(signature, signature_last, sizeof(signature));
1553
1554		// save [new] configuration
1555		if (!_dns_configuration_store(&_config)) {
1556			my_log(LOG_ERR, "dns_configuration_set: could not store configuration");
1557		}
1558
1559		changed = TRUE;
1560	}
1561
1562	if (_config != NULL) _dns_configuration_free(&_config);
1563
1564	CFRelease(resolvers);
1565	return changed;
1566}
1567
1568
1569#if	!TARGET_OS_IPHONE
1570static SCDynamicStoreRef	dns_configuration_store;
1571static SCDynamicStoreCallBack	dns_configuration_callout;
1572
1573static void
1574dns_configuration_changed(CFMachPortRef port, void *msg, CFIndex size, void *info)
1575{
1576	CFStringRef	key		= CFSTR(_PATH_RESOLVER_DIR);
1577	CFArrayRef	keys;
1578	Boolean		resolvers_now;
1579	static Boolean	resolvers_save	= FALSE;
1580	struct stat	statbuf;
1581
1582	resolvers_now = (stat(_PATH_RESOLVER_DIR, &statbuf) == 0);
1583	if (!resolvers_save && (resolvers_save == resolvers_now)) {
1584		// if we did not (and still do not) have an "/etc/resolvers"
1585		// directory than this notification is the result of a change
1586		// to the "/etc" directory.
1587		return;
1588	}
1589	resolvers_save = resolvers_now;
1590
1591	my_log(LOG_DEBUG, _PATH_RESOLVER_DIR " changed");
1592
1593	// fake a "DNS" change
1594	keys = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks);
1595	(*dns_configuration_callout)(dns_configuration_store, keys, NULL);
1596	CFRelease(keys);
1597	return;
1598}
1599
1600
1601__private_extern__
1602void
1603dns_configuration_monitor(SCDynamicStoreRef store, SCDynamicStoreCallBack callout)
1604{
1605	CFMachPortRef		mp;
1606	mach_port_t		notify_port;
1607	int			notify_token;
1608	CFRunLoopSourceRef	rls;
1609	uint32_t		status;
1610
1611	dns_configuration_store   = store;
1612	dns_configuration_callout = callout;
1613
1614	status = notify_register_mach_port(_PATH_RESOLVER_DIR, &notify_port, 0, &notify_token);
1615	if (status != NOTIFY_STATUS_OK) {
1616		my_log(LOG_ERR, "notify_register_mach_port() failed");
1617		return;
1618	}
1619
1620	status = notify_monitor_file(notify_token, "/private" _PATH_RESOLVER_DIR, 0);
1621	if (status != NOTIFY_STATUS_OK) {
1622		my_log(LOG_ERR, "notify_monitor_file() failed");
1623		(void)notify_cancel(notify_token);
1624		return;
1625	}
1626
1627	mp = _SC_CFMachPortCreateWithPort("IPMonitor/dns_configuration",
1628					  notify_port,
1629					  dns_configuration_changed,
1630					  NULL);
1631
1632	rls = CFMachPortCreateRunLoopSource(NULL, mp, -1);
1633	if (rls == NULL) {
1634		my_log(LOG_ERR, "SCDynamicStoreCreateRunLoopSource() failed");
1635		CFRelease(mp);
1636		(void)notify_cancel(notify_token);
1637		return;
1638	}
1639	CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
1640	CFRelease(rls);
1641
1642	CFRelease(mp);
1643	return;
1644}
1645#endif	// !TARGET_OS_IPHONE
1646
1647
1648__private_extern__
1649void
1650dns_configuration_init(CFBundleRef bundle)
1651{
1652	CFDictionaryRef	dict;
1653
1654	dict = CFBundleGetInfoDictionary(bundle);
1655	if (isA_CFDictionary(dict)) {
1656		S_mdns_timeout = CFDictionaryGetValue(dict, CFSTR("mdns_timeout"));
1657		S_mdns_timeout = isA_CFNumber(S_mdns_timeout);
1658
1659		S_pdns_timeout = CFDictionaryGetValue(dict, CFSTR("pdns_timeout"));
1660		S_pdns_timeout = isA_CFNumber(S_pdns_timeout);
1661	}
1662
1663	return;
1664}
1665
1666
1667#pragma mark -
1668#pragma mark Standalone test code
1669
1670
1671#ifdef	MAIN
1672
1673static void
1674split(const void * key, const void * value, void * context)
1675{
1676	CFArrayRef		components;
1677	CFStringRef		entity_id;
1678	CFStringRef		service_id;
1679	CFMutableDictionaryRef	state_dict;
1680
1681	components = CFStringCreateArrayBySeparatingStrings(NULL, (CFStringRef)key, CFSTR("/"));
1682	service_id = CFArrayGetValueAtIndex(components, 3);
1683	entity_id  = CFArrayGetValueAtIndex(components, 4);
1684	state_dict = (CFMutableDictionaryRef)CFDictionaryGetValue(context, service_id);
1685	if (state_dict != NULL) {
1686		state_dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
1687	} else {
1688		state_dict = CFDictionaryCreateMutable(NULL,
1689						       0,
1690						       &kCFTypeDictionaryKeyCallBacks,
1691						       &kCFTypeDictionaryValueCallBacks);
1692	}
1693
1694	if (CFEqual(entity_id, kSCEntNetIPv4) ||
1695	    CFEqual(entity_id, kSCEntNetIPv6)) {
1696		CFDictionaryRef		dict;
1697		CFStringRef		interface;
1698
1699		if (CFEqual(entity_id, kSCEntNetIPv4)) {
1700			dict = ipv4_dict_create(value);
1701		}
1702		else {
1703			dict = ipv6_dict_create(value);
1704		}
1705		if (dict != NULL) {
1706			CFDictionarySetValue(state_dict, entity_id, dict);
1707		}
1708
1709		interface = CFDictionaryGetValue((CFDictionaryRef)value, kSCPropInterfaceName);
1710		if (interface != NULL) {
1711			CFDictionaryRef		dns;
1712			CFMutableDictionaryRef	new_dns;
1713
1714			dns = CFDictionaryGetValue(state_dict, kSCEntNetDNS);
1715			if (dns != NULL) {
1716				new_dns = CFDictionaryCreateMutableCopy(NULL, 0, dns);
1717			} else {
1718				new_dns = CFDictionaryCreateMutable(NULL,
1719								    0,
1720								    &kCFTypeDictionaryKeyCallBacks,
1721								    &kCFTypeDictionaryValueCallBacks);
1722			}
1723			CFDictionarySetValue(new_dns, kSCPropInterfaceName, interface);
1724			CFDictionarySetValue(state_dict, kSCEntNetDNS, new_dns);
1725			CFRelease(new_dns);
1726		}
1727	} else if (CFEqual(entity_id, kSCEntNetDNS)) {
1728		CFDictionaryRef	dns;
1729
1730		dns = CFDictionaryGetValue(state_dict, kSCEntNetDNS);
1731		if (dns != NULL) {
1732			CFStringRef	interface;
1733
1734			interface = CFDictionaryGetValue(dns, kSCPropInterfaceName);
1735			if (interface != NULL) {
1736				CFMutableDictionaryRef	new_dns;
1737
1738				new_dns = CFDictionaryCreateMutableCopy(NULL, 0, (CFDictionaryRef)value);
1739				CFDictionarySetValue(new_dns, kSCPropInterfaceName, interface);
1740				CFDictionarySetValue(state_dict, kSCEntNetDNS, new_dns);
1741				CFRelease(new_dns);
1742			} else {
1743				CFDictionarySetValue(state_dict, kSCEntNetDNS, (CFDictionaryRef)value);
1744			}
1745		} else {
1746			CFDictionarySetValue(state_dict, kSCEntNetDNS, (CFDictionaryRef)value);
1747		}
1748	} else {
1749		CFDictionarySetValue(state_dict, entity_id, (CFDictionaryRef)value);
1750	}
1751
1752	CFDictionarySetValue((CFMutableDictionaryRef)context, service_id, state_dict);
1753	CFRelease(state_dict);
1754	CFRelease(components);
1755
1756	return;
1757}
1758
1759int
1760main(int argc, char **argv)
1761{
1762	CFDictionaryRef		entities;
1763	CFStringRef		key;
1764	CFArrayRef		multicast_resolvers;
1765	CFStringRef		pattern;
1766	CFMutableArrayRef	patterns;
1767	CFStringRef		primary		= NULL;
1768	CFDictionaryRef		primaryDNS	= NULL;
1769	CFArrayRef		private_resolvers;
1770	CFArrayRef		service_order	= NULL;
1771	CFMutableDictionaryRef	service_state_dict;
1772	CFDictionaryRef		setup_global_ipv4;
1773	CFDictionaryRef		state_global_ipv4;
1774	SCDynamicStoreRef	store;
1775
1776	_sc_log     = FALSE;
1777	_sc_verbose = (argc > 1) ? TRUE : FALSE;
1778
1779	store = SCDynamicStoreCreate(NULL, CFSTR("TEST"), NULL, NULL);
1780
1781	// get IPv4, IPv6, and DNS entities
1782	patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1783	pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
1784							      kSCDynamicStoreDomainState,
1785							      kSCCompAnyRegex,
1786							      kSCEntNetIPv4);
1787	CFArrayAppendValue(patterns, pattern);
1788	CFRelease(pattern);
1789	pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
1790							      kSCDynamicStoreDomainState,
1791							      kSCCompAnyRegex,
1792							      kSCEntNetIPv6);
1793	CFArrayAppendValue(patterns, pattern);
1794	CFRelease(pattern);
1795	pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
1796							      kSCDynamicStoreDomainState,
1797							      kSCCompAnyRegex,
1798							      kSCEntNetDNS);
1799	CFArrayAppendValue(patterns, pattern);
1800	CFRelease(pattern);
1801	entities = SCDynamicStoreCopyMultiple(store, NULL, patterns);
1802	CFRelease(patterns);
1803
1804	service_state_dict = CFDictionaryCreateMutable(NULL,
1805						       0,
1806						       &kCFTypeDictionaryKeyCallBacks,
1807						       &kCFTypeDictionaryValueCallBacks);
1808	CFDictionaryApplyFunction(entities, split, service_state_dict);
1809	CFRelease(entities);
1810
1811	// get primary service ID
1812	key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
1813							 kSCDynamicStoreDomainState,
1814							 kSCEntNetIPv4);
1815	state_global_ipv4 = SCDynamicStoreCopyValue(store, key);
1816	CFRelease(key);
1817	if (state_global_ipv4 != NULL) {
1818		primary = CFDictionaryGetValue(state_global_ipv4, kSCDynamicStorePropNetPrimaryService);
1819		if (primary != NULL) {
1820			CFDictionaryRef	service_dict;
1821
1822			// get DNS configuration for primary service
1823			service_dict = CFDictionaryGetValue(service_state_dict, primary);
1824			if (service_dict != NULL) {
1825				primaryDNS = CFDictionaryGetValue(service_dict, kSCEntNetDNS);
1826			}
1827		}
1828	}
1829
1830	// get serviceOrder
1831	key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
1832							 kSCDynamicStoreDomainSetup,
1833							 kSCEntNetIPv4);
1834	setup_global_ipv4 = SCDynamicStoreCopyValue(store, key);
1835	CFRelease(key);
1836	if (setup_global_ipv4 != NULL) {
1837		service_order = CFDictionaryGetValue(setup_global_ipv4, kSCPropNetServiceOrder);
1838	}
1839
1840	// get multicast resolvers
1841	key = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
1842				      kSCDynamicStoreDomainState,
1843				      kSCCompNetwork,
1844				      CFSTR(kDNSServiceCompMulticastDNS));
1845	multicast_resolvers = SCDynamicStoreCopyValue(store, key);
1846	CFRelease(key);
1847
1848	// get private resolvers
1849	key = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
1850				      kSCDynamicStoreDomainState,
1851				      kSCCompNetwork,
1852				      CFSTR(kDNSServiceCompPrivateDNS));
1853	private_resolvers = SCDynamicStoreCopyValue(store, key);
1854	CFRelease(key);
1855
1856	// update DNS configuration
1857	dns_configuration_init(CFBundleGetMainBundle());
1858	(void)dns_configuration_set(primaryDNS,
1859				    service_state_dict,
1860				    service_order,
1861				    multicast_resolvers,
1862				    private_resolvers);
1863
1864	// cleanup
1865	if (setup_global_ipv4 != NULL)	CFRelease(setup_global_ipv4);
1866	if (state_global_ipv4 != NULL)	CFRelease(state_global_ipv4);
1867	if (multicast_resolvers != NULL) CFRelease(multicast_resolvers);
1868	if (private_resolvers != NULL)	CFRelease(private_resolvers);
1869	CFRelease(service_state_dict);
1870	CFRelease(store);
1871
1872	/* not reached */
1873	exit(0);
1874	return 0;
1875}
1876#endif
1877
1878