X-Git-Url: https://git.saurik.com/apple/configd.git/blobdiff_plain/edebe297f772e4cdd76278ebb777820466d2917b..d94708881e41bd90afd74b1a1dd0524d039ba3f7:/SystemConfiguration.fproj/SCNetworkSet.c diff --git a/SystemConfiguration.fproj/SCNetworkSet.c b/SystemConfiguration.fproj/SCNetworkSet.c index 922f7e0..bfcf499 100644 --- a/SystemConfiguration.fproj/SCNetworkSet.c +++ b/SystemConfiguration.fproj/SCNetworkSet.c @@ -1,15 +1,15 @@ /* - * Copyright (c) 2004-2007 Apple Inc. All rights reserved. + * Copyright (c) 2004-2019 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -17,7 +17,7 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_LICENSE_HEADER_END@ */ @@ -31,10 +31,7 @@ #include #include -#include #include "SCNetworkConfigurationInternal.h" -#include -#include #include @@ -69,15 +66,19 @@ __SCNetworkSetCopyDescription(CFTypeRef cf) { CFAllocatorRef allocator = CFGetAllocator(cf); CFMutableStringRef result; - SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)cf; + SCNetworkSetRef set = (SCNetworkSetRef)cf; + SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set; result = CFStringCreateMutable(allocator, 0); - CFStringAppendFormat(result, NULL, CFSTR(" {"), cf, allocator); + CFStringAppendFormat(result, NULL, CFSTR(" {"), set, 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); } + if (!__SCNetworkSetExists(set)) { + CFStringAppendFormat(result, NULL, CFSTR(", REMOVED")); + } CFStringAppendFormat(result, NULL, CFSTR("}")); return result; @@ -157,9 +158,9 @@ __SCNetworkSetCreatePrivate(CFAllocatorRef allocator, return NULL; } + /* initialize non-zero/NULL members */ setPrivate->setID = CFStringCreateCopy(NULL, setID); setPrivate->prefs = CFRetain(prefs); - setPrivate->name = NULL; return setPrivate; } @@ -168,70 +169,56 @@ __SCNetworkSetCreatePrivate(CFAllocatorRef allocator, #pragma mark - -static Boolean -_serviceIsPPP(SCNetworkServiceRef service) +static int +_serviceOrder(SCNetworkServiceRef service) { SCNetworkInterfaceRef interface; - CFStringRef interfaceType; interface = SCNetworkServiceGetInterface(service); - if (interface == NULL) { - return FALSE; + if ((interface == NULL) || _SCNetworkServiceIsVPN(service)) { + return 100000; // if unknown or VPN interface, sort last } - interfaceType = SCNetworkInterfaceGetInterfaceType(interface); - return CFEqual(interfaceType, kSCNetworkInterfaceTypePPP); + return __SCNetworkInterfaceOrder(interface); } -static Boolean -_serviceIsVPN(SCNetworkServiceRef service) +static CFIndex +_serviceOrder_clear(CFMutableArrayRef order, CFStringRef serviceID) { - SCNetworkInterfaceRef interface; - CFStringRef interfaceType; - -// if (!_serviceIsPPP(service)) { -// return FALSE; -// } - - interface = SCNetworkServiceGetInterface(service); -// if (interface == NULL) { -// return FALSE; -// } - - interface = SCNetworkInterfaceGetInterface(interface); - if (interface == NULL) { - return FALSE; - } - - interfaceType = SCNetworkInterfaceGetInterfaceType(interface); - return (CFEqual(interfaceType, kSCNetworkInterfaceTypeL2TP) || - CFEqual(interfaceType, kSCNetworkInterfaceTypePPTP)); -} - + CFIndex f; // # of serviceID's found + CFIndex i; + CFIndex n; -static int -_serviceOrder(SCNetworkServiceRef service) -{ - SCNetworkInterfaceRef interface; + f = 0; + i = 0; + n = CFArrayGetCount(order); + while (i < n) { + CFStringRef thisServiceID = CFArrayGetValueAtIndex(order, i); + + if (CFEqual(thisServiceID, serviceID)) { + // remove the serviceID + CFArrayRemoveValueAtIndex(order, i); + n--; + f++; + continue; + } - interface = SCNetworkServiceGetInterface(service); - if (interface == NULL) { - return 100000; // sort last + i++; // move to the next serviceID } - return __SCNetworkInterfaceOrder(interface); + return f; } static void _serviceOrder_add(SCNetworkSetRef set, SCNetworkServiceRef service) { - CFIndex i; CFIndex n; CFMutableArrayRef newOrder; CFArrayRef order; - CFStringRef serviceID; + CFStringRef serviceID = SCNetworkServiceGetServiceID(service); + CFIndex serviceOrder = _serviceOrder(service); SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set; CFIndex slot; @@ -241,66 +228,46 @@ _serviceOrder_add(SCNetworkSetRef set, SCNetworkServiceRef service) } else { newOrder = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); } - n = CFArrayGetCount(newOrder); + assert(newOrder != NULL); - serviceID = SCNetworkServiceGetServiceID(service); - if (CFArrayContainsValue(newOrder, CFRangeMake(0, n), serviceID)) { - // if serviceID already present - goto done; + n = _serviceOrder_clear(newOrder, serviceID); + if (n > 0) { + SC_log(LOG_ERR, "SCNetworkSetAddService() w/service already in ServiceOrder\n service = %@\n matched = %ld", + service, + n); + _SC_crash_once("SCNetworkSetAddService() w/service already in ServiceOrder", NULL, NULL); } - if (_serviceIsPPP(service) && !_serviceIsVPN(service)) { - int serviceOrder; - - /* - * we add [non-VPN] PPP interfaces to the head of the - * service order but ensure that "modem" devices are - * in front of "Bluetooth" & "IrDA" devices. - */ - slot = 0; - - serviceOrder = _serviceOrder(service); - - for (i = 0; i < n; i++) { - int slotOrder; - CFStringRef slotServiceID; - SCNetworkServiceRef slotService; - - slotServiceID = CFArrayGetValueAtIndex(newOrder, i); - if (!isA_CFString(slotServiceID)) { - // if bad prefs - continue; - } - - slotService = SCNetworkServiceCopy(setPrivate->prefs, slotServiceID); - if (slotService == NULL) { - // if serviceID not valid - continue; - } + slot = 0; + n = CFArrayGetCount(newOrder); + for (CFIndex i = 0; i < n; i++) { + int slotOrder; + SCNetworkServiceRef slotService; + CFStringRef slotServiceID; + + slotServiceID = CFArrayGetValueAtIndex(newOrder, i); + if (!isA_CFString(slotServiceID)) { + // if bad prefs + continue; + } - if (_serviceIsPPP(slotService)) { - // if PPP service - slotOrder = _serviceOrder(slotService); - if (serviceOrder >= slotOrder) { - // add the service *after* this one - slot = i + 1; - } - } + slotService = SCNetworkServiceCopy(setPrivate->prefs, slotServiceID); + if (slotService == NULL) { + // if serviceID not valid + continue; + } - CFRelease(slotService); + slotOrder = _serviceOrder(slotService); + if (serviceOrder >= slotOrder) { + // add the service *after* this one + slot = i + 1; } - } else { - /* - * non-PPP interfaces are added to the end of the list. - */ - slot = n; + + CFRelease(slotService); } CFArrayInsertValueAtIndex(newOrder, slot, serviceID); (void) SCNetworkSetSetServiceOrder(set, newOrder); - - done : - CFRelease(newOrder); return; @@ -310,6 +277,7 @@ _serviceOrder_add(SCNetworkSetRef set, SCNetworkServiceRef service) static void _serviceOrder_remove(SCNetworkSetRef set, SCNetworkServiceRef service) { + CFIndex n; CFMutableArrayRef newOrder; CFArrayRef order; CFStringRef serviceID; @@ -318,22 +286,17 @@ _serviceOrder_remove(SCNetworkSetRef set, SCNetworkServiceRef service) 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; - } + serviceID = SCNetworkServiceGetServiceID(service); - CFArrayRemoveValueAtIndex(newOrder, i); + n = _serviceOrder_clear(newOrder, serviceID); + if (n > 1) { + SC_log(LOG_ERR, "SCNetworkSetRemoveService() w/multiple instances of service in ServiceOrder\n service = %@\n count = %ld", + service, + n); } + (void) SCNetworkSetSetServiceOrder(set, newOrder); CFRelease(newOrder); @@ -345,9 +308,159 @@ _serviceOrder_remove(SCNetworkSetRef set, SCNetworkServiceRef service) #pragma mark SCNetworkSet APIs +#define DEFAULT_SET_NAME CFSTR("Automatic") #define N_QUICK 16 +static CFStringRef +copy_default_set_name(Boolean loc) +{ + CFStringRef name; + static CFStringRef non_localized = NULL; + static CFStringRef localized = NULL; + + if (!loc) { + static dispatch_once_t once; + + dispatch_once(&once, ^{ + CFBundleRef bundle; + + bundle = _SC_CFBundleGet(); + if (bundle != NULL) { + non_localized = _SC_CFBundleCopyNonLocalizedString(bundle, + CFSTR("DEFAULT_SET_NAME"), + DEFAULT_SET_NAME, + NULL); + } + }); + name = non_localized; + } else { + static dispatch_once_t once; + + dispatch_once(&once, ^{ + CFBundleRef bundle; + + bundle = _SC_CFBundleGet(); + if (bundle != NULL) { + localized = CFBundleCopyLocalizedString(bundle, + CFSTR("DEFAULT_SET_NAME"), + DEFAULT_SET_NAME, + NULL); + } + }); + name = localized; + } + + if (name == NULL) { + // if bundle or localized names not available + name = DEFAULT_SET_NAME; + } + + CFRetain(name); + return name; +} + + +#define PREVENT_DUPLICATE_SERVICE_NAMES +#ifdef PREVENT_DUPLICATE_SERVICE_NAMES +static CFStringRef +copy_next_name(CFStringRef name) +{ + CFArrayRef components; + CFIndex n; + CFMutableArrayRef newComponents; + SInt32 suffix = 2; + + if (name == NULL) { + return NULL; + } + + components = CFStringCreateArrayBySeparatingStrings(NULL, name, CFSTR(" ")); + if (components != NULL) { + newComponents = CFArrayCreateMutableCopy(NULL, 0, components); + CFRelease(components); + } else { + newComponents = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + CFArrayAppendValue(newComponents, name); + } + + n = CFArrayGetCount(newComponents); + if (n > 1) { + CFStringRef str; + + str = CFArrayGetValueAtIndex(newComponents, n - 1); + suffix = CFStringGetIntValue(str); + if (suffix++ > 0) { + CFArrayRemoveValueAtIndex(newComponents, n - 1); + } else { + suffix = 2; + } + } + + name = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), (int)suffix); + CFArrayAppendValue(newComponents, name); + CFRelease(name); + + name = CFStringCreateByCombiningStrings(NULL, newComponents, CFSTR(" ")); + CFRelease(newComponents); + + return name; +} + + +static Boolean +ensure_unique_service_name(SCNetworkServiceRef service) +{ + SCNetworkInterfaceRef interface; + CFStringRef name; + Boolean ok = TRUE; + + interface = SCNetworkServiceGetInterface(service); + + name = SCNetworkServiceGetName(service); + if (name != NULL) { + CFRetain(name); + } + + while (TRUE) { + CFStringRef newName; + + ok = SCNetworkServiceSetName(service, name); + if (ok) { + break; + } + + if (SCError() != kSCStatusKeyExists) { + SC_log(LOG_INFO, "could not update service name for \"%@\": %s", + SCNetworkInterfaceGetLocalizedDisplayName(interface), + SCErrorString(SCError())); + break; + } + + newName = copy_next_name(name); + if (newName == NULL) { + SC_log(LOG_INFO, "could not create unique name for \"%@\": %s", + SCNetworkInterfaceGetLocalizedDisplayName(interface), + SCErrorString(SCError())); + break; + } + + // try again with the "new" name + if (name != NULL) { + CFRelease(name); + } + name = newName; + } + + if (name != NULL) { + CFRelease(name); + } + + return ok; +} +#endif // PREVENT_DUPLICATE_SERVICE_NAMES + + Boolean SCNetworkSetAddService(SCNetworkSetRef set, SCNetworkServiceRef service) { @@ -364,46 +477,36 @@ SCNetworkSetAddService(SCNetworkSetRef set, SCNetworkServiceRef service) return FALSE; } - if (!isA_SCNetworkService(service)) { + if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) { _SCErrorSet(kSCStatusInvalidArgument); 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; + if (!__SCNetworkSetExists(set)) { + SC_log(LOG_ERR, "SCNetworkSetAddService() w/removed set\n set = %@\n service = %@", + set, + service); + _SC_crash_once("SCNetworkSetAddService() w/removed set", NULL, NULL); + _SCErrorSet(kSCStatusInvalidArgument); + } - otherService = CFArrayGetValueAtIndex(services, i); - otherName = SCNetworkServiceGetName(otherService); - if ((otherName != NULL) && CFEqual(name, otherName)) { - /* - * if a service with the same "name" is - * already a member of the set. - */ - CFRelease(services); - _SCErrorSet(kSCStatusKeyExists); - return FALSE; - } - } - } + if (!__SCNetworkServiceExists(service)) { + SC_log(LOG_ERR, "SCNetworkSetAddService() w/removed service\n set = %@\n service = %@", + set, + service); + _SC_crash_once("SCNetworkSetAddService() w/removed service", NULL, NULL); + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } - CFRelease(services); + // make sure that we do not add an orphaned network service if its + // associated interface is a member of a bond or bridge. + interface = SCNetworkServiceGetInterface(service); + if ((interface != NULL) && + __SCNetworkInterfaceIsMember(servicePrivate->prefs, interface)) { + _SCErrorSet(kSCStatusKeyExists); + return FALSE; } -#endif // PREVENT_DUPLICATE_SERVICE_NAMES //#define PREVENT_DUPLICATE_SETS #ifdef PREVENT_DUPLICATE_SETS @@ -441,7 +544,7 @@ SCNetworkSetAddService(SCNetworkSetRef set, SCNetworkServiceRef service) // get the [deep] interface configuration settings interface = SCNetworkServiceGetInterface(service); if (interface != NULL) { - interface_config = __SCNetworkInterfaceCopyDeepConfiguration(interface); + interface_config = __SCNetworkInterfaceCopyDeepConfiguration(set, interface); } // create the link between "set" and the "service" @@ -453,6 +556,21 @@ SCNetworkSetAddService(SCNetworkSetRef set, SCNetworkServiceRef service) servicePrivate->serviceID, // service NULL); // entity ok = SCPreferencesPathSetLink(setPrivate->prefs, path, link); +#ifdef PREVENT_DUPLICATE_SERVICE_NAMES + if (ok) { + // We use the interface cache here to not reach into the + // IORegistry for every service we go through + _SCNetworkInterfaceCacheOpen(); + ok = ensure_unique_service_name(service); + _SCNetworkInterfaceCacheClose(); + + if (!ok) { + // if we could not ensure a unique name, remove the (just added) + // link between the "set" and the "service" + (void) SCPreferencesPathRemoveValue(setPrivate->prefs, path); + } + } +#endif // PREVENT_DUPLICATE_SERVICE_NAMES CFRelease(path); CFRelease(link); if (!ok) { @@ -461,14 +579,21 @@ SCNetworkSetAddService(SCNetworkSetRef set, SCNetworkServiceRef service) // push the [deep] interface configuration into all sets which contain this service. if (interface != NULL) { - __SCNetworkInterfaceSetDeepConfiguration(interface, interface_config); + __SCNetworkInterfaceSetDeepConfiguration(set, interface, interface_config); } // add service to ServiceOrder _serviceOrder_add(set, service); + // mark set as no longer "new" + setPrivate->established = TRUE; + done : + if (ok) { + SC_log(LOG_DEBUG, "SCNetworkSetAddService(): %@, %@", set, service); + } + if (interface_config != NULL) CFRelease(interface_config); return ok; } @@ -496,34 +621,12 @@ SCNetworkSetCopy(SCPreferencesRef prefs, CFStringRef setID) } setPrivate = __SCNetworkSetCreatePrivate(NULL, prefs, setID); - return (SCNetworkSetRef)setPrivate; -} - - -static Boolean -_SCNetworkServiceExistsForInterface(CFArrayRef services, SCNetworkInterfaceRef interface) -{ - CFIndex i; - CFIndex n; - - n = isA_CFArray(services) ? CFArrayGetCount(services) : 0; - for (i = 0; i < n; i++) { - SCNetworkServiceRef service; - SCNetworkInterfaceRef service_interface; - - service = CFArrayGetValueAtIndex(services, i); - - service_interface = SCNetworkServiceGetInterface(service); - while (service_interface != NULL) { - if (CFEqual(interface, service_interface)) { - return TRUE; - } + assert(setPrivate != NULL); - service_interface = SCNetworkInterfaceGetInterface(service_interface); - } - } + // mark set as "old" (already established) + setPrivate->established = TRUE; - return FALSE; + return (SCNetworkSetRef)setPrivate; } @@ -535,7 +638,7 @@ SCNetworkSetContainsInterface(SCNetworkSetRef set, SCNetworkInterfaceRef interfa services = SCNetworkSetCopyServices(set); if (services != NULL) { - found = _SCNetworkServiceExistsForInterface(services, interface); + found = __SCNetworkServiceExistsForInterface(services, interface); CFRelease(services); } @@ -578,14 +681,16 @@ SCNetworkSetCopyAll(SCPreferencesRef prefs) SCNetworkSetPrivateRef setPrivate; if (!isA_CFDictionary(vals[i])) { - SCLog(TRUE, - LOG_INFO, - CFSTR("SCNetworkSetCopyAll(): error w/set \"%@\"\n"), - keys[i]); + SC_log(LOG_INFO, "error w/set \"%@\"", keys[i]); continue; } setPrivate = __SCNetworkSetCreatePrivate(NULL, prefs, keys[i]); + assert(setPrivate != NULL); + + // mark set as "old" (already established) + setPrivate->established = TRUE; + CFArrayAppendValue(array, (SCNetworkSetRef)setPrivate); CFRelease(setPrivate); } @@ -599,6 +704,80 @@ SCNetworkSetCopyAll(SCPreferencesRef prefs) } +CFArrayRef /* of SCNetworkInterfaceRef's */ +SCNetworkSetCopyAvailableInterfaces(SCNetworkSetRef set) +{ + CFMutableArrayRef available; + CFMutableSetRef excluded = NULL; + int i; + CFArrayRef interfaces; + CFIndex n_interfaces; + CFIndex 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 /* !TARGET_OS_IPHONE */ + + 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)) { + // if excluded + continue; + } + + CFArrayAppendValue(available, interface); + } + + CFRelease(interfaces); + CFRelease(excluded); + + return available; +} + + SCNetworkSetRef SCNetworkSetCopyCurrent(SCPreferencesRef prefs) { @@ -620,8 +799,12 @@ SCNetworkSetCopyCurrent(SCPreferencesRef prefs) path = SCPreferencesPathKeyCreateSet(NULL, setID); if (CFEqual(path, currentID)) { setPrivate = __SCNetworkSetCreatePrivate(NULL, prefs, setID); + assert(setPrivate != NULL); + + // mark set as "old" (already established) + setPrivate->established = TRUE; } else { - SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkSetCopyCurrent(): preferences are non-conformant")); + SC_log(LOG_NOTICE, "SCNetworkSetCopyCurrent(): preferences are non-conformant"); } CFRelease(path); } @@ -675,11 +858,9 @@ SCNetworkSetCopyServices(SCNetworkSetRef set) 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); + SC_log(LOG_INFO, "service \"%@\" for set \"%@\" is not a link", + keys[i], + setPrivate->setID); continue; // if the service is not a link } @@ -692,14 +873,31 @@ SCNetworkSetCopyServices(SCNetworkSetRef set) serviceID, // service NULL); // entity if (CFEqual(path, link)) { - SCNetworkServicePrivateRef servicePrivate; - - servicePrivate = __SCNetworkServiceCreatePrivate(NULL, - setPrivate->prefs, - serviceID, - NULL); - CFArrayAppendValue(array, (SCNetworkServiceRef)servicePrivate); - CFRelease(servicePrivate); + CFDictionaryRef entity; + CFStringRef interfacePath; + Boolean skip = FALSE; + + interfacePath = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL, // allocator + serviceID, // service + kSCEntNetInterface); // entity + entity = SCPreferencesPathGetValue(setPrivate->prefs, interfacePath); + CFRelease(interfacePath); + + if (__SCNetworkInterfaceEntityIsPPTP(entity)) { + SC_log(LOG_INFO, "PPTP services are no longer supported"); + skip = TRUE; + } + + if (!skip) { + SCNetworkServicePrivateRef servicePrivate; + + servicePrivate = __SCNetworkServiceCreatePrivate(NULL, + setPrivate->prefs, + serviceID, + NULL); + CFArrayAppendValue(array, (SCNetworkServiceRef)servicePrivate); + CFRelease(servicePrivate); + } } CFRelease(path); } @@ -718,6 +916,8 @@ SCNetworkSetRef SCNetworkSetCreate(SCPreferencesRef prefs) { CFArrayRef components; + CFDictionaryRef entity; + Boolean ok; CFStringRef path; CFStringRef prefix; CFStringRef setID; @@ -732,50 +932,127 @@ SCNetworkSetCreate(SCPreferencesRef prefs) } components = CFStringCreateArrayBySeparatingStrings(NULL, path, CFSTR("/")); - CFRelease(path); - setID = CFArrayGetValueAtIndex(components, 2); setPrivate = __SCNetworkSetCreatePrivate(NULL, prefs, setID); + assert(setPrivate != NULL); CFRelease(components); - return (SCNetworkSetRef)setPrivate; -} - + // mark set as "new" (not yet established) + setPrivate->established = FALSE; -CFStringRef -SCNetworkSetGetSetID(SCNetworkSetRef set) -{ - SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set; + // establish the set in the preferences + entity = CFDictionaryCreate(NULL, + NULL, NULL, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + ok = SCPreferencesPathSetValue(prefs, path, entity); + CFRelease(path); + CFRelease(entity); + if (!ok) { + CFRelease(setPrivate); + setPrivate = NULL; + } - if (!isA_SCNetworkSet(set)) { - _SCErrorSet(kSCStatusInvalidArgument); - return NULL; + if (setPrivate != NULL) { + SC_log(LOG_DEBUG, "SCNetworkSetCreate(): %@", setPrivate); } - return setPrivate->setID; + return (SCNetworkSetRef)setPrivate; } -CFStringRef -SCNetworkSetGetName(SCNetworkSetRef set) +SCNetworkSetRef +_SCNetworkSetCreateDefault(SCPreferencesRef prefs) { - CFBundleRef bundle; - CFDictionaryRef entity; - CFStringRef path; - SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set; - - if (!isA_SCNetworkSet(set)) { - _SCErrorSet(kSCStatusInvalidArgument); + CFStringRef model; + Boolean ok = TRUE; + SCNetworkSetRef set; + CFStringRef setName = NULL; + + set = SCNetworkSetCopyCurrent(prefs); + if (set != NULL) { + SC_log(LOG_NOTICE, "creating default set w/already existing set"); + CFRelease(set); + _SCErrorSet(kSCStatusKeyExists); return NULL; } - if (setPrivate->name != NULL) { - return setPrivate->name;; + // create a new ("Automatic") set + set = SCNetworkSetCreate(prefs); + if (set == NULL) { + SC_log(LOG_NOTICE, "could not create \"new\" set: %s", + SCErrorString(SCError())); + goto done; } - path = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID); - entity = SCPreferencesPathGetValue(setPrivate->prefs, path); - CFRelease(path); + setName = copy_default_set_name(TRUE); + ok = SCNetworkSetSetName(set, setName); + CFRelease(setName); + if (!ok) { + // if we could not save the new set's "name" + SC_log(LOG_NOTICE, "could not save the new set's name: %s", + SCErrorString(SCError())); + goto done; + } + + ok = SCNetworkSetSetCurrent(set); + if (!ok) { + // if we could not make this the "current" set + SC_log(LOG_NOTICE, "could not establish new set as current: %s", + SCErrorString(SCError())); +// goto done; + } + + model = SCPreferencesGetValue(prefs, MODEL); + if (model == NULL) { + model = _SC_hw_model(FALSE); + SCPreferencesSetValue(prefs, MODEL, model); + } + + done : + + if (!ok && (set != NULL)) { + SCNetworkSetRemove(set); + CFRelease(set); + set = NULL; + } + return set; +} + + +CFStringRef +SCNetworkSetGetSetID(SCNetworkSetRef set) +{ + SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set; + + if (!isA_SCNetworkSet(set)) { + _SCErrorSet(kSCStatusInvalidArgument); + return NULL; + } + + return setPrivate->setID; +} + + +CFStringRef +SCNetworkSetGetName(SCNetworkSetRef set) +{ + 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; @@ -786,33 +1063,20 @@ SCNetworkSetGetName(SCNetworkSetRef set) } } - 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; - - // if "Automatic", return localized name - localized = CFBundleCopyLocalizedString(bundle, - CFSTR("DEFAULT_SET_NAME"), - CFSTR("Automatic"), - NULL); - if (localized != NULL) { - CFRelease(setPrivate->name); - setPrivate->name = localized; - } - } + if (setPrivate->name != NULL) { + CFStringRef non_localized; - CFRelease(non_localized); - } + non_localized = copy_default_set_name(FALSE); + if (CFEqual(setPrivate->name, non_localized)) { + CFStringRef localized; + + // if "Automatic", return localized name + localized = copy_default_set_name(TRUE); + CFRelease(setPrivate->name); + setPrivate->name = localized; } + + CFRelease(non_localized); } return setPrivate->name; @@ -858,6 +1122,24 @@ SCNetworkSetGetTypeID(void) } +#if TARGET_OS_IPHONE +static Boolean +isDefaultSet(SCNetworkSetRef set) +{ + CFStringRef defaultName; + Boolean isDefault = FALSE; + CFStringRef setName; + + defaultName = copy_default_set_name(TRUE); + setName = SCNetworkSetGetName(set); + isDefault = _SC_CFEqual(setName, defaultName); + CFRelease(defaultName); + + return isDefault; +} +#endif // TARGET_OS_IPHONE + + Boolean SCNetworkSetRemove(SCNetworkSetRef set) { @@ -871,13 +1153,35 @@ SCNetworkSetRemove(SCNetworkSetRef set) return FALSE; } + if (!__SCNetworkSetExists(set)) { + SC_log(LOG_ERR, "SCNetworkSetRemove() w/removed set\n set = %@", set); + _SC_crash_once("SCNetworkSetRemove() w/removed set", NULL, NULL); + _SCErrorSet(kSCStatusInvalidArgument); + } + +#if TARGET_OS_IPHONE + if (isDefaultSet(set) && (geteuid() != 0)) { + SC_log(LOG_ERR, "SCNetworkSetRemove() failed, cannot remove set : %@", set); + _SC_crash("The \"Automatic\" network set cannot be removed", NULL, NULL); + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } +#endif // TARGET_OS_IPHONE + currentPath = SCPreferencesGetValue(setPrivate->prefs, kSCPrefCurrentSet); path = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID); if (!isA_CFString(currentPath) || !CFEqual(currentPath, path)) { ok = SCPreferencesPathRemoveValue(setPrivate->prefs, path); + } else { + SC_log(LOG_DEBUG, "SCNetworkSetRemove() failed, currently active: %@", setPrivate->setID); + _SCErrorSet(kSCStatusInvalidArgument); } CFRelease(path); + if (ok) { + SC_log(LOG_DEBUG, "SCNetworkSetRemove(): %@", set); + } + return ok; } @@ -889,6 +1193,7 @@ SCNetworkSetRemoveService(SCNetworkSetRef set, SCNetworkServiceRef service) CFArrayRef interface_config = NULL; Boolean ok; CFStringRef path; + int sc_status = kSCStatusOK; SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service; SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set; @@ -897,7 +1202,24 @@ SCNetworkSetRemoveService(SCNetworkSetRef set, SCNetworkServiceRef service) return FALSE; } - if (!isA_SCNetworkService(service)) { + if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + if (!__SCNetworkSetExists(set)) { + SC_log(LOG_ERR, "SCNetworkSetRemoveService() w/removed set\n set = %@\n service = %@", + set, + service); + _SC_crash_once("SCNetworkSetRemoveService() w/removed set", NULL, NULL); + _SCErrorSet(kSCStatusInvalidArgument); + } + + if (!__SCNetworkServiceExists(service)) { + SC_log(LOG_ERR, "SCNetworkSetRemoveService() w/removed service\n set = %@\n service = %@", + set, + service); + _SC_crash_once("SCNetworkSetRemoveService() w/removed service", NULL, NULL); _SCErrorSet(kSCStatusInvalidArgument); return FALSE; } @@ -908,10 +1230,10 @@ SCNetworkSetRemoveService(SCNetworkSetRef set, SCNetworkServiceRef service) // get the [deep] interface configuration settings interface = SCNetworkServiceGetInterface(service); if (interface != NULL) { - interface_config = __SCNetworkInterfaceCopyDeepConfiguration(interface); + interface_config = __SCNetworkInterfaceCopyDeepConfiguration(set, interface); if (interface_config != NULL) { // remove the interface configuration from all sets which contain this service. - __SCNetworkInterfaceSetDeepConfiguration(interface, NULL); + __SCNetworkInterfaceSetDeepConfiguration(set, interface, NULL); } } @@ -921,19 +1243,23 @@ SCNetworkSetRemoveService(SCNetworkSetRef set, SCNetworkServiceRef service) servicePrivate->serviceID, NULL); ok = SCPreferencesPathRemoveValue(setPrivate->prefs, path); - CFRelease(path); if (!ok) { - goto done; + sc_status = SCError(); // preserve the error } + CFRelease(path); // push the [deep] interface configuration [back] into all sets which contain the service. if (interface_config != NULL) { - __SCNetworkInterfaceSetDeepConfiguration(interface, interface_config); + __SCNetworkInterfaceSetDeepConfiguration(set, interface, interface_config); + CFRelease(interface_config); } - done : + if (ok) { + SC_log(LOG_DEBUG, "SCNetworkSetRemoveService(): %@, %@", set, service); + } else { + _SCErrorSet(sc_status); + } - if (interface_config != NULL) CFRelease(interface_config); return ok; } @@ -950,9 +1276,21 @@ SCNetworkSetSetCurrent(SCNetworkSetRef set) return FALSE; } + if (!__SCNetworkSetExists(set)) { + SC_log(LOG_ERR, "SCNetworkSetSetCurrent() w/removed set\n set = %@", set); + _SC_crash_once("SCNetworkSetSetCurrent() w/removed set", NULL, NULL); + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + path = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID); ok = SCPreferencesSetValue(setPrivate->prefs, kSCPrefCurrentSet, path); CFRelease(path); + + if (ok) { + SC_log(LOG_DEBUG, "SCNetworkSetSetCurrent(): %@", set); + } + return ok; } @@ -960,8 +1298,10 @@ SCNetworkSetSetCurrent(SCNetworkSetRef set) Boolean SCNetworkSetSetName(SCNetworkSetRef set, CFStringRef name) { - CFBundleRef bundle = NULL; CFDictionaryRef entity; +#if TARGET_OS_IPHONE + Boolean isDefaultName = FALSE; +#endif // TARGET_OS_IPHONE CFStringRef localized = NULL; CFStringRef non_localized = NULL; Boolean ok = FALSE; @@ -973,6 +1313,15 @@ SCNetworkSetSetName(SCNetworkSetRef set, CFStringRef name) return FALSE; } + if (!__SCNetworkSetExists(set)) { + SC_log(LOG_ERR, "SCNetworkSetSetName() w/removed set\n set = %@\n name = %@", + set, + name != NULL ? name : CFSTR("")); + _SC_crash_once("SCNetworkSetSetName() w/removed set", NULL, NULL); + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + if ((name != NULL) && !isA_CFString(name)) { _SCErrorSet(kSCStatusInvalidArgument); return FALSE; @@ -981,28 +1330,42 @@ SCNetworkSetSetName(SCNetworkSetRef set, CFStringRef name) // if known, compare against localized name 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; - } - } - } + non_localized = copy_default_set_name(FALSE); + if (CFEqual(name, non_localized)) { + localized = copy_default_set_name(TRUE); + name = localized; +#if TARGET_OS_IPHONE + isDefaultName = TRUE; +#endif // TARGET_OS_IPHONE } +#if TARGET_OS_IPHONE + else { + localized = copy_default_set_name(TRUE); + isDefaultName = CFEqual(name, non_localized); + } +#endif // TARGET_OS_IPHONE + } + +#if TARGET_OS_IPHONE + if (!isDefaultName && isDefaultSet(set) && (geteuid() != 0)) { + // if we are trying to change the name of the "Automatic" set + SC_log(LOG_ERR, "SCNetworkSetSetName() failed, cannot rename : %@", set); + _SC_crash("The \"Automatic\" network set cannot be renamed", NULL, NULL); + _SCErrorSet(kSCStatusInvalidArgument); + goto done; } +#endif // TARGET_OS_IPHONE + +#define PREVENT_DUPLICATE_SET_NAMES +#ifdef PREVENT_DUPLICATE_SET_NAMES + +#if TARGET_OS_IPHONE + if (!isDefaultName) { + // On iOS, only block naming multiple sets with the name + // "Automatic". Others names are OK. + } else +#endif // TARGET_OS_IPHONE -#define PREVENT_DUPLICATE_SET_NAMES -#ifdef PREVENT_DUPLICATE_SET_NAMES if (name != NULL) { CFArrayRef sets; @@ -1039,18 +1402,12 @@ SCNetworkSetSetName(SCNetworkSetRef set, CFStringRef name) // if known, store non-localized name - if ((name != NULL) && (bundle != NULL) && (non_localized != NULL)) { + if ((name != NULL) && (non_localized != NULL)) { if (localized == NULL) { - localized = CFBundleCopyLocalizedString(bundle, - CFSTR("DEFAULT_SET_NAME"), - CFSTR("Automatic"), - NULL); + localized = copy_default_set_name(TRUE); } - - if (localized != NULL) { - if (CFEqual(name, localized)) { - name = non_localized; - } + if (CFEqual(name, localized)) { + name = non_localized; } } @@ -1058,18 +1415,18 @@ SCNetworkSetSetName(SCNetworkSetRef set, CFStringRef name) path = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID); entity = SCPreferencesPathGetValue(setPrivate->prefs, path); - if ((entity == NULL) && (name != NULL)) { - entity = CFDictionaryCreate(NULL, - NULL, - NULL, - 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - } - if (isA_CFDictionary(entity)) { + if (isA_CFDictionary(entity) || + ((entity == NULL) && (name != NULL))) { CFMutableDictionaryRef newEntity; - newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity); + if (entity != NULL) { + newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity); + } else { + newEntity = CFDictionaryCreateMutable(NULL, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + } if (name != NULL) { CFDictionarySetValue(newEntity, kSCPropUserDefinedName, name); } else { @@ -1082,6 +1439,10 @@ SCNetworkSetSetName(SCNetworkSetRef set, CFStringRef name) done : + if (ok) { + SC_log(LOG_DEBUG, "SCNetworkSetSetName(): %@", set); + } + if (localized != NULL) CFRelease(localized); if (non_localized != NULL) CFRelease(non_localized); return ok; @@ -1091,7 +1452,10 @@ SCNetworkSetSetName(SCNetworkSetRef set, CFStringRef name) Boolean SCNetworkSetSetServiceOrder(SCNetworkSetRef set, CFArrayRef newOrder) { + CFMutableArrayRef cleanOrder; CFDictionaryRef dict; + CFIndex i; + CFIndex n; CFMutableDictionaryRef newDict; Boolean ok; CFStringRef path; @@ -1102,10 +1466,15 @@ SCNetworkSetSetServiceOrder(SCNetworkSetRef set, CFArrayRef newOrder) return FALSE; } - if (isA_CFArray(newOrder)) { - CFIndex i; - CFIndex n = CFArrayGetCount(newOrder); + if (!__SCNetworkSetExists(set)) { + SC_log(LOG_ERR, "SCNetworkSetSetServiceOrder() w/removed set\n set = %@", set); + _SC_crash_once("SCNetworkSetSetServiceOrder() w/removed set", NULL, NULL); + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + if (isA_CFArray(newOrder)) { + n = CFArrayGetCount(newOrder); for (i = 0; i < n; i++) { CFStringRef serviceID; @@ -1135,7 +1504,24 @@ SCNetworkSetSetServiceOrder(SCNetworkSetRef set, CFArrayRef newOrder) &kCFTypeDictionaryValueCallBacks); } - CFDictionarySetValue(newDict, kSCPropNetServiceOrder, newOrder); + cleanOrder = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + n = CFArrayGetCount(newOrder); + for (i = 0; i < n; i++) { + CFIndex nClean = CFArrayGetCount(cleanOrder); + CFStringRef serviceID = CFArrayGetValueAtIndex(newOrder, i); + + if ((nClean == 0) || + !CFArrayContainsValue(cleanOrder, CFRangeMake(0, nClean), serviceID)) { + // if first reference to this serviceID + CFArrayAppendValue(cleanOrder, serviceID); + } else { + // skip duplicate serviceID + SC_log(LOG_ERR, "SCNetworkSetSetServiceOrder() found duplicate serviceID: removed %@\n", serviceID); + } + } + CFDictionarySetValue(newDict, kSCPropNetServiceOrder, cleanOrder); + CFRelease(cleanOrder); + ok = SCPreferencesPathSetValue(setPrivate->prefs, path, newDict); CFRelease(newDict); CFRelease(path); @@ -1148,6 +1534,31 @@ SCNetworkSetSetServiceOrder(SCNetworkSetRef set, CFArrayRef newOrder) #pragma mark SCNetworkSet SPIs +__private_extern__ +Boolean +__SCNetworkSetExists(SCNetworkSetRef set) +{ + CFDictionaryRef entity; + CFStringRef path; + SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set; + + if (setPrivate->prefs == NULL) { + return FALSE; + } + + path = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID); + entity = SCPreferencesPathGetValue(setPrivate->prefs, path); + CFRelease(path); + + if (!isA_CFDictionary(entity)) { + // if no "set" + return FALSE; + } + + return TRUE; +} + + static void add_supported_interfaces(CFMutableArrayRef interface_list, SCNetworkInterfaceRef interface) { @@ -1173,43 +1584,369 @@ add_supported_interfaces(CFMutableArrayRef interface_list, SCNetworkInterfaceRef } -Boolean -SCNetworkSetEstablishDefaultConfiguration(SCNetworkSetRef set) +static CFSetRef /* of SCNetworkInterfaceRef's */ +copyExcludedInterfaces(SCPreferencesRef prefs) { - CFIndex i; + CFMutableSetRef excluded; + CFArrayRef interfaces; + + excluded = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks); + +#if !TARGET_OS_IPHONE + // exclude Bond [member] interfaces + interfaces = SCBondInterfaceCopyAll(prefs); + if (interfaces != NULL) { + __SCBondInterfaceListCollectMembers(interfaces, excluded); + CFRelease(interfaces); + } +#endif // !TARGET_OS_IPHONE + + // exclude Bridge [member] interfaces + 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; - CFIndex n; - Boolean ok = TRUE; - CFArrayRef services; - Boolean setUpdated = FALSE; - SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set; - if (!isA_SCNetworkSet(set)) { - _SCErrorSet(kSCStatusInvalidArgument); - return FALSE; + // exclude Bridge [member] 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; + // first, assume that we only want to add new services // for those interfaces that are not represented in the // current set. services = SCNetworkSetCopyServices(set); - if ((services != NULL) && (CFArrayGetCount(services) > 0)) { - // but, if we are starting off with a non-empty - // set than we only want to add new services for - // those interfaces that are not represented in - // *any* set. + if ((services != NULL) && setPrivate->established) { + // but, if we are given an existing (or "established") set + // than we only want to add new services for those interfaces + // that are not represented in *any* set. CFRelease(services); services = SCNetworkServiceCopyAll(setPrivate->prefs); } - interfaces = SCNetworkInterfaceCopyAll(); - n = (interfaces != NULL) ? CFArrayGetCount(interfaces) : 0; + return services; +} + + +#if !TARGET_OS_IPHONE +static CF_RETURNS_RETAINED 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)) { + // if not a match, retain + CFArrayAppendValue(newServices, service); + continue; + } + + // if a match, update + 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 __inline__ Boolean +skipInterface(SCNetworkInterfaceRef interface) +{ + CFStringRef action; + + action = _SCNetworkInterfaceGetConfigurationAction(interface); + if (isA_CFString(action) && + CFEqual(action, kSCNetworkInterfaceConfigurationActionValueNone)) { + return TRUE; + } + + return FALSE; +} + + +CFComparisonResult +_SCNetworkSetCompare(const void *val1, const void *val2, void *context) +{ +#pragma unused(context) + CFStringRef id1; + CFStringRef id2; + CFStringRef name1; + CFStringRef name2; + SCNetworkSetRef s1 = (SCNetworkSetRef)val1; + SCNetworkSetRef s2 = (SCNetworkSetRef)val2; + + name1 = SCNetworkSetGetName(s1); + name2 = SCNetworkSetGetName(s2); + + if (name1 != NULL) { + if (name2 != NULL) { + return CFStringCompare(name1, name2, 0); + } else { + return kCFCompareLessThan; + } + } + + if (name2 != NULL) { + return kCFCompareGreaterThan; + } + + id1 = SCNetworkSetGetSetID(s1); + id2 = SCNetworkSetGetSetID(s2); + return CFStringCompare(id1, id2, 0); +} + + +static Boolean +__SCNetworkSetEstablishDefaultConfigurationForInterfaces(SCNetworkSetRef set, CFArrayRef interfaces, Boolean excludeHidden) +{ + CFSetRef excluded; + CFIndex i; + CFIndex n = 0; + Boolean ok = TRUE; + CFArrayRef services; + SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set; + Boolean updated = FALSE; +#if !TARGET_OS_IPHONE + Boolean updatedIFs = FALSE; +#endif // !TARGET_OS_IPHONE + +#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))) { + // after a "Reset Network Settings" we need to find (and + // add back) any VPN services that were orphaned. + orphans = SCNetworkServiceCopyAll(setPrivate->prefs); + } + } + + CFRelease(sets); + } +#endif // TARGET_OS_IPHONE + + // copy network services + services = copyServices(set); + + // copy network interfaces to be excluded + excluded = copyExcludedInterfaces(setPrivate->prefs); + +#if !TARGET_OS_IPHONE + // look for interfaces that should auto-magically be added + // to an Ethernet bridge + n = ((services != NULL) && (interfaces != NULL)) ? CFArrayGetCount(interfaces) : 0; + for (i = 0; i < n; i++) { + SCBridgeInterfaceRef bridge = NULL; + SCNetworkInterfaceRef interface; + + interface = CFArrayGetValueAtIndex(interfaces, i); + + if (excludeHidden && skipInterface(interface)) { + // if not auto-configure + continue; + } + + if (CFSetContainsValue(excluded, interface)) { + // if this interface is a member of a Bond or Bridge + continue; + } + + if (__SCNetworkServiceExistsForInterface(services, interface)) { + // if this is not a new interface + continue; + } + + if (_SCNetworkInterfaceIsBuiltin(interface) && + _SCNetworkInterfaceIsThunderbolt(interface) && + !isA_SCBridgeInterface(interface)) { + // add built-in Thunderbolt interfaces to bridge + bridge = copyAutoBridgeInterface(setPrivate->prefs, CFSTR("thunderbolt-bridge")); + } + + if (bridge != NULL) { + CFIndex bridgeIndex; + CFArrayRef members; + CFMutableArrayRef newMembers; + CFMutableSetRef newExcluded; + CFMutableArrayRef newInterfaces; + CFArrayRef newServices; + + // track the bridge interface (if it's in our list) + bridgeIndex = CFArrayGetFirstIndexOfValue(interfaces, + CFRangeMake(0, CFArrayGetCount(interfaces)), + bridge); + + // add new member interface + members = SCBridgeInterfaceGetMemberInterfaces(bridge); + if ((members != NULL) && (CFArrayGetCount(members) > 0)) { + newMembers = CFArrayCreateMutableCopy(NULL, 0, members); + updated = TRUE; // if we're updating an existing bridge + } else { + newMembers = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + } + CFArrayAppendValue(newMembers, interface); + ok = SCBridgeInterfaceSetMemberInterfaces(bridge, newMembers); + CFRelease(newMembers); + if (!ok) { + SC_log(LOG_INFO, "could not update bridge with \"%@\": %s", + SCNetworkInterfaceGetLocalizedDisplayName(interface), + SCErrorString(SCError())); + CFRelease(bridge); + continue; + } + + // exclude the new member interface + newExcluded = CFSetCreateMutableCopy(NULL, 0, excluded); + CFRelease(excluded); + CFSetAddValue(newExcluded, interface); + excluded = newExcluded; + + // update the list of interfaces to include the [new or updated] bridge + newInterfaces = CFArrayCreateMutableCopy(NULL, 0, interfaces); + if (bridgeIndex != kCFNotFound) { + CFArraySetValueAtIndex(newInterfaces, bridgeIndex, bridge); + } else { + CFArrayAppendValue(newInterfaces, bridge); + } + if (updatedIFs) { + CFRelease(interfaces); + } + interfaces = newInterfaces; + updatedIFs = TRUE; + + // refresh [existing] services + newServices = updateServices(services, bridge); + if (newServices != NULL) { + CFRelease(services); + services = newServices; + } + + CFRelease(bridge); + } + } +#endif // !TARGET_OS_IPHONE + + n = ((services != NULL) && (interfaces != NULL)) ? CFArrayGetCount(interfaces) : 0; for (i = 0; i < n; i++) { SCNetworkInterfaceRef interface; CFMutableArrayRef interface_list; interface = CFArrayGetValueAtIndex(interfaces, i); - if (_SCNetworkServiceExistsForInterface(services, interface)) { + + if (excludeHidden && skipInterface(interface)) { + // if not auto-configure + continue; + } + + if (CFSetContainsValue(excluded, interface)) { + // if this interface is a member of a Bond or Bridge + continue; + } + + if (__SCNetworkServiceExistsForInterface(services, interface)) { // if this is not a new interface continue; } @@ -1228,20 +1965,18 @@ SCNetworkSetEstablishDefaultConfiguration(SCNetworkSetRef set) service = SCNetworkServiceCreate(setPrivate->prefs, interface); if (service == NULL) { - SCLog(TRUE, LOG_DEBUG, - CFSTR("could not create service for \"%@\": %s\n"), - SCNetworkInterfaceGetLocalizedDisplayName(interface), - SCErrorString(SCError())); + SC_log(LOG_ERR, "could not create service for \"%@\": %s", + 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())); + SC_log(LOG_ERR, "could not estabish default configuration for \"%@\": %s", + SCNetworkInterfaceGetLocalizedDisplayName(interface), + SCErrorString(SCError())); SCNetworkServiceRemove(service); CFRelease(service); goto nextInterface; @@ -1249,17 +1984,16 @@ SCNetworkSetEstablishDefaultConfiguration(SCNetworkSetRef set) ok = SCNetworkSetAddService(set, service); if (!ok) { - SCLog(TRUE, LOG_DEBUG, - CFSTR("could not add service for \"%@\": %s\n"), - SCNetworkInterfaceGetLocalizedDisplayName(interface), - SCErrorString(SCError())); + SC_log(LOG_ERR, "could not add service for \"%@\": %s", + SCNetworkInterfaceGetLocalizedDisplayName(interface), + SCErrorString(SCError())); SCNetworkServiceRemove(service); CFRelease(service); goto nextInterface; } CFRelease(service); - setUpdated = TRUE; + updated = TRUE; } else { add_supported_interfaces(interface_list, interface); } @@ -1270,13 +2004,341 @@ SCNetworkSetEstablishDefaultConfiguration(SCNetworkSetRef set) } CFRelease(interface_list); } - if (interfaces != NULL) CFRelease(interfaces); +#if !TARGET_OS_IPHONE + if (updatedIFs && (interfaces != NULL)) { + CFRelease(interfaces); + } +#endif // !TARGET_OS_IPHONE if (services != NULL) CFRelease(services); + 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) { + if (updated) { + CFStringRef model; + + model = SCPreferencesGetValue(setPrivate->prefs, MODEL); + if (model == NULL) { + model = _SC_hw_model(FALSE); + SCPreferencesSetValue(setPrivate->prefs, MODEL, model); + } + } else { + // if no changes were made + _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, TRUE); + 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, FALSE); + 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; + } - if (ok && !setUpdated) { - // if no changes were made - _SCErrorSet(kSCStatusOK); + services = SCNetworkSetCopyServices(set); + if (services != NULL) { + n = CFArrayGetCount(services); + for (i = 0; i < n; i++) { + SCNetworkServiceRef service; + + service = CFArrayGetValueAtIndex(services, i); + if (!SCNetworkServiceGetEnabled(service)) { + // if not enabled + continue; + } + + if (!_SCNetworkServiceIsVPN(service)) { + // if not VPN service + continue; + } + + if (services_vpn == NULL) { + services_vpn = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + } + CFArrayAppendValue(services_vpn, service); + } + + CFRelease(services); + } + + if (services_vpn == NULL) { + // if no VPN services + 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 - return setUpdated; + 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)) { + // if selected service not a member of the current set + _SCErrorSet(kSCStatusInvalidArgument); + ok = FALSE; + goto done; + } + + for (i = 0; ok && (i < n); i++) { + SCNetworkServiceRef vpn; + + vpn = CFArrayGetValueAtIndex(services, i); + if (!_SCNetworkServiceIsVPN(vpn)) { + // if not VPN service + continue; + } + + ok = SCNetworkServiceSetEnabled(vpn, CFEqual(service, vpn)); + } + } + + done : + + if (services != NULL) CFRelease(services); + return ok; +} + + +Boolean +_SCNetworkSetSetSetID(SCNetworkSetRef set, CFStringRef newSetID) +{ + SCNetworkSetRef currentSet = NULL; + SCNetworkSetPrivateRef currentSetPrivate = NULL; + CFDictionaryRef entity; + CFStringRef newPath; + Boolean ok = FALSE; + CFStringRef oldPath = NULL; + SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set; + Boolean updateCurrentSet = FALSE; + + if (!isA_SCNetworkSet(set)) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + if (!isA_CFString(newSetID)) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + if (!__SCNetworkSetExists(set)) { + SC_log(LOG_ERR, "_SCNetworkSetSetSetID() w/removed set\n set = %@\n setID = %@", + set, + newSetID); + _SC_crash_once("_SCNetworkSetSetSetID() w/removed set", NULL, NULL); + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + // If newSetID is equal to current setID, our work is done + if (CFEqual(newSetID, setPrivate->setID)) { + return TRUE; + } + + newPath = SCPreferencesPathKeyCreateSet(NULL, newSetID); + entity = SCPreferencesPathGetValue(setPrivate->prefs, newPath); + if (isA_CFDictionary(entity)) { + // if the new set already exists + _SCErrorSet(kSCStatusKeyExists); + goto done; + } + + oldPath = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID); + entity = SCPreferencesPathGetValue(setPrivate->prefs, oldPath); + if (!isA_CFDictionary(entity)) { + // if the set has already been removed + _SCErrorSet(kSCStatusNoKey); + goto done; + } + + ok = SCPreferencesPathSetValue(setPrivate->prefs, newPath, entity); + if (!ok) { + goto done; + } + + ok = SCPreferencesPathRemoveValue(setPrivate->prefs, oldPath); + if (!ok) { + goto done; + } + + // update current set (if needed) + currentSet = SCNetworkSetCopyCurrent(setPrivate->prefs); + if (currentSet != NULL) { + currentSetPrivate = (SCNetworkSetPrivateRef)currentSet; + if (CFEqual(currentSetPrivate->setID, setPrivate->setID)) { + updateCurrentSet = TRUE; + } + CFRelease(currentSet); + } + + SC_log(LOG_DEBUG, "_SCNetworkSetSetID(): %@ --> %@", set, newSetID); + + // replace setID with new one + CFRetain(newSetID); + CFRelease(setPrivate->setID); + setPrivate->setID = newSetID; + + if (updateCurrentSet) { + SCNetworkSetSetCurrent(set); + } + + done: + + if (oldPath != NULL) { + CFRelease(oldPath); + } + if (newPath != NULL) { + CFRelease(newPath); + } + + return ok; }