1/* 2 * Copyright (c) 2013 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <SystemConfiguration/SystemConfiguration.h> 25#include <SystemConfiguration/SCPrivate.h> 26#include <arpa/inet.h> 27#include "scnc_main.h" 28#include "scnc_utils.h" 29#include "scnc_cache.h" 30 31#define kSCNCCacheFile CFSTR("com.apple.scnc-cache.plist") 32 33static SCPreferencesRef 34scnc_cache_get_prefs(void) 35{ 36 static SCPreferencesRef prefs = NULL; 37 static dispatch_once_t predicate = 0; 38 39 dispatch_once(&predicate, ^{ 40 prefs = SCPreferencesCreate(kCFAllocatorDefault, CFSTR("PPPController"), kSCNCCacheFile); 41 if (prefs == NULL) { 42 SCLog(TRUE, LOG_ERR, CFSTR("SCPreferencesCreate failed: %s"), SCErrorString(SCError())); 43 } 44 45 SCPreferencesSynchronize(prefs); 46 }); 47 48 return prefs; 49} 50 51static CFDictionaryRef 52scnc_cache_get_routes (struct service *serv) 53{ 54 SCPreferencesRef prefs = NULL; 55 prefs = scnc_cache_get_prefs(); 56 if (prefs == NULL) { 57 return NULL; 58 } 59 60 return SCPreferencesGetValue(prefs, serv->serviceID); 61} 62 63/* 64 scnc_cache_update_key 65 66 This function should be used for modifying the cache. It merges the desired key (cacheKey) from an existing 67 dictionary (sourceDict) into the cache's mutable dictionary (cacheDict). If the value in the replacementDict is NULL 68 and addChildDictionaryIfNULL is not set, the value is removed from the cacheDict; if addChildDictionaryIfNULL is 69 set, then an empty dictionary will be added. 70 71 If a block is passed as the final argument, it is assumed that the cacheKey points to a subdictionary, and the 72 block will include further recursive calls to scnc_cache_update_key. If the block is NULL, then the entire value 73 for cacheKey is replaced wholesale. 74 75 If sourceKey is not NULL, then that key will be used to look up values in sourceDict. When the values are entered 76 into cacheDict, they will use cacheKey. 77*/ 78static void 79scnc_cache_update_key (CFMutableDictionaryRef cacheDict, CFDictionaryRef sourceDict, Boolean addChildDictionaryIfNULL, CFStringRef cacheKey, CFStringRef sourceKey, void(^block)(CFMutableDictionaryRef, CFDictionaryRef)) 80{ 81 if (block) { 82 /* The type of the value being updated is a dictionary */ 83 CFDictionaryRef oldCacheSubDict = CFDictionaryGetValue(cacheDict, cacheKey); 84 CFMutableDictionaryRef newCacheSubDict = NULL; 85 CFDictionaryRef sourceChildDict = NULL; 86 if (isA_CFDictionary(sourceDict)) { 87 sourceChildDict = CFDictionaryGetValue(sourceDict, sourceKey?sourceKey:cacheKey); 88 } 89 90 if (isA_CFDictionary(sourceChildDict) || addChildDictionaryIfNULL) { 91 if (isA_CFDictionary(oldCacheSubDict)) { 92 newCacheSubDict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, oldCacheSubDict); 93 } else { 94 newCacheSubDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 95 } 96 97 if (newCacheSubDict) { 98 block(newCacheSubDict, sourceChildDict); 99 100 if (CFDictionaryGetCount(newCacheSubDict) > 0) { 101 if (!my_CFEqual(oldCacheSubDict, newCacheSubDict)) { 102 CFDictionarySetValue(cacheDict, cacheKey, newCacheSubDict); 103 } 104 } else if (oldCacheSubDict) { 105 CFDictionaryRemoveValue(cacheDict, cacheKey); 106 } 107 CFRelease(newCacheSubDict); 108 } 109 } else if (oldCacheSubDict) { 110 /* Remove the key if it is not in the replacement dictionary */ 111 CFDictionaryRemoveValue(cacheDict, cacheKey); 112 } 113 } else { 114 /* The type of the value being updated is ignored */ 115 CFPropertyListRef sourceValue = NULL; 116 if (isA_CFDictionary(sourceDict)) { 117 sourceValue = CFDictionaryGetValue(sourceDict, sourceKey?sourceKey:cacheKey); 118 } 119 120 if (isA_CFPropertyList(sourceValue)) { 121 CFDictionarySetValue(cacheDict, cacheKey, sourceValue); 122 } else if (CFDictionaryContainsKey(cacheDict, cacheKey)) { 123 /* Remove the key if it is not in the replacement dictionary */ 124 CFDictionaryRemoveValue(cacheDict, cacheKey); 125 } 126 } 127} 128 129/* Returns TRUE if updated the preferences file */ 130Boolean 131scnc_cache_routing_table (struct service *serv, CFDictionaryRef serviceConfig, Boolean useOldKeys, Boolean doFullTunnel) 132{ 133 SCPreferencesRef prefs = NULL; 134 CFDictionaryRef oldServiceDict = NULL; 135 CFMutableDictionaryRef newServiceDict = NULL; 136 137 prefs = scnc_cache_get_prefs(); 138 if (prefs == NULL) { 139 return FALSE; 140 } 141 142 oldServiceDict = SCPreferencesGetValue(prefs, serv->serviceID); 143 if (isA_CFDictionary(oldServiceDict)) { 144 newServiceDict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, oldServiceDict); 145 } else { 146 newServiceDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 147 } 148 149 if (newServiceDict) { 150 scnc_cache_update_key(newServiceDict, serviceConfig, doFullTunnel, kSCNetworkConnectionNetworkInfoIPv4, useOldKeys?kSCEntNetIPv4:NULL, ^(CFMutableDictionaryRef newIPv4Dict, CFDictionaryRef ipv4Config) { 151 scnc_cache_update_key(newIPv4Dict, ipv4Config, doFullTunnel, kSCNetworkConnectionNetworkInfoIncludedRoutes, useOldKeys?kSCPropNetIPv4IncludedRoutes:NULL, ^(CFMutableDictionaryRef dict, CFDictionaryRef config) { 152 if (doFullTunnel) { 153 struct in_addr v4_zeros = {INADDR_ANY}; 154 CFDataRef v4ZerosData = CFDataCreate(kCFAllocatorDefault, (uint8_t*)&v4_zeros, sizeof(struct in_addr)); 155 CFMutableDictionaryRef tempDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 156 if (v4ZerosData && tempDict) { 157 CFDictionaryAddValue(tempDict, kSCNetworkConnectionNetworkInfoAddresses, v4ZerosData); 158 CFDictionaryAddValue(tempDict, kSCNetworkConnectionNetworkInfoMasks, v4ZerosData); 159 } 160 scnc_cache_update_key(dict, tempDict, FALSE, kSCNetworkConnectionNetworkInfoAddresses, NULL, NULL); 161 scnc_cache_update_key(dict, tempDict, FALSE, kSCNetworkConnectionNetworkInfoMasks, NULL, NULL); 162 my_CFRelease(&tempDict); 163 my_CFRelease(&v4ZerosData); 164 } else { 165 scnc_cache_update_key(dict, config, FALSE, kSCNetworkConnectionNetworkInfoAddresses, useOldKeys?kSCPropNetIPv4RouteDestinationAddress:NULL, NULL); 166 scnc_cache_update_key(dict, config, FALSE, kSCNetworkConnectionNetworkInfoMasks, useOldKeys?kSCPropNetIPv4RouteSubnetMask:NULL, NULL); 167 } 168 }); 169 scnc_cache_update_key(newIPv4Dict, ipv4Config, FALSE, kSCNetworkConnectionNetworkInfoExcludedRoutes, useOldKeys?kSCPropNetIPv4ExcludedRoutes:NULL, ^(CFMutableDictionaryRef dict, CFDictionaryRef config) { 170 scnc_cache_update_key(dict, config, FALSE, kSCNetworkConnectionNetworkInfoAddresses, useOldKeys?kSCPropNetIPv4RouteDestinationAddress:NULL, NULL); 171 scnc_cache_update_key(dict, config, FALSE, kSCPropNetIPv4RouteSubnetMask, useOldKeys?kSCNetworkConnectionNetworkInfoMasks:NULL, NULL); 172 }); 173 }); 174 175 scnc_cache_update_key(newServiceDict, serviceConfig, doFullTunnel, kSCNetworkConnectionNetworkInfoIPv6, useOldKeys?kSCEntNetIPv6:NULL, ^(CFMutableDictionaryRef newIPv6Dict, CFDictionaryRef ipv6Config) { 176 scnc_cache_update_key(newIPv6Dict, ipv6Config, doFullTunnel, kSCNetworkConnectionNetworkInfoIncludedRoutes, useOldKeys?kSCPropNetIPv6IncludedRoutes:NULL, ^(CFMutableDictionaryRef dict, CFDictionaryRef config) { 177 if (doFullTunnel) { 178 struct in6_addr v6_zeros = IN6ADDR_ANY_INIT; 179 CFDataRef v6ZerosData = CFDataCreate(kCFAllocatorDefault, (uint8_t*)&v6_zeros, sizeof(struct in6_addr)); 180 CFMutableDictionaryRef tempDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 181 if (v6ZerosData && tempDict) { 182 CFDictionaryAddValue(tempDict, kSCNetworkConnectionNetworkInfoAddresses, v6ZerosData); 183 CFDictionaryAddValue(tempDict, kSCNetworkConnectionNetworkInfoMasks, v6ZerosData); 184 } 185 scnc_cache_update_key(dict, tempDict, FALSE, kSCNetworkConnectionNetworkInfoAddresses, NULL, NULL); 186 scnc_cache_update_key(dict, tempDict, FALSE, kSCNetworkConnectionNetworkInfoMasks, NULL, NULL); 187 my_CFRelease(&tempDict); 188 my_CFRelease(&v6ZerosData); 189 } else { 190 scnc_cache_update_key(dict, config, FALSE, kSCNetworkConnectionNetworkInfoAddresses, useOldKeys?kSCPropNetIPv6RouteDestinationAddress:NULL, NULL); 191 scnc_cache_update_key(dict, config, FALSE, kSCNetworkConnectionNetworkInfoMasks, useOldKeys?kSCPropNetIPv6RoutePrefixLength:NULL, NULL); 192 } 193 }); 194 scnc_cache_update_key(newIPv6Dict, ipv6Config, FALSE, kSCNetworkConnectionNetworkInfoExcludedRoutes, useOldKeys?kSCPropNetIPv6ExcludedRoutes:NULL, ^(CFMutableDictionaryRef dict, CFDictionaryRef config) { 195 scnc_cache_update_key(dict, config, FALSE, kSCNetworkConnectionNetworkInfoAddresses, useOldKeys?kSCPropNetIPv6RouteDestinationAddress:NULL, NULL); 196 scnc_cache_update_key(dict, config, FALSE, kSCNetworkConnectionNetworkInfoMasks, useOldKeys?kSCPropNetIPv6RoutePrefixLength:NULL, NULL); 197 }); 198 }); 199 200 if (!my_CFEqual(oldServiceDict, newServiceDict)) { 201 SCPreferencesSetValue(prefs, serv->serviceID, newServiceDict); 202 SCPreferencesCommitChanges(prefs); 203 SCPreferencesApplyChanges(prefs); 204 205 my_CFRelease(&serv->routeCache); 206 serv->routeCache = newServiceDict; 207 return TRUE; 208 } 209 CFRelease(newServiceDict); 210 } 211 212 return FALSE; 213} 214 215void 216scnc_cache_init_service (struct service *serv) 217{ 218 CFDictionaryRef routeCache = scnc_cache_get_routes(serv); 219 my_CFRelease(&serv->routeCache); 220 serv->routeCache = my_CFRetain(routeCache); 221} 222 223void 224scnc_cache_flush_removed_services (CFArrayRef activeServices) 225{ 226 SCPreferencesRef prefs = NULL; 227 CFArrayRef keys = NULL; 228 CFIndex numKeys = 0; 229 CFIndex numActiveServices = 0; 230 Boolean removed_values = FALSE; 231 232 prefs = scnc_cache_get_prefs(); 233 keys = SCPreferencesCopyKeyList(prefs); 234 numKeys = CFArrayGetCount(keys); 235 numActiveServices = CFArrayGetCount(activeServices); 236 237 for (CFIndex i = 0; i < numKeys; i++) { 238 CFStringRef key = CFArrayGetValueAtIndex(keys, i); 239 if (!CFArrayContainsValue(activeServices, CFRangeMake(0, numActiveServices), CFArrayGetValueAtIndex(keys, i))) { 240 SCPreferencesRemoveValue(prefs, key); 241 removed_values = TRUE; 242 } 243 } 244 245 my_CFRelease(&keys); 246 247 if (removed_values) { 248 SCPreferencesCommitChanges(prefs); 249 SCPreferencesApplyChanges(prefs); 250 } 251} 252