1/* 2 * Copyright (c) 2012 Apple Inc. 3 * All rights reserved. 4 */ 5 6#include <SystemConfiguration/SystemConfiguration.h> 7#include <SystemConfiguration/SCPrivate.h> 8 9#include "scnc_main.h" 10#include "scnc_utils.h" 11#include "behaviors.h" 12 13#define kVPNBehaviorsPlistFile CFSTR("com.apple.pppcontroller-vpn-behaviors.plist") 14#define kVPNBehaviorsVODAlwaysMeansOnRetry CFSTR("VODAlwaysMeansOnRetry") 15 16#define kVPNBehaviorsVODAlwaysMeansOnRetryDefault kCFBooleanTrue 17 18typedef struct behaviors_context { 19 SCPreferencesRef prefs; 20 BehaviorsUpdatedBlock cb_block; 21} BehaviorsContext; 22 23static void 24behaviors_prefs_changed(SCPreferencesRef prefs, SCPreferencesNotification notification_type, void *info) 25{ 26#pragma unused(prefs) 27 if (notification_type == kSCPreferencesNotificationCommit) { 28 BehaviorsContext *context = (BehaviorsContext *)info; 29 context->cb_block(); 30 } 31} 32 33void 34behaviors_modify_ondemand(CFMutableDictionaryRef trigger_dict, BehaviorsUpdatedBlock cb_block) 35{ 36 CFBooleanRef always_means_on_retry; 37 static BehaviorsContext *context = NULL; 38 static dispatch_once_t predicate = 0; 39 __block Boolean success = TRUE; 40 41 dispatch_once(&predicate, ^{ 42 SCPreferencesContext prefs_ctx; 43 44 context = (BehaviorsContext *)CFAllocatorAllocate(kCFAllocatorDefault, sizeof(*context), 0); 45 46 context->prefs = SCPreferencesCreate(kCFAllocatorDefault, CFSTR("PPPController"), kVPNBehaviorsPlistFile); 47 if (context->prefs == NULL) { 48 SCLog(TRUE, LOG_ERR, CFSTR("SCPreferencesCreate failed: %s"), SCErrorString(SCError())); 49 success = FALSE; 50 goto done; 51 } 52 53 memset(&prefs_ctx, 0, sizeof(prefs_ctx)); 54 prefs_ctx.info = context; 55 if (!SCPreferencesSetCallback(context->prefs, behaviors_prefs_changed, &prefs_ctx)) { 56 SCLog(TRUE, LOG_ERR, CFSTR("SCPreferencesSetCallback failed: %s"), SCErrorString(SCError())); 57 success = FALSE; 58 goto done; 59 } 60 61 if (!SCPreferencesScheduleWithRunLoop(context->prefs, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) { 62 SCLog(TRUE, LOG_ERR, CFSTR("SCPreferencesSetDispatchQueue failed: %s"), SCErrorString(SCError())); 63 success = FALSE; 64 goto done; 65 } 66 67done: 68 if (!success) { 69 if (context->prefs != NULL) { 70 CFRelease(context->prefs); 71 } 72 CFAllocatorDeallocate(kCFAllocatorDefault, context); 73 context = NULL; 74 } else { 75 context->cb_block = Block_copy(cb_block); 76 } 77 }); 78 79 if (!success) { 80 return; 81 } 82 83 always_means_on_retry = SCPreferencesGetValue(context->prefs, kVPNBehaviorsVODAlwaysMeansOnRetry); 84 if (!isA_CFBoolean(always_means_on_retry)) { 85 always_means_on_retry = kVPNBehaviorsVODAlwaysMeansOnRetryDefault; 86 } 87 88 if (CFBooleanGetValue(always_means_on_retry)) { 89 CFArrayRef always_domains = CFDictionaryGetValue(trigger_dict, kSCNetworkConnectionOnDemandMatchDomainsAlways); 90 if (isA_CFArray(always_domains)) { 91 CFMutableArrayRef new_on_retry_domains; 92 CFArrayRef on_retry_domains = CFDictionaryGetValue(trigger_dict, kSCNetworkConnectionOnDemandMatchDomainsOnRetry); 93 94 new_on_retry_domains = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, always_domains); 95 96 if (isA_CFArray(on_retry_domains)) { 97 CFIndex on_retry_count = CFArrayGetCount(on_retry_domains); 98 if (on_retry_count > 0) { 99 CFArrayAppendArray(new_on_retry_domains, on_retry_domains, CFRangeMake(0, on_retry_count)); 100 } 101 } 102 103 CFDictionaryRemoveValue(trigger_dict, kSCNetworkConnectionOnDemandMatchDomainsAlways); 104 CFDictionarySetValue(trigger_dict, kSCNetworkConnectionOnDemandMatchDomainsOnRetry, new_on_retry_domains); 105 106 CFRelease(new_on_retry_domains); 107 } 108 } 109 110 SCPreferencesSynchronize(context->prefs); 111} 112