1/* 2 * Copyright (c) 2012 Apple 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 <stdio.h> 25#include <pthread.h> 26 27#include <CoreFoundation/CoreFoundation.h> 28#include <CFNetwork/CFNetwork.h> 29#include <SystemConfiguration/SystemConfiguration.h> 30 31static SCNetworkReachabilityRef g_reachability = NULL; 32static CFURLRef g_url = NULL; 33static CFReadStreamRef g_rstream = NULL; 34 35static char * 36string2CString(CFStringRef str) 37{ 38 UInt8 *buffer; 39 CFIndex clen; 40 CFRange r = CFRangeMake(0, CFStringGetLength(str)); 41 42 if (CFStringGetBytes(str, r, kCFStringEncodingASCII, 0, false, NULL, 0, &clen) > 0) { 43 buffer = (UInt8 *)CFAllocatorAllocate(kCFAllocatorDefault, (clen + 1) * sizeof(UInt8), 0); 44 45 if (buffer != NULL) { 46 if (CFStringGetBytes(str, r, kCFStringEncodingASCII, 0, false, buffer, clen, NULL)) { 47 buffer[clen] = '\0'; 48 return (char *)buffer; 49 } 50 CFAllocatorDeallocate(kCFAllocatorDefault, buffer); 51 } 52 } 53 54 return NULL; 55} 56 57static void 58printReachabilityFlags(const char *source, SCNetworkReachabilityFlags flags) 59{ 60 printf("[%s] Reachability flags (%x):\n", source, flags); 61 if (flags & kSCNetworkReachabilityFlagsTransientConnection) { 62 printf("[%s] transient\n", source); 63 } 64 if (flags & kSCNetworkReachabilityFlagsReachable) { 65 printf("[%s] reachable\n", source); 66 } 67 if (flags & kSCNetworkReachabilityFlagsConnectionRequired) { 68 printf("[%s] connection required\n", source); 69 } 70 if (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) { 71 printf("[%s] connection on traffic\n", source); 72 } 73 if (flags & kSCNetworkReachabilityFlagsInterventionRequired) { 74 printf("[%s] intervention required\n", source); 75 } 76 if (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) { 77 printf("[%s] connection on demand\n", source); 78 } 79 if (flags & kSCNetworkReachabilityFlagsIsLocalAddress) { 80 printf("[%s] local address\n", source); 81 } 82 if (flags & kSCNetworkReachabilityFlagsIsDirect) { 83 printf("[%s] direct\n", source); 84 } 85#if TARGET_OS_EMBEDDED 86 if (flags & kSCNetworkReachabilityFlagsIsWWAN) { 87 printf("[%s] wwan\n", source); 88 } 89#endif 90} 91 92static void 93handleReachabilityUpdate( 94 SCNetworkReachabilityRef target, 95 SCNetworkReachabilityFlags flags, 96 void *info) 97{ 98 printReachabilityFlags("RunLoop", flags); 99} 100 101static SCNetworkReachabilityRef 102createReachabilityWithCFHost(CFHostRef theHost) 103{ 104 SCNetworkReachabilityRef reachRef = NULL; 105 Boolean resolved = FALSE; 106 CFArrayRef addrs = CFHostGetAddressing(theHost, &resolved); 107 108 if (resolved && addrs != NULL && CFArrayGetCount(addrs) > 0) { 109 CFDataRef addr = (CFDataRef)CFArrayGetValueAtIndex(addrs, 0); 110 111 reachRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (struct sockaddr *)CFDataGetBytePtr(addr)); 112 } else { 113 CFArrayRef names = CFHostGetNames(theHost, NULL); 114 115 if (names != NULL && CFArrayGetCount(names) > 0) { 116 CFStringRef host = (CFStringRef)CFArrayGetValueAtIndex(names, 0); 117 char *chost = string2CString(host); 118 119 reachRef = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, chost); 120 121 CFAllocatorDeallocate(kCFAllocatorDefault, chost); 122 } 123 } 124 125 if (reachRef != NULL) { 126 SCNetworkReachabilityContext reach_ctx = { 0, NULL, NULL, NULL, NULL }; 127 SCNetworkReachabilitySetCallback(reachRef, handleReachabilityUpdate, 128 &reach_ctx); 129 CFShow(reachRef); 130 } else { 131 fprintf(stderr, "Failed to create a reachability object\n"); 132 } 133 134 return reachRef; 135} 136 137static void 138handleDownload(CFReadStreamRef rstream, CFStreamEventType eventType, void *info) 139{ 140 Boolean done = FALSE; 141 142 if (eventType == kCFStreamEventHasBytesAvailable) { 143 UInt8 buffer[1024]; 144 145 while (CFReadStreamHasBytesAvailable(rstream)) { 146 CFIndex count = CFReadStreamRead(rstream, buffer, sizeof(buffer)); 147 if (count == 0) { 148 done = TRUE; 149 } 150 } 151 } else if (eventType == kCFStreamEventEndEncountered) { 152 printf("Download completed\n"); 153 done = TRUE; 154 } else if (eventType == kCFStreamEventErrorOccurred) { 155 printf("Download error\n"); 156 done = TRUE; 157 } else { 158 printf("Got stream event: %lu\n", eventType); 159 } 160 161 if (!done) { 162 return; 163 } 164 165 CFReadStreamUnscheduleFromRunLoop(rstream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); 166 CFReadStreamClose(rstream); 167 CFRelease(rstream); 168 169 g_rstream = NULL; 170} 171 172static void 173startDownload(void) 174{ 175 CFHTTPMessageRef request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("GET"), g_url, kCFHTTPVersion1_1); 176 CFReadStreamRef rstream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, request); 177 CFStreamClientContext ctx = { 0, NULL, NULL, NULL, NULL }; 178 179 printf("Starting download\n"); 180 181 CFReadStreamSetClient(rstream, 182 kCFStreamEventEndEncountered | kCFStreamEventErrorOccurred | kCFStreamEventHasBytesAvailable, 183 handleDownload, 184 &ctx); 185 186 CFReadStreamScheduleWithRunLoop(rstream, CFRunLoopGetMain(), kCFRunLoopDefaultMode); 187 188 CFReadStreamOpen(rstream); 189 190 g_rstream = rstream; 191 192 CFRelease(request); 193} 194 195static void 196downloadTimerFired(CFRunLoopTimerRef timer, void *info) 197{ 198 if (g_rstream != NULL) { 199 handleDownload(g_rstream, kCFStreamEventErrorOccurred, NULL); 200 } 201 202 203 SCNetworkReachabilityUnscheduleFromRunLoop(g_reachability, CFRunLoopGetMain(), kCFRunLoopDefaultMode); 204 205 startDownload(); 206 207 SCNetworkReachabilityScheduleWithRunLoop(g_reachability, CFRunLoopGetMain(), kCFRunLoopDefaultMode); 208} 209 210static void 211startDownloadLoop(void) 212{ 213 CFRunLoopTimerRef timer; 214 CFRunLoopTimerContext ctx = { 0, NULL, NULL, NULL, NULL }; 215 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); 216 217 timer = CFRunLoopTimerCreate(kCFAllocatorDefault, 218 now + 0.1, 219 7.0, 220 0, 221 0, 222 downloadTimerFired, 223 &ctx); 224 225 CFRunLoopAddTimer(CFRunLoopGetMain(), timer, kCFRunLoopCommonModes); 226} 227 228static void * 229reachabilityLoop(void *arg) 230{ 231 while (1) { 232 SCNetworkReachabilityFlags flags; 233 SCNetworkReachabilityGetFlags(g_reachability, &flags); 234 235 printReachabilityFlags("thread", flags); 236 237 sleep(1); 238 } 239 240 return NULL; 241} 242 243static void 244startReachabilityThread(void) 245{ 246 pthread_attr_t tattr; 247 pthread_t th; 248 249 pthread_attr_init(&tattr); 250 pthread_create(&th, &tattr, reachabilityLoop, NULL); 251 pthread_attr_destroy(&tattr); 252} 253 254static void 255addressResolutionCallback(CFHostRef theHost, CFHostInfoType typeInfo, const CFStreamError *error, void *info) 256{ 257 g_reachability = createReachabilityWithCFHost(theHost); 258 259 if (g_reachability != NULL) { 260 startDownloadLoop(); 261 startReachabilityThread(); 262 } 263 264 CFRelease(theHost); 265} 266 267static void 268startAddressResolution(Boolean resolve) 269{ 270 CFStringRef hostStr = CFURLCopyHostName(g_url); 271 CFHostRef cfhost = CFHostCreateWithName(kCFAllocatorDefault, hostStr); 272 CFHostClientContext ctx = { 0, NULL, NULL, NULL, NULL }; 273 CFStreamError err; 274 275 if (resolve) { 276 CFHostSetClient(cfhost, addressResolutionCallback, &ctx); 277 CFHostScheduleWithRunLoop(cfhost, CFRunLoopGetMain(), kCFRunLoopDefaultMode); 278 CFHostStartInfoResolution(cfhost, kCFHostAddresses, &err); 279 } else { 280 addressResolutionCallback(cfhost, kCFHostNames, NULL, NULL); 281 } 282 283 CFRelease(hostStr); 284} 285 286int 287main(int argc, char *argv[]) 288{ 289 CFStringRef urlStr; 290 Boolean resolve = TRUE; 291 292 if (argc < 2) { 293 fprintf(stderr, "usage: %s <url> [-byname]", argv[0]); 294 return 1; 295 } 296 297 urlStr = CFStringCreateWithCString(kCFAllocatorDefault, argv[1], kCFStringEncodingASCII); 298 g_url = CFURLCreateWithString(kCFAllocatorDefault, urlStr, NULL); 299 300 CFRelease(urlStr); 301 302 if (argc > 2 && !strcmp(argv[2], "-byname")) { 303 resolve = FALSE; 304 } 305 306 startAddressResolution(resolve); 307 308 CFRunLoopRun(); 309 310 CFRelease(g_url); 311 312 SCNetworkReachabilityUnscheduleFromRunLoop(g_reachability, CFRunLoopGetMain(), kCFRunLoopDefaultMode); 313 CFRelease(g_reachability); 314 315 return 0; 316} 317