ReachabilityTester.c [plain text]
#include <stdio.h>
#include <pthread.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CFNetwork/CFNetwork.h>
#include <SystemConfiguration/SystemConfiguration.h>
static SCNetworkReachabilityRef g_reachability = NULL;
static CFURLRef g_url = NULL;
static CFReadStreamRef g_rstream = NULL;
static char *
string2CString(CFStringRef str)
{
UInt8 *buffer;
CFIndex clen;
CFRange r = CFRangeMake(0, CFStringGetLength(str));
if (CFStringGetBytes(str, r, kCFStringEncodingASCII, 0, false, NULL, 0, &clen) > 0) {
buffer = (UInt8 *)CFAllocatorAllocate(kCFAllocatorDefault, (clen + 1) * sizeof(UInt8), 0);
if (buffer != NULL) {
if (CFStringGetBytes(str, r, kCFStringEncodingASCII, 0, false, buffer, clen, NULL)) {
buffer[clen] = '\0';
return (char *)buffer;
}
CFAllocatorDeallocate(kCFAllocatorDefault, buffer);
}
}
return NULL;
}
static void
printReachabilityFlags(const char *source, SCNetworkReachabilityFlags flags)
{
printf("[%s] Reachability flags (%x):\n", source, flags);
if (flags & kSCNetworkReachabilityFlagsTransientConnection) {
printf("[%s] transient\n", source);
}
if (flags & kSCNetworkReachabilityFlagsReachable) {
printf("[%s] reachable\n", source);
}
if (flags & kSCNetworkReachabilityFlagsConnectionRequired) {
printf("[%s] connection required\n", source);
}
if (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) {
printf("[%s] connection on traffic\n", source);
}
if (flags & kSCNetworkReachabilityFlagsInterventionRequired) {
printf("[%s] intervention required\n", source);
}
if (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) {
printf("[%s] connection on demand\n", source);
}
if (flags & kSCNetworkReachabilityFlagsIsLocalAddress) {
printf("[%s] local address\n", source);
}
if (flags & kSCNetworkReachabilityFlagsIsDirect) {
printf("[%s] direct\n", source);
}
#if TARGET_OS_IPHONE
if (flags & kSCNetworkReachabilityFlagsIsWWAN) {
printf("[%s] wwan\n", source);
}
#endif
}
static void
handleReachabilityUpdate(
SCNetworkReachabilityRef target,
SCNetworkReachabilityFlags flags,
void *info)
{
printReachabilityFlags("RunLoop", flags);
}
static SCNetworkReachabilityRef
createReachabilityWithCFHost(CFHostRef theHost)
{
SCNetworkReachabilityRef reachRef = NULL;
Boolean resolved = FALSE;
CFArrayRef addrs = CFHostGetAddressing(theHost, &resolved);
if (resolved && addrs != NULL && CFArrayGetCount(addrs) > 0) {
CFDataRef addr = (CFDataRef)CFArrayGetValueAtIndex(addrs, 0);
reachRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (struct sockaddr *)CFDataGetBytePtr(addr));
} else {
CFArrayRef names = CFHostGetNames(theHost, NULL);
if (names != NULL && CFArrayGetCount(names) > 0) {
CFStringRef host = (CFStringRef)CFArrayGetValueAtIndex(names, 0);
char *chost = string2CString(host);
reachRef = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, chost);
CFAllocatorDeallocate(kCFAllocatorDefault, chost);
}
}
if (reachRef != NULL) {
SCNetworkReachabilityContext reach_ctx = { 0, NULL, NULL, NULL, NULL };
SCNetworkReachabilitySetCallback(reachRef, handleReachabilityUpdate,
&reach_ctx);
CFShow(reachRef);
} else {
fprintf(stderr, "Failed to create a reachability object\n");
}
return reachRef;
}
static void
handleDownload(CFReadStreamRef rstream, CFStreamEventType eventType, void *info)
{
Boolean done = FALSE;
if (eventType == kCFStreamEventHasBytesAvailable) {
UInt8 buffer[1024];
while (CFReadStreamHasBytesAvailable(rstream)) {
CFIndex count = CFReadStreamRead(rstream, buffer, sizeof(buffer));
if (count == 0) {
done = TRUE;
}
}
} else if (eventType == kCFStreamEventEndEncountered) {
printf("Download completed\n");
done = TRUE;
} else if (eventType == kCFStreamEventErrorOccurred) {
printf("Download error\n");
done = TRUE;
} else {
printf("Got stream event: %lu\n", eventType);
}
if (!done) {
return;
}
CFReadStreamUnscheduleFromRunLoop(rstream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
CFReadStreamClose(rstream);
CFRelease(rstream);
g_rstream = NULL;
}
static void
startDownload(void)
{
CFHTTPMessageRef request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("GET"), g_url, kCFHTTPVersion1_1);
CFReadStreamRef rstream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, request);
CFStreamClientContext ctx = { 0, NULL, NULL, NULL, NULL };
printf("Starting download\n");
CFReadStreamSetClient(rstream,
kCFStreamEventEndEncountered | kCFStreamEventErrorOccurred | kCFStreamEventHasBytesAvailable,
handleDownload,
&ctx);
CFReadStreamScheduleWithRunLoop(rstream, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
CFReadStreamOpen(rstream);
g_rstream = rstream;
CFRelease(request);
}
static void
downloadTimerFired(CFRunLoopTimerRef timer, void *info)
{
if (g_rstream != NULL) {
handleDownload(g_rstream, kCFStreamEventErrorOccurred, NULL);
}
SCNetworkReachabilityUnscheduleFromRunLoop(g_reachability, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
startDownload();
SCNetworkReachabilityScheduleWithRunLoop(g_reachability, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
}
static void
startDownloadLoop(void)
{
CFRunLoopTimerRef timer;
CFRunLoopTimerContext ctx = { 0, NULL, NULL, NULL, NULL };
CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
timer = CFRunLoopTimerCreate(kCFAllocatorDefault,
now + 0.1,
7.0,
0,
0,
downloadTimerFired,
&ctx);
CFRunLoopAddTimer(CFRunLoopGetMain(), timer, kCFRunLoopCommonModes);
}
static void *
reachabilityLoop(void *arg)
{
while (1) {
SCNetworkReachabilityFlags flags;
SCNetworkReachabilityGetFlags(g_reachability, &flags);
printReachabilityFlags("thread", flags);
sleep(1);
}
return NULL;
}
static void
startReachabilityThread(void)
{
pthread_attr_t tattr;
pthread_t th;
pthread_attr_init(&tattr);
pthread_create(&th, &tattr, reachabilityLoop, NULL);
pthread_attr_destroy(&tattr);
}
static void
addressResolutionCallback(CFHostRef theHost, CFHostInfoType typeInfo, const CFStreamError *error, void *info)
{
g_reachability = createReachabilityWithCFHost(theHost);
if (g_reachability != NULL) {
startDownloadLoop();
startReachabilityThread();
}
CFRelease(theHost);
}
static void
startAddressResolution(Boolean resolve)
{
CFStringRef hostStr = CFURLCopyHostName(g_url);
CFHostRef cfhost = CFHostCreateWithName(kCFAllocatorDefault, hostStr);
CFHostClientContext ctx = { 0, NULL, NULL, NULL, NULL };
CFStreamError err;
if (resolve) {
CFHostSetClient(cfhost, addressResolutionCallback, &ctx);
CFHostScheduleWithRunLoop(cfhost, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
CFHostStartInfoResolution(cfhost, kCFHostAddresses, &err);
} else {
addressResolutionCallback(cfhost, kCFHostNames, NULL, NULL);
}
CFRelease(hostStr);
}
int
main(int argc, char *argv[])
{
CFStringRef urlStr;
Boolean resolve = TRUE;
if (argc < 2) {
fprintf(stderr, "usage: %s <url> [-byname]", argv[0]);
return 1;
}
urlStr = CFStringCreateWithCString(kCFAllocatorDefault, argv[1], kCFStringEncodingASCII);
g_url = CFURLCreateWithString(kCFAllocatorDefault, urlStr, NULL);
CFRelease(urlStr);
if (argc > 2 && !strcmp(argv[2], "-byname")) {
resolve = FALSE;
}
startAddressResolution(resolve);
CFRunLoopRun();
CFRelease(g_url);
SCNetworkReachabilityUnscheduleFromRunLoop(g_reachability, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
CFRelease(g_reachability);
return 0;
}