]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCNetworkSet.c
configd-395.6.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCNetworkSet.c
1 /*
2 * Copyright (c) 2004-2007, 2009-2011 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
42 static CFStringRef __SCNetworkSetCopyDescription (CFTypeRef cf);
43 static void __SCNetworkSetDeallocate (CFTypeRef cf);
44 static Boolean __SCNetworkSetEqual (CFTypeRef cf1, CFTypeRef cf2);
45 static CFHashCode __SCNetworkSetHash (CFTypeRef cf);
46
47
48 static CFTypeID __kSCNetworkSetTypeID = _kCFRuntimeNotATypeID;
49
50
51 static 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
64 static pthread_once_t initialized = PTHREAD_ONCE_INIT;
65
66
67 static 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
87 static 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
103 static 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
122 static CFHashCode
123 __SCNetworkSetHash(CFTypeRef cf)
124 {
125 SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)cf;
126
127 return CFHash(setPrivate->setID);
128 }
129
130
131 static void
132 __SCNetworkSetInitialize(void)
133 {
134 __kSCNetworkSetTypeID = _CFRuntimeRegisterClass(&__SCNetworkSetClass);
135 return;
136 }
137
138
139 static 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
172 static 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
186 static 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 n = CFArrayGetCount(newOrder);
205
206 serviceID = SCNetworkServiceGetServiceID(service);
207 if (CFArrayContainsValue(newOrder, CFRangeMake(0, n), serviceID)) {
208 // if serviceID already present
209 goto done;
210 }
211
212 serviceOrder = _serviceOrder(service);
213
214 slot = 0;
215 for (i = 0; i < n; i++) {
216 int slotOrder;
217 SCNetworkServiceRef slotService;
218 CFStringRef slotServiceID;
219
220 slotServiceID = CFArrayGetValueAtIndex(newOrder, i);
221 if (!isA_CFString(slotServiceID)) {
222 // if bad prefs
223 continue;
224 }
225
226 slotService = SCNetworkServiceCopy(setPrivate->prefs, slotServiceID);
227 if (slotService == NULL) {
228 // if serviceID not valid
229 continue;
230 }
231
232 slotOrder = _serviceOrder(slotService);
233 if (serviceOrder >= slotOrder) {
234 // add the service *after* this one
235 slot = i + 1;
236 }
237
238 CFRelease(slotService);
239 }
240
241 CFArrayInsertValueAtIndex(newOrder, slot, serviceID);
242 (void) SCNetworkSetSetServiceOrder(set, newOrder);
243
244 done :
245
246 CFRelease(newOrder);
247
248 return;
249 }
250
251
252 static void
253 _serviceOrder_remove(SCNetworkSetRef set, SCNetworkServiceRef service)
254 {
255 CFMutableArrayRef newOrder;
256 CFArrayRef order;
257 CFStringRef serviceID;
258
259 order = SCNetworkSetGetServiceOrder(set);
260 if (order == NULL) {
261 return;
262 }
263
264 serviceID = SCNetworkServiceGetServiceID(service);
265
266 newOrder = CFArrayCreateMutableCopy(NULL, 0, order);
267 while (TRUE) {
268 CFIndex i;
269
270 i = CFArrayGetFirstIndexOfValue(newOrder,
271 CFRangeMake(0, CFArrayGetCount(newOrder)),
272 serviceID);
273 if (i == kCFNotFound) {
274 break;
275 }
276
277 CFArrayRemoveValueAtIndex(newOrder, i);
278 }
279 (void) SCNetworkSetSetServiceOrder(set, newOrder);
280 CFRelease(newOrder);
281
282 return;
283 }
284
285
286 #pragma mark -
287 #pragma mark SCNetworkSet APIs
288
289
290 #define N_QUICK 16
291
292
293 Boolean
294 SCNetworkSetAddService(SCNetworkSetRef set, SCNetworkServiceRef service)
295 {
296 SCNetworkInterfaceRef interface;
297 CFArrayRef interface_config = NULL;
298 CFStringRef link;
299 Boolean ok;
300 CFStringRef path;
301 SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service;
302 SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
303
304 if (!isA_SCNetworkSet(set)) {
305 _SCErrorSet(kSCStatusInvalidArgument);
306 return FALSE;
307 }
308
309 if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) {
310 _SCErrorSet(kSCStatusInvalidArgument);
311 return FALSE;
312 }
313
314 // make sure that we do not add an orphaned network service if its
315 // associated interface is a member of a bond or bridge.
316 interface = SCNetworkServiceGetInterface(service);
317 if ((interface != NULL) &&
318 __SCNetworkInterfaceIsMember(servicePrivate->prefs, interface)) {
319 _SCErrorSet(kSCStatusKeyExists);
320 return FALSE;
321 }
322
323 #define PREVENT_DUPLICATE_SERVICE_NAMES
324 #ifdef PREVENT_DUPLICATE_SERVICE_NAMES
325 CFStringRef name;
326
327 name = SCNetworkServiceGetName(service);
328 if (name != NULL) {
329 CFArrayRef services;
330
331 services = SCNetworkSetCopyServices(set);
332 if (services != NULL) {
333 CFIndex i;
334 CFIndex n;
335
336 n = CFArrayGetCount(services);
337 for (i = 0; i < n; i++) {
338 CFStringRef otherName;
339 SCNetworkServiceRef otherService;
340
341 otherService = CFArrayGetValueAtIndex(services, i);
342 otherName = SCNetworkServiceGetName(otherService);
343 if ((otherName != NULL) && CFEqual(name, otherName)) {
344 /*
345 * if a service with the same "name" is
346 * already a member of the set.
347 */
348 CFRelease(services);
349 _SCErrorSet(kSCStatusKeyExists);
350 return FALSE;
351 }
352 }
353
354 CFRelease(services);
355 }
356 }
357 #endif // PREVENT_DUPLICATE_SERVICE_NAMES
358
359 //#define PREVENT_DUPLICATE_SETS
360 #ifdef PREVENT_DUPLICATE_SETS
361 CFArrayRef sets;
362
363 // ensure that each service is only a member of ONE set
364 sets = SCNetworkSetCopyAll(setPrivate->prefs);
365 if (sets != NULL) {
366 CFIndex i;
367 CFIndex n;
368
369 n = CFArrayGetCount(sets);
370 for (i = 0; i < n; i++) {
371 Boolean found;
372 CFArrayRef services;
373 SCNetworkSetRef set;
374
375 set = CFArrayGetValueAtIndex(sets, i);
376 services = SCNetworkSetCopyServices(set);
377 found = CFArrayContainsValue(services,
378 CFRangeMake(0, CFArrayGetCount(services)),
379 service);
380 CFRelease(services);
381
382 if (found) {
383 CFRelease(sets);
384 _SCErrorSet(kSCStatusKeyExists);
385 return FALSE;
386 }
387 }
388 CFRelease(sets);
389 }
390 #endif /* PREVENT_DUPLICATE_SETS */
391
392 // get the [deep] interface configuration settings
393 interface = SCNetworkServiceGetInterface(service);
394 if (interface != NULL) {
395 interface_config = __SCNetworkInterfaceCopyDeepConfiguration(set, interface);
396 }
397
398 // create the link between "set" and the "service"
399 path = SCPreferencesPathKeyCreateSetNetworkServiceEntity(NULL, // allocator
400 setPrivate->setID, // set
401 servicePrivate->serviceID, // service
402 NULL); // entity
403 link = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL, // allocator
404 servicePrivate->serviceID, // service
405 NULL); // entity
406 ok = SCPreferencesPathSetLink(setPrivate->prefs, path, link);
407 CFRelease(path);
408 CFRelease(link);
409 if (!ok) {
410 goto done;
411 }
412
413 // push the [deep] interface configuration into all sets which contain this service.
414 if (interface != NULL) {
415 __SCNetworkInterfaceSetDeepConfiguration(set, interface, interface_config);
416 }
417
418 // add service to ServiceOrder
419 _serviceOrder_add(set, service);
420
421 // mark set as no longer "new"
422 setPrivate->established = TRUE;
423
424 done :
425
426 if (interface_config != NULL) CFRelease(interface_config);
427 return ok;
428 }
429
430
431 SCNetworkSetRef
432 SCNetworkSetCopy(SCPreferencesRef prefs, CFStringRef setID)
433 {
434 CFDictionaryRef entity;
435 CFStringRef path;
436 SCNetworkSetPrivateRef setPrivate;
437
438 if (!isA_CFString(setID)) {
439 _SCErrorSet(kSCStatusInvalidArgument);
440 return NULL;
441 }
442
443 path = SCPreferencesPathKeyCreateSet(NULL, setID);
444 entity = SCPreferencesPathGetValue(prefs, path);
445 CFRelease(path);
446
447 if (!isA_CFDictionary(entity)) {
448 _SCErrorSet(kSCStatusNoKey);
449 return NULL;
450 }
451
452 setPrivate = __SCNetworkSetCreatePrivate(NULL, prefs, setID);
453
454 // mark set as "old" (already established)
455 setPrivate->established = TRUE;
456
457 return (SCNetworkSetRef)setPrivate;
458 }
459
460
461 Boolean
462 SCNetworkSetContainsInterface(SCNetworkSetRef set, SCNetworkInterfaceRef interface)
463 {
464 Boolean found = FALSE;
465 CFArrayRef services;
466
467 services = SCNetworkSetCopyServices(set);
468 if (services != NULL) {
469 found = __SCNetworkServiceExistsForInterface(services, interface);
470 CFRelease(services);
471 }
472
473 return found;
474 }
475
476
477 CFArrayRef /* of SCNetworkSetRef's */
478 SCNetworkSetCopyAll(SCPreferencesRef prefs)
479 {
480 CFMutableArrayRef array;
481 CFIndex n;
482 CFStringRef path;
483 CFDictionaryRef sets;
484
485 path = SCPreferencesPathKeyCreateSets(NULL);
486 sets = SCPreferencesPathGetValue(prefs, path);
487 CFRelease(path);
488
489 if ((sets != NULL) && !isA_CFDictionary(sets)) {
490 return NULL;
491 }
492
493 array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
494
495 n = (sets != NULL) ? CFDictionaryGetCount(sets) : 0;
496 if (n > 0) {
497 CFIndex i;
498 const void * keys_q[N_QUICK];
499 const void ** keys = keys_q;
500 const void * vals_q[N_QUICK];
501 const void ** vals = vals_q;
502
503 if (n > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
504 keys = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
505 vals = CFAllocatorAllocate(NULL, n * sizeof(CFPropertyListRef), 0);
506 }
507 CFDictionaryGetKeysAndValues(sets, keys, vals);
508 for (i = 0; i < n; i++) {
509 SCNetworkSetPrivateRef setPrivate;
510
511 if (!isA_CFDictionary(vals[i])) {
512 SCLog(TRUE,
513 LOG_INFO,
514 CFSTR("SCNetworkSetCopyAll(): error w/set \"%@\"\n"),
515 keys[i]);
516 continue;
517 }
518
519 setPrivate = __SCNetworkSetCreatePrivate(NULL, prefs, keys[i]);
520
521 // mark set as "old" (already established)
522 setPrivate->established = TRUE;
523
524 CFArrayAppendValue(array, (SCNetworkSetRef)setPrivate);
525 CFRelease(setPrivate);
526 }
527 if (keys != keys_q) {
528 CFAllocatorDeallocate(NULL, keys);
529 CFAllocatorDeallocate(NULL, vals);
530 }
531 }
532
533 return array;
534 }
535
536
537 SCNetworkSetRef
538 SCNetworkSetCopyCurrent(SCPreferencesRef prefs)
539 {
540 CFArrayRef components;
541 CFStringRef currentID;
542 SCNetworkSetPrivateRef setPrivate = NULL;
543
544 currentID = SCPreferencesGetValue(prefs, kSCPrefCurrentSet);
545 if (!isA_CFString(currentID)) {
546 return NULL;
547 }
548
549 components = CFStringCreateArrayBySeparatingStrings(NULL, currentID, CFSTR("/"));
550 if (CFArrayGetCount(components) == 3) {
551 CFStringRef setID;
552 CFStringRef path;
553
554 setID = CFArrayGetValueAtIndex(components, 2);
555 path = SCPreferencesPathKeyCreateSet(NULL, setID);
556 if (CFEqual(path, currentID)) {
557 setPrivate = __SCNetworkSetCreatePrivate(NULL, prefs, setID);
558
559 // mark set as "old" (already established)
560 setPrivate->established = TRUE;
561 } else {
562 SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkSetCopyCurrent(): preferences are non-conformant"));
563 }
564 CFRelease(path);
565 }
566 CFRelease(components);
567
568 return (SCNetworkSetRef)setPrivate;
569 }
570
571
572 CFArrayRef /* of SCNetworkServiceRef's */
573 SCNetworkSetCopyServices(SCNetworkSetRef set)
574 {
575 CFMutableArrayRef array;
576 CFDictionaryRef dict;
577 CFIndex n;
578 CFStringRef path;
579 SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
580
581 if (!isA_SCNetworkSet(set)) {
582 _SCErrorSet(kSCStatusInvalidArgument);
583 return NULL;
584 }
585
586 path = SCPreferencesPathKeyCreateSetNetworkService(NULL, setPrivate->setID, NULL);
587 dict = SCPreferencesPathGetValue(setPrivate->prefs, path);
588 CFRelease(path);
589 if ((dict != NULL) && !isA_CFDictionary(dict)) {
590 return NULL;
591 }
592
593 array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
594
595 n = (dict != NULL) ? CFDictionaryGetCount(dict) : 0;
596 if (n > 0) {
597 CFIndex i;
598 const void * keys_q[N_QUICK];
599 const void ** keys = keys_q;
600
601 if (n > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
602 keys = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
603 }
604 CFDictionaryGetKeysAndValues(dict, keys, NULL);
605 for (i = 0; i < n; i++) {
606 CFArrayRef components;
607 CFStringRef link;
608
609 path = SCPreferencesPathKeyCreateSetNetworkServiceEntity(NULL,
610 setPrivate->setID,
611 (CFStringRef)keys[i],
612 NULL);
613 link = SCPreferencesPathGetLink(setPrivate->prefs, path);
614 CFRelease(path);
615 if (link == NULL) {
616 SCLog(TRUE,
617 LOG_INFO,
618 CFSTR("SCNetworkSetCopyServices(): service \"%@\" for set \"%@\" is not a link\n"),
619 keys[i],
620 setPrivate->setID);
621 continue; // if the service is not a link
622 }
623
624 components = CFStringCreateArrayBySeparatingStrings(NULL, link, CFSTR("/"));
625 if (CFArrayGetCount(components) == 3) {
626 CFStringRef serviceID;
627
628 serviceID = CFArrayGetValueAtIndex(components, 2);
629 path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL, // allocator
630 serviceID, // service
631 NULL); // entity
632 if (CFEqual(path, link)) {
633 SCNetworkServicePrivateRef servicePrivate;
634
635 servicePrivate = __SCNetworkServiceCreatePrivate(NULL,
636 setPrivate->prefs,
637 serviceID,
638 NULL);
639 CFArrayAppendValue(array, (SCNetworkServiceRef)servicePrivate);
640 CFRelease(servicePrivate);
641 }
642 CFRelease(path);
643 }
644 CFRelease(components);
645 }
646 if (keys != keys_q) {
647 CFAllocatorDeallocate(NULL, keys);
648 }
649 }
650
651 return array;
652 }
653
654
655 SCNetworkSetRef
656 SCNetworkSetCreate(SCPreferencesRef prefs)
657 {
658 CFArrayRef components;
659 CFDictionaryRef entity;
660 Boolean ok;
661 CFStringRef path;
662 CFStringRef prefix;
663 CFStringRef setID;
664 SCNetworkSetPrivateRef setPrivate;
665
666 prefix = SCPreferencesPathKeyCreateSets(NULL);
667 path = __SCPreferencesPathCreateUniqueChild_WithMoreSCFCompatibility(prefs, prefix);
668 if (path == NULL) path = SCPreferencesPathCreateUniqueChild(prefs, prefix);
669 CFRelease(prefix);
670 if (path == NULL) {
671 return NULL;
672 }
673
674 components = CFStringCreateArrayBySeparatingStrings(NULL, path, CFSTR("/"));
675 setID = CFArrayGetValueAtIndex(components, 2);
676 setPrivate = __SCNetworkSetCreatePrivate(NULL, prefs, setID);
677 CFRelease(components);
678
679 // mark set as "new" (not yet established)
680 setPrivate->established = FALSE;
681
682 // establish the set in the preferences
683 entity = CFDictionaryCreate(NULL,
684 NULL, NULL, 0,
685 &kCFTypeDictionaryKeyCallBacks,
686 &kCFTypeDictionaryValueCallBacks);
687 ok = SCPreferencesPathSetValue(prefs, path, entity);
688 CFRelease(path);
689 CFRelease(entity);
690 if (!ok) {
691 CFRelease(setPrivate);
692 setPrivate = NULL;
693 }
694
695 return (SCNetworkSetRef)setPrivate;
696 }
697
698
699 CFStringRef
700 SCNetworkSetGetSetID(SCNetworkSetRef set)
701 {
702 SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
703
704 if (!isA_SCNetworkSet(set)) {
705 _SCErrorSet(kSCStatusInvalidArgument);
706 return NULL;
707 }
708
709 return setPrivate->setID;
710 }
711
712
713 CFStringRef
714 SCNetworkSetGetName(SCNetworkSetRef set)
715 {
716 CFBundleRef bundle;
717 CFDictionaryRef entity;
718 CFStringRef path;
719 SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
720
721 if (!isA_SCNetworkSet(set)) {
722 _SCErrorSet(kSCStatusInvalidArgument);
723 return NULL;
724 }
725
726 if (setPrivate->name != NULL) {
727 return setPrivate->name;
728 }
729
730 path = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID);
731 entity = SCPreferencesPathGetValue(setPrivate->prefs, path);
732 CFRelease(path);
733
734 if (isA_CFDictionary(entity)) {
735 CFStringRef name;
736
737 name = CFDictionaryGetValue(entity, kSCPropUserDefinedName);
738 if (isA_CFString(name)) {
739 setPrivate->name = CFRetain(name);
740 }
741 }
742
743 bundle = _SC_CFBundleGet();
744 if (bundle != NULL) {
745 if (setPrivate->name != NULL) {
746 CFStringRef non_localized;
747
748 non_localized = _SC_CFBundleCopyNonLocalizedString(bundle,
749 CFSTR("DEFAULT_SET_NAME"),
750 CFSTR("Automatic"),
751 NULL);
752 if (non_localized != NULL) {
753 if (CFEqual(setPrivate->name, non_localized)) {
754 CFStringRef localized;
755
756 // if "Automatic", return localized name
757 localized = CFBundleCopyLocalizedString(bundle,
758 CFSTR("DEFAULT_SET_NAME"),
759 CFSTR("Automatic"),
760 NULL);
761 if (localized != NULL) {
762 CFRelease(setPrivate->name);
763 setPrivate->name = localized;
764 }
765 }
766
767 CFRelease(non_localized);
768 }
769 }
770 }
771
772 return setPrivate->name;
773 }
774
775
776 CFArrayRef /* of serviceID CFStringRef's */
777 SCNetworkSetGetServiceOrder(SCNetworkSetRef set)
778 {
779 CFDictionaryRef dict;
780 CFStringRef path;
781 CFArrayRef serviceOrder;
782 SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
783
784 if (!isA_SCNetworkSet(set)) {
785 _SCErrorSet(kSCStatusInvalidArgument);
786 return NULL;
787 }
788
789 path = SCPreferencesPathKeyCreateSetNetworkGlobalEntity(NULL, setPrivate->setID, kSCEntNetIPv4);
790 if (path == NULL) {
791 return NULL;
792 }
793
794 dict = SCPreferencesPathGetValue(setPrivate->prefs, path);
795 CFRelease(path);
796 if (!isA_CFDictionary(dict)) {
797 return NULL;
798 }
799
800 serviceOrder = CFDictionaryGetValue(dict, kSCPropNetServiceOrder);
801 serviceOrder = isA_CFArray(serviceOrder);
802
803 return serviceOrder;
804 }
805
806
807 CFTypeID
808 SCNetworkSetGetTypeID(void)
809 {
810 pthread_once(&initialized, __SCNetworkSetInitialize); /* initialize runtime */
811 return __kSCNetworkSetTypeID;
812 }
813
814
815 Boolean
816 SCNetworkSetRemove(SCNetworkSetRef set)
817 {
818 CFStringRef currentPath;
819 Boolean ok = FALSE;
820 CFStringRef path;
821 SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
822
823 if (!isA_SCNetworkSet(set)) {
824 _SCErrorSet(kSCStatusInvalidArgument);
825 return FALSE;
826 }
827
828 currentPath = SCPreferencesGetValue(setPrivate->prefs, kSCPrefCurrentSet);
829 path = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID);
830 if (!isA_CFString(currentPath) || !CFEqual(currentPath, path)) {
831 ok = SCPreferencesPathRemoveValue(setPrivate->prefs, path);
832 } else {
833 _SCErrorSet(kSCStatusInvalidArgument);
834 }
835 CFRelease(path);
836
837 return ok;
838 }
839
840
841 Boolean
842 SCNetworkSetRemoveService(SCNetworkSetRef set, SCNetworkServiceRef service)
843 {
844 SCNetworkInterfaceRef interface;
845 CFArrayRef interface_config = NULL;
846 Boolean ok;
847 CFStringRef path;
848 int sc_status = kSCStatusOK;
849 SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service;
850 SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
851
852 if (!isA_SCNetworkSet(set)) {
853 _SCErrorSet(kSCStatusInvalidArgument);
854 return FALSE;
855 }
856
857 if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) {
858 _SCErrorSet(kSCStatusInvalidArgument);
859 return FALSE;
860 }
861
862 // remove service from ServiceOrder
863 _serviceOrder_remove(set, service);
864
865 // get the [deep] interface configuration settings
866 interface = SCNetworkServiceGetInterface(service);
867 if (interface != NULL) {
868 interface_config = __SCNetworkInterfaceCopyDeepConfiguration(set, interface);
869 if (interface_config != NULL) {
870 // remove the interface configuration from all sets which contain this service.
871 __SCNetworkInterfaceSetDeepConfiguration(set, interface, NULL);
872 }
873 }
874
875 // remove the link between "set" and the "service"
876 path = SCPreferencesPathKeyCreateSetNetworkServiceEntity(NULL,
877 setPrivate->setID,
878 servicePrivate->serviceID,
879 NULL);
880 ok = SCPreferencesPathRemoveValue(setPrivate->prefs, path);
881 if (!ok) {
882 sc_status = SCError(); // preserve the error
883 }
884 CFRelease(path);
885
886 // push the [deep] interface configuration [back] into all sets which contain the service.
887 if (interface_config != NULL) {
888 __SCNetworkInterfaceSetDeepConfiguration(set, interface, interface_config);
889 }
890
891 if (interface_config != NULL) CFRelease(interface_config);
892 if (!ok) {
893 _SCErrorSet(sc_status);
894 }
895 return ok;
896 }
897
898
899 Boolean
900 SCNetworkSetSetCurrent(SCNetworkSetRef set)
901 {
902 Boolean ok;
903 CFStringRef path;
904 SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
905
906 if (!isA_SCNetworkSet(set)) {
907 _SCErrorSet(kSCStatusInvalidArgument);
908 return FALSE;
909 }
910
911 path = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID);
912 ok = SCPreferencesSetValue(setPrivate->prefs, kSCPrefCurrentSet, path);
913 CFRelease(path);
914 return ok;
915 }
916
917
918 Boolean
919 SCNetworkSetSetName(SCNetworkSetRef set, CFStringRef name)
920 {
921 CFBundleRef bundle = NULL;
922 CFDictionaryRef entity;
923 CFStringRef localized = NULL;
924 CFStringRef non_localized = NULL;
925 Boolean ok = FALSE;
926 CFStringRef path;
927 SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
928
929 if (!isA_SCNetworkSet(set)) {
930 _SCErrorSet(kSCStatusInvalidArgument);
931 return FALSE;
932 }
933
934 if ((name != NULL) && !isA_CFString(name)) {
935 _SCErrorSet(kSCStatusInvalidArgument);
936 return FALSE;
937 }
938
939 // if known, compare against localized name
940
941 if (name != NULL) {
942 bundle = _SC_CFBundleGet();
943 if (bundle != NULL) {
944 non_localized = _SC_CFBundleCopyNonLocalizedString(bundle,
945 CFSTR("DEFAULT_SET_NAME"),
946 CFSTR("Automatic"),
947 NULL);
948 if (non_localized != NULL) {
949 if (CFEqual(name, non_localized)) {
950 localized = CFBundleCopyLocalizedString(bundle,
951 CFSTR("DEFAULT_SET_NAME"),
952 CFSTR("Automatic"),
953 NULL);
954 if (localized != NULL) {
955 name = localized;
956 }
957 }
958 }
959 }
960 }
961
962 #define PREVENT_DUPLICATE_SET_NAMES
963 #ifdef PREVENT_DUPLICATE_SET_NAMES
964 if (name != NULL) {
965 CFArrayRef sets;
966
967 // ensure that each set is uniquely named
968
969 sets = SCNetworkSetCopyAll(setPrivate->prefs);
970 if (sets != NULL) {
971 CFIndex i;
972 CFIndex n;
973
974 n = CFArrayGetCount(sets);
975 for (i = 0; i < n; i++) {
976 CFStringRef otherID;
977 CFStringRef otherName;
978 SCNetworkSetRef set = CFArrayGetValueAtIndex(sets, i);
979
980 otherID = SCNetworkSetGetSetID(set);
981 if (CFEqual(setPrivate->setID, otherID)) {
982 continue; // skip current set
983 }
984
985 otherName = SCNetworkSetGetName(set);
986 if ((otherName != NULL) && CFEqual(name, otherName)) {
987 // if "name" not unique
988 CFRelease(sets);
989 _SCErrorSet(kSCStatusKeyExists);
990 goto done;
991 }
992 }
993 CFRelease(sets);
994 }
995 }
996 #endif /* PREVENT_DUPLICATE_SET_NAMES */
997
998 // if known, store non-localized name
999
1000 if ((name != NULL) && (bundle != NULL) && (non_localized != NULL)) {
1001 if (localized == NULL) {
1002 localized = CFBundleCopyLocalizedString(bundle,
1003 CFSTR("DEFAULT_SET_NAME"),
1004 CFSTR("Automatic"),
1005 NULL);
1006 }
1007
1008 if (localized != NULL) {
1009 if (CFEqual(name, localized)) {
1010 name = non_localized;
1011 }
1012 }
1013 }
1014
1015 // update the "name"
1016
1017 path = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID);
1018 entity = SCPreferencesPathGetValue(setPrivate->prefs, path);
1019 if (isA_CFDictionary(entity) ||
1020 ((entity == NULL) && (name != NULL))) {
1021 CFMutableDictionaryRef newEntity;
1022
1023 if (entity != NULL) {
1024 newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity);
1025 } else {
1026 newEntity = CFDictionaryCreateMutable(NULL,
1027 0,
1028 &kCFTypeDictionaryKeyCallBacks,
1029 &kCFTypeDictionaryValueCallBacks);
1030 }
1031 if (name != NULL) {
1032 CFDictionarySetValue(newEntity, kSCPropUserDefinedName, name);
1033 } else {
1034 CFDictionaryRemoveValue(newEntity, kSCPropUserDefinedName);
1035 }
1036 ok = SCPreferencesPathSetValue(setPrivate->prefs, path, newEntity);
1037 CFRelease(newEntity);
1038 }
1039 CFRelease(path);
1040
1041 done :
1042
1043 if (localized != NULL) CFRelease(localized);
1044 if (non_localized != NULL) CFRelease(non_localized);
1045 return ok;
1046 }
1047
1048
1049 Boolean
1050 SCNetworkSetSetServiceOrder(SCNetworkSetRef set, CFArrayRef newOrder)
1051 {
1052 CFDictionaryRef dict;
1053 CFMutableDictionaryRef newDict;
1054 Boolean ok;
1055 CFStringRef path;
1056 SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
1057
1058 if (!isA_SCNetworkSet(set)) {
1059 _SCErrorSet(kSCStatusInvalidArgument);
1060 return FALSE;
1061 }
1062
1063 if (isA_CFArray(newOrder)) {
1064 CFIndex i;
1065 CFIndex n = CFArrayGetCount(newOrder);
1066
1067 for (i = 0; i < n; i++) {
1068 CFStringRef serviceID;
1069
1070 serviceID = CFArrayGetValueAtIndex(newOrder, i);
1071 if (!isA_CFString(serviceID)) {
1072 _SCErrorSet(kSCStatusInvalidArgument);
1073 return FALSE;
1074 }
1075 }
1076 } else {
1077 _SCErrorSet(kSCStatusInvalidArgument);
1078 return FALSE;
1079 }
1080
1081 path = SCPreferencesPathKeyCreateSetNetworkGlobalEntity(NULL, setPrivate->setID, kSCEntNetIPv4);
1082 if (path == NULL) {
1083 return FALSE;
1084 }
1085
1086 dict = SCPreferencesPathGetValue(setPrivate->prefs, path);
1087 if (dict != NULL) {
1088 newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
1089 } else {
1090 newDict = CFDictionaryCreateMutable(NULL,
1091 0,
1092 &kCFTypeDictionaryKeyCallBacks,
1093 &kCFTypeDictionaryValueCallBacks);
1094 }
1095
1096 CFDictionarySetValue(newDict, kSCPropNetServiceOrder, newOrder);
1097 ok = SCPreferencesPathSetValue(setPrivate->prefs, path, newDict);
1098 CFRelease(newDict);
1099 CFRelease(path);
1100
1101 return ok;
1102 }
1103
1104
1105 #pragma mark -
1106 #pragma mark SCNetworkSet SPIs
1107
1108
1109 static void
1110 add_supported_interfaces(CFMutableArrayRef interface_list, SCNetworkInterfaceRef interface)
1111 {
1112 CFIndex i;
1113 CFArrayRef interface_types;
1114 CFIndex n;
1115
1116 interface_types = SCNetworkInterfaceGetSupportedInterfaceTypes(interface);
1117 n = (interface_types != NULL) ? CFArrayGetCount(interface_types) : 0;
1118 for (i = 0; i < n; i++) {
1119 SCNetworkInterfaceRef parent;
1120 CFStringRef interface_type;
1121
1122 interface_type = CFArrayGetValueAtIndex(interface_types, i);
1123 parent = SCNetworkInterfaceCreateWithInterface(interface, interface_type);
1124 if (parent != NULL) {
1125 CFArrayAppendValue(interface_list, parent);
1126 CFRelease(parent);
1127 }
1128 }
1129
1130 return;
1131 }
1132
1133 static CFSetRef /* of SCNetworkInterfaceRef's */
1134 copyExcludedInterfaces(SCPreferencesRef prefs)
1135 {
1136 CFMutableSetRef excluded;
1137 CFArrayRef interfaces;
1138
1139 excluded = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
1140
1141 // exclude Bond [member] interfaces
1142 interfaces = SCBondInterfaceCopyAll(prefs);
1143 if (interfaces != NULL) {
1144 __SCBondInterfaceListCollectMembers(interfaces, excluded);
1145 CFRelease(interfaces);
1146 }
1147
1148 // exclude Bridge [member] interfaces
1149 interfaces = SCBridgeInterfaceCopyAll(prefs);
1150 if (interfaces != NULL) {
1151 __SCBridgeInterfaceListCollectMembers(interfaces, excluded);
1152 CFRelease(interfaces);
1153 }
1154
1155 return excluded;
1156 }
1157
1158
1159 static Boolean
1160 __SCNetworkSetEstablishDefaultConfigurationForInterfaces(SCNetworkSetRef set, CFArrayRef interfaces)
1161 {
1162 CFSetRef excluded = NULL;
1163 CFIndex i;
1164 CFIndex n = 0;
1165 Boolean ok = TRUE;
1166 CFArrayRef services;
1167 SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
1168 Boolean updated = FALSE;
1169
1170 #if TARGET_OS_IPHONE
1171 CFArrayRef orphans = NULL;
1172 CFArrayRef sets;
1173
1174 sets = SCNetworkSetCopyAll(setPrivate->prefs);
1175 if (sets != NULL) {
1176 if (CFArrayGetCount(sets) == 1) {
1177 services = SCNetworkSetCopyServices(set);
1178 if (services != NULL) {
1179 n = CFArrayGetCount(services);
1180 CFRelease(services);
1181 }
1182
1183 if ((n == 0) && CFEqual(set, CFArrayGetValueAtIndex(sets, 0))) {
1184 // after a "Reset Network Settings" we need to find (and
1185 // add back) any VPN services that were orphaned.
1186 orphans = SCNetworkServiceCopyAll(setPrivate->prefs);
1187 }
1188 }
1189
1190 CFRelease(sets);
1191 }
1192 #endif // TARGET_OS_IPHONE
1193
1194 // first, assume that we only want to add new services
1195 // for those interfaces that are not represented in the
1196 // current set.
1197 services = SCNetworkSetCopyServices(set);
1198 if ((services != NULL) && setPrivate->established) {
1199 // but, if we are given an existing (or "established") set
1200 // than we only want to add new services for those interfaces
1201 // that are not represented in *any* set.
1202 CFRelease(services);
1203 services = SCNetworkServiceCopyAll(setPrivate->prefs);
1204 }
1205
1206 excluded = copyExcludedInterfaces(setPrivate->prefs);
1207
1208 n = (interfaces != NULL) ? CFArrayGetCount(interfaces) : 0;
1209 for (i = 0; i < n; i++) {
1210 SCNetworkInterfaceRef interface;
1211 CFMutableArrayRef interface_list;
1212
1213 interface = CFArrayGetValueAtIndex(interfaces, i);
1214 if ((excluded != NULL)
1215 && CFSetContainsValue(excluded, interface)) {
1216 // if this interface is a member of a Bond or Bridge
1217 continue;
1218 }
1219
1220 if (__SCNetworkServiceExistsForInterface(services, interface)) {
1221 // if this is not a new interface
1222 continue;
1223 }
1224
1225 interface_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1226 CFArrayAppendValue(interface_list, interface);
1227
1228 while (ok && (CFArrayGetCount(interface_list) > 0)) {
1229 CFArrayRef protocol_types;
1230
1231 interface = CFArrayGetValueAtIndex(interface_list, 0);
1232
1233 protocol_types = SCNetworkInterfaceGetSupportedProtocolTypes(interface);
1234 if ((protocol_types != NULL) && (CFArrayGetCount(protocol_types) > 0)) {
1235 SCNetworkServiceRef service;
1236
1237 service = SCNetworkServiceCreate(setPrivate->prefs, interface);
1238 if (service == NULL) {
1239 SCLog(TRUE, LOG_DEBUG,
1240 CFSTR("could not create service for \"%@\": %s\n"),
1241 SCNetworkInterfaceGetLocalizedDisplayName(interface),
1242 SCErrorString(SCError()));
1243 ok = FALSE;
1244 goto nextInterface;
1245 }
1246
1247 ok = SCNetworkServiceEstablishDefaultConfiguration(service);
1248 if (!ok) {
1249 SCLog(TRUE, LOG_DEBUG,
1250 CFSTR("could not estabish default configuration for \"%@\": %s\n"),
1251 SCNetworkInterfaceGetLocalizedDisplayName(interface),
1252 SCErrorString(SCError()));
1253 SCNetworkServiceRemove(service);
1254 CFRelease(service);
1255 goto nextInterface;
1256 }
1257
1258 while (TRUE) {
1259 CFStringRef newName;
1260
1261 ok = SCNetworkSetAddService(set, service);
1262 if (ok) {
1263 break;
1264 }
1265
1266 if (SCError() != kSCStatusKeyExists) {
1267 SCLog(TRUE, LOG_DEBUG,
1268 CFSTR("could not add service for \"%@\": %s\n"),
1269 SCNetworkInterfaceGetLocalizedDisplayName(interface),
1270 SCErrorString(SCError()));
1271 SCNetworkServiceRemove(service);
1272 CFRelease(service);
1273 goto nextInterface;
1274 }
1275
1276 // we have two interfaces with the same service
1277 // name, acquire a new, hopefully unique, name
1278
1279 newName = __SCNetworkServiceNextName(service);
1280 if (newName == NULL) {
1281 SCLog(TRUE, LOG_DEBUG,
1282 CFSTR("could not set unique name for \"%@\": %s\n"),
1283 SCNetworkInterfaceGetLocalizedDisplayName(interface),
1284 SCErrorString(SCError()));
1285 SCNetworkServiceRemove(service);
1286 CFRelease(service);
1287 goto nextInterface;
1288 }
1289
1290 ok = SCNetworkServiceSetName(service, newName);
1291 CFRelease(newName);
1292 if (ok) {
1293 continue;
1294 }
1295
1296 if (SCError() != kSCStatusKeyExists) {
1297 SCLog(TRUE, LOG_DEBUG,
1298 CFSTR("could not set unique name for \"%@\": %s\n"),
1299 SCNetworkInterfaceGetLocalizedDisplayName(interface),
1300 SCErrorString(SCError()));
1301 SCNetworkServiceRemove(service);
1302 CFRelease(service);
1303 goto nextInterface;
1304 }
1305 }
1306
1307 CFRelease(service);
1308 updated = TRUE;
1309 } else {
1310 add_supported_interfaces(interface_list, interface);
1311 }
1312
1313 nextInterface :
1314
1315 CFArrayRemoveValueAtIndex(interface_list, 0);
1316 }
1317 CFRelease(interface_list);
1318 }
1319 if (services != NULL) CFRelease(services);
1320 if (excluded != NULL) CFRelease(excluded);
1321
1322 #if TARGET_OS_IPHONE
1323 if (orphans != NULL) {
1324 if (ok && updated) {
1325 CFIndex i;
1326 CFIndex n = CFArrayGetCount(orphans);
1327
1328 for (i = 0; i < n; i++) {
1329 SCNetworkServiceRef service;
1330
1331 service = CFArrayGetValueAtIndex(orphans, i);
1332 if (_SCNetworkServiceIsVPN(service)) {
1333 ok = SCNetworkSetAddService(set, service);
1334 if (!ok) {
1335 break;
1336 }
1337 }
1338 }
1339 }
1340
1341 CFRelease(orphans);
1342 }
1343 #endif // TARGET_OS_IPHONE
1344
1345 if (ok && !updated) {
1346 // if no changes were made
1347 _SCErrorSet(kSCStatusOK);
1348 }
1349
1350 return updated;
1351 }
1352
1353
1354 Boolean
1355 SCNetworkSetEstablishDefaultConfiguration(SCNetworkSetRef set)
1356 {
1357 CFArrayRef interfaces;
1358 SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
1359 Boolean updated = FALSE;
1360
1361 if (!isA_SCNetworkSet(set)) {
1362 _SCErrorSet(kSCStatusInvalidArgument);
1363 return FALSE;
1364 }
1365
1366 interfaces = _SCNetworkInterfaceCopyAllWithPreferences(setPrivate->prefs);
1367 if (interfaces != NULL) {
1368 updated = __SCNetworkSetEstablishDefaultConfigurationForInterfaces(set, interfaces);
1369 CFRelease(interfaces);
1370 }
1371
1372 return updated;
1373 }
1374
1375
1376 Boolean
1377 SCNetworkSetEstablishDefaultInterfaceConfiguration(SCNetworkSetRef set, SCNetworkInterfaceRef interface)
1378 {
1379 CFArrayRef interfaces;
1380 Boolean updated;
1381
1382 if (!isA_SCNetworkSet(set)) {
1383 _SCErrorSet(kSCStatusInvalidArgument);
1384 return FALSE;
1385 }
1386
1387 if (!isA_SCNetworkInterface(interface)) {
1388 _SCErrorSet(kSCStatusInvalidArgument);
1389 return FALSE;
1390 }
1391
1392 interfaces = CFArrayCreate(NULL, (const void **)&interface, 1, &kCFTypeArrayCallBacks);
1393 updated = __SCNetworkSetEstablishDefaultConfigurationForInterfaces(set, interfaces);
1394 CFRelease(interfaces);
1395
1396 return updated;
1397 }
1398
1399
1400 SCNetworkServiceRef
1401 SCNetworkSetCopySelectedVPNService(SCNetworkSetRef set)
1402 {
1403 CFIndex i;
1404 CFIndex n;
1405 SCNetworkServiceRef selected = NULL;
1406 CFArrayRef services;
1407 CFMutableArrayRef services_vpn = NULL;
1408
1409 if (!isA_SCNetworkSet(set)) {
1410 _SCErrorSet(kSCStatusInvalidArgument);
1411 return NULL;
1412 }
1413
1414 services = SCNetworkSetCopyServices(set);
1415 if (services != NULL) {
1416 n = CFArrayGetCount(services);
1417 for (i = 0; i < n; i++) {
1418 SCNetworkServiceRef service;
1419
1420 service = CFArrayGetValueAtIndex(services, i);
1421 if (!SCNetworkServiceGetEnabled(service)) {
1422 // if not enabled
1423 continue;
1424 }
1425
1426 if (!_SCNetworkServiceIsVPN(service)) {
1427 // if not VPN service
1428 continue;
1429 }
1430
1431 if (services_vpn == NULL) {
1432 services_vpn = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1433 }
1434 CFArrayAppendValue(services_vpn, service);
1435 }
1436
1437 CFRelease(services);
1438 }
1439
1440 if (services_vpn == NULL) {
1441 // if no VPN services
1442 return NULL;
1443 }
1444
1445 n = CFArrayGetCount(services_vpn);
1446 if (n > 1) {
1447 CFArrayRef order;
1448 CFMutableArrayRef sorted;
1449
1450 order = SCNetworkSetGetServiceOrder(set);
1451 sorted = CFArrayCreateMutableCopy(NULL, 0, services_vpn);
1452 CFArraySortValues(sorted,
1453 CFRangeMake(0, CFArrayGetCount(sorted)),
1454 _SCNetworkServiceCompare,
1455 (void *)order);
1456 CFRelease(services_vpn);
1457 services_vpn = sorted;
1458 }
1459
1460 #if TARGET_OS_IPHONE
1461 if (n > 1) {
1462 CFStringRef serviceID_prefs;
1463
1464 #define VPN_PREFERENCES CFSTR("com.apple.mobilevpn")
1465 #define VPN_SERVICE_ID CFSTR("activeVPNID")
1466
1467 CFPreferencesAppSynchronize(VPN_PREFERENCES);
1468 serviceID_prefs = CFPreferencesCopyAppValue(VPN_SERVICE_ID, VPN_PREFERENCES);
1469 if (serviceID_prefs != NULL) {
1470 for (i = 0; i < n; i++) {
1471 SCNetworkServiceRef service;
1472 CFStringRef serviceID;
1473
1474 service = CFArrayGetValueAtIndex(services_vpn, i);
1475 serviceID = SCNetworkServiceGetServiceID(service);
1476 if (CFEqual(serviceID, serviceID_prefs)) {
1477 selected = service;
1478 CFRetain(selected);
1479 break;
1480 }
1481
1482 }
1483
1484 CFRelease(serviceID_prefs);
1485 }
1486 }
1487 #endif // TARGET_OS_IPHONE
1488
1489 if (selected == NULL) {
1490 selected = CFArrayGetValueAtIndex(services_vpn, 0);
1491 CFRetain(selected);
1492 }
1493
1494 CFRelease(services_vpn);
1495 return selected;
1496 }
1497
1498
1499 Boolean
1500 SCNetworkSetSetSelectedVPNService(SCNetworkSetRef set, SCNetworkServiceRef service)
1501 {
1502 Boolean ok = TRUE;
1503 CFArrayRef services;
1504
1505 if (!isA_SCNetworkSet(set)) {
1506 _SCErrorSet(kSCStatusInvalidArgument);
1507 return FALSE;
1508 }
1509
1510 if (!isA_SCNetworkService(service) || !_SCNetworkServiceIsVPN(service)) {
1511 _SCErrorSet(kSCStatusInvalidArgument);
1512 return FALSE;
1513 }
1514
1515 services = SCNetworkSetCopyServices(set);
1516 if (services != NULL) {
1517 CFIndex i;
1518 CFIndex n = CFArrayGetCount(services);
1519
1520 if (!CFArrayContainsValue(services, CFRangeMake(0, n), service)) {
1521 // if selected service not a member of the current set
1522 _SCErrorSet(kSCStatusInvalidArgument);
1523 ok = FALSE;
1524 goto done;
1525 }
1526
1527 for (i = 0; ok && (i < n); i++) {
1528 SCNetworkServiceRef vpn;
1529
1530 vpn = CFArrayGetValueAtIndex(services, i);
1531 if (!_SCNetworkServiceIsVPN(vpn)) {
1532 // if not VPN service
1533 continue;
1534 }
1535
1536 ok = SCNetworkServiceSetEnabled(vpn, CFEqual(service, vpn));
1537 }
1538 }
1539
1540 done :
1541
1542 if (services != NULL) CFRelease(services);
1543 return ok;
1544 }