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 * May 13, 2004		Allan Nathanson <ajn@apple.com>
28 * - initial revision
29 */
30
31
32#include <CoreFoundation/CoreFoundation.h>
33#include <CoreFoundation/CFRuntime.h>
34#include <SystemConfiguration/SystemConfiguration.h>
35#include "SCNetworkConfigurationInternal.h"
36#include <SystemConfiguration/SCValidation.h>
37#include <SystemConfiguration/SCPrivate.h>
38
39#include <pthread.h>
40
41
42static CFStringRef	__SCNetworkSetCopyDescription		(CFTypeRef cf);
43static void		__SCNetworkSetDeallocate		(CFTypeRef cf);
44static Boolean		__SCNetworkSetEqual			(CFTypeRef cf1, CFTypeRef cf2);
45static CFHashCode	__SCNetworkSetHash			(CFTypeRef cf);
46
47
48static CFTypeID __kSCNetworkSetTypeID	= _kCFRuntimeNotATypeID;
49
50
51static const CFRuntimeClass __SCNetworkSetClass = {
52	0,				// version
53	"SCNetworkSet",			// className
54	NULL,				// init
55	NULL,				// copy
56	__SCNetworkSetDeallocate,       // dealloc
57	__SCNetworkSetEqual,		// equal
58	__SCNetworkSetHash,		// hash
59	NULL,				// copyFormattingDesc
60	__SCNetworkSetCopyDescription	// copyDebugDesc
61};
62
63
64static pthread_once_t		initialized	= PTHREAD_ONCE_INIT;
65
66
67static CFStringRef
68__SCNetworkSetCopyDescription(CFTypeRef cf)
69{
70	CFAllocatorRef		allocator	= CFGetAllocator(cf);
71	CFMutableStringRef      result;
72	SCNetworkSetPrivateRef	setPrivate	= (SCNetworkSetPrivateRef)cf;
73
74	result = CFStringCreateMutable(allocator, 0);
75	CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkSet %p [%p]> {"), cf, allocator);
76	CFStringAppendFormat(result, NULL, CFSTR("id = %@"), setPrivate->setID);
77	CFStringAppendFormat(result, NULL, CFSTR(", prefs = %p"), setPrivate->prefs);
78	if (setPrivate->name != NULL) {
79		CFStringAppendFormat(result, NULL, CFSTR(", name = %@"), setPrivate->name);
80	}
81	CFStringAppendFormat(result, NULL, CFSTR("}"));
82
83	return result;
84}
85
86
87static void
88__SCNetworkSetDeallocate(CFTypeRef cf)
89{
90	SCNetworkSetPrivateRef	setPrivate	= (SCNetworkSetPrivateRef)cf;
91
92	/* release resources */
93
94	CFRelease(setPrivate->setID);
95	CFRelease(setPrivate->prefs);
96	if (setPrivate->name != NULL)
97		CFRelease(setPrivate->name);
98
99	return;
100}
101
102
103static Boolean
104__SCNetworkSetEqual(CFTypeRef cf1, CFTypeRef cf2)
105{
106	SCNetworkSetPrivateRef	s1	= (SCNetworkSetPrivateRef)cf1;
107	SCNetworkSetPrivateRef	s2	= (SCNetworkSetPrivateRef)cf2;
108
109	if (s1 == s2)
110		return TRUE;
111
112	if (s1->prefs != s2->prefs)
113		return FALSE;   // if not the same prefs
114
115	if (!CFEqual(s1->setID, s2->setID))
116		return FALSE;	// if not the same set identifier
117
118	return TRUE;
119}
120
121
122static CFHashCode
123__SCNetworkSetHash(CFTypeRef cf)
124{
125	SCNetworkSetPrivateRef	setPrivate	= (SCNetworkSetPrivateRef)cf;
126
127	return CFHash(setPrivate->setID);
128}
129
130
131static void
132__SCNetworkSetInitialize(void)
133{
134	__kSCNetworkSetTypeID = _CFRuntimeRegisterClass(&__SCNetworkSetClass);
135	return;
136}
137
138
139static SCNetworkSetPrivateRef
140__SCNetworkSetCreatePrivate(CFAllocatorRef      allocator,
141			    SCPreferencesRef	prefs,
142			    CFStringRef		setID)
143{
144	SCNetworkSetPrivateRef  setPrivate;
145	uint32_t		size;
146
147	/* initialize runtime */
148	pthread_once(&initialized, __SCNetworkSetInitialize);
149
150	/* allocate target */
151	size            = sizeof(SCNetworkSetPrivate) - sizeof(CFRuntimeBase);
152	setPrivate = (SCNetworkSetPrivateRef)_CFRuntimeCreateInstance(allocator,
153								      __kSCNetworkSetTypeID,
154								      size,
155								      NULL);
156	if (setPrivate == NULL) {
157		return NULL;
158	}
159
160	setPrivate->setID       = CFStringCreateCopy(NULL, setID);
161	setPrivate->prefs       = CFRetain(prefs);
162	setPrivate->name	= NULL;
163	setPrivate->established	= FALSE;	// "new" (not yet established) set
164
165	return setPrivate;
166}
167
168
169#pragma mark -
170
171
172static int
173_serviceOrder(SCNetworkServiceRef service)
174{
175	SCNetworkInterfaceRef	interface;
176
177	interface = SCNetworkServiceGetInterface(service);
178	if ((interface == NULL) || _SCNetworkServiceIsVPN(service)) {
179		return 100000;	// if unknown or VPN interface, sort last
180	}
181
182	return __SCNetworkInterfaceOrder(interface);
183}
184
185
186static void
187_serviceOrder_add(SCNetworkSetRef set, SCNetworkServiceRef service)
188{
189	CFIndex			i;
190	CFIndex			n;
191	CFMutableArrayRef	newOrder;
192	CFArrayRef		order;
193	CFStringRef		serviceID;
194	CFIndex			serviceOrder;
195	SCNetworkSetPrivateRef	setPrivate	= (SCNetworkSetPrivateRef)set;
196	CFIndex			slot;
197
198	order = SCNetworkSetGetServiceOrder(set);
199	if (order != NULL) {
200		newOrder = CFArrayCreateMutableCopy(NULL, 0, order);
201	} else {
202		newOrder = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
203	}
204	assert(newOrder != NULL);
205	n = CFArrayGetCount(newOrder);
206
207	serviceID = SCNetworkServiceGetServiceID(service);
208	if (CFArrayContainsValue(newOrder, CFRangeMake(0, n), serviceID)) {
209		// if serviceID already present
210		goto done;
211	}
212
213	serviceOrder = _serviceOrder(service);
214
215	slot = 0;
216	for (i = 0; i < n; i++) {
217		int			slotOrder;
218		SCNetworkServiceRef	slotService;
219		CFStringRef		slotServiceID;
220
221		slotServiceID = CFArrayGetValueAtIndex(newOrder, i);
222		if (!isA_CFString(slotServiceID)) {
223			// if bad prefs
224			continue;
225		}
226
227		slotService = SCNetworkServiceCopy(setPrivate->prefs, slotServiceID);
228		if (slotService == NULL) {
229			// if serviceID not valid
230			continue;
231		}
232
233		slotOrder = _serviceOrder(slotService);
234		if (serviceOrder >= slotOrder) {
235			// add the service *after* this one
236			slot = i + 1;
237		}
238
239		CFRelease(slotService);
240	}
241
242	CFArrayInsertValueAtIndex(newOrder, slot, serviceID);
243	(void) SCNetworkSetSetServiceOrder(set, newOrder);
244
245    done :
246
247	CFRelease(newOrder);
248
249	return;
250}
251
252
253static void
254_serviceOrder_remove(SCNetworkSetRef set, SCNetworkServiceRef service)
255{
256	CFMutableArrayRef	newOrder;
257	CFArrayRef		order;
258	CFStringRef		serviceID;
259
260	order = SCNetworkSetGetServiceOrder(set);
261	if (order == NULL) {
262		return;
263	}
264
265	serviceID = SCNetworkServiceGetServiceID(service);
266
267	newOrder = CFArrayCreateMutableCopy(NULL, 0, order);
268	while (TRUE) {
269		CFIndex	i;
270
271		i = CFArrayGetFirstIndexOfValue(newOrder,
272						CFRangeMake(0, CFArrayGetCount(newOrder)),
273						serviceID);
274		if (i == kCFNotFound) {
275			break;
276		}
277
278		CFArrayRemoveValueAtIndex(newOrder, i);
279	}
280	(void) SCNetworkSetSetServiceOrder(set, newOrder);
281	CFRelease(newOrder);
282
283	return;
284}
285
286
287#pragma mark -
288#pragma mark SCNetworkSet APIs
289
290
291#define	N_QUICK	16
292
293
294#define PREVENT_DUPLICATE_SERVICE_NAMES
295#ifdef  PREVENT_DUPLICATE_SERVICE_NAMES
296static CFStringRef
297copy_next_name(CFStringRef name)
298{
299	CFArrayRef		components;
300	CFIndex			n;
301	CFMutableArrayRef	newComponents;
302	SInt32			suffix	= 2;
303
304	if (name == NULL) {
305		return NULL;
306	}
307
308	components = CFStringCreateArrayBySeparatingStrings(NULL, name, CFSTR(" "));
309	if (components != NULL) {
310		newComponents = CFArrayCreateMutableCopy(NULL, 0, components);
311		CFRelease(components);
312	} else {
313		newComponents = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
314		CFArrayAppendValue(newComponents, name);
315	}
316
317	n = CFArrayGetCount(newComponents);
318	if (n > 1) {
319		CFStringRef	str;
320
321		str = CFArrayGetValueAtIndex(newComponents, n - 1);
322		suffix = CFStringGetIntValue(str);
323		if (suffix++ > 0) {
324			CFArrayRemoveValueAtIndex(newComponents, n - 1);
325		} else {
326			suffix = 2;
327		}
328	}
329
330	name = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), (int)suffix);
331	CFArrayAppendValue(newComponents, name);
332	CFRelease(name);
333
334	name = CFStringCreateByCombiningStrings(NULL, newComponents, CFSTR(" "));
335	CFRelease(newComponents);
336
337	return name;
338}
339
340
341static Boolean
342ensure_unique_service_name(SCNetworkServiceRef service)
343{
344	SCNetworkInterfaceRef	interface;
345	CFStringRef		name;
346	Boolean			ok	= TRUE;
347
348	interface = SCNetworkServiceGetInterface(service);
349
350	name = SCNetworkServiceGetName(service);
351	if (name != NULL) {
352		CFRetain(name);
353	}
354
355	while (TRUE) {
356		CFStringRef	newName;
357
358		ok = SCNetworkServiceSetName(service, name);
359		if (ok) {
360			break;
361		}
362
363		if (SCError() != kSCStatusKeyExists) {
364			SCLog(TRUE, LOG_DEBUG,
365			      CFSTR("could not update service name for \"%@\": %s"),
366			      SCNetworkInterfaceGetLocalizedDisplayName(interface),
367			      SCErrorString(SCError()));
368			break;
369		}
370
371		newName = copy_next_name(name);
372		if (newName == NULL) {
373			SCLog(TRUE, LOG_DEBUG,
374			      CFSTR("could not create unique name for \"%@\": %s"),
375			      SCNetworkInterfaceGetLocalizedDisplayName(interface),
376			      SCErrorString(SCError()));
377			break;
378		}
379
380		// try again with the "new" name
381		if (name != NULL) {
382			CFRelease(name);
383		}
384		name = newName;
385	}
386
387	if (name != NULL) {
388		CFRelease(name);
389	}
390
391	return ok;
392}
393#endif	// PREVENT_DUPLICATE_SERVICE_NAMES
394
395
396Boolean
397SCNetworkSetAddService(SCNetworkSetRef set, SCNetworkServiceRef service)
398{
399	SCNetworkInterfaceRef		interface;
400	CFArrayRef			interface_config	= NULL;
401	CFStringRef			link;
402	Boolean				ok;
403	CFStringRef			path;
404	SCNetworkServicePrivateRef	servicePrivate		= (SCNetworkServicePrivateRef)service;
405	SCNetworkSetPrivateRef		setPrivate		= (SCNetworkSetPrivateRef)set;
406
407	if (!isA_SCNetworkSet(set)) {
408		_SCErrorSet(kSCStatusInvalidArgument);
409		return FALSE;
410	}
411
412	if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) {
413		_SCErrorSet(kSCStatusInvalidArgument);
414		return FALSE;
415	}
416
417	// make sure that we do not add an orphaned network service if its
418	// associated interface is a member of a bond or bridge.
419	interface = SCNetworkServiceGetInterface(service);
420	if ((interface != NULL) &&
421	    __SCNetworkInterfaceIsMember(servicePrivate->prefs, interface)) {
422		_SCErrorSet(kSCStatusKeyExists);
423		return FALSE;
424	}
425
426//#define PREVENT_DUPLICATE_SETS
427#ifdef  PREVENT_DUPLICATE_SETS
428	CFArrayRef	sets;
429
430	// ensure that each service is only a member of ONE set
431	sets = SCNetworkSetCopyAll(setPrivate->prefs);
432	if (sets != NULL) {
433		CFIndex		i;
434		CFIndex		n;
435
436		n = CFArrayGetCount(sets);
437		for (i = 0; i < n; i++) {
438			Boolean		found;
439			CFArrayRef      services;
440			SCNetworkSetRef set;
441
442			set = CFArrayGetValueAtIndex(sets, i);
443			services = SCNetworkSetCopyServices(set);
444			found = CFArrayContainsValue(services,
445						     CFRangeMake(0, CFArrayGetCount(services)),
446						     service);
447			CFRelease(services);
448
449			if (found) {
450				CFRelease(sets);
451				_SCErrorSet(kSCStatusKeyExists);
452				return FALSE;
453			}
454		}
455		CFRelease(sets);
456	}
457#endif  /* PREVENT_DUPLICATE_SETS */
458
459	// get the [deep] interface configuration settings
460	interface = SCNetworkServiceGetInterface(service);
461	if (interface != NULL) {
462		interface_config = __SCNetworkInterfaceCopyDeepConfiguration(set, interface);
463	}
464
465	// create the link between "set" and the "service"
466	path = SCPreferencesPathKeyCreateSetNetworkServiceEntity(NULL,				// allocator
467								 setPrivate->setID,		// set
468								 servicePrivate->serviceID,     // service
469								 NULL);				// entity
470	link = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,				// allocator
471							      servicePrivate->serviceID,	// service
472							      NULL);				// entity
473	ok = SCPreferencesPathSetLink(setPrivate->prefs, path, link);
474#ifdef	PREVENT_DUPLICATE_SERVICE_NAMES
475	if (ok) {
476		ok = ensure_unique_service_name(service);
477		if (!ok) {
478			// if we could not ensure a unique name, remove the (just added)
479			// link between the "set" and the "service"
480			(void) SCPreferencesPathRemoveValue(setPrivate->prefs, path);
481		}
482	}
483#endif	// PREVENT_DUPLICATE_SERVICE_NAMES
484	CFRelease(path);
485	CFRelease(link);
486	if (!ok) {
487		goto done;
488	}
489
490	// push the [deep] interface configuration into all sets which contain this service.
491	if (interface != NULL) {
492		__SCNetworkInterfaceSetDeepConfiguration(set, interface, interface_config);
493	}
494
495	// add service to ServiceOrder
496	_serviceOrder_add(set, service);
497
498	// mark set as no longer "new"
499	setPrivate->established	= TRUE;
500
501    done :
502
503	if (interface_config != NULL)	CFRelease(interface_config);
504	return ok;
505}
506
507
508SCNetworkSetRef
509SCNetworkSetCopy(SCPreferencesRef prefs, CFStringRef setID)
510{
511	CFDictionaryRef		entity;
512	CFStringRef		path;
513	SCNetworkSetPrivateRef	setPrivate;
514
515	if (!isA_CFString(setID)) {
516		_SCErrorSet(kSCStatusInvalidArgument);
517		return NULL;
518	}
519
520	path = SCPreferencesPathKeyCreateSet(NULL, setID);
521	entity = SCPreferencesPathGetValue(prefs, path);
522	CFRelease(path);
523
524	if (!isA_CFDictionary(entity)) {
525		_SCErrorSet(kSCStatusNoKey);
526		return NULL;
527	}
528
529	setPrivate = __SCNetworkSetCreatePrivate(NULL, prefs, setID);
530	assert(setPrivate != NULL);
531
532	// mark set as "old" (already established)
533	setPrivate->established = TRUE;
534
535	return (SCNetworkSetRef)setPrivate;
536}
537
538
539Boolean
540SCNetworkSetContainsInterface(SCNetworkSetRef set, SCNetworkInterfaceRef interface)
541{
542	Boolean		found	= FALSE;
543	CFArrayRef	services;
544
545	services = SCNetworkSetCopyServices(set);
546	if (services != NULL) {
547		found = __SCNetworkServiceExistsForInterface(services, interface);
548		CFRelease(services);
549	}
550
551	return found;
552}
553
554
555CFArrayRef /* of SCNetworkSetRef's */
556SCNetworkSetCopyAll(SCPreferencesRef prefs)
557{
558	CFMutableArrayRef	array;
559	CFIndex			n;
560	CFStringRef		path;
561	CFDictionaryRef		sets;
562
563	path = SCPreferencesPathKeyCreateSets(NULL);
564	sets = SCPreferencesPathGetValue(prefs, path);
565	CFRelease(path);
566
567	if ((sets != NULL) && !isA_CFDictionary(sets)) {
568		return NULL;
569	}
570
571	array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
572
573	n = (sets != NULL) ? CFDictionaryGetCount(sets) : 0;
574	if (n > 0) {
575		CFIndex		i;
576		const void *    keys_q[N_QUICK];
577		const void **   keys	= keys_q;
578		const void *    vals_q[N_QUICK];
579		const void **   vals	= vals_q;
580
581		if (n > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
582			keys = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
583			vals = CFAllocatorAllocate(NULL, n * sizeof(CFPropertyListRef), 0);
584		}
585		CFDictionaryGetKeysAndValues(sets, keys, vals);
586		for (i = 0; i < n; i++) {
587			SCNetworkSetPrivateRef	setPrivate;
588
589			if (!isA_CFDictionary(vals[i])) {
590				SCLog(TRUE,
591				      LOG_INFO,
592				      CFSTR("SCNetworkSetCopyAll(): error w/set \"%@\"\n"),
593				      keys[i]);
594				continue;
595			}
596
597			setPrivate = __SCNetworkSetCreatePrivate(NULL, prefs, keys[i]);
598			assert(setPrivate != NULL);
599
600			// mark set as "old" (already established)
601			setPrivate->established = TRUE;
602
603			CFArrayAppendValue(array, (SCNetworkSetRef)setPrivate);
604			CFRelease(setPrivate);
605		}
606		if (keys != keys_q) {
607			CFAllocatorDeallocate(NULL, keys);
608			CFAllocatorDeallocate(NULL, vals);
609		}
610	}
611
612	return array;
613}
614
615
616CFArrayRef /* of SCNetworkInterfaceRef's */
617SCNetworkSetCopyAvailableInterfaces(SCNetworkSetRef set)
618{
619	CFMutableArrayRef	available;
620	CFMutableSetRef		excluded	= NULL;
621	int			i;
622	CFArrayRef		interfaces;
623	CFIndex			n_interfaces;
624	CFIndex			n_exclusions	= 0;
625	SCPreferencesRef	prefs;
626	SCNetworkSetPrivateRef	setPrivate;
627
628	setPrivate = (SCNetworkSetPrivateRef)set;
629	prefs = setPrivate->prefs;
630
631	interfaces = _SCNetworkInterfaceCopyAllWithPreferences(prefs);
632	n_interfaces = CFArrayGetCount(interfaces);
633	if (n_interfaces == 0) {
634		return interfaces;
635	}
636
637	if (prefs != NULL) {
638		CFArrayRef	bridges	= NULL;
639
640		excluded = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
641
642#if	!TARGET_OS_IPHONE
643		CFArrayRef	bonds	= NULL;
644
645		bonds = SCBondInterfaceCopyAll(prefs);
646		if (bonds != NULL) {
647			__SCBondInterfaceListCollectMembers(bonds, excluded);
648			CFRelease(bonds);
649		}
650#endif	/* !TARGET_OS_IPHONE */
651
652		bridges = SCBridgeInterfaceCopyAll(prefs);
653		if (bridges != NULL) {
654			__SCBridgeInterfaceListCollectMembers(bridges, excluded);
655			CFRelease(bridges);
656		}
657
658		n_exclusions = CFSetGetCount(excluded);
659	}
660
661	if (n_exclusions == 0) {
662		if (excluded != NULL) {
663			CFRelease(excluded);
664		}
665
666		return interfaces;
667	}
668
669	available = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
670
671	for (i = 0; i < n_interfaces; i++) {
672		SCNetworkInterfaceRef	interface;
673
674		interface = CFArrayGetValueAtIndex(interfaces, i);
675		if (CFSetContainsValue(excluded, interface)) {
676			// if excluded
677			continue;
678		}
679
680		CFArrayAppendValue(available, interface);
681	}
682
683	CFRelease(interfaces);
684	CFRelease(excluded);
685
686	return available;
687}
688
689
690SCNetworkSetRef
691SCNetworkSetCopyCurrent(SCPreferencesRef prefs)
692{
693	CFArrayRef		components;
694	CFStringRef		currentID;
695	SCNetworkSetPrivateRef	setPrivate	= NULL;
696
697	currentID = SCPreferencesGetValue(prefs, kSCPrefCurrentSet);
698	if (!isA_CFString(currentID)) {
699		return NULL;
700	}
701
702	components = CFStringCreateArrayBySeparatingStrings(NULL, currentID, CFSTR("/"));
703	if (CFArrayGetCount(components) == 3) {
704		CFStringRef	setID;
705		CFStringRef	path;
706
707		setID = CFArrayGetValueAtIndex(components, 2);
708		path = SCPreferencesPathKeyCreateSet(NULL, setID);
709		if (CFEqual(path, currentID)) {
710			setPrivate = __SCNetworkSetCreatePrivate(NULL, prefs, setID);
711			assert(setPrivate != NULL);
712
713			// mark set as "old" (already established)
714			setPrivate->established = TRUE;
715		} else {
716			SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkSetCopyCurrent(): preferences are non-conformant"));
717		}
718		CFRelease(path);
719	}
720	CFRelease(components);
721
722	return (SCNetworkSetRef)setPrivate;
723}
724
725
726CFArrayRef /* of SCNetworkServiceRef's */
727SCNetworkSetCopyServices(SCNetworkSetRef set)
728{
729	CFMutableArrayRef       array;
730	CFDictionaryRef		dict;
731	CFIndex			n;
732	CFStringRef		path;
733	SCNetworkSetPrivateRef	setPrivate	= (SCNetworkSetPrivateRef)set;
734
735	if (!isA_SCNetworkSet(set)) {
736		_SCErrorSet(kSCStatusInvalidArgument);
737		return NULL;
738	}
739
740	path = SCPreferencesPathKeyCreateSetNetworkService(NULL, setPrivate->setID, NULL);
741	dict = SCPreferencesPathGetValue(setPrivate->prefs, path);
742	CFRelease(path);
743	if ((dict != NULL) && !isA_CFDictionary(dict)) {
744		return NULL;
745	}
746
747	array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
748
749	n = (dict != NULL) ? CFDictionaryGetCount(dict) : 0;
750	if (n > 0) {
751		CFIndex		i;
752		const void *    keys_q[N_QUICK];
753		const void **   keys	= keys_q;
754
755		if (n > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
756			keys = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
757		}
758		CFDictionaryGetKeysAndValues(dict, keys, NULL);
759		for (i = 0; i < n; i++) {
760			CFArrayRef	components;
761			CFStringRef	link;
762
763			path = SCPreferencesPathKeyCreateSetNetworkServiceEntity(NULL,
764										 setPrivate->setID,
765										 (CFStringRef)keys[i],
766										 NULL);
767			link = SCPreferencesPathGetLink(setPrivate->prefs, path);
768			CFRelease(path);
769			if (link == NULL) {
770				SCLog(TRUE,
771				      LOG_INFO,
772				      CFSTR("SCNetworkSetCopyServices(): service \"%@\" for set \"%@\" is not a link\n"),
773				      keys[i],
774				      setPrivate->setID);
775				continue;	 // if the service is not a link
776			}
777
778			components = CFStringCreateArrayBySeparatingStrings(NULL, link, CFSTR("/"));
779			if (CFArrayGetCount(components) == 3) {
780				CFStringRef serviceID;
781
782				serviceID = CFArrayGetValueAtIndex(components, 2);
783				path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,		// allocator
784										      serviceID,	// service
785										      NULL);		// entity
786				if (CFEqual(path, link)) {
787					SCNetworkServicePrivateRef	servicePrivate;
788
789					servicePrivate = __SCNetworkServiceCreatePrivate(NULL,
790											 setPrivate->prefs,
791											 serviceID,
792											 NULL);
793					CFArrayAppendValue(array, (SCNetworkServiceRef)servicePrivate);
794					CFRelease(servicePrivate);
795				}
796				CFRelease(path);
797			}
798			CFRelease(components);
799		}
800		if (keys != keys_q) {
801			CFAllocatorDeallocate(NULL, keys);
802		}
803	}
804
805	return array;
806}
807
808
809SCNetworkSetRef
810SCNetworkSetCreate(SCPreferencesRef prefs)
811{
812	CFArrayRef		components;
813	CFDictionaryRef		entity;
814	Boolean			ok;
815	CFStringRef		path;
816	CFStringRef		prefix;
817	CFStringRef		setID;
818	SCNetworkSetPrivateRef	setPrivate;
819
820	prefix = SCPreferencesPathKeyCreateSets(NULL);
821	path = __SCPreferencesPathCreateUniqueChild_WithMoreSCFCompatibility(prefs, prefix);
822	if (path == NULL) path = SCPreferencesPathCreateUniqueChild(prefs, prefix);
823	CFRelease(prefix);
824	if (path == NULL) {
825		return NULL;
826	}
827
828	components = CFStringCreateArrayBySeparatingStrings(NULL, path, CFSTR("/"));
829	setID = CFArrayGetValueAtIndex(components, 2);
830	setPrivate = __SCNetworkSetCreatePrivate(NULL, prefs, setID);
831	assert(setPrivate != NULL);
832	CFRelease(components);
833
834	// mark set as "new" (not yet established)
835	setPrivate->established = FALSE;
836
837	// establish the set in the preferences
838	entity = CFDictionaryCreate(NULL,
839				    NULL, NULL, 0,
840				    &kCFTypeDictionaryKeyCallBacks,
841				    &kCFTypeDictionaryValueCallBacks);
842	ok = SCPreferencesPathSetValue(prefs, path, entity);
843	CFRelease(path);
844	CFRelease(entity);
845	if (!ok) {
846		CFRelease(setPrivate);
847		setPrivate = NULL;
848	}
849
850	return (SCNetworkSetRef)setPrivate;
851}
852
853
854CFStringRef
855SCNetworkSetGetSetID(SCNetworkSetRef set)
856{
857	SCNetworkSetPrivateRef	setPrivate	= (SCNetworkSetPrivateRef)set;
858
859	if (!isA_SCNetworkSet(set)) {
860		_SCErrorSet(kSCStatusInvalidArgument);
861		return NULL;
862	}
863
864	return setPrivate->setID;
865}
866
867
868CFStringRef
869SCNetworkSetGetName(SCNetworkSetRef set)
870{
871	CFBundleRef		bundle;
872	CFDictionaryRef		entity;
873	CFStringRef		path;
874	SCNetworkSetPrivateRef	setPrivate	= (SCNetworkSetPrivateRef)set;
875
876	if (!isA_SCNetworkSet(set)) {
877		_SCErrorSet(kSCStatusInvalidArgument);
878		return NULL;
879	}
880
881	if (setPrivate->name != NULL) {
882		return setPrivate->name;
883	}
884
885	path = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID);
886	entity = SCPreferencesPathGetValue(setPrivate->prefs, path);
887	CFRelease(path);
888
889	if (isA_CFDictionary(entity)) {
890		CFStringRef	name;
891
892		name = CFDictionaryGetValue(entity, kSCPropUserDefinedName);
893		if (isA_CFString(name)) {
894			setPrivate->name = CFRetain(name);
895		}
896	}
897
898	bundle = _SC_CFBundleGet();
899	if (bundle != NULL) {
900		if (setPrivate->name != NULL) {
901			CFStringRef	non_localized;
902
903			non_localized = _SC_CFBundleCopyNonLocalizedString(bundle,
904									   CFSTR("DEFAULT_SET_NAME"),
905									   CFSTR("Automatic"),
906									   NULL);
907			if (non_localized != NULL) {
908				if (CFEqual(setPrivate->name, non_localized)) {
909					CFStringRef	localized;
910
911					// if "Automatic", return localized name
912					localized = CFBundleCopyLocalizedString(bundle,
913										CFSTR("DEFAULT_SET_NAME"),
914										CFSTR("Automatic"),
915										NULL);
916					if (localized != NULL) {
917						CFRelease(setPrivate->name);
918						setPrivate->name = localized;
919					}
920				}
921
922				CFRelease(non_localized);
923			}
924		}
925	}
926
927	return setPrivate->name;
928}
929
930
931CFArrayRef /* of serviceID CFStringRef's */
932SCNetworkSetGetServiceOrder(SCNetworkSetRef set)
933{
934	CFDictionaryRef		dict;
935	CFStringRef		path;
936	CFArrayRef		serviceOrder;
937	SCNetworkSetPrivateRef	setPrivate	= (SCNetworkSetPrivateRef)set;
938
939	if (!isA_SCNetworkSet(set)) {
940		_SCErrorSet(kSCStatusInvalidArgument);
941		return NULL;
942	}
943
944	path = SCPreferencesPathKeyCreateSetNetworkGlobalEntity(NULL, setPrivate->setID, kSCEntNetIPv4);
945	if (path == NULL) {
946		return NULL;
947	}
948
949	dict = SCPreferencesPathGetValue(setPrivate->prefs, path);
950	CFRelease(path);
951	if (!isA_CFDictionary(dict)) {
952		return NULL;
953	}
954
955	serviceOrder = CFDictionaryGetValue(dict, kSCPropNetServiceOrder);
956	serviceOrder = isA_CFArray(serviceOrder);
957
958	return serviceOrder;
959}
960
961
962CFTypeID
963SCNetworkSetGetTypeID(void)
964{
965	pthread_once(&initialized, __SCNetworkSetInitialize);	/* initialize runtime */
966	return __kSCNetworkSetTypeID;
967}
968
969
970Boolean
971SCNetworkSetRemove(SCNetworkSetRef set)
972{
973	CFStringRef		currentPath;
974	Boolean			ok		= FALSE;
975	CFStringRef		path;
976	SCNetworkSetPrivateRef	setPrivate	= (SCNetworkSetPrivateRef)set;
977
978	if (!isA_SCNetworkSet(set)) {
979		_SCErrorSet(kSCStatusInvalidArgument);
980		return FALSE;
981	}
982
983	currentPath = SCPreferencesGetValue(setPrivate->prefs, kSCPrefCurrentSet);
984	path = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID);
985	if (!isA_CFString(currentPath) || !CFEqual(currentPath, path)) {
986		ok = SCPreferencesPathRemoveValue(setPrivate->prefs, path);
987	} else {
988		_SCErrorSet(kSCStatusInvalidArgument);
989	}
990	CFRelease(path);
991
992	return ok;
993}
994
995
996Boolean
997SCNetworkSetRemoveService(SCNetworkSetRef set, SCNetworkServiceRef service)
998{
999	SCNetworkInterfaceRef		interface;
1000	CFArrayRef			interface_config	= NULL;
1001	Boolean				ok;
1002	CFStringRef			path;
1003	int				sc_status		= kSCStatusOK;
1004	SCNetworkServicePrivateRef	servicePrivate		= (SCNetworkServicePrivateRef)service;
1005	SCNetworkSetPrivateRef		setPrivate		= (SCNetworkSetPrivateRef)set;
1006
1007	if (!isA_SCNetworkSet(set)) {
1008		_SCErrorSet(kSCStatusInvalidArgument);
1009		return FALSE;
1010	}
1011
1012	if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) {
1013		_SCErrorSet(kSCStatusInvalidArgument);
1014		return FALSE;
1015	}
1016
1017	// remove service from ServiceOrder
1018	_serviceOrder_remove(set, service);
1019
1020	// get the [deep] interface configuration settings
1021	interface = SCNetworkServiceGetInterface(service);
1022	if (interface != NULL) {
1023		interface_config = __SCNetworkInterfaceCopyDeepConfiguration(set, interface);
1024		if (interface_config != NULL) {
1025			// remove the interface configuration from all sets which contain this service.
1026			__SCNetworkInterfaceSetDeepConfiguration(set, interface, NULL);
1027		}
1028	}
1029
1030	// remove the link between "set" and the "service"
1031	path = SCPreferencesPathKeyCreateSetNetworkServiceEntity(NULL,
1032								 setPrivate->setID,
1033								 servicePrivate->serviceID,
1034								 NULL);
1035	ok = SCPreferencesPathRemoveValue(setPrivate->prefs, path);
1036	if (!ok) {
1037		sc_status = SCError();	// preserve the error
1038	}
1039	CFRelease(path);
1040
1041	// push the [deep] interface configuration [back] into all sets which contain the service.
1042	if (interface_config != NULL) {
1043		__SCNetworkInterfaceSetDeepConfiguration(set, interface, interface_config);
1044	}
1045
1046	if (interface_config != NULL)     CFRelease(interface_config);
1047	if (!ok) {
1048		_SCErrorSet(sc_status);
1049	}
1050	return ok;
1051}
1052
1053
1054Boolean
1055SCNetworkSetSetCurrent(SCNetworkSetRef set)
1056{
1057	Boolean			ok;
1058	CFStringRef		path;
1059	SCNetworkSetPrivateRef	setPrivate	= (SCNetworkSetPrivateRef)set;
1060
1061	if (!isA_SCNetworkSet(set)) {
1062		_SCErrorSet(kSCStatusInvalidArgument);
1063		return FALSE;
1064	}
1065
1066	path = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID);
1067	ok = SCPreferencesSetValue(setPrivate->prefs, kSCPrefCurrentSet, path);
1068	CFRelease(path);
1069	return ok;
1070}
1071
1072
1073Boolean
1074SCNetworkSetSetName(SCNetworkSetRef set, CFStringRef name)
1075{
1076	CFBundleRef		bundle		= NULL;
1077	CFDictionaryRef		entity;
1078	CFStringRef		localized	= NULL;
1079	CFStringRef		non_localized	= NULL;
1080	Boolean			ok		= FALSE;
1081	CFStringRef		path;
1082	SCNetworkSetPrivateRef	setPrivate	= (SCNetworkSetPrivateRef)set;
1083
1084	if (!isA_SCNetworkSet(set)) {
1085		_SCErrorSet(kSCStatusInvalidArgument);
1086		return FALSE;
1087	}
1088
1089	if ((name != NULL) && !isA_CFString(name)) {
1090		_SCErrorSet(kSCStatusInvalidArgument);
1091		return FALSE;
1092	}
1093
1094	// if known, compare against localized name
1095
1096	if (name != NULL) {
1097		bundle = _SC_CFBundleGet();
1098		if (bundle != NULL) {
1099			non_localized = _SC_CFBundleCopyNonLocalizedString(bundle,
1100									   CFSTR("DEFAULT_SET_NAME"),
1101									   CFSTR("Automatic"),
1102									   NULL);
1103			if (non_localized != NULL) {
1104				if (CFEqual(name, non_localized)) {
1105					localized = CFBundleCopyLocalizedString(bundle,
1106										CFSTR("DEFAULT_SET_NAME"),
1107										CFSTR("Automatic"),
1108										NULL);
1109					if (localized != NULL) {
1110						name = localized;
1111					}
1112				}
1113			}
1114		}
1115	}
1116
1117#define PREVENT_DUPLICATE_SET_NAMES
1118#ifdef  PREVENT_DUPLICATE_SET_NAMES
1119	if (name != NULL) {
1120		CFArrayRef      sets;
1121
1122		// ensure that each set is uniquely named
1123
1124		sets = SCNetworkSetCopyAll(setPrivate->prefs);
1125		if (sets != NULL) {
1126			CFIndex		i;
1127			CFIndex		n;
1128
1129			n = CFArrayGetCount(sets);
1130			for (i = 0; i < n; i++) {
1131				CFStringRef     otherID;
1132				CFStringRef     otherName;
1133				SCNetworkSetRef set		= CFArrayGetValueAtIndex(sets, i);
1134
1135				otherID = SCNetworkSetGetSetID(set);
1136				if (CFEqual(setPrivate->setID, otherID)) {
1137					continue;       // skip current set
1138				}
1139
1140				otherName = SCNetworkSetGetName(set);
1141				if ((otherName != NULL) && CFEqual(name, otherName)) {
1142					// if "name" not unique
1143					CFRelease(sets);
1144					_SCErrorSet(kSCStatusKeyExists);
1145					goto done;
1146				}
1147			}
1148			CFRelease(sets);
1149		}
1150	}
1151#endif  /* PREVENT_DUPLICATE_SET_NAMES */
1152
1153	// if known, store non-localized name
1154
1155	if ((name != NULL) && (bundle != NULL) && (non_localized != NULL)) {
1156		if (localized == NULL) {
1157			localized = CFBundleCopyLocalizedString(bundle,
1158								CFSTR("DEFAULT_SET_NAME"),
1159								CFSTR("Automatic"),
1160								NULL);
1161		}
1162
1163		if (localized != NULL) {
1164			if (CFEqual(name, localized)) {
1165				name = non_localized;
1166			}
1167		}
1168	}
1169
1170	// update the "name"
1171
1172	path = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID);
1173	entity = SCPreferencesPathGetValue(setPrivate->prefs, path);
1174	if (isA_CFDictionary(entity) ||
1175	    ((entity == NULL) && (name != NULL))) {
1176		CFMutableDictionaryRef	newEntity;
1177
1178		if (entity != NULL) {
1179			newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity);
1180		} else {
1181			newEntity = CFDictionaryCreateMutable(NULL,
1182							      0,
1183							      &kCFTypeDictionaryKeyCallBacks,
1184							      &kCFTypeDictionaryValueCallBacks);
1185		}
1186		if (name != NULL) {
1187			CFDictionarySetValue(newEntity, kSCPropUserDefinedName, name);
1188		} else {
1189			CFDictionaryRemoveValue(newEntity, kSCPropUserDefinedName);
1190		}
1191		ok = SCPreferencesPathSetValue(setPrivate->prefs, path, newEntity);
1192		CFRelease(newEntity);
1193	}
1194	CFRelease(path);
1195
1196    done :
1197
1198	if (localized != NULL)		CFRelease(localized);
1199	if (non_localized != NULL)	CFRelease(non_localized);
1200	return ok;
1201}
1202
1203
1204Boolean
1205SCNetworkSetSetServiceOrder(SCNetworkSetRef set, CFArrayRef newOrder)
1206{
1207	CFDictionaryRef		dict;
1208	CFMutableDictionaryRef  newDict;
1209	Boolean			ok;
1210	CFStringRef		path;
1211	SCNetworkSetPrivateRef	setPrivate	= (SCNetworkSetPrivateRef)set;
1212
1213	if (!isA_SCNetworkSet(set)) {
1214		_SCErrorSet(kSCStatusInvalidArgument);
1215		return FALSE;
1216	}
1217
1218	if (isA_CFArray(newOrder)) {
1219		CFIndex	i;
1220		CFIndex	n	= CFArrayGetCount(newOrder);
1221
1222		for (i = 0; i < n; i++) {
1223			CFStringRef	serviceID;
1224
1225			serviceID = CFArrayGetValueAtIndex(newOrder, i);
1226			if (!isA_CFString(serviceID)) {
1227				_SCErrorSet(kSCStatusInvalidArgument);
1228				return FALSE;
1229			}
1230		}
1231	} else {
1232		_SCErrorSet(kSCStatusInvalidArgument);
1233		return FALSE;
1234	}
1235
1236	path = SCPreferencesPathKeyCreateSetNetworkGlobalEntity(NULL, setPrivate->setID, kSCEntNetIPv4);
1237	if (path == NULL) {
1238		return FALSE;
1239	}
1240
1241	dict = SCPreferencesPathGetValue(setPrivate->prefs, path);
1242	if (dict != NULL) {
1243		newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
1244	} else {
1245		newDict = CFDictionaryCreateMutable(NULL,
1246						    0,
1247						    &kCFTypeDictionaryKeyCallBacks,
1248						    &kCFTypeDictionaryValueCallBacks);
1249	}
1250
1251	CFDictionarySetValue(newDict, kSCPropNetServiceOrder, newOrder);
1252	ok = SCPreferencesPathSetValue(setPrivate->prefs, path, newDict);
1253	CFRelease(newDict);
1254	CFRelease(path);
1255
1256	return ok;
1257}
1258
1259
1260#pragma mark -
1261#pragma mark SCNetworkSet SPIs
1262
1263
1264static void
1265add_supported_interfaces(CFMutableArrayRef interface_list, SCNetworkInterfaceRef interface)
1266{
1267	CFIndex		i;
1268	CFArrayRef	interface_types;
1269	CFIndex		n;
1270
1271	interface_types = SCNetworkInterfaceGetSupportedInterfaceTypes(interface);
1272	n = (interface_types != NULL) ? CFArrayGetCount(interface_types) : 0;
1273	for (i = 0; i < n; i++) {
1274		SCNetworkInterfaceRef	parent;
1275		CFStringRef		interface_type;
1276
1277		interface_type = CFArrayGetValueAtIndex(interface_types, i);
1278		parent = SCNetworkInterfaceCreateWithInterface(interface, interface_type);
1279		if (parent != NULL) {
1280			CFArrayAppendValue(interface_list, parent);
1281			CFRelease(parent);
1282		}
1283	}
1284
1285	return;
1286}
1287
1288
1289static CFSetRef	/* of SCNetworkInterfaceRef's */
1290copyExcludedInterfaces(SCPreferencesRef prefs)
1291{
1292	CFMutableSetRef	excluded;
1293	CFArrayRef	interfaces;
1294
1295	excluded = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
1296
1297#if	!TARGET_OS_IPHONE
1298	// exclude Bond [member] interfaces
1299	interfaces = SCBondInterfaceCopyAll(prefs);
1300	if (interfaces != NULL) {
1301		__SCBondInterfaceListCollectMembers(interfaces, excluded);
1302		CFRelease(interfaces);
1303	}
1304#endif	// !TARGET_OS_IPHONE
1305
1306	// exclude Bridge [member] interfaces
1307	interfaces = SCBridgeInterfaceCopyAll(prefs);
1308	if (interfaces != NULL) {
1309		__SCBridgeInterfaceListCollectMembers(interfaces, excluded);
1310		CFRelease(interfaces);
1311	}
1312
1313	return excluded;
1314}
1315
1316
1317#if	!TARGET_OS_IPHONE
1318static SCBridgeInterfaceRef
1319copyAutoBridgeInterface(SCPreferencesRef prefs, CFStringRef bridgeName)
1320{
1321	SCBridgeInterfaceRef	bridge		= NULL;
1322	CFArrayRef		interfaces;
1323
1324	// exclude Bridge [member] interfaces
1325	interfaces = SCBridgeInterfaceCopyAll(prefs);
1326	if (interfaces != NULL) {
1327		CFIndex		i;
1328		CFIndex		n;
1329
1330		n = CFArrayGetCount(interfaces);
1331		for (i = 0; i < n; i++) {
1332			SCBridgeInterfaceRef	interface;
1333			CFStringRef		name	= NULL;
1334			CFDictionaryRef		options;
1335
1336			interface = CFArrayGetValueAtIndex(interfaces, i);
1337			options = SCBridgeInterfaceGetOptions(interface);
1338			if ((options != NULL) &&
1339			    CFDictionaryGetValueIfPresent(options,
1340							  CFSTR("__AUTO__"),
1341							  (const void **)&name) &&
1342			    _SC_CFEqual(name, bridgeName)) {
1343				bridge = interface;
1344				CFRetain(bridge);
1345				break;
1346			}
1347		}
1348
1349		CFRelease(interfaces);
1350	}
1351
1352	if (bridge == NULL) {
1353		bridge = SCBridgeInterfaceCreate(prefs);
1354		if (bridge != NULL) {
1355			CFMutableDictionaryRef	newOptions;
1356			Boolean			ok;
1357
1358			newOptions = CFDictionaryCreateMutable(NULL, 0,
1359							       &kCFTypeDictionaryKeyCallBacks,
1360							       &kCFTypeDictionaryValueCallBacks);
1361			CFDictionarySetValue(newOptions, CFSTR("__AUTO__"), bridgeName);
1362			ok = SCBridgeInterfaceSetOptions(bridge, newOptions);
1363			CFRelease(newOptions);
1364			if (!ok) {
1365				CFRelease(bridge);
1366				bridge = NULL;
1367			}
1368		}
1369	}
1370
1371	return bridge;
1372}
1373#endif	// !TARGET_OS_IPHONE
1374
1375
1376static CFArrayRef
1377copyServices(SCNetworkSetRef set)
1378{
1379	CFArrayRef		services;
1380	SCNetworkSetPrivateRef	setPrivate	= (SCNetworkSetPrivateRef)set;
1381
1382	// first, assume that we only want to add new services
1383	// for those interfaces that are not represented in the
1384	// current set.
1385	services = SCNetworkSetCopyServices(set);
1386	if ((services != NULL) && setPrivate->established) {
1387		// but, if we are given an existing (or "established") set
1388		// than we only want to add new services for those interfaces
1389		// that are not represented in *any* set.
1390		CFRelease(services);
1391		services = SCNetworkServiceCopyAll(setPrivate->prefs);
1392	}
1393
1394	return services;
1395}
1396
1397
1398#if	!TARGET_OS_IPHONE
1399static CFArrayRef
1400updateServices(CFArrayRef services, SCNetworkInterfaceRef interface)
1401{
1402	CFStringRef		bsdName;
1403	CFIndex			i;
1404	CFIndex			n;
1405	CFMutableArrayRef	newServices;
1406
1407	if (services == NULL) {
1408		return NULL;
1409	}
1410
1411	bsdName = SCNetworkInterfaceGetBSDName(interface);
1412
1413	newServices = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1414
1415	n = CFArrayGetCount(services);
1416	for (i = 0; i < n; i++) {
1417		SCNetworkInterfaceRef		interface;
1418		CFStringRef			interfaceName;
1419		SCNetworkServiceRef		newService;
1420		SCNetworkServiceRef		service;
1421		CFStringRef			serviceID;
1422		SCNetworkServicePrivateRef	servicePrivate;
1423
1424		service = CFArrayGetValueAtIndex(services, i);
1425		interface = SCNetworkServiceGetInterface(service);
1426		interfaceName = SCNetworkInterfaceGetBSDName(interface);
1427		if (!_SC_CFEqual(interfaceName, bsdName)) {
1428			// if not a match, retain
1429			CFArrayAppendValue(newServices, service);
1430			continue;
1431		}
1432
1433		// if a match, update
1434		serviceID = SCNetworkServiceGetServiceID(service);
1435		servicePrivate = (SCNetworkServicePrivateRef)service;
1436		newService = SCNetworkServiceCopy(servicePrivate->prefs, serviceID);
1437		if (newService != NULL) {
1438			CFArrayAppendValue(newServices, newService);
1439			CFRelease(newService);
1440		}
1441	}
1442
1443	return newServices;
1444}
1445#endif	// !TARGET_OS_IPHONE
1446
1447
1448static __inline__ Boolean
1449skipInterface(SCNetworkInterfaceRef interface)
1450{
1451	CFStringRef	action;
1452
1453	action = _SCNetworkInterfaceGetConfigurationAction(interface);
1454	if (isA_CFString(action) &&
1455	    CFEqual(action, kSCNetworkInterfaceConfigurationActionValueNone)) {
1456		return TRUE;
1457	}
1458
1459	return FALSE;
1460}
1461
1462
1463static Boolean
1464__SCNetworkSetEstablishDefaultConfigurationForInterfaces(SCNetworkSetRef set, CFArrayRef interfaces, Boolean excludeHidden)
1465{
1466	CFSetRef		excluded	= NULL;
1467	CFIndex			i;
1468	CFIndex			n		= 0;
1469	Boolean			ok		= TRUE;
1470	CFArrayRef		services;
1471	SCNetworkSetPrivateRef	setPrivate	= (SCNetworkSetPrivateRef)set;
1472	Boolean			updated		= FALSE;
1473	Boolean			updatedIFs	= FALSE;
1474
1475#if	TARGET_OS_IPHONE
1476	CFArrayRef		orphans		= NULL;
1477	CFArrayRef		sets;
1478
1479	sets = SCNetworkSetCopyAll(setPrivate->prefs);
1480	if (sets != NULL) {
1481		if (CFArrayGetCount(sets) == 1) {
1482			services = SCNetworkSetCopyServices(set);
1483			if (services != NULL) {
1484				n = CFArrayGetCount(services);
1485				CFRelease(services);
1486			}
1487
1488			if ((n == 0) && CFEqual(set, CFArrayGetValueAtIndex(sets, 0))) {
1489				// after a "Reset Network Settings" we need to find (and
1490				// add back) any VPN services that were orphaned.
1491				orphans = SCNetworkServiceCopyAll(setPrivate->prefs);
1492			}
1493		}
1494
1495		CFRelease(sets);
1496	}
1497#endif	// TARGET_OS_IPHONE
1498
1499	// copy network services
1500	services = copyServices(set);
1501
1502	// copy network interfaces to be excluded
1503	excluded = copyExcludedInterfaces(setPrivate->prefs);
1504
1505#if	!TARGET_OS_IPHONE
1506	// look for interfaces that should auto-magically be added
1507	// to an Ethernet bridge
1508	n = (interfaces != NULL) ? CFArrayGetCount(interfaces) : 0;
1509	for (i = 0; i < n; i++) {
1510		SCBridgeInterfaceRef	bridge		= NULL;
1511		SCNetworkInterfaceRef	interface;
1512
1513		interface = CFArrayGetValueAtIndex(interfaces, i);
1514
1515		if (excludeHidden && skipInterface(interface)) {
1516			// if not auto-configure
1517			continue;
1518		}
1519
1520		if ((excluded != NULL)
1521		    && CFSetContainsValue(excluded, interface)) {
1522			// if this interface is a member of a Bond or Bridge
1523			continue;
1524		}
1525
1526		if (__SCNetworkServiceExistsForInterface(services, interface)) {
1527			// if this is not a new interface
1528			continue;
1529		}
1530
1531		if (_SCNetworkInterfaceIsBuiltin(interface) &&
1532		    _SCNetworkInterfaceIsThunderbolt(interface) &&
1533		    !isA_SCBridgeInterface(interface)) {
1534			// add built-in Thunderbolt interfaces to bridge
1535			bridge = copyAutoBridgeInterface(setPrivate->prefs, CFSTR("thunderbolt-bridge"));
1536		}
1537
1538		if (bridge != NULL) {
1539			CFIndex			bridgeIndex;
1540			CFArrayRef		members;
1541			CFMutableArrayRef	newMembers;
1542			CFMutableSetRef		newExcluded;
1543			CFMutableArrayRef	newInterfaces;
1544			CFArrayRef		newServices;
1545
1546			// track the bridge interface (if it's in our list)
1547			bridgeIndex = CFArrayGetFirstIndexOfValue(interfaces,
1548								  CFRangeMake(0, CFArrayGetCount(interfaces)),
1549								  bridge);
1550
1551			// add new member interface
1552			members = SCBridgeInterfaceGetMemberInterfaces(bridge);
1553			if ((members != NULL) && (CFArrayGetCount(members) > 0)) {
1554				newMembers = CFArrayCreateMutableCopy(NULL, 0, members);
1555				updated = TRUE;		// if we're updating an existing bridge
1556			} else {
1557				newMembers = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1558			}
1559			CFArrayAppendValue(newMembers, interface);
1560			ok = SCBridgeInterfaceSetMemberInterfaces(bridge, newMembers);
1561			CFRelease(newMembers);
1562			if (!ok) {
1563				SCLog(TRUE, LOG_DEBUG,
1564				      CFSTR("could not update bridge with \"%@\": %s\n"),
1565				      SCNetworkInterfaceGetLocalizedDisplayName(interface),
1566				      SCErrorString(SCError()));
1567				CFRelease(bridge);
1568				continue;
1569			}
1570
1571			// exclude the new member interface
1572			newExcluded = CFSetCreateMutableCopy(NULL, 0, excluded);
1573			CFRelease(excluded);
1574			CFSetAddValue(newExcluded, interface);
1575			excluded = newExcluded;
1576
1577			// update the list of interfaces to include the [new or updated] bridge
1578			newInterfaces = CFArrayCreateMutableCopy(NULL, 0, interfaces);
1579			if (bridgeIndex != kCFNotFound) {
1580				CFArraySetValueAtIndex(newInterfaces, bridgeIndex, bridge);
1581			} else {
1582				CFArrayAppendValue(newInterfaces, bridge);
1583			}
1584			if (updatedIFs) {
1585				CFRelease(interfaces);
1586			}
1587			interfaces = newInterfaces;
1588			updatedIFs = TRUE;
1589
1590			// refresh [existing] services
1591			newServices = updateServices(services, bridge);
1592			if (newServices != NULL) {
1593				CFRelease(services);
1594				services = newServices;
1595			}
1596
1597			CFRelease(bridge);
1598		}
1599	}
1600#endif	// !TARGET_OS_IPHONE
1601
1602	n = (interfaces != NULL) ? CFArrayGetCount(interfaces) : 0;
1603	for (i = 0; i < n; i++) {
1604		SCNetworkInterfaceRef	interface;
1605		CFMutableArrayRef	interface_list;
1606
1607		interface = CFArrayGetValueAtIndex(interfaces, i);
1608
1609		if (excludeHidden && skipInterface(interface)) {
1610			// if not auto-configure
1611			continue;
1612		}
1613
1614		if ((excluded != NULL)
1615		    && CFSetContainsValue(excluded, interface)) {
1616			// if this interface is a member of a Bond or Bridge
1617			continue;
1618		}
1619
1620		if (__SCNetworkServiceExistsForInterface(services, interface)) {
1621			// if this is not a new interface
1622			continue;
1623		}
1624
1625		interface_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1626		CFArrayAppendValue(interface_list, interface);
1627
1628		while (ok && (CFArrayGetCount(interface_list) > 0)) {
1629			CFArrayRef		protocol_types;
1630
1631			interface = CFArrayGetValueAtIndex(interface_list, 0);
1632
1633			protocol_types = SCNetworkInterfaceGetSupportedProtocolTypes(interface);
1634			if ((protocol_types != NULL) && (CFArrayGetCount(protocol_types) > 0)) {
1635				SCNetworkServiceRef	service;
1636
1637				service = SCNetworkServiceCreate(setPrivate->prefs, interface);
1638				if (service == NULL) {
1639					SCLog(TRUE, LOG_DEBUG,
1640					      CFSTR("could not create service for \"%@\": %s\n"),
1641					      SCNetworkInterfaceGetLocalizedDisplayName(interface),
1642					      SCErrorString(SCError()));
1643					ok = FALSE;
1644					goto nextInterface;
1645				}
1646
1647				ok = SCNetworkServiceEstablishDefaultConfiguration(service);
1648				if (!ok) {
1649					SCLog(TRUE, LOG_DEBUG,
1650					      CFSTR("could not estabish default configuration for \"%@\": %s\n"),
1651					      SCNetworkInterfaceGetLocalizedDisplayName(interface),
1652					      SCErrorString(SCError()));
1653					SCNetworkServiceRemove(service);
1654					CFRelease(service);
1655					goto nextInterface;
1656				}
1657
1658				ok = SCNetworkSetAddService(set, service);
1659				if (!ok) {
1660					SCLog(TRUE, LOG_DEBUG,
1661					      CFSTR("could not add service for \"%@\": %s\n"),
1662					      SCNetworkInterfaceGetLocalizedDisplayName(interface),
1663					      SCErrorString(SCError()));
1664					SCNetworkServiceRemove(service);
1665					CFRelease(service);
1666					goto nextInterface;
1667				}
1668
1669				CFRelease(service);
1670				updated = TRUE;
1671			} else {
1672				add_supported_interfaces(interface_list, interface);
1673			}
1674
1675		    nextInterface :
1676
1677			CFArrayRemoveValueAtIndex(interface_list, 0);
1678		}
1679		CFRelease(interface_list);
1680	}
1681	if (updatedIFs)		CFRelease(interfaces);
1682	if (services != NULL)	CFRelease(services);
1683	if (excluded != NULL)	CFRelease(excluded);
1684
1685#if	TARGET_OS_IPHONE
1686	if (orphans != NULL) {
1687		if (ok && updated) {
1688			CFIndex	i;
1689			CFIndex	n	= CFArrayGetCount(orphans);
1690
1691			for (i = 0; i < n; i++) {
1692				SCNetworkServiceRef	service;
1693
1694				service = CFArrayGetValueAtIndex(orphans, i);
1695				if (_SCNetworkServiceIsVPN(service)) {
1696					ok = SCNetworkSetAddService(set, service);
1697					if (!ok) {
1698						break;
1699					}
1700				}
1701			}
1702		}
1703
1704		CFRelease(orphans);
1705	}
1706#endif	// TARGET_OS_IPHONE
1707
1708	if (ok && !updated) {
1709		// if no changes were made
1710		_SCErrorSet(kSCStatusOK);
1711	}
1712
1713	return updated;
1714}
1715
1716
1717Boolean
1718SCNetworkSetEstablishDefaultConfiguration(SCNetworkSetRef set)
1719{
1720	CFArrayRef		interfaces;
1721	SCNetworkSetPrivateRef	setPrivate	= (SCNetworkSetPrivateRef)set;
1722	Boolean			updated		= FALSE;
1723
1724	if (!isA_SCNetworkSet(set)) {
1725		_SCErrorSet(kSCStatusInvalidArgument);
1726		return FALSE;
1727	}
1728
1729	interfaces = _SCNetworkInterfaceCopyAllWithPreferences(setPrivate->prefs);
1730	if (interfaces != NULL) {
1731		updated = __SCNetworkSetEstablishDefaultConfigurationForInterfaces(set, interfaces, TRUE);
1732		CFRelease(interfaces);
1733	}
1734
1735	return updated;
1736}
1737
1738
1739Boolean
1740SCNetworkSetEstablishDefaultInterfaceConfiguration(SCNetworkSetRef set, SCNetworkInterfaceRef interface)
1741{
1742	CFArrayRef	interfaces;
1743	Boolean		updated;
1744
1745	if (!isA_SCNetworkSet(set)) {
1746		_SCErrorSet(kSCStatusInvalidArgument);
1747		return FALSE;
1748	}
1749
1750	if (!isA_SCNetworkInterface(interface)) {
1751		_SCErrorSet(kSCStatusInvalidArgument);
1752		return FALSE;
1753	}
1754
1755	interfaces = CFArrayCreate(NULL, (const void **)&interface, 1, &kCFTypeArrayCallBacks);
1756	assert(interfaces != NULL);
1757	updated = __SCNetworkSetEstablishDefaultConfigurationForInterfaces(set, interfaces, FALSE);
1758	CFRelease(interfaces);
1759
1760	return updated;
1761}
1762
1763
1764SCNetworkServiceRef
1765SCNetworkSetCopySelectedVPNService(SCNetworkSetRef set)
1766{
1767	CFIndex			i;
1768	CFIndex			n;
1769	SCNetworkServiceRef	selected	= NULL;
1770	CFArrayRef		services;
1771	CFMutableArrayRef	services_vpn	= NULL;
1772
1773	if (!isA_SCNetworkSet(set)) {
1774		_SCErrorSet(kSCStatusInvalidArgument);
1775		return NULL;
1776	}
1777
1778	services = SCNetworkSetCopyServices(set);
1779	if (services != NULL) {
1780		n = CFArrayGetCount(services);
1781		for (i = 0; i < n; i++) {
1782			SCNetworkServiceRef	service;
1783
1784			service = CFArrayGetValueAtIndex(services, i);
1785			if (!SCNetworkServiceGetEnabled(service)) {
1786				// if not enabled
1787				continue;
1788			}
1789
1790			if (!_SCNetworkServiceIsVPN(service)) {
1791				// if not VPN service
1792				continue;
1793			}
1794
1795			if (services_vpn == NULL) {
1796				services_vpn = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1797			}
1798			CFArrayAppendValue(services_vpn, service);
1799		}
1800
1801		CFRelease(services);
1802	}
1803
1804	if (services_vpn == NULL) {
1805		// if no VPN services
1806		return NULL;
1807	}
1808
1809	n = CFArrayGetCount(services_vpn);
1810	if (n > 1) {
1811		CFArrayRef		order;
1812		CFMutableArrayRef	sorted;
1813
1814		order = SCNetworkSetGetServiceOrder(set);
1815		sorted = CFArrayCreateMutableCopy(NULL, 0, services_vpn);
1816		CFArraySortValues(sorted,
1817				  CFRangeMake(0, CFArrayGetCount(sorted)),
1818				  _SCNetworkServiceCompare,
1819				  (void *)order);
1820		CFRelease(services_vpn);
1821		services_vpn = sorted;
1822	}
1823
1824#if	TARGET_OS_IPHONE
1825	if (n > 1) {
1826		CFStringRef	serviceID_prefs;
1827
1828#define VPN_PREFERENCES	CFSTR("com.apple.mobilevpn")
1829#define VPN_SERVICE_ID	CFSTR("activeVPNID")
1830
1831		CFPreferencesAppSynchronize(VPN_PREFERENCES);
1832		serviceID_prefs = CFPreferencesCopyAppValue(VPN_SERVICE_ID, VPN_PREFERENCES);
1833		if (serviceID_prefs != NULL) {
1834			for (i = 0; i < n; i++) {
1835				SCNetworkServiceRef	service;
1836				CFStringRef		serviceID;
1837
1838				service = CFArrayGetValueAtIndex(services_vpn, i);
1839				serviceID = SCNetworkServiceGetServiceID(service);
1840				if (CFEqual(serviceID, serviceID_prefs)) {
1841					selected = service;
1842					CFRetain(selected);
1843					break;
1844				}
1845
1846			}
1847
1848			CFRelease(serviceID_prefs);
1849		}
1850	}
1851#endif	// TARGET_OS_IPHONE
1852
1853	if (selected == NULL) {
1854		selected = CFArrayGetValueAtIndex(services_vpn, 0);
1855		CFRetain(selected);
1856	}
1857
1858	CFRelease(services_vpn);
1859	return selected;
1860}
1861
1862
1863Boolean
1864SCNetworkSetSetSelectedVPNService(SCNetworkSetRef set, SCNetworkServiceRef service)
1865{
1866	Boolean		ok	= TRUE;
1867	CFArrayRef	services;
1868
1869	if (!isA_SCNetworkSet(set)) {
1870		_SCErrorSet(kSCStatusInvalidArgument);
1871		return FALSE;
1872	}
1873
1874	if (!isA_SCNetworkService(service) || !_SCNetworkServiceIsVPN(service)) {
1875		_SCErrorSet(kSCStatusInvalidArgument);
1876		return FALSE;
1877	}
1878
1879	services = SCNetworkSetCopyServices(set);
1880	if (services != NULL) {
1881		CFIndex	i;
1882		CFIndex	n	= CFArrayGetCount(services);
1883
1884		if (!CFArrayContainsValue(services, CFRangeMake(0, n), service)) {
1885			// if selected service not a member of the current set
1886			_SCErrorSet(kSCStatusInvalidArgument);
1887			ok = FALSE;
1888			goto done;
1889		}
1890
1891		for (i = 0; ok && (i < n); i++) {
1892			SCNetworkServiceRef	vpn;
1893
1894			vpn = CFArrayGetValueAtIndex(services, i);
1895			if (!_SCNetworkServiceIsVPN(vpn)) {
1896				// if not VPN service
1897				continue;
1898			}
1899
1900			ok = SCNetworkServiceSetEnabled(vpn, CFEqual(service, vpn));
1901		}
1902	}
1903
1904    done :
1905
1906	if (services != NULL) CFRelease(services);
1907	return ok;
1908}
1909
1910
1911Boolean
1912_SCNetworkSetSetSetID(SCNetworkSetRef set, CFStringRef newSetID)
1913{
1914	SCNetworkSetRef		currentSet		= NULL;
1915	SCNetworkSetPrivateRef	currentSetPrivate	= NULL;
1916	CFDictionaryRef		entity;
1917	CFStringRef		newPath;
1918	Boolean			ok			= FALSE;
1919	CFStringRef		oldPath			= NULL;
1920	SCNetworkSetPrivateRef	setPrivate		= (SCNetworkSetPrivateRef)set;
1921	Boolean			updateCurrentSet	= FALSE;
1922
1923	if (!isA_SCNetworkSet(set)) {
1924		_SCErrorSet(kSCStatusInvalidArgument);
1925		return FALSE;
1926	}
1927
1928	if (!isA_CFString(newSetID)) {
1929		_SCErrorSet(kSCStatusInvalidArgument);
1930		return FALSE;
1931	}
1932
1933	// If newSetID is equal to current setID, our work is done
1934	if (CFEqual(newSetID, setPrivate->setID)) {
1935		return TRUE;
1936	}
1937
1938	newPath = SCPreferencesPathKeyCreateSet(NULL, newSetID);
1939	entity = SCPreferencesPathGetValue(setPrivate->prefs, newPath);
1940	if (isA_CFDictionary(entity)) {
1941		// if the new set already exists
1942		_SCErrorSet(kSCStatusKeyExists);
1943		goto done;
1944	}
1945
1946	oldPath = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID);
1947	entity = SCPreferencesPathGetValue(setPrivate->prefs, oldPath);
1948	if (!isA_CFDictionary(entity)) {
1949		// if the set has already been removed
1950		_SCErrorSet(kSCStatusNoKey);
1951		goto done;
1952	}
1953
1954	ok = SCPreferencesPathSetValue(setPrivate->prefs, newPath, entity);
1955	if (!ok) {
1956		goto done;
1957	}
1958
1959	ok = SCPreferencesPathRemoveValue(setPrivate->prefs, oldPath);
1960	if (!ok) {
1961		goto done;
1962	}
1963
1964	// update current set (if needed)
1965	currentSet = SCNetworkSetCopyCurrent(setPrivate->prefs);
1966	if (currentSet != NULL) {
1967		currentSetPrivate = (SCNetworkSetPrivateRef)currentSet;
1968		if (CFEqual(currentSetPrivate->setID, setPrivate->setID)) {
1969			updateCurrentSet = TRUE;
1970		}
1971		CFRelease(currentSet);
1972	}
1973
1974	CFRetain(newSetID);
1975	CFRelease(setPrivate->setID);
1976
1977	setPrivate->setID = newSetID;
1978
1979	if (updateCurrentSet) {
1980		SCNetworkSetSetCurrent(set);
1981	}
1982
1983    done:
1984
1985	if (oldPath != NULL) {
1986		CFRelease(oldPath);
1987	}
1988	if (newPath != NULL) {
1989		CFRelease(newPath);
1990	}
1991
1992	return ok;
1993}
1994