1/*
2 * Copyright (c) 2011-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 * January 3, 2011	Allan Nathanson <ajn@apple.com>
28 * - initial revision
29 */
30
31#include <TargetConditionals.h>
32#include <sys/types.h>
33#include <sys/socket.h>
34#include <net/if.h>
35
36#include <CoreFoundation/CoreFoundation.h>
37#include <SystemConfiguration/SystemConfiguration.h>
38#include <SystemConfiguration/SCPrivate.h>
39#include <SystemConfiguration/SCValidation.h>
40#include "ip_plugin.h"
41
42
43#define DEFAULT_MATCH_ORDER    200000   /* match order for the "default" proxy configuration */
44
45
46#define PROXY_MATCH_ORDER_KEY	CFSTR("__MATCH_ORDER__")
47#define ORDER_KEY		CFSTR("__ORDER__")
48
49
50CFBooleanRef	G_supplemental_proxies_follow_dns	= NULL;
51
52
53static void
54add_proxy(CFMutableArrayRef proxies, CFMutableDictionaryRef proxy)
55{
56	CFIndex		i;
57	CFIndex		n_proxies;
58	CFNumberRef	order;
59
60	n_proxies = CFArrayGetCount(proxies);
61	for (i = 0; i < n_proxies; i++) {
62		CFDictionaryRef		match_proxy;
63
64		match_proxy = CFArrayGetValueAtIndex(proxies, i);
65		if (CFEqual(proxy, match_proxy)) {
66			// a real duplicate
67			return;
68		}
69	}
70
71	order = CFNumberCreate(NULL, kCFNumberCFIndexType, &n_proxies);
72	CFDictionarySetValue(proxy, ORDER_KEY, order);
73	CFRelease(order);
74
75	CFArrayAppendValue(proxies, proxy);
76	return;
77}
78
79
80static void
81add_supplemental(CFMutableArrayRef proxies, CFDictionaryRef proxy, uint32_t defaultOrder)
82{
83	CFArrayRef	domains;
84	CFIndex		i;
85	CFIndex		n_domains;
86	CFArrayRef	orders;
87
88	domains = CFDictionaryGetValue(proxy, kSCPropNetProxiesSupplementalMatchDomains);
89	n_domains = isA_CFArray(domains) ? CFArrayGetCount(domains) : 0;
90	if (n_domains == 0) {
91		return;
92	}
93
94	orders = CFDictionaryGetValue(proxy, kSCPropNetProxiesSupplementalMatchOrders);
95	if (orders != NULL) {
96		if (!isA_CFArray(orders) || (n_domains != CFArrayGetCount(orders))) {
97			return;
98		}
99	}
100
101	/*
102	 * yes, this is a "supplemental" proxy configuration, expand
103	 * the match domains and add each to the proxies list.
104	 */
105	for (i = 0; i < n_domains; i++) {
106		CFStringRef		match_domain;
107		CFNumberRef		match_order;
108		CFMutableDictionaryRef	match_proxy;
109
110		match_domain = CFArrayGetValueAtIndex(domains, i);
111		if (!isA_CFString(match_domain)) {
112			continue;
113		}
114
115		match_proxy = CFDictionaryCreateMutableCopy(NULL, 0, proxy);
116
117		// set supplemental proxy match "domain"
118		match_domain = _SC_trimDomain(match_domain);
119		if (match_domain != NULL) {
120			CFDictionarySetValue(match_proxy, kSCPropNetProxiesSupplementalMatchDomain, match_domain);
121			CFRelease(match_domain);
122		} else {
123			CFDictionaryRemoveValue(match_proxy, kSCPropNetProxiesSupplementalMatchDomain);
124		}
125
126		// set supplemental proxy match "order"
127		match_order = (orders != NULL) ? CFArrayGetValueAtIndex(orders, i) : NULL;
128		if (isA_CFNumber(match_order)) {
129			CFDictionarySetValue(match_proxy, PROXY_MATCH_ORDER_KEY, match_order);
130		} else {
131			CFNumberRef     num;
132
133			num = CFNumberCreate(NULL, kCFNumberIntType, &defaultOrder);
134			CFDictionarySetValue(match_proxy, PROXY_MATCH_ORDER_KEY, num);
135			CFRelease(num);
136
137			defaultOrder++;		// if multiple domains, maintain ordering
138		}
139
140		// remove keys we don't want in a supplemental proxy
141		CFDictionaryRemoveValue(match_proxy, kSCPropNetProxiesSupplementalMatchDomains);
142		CFDictionaryRemoveValue(match_proxy, kSCPropNetProxiesSupplementalMatchOrders);
143		CFDictionaryRemoveValue(match_proxy, kSCPropInterfaceName);
144
145		add_proxy(proxies, match_proxy);
146		CFRelease(match_proxy);
147	}
148
149	return;
150}
151
152
153#define	N_QUICK	32
154
155
156static void
157add_supplemental_proxies(CFMutableArrayRef proxies, CFDictionaryRef services, CFArrayRef service_order)
158{
159	const void *		keys_q[N_QUICK];
160	const void **		keys	= keys_q;
161	CFIndex			i;
162	CFIndex			n_order;
163	CFIndex			n_services;
164	const void *		vals_q[N_QUICK];
165	const void **		vals	= vals_q;
166
167	n_services = isA_CFDictionary(services) ? CFDictionaryGetCount(services) : 0;
168	if (n_services == 0) {
169		return;		// if no services
170	}
171
172	if (n_services > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
173		keys = CFAllocatorAllocate(NULL, n_services * sizeof(CFTypeRef), 0);
174		vals = CFAllocatorAllocate(NULL, n_services * sizeof(CFTypeRef), 0);
175	}
176
177	n_order = isA_CFArray(service_order) ? CFArrayGetCount(service_order) : 0;
178
179	CFDictionaryGetKeysAndValues(services, keys, vals);
180	for (i = 0; i < n_services; i++) {
181		uint32_t		defaultOrder;
182		CFDictionaryRef		proxy;
183		CFMutableDictionaryRef	proxyWithDNS	= NULL;
184		CFDictionaryRef		service		= (CFDictionaryRef)vals[i];
185
186		if (!isA_CFDictionary(service)) {
187			continue;
188		}
189
190		proxy = CFDictionaryGetValue(service, kSCEntNetProxies);
191		if (!isA_CFDictionary(proxy)) {
192			continue;
193		}
194
195		if ((G_supplemental_proxies_follow_dns != NULL) && CFBooleanGetValue(G_supplemental_proxies_follow_dns)) {
196			CFDictionaryRef	dns;
197			CFArrayRef	matchDomains;
198			CFArrayRef	matchOrders;
199
200			if (!CFDictionaryContainsKey(proxy, kSCPropNetProxiesSupplementalMatchDomains) &&
201			    CFDictionaryGetValueIfPresent(service, kSCEntNetDNS, (const void **)&dns) &&
202			    isA_CFDictionary(dns) &&
203			    CFDictionaryGetValueIfPresent(dns, kSCPropNetDNSSupplementalMatchDomains, (const void **)&matchDomains) &&
204			    isA_CFArray(matchDomains)) {
205				proxyWithDNS = CFDictionaryCreateMutableCopy(NULL, 0, proxy);
206				CFDictionarySetValue(proxyWithDNS, kSCPropNetProxiesSupplementalMatchDomains, matchDomains);
207				if (CFDictionaryGetValueIfPresent(dns, kSCPropNetDNSSupplementalMatchOrders, (const void **)&matchOrders) &&
208				    isA_CFArray(matchOrders)) {
209					CFDictionarySetValue(proxyWithDNS, kSCPropNetProxiesSupplementalMatchOrders, matchOrders);
210				} else {
211					CFDictionaryRemoveValue(proxyWithDNS, kSCPropNetProxiesSupplementalMatchOrders);
212				}
213				proxy = proxyWithDNS;
214			}
215		}
216
217		defaultOrder = DEFAULT_MATCH_ORDER
218			       - (DEFAULT_MATCH_ORDER / 2)
219			       + ((DEFAULT_MATCH_ORDER / 1000) * (uint32_t)i);
220		if ((n_order > 0) &&
221		    !CFArrayContainsValue(service_order, CFRangeMake(0, n_order), keys[i])) {
222			// push out services not specified in service order
223			defaultOrder += (DEFAULT_MATCH_ORDER / 1000) * n_services;
224		}
225
226		add_supplemental(proxies, proxy, defaultOrder);
227		if (proxyWithDNS != NULL) CFRelease(proxyWithDNS);
228	}
229
230	if (keys != keys_q) {
231		CFAllocatorDeallocate(NULL, keys);
232		CFAllocatorDeallocate(NULL, vals);
233	}
234
235	return;
236}
237
238
239static CFComparisonResult
240compareBySearchOrder(const void *val1, const void *val2, void *context)
241{
242	CFDictionaryRef	proxy1	= (CFDictionaryRef)val1;
243	CFDictionaryRef	proxy2	= (CFDictionaryRef)val2;
244	CFNumberRef	num1;
245	CFNumberRef	num2;
246	uint32_t	order1	= DEFAULT_MATCH_ORDER;
247	uint32_t	order2	= DEFAULT_MATCH_ORDER;
248
249	num1 = CFDictionaryGetValue(proxy1, PROXY_MATCH_ORDER_KEY);
250	if (!isA_CFNumber(num1) ||
251	    !CFNumberGetValue(num1, kCFNumberIntType, &order1)) {
252		order1 = DEFAULT_MATCH_ORDER;
253	}
254
255	num2 = CFDictionaryGetValue(proxy2, PROXY_MATCH_ORDER_KEY);
256	if (!isA_CFNumber(num2) ||
257	    !CFNumberGetValue(num2, kCFNumberIntType, &order2)) {
258		order2 = DEFAULT_MATCH_ORDER;
259	}
260
261	if (order1 == order2) {
262		// if same match "order", retain original ordering for configurations
263		if (CFDictionaryGetValueIfPresent(proxy1, ORDER_KEY, (const void **)&num1) &&
264		    CFDictionaryGetValueIfPresent(proxy2, ORDER_KEY, (const void **)&num2) &&
265		    isA_CFNumber(num1) &&
266		    isA_CFNumber(num2) &&
267		    CFNumberGetValue(num1, kCFNumberIntType, &order1) &&
268		    CFNumberGetValue(num2, kCFNumberIntType, &order2)) {
269			if (order1 == order2) {
270				return kCFCompareEqualTo;
271			} else {
272				return (order1 < order2) ? kCFCompareLessThan : kCFCompareGreaterThan;
273			}
274		}
275
276		return kCFCompareEqualTo;
277	}
278
279	return (order1 < order2) ? kCFCompareLessThan : kCFCompareGreaterThan;
280}
281
282
283static __inline__ Boolean
284isSupplementalProxy(CFDictionaryRef proxy)
285{
286	if ((proxy != NULL) &&
287	    CFDictionaryContainsKey(proxy, kSCPropNetProxiesSupplementalMatchDomain)) {
288		return TRUE;
289	}
290
291	return FALSE;
292}
293
294
295static CFArrayRef
296copy_supplemental_proxies(CFArrayRef proxies, Boolean skip)
297{
298	CFIndex			i;
299	CFIndex			n_proxies;
300	CFMutableArrayRef	supplemental	= NULL;
301
302	// iterate over services
303
304	n_proxies = isA_CFArray(proxies) ? CFArrayGetCount(proxies) : 0;
305	for (i = 0; i < n_proxies; i++) {
306		CFDictionaryRef		proxy;
307
308		proxy = CFArrayGetValueAtIndex(proxies, i);
309		if (!isSupplementalProxy(proxy)) {
310			// if not supplemental proxy (i.e. no match domain)
311			continue;
312		}
313
314		// add [supplemental] proxy entry
315		if (supplemental == NULL) {
316			supplemental = CFArrayCreateMutable(NULL,
317							    0,
318							    &kCFTypeArrayCallBacks);
319		}
320		CFArrayAppendValue(supplemental, proxy);
321	}
322
323	return supplemental;
324}
325
326
327static CFArrayRef
328service_order_copy_all(CFDictionaryRef services, CFArrayRef service_order)
329{
330	const void *		keys_q[N_QUICK];
331	const void **		keys	= keys_q;
332	CFIndex			i;
333	CFIndex			n_order;
334	CFIndex			n_services;
335	CFMutableArrayRef	order;
336
337	// ensure that we process all services in order
338	n_services = isA_CFDictionary(services) ? CFDictionaryGetCount(services) : 0;
339	if (n_services == 0) {
340		// if no services
341		return NULL;
342	}
343
344	// ensure that we process all services in order
345
346	n_order = isA_CFArray(service_order) ? CFArrayGetCount(service_order) : 0;
347	if (n_order > 0) {
348		order = CFArrayCreateMutableCopy(NULL, 0, service_order);
349	} else {
350		order = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
351	}
352
353	if (n_services > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
354		keys = CFAllocatorAllocate(NULL, n_services * sizeof(CFTypeRef), 0);
355	}
356	CFDictionaryGetKeysAndValues(services, keys, NULL);
357	for (i = 0; i < n_services; i++) {
358		CFStringRef	serviceID	= (CFStringRef)keys[i];
359
360		if (!CFArrayContainsValue(order, CFRangeMake(0, n_order), serviceID)) {
361			CFArrayAppendValue(order, serviceID);
362			n_order++;
363		}
364	}
365	if (keys != keys_q) {
366		CFAllocatorDeallocate(NULL, keys);
367	}
368
369	return order;
370}
371
372
373static CFDictionaryRef
374copy_app_layer_vpn_proxies(CFDictionaryRef services, CFArrayRef order, CFDictionaryRef services_info)
375{
376	CFMutableDictionaryRef	app_layer_proxies	= NULL;
377	CFIndex			i;
378	CFIndex			n_order;
379
380	if (!isA_CFDictionary(services_info)) {
381		return NULL;
382	}
383
384	// iterate over services
385
386	n_order = isA_CFArray(order) ? CFArrayGetCount(order) : 0;
387	for (i = 0; i < n_order; i++) {
388		CFMutableDictionaryRef	newProxy;
389		CFDictionaryRef		proxy;
390		CFDictionaryRef		service;
391		CFStringRef		serviceID;
392		CFDictionaryRef		vpn;
393		CFStringRef		vpn_key;
394
395		serviceID = CFArrayGetValueAtIndex(order, i);
396		service = CFDictionaryGetValue(services, serviceID);
397		if (!isA_CFDictionary(service)) {
398			// if no service
399			continue;
400		}
401
402		proxy = CFDictionaryGetValue(service, kSCEntNetProxies);
403		if (!isA_CFDictionary(proxy)) {
404			// if no proxy
405			continue;
406		}
407
408		vpn_key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
409								      kSCDynamicStoreDomainSetup,
410								      serviceID,
411								      kSCEntNetVPN);
412		vpn = CFDictionaryGetValue(services_info, vpn_key);
413		CFRelease(vpn_key);
414
415		if (!isA_CFDictionary(vpn) || !CFDictionaryContainsKey(vpn, kSCPropNetVPNAppRules)) {
416			// if not App Layer vpn
417			continue;
418		}
419
420		if ((app_layer_proxies != NULL) &&
421		    CFDictionaryContainsKey(app_layer_proxies, serviceID)) {
422			// if we've already processed this [app_layer_proxies] interface
423			continue;
424		}
425
426		// add [app_layer_proxies] proxy entry
427		newProxy = CFDictionaryCreateMutableCopy(NULL, 0, proxy);
428		CFDictionaryRemoveValue(newProxy, kSCPropNetProxiesSupplementalMatchDomains);
429		CFDictionaryRemoveValue(newProxy, kSCPropNetProxiesSupplementalMatchOrders);
430		if (app_layer_proxies == NULL) {
431			app_layer_proxies = CFDictionaryCreateMutable(NULL,
432								      0,
433								      &kCFTypeDictionaryKeyCallBacks,
434								      &kCFTypeDictionaryValueCallBacks);
435		}
436		CFDictionarySetValue(app_layer_proxies, serviceID, newProxy);
437		CFRelease(newProxy);
438	}
439
440	return app_layer_proxies;
441}
442
443
444static CFDictionaryRef
445copy_scoped_proxies(CFDictionaryRef services, CFArrayRef order)
446{
447	CFIndex			i;
448	CFIndex			n_order;
449	CFMutableDictionaryRef	scoped	= NULL;
450
451	// iterate over services
452
453	n_order = isA_CFArray(order) ? CFArrayGetCount(order) : 0;
454	for (i = 0; i < n_order; i++) {
455		char			if_name[IF_NAMESIZE];
456		CFStringRef		interface;
457		CFMutableDictionaryRef	newProxy;
458		CFDictionaryRef		proxy;
459		CFDictionaryRef		service;
460		CFStringRef		serviceID;
461
462		serviceID = CFArrayGetValueAtIndex(order, i);
463		service = CFDictionaryGetValue(services, serviceID);
464		if (!isA_CFDictionary(service)) {
465			// if no service
466			continue;
467		}
468
469		proxy = CFDictionaryGetValue(service, kSCEntNetProxies);
470		if (!isA_CFDictionary(proxy)) {
471			// if no proxy
472			continue;
473		}
474
475		interface = CFDictionaryGetValue(proxy, kSCPropInterfaceName);
476		if (interface == NULL) {
477			// if no [scoped] interface
478			continue;
479		}
480		if ((scoped != NULL) &&
481		    CFDictionaryContainsKey(scoped, interface)) {
482			// if we've already processed this [scoped] interface
483			continue;
484		}
485
486		if ((_SC_cfstring_to_cstring(interface,
487					     if_name,
488					     sizeof(if_name),
489					     kCFStringEncodingASCII) == NULL) ||
490		    ((my_if_nametoindex(if_name)) == 0)) {
491			// if interface index not available
492			continue;
493		}
494
495		// add [scoped] proxy entry
496		// ... and remove keys we don't want in a [scoped] proxy
497		CFRetain(interface);
498		newProxy = CFDictionaryCreateMutableCopy(NULL, 0, proxy);
499		CFDictionaryRemoveValue(newProxy, kSCPropNetProxiesSupplementalMatchDomains);
500		CFDictionaryRemoveValue(newProxy, kSCPropNetProxiesSupplementalMatchOrders);
501		CFDictionaryRemoveValue(newProxy, kSCPropInterfaceName);
502		if (scoped == NULL) {
503			scoped = CFDictionaryCreateMutable(NULL,
504							   0,
505							   &kCFTypeDictionaryKeyCallBacks,
506							   &kCFTypeDictionaryValueCallBacks);
507		}
508		CFDictionarySetValue(scoped, interface, newProxy);
509		CFRelease(newProxy);
510		CFRelease(interface);
511	}
512
513	return scoped;
514}
515
516
517static void
518add_default_proxy(CFMutableArrayRef	proxies,
519		  CFDictionaryRef	defaultProxy,
520		  Boolean		*orderAdded)
521{
522	CFMutableDictionaryRef	myDefault;
523	uint32_t		myOrder	= DEFAULT_MATCH_ORDER;
524	CFNumberRef		order	= NULL;
525
526	if (defaultProxy == NULL) {
527		myDefault = CFDictionaryCreateMutable(NULL,
528						      0,
529						      &kCFTypeDictionaryKeyCallBacks,
530						      &kCFTypeDictionaryValueCallBacks);
531	} else {
532		myDefault = CFDictionaryCreateMutableCopy(NULL, 0, defaultProxy);
533		CFDictionaryRemoveValue(myDefault, kSCPropInterfaceName);
534		order = CFDictionaryGetValue(myDefault, PROXY_MATCH_ORDER_KEY);
535	}
536
537	// ensure that the default proxy has a search order
538
539	if (!isA_CFNumber(order) ||
540	    !CFNumberGetValue(order, kCFNumberIntType, &myOrder)) {
541		myOrder = DEFAULT_MATCH_ORDER;
542		order = CFNumberCreate(NULL, kCFNumberIntType, &myOrder);
543		CFDictionarySetValue(myDefault, PROXY_MATCH_ORDER_KEY, order);
544		CFRelease(order);
545		*orderAdded = TRUE;
546	}
547
548	// add the default proxy
549
550	add_proxy(proxies, myDefault);
551	CFRelease(myDefault);
552	return;
553}
554
555
556static CFComparisonResult
557compareDomain(const void *val1, const void *val2, void *context)
558{
559	CFDictionaryRef		proxy1	= (CFDictionaryRef)val1;
560	CFDictionaryRef		proxy2	= (CFDictionaryRef)val2;
561	CFStringRef		domain1;
562	CFStringRef		domain2;
563	CFArrayRef		labels1	= NULL;
564	CFArrayRef		labels2	= NULL;
565	CFIndex			n1;
566	CFIndex			n2;
567	CFComparisonResult	result;
568	Boolean			rev1;
569	Boolean			rev2;
570
571	// "default" domains sort before "supplemental" domains
572	domain1 = CFDictionaryGetValue(proxy1, kSCPropNetProxiesSupplementalMatchDomain);
573	domain2 = CFDictionaryGetValue(proxy2, kSCPropNetProxiesSupplementalMatchDomain);
574	if (domain1 == NULL) {
575		if (domain2 == NULL) {
576			return kCFCompareEqualTo;
577		}
578		return kCFCompareLessThan;
579	} else if (domain2 == NULL) {
580		return kCFCompareGreaterThan;
581	}
582
583	// forward (A, AAAA) domains sort before reverse (PTR) domains
584	rev1 = CFStringHasSuffix(domain1, CFSTR(".arpa"));
585	rev2 = CFStringHasSuffix(domain2, CFSTR(".arpa"));
586	if (rev1 != rev2) {
587		if (rev1) {
588			return kCFCompareGreaterThan;
589		} else {
590			return kCFCompareLessThan;
591		}
592	}
593
594	labels1 = CFStringCreateArrayBySeparatingStrings(NULL, domain1, CFSTR("."));
595	n1 = CFArrayGetCount(labels1);
596
597	labels2 = CFStringCreateArrayBySeparatingStrings(NULL, domain2, CFSTR("."));
598	n2 = CFArrayGetCount(labels2);
599
600	while ((n1 > 0) && (n2 > 0)) {
601		CFStringRef	label1	= CFArrayGetValueAtIndex(labels1, --n1);
602		CFStringRef	label2	= CFArrayGetValueAtIndex(labels2, --n2);
603
604		// compare domain labels
605		result = CFStringCompare(label1, label2, kCFCompareCaseInsensitive);
606		if (result != kCFCompareEqualTo) {
607			goto done;
608		}
609	}
610
611	// longer labels (corp.apple.com) sort before shorter labels (apple.com)
612	if (n1 > n2) {
613		result = kCFCompareLessThan;
614		goto done;
615	} else if (n1 < n2) {
616		result = kCFCompareGreaterThan;
617		goto done;
618	}
619
620	// sort by search order
621	result = compareBySearchOrder(val1, val2, context);
622
623    done :
624
625	if (labels1 != NULL) CFRelease(labels1);
626	if (labels2 != NULL) CFRelease(labels2);
627	return result;
628}
629
630
631__private_extern__
632CF_RETURNS_RETAINED CFDictionaryRef
633proxy_configuration_update(CFDictionaryRef	defaultProxy,
634			   CFDictionaryRef	services,
635			   CFArrayRef		serviceOrder,
636			   CFDictionaryRef	servicesInfo)
637{
638	CFIndex			i;
639	CFMutableDictionaryRef	myDefault;
640	Boolean			myOrderAdded	= FALSE;
641	CFMutableDictionaryRef	newProxy	= NULL;
642	CFIndex			n_proxies;
643	CFDictionaryRef		proxy;
644	CFMutableArrayRef	proxies;
645
646	// establish full list of proxies
647
648	proxies = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
649
650	// collect (and add) any "supplemental" proxy configurations
651
652	add_supplemental_proxies(proxies, services, serviceOrder);
653
654	// add the "default" proxy
655
656	add_default_proxy(proxies, defaultProxy, &myOrderAdded);
657
658	// sort proxies, cleanup
659
660	n_proxies = CFArrayGetCount(proxies);
661	if (n_proxies > 1) {
662		CFArraySortValues(proxies, CFRangeMake(0, n_proxies), compareDomain, NULL);
663	}
664
665	// cleanup
666
667	for (i = n_proxies - 1; i >= 0; i--) {
668		proxy = CFArrayGetValueAtIndex(proxies, i);
669
670		if ((i > 0) &&
671		    !CFDictionaryContainsKey(proxy, kSCPropNetProxiesSupplementalMatchDomain)) {
672			// remove non-supplemental proxy
673			CFArrayRemoveValueAtIndex(proxies, i);
674			n_proxies--;
675			continue;
676		}
677
678		newProxy = CFDictionaryCreateMutableCopy(NULL, 0, proxy);
679		CFDictionaryRemoveValue(newProxy, PROXY_MATCH_ORDER_KEY);
680		CFDictionaryRemoveValue(newProxy, ORDER_KEY);
681		CFArraySetValueAtIndex(proxies, i, newProxy);
682		CFRelease(newProxy);
683	}
684
685	// update the default proxy
686
687	myDefault = CFDictionaryCreateMutableCopy(NULL,
688						  0,
689						  CFArrayGetValueAtIndex(proxies, 0));
690	if (myOrderAdded && (n_proxies > 1)) {
691		CFDictionaryRef	proxy;
692
693		proxy = CFArrayGetValueAtIndex(proxies, 1);
694		if (CFDictionaryContainsKey(proxy, kSCPropNetProxiesSupplementalMatchDomain)) {
695			// if not a supplemental "default" proxy (a match domain name is
696			// present)
697			CFDictionaryRemoveValue(myDefault, PROXY_MATCH_ORDER_KEY);
698		}
699	}
700	CFArraySetValueAtIndex(proxies, 0, myDefault);
701	CFRelease(myDefault);
702
703	// establish proxy configuration
704
705	if (n_proxies > 0) {
706		CFDictionaryRef		app_layer;
707		CFDictionaryRef		scoped;
708		CFArrayRef		serviceOrderAll;
709		Boolean			skip		= FALSE;
710		CFArrayRef		supplemental;
711
712		proxy = CFArrayGetValueAtIndex(proxies, 0);
713		if (!CFDictionaryContainsKey(proxy, kSCPropNetProxiesSupplementalMatchDomain)) {
714			// if we have "a" default (non-supplemental) proxy
715			newProxy = CFDictionaryCreateMutableCopy(NULL, 0, proxy);
716			CFDictionaryRemoveValue(newProxy, kSCPropNetProxiesSupplementalMatchDomains);
717			CFDictionaryRemoveValue(newProxy, kSCPropNetProxiesSupplementalMatchOrders);
718			skip = TRUE;
719		} else {
720			newProxy = CFDictionaryCreateMutable(NULL,
721							     0,
722							     &kCFTypeDictionaryKeyCallBacks,
723							     &kCFTypeDictionaryValueCallBacks);
724		}
725
726		serviceOrderAll = service_order_copy_all(services, serviceOrder);
727
728		// collect (and add) any "supplemental" proxy configurations
729
730		supplemental = copy_supplemental_proxies(proxies, skip);
731		if (supplemental != NULL) {
732			CFDictionarySetValue(newProxy, kSCPropNetProxiesSupplemental, supplemental);
733			CFRelease(supplemental);
734		}
735
736		// collect (and add) any "scoped" proxy configurations
737
738		scoped = copy_scoped_proxies(services, serviceOrderAll);
739		if (scoped != NULL) {
740			CFDictionarySetValue(newProxy, kSCPropNetProxiesScoped, scoped);
741			CFRelease(scoped);
742		}
743
744		// collect (and add) any "services" based proxy configurations
745
746		app_layer = copy_app_layer_vpn_proxies(services, serviceOrderAll, servicesInfo);
747		if (app_layer != NULL) {
748			CFDictionarySetValue(newProxy, kSCPropNetProxiesServices, app_layer);
749			CFRelease(app_layer);
750		}
751
752		if (serviceOrderAll != NULL) {
753			CFRelease(serviceOrderAll);
754		}
755	} else {
756		newProxy = NULL;
757	}
758
759	CFRelease(proxies);
760	return newProxy;
761}
762
763
764__private_extern__
765void
766proxy_configuration_init(CFBundleRef bundle)
767{
768	CFDictionaryRef	dict;
769
770	dict = CFBundleGetInfoDictionary(bundle);
771	if (isA_CFDictionary(dict)) {
772		G_supplemental_proxies_follow_dns = CFDictionaryGetValue(dict, CFSTR("SupplementalProxiesFollowSupplementalDNS"));
773		G_supplemental_proxies_follow_dns = isA_CFBoolean(G_supplemental_proxies_follow_dns);
774	}
775
776	return;
777}
778
779
780#pragma mark -
781#pragma mark Standalone test code
782
783
784#ifdef	MAIN
785
786static void
787mergeDict(const void *key, const void *value, void *context)
788{
789	CFMutableDictionaryRef	newDict	= (CFMutableDictionaryRef)context;
790
791	CFDictionarySetValue(newDict, key, value);
792	return;
793}
794
795
796static void
797split(const void * key, const void * value, void * context)
798{
799	CFArrayRef		components;
800	CFStringRef		entity_id;
801	CFStringRef		service_id;
802	CFMutableDictionaryRef	state_dict;
803
804	components = CFStringCreateArrayBySeparatingStrings(NULL, (CFStringRef)key, CFSTR("/"));
805	service_id = CFArrayGetValueAtIndex(components, 3);
806	entity_id  = CFArrayGetValueAtIndex(components, 4);
807	state_dict = (CFMutableDictionaryRef)CFDictionaryGetValue(context, service_id);
808	if (state_dict != NULL) {
809		state_dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
810	} else {
811		state_dict = CFDictionaryCreateMutable(NULL,
812						       0,
813						       &kCFTypeDictionaryKeyCallBacks,
814						       &kCFTypeDictionaryValueCallBacks);
815	}
816
817	if (CFEqual(entity_id, kSCEntNetIPv4) ||
818	    CFEqual(entity_id, kSCEntNetIPv6)) {
819		CFStringRef	interface;
820
821		interface = CFDictionaryGetValue((CFDictionaryRef)value, kSCPropInterfaceName);
822		if (interface != NULL) {
823			CFDictionaryRef		proxy;
824			CFMutableDictionaryRef	new_proxy;
825
826			proxy = CFDictionaryGetValue(state_dict, kSCEntNetProxies);
827			if (proxy != NULL) {
828				new_proxy = CFDictionaryCreateMutableCopy(NULL, 0, proxy);
829			} else {
830				new_proxy = CFDictionaryCreateMutable(NULL,
831								0,
832								&kCFTypeDictionaryKeyCallBacks,
833								&kCFTypeDictionaryValueCallBacks);
834			}
835			CFDictionarySetValue(new_proxy, kSCPropInterfaceName, interface);
836			CFDictionarySetValue(state_dict, kSCEntNetProxies, new_proxy);
837			CFRelease(new_proxy);
838		}
839	} else if (CFEqual(entity_id, kSCEntNetProxies)) {
840		CFDictionaryRef	proxy;
841
842		proxy = CFDictionaryGetValue(state_dict, kSCEntNetProxies);
843		if (proxy != NULL) {
844			CFStringRef		domain;
845			CFMutableDictionaryRef	new_proxy;
846
847			// if we already have some Setup: or State: proxy content
848			domain = CFArrayGetValueAtIndex(components, 0);
849			if (CFEqual(domain, kSCDynamicStoreDomainState)) {
850				// if we've already seen the Setup: key
851				new_proxy = CFDictionaryCreateMutableCopy(NULL, 0, (CFDictionaryRef)value);
852				CFDictionaryApplyFunction(proxy, mergeDict, new_proxy);
853			} else {
854				// if we've already seen the State: key
855				new_proxy = CFDictionaryCreateMutableCopy(NULL, 0, proxy);
856				CFDictionaryApplyFunction((CFDictionaryRef)value, mergeDict, new_proxy);
857			}
858			CFDictionarySetValue(state_dict, kSCEntNetProxies, new_proxy);
859			CFRelease(new_proxy);
860		} else {
861			CFDictionarySetValue(state_dict, kSCEntNetProxies, (CFDictionaryRef)value);
862		}
863	} else {
864		CFDictionarySetValue(state_dict, entity_id, (CFDictionaryRef)value);
865	}
866
867	CFDictionarySetValue((CFMutableDictionaryRef)context, service_id, state_dict);
868	CFRelease(state_dict);
869	CFRelease(components);
870
871	return;
872}
873
874int
875main(int argc, char **argv)
876{
877	CFDictionaryRef		entities;
878	CFStringRef		key;
879	CFDictionaryRef		newProxy	= NULL;
880	CFStringRef		pattern;
881	CFMutableArrayRef	patterns;
882	CFStringRef		primary		= NULL;
883	CFMutableDictionaryRef	primary_proxy	= NULL;
884	CFArrayRef		service_order	= NULL;
885	CFMutableDictionaryRef	service_state_dict;
886	CFDictionaryRef		setup_global_ipv4;
887	CFDictionaryRef		state_global_ipv4;
888	SCDynamicStoreRef	store;
889
890	_sc_log     = FALSE;
891	_sc_verbose = (argc > 1) ? TRUE : FALSE;
892
893	store = SCDynamicStoreCreate(NULL, CFSTR("TEST"), NULL, NULL);
894
895	// get IPv4, IPv6, and Proxies entities
896	patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
897	pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
898							      kSCDynamicStoreDomainState,
899							      kSCCompAnyRegex,
900							      kSCEntNetIPv4);
901	CFArrayAppendValue(patterns, pattern);
902	CFRelease(pattern);
903	pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
904							      kSCDynamicStoreDomainState,
905							      kSCCompAnyRegex,
906							      kSCEntNetIPv6);
907	CFArrayAppendValue(patterns, pattern);
908	CFRelease(pattern);
909	pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
910							      kSCDynamicStoreDomainSetup,
911							      kSCCompAnyRegex,
912							      kSCEntNetProxies);
913	CFArrayAppendValue(patterns, pattern);
914	CFRelease(pattern);
915	pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
916							      kSCDynamicStoreDomainState,
917							      kSCCompAnyRegex,
918							      kSCEntNetProxies);
919	CFArrayAppendValue(patterns, pattern);
920	CFRelease(pattern);
921	entities = SCDynamicStoreCopyMultiple(store, NULL, patterns);
922	CFRelease(patterns);
923
924	service_state_dict = CFDictionaryCreateMutable(NULL,
925						       0,
926						       &kCFTypeDictionaryKeyCallBacks,
927						       &kCFTypeDictionaryValueCallBacks);
928	CFDictionaryApplyFunction(entities, split, service_state_dict);
929	CFRelease(entities);
930
931	// get primary service ID
932	key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
933							 kSCDynamicStoreDomainState,
934							 kSCEntNetIPv4);
935	state_global_ipv4 = SCDynamicStoreCopyValue(store, key);
936	CFRelease(key);
937	if (state_global_ipv4 != NULL) {
938		primary = CFDictionaryGetValue(state_global_ipv4, kSCDynamicStorePropNetPrimaryService);
939		if (primary != NULL) {
940			CFDictionaryRef	service_dict;
941
942			// get proxy configuration for primary service
943			service_dict = CFDictionaryGetValue(service_state_dict, primary);
944			if (service_dict != NULL) {
945				CFDictionaryRef	service_proxy;
946
947				service_proxy = CFDictionaryGetValue(service_dict, kSCEntNetProxies);
948				if (service_proxy != NULL) {
949					primary_proxy = CFDictionaryCreateMutableCopy(NULL, 0, service_proxy);
950					CFDictionaryRemoveValue(primary_proxy, kSCPropInterfaceName);
951				}
952			}
953		}
954	}
955
956	// get serviceOrder
957	key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
958							 kSCDynamicStoreDomainSetup,
959							 kSCEntNetIPv4);
960	setup_global_ipv4 = SCDynamicStoreCopyValue(store, key);
961	CFRelease(key);
962	if (setup_global_ipv4 != NULL) {
963		service_order = CFDictionaryGetValue(setup_global_ipv4, kSCPropNetServiceOrder);
964	}
965
966	// update proxy configuration
967	proxy_configuration_init(CFBundleGetMainBundle());
968	newProxy = proxy_configuration_update(primary_proxy,
969					      service_state_dict,
970					      service_order,
971					      NULL);
972	if (newProxy != NULL) {
973		SCPrint(TRUE, stdout, CFSTR("%@\n"), newProxy);
974		CFRelease(newProxy);
975	}
976
977	// cleanup
978	if (setup_global_ipv4 != NULL)	CFRelease(setup_global_ipv4);
979	if (state_global_ipv4 != NULL)	CFRelease(state_global_ipv4);
980	CFRelease(service_state_dict);
981	CFRelease(store);
982
983	/* not reached */
984	exit(0);
985	return 0;
986}
987#endif
988
989