+ if (saveName != NULL) CFRelease(saveName);
+
+ if (servicePrivate->name != NULL) CFRelease(servicePrivate->name);
+ if (name != NULL) CFRetain(name);
+ servicePrivate->name = name;
+
+ if (ok) {
+ SC_log(LOG_DEBUG, "SCNetworkServiceSetName(): %@", service);
+ }
+
+ return ok;
+}
+
+
+#pragma mark -
+#pragma mark SCNetworkService SPIs
+
+
+__private_extern__
+Boolean
+__SCNetworkServiceExists(SCNetworkServiceRef service)
+{
+ CFDictionaryRef entity;
+ CFStringRef path;
+ SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service;
+
+ if (servicePrivate->prefs == NULL) {
+ // if no prefs
+ return FALSE;
+ }
+
+ path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL, // allocator
+ servicePrivate->serviceID, // service
+ kSCEntNetInterface); // entity
+ entity = SCPreferencesPathGetValue(servicePrivate->prefs, path);
+ CFRelease(path);
+
+ if (!isA_CFDictionary(entity)) {
+ // a "service" must have an "interface"
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+SCNetworkServicePrimaryRank
+SCNetworkServiceGetPrimaryRank(SCNetworkServiceRef service)
+{
+ CFDictionaryRef entity;
+ Boolean ok = TRUE;
+ CFStringRef path;
+ SCNetworkServicePrimaryRank rank = kSCNetworkServicePrimaryRankDefault;
+ CFStringRef rankStr = NULL;
+ SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service;
+
+ if (!isA_SCNetworkService(service)) {
+ _SCErrorSet(kSCStatusInvalidArgument);
+ return rank;
+ }
+
+ if (servicePrivate->prefs != NULL) {
+ path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,
+ servicePrivate->serviceID,
+ NULL);
+ entity = SCPreferencesPathGetValue(servicePrivate->prefs, path);
+ CFRelease(path);
+ if (isA_CFDictionary(entity)) {
+ rankStr = CFDictionaryGetValue(entity, kSCPropNetServicePrimaryRank);
+ ok = __str_to_rank(rankStr, &rank);
+ }
+ } else if (servicePrivate->store != NULL) {
+ path = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
+ kSCDynamicStoreDomainState,
+ servicePrivate->serviceID,
+ NULL);
+ entity = SCDynamicStoreCopyValue(servicePrivate->store, path);
+ CFRelease(path);
+ if (entity != NULL) {
+ if (isA_CFDictionary(entity)) {
+ rankStr = CFDictionaryGetValue(entity, kSCPropNetServicePrimaryRank);
+ ok = __str_to_rank(rankStr, &rank);
+ }
+ CFRelease(entity);
+ }
+ } else {
+ _SCErrorSet(kSCStatusInvalidArgument);
+ return rank;
+ }
+
+ if (!ok) {
+ rank = kSCNetworkServicePrimaryRankDefault;
+ _SCErrorSet(kSCStatusInvalidArgument);
+ } else if (rank == kSCNetworkServicePrimaryRankDefault) {
+ _SCErrorSet(kSCStatusOK);
+ }
+
+ return rank;
+}
+
+
+Boolean
+SCNetworkServiceSetPrimaryRank(SCNetworkServiceRef service,
+ SCNetworkServicePrimaryRank newRank)
+{
+ Boolean ok;
+ CFDictionaryRef entity;
+ CFMutableDictionaryRef newEntity;
+ CFStringRef path = NULL;
+ CFStringRef rankStr = NULL;
+ SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service;
+
+ if (!isA_SCNetworkService(service)) {
+ _SCErrorSet(kSCStatusInvalidArgument);
+ return FALSE;
+ }
+
+ if ((servicePrivate->prefs != NULL) && !__SCNetworkServiceExists(service)) {
+ SC_log(LOG_ERR, "SCNetworkServiceSetPrimaryRank() w/removed\n service = %@", service);
+ _SC_crash_once("SCNetworkServiceSetPrimaryRank() w/removed service", NULL, NULL);
+ _SCErrorSet(kSCStatusInvalidArgument);
+ return FALSE;
+ }
+
+ ok = __rank_to_str(newRank, &rankStr);
+ if (!ok) {
+ _SCErrorSet(kSCStatusInvalidArgument);
+ return FALSE;
+ }
+
+ if (servicePrivate->prefs != NULL) {
+ switch (newRank) {
+ case kSCNetworkServicePrimaryRankDefault:
+ case kSCNetworkServicePrimaryRankNever:
+ case kSCNetworkServicePrimaryRankScoped:
+ path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,
+ servicePrivate->serviceID,
+ NULL);
+ entity = SCPreferencesPathGetValue(servicePrivate->prefs, path);
+ if (entity != NULL) {
+ if (!isA_CFDictionary(entity)) {
+ // if corrupt prefs
+ _SCErrorSet(kSCStatusFailed);
+ goto done;
+ }
+ newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity);
+ } else {
+ newEntity = CFDictionaryCreateMutable(NULL,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ }
+ if (rankStr != NULL) {
+ CFDictionarySetValue(newEntity, kSCPropNetServicePrimaryRank, rankStr);
+ } else {
+ CFDictionaryRemoveValue(newEntity, kSCPropNetServicePrimaryRank);
+ }
+ if (CFDictionaryGetCount(newEntity) > 0) {
+ ok = SCPreferencesPathSetValue(servicePrivate->prefs, path, newEntity);
+ } else {
+ ok = SCPreferencesPathRemoveValue(servicePrivate->prefs, path);
+ }
+ CFRelease(newEntity);
+ if (!ok) {
+ goto done;
+ }
+ break;
+ default:
+ _SCErrorSet(kSCStatusInvalidArgument);
+ return FALSE;
+ }
+ } else if (servicePrivate->store != NULL) {
+ path = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
+ kSCDynamicStoreDomainState,
+ servicePrivate->serviceID,
+ NULL);
+ entity = SCDynamicStoreCopyValue(servicePrivate->store, path);
+ if (entity != NULL) {
+ if (!isA_CFDictionary(entity)) {
+ // if corrupt prefs
+ CFRelease(entity);
+ _SCErrorSet(kSCStatusFailed);
+ goto done;
+ }
+ newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity);
+ CFRelease(entity);
+ } else {
+ newEntity = CFDictionaryCreateMutable(NULL,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ }
+ if (rankStr != NULL) {
+ CFDictionarySetValue(newEntity, kSCPropNetServicePrimaryRank, rankStr);
+ } else {
+ CFDictionaryRemoveValue(newEntity, kSCPropNetServicePrimaryRank);
+ }
+ if (CFDictionaryGetCount(newEntity) > 0) {
+ ok = SCDynamicStoreSetValue(servicePrivate->store, path, newEntity);
+ } else {
+ ok = SCDynamicStoreRemoveValue(servicePrivate->store, path);
+ }
+ CFRelease(newEntity);
+ if (!ok) {
+ goto done;
+ }
+ } else {
+ _SCErrorSet(kSCStatusInvalidArgument);
+ return FALSE;
+ }
+
+ done :
+
+ if (path != NULL) CFRelease(path);
+ return ok;
+}
+
+
+Boolean
+_SCNetworkServiceIsVPN(SCNetworkServiceRef service)
+{
+ SCNetworkInterfaceRef interface;
+ CFStringRef interfaceType;
+
+ interface = SCNetworkServiceGetInterface(service);
+ if (interface == NULL) {
+ return FALSE;
+ }
+
+ interfaceType = SCNetworkInterfaceGetInterfaceType(interface);
+ if (CFEqual(interfaceType, kSCNetworkInterfaceTypePPP)) {
+ interface = SCNetworkInterfaceGetInterface(interface);
+ if (interface == NULL) {
+ return FALSE;
+ }
+
+ interfaceType = SCNetworkInterfaceGetInterfaceType(interface);
+ if (CFEqual(interfaceType, kSCNetworkInterfaceTypeL2TP)) {
+ return TRUE;
+ }
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated"
+ if (CFEqual(interfaceType, kSCNetworkInterfaceTypePPTP)) {
+ return TRUE;
+ }
+#pragma GCC diagnostic pop
+ } else if (CFEqual(interfaceType, kSCNetworkInterfaceTypeVPN)) {
+ return TRUE;
+ } else if (CFEqual(interfaceType, kSCNetworkInterfaceTypeIPSec)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+Boolean
+SCNetworkServiceSetExternalID(SCNetworkServiceRef service, CFStringRef identifierDomain, CFStringRef identifier)
+{
+ CFStringRef prefs_path;
+ CFDictionaryRef service_dictionary;
+ SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service;
+ Boolean success = FALSE;
+ CFStringRef prefixed_domain;
+
+ if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL) || !isA_CFString(identifierDomain)) {
+ _SCErrorSet(kSCStatusInvalidArgument);
+ return FALSE;
+ }
+
+ if (!__SCNetworkServiceExists(service)) {
+ SC_log(LOG_ERR, "SCNetworkServiceSetExternalID() w/removed\n service = %@\n id = %@",
+ service,
+ identifier);
+ _SC_crash_once("SCNetworkServiceSetExternalID() w/removed service", NULL, NULL);
+ _SCErrorSet(kSCStatusInvalidArgument);
+ return FALSE;
+ }
+
+ if (identifier != NULL && !isA_CFString(identifier)) {
+ _SCErrorSet(kSCStatusInvalidArgument);
+ return FALSE;
+ }
+
+ prefixed_domain = CFStringCreateWithFormat(NULL, 0, CFSTR("%s%@"), EXTERNAL_ID_DOMAIN_PREFIX, identifierDomain);
+
+ prefs_path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,
+ servicePrivate->serviceID,
+ NULL);
+
+ service_dictionary = SCPreferencesPathGetValue(servicePrivate->prefs, prefs_path);
+ if (isA_CFDictionary(service_dictionary) || ((service_dictionary == NULL) && (identifier != NULL))) {
+ CFMutableDictionaryRef new_service_dictionary;
+
+ if (service_dictionary != NULL) {
+ new_service_dictionary = CFDictionaryCreateMutableCopy(NULL, 0, service_dictionary);
+ } else {
+ new_service_dictionary = CFDictionaryCreateMutable(NULL,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ }
+
+ if (identifier != NULL) {
+ CFDictionarySetValue(new_service_dictionary, prefixed_domain, identifier);
+ } else {
+ CFDictionaryRemoveValue(new_service_dictionary, prefixed_domain);
+ }
+ success = SCPreferencesPathSetValue(servicePrivate->prefs, prefs_path, new_service_dictionary);
+ CFRelease(new_service_dictionary);
+ }
+ CFRelease(prefs_path);
+
+ if (identifier != NULL) {
+ if (servicePrivate->externalIDs == NULL) {
+ servicePrivate->externalIDs = CFDictionaryCreateMutable(NULL,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ }
+ CFDictionarySetValue(servicePrivate->externalIDs, prefixed_domain, identifier);
+ } else {
+ if (servicePrivate->externalIDs != NULL) {
+ CFDictionaryRemoveValue(servicePrivate->externalIDs, prefixed_domain);
+ }
+ }
+
+ CFRelease(prefixed_domain);
+
+ if (!success) {
+ _SCErrorSet(kSCStatusFailed);
+ }
+
+ return success;
+}
+
+
+CFStringRef
+SCNetworkServiceCopyExternalID(SCNetworkServiceRef service, CFStringRef identifierDomain)
+{
+ CFStringRef identifier = NULL;
+ CFStringRef prefixed_domain;
+ SCNetworkServicePrivateRef service_private = (SCNetworkServicePrivateRef)service;
+
+ if (!isA_SCNetworkService(service) || (service_private->prefs == NULL) || !isA_CFString(identifierDomain)) {
+ _SCErrorSet(kSCStatusInvalidArgument);
+ return NULL;
+ }
+
+ prefixed_domain = CFStringCreateWithFormat(NULL, 0, CFSTR("%s%@"), EXTERNAL_ID_DOMAIN_PREFIX, identifierDomain);
+
+ if (service_private->externalIDs != NULL) {
+ identifier = CFDictionaryGetValue(service_private->externalIDs, prefixed_domain);
+ if (identifier != NULL) {
+ CFRetain(identifier);
+ }
+ }
+
+ if (identifier == NULL) {
+ CFStringRef prefs_path;
+ CFDictionaryRef service_dictionary;
+
+ prefs_path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,
+ service_private->serviceID,
+ NULL);
+
+ service_dictionary = SCPreferencesPathGetValue(service_private->prefs, prefs_path);
+ if (isA_CFDictionary(service_dictionary)) {
+ identifier = CFDictionaryGetValue(service_dictionary, prefixed_domain);
+ if (identifier != NULL) {
+ CFRetain(identifier);
+ if (service_private->externalIDs == NULL) {
+ service_private->externalIDs = CFDictionaryCreateMutable(NULL,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ }
+ CFDictionarySetValue(service_private->externalIDs, prefixed_domain, identifier);
+ }
+ }
+ CFRelease(prefs_path);
+ }
+
+ CFRelease(prefixed_domain);
+
+ if (identifier == NULL) {
+ _SCErrorSet(kSCStatusNoKey);
+ }
+
+ return identifier;
+}
+
+
+typedef struct {
+ CFStringRef oldServiceID;
+ CFStringRef newServiceID;
+} serviceContext, *serviceContextRef;
+
+
+static void
+replaceServiceID(const void *value, void *context)
+{
+ CFStringRef link = NULL;
+ CFStringRef oldLink;
+ CFMutableArrayRef newServiceOrder;
+ CFStringRef path;
+ serviceContextRef service_context = (serviceContextRef)context;
+ CFArrayRef serviceOrder = NULL;
+ SCNetworkSetRef set = (SCNetworkSetRef)value;
+ SCNetworkSetPrivateRef setPrivate = (SCNetworkSetPrivateRef)set;
+
+ // update service order
+ serviceOrder = SCNetworkSetGetServiceOrder(set);
+ if ((isA_CFArray(serviceOrder) != NULL) &&
+ CFArrayContainsValue(serviceOrder,
+ CFRangeMake(0, CFArrayGetCount(serviceOrder)),
+ service_context->oldServiceID)) {
+ CFIndex count;
+ CFIndex serviceOrderIndex;
+
+ // replacing all instances of old service ID with new one
+ newServiceOrder = CFArrayCreateMutableCopy(NULL, 0, serviceOrder);
+ count = CFArrayGetCount(newServiceOrder);
+ for (serviceOrderIndex = 0; serviceOrderIndex < count; serviceOrderIndex++) {
+ CFStringRef serviceID;
+
+ serviceID = CFArrayGetValueAtIndex(newServiceOrder, serviceOrderIndex);
+ if (CFEqual(serviceID, service_context->oldServiceID)) {
+ CFArraySetValueAtIndex(newServiceOrder, serviceOrderIndex, service_context->newServiceID);
+ }
+ }
+ SCNetworkSetSetServiceOrder(set, newServiceOrder);
+ CFRelease(newServiceOrder);
+ }
+
+ // check if service with old serviceID is part of the set
+ path = SCPreferencesPathKeyCreateSetNetworkServiceEntity(NULL, // allocator
+ setPrivate->setID, // set
+ service_context->oldServiceID, // service
+ NULL); // entity
+ oldLink = SCPreferencesPathGetLink(setPrivate->prefs, path);
+ if (oldLink == NULL) {
+ // don't make any changes if service with old serviceID is not found
+ goto done;
+ }
+
+ // remove link between "set" and old "service"
+ (void) SCPreferencesPathRemoveValue(setPrivate->prefs, path);
+ CFRelease(path);
+
+ // create the link between "set" and the "service"
+ path = SCPreferencesPathKeyCreateSetNetworkServiceEntity(NULL, // allocator
+ setPrivate->setID, // set
+ service_context->newServiceID, // service
+ NULL); // entity
+ link = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL, // allocator
+ service_context->newServiceID, // service
+ NULL); // entity
+ (void) SCPreferencesPathSetLink(setPrivate->prefs, path, link);
+
+ done:
+
+ if (path != NULL) {
+ CFRelease(path);
+ }
+ if (link != NULL) {
+ CFRelease(link);
+ }
+
+ return;
+}
+
+
+Boolean
+_SCNetworkServiceSetServiceID(SCNetworkServiceRef service, CFStringRef newServiceID)
+{
+ CFArrayRef allSets = NULL;
+ CFDictionaryRef entity;
+ CFStringRef newPath;
+ Boolean ok = FALSE;
+ CFStringRef oldPath = NULL;
+ serviceContext service_context;
+ SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service;
+
+ if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) {
+ _SCErrorSet(kSCStatusInvalidArgument);
+ return FALSE;
+ }
+
+ if (!isA_CFString(newServiceID)) {
+ _SCErrorSet(kSCStatusInvalidArgument);
+ return FALSE;
+ }
+
+ if (CFEqual(newServiceID, servicePrivate->serviceID)) {
+ // no work needs to be done if new service ID is equal to current service ID
+ return TRUE;
+ }
+
+ if (!__SCNetworkServiceExists(service)) {
+ SC_log(LOG_ERR, "_SCNetworkServiceSetServiceID() w/removed service\n service = %@\n serviceID = %@",
+ service,
+ newServiceID);
+ _SC_crash_once("_SCNetworkServiceSetServiceID() w/removed service", NULL, NULL);
+ _SCErrorSet(kSCStatusInvalidArgument);
+ return FALSE;
+ }
+
+ newPath = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL, // allocator
+ newServiceID, // service
+ NULL); // entity
+ entity = SCPreferencesPathGetValue(servicePrivate->prefs, newPath);
+ if (isA_CFDictionary(entity)) {
+ // if the new service already exists
+ _SCErrorSet(kSCStatusKeyExists);
+ goto done;
+ }
+
+ oldPath = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL, // allocator
+ servicePrivate->serviceID, // service
+ NULL); // entity
+ entity = SCPreferencesPathGetValue(servicePrivate->prefs, oldPath);
+ if (!isA_CFDictionary(entity)) {
+ // if the service has already been removed
+ _SCErrorSet(kSCStatusNoKey);
+ goto done;
+ }
+
+ ok = SCPreferencesPathSetValue(servicePrivate->prefs, newPath, entity);
+ if (!ok) goto done;
+
+ ok = SCPreferencesPathRemoveValue(servicePrivate->prefs, oldPath);
+ if (!ok) goto done;
+
+ allSets = SCNetworkSetCopyAll(servicePrivate->prefs);
+
+ service_context.newServiceID = newServiceID;
+ service_context.oldServiceID = servicePrivate->serviceID;
+
+ // find all sets w/oldServiceID and update
+ // ... and update the serviceOrder
+ CFArrayApplyFunction(allSets,
+ CFRangeMake(0, CFArrayGetCount(allSets)),
+ replaceServiceID,
+ &service_context);
+
+ if (servicePrivate->interface != NULL) {
+ SCNetworkInterfaceRef newInterface;
+
+ // duplicate the interface and associate the copy with the new service ID
+ newInterface = (SCNetworkInterfaceRef)__SCNetworkInterfaceCreateCopy(NULL,
+ servicePrivate->interface,
+ servicePrivate->prefs,
+ newServiceID);
+ CFRelease(servicePrivate->interface);
+ servicePrivate->interface = newInterface;
+ }
+
+ SC_log(LOG_DEBUG, "_SCNetworkServiceSetServiceID(): %@ --> %@", service, newServiceID);
+
+ // replace serviceID with new one
+ CFRetain(newServiceID);
+ CFRelease(servicePrivate->serviceID);
+ servicePrivate->serviceID = newServiceID;
+
+ done:
+
+ if (oldPath != NULL) {
+ CFRelease(oldPath);
+ }
+ if (newPath != NULL) {
+ CFRelease(newPath);
+ }
+ if (allSets != NULL) {
+ CFRelease(allSets);
+ }