#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFRuntime.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include "SCNetworkConfigurationInternal.h"
#include <SystemConfiguration/SCValidation.h>
#include <SystemConfiguration/SCPrivate.h>
#include <pthread.h>
static CFStringRef __SCNetworkSetCopyDescription (CFTypeRef cf);
static void __SCNetworkSetDeallocate (CFTypeRef cf);
static Boolean __SCNetworkSetEqual (CFTypeRef cf1, CFTypeRef cf2);
static CFHashCode __SCNetworkSetHash (CFTypeRef cf);
static CFTypeID __kSCNetworkSetTypeID = _kCFRuntimeNotATypeID;
static const CFRuntimeClass __SCNetworkSetClass = {
0, "SCNetworkSet", NULL, NULL, __SCNetworkSetDeallocate, __SCNetworkSetEqual, __SCNetworkSetHash, NULL, __SCNetworkSetCopyDescription };
static pthread_once_t initialized = PTHREAD_ONCE_INIT;
static CFStringRef
__SCNetworkSetCopyDescription(CFTypeRef cf)
{
CFAllocatorRef allocator = CFGetAllocator(cf);
CFMutableStringRef result;
SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)cf;
result = CFStringCreateMutable(allocator, 0);
CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkSet %p [%p]> {"), cf, allocator);
CFStringAppendFormat(result, NULL, CFSTR("id = %@"), setPrivate->setID);
CFStringAppendFormat(result, NULL, CFSTR(", prefs = %p"), setPrivate->prefs);
if (setPrivate->name != NULL) {
CFStringAppendFormat(result, NULL, CFSTR(", name = %@"), setPrivate->name);
}
CFStringAppendFormat(result, NULL, CFSTR("}"));
return result;
}
static void
__SCNetworkSetDeallocate(CFTypeRef cf)
{
SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)cf;
CFRelease(setPrivate->setID);
CFRelease(setPrivate->prefs);
if (setPrivate->name != NULL)
CFRelease(setPrivate->name);
return;
}
static Boolean
__SCNetworkSetEqual(CFTypeRef cf1, CFTypeRef cf2)
{
SCNetworkSetPrivateRef s1 = (SCNetworkSetPrivateRef)cf1;
SCNetworkSetPrivateRef s2 = (SCNetworkSetPrivateRef)cf2;
if (s1 == s2)
return TRUE;
if (s1->prefs != s2->prefs)
return FALSE;
if (!CFEqual(s1->setID, s2->setID))
return FALSE;
return TRUE;
}
static CFHashCode
__SCNetworkSetHash(CFTypeRef cf)
{
SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)cf;
return CFHash(setPrivate->setID);
}
static void
__SCNetworkSetInitialize(void)
{
__kSCNetworkSetTypeID = _CFRuntimeRegisterClass(&__SCNetworkSetClass);
return;
}
static SCNetworkSetPrivateRef
__SCNetworkSetCreatePrivate(CFAllocatorRef allocator,
SCPreferencesRef prefs,
CFStringRef setID)
{
SCNetworkSetPrivateRef setPrivate;
uint32_t size;
pthread_once(&initialized, __SCNetworkSetInitialize);
size = sizeof(SCNetworkSetPrivate) - sizeof(CFRuntimeBase);
setPrivate = (SCNetworkSetPrivateRef)_CFRuntimeCreateInstance(allocator,
__kSCNetworkSetTypeID,
size,
NULL);
if (setPrivate == NULL) {
return NULL;
}
setPrivate->setID = CFStringCreateCopy(NULL, setID);
setPrivate->prefs = CFRetain(prefs);
setPrivate->name = NULL;
setPrivate->established = FALSE;
return setPrivate;
}
#pragma mark -
static int
_serviceOrder(SCNetworkServiceRef service)
{
SCNetworkInterfaceRef interface;
interface = SCNetworkServiceGetInterface(service);
if ((interface == NULL) || _SCNetworkServiceIsVPN(service)) {
return 100000; }
return __SCNetworkInterfaceOrder(interface);
}
static void
_serviceOrder_add(SCNetworkSetRef set, SCNetworkServiceRef service)
{
CFIndex i;
CFIndex n;
CFMutableArrayRef newOrder;
CFArrayRef order;
CFStringRef serviceID;
CFIndex serviceOrder;
SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
CFIndex slot;
order = SCNetworkSetGetServiceOrder(set);
if (order != NULL) {
newOrder = CFArrayCreateMutableCopy(NULL, 0, order);
} else {
newOrder = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
}
assert(newOrder != NULL);
n = CFArrayGetCount(newOrder);
serviceID = SCNetworkServiceGetServiceID(service);
if (CFArrayContainsValue(newOrder, CFRangeMake(0, n), serviceID)) {
goto done;
}
serviceOrder = _serviceOrder(service);
slot = 0;
for (i = 0; i < n; i++) {
int slotOrder;
SCNetworkServiceRef slotService;
CFStringRef slotServiceID;
slotServiceID = CFArrayGetValueAtIndex(newOrder, i);
if (!isA_CFString(slotServiceID)) {
continue;
}
slotService = SCNetworkServiceCopy(setPrivate->prefs, slotServiceID);
if (slotService == NULL) {
continue;
}
slotOrder = _serviceOrder(slotService);
if (serviceOrder >= slotOrder) {
slot = i + 1;
}
CFRelease(slotService);
}
CFArrayInsertValueAtIndex(newOrder, slot, serviceID);
(void) SCNetworkSetSetServiceOrder(set, newOrder);
done :
CFRelease(newOrder);
return;
}
static void
_serviceOrder_remove(SCNetworkSetRef set, SCNetworkServiceRef service)
{
CFMutableArrayRef newOrder;
CFArrayRef order;
CFStringRef serviceID;
order = SCNetworkSetGetServiceOrder(set);
if (order == NULL) {
return;
}
serviceID = SCNetworkServiceGetServiceID(service);
newOrder = CFArrayCreateMutableCopy(NULL, 0, order);
while (TRUE) {
CFIndex i;
i = CFArrayGetFirstIndexOfValue(newOrder,
CFRangeMake(0, CFArrayGetCount(newOrder)),
serviceID);
if (i == kCFNotFound) {
break;
}
CFArrayRemoveValueAtIndex(newOrder, i);
}
(void) SCNetworkSetSetServiceOrder(set, newOrder);
CFRelease(newOrder);
return;
}
#pragma mark -
#pragma mark SCNetworkSet APIs
#define N_QUICK 16
Boolean
SCNetworkSetAddService(SCNetworkSetRef set, SCNetworkServiceRef service)
{
SCNetworkInterfaceRef interface;
CFArrayRef interface_config = NULL;
CFStringRef link;
Boolean ok;
CFStringRef path;
SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service;
SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
if (!isA_SCNetworkSet(set)) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
interface = SCNetworkServiceGetInterface(service);
if ((interface != NULL) &&
__SCNetworkInterfaceIsMember(servicePrivate->prefs, interface)) {
_SCErrorSet(kSCStatusKeyExists);
return FALSE;
}
#define PREVENT_DUPLICATE_SERVICE_NAMES
#ifdef PREVENT_DUPLICATE_SERVICE_NAMES
CFStringRef name;
name = SCNetworkServiceGetName(service);
if (name != NULL) {
CFArrayRef services;
services = SCNetworkSetCopyServices(set);
if (services != NULL) {
CFIndex i;
CFIndex n;
n = CFArrayGetCount(services);
for (i = 0; i < n; i++) {
CFStringRef otherName;
SCNetworkServiceRef otherService;
otherService = CFArrayGetValueAtIndex(services, i);
otherName = SCNetworkServiceGetName(otherService);
if ((otherName != NULL) && CFEqual(name, otherName)) {
CFRelease(services);
_SCErrorSet(kSCStatusKeyExists);
return FALSE;
}
}
CFRelease(services);
}
}
#endif // PREVENT_DUPLICATE_SERVICE_NAMES
#ifdef PREVENT_DUPLICATE_SETS
CFArrayRef sets;
sets = SCNetworkSetCopyAll(setPrivate->prefs);
if (sets != NULL) {
CFIndex i;
CFIndex n;
n = CFArrayGetCount(sets);
for (i = 0; i < n; i++) {
Boolean found;
CFArrayRef services;
SCNetworkSetRef set;
set = CFArrayGetValueAtIndex(sets, i);
services = SCNetworkSetCopyServices(set);
found = CFArrayContainsValue(services,
CFRangeMake(0, CFArrayGetCount(services)),
service);
CFRelease(services);
if (found) {
CFRelease(sets);
_SCErrorSet(kSCStatusKeyExists);
return FALSE;
}
}
CFRelease(sets);
}
#endif
interface = SCNetworkServiceGetInterface(service);
if (interface != NULL) {
interface_config = __SCNetworkInterfaceCopyDeepConfiguration(set, interface);
}
path = SCPreferencesPathKeyCreateSetNetworkServiceEntity(NULL, setPrivate->setID, servicePrivate->serviceID, NULL); link = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL, servicePrivate->serviceID, NULL); ok = SCPreferencesPathSetLink(setPrivate->prefs, path, link);
CFRelease(path);
CFRelease(link);
if (!ok) {
goto done;
}
if (interface != NULL) {
__SCNetworkInterfaceSetDeepConfiguration(set, interface, interface_config);
}
_serviceOrder_add(set, service);
setPrivate->established = TRUE;
done :
if (interface_config != NULL) CFRelease(interface_config);
return ok;
}
SCNetworkSetRef
SCNetworkSetCopy(SCPreferencesRef prefs, CFStringRef setID)
{
CFDictionaryRef entity;
CFStringRef path;
SCNetworkSetPrivateRef setPrivate;
if (!isA_CFString(setID)) {
_SCErrorSet(kSCStatusInvalidArgument);
return NULL;
}
path = SCPreferencesPathKeyCreateSet(NULL, setID);
entity = SCPreferencesPathGetValue(prefs, path);
CFRelease(path);
if (!isA_CFDictionary(entity)) {
_SCErrorSet(kSCStatusNoKey);
return NULL;
}
setPrivate = __SCNetworkSetCreatePrivate(NULL, prefs, setID);
assert(setPrivate != NULL);
setPrivate->established = TRUE;
return (SCNetworkSetRef)setPrivate;
}
Boolean
SCNetworkSetContainsInterface(SCNetworkSetRef set, SCNetworkInterfaceRef interface)
{
Boolean found = FALSE;
CFArrayRef services;
services = SCNetworkSetCopyServices(set);
if (services != NULL) {
found = __SCNetworkServiceExistsForInterface(services, interface);
CFRelease(services);
}
return found;
}
CFArrayRef
SCNetworkSetCopyAll(SCPreferencesRef prefs)
{
CFMutableArrayRef array;
CFIndex n;
CFStringRef path;
CFDictionaryRef sets;
path = SCPreferencesPathKeyCreateSets(NULL);
sets = SCPreferencesPathGetValue(prefs, path);
CFRelease(path);
if ((sets != NULL) && !isA_CFDictionary(sets)) {
return NULL;
}
array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
n = (sets != NULL) ? CFDictionaryGetCount(sets) : 0;
if (n > 0) {
CFIndex i;
const void * keys_q[N_QUICK];
const void ** keys = keys_q;
const void * vals_q[N_QUICK];
const void ** vals = vals_q;
if (n > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
keys = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
vals = CFAllocatorAllocate(NULL, n * sizeof(CFPropertyListRef), 0);
}
CFDictionaryGetKeysAndValues(sets, keys, vals);
for (i = 0; i < n; i++) {
SCNetworkSetPrivateRef setPrivate;
if (!isA_CFDictionary(vals[i])) {
SCLog(TRUE,
LOG_INFO,
CFSTR("SCNetworkSetCopyAll(): error w/set \"%@\"\n"),
keys[i]);
continue;
}
setPrivate = __SCNetworkSetCreatePrivate(NULL, prefs, keys[i]);
assert(setPrivate != NULL);
setPrivate->established = TRUE;
CFArrayAppendValue(array, (SCNetworkSetRef)setPrivate);
CFRelease(setPrivate);
}
if (keys != keys_q) {
CFAllocatorDeallocate(NULL, keys);
CFAllocatorDeallocate(NULL, vals);
}
}
return array;
}
CFArrayRef
SCNetworkSetCopyAvailableInterfaces(SCNetworkSetRef set)
{
CFMutableArrayRef available;
CFMutableSetRef excluded = NULL;
int i;
CFArrayRef interfaces;
int n_interfaces;
int n_exclusions = 0;
SCPreferencesRef prefs;
SCNetworkSetPrivateRef setPrivate;
setPrivate = (SCNetworkSetPrivateRef)set;
prefs = setPrivate->prefs;
interfaces = _SCNetworkInterfaceCopyAllWithPreferences(prefs);
n_interfaces = CFArrayGetCount(interfaces);
if (n_interfaces == 0) {
return interfaces;
}
if (prefs != NULL) {
CFArrayRef bridges = NULL;
excluded = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
#if !TARGET_OS_IPHONE
CFArrayRef bonds = NULL;
bonds = SCBondInterfaceCopyAll(prefs);
if (bonds != NULL) {
__SCBondInterfaceListCollectMembers(bonds, excluded);
CFRelease(bonds);
}
#endif
bridges = SCBridgeInterfaceCopyAll(prefs);
if (bridges != NULL) {
__SCBridgeInterfaceListCollectMembers(bridges, excluded);
CFRelease(bridges);
}
n_exclusions = CFSetGetCount(excluded);
}
if (n_exclusions == 0) {
if (excluded != NULL) {
CFRelease(excluded);
}
return interfaces;
}
available = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
for (i = 0; i < n_interfaces; i++) {
SCNetworkInterfaceRef interface;
interface = CFArrayGetValueAtIndex(interfaces, i);
if (CFSetContainsValue(excluded, interface)) {
continue;
}
CFArrayAppendValue(available, interface);
}
CFRelease(interfaces);
CFRelease(excluded);
return available;
}
SCNetworkSetRef
SCNetworkSetCopyCurrent(SCPreferencesRef prefs)
{
CFArrayRef components;
CFStringRef currentID;
SCNetworkSetPrivateRef setPrivate = NULL;
currentID = SCPreferencesGetValue(prefs, kSCPrefCurrentSet);
if (!isA_CFString(currentID)) {
return NULL;
}
components = CFStringCreateArrayBySeparatingStrings(NULL, currentID, CFSTR("/"));
if (CFArrayGetCount(components) == 3) {
CFStringRef setID;
CFStringRef path;
setID = CFArrayGetValueAtIndex(components, 2);
path = SCPreferencesPathKeyCreateSet(NULL, setID);
if (CFEqual(path, currentID)) {
setPrivate = __SCNetworkSetCreatePrivate(NULL, prefs, setID);
assert(setPrivate != NULL);
setPrivate->established = TRUE;
} else {
SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkSetCopyCurrent(): preferences are non-conformant"));
}
CFRelease(path);
}
CFRelease(components);
return (SCNetworkSetRef)setPrivate;
}
CFArrayRef
SCNetworkSetCopyServices(SCNetworkSetRef set)
{
CFMutableArrayRef array;
CFDictionaryRef dict;
CFIndex n;
CFStringRef path;
SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
if (!isA_SCNetworkSet(set)) {
_SCErrorSet(kSCStatusInvalidArgument);
return NULL;
}
path = SCPreferencesPathKeyCreateSetNetworkService(NULL, setPrivate->setID, NULL);
dict = SCPreferencesPathGetValue(setPrivate->prefs, path);
CFRelease(path);
if ((dict != NULL) && !isA_CFDictionary(dict)) {
return NULL;
}
array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
n = (dict != NULL) ? CFDictionaryGetCount(dict) : 0;
if (n > 0) {
CFIndex i;
const void * keys_q[N_QUICK];
const void ** keys = keys_q;
if (n > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
keys = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
}
CFDictionaryGetKeysAndValues(dict, keys, NULL);
for (i = 0; i < n; i++) {
CFArrayRef components;
CFStringRef link;
path = SCPreferencesPathKeyCreateSetNetworkServiceEntity(NULL,
setPrivate->setID,
(CFStringRef)keys[i],
NULL);
link = SCPreferencesPathGetLink(setPrivate->prefs, path);
CFRelease(path);
if (link == NULL) {
SCLog(TRUE,
LOG_INFO,
CFSTR("SCNetworkSetCopyServices(): service \"%@\" for set \"%@\" is not a link\n"),
keys[i],
setPrivate->setID);
continue; }
components = CFStringCreateArrayBySeparatingStrings(NULL, link, CFSTR("/"));
if (CFArrayGetCount(components) == 3) {
CFStringRef serviceID;
serviceID = CFArrayGetValueAtIndex(components, 2);
path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL, serviceID, NULL); if (CFEqual(path, link)) {
SCNetworkServicePrivateRef servicePrivate;
servicePrivate = __SCNetworkServiceCreatePrivate(NULL,
setPrivate->prefs,
serviceID,
NULL);
CFArrayAppendValue(array, (SCNetworkServiceRef)servicePrivate);
CFRelease(servicePrivate);
}
CFRelease(path);
}
CFRelease(components);
}
if (keys != keys_q) {
CFAllocatorDeallocate(NULL, keys);
}
}
return array;
}
SCNetworkSetRef
SCNetworkSetCreate(SCPreferencesRef prefs)
{
CFArrayRef components;
CFDictionaryRef entity;
Boolean ok;
CFStringRef path;
CFStringRef prefix;
CFStringRef setID;
SCNetworkSetPrivateRef setPrivate;
prefix = SCPreferencesPathKeyCreateSets(NULL);
path = __SCPreferencesPathCreateUniqueChild_WithMoreSCFCompatibility(prefs, prefix);
if (path == NULL) path = SCPreferencesPathCreateUniqueChild(prefs, prefix);
CFRelease(prefix);
if (path == NULL) {
return NULL;
}
components = CFStringCreateArrayBySeparatingStrings(NULL, path, CFSTR("/"));
setID = CFArrayGetValueAtIndex(components, 2);
setPrivate = __SCNetworkSetCreatePrivate(NULL, prefs, setID);
assert(setPrivate != NULL);
CFRelease(components);
setPrivate->established = FALSE;
entity = CFDictionaryCreate(NULL,
NULL, NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
ok = SCPreferencesPathSetValue(prefs, path, entity);
CFRelease(path);
CFRelease(entity);
if (!ok) {
CFRelease(setPrivate);
setPrivate = NULL;
}
return (SCNetworkSetRef)setPrivate;
}
CFStringRef
SCNetworkSetGetSetID(SCNetworkSetRef set)
{
SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
if (!isA_SCNetworkSet(set)) {
_SCErrorSet(kSCStatusInvalidArgument);
return NULL;
}
return setPrivate->setID;
}
CFStringRef
SCNetworkSetGetName(SCNetworkSetRef set)
{
CFBundleRef bundle;
CFDictionaryRef entity;
CFStringRef path;
SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
if (!isA_SCNetworkSet(set)) {
_SCErrorSet(kSCStatusInvalidArgument);
return NULL;
}
if (setPrivate->name != NULL) {
return setPrivate->name;
}
path = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID);
entity = SCPreferencesPathGetValue(setPrivate->prefs, path);
CFRelease(path);
if (isA_CFDictionary(entity)) {
CFStringRef name;
name = CFDictionaryGetValue(entity, kSCPropUserDefinedName);
if (isA_CFString(name)) {
setPrivate->name = CFRetain(name);
}
}
bundle = _SC_CFBundleGet();
if (bundle != NULL) {
if (setPrivate->name != NULL) {
CFStringRef non_localized;
non_localized = _SC_CFBundleCopyNonLocalizedString(bundle,
CFSTR("DEFAULT_SET_NAME"),
CFSTR("Automatic"),
NULL);
if (non_localized != NULL) {
if (CFEqual(setPrivate->name, non_localized)) {
CFStringRef localized;
localized = CFBundleCopyLocalizedString(bundle,
CFSTR("DEFAULT_SET_NAME"),
CFSTR("Automatic"),
NULL);
if (localized != NULL) {
CFRelease(setPrivate->name);
setPrivate->name = localized;
}
}
CFRelease(non_localized);
}
}
}
return setPrivate->name;
}
CFArrayRef
SCNetworkSetGetServiceOrder(SCNetworkSetRef set)
{
CFDictionaryRef dict;
CFStringRef path;
CFArrayRef serviceOrder;
SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
if (!isA_SCNetworkSet(set)) {
_SCErrorSet(kSCStatusInvalidArgument);
return NULL;
}
path = SCPreferencesPathKeyCreateSetNetworkGlobalEntity(NULL, setPrivate->setID, kSCEntNetIPv4);
if (path == NULL) {
return NULL;
}
dict = SCPreferencesPathGetValue(setPrivate->prefs, path);
CFRelease(path);
if (!isA_CFDictionary(dict)) {
return NULL;
}
serviceOrder = CFDictionaryGetValue(dict, kSCPropNetServiceOrder);
serviceOrder = isA_CFArray(serviceOrder);
return serviceOrder;
}
CFTypeID
SCNetworkSetGetTypeID(void)
{
pthread_once(&initialized, __SCNetworkSetInitialize);
return __kSCNetworkSetTypeID;
}
Boolean
SCNetworkSetRemove(SCNetworkSetRef set)
{
CFStringRef currentPath;
Boolean ok = FALSE;
CFStringRef path;
SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
if (!isA_SCNetworkSet(set)) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
currentPath = SCPreferencesGetValue(setPrivate->prefs, kSCPrefCurrentSet);
path = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID);
if (!isA_CFString(currentPath) || !CFEqual(currentPath, path)) {
ok = SCPreferencesPathRemoveValue(setPrivate->prefs, path);
} else {
_SCErrorSet(kSCStatusInvalidArgument);
}
CFRelease(path);
return ok;
}
Boolean
SCNetworkSetRemoveService(SCNetworkSetRef set, SCNetworkServiceRef service)
{
SCNetworkInterfaceRef interface;
CFArrayRef interface_config = NULL;
Boolean ok;
CFStringRef path;
int sc_status = kSCStatusOK;
SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service;
SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
if (!isA_SCNetworkSet(set)) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
_serviceOrder_remove(set, service);
interface = SCNetworkServiceGetInterface(service);
if (interface != NULL) {
interface_config = __SCNetworkInterfaceCopyDeepConfiguration(set, interface);
if (interface_config != NULL) {
__SCNetworkInterfaceSetDeepConfiguration(set, interface, NULL);
}
}
path = SCPreferencesPathKeyCreateSetNetworkServiceEntity(NULL,
setPrivate->setID,
servicePrivate->serviceID,
NULL);
ok = SCPreferencesPathRemoveValue(setPrivate->prefs, path);
if (!ok) {
sc_status = SCError(); }
CFRelease(path);
if (interface_config != NULL) {
__SCNetworkInterfaceSetDeepConfiguration(set, interface, interface_config);
}
if (interface_config != NULL) CFRelease(interface_config);
if (!ok) {
_SCErrorSet(sc_status);
}
return ok;
}
Boolean
SCNetworkSetSetCurrent(SCNetworkSetRef set)
{
Boolean ok;
CFStringRef path;
SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
if (!isA_SCNetworkSet(set)) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
path = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID);
ok = SCPreferencesSetValue(setPrivate->prefs, kSCPrefCurrentSet, path);
CFRelease(path);
return ok;
}
Boolean
SCNetworkSetSetName(SCNetworkSetRef set, CFStringRef name)
{
CFBundleRef bundle = NULL;
CFDictionaryRef entity;
CFStringRef localized = NULL;
CFStringRef non_localized = NULL;
Boolean ok = FALSE;
CFStringRef path;
SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
if (!isA_SCNetworkSet(set)) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
if ((name != NULL) && !isA_CFString(name)) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
if (name != NULL) {
bundle = _SC_CFBundleGet();
if (bundle != NULL) {
non_localized = _SC_CFBundleCopyNonLocalizedString(bundle,
CFSTR("DEFAULT_SET_NAME"),
CFSTR("Automatic"),
NULL);
if (non_localized != NULL) {
if (CFEqual(name, non_localized)) {
localized = CFBundleCopyLocalizedString(bundle,
CFSTR("DEFAULT_SET_NAME"),
CFSTR("Automatic"),
NULL);
if (localized != NULL) {
name = localized;
}
}
}
}
}
#define PREVENT_DUPLICATE_SET_NAMES
#ifdef PREVENT_DUPLICATE_SET_NAMES
if (name != NULL) {
CFArrayRef sets;
sets = SCNetworkSetCopyAll(setPrivate->prefs);
if (sets != NULL) {
CFIndex i;
CFIndex n;
n = CFArrayGetCount(sets);
for (i = 0; i < n; i++) {
CFStringRef otherID;
CFStringRef otherName;
SCNetworkSetRef set = CFArrayGetValueAtIndex(sets, i);
otherID = SCNetworkSetGetSetID(set);
if (CFEqual(setPrivate->setID, otherID)) {
continue; }
otherName = SCNetworkSetGetName(set);
if ((otherName != NULL) && CFEqual(name, otherName)) {
CFRelease(sets);
_SCErrorSet(kSCStatusKeyExists);
goto done;
}
}
CFRelease(sets);
}
}
#endif
if ((name != NULL) && (bundle != NULL) && (non_localized != NULL)) {
if (localized == NULL) {
localized = CFBundleCopyLocalizedString(bundle,
CFSTR("DEFAULT_SET_NAME"),
CFSTR("Automatic"),
NULL);
}
if (localized != NULL) {
if (CFEqual(name, localized)) {
name = non_localized;
}
}
}
path = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID);
entity = SCPreferencesPathGetValue(setPrivate->prefs, path);
if (isA_CFDictionary(entity) ||
((entity == NULL) && (name != NULL))) {
CFMutableDictionaryRef newEntity;
if (entity != NULL) {
newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity);
} else {
newEntity = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
}
if (name != NULL) {
CFDictionarySetValue(newEntity, kSCPropUserDefinedName, name);
} else {
CFDictionaryRemoveValue(newEntity, kSCPropUserDefinedName);
}
ok = SCPreferencesPathSetValue(setPrivate->prefs, path, newEntity);
CFRelease(newEntity);
}
CFRelease(path);
done :
if (localized != NULL) CFRelease(localized);
if (non_localized != NULL) CFRelease(non_localized);
return ok;
}
Boolean
SCNetworkSetSetServiceOrder(SCNetworkSetRef set, CFArrayRef newOrder)
{
CFDictionaryRef dict;
CFMutableDictionaryRef newDict;
Boolean ok;
CFStringRef path;
SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
if (!isA_SCNetworkSet(set)) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
if (isA_CFArray(newOrder)) {
CFIndex i;
CFIndex n = CFArrayGetCount(newOrder);
for (i = 0; i < n; i++) {
CFStringRef serviceID;
serviceID = CFArrayGetValueAtIndex(newOrder, i);
if (!isA_CFString(serviceID)) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
}
} else {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
path = SCPreferencesPathKeyCreateSetNetworkGlobalEntity(NULL, setPrivate->setID, kSCEntNetIPv4);
if (path == NULL) {
return FALSE;
}
dict = SCPreferencesPathGetValue(setPrivate->prefs, path);
if (dict != NULL) {
newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
} else {
newDict = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
}
CFDictionarySetValue(newDict, kSCPropNetServiceOrder, newOrder);
ok = SCPreferencesPathSetValue(setPrivate->prefs, path, newDict);
CFRelease(newDict);
CFRelease(path);
return ok;
}
#pragma mark -
#pragma mark SCNetworkSet SPIs
static void
add_supported_interfaces(CFMutableArrayRef interface_list, SCNetworkInterfaceRef interface)
{
CFIndex i;
CFArrayRef interface_types;
CFIndex n;
interface_types = SCNetworkInterfaceGetSupportedInterfaceTypes(interface);
n = (interface_types != NULL) ? CFArrayGetCount(interface_types) : 0;
for (i = 0; i < n; i++) {
SCNetworkInterfaceRef parent;
CFStringRef interface_type;
interface_type = CFArrayGetValueAtIndex(interface_types, i);
parent = SCNetworkInterfaceCreateWithInterface(interface, interface_type);
if (parent != NULL) {
CFArrayAppendValue(interface_list, parent);
CFRelease(parent);
}
}
return;
}
static CFSetRef
copyExcludedInterfaces(SCPreferencesRef prefs)
{
CFMutableSetRef excluded;
CFArrayRef interfaces;
excluded = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
#if !TARGET_OS_IPHONE
interfaces = SCBondInterfaceCopyAll(prefs);
if (interfaces != NULL) {
__SCBondInterfaceListCollectMembers(interfaces, excluded);
CFRelease(interfaces);
}
#endif // !TARGET_OS_IPHONE
interfaces = SCBridgeInterfaceCopyAll(prefs);
if (interfaces != NULL) {
__SCBridgeInterfaceListCollectMembers(interfaces, excluded);
CFRelease(interfaces);
}
return excluded;
}
#if !TARGET_OS_IPHONE
static SCBridgeInterfaceRef
copyAutoBridgeInterface(SCPreferencesRef prefs, CFStringRef bridgeName)
{
SCBridgeInterfaceRef bridge = NULL;
CFArrayRef interfaces;
interfaces = SCBridgeInterfaceCopyAll(prefs);
if (interfaces != NULL) {
CFIndex i;
CFIndex n;
n = CFArrayGetCount(interfaces);
for (i = 0; i < n; i++) {
SCBridgeInterfaceRef interface;
CFStringRef name = NULL;
CFDictionaryRef options;
interface = CFArrayGetValueAtIndex(interfaces, i);
options = SCBridgeInterfaceGetOptions(interface);
if ((options != NULL) &&
CFDictionaryGetValueIfPresent(options,
CFSTR("__AUTO__"),
(const void **)&name) &&
_SC_CFEqual(name, bridgeName)) {
bridge = interface;
CFRetain(bridge);
break;
}
}
CFRelease(interfaces);
}
if (bridge == NULL) {
bridge = SCBridgeInterfaceCreate(prefs);
if (bridge != NULL) {
CFMutableDictionaryRef newOptions;
Boolean ok;
newOptions = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(newOptions, CFSTR("__AUTO__"), bridgeName);
ok = SCBridgeInterfaceSetOptions(bridge, newOptions);
CFRelease(newOptions);
if (!ok) {
CFRelease(bridge);
bridge = NULL;
}
}
}
return bridge;
}
#endif // !TARGET_OS_IPHONE
static CFArrayRef
copyServices(SCNetworkSetRef set)
{
CFArrayRef services;
SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
services = SCNetworkSetCopyServices(set);
if ((services != NULL) && setPrivate->established) {
CFRelease(services);
services = SCNetworkServiceCopyAll(setPrivate->prefs);
}
return services;
}
#if !TARGET_OS_IPHONE
static CFArrayRef
updateServices(CFArrayRef services, SCNetworkInterfaceRef interface)
{
CFStringRef bsdName;
CFIndex i;
CFIndex n;
CFMutableArrayRef newServices;
if (services == NULL) {
return NULL;
}
bsdName = SCNetworkInterfaceGetBSDName(interface);
newServices = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
n = CFArrayGetCount(services);
for (i = 0; i < n; i++) {
SCNetworkInterfaceRef interface;
CFStringRef interfaceName;
SCNetworkServiceRef newService;
SCNetworkServiceRef service;
CFStringRef serviceID;
SCNetworkServicePrivateRef servicePrivate;
service = CFArrayGetValueAtIndex(services, i);
interface = SCNetworkServiceGetInterface(service);
interfaceName = SCNetworkInterfaceGetBSDName(interface);
if (!_SC_CFEqual(interfaceName, bsdName)) {
CFArrayAppendValue(newServices, service);
continue;
}
serviceID = SCNetworkServiceGetServiceID(service);
servicePrivate = (SCNetworkServicePrivateRef)service;
newService = SCNetworkServiceCopy(servicePrivate->prefs, serviceID);
if (newService != NULL) {
CFArrayAppendValue(newServices, newService);
CFRelease(newService);
}
}
return newServices;
}
#endif // !TARGET_OS_IPHONE
static Boolean
__SCNetworkSetEstablishDefaultConfigurationForInterfaces(SCNetworkSetRef set, CFArrayRef interfaces)
{
CFSetRef excluded = NULL;
CFIndex i;
CFIndex n = 0;
Boolean ok = TRUE;
CFArrayRef services;
SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
Boolean updated = FALSE;
Boolean updatedIFs = FALSE;
#if TARGET_OS_IPHONE
CFArrayRef orphans = NULL;
CFArrayRef sets;
sets = SCNetworkSetCopyAll(setPrivate->prefs);
if (sets != NULL) {
if (CFArrayGetCount(sets) == 1) {
services = SCNetworkSetCopyServices(set);
if (services != NULL) {
n = CFArrayGetCount(services);
CFRelease(services);
}
if ((n == 0) && CFEqual(set, CFArrayGetValueAtIndex(sets, 0))) {
orphans = SCNetworkServiceCopyAll(setPrivate->prefs);
}
}
CFRelease(sets);
}
#endif // TARGET_OS_IPHONE
services = copyServices(set);
excluded = copyExcludedInterfaces(setPrivate->prefs);
#if !TARGET_OS_IPHONE
n = (interfaces != NULL) ? CFArrayGetCount(interfaces) : 0;
for (i = 0; i < n; i++) {
SCBridgeInterfaceRef bridge = NULL;
SCNetworkInterfaceRef interface;
interface = CFArrayGetValueAtIndex(interfaces, i);
if ((excluded != NULL)
&& CFSetContainsValue(excluded, interface)) {
continue;
}
if (__SCNetworkServiceExistsForInterface(services, interface)) {
continue;
}
if (_SCNetworkInterfaceIsBuiltin(interface) &&
_SCNetworkInterfaceIsThunderbolt(interface) &&
!isA_SCBridgeInterface(interface)) {
bridge = copyAutoBridgeInterface(setPrivate->prefs, CFSTR("thunderbolt-bridge"));
}
if (bridge != NULL) {
CFIndex bridgeIndex;
CFArrayRef members;
CFMutableArrayRef newMembers;
CFMutableSetRef newExcluded;
CFMutableArrayRef newInterfaces;
CFArrayRef newServices;
bridgeIndex = CFArrayGetFirstIndexOfValue(interfaces,
CFRangeMake(0, CFArrayGetCount(interfaces)),
bridge);
members = SCBridgeInterfaceGetMemberInterfaces(bridge);
if ((members != NULL) && (CFArrayGetCount(members) > 0)) {
newMembers = CFArrayCreateMutableCopy(NULL, 0, members);
updated = TRUE; } else {
newMembers = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
}
CFArrayAppendValue(newMembers, interface);
ok = SCBridgeInterfaceSetMemberInterfaces(bridge, newMembers);
CFRelease(newMembers);
if (!ok) {
SCLog(TRUE, LOG_DEBUG,
CFSTR("could not update bridge with \"%@\": %s\n"),
SCNetworkInterfaceGetLocalizedDisplayName(interface),
SCErrorString(SCError()));
CFRelease(bridge);
continue;
}
newExcluded = CFSetCreateMutableCopy(NULL, 0, excluded);
CFRelease(excluded);
CFSetAddValue(newExcluded, interface);
excluded = newExcluded;
newInterfaces = CFArrayCreateMutableCopy(NULL, 0, interfaces);
if (bridgeIndex != kCFNotFound) {
CFArraySetValueAtIndex(newInterfaces, bridgeIndex, bridge);
} else {
CFArrayAppendValue(newInterfaces, bridge);
}
if (updatedIFs) {
CFRelease(interfaces);
}
interfaces = newInterfaces;
updatedIFs = TRUE;
newServices = updateServices(services, bridge);
if (newServices != NULL) {
CFRelease(services);
services = newServices;
}
CFRelease(bridge);
}
}
#endif // !TARGET_OS_IPHONE
n = (interfaces != NULL) ? CFArrayGetCount(interfaces) : 0;
for (i = 0; i < n; i++) {
SCNetworkInterfaceRef interface;
CFMutableArrayRef interface_list;
interface = CFArrayGetValueAtIndex(interfaces, i);
if ((excluded != NULL)
&& CFSetContainsValue(excluded, interface)) {
continue;
}
if (__SCNetworkServiceExistsForInterface(services, interface)) {
continue;
}
interface_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
CFArrayAppendValue(interface_list, interface);
while (ok && (CFArrayGetCount(interface_list) > 0)) {
CFArrayRef protocol_types;
interface = CFArrayGetValueAtIndex(interface_list, 0);
protocol_types = SCNetworkInterfaceGetSupportedProtocolTypes(interface);
if ((protocol_types != NULL) && (CFArrayGetCount(protocol_types) > 0)) {
SCNetworkServiceRef service;
service = SCNetworkServiceCreate(setPrivate->prefs, interface);
if (service == NULL) {
SCLog(TRUE, LOG_DEBUG,
CFSTR("could not create service for \"%@\": %s\n"),
SCNetworkInterfaceGetLocalizedDisplayName(interface),
SCErrorString(SCError()));
ok = FALSE;
goto nextInterface;
}
ok = SCNetworkServiceEstablishDefaultConfiguration(service);
if (!ok) {
SCLog(TRUE, LOG_DEBUG,
CFSTR("could not estabish default configuration for \"%@\": %s\n"),
SCNetworkInterfaceGetLocalizedDisplayName(interface),
SCErrorString(SCError()));
SCNetworkServiceRemove(service);
CFRelease(service);
goto nextInterface;
}
while (TRUE) {
CFStringRef newName;
ok = SCNetworkSetAddService(set, service);
if (ok) {
break;
}
if (SCError() != kSCStatusKeyExists) {
SCLog(TRUE, LOG_DEBUG,
CFSTR("could not add service for \"%@\": %s\n"),
SCNetworkInterfaceGetLocalizedDisplayName(interface),
SCErrorString(SCError()));
SCNetworkServiceRemove(service);
CFRelease(service);
goto nextInterface;
}
newName = __SCNetworkServiceNextName(service);
if (newName == NULL) {
SCLog(TRUE, LOG_DEBUG,
CFSTR("could not set unique name for \"%@\": %s\n"),
SCNetworkInterfaceGetLocalizedDisplayName(interface),
SCErrorString(SCError()));
SCNetworkServiceRemove(service);
CFRelease(service);
goto nextInterface;
}
ok = SCNetworkServiceSetName(service, newName);
CFRelease(newName);
if (ok) {
continue;
}
if (SCError() != kSCStatusKeyExists) {
SCLog(TRUE, LOG_DEBUG,
CFSTR("could not set unique name for \"%@\": %s\n"),
SCNetworkInterfaceGetLocalizedDisplayName(interface),
SCErrorString(SCError()));
SCNetworkServiceRemove(service);
CFRelease(service);
goto nextInterface;
}
}
CFRelease(service);
updated = TRUE;
} else {
add_supported_interfaces(interface_list, interface);
}
nextInterface :
CFArrayRemoveValueAtIndex(interface_list, 0);
}
CFRelease(interface_list);
}
if (updatedIFs) CFRelease(interfaces);
if (services != NULL) CFRelease(services);
if (excluded != NULL) CFRelease(excluded);
#if TARGET_OS_IPHONE
if (orphans != NULL) {
if (ok && updated) {
CFIndex i;
CFIndex n = CFArrayGetCount(orphans);
for (i = 0; i < n; i++) {
SCNetworkServiceRef service;
service = CFArrayGetValueAtIndex(orphans, i);
if (_SCNetworkServiceIsVPN(service)) {
ok = SCNetworkSetAddService(set, service);
if (!ok) {
break;
}
}
}
}
CFRelease(orphans);
}
#endif // TARGET_OS_IPHONE
if (ok && !updated) {
_SCErrorSet(kSCStatusOK);
}
return updated;
}
Boolean
SCNetworkSetEstablishDefaultConfiguration(SCNetworkSetRef set)
{
CFArrayRef interfaces;
SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
Boolean updated = FALSE;
if (!isA_SCNetworkSet(set)) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
interfaces = _SCNetworkInterfaceCopyAllWithPreferences(setPrivate->prefs);
if (interfaces != NULL) {
updated = __SCNetworkSetEstablishDefaultConfigurationForInterfaces(set, interfaces);
CFRelease(interfaces);
}
return updated;
}
Boolean
SCNetworkSetEstablishDefaultInterfaceConfiguration(SCNetworkSetRef set, SCNetworkInterfaceRef interface)
{
CFArrayRef interfaces;
Boolean updated;
if (!isA_SCNetworkSet(set)) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
if (!isA_SCNetworkInterface(interface)) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
interfaces = CFArrayCreate(NULL, (const void **)&interface, 1, &kCFTypeArrayCallBacks);
assert(interfaces != NULL);
updated = __SCNetworkSetEstablishDefaultConfigurationForInterfaces(set, interfaces);
CFRelease(interfaces);
return updated;
}
SCNetworkServiceRef
SCNetworkSetCopySelectedVPNService(SCNetworkSetRef set)
{
CFIndex i;
CFIndex n;
SCNetworkServiceRef selected = NULL;
CFArrayRef services;
CFMutableArrayRef services_vpn = NULL;
if (!isA_SCNetworkSet(set)) {
_SCErrorSet(kSCStatusInvalidArgument);
return NULL;
}
services = SCNetworkSetCopyServices(set);
if (services != NULL) {
n = CFArrayGetCount(services);
for (i = 0; i < n; i++) {
SCNetworkServiceRef service;
service = CFArrayGetValueAtIndex(services, i);
if (!SCNetworkServiceGetEnabled(service)) {
continue;
}
if (!_SCNetworkServiceIsVPN(service)) {
continue;
}
if (services_vpn == NULL) {
services_vpn = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
}
CFArrayAppendValue(services_vpn, service);
}
CFRelease(services);
}
if (services_vpn == NULL) {
return NULL;
}
n = CFArrayGetCount(services_vpn);
if (n > 1) {
CFArrayRef order;
CFMutableArrayRef sorted;
order = SCNetworkSetGetServiceOrder(set);
sorted = CFArrayCreateMutableCopy(NULL, 0, services_vpn);
CFArraySortValues(sorted,
CFRangeMake(0, CFArrayGetCount(sorted)),
_SCNetworkServiceCompare,
(void *)order);
CFRelease(services_vpn);
services_vpn = sorted;
}
#if TARGET_OS_IPHONE
if (n > 1) {
CFStringRef serviceID_prefs;
#define VPN_PREFERENCES CFSTR("com.apple.mobilevpn")
#define VPN_SERVICE_ID CFSTR("activeVPNID")
CFPreferencesAppSynchronize(VPN_PREFERENCES);
serviceID_prefs = CFPreferencesCopyAppValue(VPN_SERVICE_ID, VPN_PREFERENCES);
if (serviceID_prefs != NULL) {
for (i = 0; i < n; i++) {
SCNetworkServiceRef service;
CFStringRef serviceID;
service = CFArrayGetValueAtIndex(services_vpn, i);
serviceID = SCNetworkServiceGetServiceID(service);
if (CFEqual(serviceID, serviceID_prefs)) {
selected = service;
CFRetain(selected);
break;
}
}
CFRelease(serviceID_prefs);
}
}
#endif // TARGET_OS_IPHONE
if (selected == NULL) {
selected = CFArrayGetValueAtIndex(services_vpn, 0);
CFRetain(selected);
}
CFRelease(services_vpn);
return selected;
}
Boolean
SCNetworkSetSetSelectedVPNService(SCNetworkSetRef set, SCNetworkServiceRef service)
{
Boolean ok = TRUE;
CFArrayRef services;
if (!isA_SCNetworkSet(set)) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
if (!isA_SCNetworkService(service) || !_SCNetworkServiceIsVPN(service)) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
services = SCNetworkSetCopyServices(set);
if (services != NULL) {
CFIndex i;
CFIndex n = CFArrayGetCount(services);
if (!CFArrayContainsValue(services, CFRangeMake(0, n), service)) {
_SCErrorSet(kSCStatusInvalidArgument);
ok = FALSE;
goto done;
}
for (i = 0; ok && (i < n); i++) {
SCNetworkServiceRef vpn;
vpn = CFArrayGetValueAtIndex(services, i);
if (!_SCNetworkServiceIsVPN(vpn)) {
continue;
}
ok = SCNetworkServiceSetEnabled(vpn, CFEqual(service, vpn));
}
}
done :
if (services != NULL) CFRelease(services);
return ok;
}