1/* 2 * Copyright (c) 2013 Apple Inc. All rights reserved. 3 */ 4#include <arpa/inet.h> 5#include <SystemConfiguration/SCPrivate.h> 6 7#include "reachability.h" 8#include "scnc_main.h" 9 10static ReachabilityChangedBlock g_cb_block = NULL; 11static CFRunLoopRef g_cb_runloop = NULL; 12static CFTypeRef g_cb_runloop_mode = NULL; 13static dispatch_queue_t g_reach_queue = NULL; 14 15static void 16reachability_target_dispose(SCNetworkReachabilityRef target) 17{ 18 dispatch_async(g_reach_queue, ^{ 19 SCNetworkReachabilitySetCallback(target, NULL, NULL); 20 SCNetworkReachabilitySetDispatchQueue(target, NULL); 21 CFRelease(target); 22 }); 23} 24 25static void 26remote_address_reachability_callback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) 27{ 28 CFStringRef service_id = (CFStringRef)info; 29 int reach_if_index; 30 31 CFRetain(target); 32 CFRetain(service_id); 33 34 SCNetworkReachabilityGetFlags(target, &flags); 35 reach_if_index = SCNetworkReachabilityGetInterfaceIndex(target); 36 37 CFRunLoopPerformBlock(g_cb_runloop, g_cb_runloop_mode, ^{ 38 struct service *serv = findbyserviceID(service_id); 39 if (serv != NULL && serv->remote_address_reachability == target) { 40 serv->remote_address_reach_flags = flags; 41 serv->remote_address_reach_ifindex = reach_if_index; 42 g_cb_block(serv); 43 } 44 CFRelease(target); 45 CFRelease(service_id); 46 }); 47 CFRunLoopWakeUp(g_cb_runloop); 48} 49 50void 51reachability_init(CFRunLoopRef cb_runloop, CFTypeRef cb_runloop_mode, ReachabilityChangedBlock cb_block) 52{ 53 static dispatch_once_t init_reachability = 0; 54 55 dispatch_once(&init_reachability, ^{ 56 g_reach_queue = dispatch_queue_create("PPPController reachability dispatch queue", NULL); 57 g_cb_runloop = cb_runloop; 58 CFRetain(g_cb_runloop); 59 g_cb_runloop_mode = cb_runloop_mode; 60 CFRetain(g_cb_runloop_mode); 61 g_cb_block = Block_copy(cb_block); 62 }); 63} 64 65void 66reachability_clear(struct service *serv) 67{ 68 if (serv->remote_address_reachability != NULL) { 69 reachability_target_dispose(serv->remote_address_reachability); 70 } 71 serv->remote_address_reachability = NULL; 72 serv->remote_address_reach_flags = 0; 73 serv->remote_address_reach_ifindex = -1; 74} 75 76void 77reachability_reset(struct service *serv) 78{ 79 CFStringRef remote_address_key = NULL; 80 81 reachability_clear(serv); 82 83 switch (serv->type) { 84 case TYPE_PPP: 85 remote_address_key = kSCPropNetPPPCommRemoteAddress; 86 break; 87 case TYPE_IPSEC: 88 remote_address_key = kSCPropNetIPSecRemoteAddress; 89 break; 90 case TYPE_VPN: 91 remote_address_key = kSCPropNetVPNRemoteAddress; 92 break; 93 } 94 95 if (remote_address_key == NULL) { 96 return; 97 } 98 99 CFStringRef remote_address = CFDictionaryGetValue(serv->systemprefs, remote_address_key); 100 if (isA_CFString(remote_address) && CFStringGetLength(remote_address) > 0) { 101 CFCharacterSetRef slash_set = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, CFSTR("/")); 102 CFCharacterSetRef colon_set = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, CFSTR(":")); 103 CFRange slash_range; 104 CFRange colon_range; 105 char *addr_cstr; 106 CFIndex addr_cstr_len; 107 struct sockaddr_storage sa_storage; 108 CFMutableDictionaryRef reach_options; 109 CFStringRef service_id; 110 111 CFRetain(remote_address); 112 113 memset(&sa_storage, 0, sizeof(sa_storage)); 114 115 if (CFStringFindCharacterFromSet(remote_address, 116 slash_set, 117 CFRangeMake(0, CFStringGetLength(remote_address)), 118 0, 119 &slash_range)) 120 { 121 CFURLRef url = CFURLCreateWithString(kCFAllocatorDefault, remote_address, NULL); 122 if (url != NULL) { 123 /* Check to see if there is a scheme on the URL. If not, add one. The hostname will not parse without a scheme */ 124 CFRange schemeRange = CFURLGetByteRangeForComponent(url, kCFURLComponentScheme, NULL); 125 if (schemeRange.location == kCFNotFound) { 126 CFRelease(url); 127 CFStringRef address = CFStringCreateWithFormat(kCFAllocatorDefault, 0, CFSTR("https://%@"), remote_address); 128 url = CFURLCreateWithString(kCFAllocatorDefault, address, NULL); 129 CFRelease(address); 130 } 131 if (url != NULL) { 132 CFRelease(remote_address); 133 remote_address = CFURLCopyHostName(url); 134 CFRelease(url); 135 } 136 } 137 } else if (CFStringFindCharacterFromSet(remote_address, 138 colon_set, 139 CFRangeMake(0, CFStringGetLength(remote_address)), 140 0, 141 &colon_range)) 142 { 143 CFStringRef address = CFStringCreateWithSubstring(kCFAllocatorDefault, 144 remote_address, 145 CFRangeMake(0, colon_range.location)); 146 CFRelease(remote_address); 147 remote_address = address; 148 } 149 150 CFRelease(slash_set); 151 CFRelease(colon_set); 152 153 if (remote_address == NULL) { 154 return; 155 } 156 157 addr_cstr_len = CFStringGetLength(remote_address); /* Assume that the address is ASCII */ 158 addr_cstr = CFAllocatorAllocate(kCFAllocatorDefault, addr_cstr_len + 1, 0); 159 160 CFStringGetCString(remote_address, addr_cstr, addr_cstr_len, kCFStringEncodingASCII); 161 162 reach_options = CFDictionaryCreateMutable(kCFAllocatorDefault, 163 0, 164 &kCFTypeDictionaryKeyCallBacks, 165 &kCFTypeDictionaryValueCallBacks); 166 167 if (inet_pton(AF_INET, addr_cstr, &((struct sockaddr_in *)&sa_storage)->sin_addr) == 1) { 168 CFDataRef addr_data; 169 struct sockaddr_in *sa_in = (struct sockaddr_in *)&sa_storage; 170 sa_in->sin_family = AF_INET; 171 sa_in->sin_len = sizeof(struct sockaddr_in); 172 addr_data = CFDataCreate(kCFAllocatorDefault, (const uint8_t *)sa_in, sa_in->sin_len); 173 CFDictionarySetValue(reach_options, kSCNetworkReachabilityOptionRemoteAddress, addr_data); 174 CFRelease(addr_data); 175 } else if (inet_pton(AF_INET6, addr_cstr, &((struct sockaddr_in6 *)&sa_storage)->sin6_addr) == 1) { 176 CFDataRef addr_data; 177 struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)&sa_storage; 178 sa_in6->sin6_family = AF_INET6; 179 sa_in6->sin6_len = sizeof(struct sockaddr_in6); 180 addr_data = CFDataCreate(kCFAllocatorDefault, (const uint8_t *)sa_in6, sa_in6->sin6_len); 181 CFDictionarySetValue(reach_options, kSCNetworkReachabilityOptionRemoteAddress, addr_data); 182 CFRelease(addr_data); 183 } else { 184 CFDictionarySetValue(reach_options, kSCNetworkReachabilityOptionNodeName, remote_address); 185 } 186 187 CFRelease(remote_address); 188 CFAllocatorDeallocate(kCFAllocatorDefault, addr_cstr); 189 190 CFDictionarySetValue(reach_options, kSCNetworkReachabilityOptionConnectionOnDemandBypass, kCFBooleanTrue); 191 192 service_id = serv->serviceID; 193 CFRetain(service_id); 194 195 dispatch_async(g_reach_queue, ^{ 196 SCNetworkReachabilityRef reach_ref = SCNetworkReachabilityCreateWithOptions(kCFAllocatorDefault, reach_options); 197 CFRelease(reach_options); 198 199 if (reach_ref != NULL) { 200 SCNetworkReachabilityContext reach_ctx = { 0, (void *)service_id, CFRetain, CFRelease, CFCopyDescription }; 201 SCNetworkReachabilitySetCallback(reach_ref, remote_address_reachability_callback, &reach_ctx); 202 SCNetworkReachabilitySetDispatchQueue(reach_ref, g_reach_queue); 203 204 CFRunLoopPerformBlock(g_cb_runloop, g_cb_runloop_mode, ^{ 205 struct service *serv = findbyserviceID(service_id); 206 Boolean retained = FALSE; 207 if (serv != NULL) { 208 if (serv->remote_address_reachability != NULL) { 209 reachability_target_dispose(serv->remote_address_reachability); 210 serv->remote_address_reachability = NULL; 211 } 212 serv->remote_address_reachability = reach_ref; 213 retained = TRUE; 214 dispatch_async(g_reach_queue, ^{ 215 remote_address_reachability_callback(reach_ref, 0, (void *)service_id); 216 }); 217 } 218 219 if (!retained) { 220 reachability_target_dispose(reach_ref); 221 } 222 }); 223 CFRunLoopWakeUp(g_cb_runloop); 224 } else { 225 SCLog(TRUE, LOG_ERR, CFSTR("reset_reachability: failed to create a reachability target for %@"), remote_address); 226 } 227 CFRelease(service_id); 228 }); 229 } 230} 231 232