]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCNetworkSet.c
126300fe820bea67e1ac3a1ee4f036a62b85dda8
[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 #if !TARGET_OS_IPHONE
1142 // exclude Bond [member] interfaces
1143 interfaces = SCBondInterfaceCopyAll(prefs);
1144 if (interfaces != NULL) {
1145 __SCBondInterfaceListCollectMembers(interfaces, excluded);
1146 CFRelease(interfaces);
1147 }
1148 #endif // !TARGET_OS_IPHONE
1149
1150 // exclude Bridge [member] interfaces
1151 interfaces = SCBridgeInterfaceCopyAll(prefs);
1152 if (interfaces != NULL) {
1153 __SCBridgeInterfaceListCollectMembers(interfaces, excluded);
1154 CFRelease(interfaces);
1155 }
1156
1157 return excluded;
1158 }
1159
1160
1161 static Boolean
1162 __SCNetworkSetEstablishDefaultConfigurationForInterfaces(SCNetworkSetRef set, CFArrayRef interfaces)
1163 {
1164 CFSetRef excluded = NULL;
1165 CFIndex i;
1166 CFIndex n = 0;
1167 Boolean ok = TRUE;
1168 CFArrayRef services;
1169 SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
1170 Boolean updated = FALSE;
1171
1172 #if TARGET_OS_IPHONE
1173 CFArrayRef orphans = NULL;
1174 CFArrayRef sets;
1175
1176 sets = SCNetworkSetCopyAll(setPrivate->prefs);
1177 if (sets != NULL) {
1178 if (CFArrayGetCount(sets) == 1) {
1179 services = SCNetworkSetCopyServices(set);
1180 if (services != NULL) {
1181 n = CFArrayGetCount(services);
1182 CFRelease(services);
1183 }
1184
1185 if ((n == 0) && CFEqual(set, CFArrayGetValueAtIndex(sets, 0))) {
1186 // after a "Reset Network Settings" we need to find (and
1187 // add back) any VPN services that were orphaned.
1188 orphans = SCNetworkServiceCopyAll(setPrivate->prefs);
1189 }
1190 }
1191
1192 CFRelease(sets);
1193 }
1194 #endif // TARGET_OS_IPHONE
1195
1196 // first, assume that we only want to add new services
1197 // for those interfaces that are not represented in the
1198 // current set.
1199 services = SCNetworkSetCopyServices(set);
1200 if ((services != NULL) && setPrivate->established) {
1201 // but, if we are given an existing (or "established") set
1202 // than we only want to add new services for those interfaces
1203 // that are not represented in *any* set.
1204 CFRelease(services);
1205 services = SCNetworkServiceCopyAll(setPrivate->prefs);
1206 }
1207
1208 excluded = copyExcludedInterfaces(setPrivate->prefs);
1209
1210 n = (interfaces != NULL) ? CFArrayGetCount(interfaces) : 0;
1211 for (i = 0; i < n; i++) {
1212 SCNetworkInterfaceRef interface;
1213 CFMutableArrayRef interface_list;
1214
1215 interface = CFArrayGetValueAtIndex(interfaces, i);
1216 if ((excluded != NULL)
1217 && CFSetContainsValue(excluded, interface)) {
1218 // if this interface is a member of a Bond or Bridge
1219 continue;
1220 }
1221
1222 if (__SCNetworkServiceExistsForInterface(services, interface)) {
1223 // if this is not a new interface
1224 continue;
1225 }
1226
1227 interface_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1228 CFArrayAppendValue(interface_list, interface);
1229
1230 while (ok && (CFArrayGetCount(interface_list) > 0)) {
1231 CFArrayRef protocol_types;
1232
1233 interface = CFArrayGetValueAtIndex(interface_list, 0);
1234
1235 protocol_types = SCNetworkInterfaceGetSupportedProtocolTypes(interface);
1236 if ((protocol_types != NULL) && (CFArrayGetCount(protocol_types) > 0)) {
1237 SCNetworkServiceRef service;
1238
1239 service = SCNetworkServiceCreate(setPrivate->prefs, interface);
1240 if (service == NULL) {
1241 SCLog(TRUE, LOG_DEBUG,
1242 CFSTR("could not create service for \"%@\": %s\n"),
1243 SCNetworkInterfaceGetLocalizedDisplayName(interface),
1244 SCErrorString(SCError()));
1245 ok = FALSE;
1246 goto nextInterface;
1247 }
1248
1249 ok = SCNetworkServiceEstablishDefaultConfiguration(service);
1250 if (!ok) {
1251 SCLog(TRUE, LOG_DEBUG,
1252 CFSTR("could not estabish default configuration for \"%@\": %s\n"),
1253 SCNetworkInterfaceGetLocalizedDisplayName(interface),
1254 SCErrorString(SCError()));
1255 SCNetworkServiceRemove(service);
1256 CFRelease(service);
1257 goto nextInterface;
1258 }
1259
1260 while (TRUE) {
1261 CFStringRef newName;
1262
1263 ok = SCNetworkSetAddService(set, service);
1264 if (ok) {
1265 break;
1266 }
1267
1268 if (SCError() != kSCStatusKeyExists) {
1269 SCLog(TRUE, LOG_DEBUG,
1270 CFSTR("could not add service for \"%@\": %s\n"),
1271 SCNetworkInterfaceGetLocalizedDisplayName(interface),
1272 SCErrorString(SCError()));
1273 SCNetworkServiceRemove(service);
1274 CFRelease(service);
1275 goto nextInterface;
1276 }
1277
1278 // we have two interfaces with the same service
1279 // name, acquire a new, hopefully unique, name
1280
1281 newName = __SCNetworkServiceNextName(service);
1282 if (newName == NULL) {
1283 SCLog(TRUE, LOG_DEBUG,
1284 CFSTR("could not set unique name for \"%@\": %s\n"),
1285 SCNetworkInterfaceGetLocalizedDisplayName(interface),
1286 SCErrorString(SCError()));
1287 SCNetworkServiceRemove(service);
1288 CFRelease(service);
1289 goto nextInterface;
1290 }
1291
1292 ok = SCNetworkServiceSetName(service, newName);
1293 CFRelease(newName);
1294 if (ok) {
1295 continue;
1296 }
1297
1298 if (SCError() != kSCStatusKeyExists) {
1299 SCLog(TRUE, LOG_DEBUG,
1300 CFSTR("could not set unique name for \"%@\": %s\n"),
1301 SCNetworkInterfaceGetLocalizedDisplayName(interface),
1302 SCErrorString(SCError()));
1303 SCNetworkServiceRemove(service);
1304 CFRelease(service);
1305 goto nextInterface;
1306 }
1307 }
1308
1309 CFRelease(service);
1310 updated = TRUE;
1311 } else {
1312 add_supported_interfaces(interface_list, interface);
1313 }
1314
1315 nextInterface :
1316
1317 CFArrayRemoveValueAtIndex(interface_list, 0);
1318 }
1319 CFRelease(interface_list);
1320 }
1321 if (services != NULL) CFRelease(services);
1322 if (excluded != NULL) CFRelease(excluded);
1323
1324 #if TARGET_OS_IPHONE
1325 if (orphans != NULL) {
1326 if (ok && updated) {
1327 CFIndex i;
1328 CFIndex n = CFArrayGetCount(orphans);
1329
1330 for (i = 0; i < n; i++) {
1331 SCNetworkServiceRef service;
1332
1333 service = CFArrayGetValueAtIndex(orphans, i);
1334 if (_SCNetworkServiceIsVPN(service)) {
1335 ok = SCNetworkSetAddService(set, service);
1336 if (!ok) {
1337 break;
1338 }
1339 }
1340 }
1341 }
1342
1343 CFRelease(orphans);
1344 }
1345 #endif // TARGET_OS_IPHONE
1346
1347 if (ok && !updated) {
1348 // if no changes were made
1349 _SCErrorSet(kSCStatusOK);
1350 }
1351
1352 return updated;
1353 }
1354
1355
1356 Boolean
1357 SCNetworkSetEstablishDefaultConfiguration(SCNetworkSetRef set)
1358 {
1359 CFArrayRef interfaces;
1360 SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
1361 Boolean updated = FALSE;
1362
1363 if (!isA_SCNetworkSet(set)) {
1364 _SCErrorSet(kSCStatusInvalidArgument);
1365 return FALSE;
1366 }
1367
1368 interfaces = _SCNetworkInterfaceCopyAllWithPreferences(setPrivate->prefs);
1369 if (interfaces != NULL) {
1370 updated = __SCNetworkSetEstablishDefaultConfigurationForInterfaces(set, interfaces);
1371 CFRelease(interfaces);
1372 }
1373
1374 return updated;
1375 }
1376
1377
1378 Boolean
1379 SCNetworkSetEstablishDefaultInterfaceConfiguration(SCNetworkSetRef set, SCNetworkInterfaceRef interface)
1380 {
1381 CFArrayRef interfaces;
1382 Boolean updated;
1383
1384 if (!isA_SCNetworkSet(set)) {
1385 _SCErrorSet(kSCStatusInvalidArgument);
1386 return FALSE;
1387 }
1388
1389 if (!isA_SCNetworkInterface(interface)) {
1390 _SCErrorSet(kSCStatusInvalidArgument);
1391 return FALSE;
1392 }
1393
1394 interfaces = CFArrayCreate(NULL, (const void **)&interface, 1, &kCFTypeArrayCallBacks);
1395 updated = __SCNetworkSetEstablishDefaultConfigurationForInterfaces(set, interfaces);
1396 CFRelease(interfaces);
1397
1398 return updated;
1399 }
1400
1401
1402 SCNetworkServiceRef
1403 SCNetworkSetCopySelectedVPNService(SCNetworkSetRef set)
1404 {
1405 CFIndex i;
1406 CFIndex n;
1407 SCNetworkServiceRef selected = NULL;
1408 CFArrayRef services;
1409 CFMutableArrayRef services_vpn = NULL;
1410
1411 if (!isA_SCNetworkSet(set)) {
1412 _SCErrorSet(kSCStatusInvalidArgument);
1413 return NULL;
1414 }
1415
1416 services = SCNetworkSetCopyServices(set);
1417 if (services != NULL) {
1418 n = CFArrayGetCount(services);
1419 for (i = 0; i < n; i++) {
1420 SCNetworkServiceRef service;
1421
1422 service = CFArrayGetValueAtIndex(services, i);
1423 if (!SCNetworkServiceGetEnabled(service)) {
1424 // if not enabled
1425 continue;
1426 }
1427
1428 if (!_SCNetworkServiceIsVPN(service)) {
1429 // if not VPN service
1430 continue;
1431 }
1432
1433 if (services_vpn == NULL) {
1434 services_vpn = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1435 }
1436 CFArrayAppendValue(services_vpn, service);
1437 }
1438
1439 CFRelease(services);
1440 }
1441
1442 if (services_vpn == NULL) {
1443 // if no VPN services
1444 return NULL;
1445 }
1446
1447 n = CFArrayGetCount(services_vpn);
1448 if (n > 1) {
1449 CFArrayRef order;
1450 CFMutableArrayRef sorted;
1451
1452 order = SCNetworkSetGetServiceOrder(set);
1453 sorted = CFArrayCreateMutableCopy(NULL, 0, services_vpn);
1454 CFArraySortValues(sorted,
1455 CFRangeMake(0, CFArrayGetCount(sorted)),
1456 _SCNetworkServiceCompare,
1457 (void *)order);
1458 CFRelease(services_vpn);
1459 services_vpn = sorted;
1460 }
1461
1462 #if TARGET_OS_IPHONE
1463 if (n > 1) {
1464 CFStringRef serviceID_prefs;
1465
1466 #define VPN_PREFERENCES CFSTR("com.apple.mobilevpn")
1467 #define VPN_SERVICE_ID CFSTR("activeVPNID")
1468
1469 CFPreferencesAppSynchronize(VPN_PREFERENCES);
1470 serviceID_prefs = CFPreferencesCopyAppValue(VPN_SERVICE_ID, VPN_PREFERENCES);
1471 if (serviceID_prefs != NULL) {
1472 for (i = 0; i < n; i++) {
1473 SCNetworkServiceRef service;
1474 CFStringRef serviceID;
1475
1476 service = CFArrayGetValueAtIndex(services_vpn, i);
1477 serviceID = SCNetworkServiceGetServiceID(service);
1478 if (CFEqual(serviceID, serviceID_prefs)) {
1479 selected = service;
1480 CFRetain(selected);
1481 break;
1482 }
1483
1484 }
1485
1486 CFRelease(serviceID_prefs);
1487 }
1488 }
1489 #endif // TARGET_OS_IPHONE
1490
1491 if (selected == NULL) {
1492 selected = CFArrayGetValueAtIndex(services_vpn, 0);
1493 CFRetain(selected);
1494 }
1495
1496 CFRelease(services_vpn);
1497 return selected;
1498 }
1499
1500
1501 Boolean
1502 SCNetworkSetSetSelectedVPNService(SCNetworkSetRef set, SCNetworkServiceRef service)
1503 {
1504 Boolean ok = TRUE;
1505 CFArrayRef services;
1506
1507 if (!isA_SCNetworkSet(set)) {
1508 _SCErrorSet(kSCStatusInvalidArgument);
1509 return FALSE;
1510 }
1511
1512 if (!isA_SCNetworkService(service) || !_SCNetworkServiceIsVPN(service)) {
1513 _SCErrorSet(kSCStatusInvalidArgument);
1514 return FALSE;
1515 }
1516
1517 services = SCNetworkSetCopyServices(set);
1518 if (services != NULL) {
1519 CFIndex i;
1520 CFIndex n = CFArrayGetCount(services);
1521
1522 if (!CFArrayContainsValue(services, CFRangeMake(0, n), service)) {
1523 // if selected service not a member of the current set
1524 _SCErrorSet(kSCStatusInvalidArgument);
1525 ok = FALSE;
1526 goto done;
1527 }
1528
1529 for (i = 0; ok && (i < n); i++) {
1530 SCNetworkServiceRef vpn;
1531
1532 vpn = CFArrayGetValueAtIndex(services, i);
1533 if (!_SCNetworkServiceIsVPN(vpn)) {
1534 // if not VPN service
1535 continue;
1536 }
1537
1538 ok = SCNetworkServiceSetEnabled(vpn, CFEqual(service, vpn));
1539 }
1540 }
1541
1542 done :
1543
1544 if (services != NULL) CFRelease(services);
1545 return ok;
1546 }