CFNetDiagnostics.c [plain text]
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include "CFNetworkInternal.h"
#include <CFNetwork/CFNetDiagnostics.h>
#include <CFNetwork/CFNetDiagnosticsPriv.h>
#include <mach/mach.h>
#include <servers/bootstrap.h>
#include <servers/bootstrap_defs.h>
#include "CFNetDiagnosticsProtocol.h"
#include "CFNetDiagnosticsInternal.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <mach/mach_time.h>
#include <stdio.h>
extern
int _CFNetDiagnosticPing(CFStringRef HostToPing, const int NumberOfPacketsToSend, const int PingTimeoutInSeconds);
const char * CFNetDiagnosticNotifyKey = "com.apple.NetworkDiagnostics.notification";
const CFStringRef CFNetDiagnosticProtocolHTTP = CFSTR("http");
const CFStringRef CFNetDiagnosticProtocolFTP = CFSTR("ftp");
const CFStringRef CFNetDiagnosticProtocolSMTP = CFSTR("smtp");
const CFStringRef CFNetDiagnosticProtocolIMAP = CFSTR("imap");
const CFStringRef CFNetDiagnosticProtocolOSCAR = CFSTR("oscar");
const CFStringRef CFNetDiagnosticProtocolUnknown = CFSTR("unknown");
#if 0
static
void _CFNetDiagnosticsPrintObject(CFTypeRef object) {
char buffer[32768];
Boolean converted;
CFStringRef desc;
desc = CFCopyDescription(object);
if(desc) {
converted = CFStringGetCString(desc, buffer, 32768, kCFStringEncodingASCII);
if(converted) {
printf("%s\n", buffer);
}
CFRelease(desc);
}
}
#endif
static
void _CFNetDiagnosticSetDictionaryKeyIfNotNull( CFStringRef key,
CFTypeRef value,
CFMutableDictionaryRef dict) {
if(key != NULL && value != NULL) {
CFDictionaryAddValue(dict, key, value);
}
}
static
void _CFNetDiagnosticSetDictionaryKeyAndReleaseIfNotNull( CFStringRef key,
CFTypeRef value,
CFMutableDictionaryRef dict) {
if(key != NULL) {
if(value != NULL) {
CFDictionaryAddValue(dict, key, value);
CFRelease(value);
}
}
}
static
CFTypeRef _CFNetDiagnosticGetValueFromDictionaryAndRetain(CFDictionaryRef dict, CFStringRef key) {
CFTypeRef s;
s = (CFTypeRef)CFDictionaryGetValue(dict, key);
if(s != NULL) {
CFRetain(s);
}
return s;
}
static
CFDictionaryRef _CFNetDiagnosticsGetDataFromSCDSAndThowAwayGarbage(CFAllocatorRef allocator, SCDynamicStoreRef store,
CFStringRef(*SCFunc)(CFAllocatorRef, CFStringRef, CFStringRef, CFStringRef), CFStringRef arg1, CFStringRef arg2, CFStringRef arg3) {
CFStringRef pattern = NULL;
CFDictionaryRef dict = NULL;
pattern = SCFunc(allocator, arg1, arg2, arg3);
if(pattern) {
dict = SCDynamicStoreCopyValue( store, pattern );
CFRelease(pattern);
}
return dict;
}
CFNetDiagnosticRef CFNetDiagnosticCreateBasic( CFAllocatorRef allocator,
CFStringRef remoteHost,
CFStringRef protocol,
CFNumberRef port) {
CFMutableDictionaryRef retval = NULL;
retval = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if(retval != NULL) {
_CFNetDiagnosticSetDictionaryKeyIfNotNull(_CFNetDiagnosticNameKey, CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), kCFBundleNameKey), retval);
_CFNetDiagnosticSetDictionaryKeyIfNotNull(_CFNetDiagnosticBundleKey, CFBundleGetIdentifier( CFBundleGetMainBundle() ), retval);
_CFNetDiagnosticSetDictionaryKeyIfNotNull(_CFNetDiagnosticRemoteHostKey, remoteHost, retval);
_CFNetDiagnosticSetDictionaryKeyIfNotNull(_CFNetDiagnosticProtocolKey, protocol, retval);
_CFNetDiagnosticSetDictionaryKeyIfNotNull(_CFNetDiagnosticPortKey, port, retval);
_CFNetDiagnosticSetDictionaryKeyIfNotNull(_CFNetDiagnosticMethodKey, CFSTR("CFNetDiagnosticCreateBasic"), retval);
}
return (CFNetDiagnosticRef)retval;
}
CFNetDiagnosticRef CFNetDiagnosticCreateWithURL(CFAllocatorRef allocator, CFURLRef url) {
CFMutableDictionaryRef retval;
SInt32 port = 0;
retval = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if(retval != NULL && CFURLCanBeDecomposed(url)) {
port = CFURLGetPortNumber(url);
_CFNetDiagnosticSetDictionaryKeyIfNotNull(_CFNetDiagnosticNameKey, CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), kCFBundleNameKey), retval);
_CFNetDiagnosticSetDictionaryKeyIfNotNull(_CFNetDiagnosticBundleKey, CFBundleGetIdentifier( CFBundleGetMainBundle() ), retval);
_CFNetDiagnosticSetDictionaryKeyAndReleaseIfNotNull(_CFNetDiagnosticRemoteHostKey, CFURLCopyHostName(url), retval);
_CFNetDiagnosticSetDictionaryKeyAndReleaseIfNotNull(_CFNetDiagnosticProtocolKey, CFURLCopyScheme(url), retval);
_CFNetDiagnosticSetDictionaryKeyAndReleaseIfNotNull(_CFNetDiagnosticPortKey, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &port), retval);
_CFNetDiagnosticSetDictionaryKeyIfNotNull(_CFNetDiagnosticMethodKey, CFSTR("CFNetDiagnosticCreateWithURL"), retval);
}
return (CFNetDiagnosticRef)retval;
}
CFNetDiagnosticRef CFNetDiagnosticCreateWithStreams(CFAllocatorRef allocator, CFReadStreamRef readStream, CFWriteStreamRef writeStream) {
CFMutableDictionaryRef retval;
#if 0
CFArrayRef hostnames;
CFHostRef host;
#endif
retval = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if(retval != NULL) {
#if 0
host = (CFHostRef)CFReadStreamCopyProperty(readStream, kCFStreamPropertySocketRemoteHost);
if(host) {
hostnames = CFHostGetAddressing(host, NULL);
_CFNetDiagnosticSetDictionaryKeyIfNotNull(_CFNetDiagnosticRemoteHostKey, CFArrayGetValueAtIndex(hostnames, 0), retval);
CFRelease(host);
}
#endif
_CFNetDiagnosticSetDictionaryKeyIfNotNull(_CFNetDiagnosticNameKey, CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), kCFBundleNameKey), retval);
_CFNetDiagnosticSetDictionaryKeyIfNotNull(_CFNetDiagnosticBundleKey, CFBundleGetIdentifier( CFBundleGetMainBundle() ), retval);
_CFNetDiagnosticSetDictionaryKeyIfNotNull(_CFNetDiagnosticMethodKey, CFSTR("CFNetDiagnosticCreateWithStreams"), retval);
}
return (CFNetDiagnosticRef)retval;
}
void CFNetDiagnosticSetName(CFNetDiagnosticRef details, CFStringRef name) {
CFMutableDictionaryRef detailsDict = (CFMutableDictionaryRef)details;
_CFNetDiagnosticSetDictionaryKeyIfNotNull(_CFNetDiagnosticNameKey, name, detailsDict);
}
void CFNetDiagnosticSetProtocol(CFNetDiagnosticRef details, CFStringRef service) {
CFMutableDictionaryRef detailsDict = (CFMutableDictionaryRef)details;
_CFNetDiagnosticSetDictionaryKeyIfNotNull(_CFNetDiagnosticProtocolKey, service, detailsDict);
}
void CFNetDiagnosticSetServiceID(CFNetDiagnosticRef details, CFStringRef service) {
CFMutableDictionaryRef detailsDict = (CFMutableDictionaryRef)details;
_CFNetDiagnosticSetDictionaryKeyIfNotNull(_CFNetDiagnosticServiceIDKey, service, detailsDict);
}
CFNetDiagnosticStatus CFNetDiagnosticDiagnoseProblemInteractively(CFNetDiagnosticRef details) {
SInt32 retval = 0;
mach_port_t port = MACH_PORT_NULL;
CFDataRef msgData = NULL;
kern_return_t err;
CFWriteStreamRef stream = CFWriteStreamCreateWithAllocatedBuffers(kCFAllocatorDefault, kCFAllocatorDefault);
CFWriteStreamOpen(stream);
CFIndex len = CFPropertyListWriteToStream(details, stream, kCFPropertyListBinaryFormat_v1_0, NULL);
CFWriteStreamClose(stream);
if(len > 0) {
msgData = CFWriteStreamCopyProperty(stream, kCFStreamPropertyDataWritten);
}
CFRelease(stream);
if(msgData) {
err = bootstrap_look_up(bootstrap_port, *((name_t*)(&_CFNetDiagnosticMachPortName)), &port);
if (err == KERN_SUCCESS) {
err = _CFNetDiagnosticClient_passDescriptor( port,
_CFNetDiagnosticMachProtocolVersion,
(vm_address_t)CFDataGetBytePtr(msgData),
CFDataGetLength(msgData));
if (err == KERN_SUCCESS) {
}
}
CFRelease(msgData);
}
return (CFNetDiagnosticStatus)retval;
}
static
CFStringRef copyCurrentRouter(void) {
SCDynamicStoreRef store;
CFPropertyListRef propList;
CFStringRef retval = NULL;
store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("Network Diagnostics"), NULL, NULL);
if(store) {
propList = SCDynamicStoreCopyValue(store, CFSTR("State:/Network/Global/IPv4"));
if(propList) {
retval = CFDictionaryGetValue(propList, CFSTR("Router"));
if (retval) {
CFRetain(retval);
}
CFRelease(propList);
}
CFRelease(store);
}
return retval;
}
static
CFStringRef copyCurrentPrimaryService(void) {
SCDynamicStoreRef store;
CFPropertyListRef propList;
CFStringRef retval = NULL;
store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("Network Diagnostics"), NULL, NULL);
if(store) {
propList = SCDynamicStoreCopyValue(store, CFSTR("State:/Network/Global/IPv4"));
if(propList) {
retval = CFDictionaryGetValue(propList, CFSTR("PrimaryService"));
if (retval) {
CFRetain(retval);
}
CFRelease(propList);
}
CFRelease(store);
}
return retval;
}
static
CFArrayRef copyCurrentDNSServers(void) {
SCDynamicStoreRef store;
CFPropertyListRef propList;
CFStringRef scdsRegexp;
CFArrayRef retval = NULL;
CFStringRef serviceID;
serviceID = copyCurrentPrimaryService();
if(serviceID) {
scdsRegexp = SCDynamicStoreKeyCreateNetworkServiceEntity (
kCFAllocatorDefault,
kSCDynamicStoreDomainState,
serviceID,
kSCEntNetDNS);
if(scdsRegexp) {
store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("Network Diagnostics"), NULL, NULL);
if(store) {
propList = SCDynamicStoreCopyValue(store, scdsRegexp);
if(propList) {
retval = CFDictionaryGetValue(propList, CFSTR("ServerAddresses"));
if (retval) {
CFRetain(retval);
}
CFRelease(propList);
}
CFRelease(store);
}
CFRelease(scdsRegexp);
}
CFRelease(serviceID);
}
return retval;
}
CFNetDiagnosticStatus CFNetDiagnosticCopyNetworkStatusActively(CFNetDiagnosticRef details, CFNumberRef timeout, CFStringRef *description) {
uint64_t timestamp;
uint32_t timeout_value;
uint32_t running_timeout;
struct mach_timebase_info timebase;
CFNetDiagnosticStatus retval = kCFNetDiagnosticConnectionDown;
kern_return_t err;
double conversion_factor;
CFStringRef pingTarget;
CFArrayRef nameServers;
CFIndex nameServerCount;
CFIndex i;
bool nameServerResponded;
timestamp = mach_absolute_time();
err = mach_timebase_info(&timebase);
if(err == KERN_SUCCESS) {
conversion_factor = 1e-9 * (double)(timebase.numer) / (double)(timebase.denom);
retval = CFNetDiagnosticCopyNetworkStatusPassively(details, description);
if (retval != kCFNetDiagnosticConnectionUp) {
if (CFNumberGetValue(timeout, kCFNumberIntType, &timeout_value)) {
running_timeout = timeout_value - conversion_factor * (mach_absolute_time() - timestamp);
pingTarget = copyCurrentRouter();
if (pingTarget) {
if(_CFNetDiagnosticPing(pingTarget, 1, running_timeout)) {
CFRelease(pingTarget);
retval = kCFNetDiagnosticConnectionDown;
if (description) {
*description = CFCopyLocalizedStringFromTableInBundle( CFSTR("ROUTER_DOWN"),
NULL,
CFBundleGetBundleWithIdentifier(CFSTR("com.apple.CFNetwork")),
"This computer's router is not responding.");
}
} else {
CFRelease(pingTarget);
nameServers = copyCurrentDNSServers();
if (nameServers) {
nameServerCount = CFArrayGetCount(nameServers);
running_timeout = ((timeout_value - conversion_factor * (mach_absolute_time() - timestamp)) / nameServerCount);
nameServerResponded = false;
for (i=0; i < nameServerCount; i++) {
pingTarget = CFArrayGetValueAtIndex(nameServers, i);
if (!nameServerResponded) {
if(!_CFNetDiagnosticPing(pingTarget, 1, running_timeout)) {
nameServerResponded = true;
}
}
}
CFRelease(nameServers);
if (!nameServerResponded) {
retval = kCFNetDiagnosticConnectionDown;
if (description) {
*description = CFCopyLocalizedStringFromTableInBundle( CFSTR("NAMESERVER_DOWN"),
NULL,
CFBundleGetBundleWithIdentifier(CFSTR("com.apple.CFNetwork")),
"This computer's DNS server is not responding.");
}
} else {
running_timeout = timeout_value - conversion_factor * (mach_absolute_time() - timestamp);
pingTarget = CFDictionaryGetValue((CFDictionaryRef)details, _CFNetDiagnosticRemoteHostKey);
if(pingTarget) {
if (_CFNetDiagnosticPing(pingTarget, 1, running_timeout)) {
retval = kCFNetDiagnosticConnectionDown;
if (description) {
*description = CFCopyLocalizedStringFromTableInBundle( CFSTR("SERVER_DOWN"),
NULL,
CFBundleGetBundleWithIdentifier(CFSTR("com.apple.CFNetwork")),
"The server this computer is attempting to connect to is not responding.");
}
} else {
retval = kCFNetDiagnosticConnectionUp;
if (description) {
*description = CFCopyLocalizedStringFromTableInBundle( CFSTR("SERVER_UP"),
NULL,
CFBundleGetBundleWithIdentifier(CFSTR("com.apple.CFNetwork")),
"This computer's Internet connection appears ot be online.");
}
}
}
}
}
}
}
}
}
}
return retval;
}
static
Boolean _CFNetDiagnosticIsLinkLocal (CFStringRef s)
{
char buffer[16];
Boolean converted;
Boolean retval = 0;
uint32_t addr;
converted = CFStringGetCString(s, buffer, 16, kCFStringEncodingASCII);
if(converted) {
addr = inet_addr(buffer);
if(addr == INADDR_NONE) {
retval = 0;
} else {
retval = IN_LINKLOCAL(ntohl(addr));
}
}
return retval;
}
static
CFNetDiagnosticStatus _CFNetDiagnosticCopyNetworkStatusPassivelyInterfaceSpecific(SCDynamicStoreRef store, CFStringRef serviceID, CFStringRef *description) {
CFDictionaryRef dict = NULL;
CFNetDiagnosticStatus retval;
CFStringRef device = NULL;
CFArrayRef addresses = NULL;
CFTypeRef address = NULL;
Boolean isLinkActive = 0;
Boolean isConnected = 0;
Boolean isLinkLocal = 0;
dict = _CFNetDiagnosticsGetDataFromSCDSAndThowAwayGarbage(NULL, store, SCDynamicStoreKeyCreateNetworkServiceEntity,
kSCDynamicStoreDomainSetup, serviceID, kSCEntNetInterface);
if(dict) {
device = (CFStringRef)_CFNetDiagnosticGetValueFromDictionaryAndRetain(dict, kSCPropNetInterfaceDeviceName);
CFRelease(dict);
}
if(device) {
dict = _CFNetDiagnosticsGetDataFromSCDSAndThowAwayGarbage(NULL, store, SCDynamicStoreKeyCreateNetworkInterfaceEntity,
kSCDynamicStoreDomainState, device, kSCEntNetLink);
if(dict) {
CFBooleanRef linkActive = CFDictionaryGetValue(dict, kSCPropNetLinkActive);
CFBooleanRef linkDetaching = CFDictionaryGetValue(dict, kSCPropNetLinkDetaching);
if (linkActive) {
isLinkActive = CFBooleanGetValue(linkActive);
} else if (linkDetaching) {
isLinkActive = !CFBooleanGetValue(linkDetaching);
} else {
isLinkActive = true;
}
CFRelease( (CFDictionaryRef) dict );
}
CFRelease(device);
}
dict = _CFNetDiagnosticsGetDataFromSCDSAndThowAwayGarbage(NULL, store, SCDynamicStoreKeyCreateNetworkServiceEntity,
kSCDynamicStoreDomainState, serviceID, kSCEntNetIPv4);
if(dict) {
isConnected = 1;
addresses = CFDictionaryGetValue(dict, kSCPropNetIPv4Addresses);
if(CFArrayGetCount(addresses) > 0) {
address = CFArrayGetValueAtIndex(addresses, 0);
}
if(address) {
isLinkLocal = _CFNetDiagnosticIsLinkLocal(address);
}
CFRelease( (CFDictionaryRef) dict );
}
if(isLinkActive && isConnected) {
if(isLinkLocal) {
retval = kCFNetDiagnosticConnectionIndeterminate;
} else {
retval = kCFNetDiagnosticConnectionUp;
}
} else {
retval = kCFNetDiagnosticConnectionDown;
}
return retval;
}
CFNetDiagnosticStatus CFNetDiagnosticCopyNetworkStatusPassively(CFNetDiagnosticRef details, CFStringRef *description) {
CFMutableDictionaryRef detailsDict = (CFMutableDictionaryRef)details;
CFNetDiagnosticStatus retval = kCFNetDiagnosticConnectionIndeterminate;
CFStringRef serviceID;
SCDynamicStoreRef store;
store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("CFNetDiagnostics"), NULL, NULL);
if(store) {
serviceID = CFDictionaryGetValue(detailsDict, _CFNetDiagnosticServiceIDKey);
if(serviceID) {
retval = _CFNetDiagnosticCopyNetworkStatusPassivelyInterfaceSpecific(store, serviceID, description);
} else {
CFStringRef pattern = NULL;
CFDictionaryRef dict = NULL;
CFArrayRef serviceOrder = NULL;
CFIndex i, count;
CFNetDiagnosticStatus serviceState = kCFNetDiagnosticConnectionDown;
pattern = SCDynamicStoreKeyCreateNetworkGlobalEntity( NULL,
(CFStringRef) kSCDynamicStoreDomainSetup,
(CFStringRef) kSCEntNetIPv4 );
if(pattern) {
dict = SCDynamicStoreCopyValue( store, pattern );
CFRelease( pattern );
}
if(dict) {
serviceOrder = CFDictionaryGetValue(dict, CFSTR("ServiceOrder"));
CFRetain(serviceOrder);
CFRelease(dict);
}
if(serviceOrder) {
count = CFArrayGetCount(serviceOrder);
retval = kCFNetDiagnosticConnectionDown;
for ( i = 0; i < count; i++ ) {
serviceID = CFArrayGetValueAtIndex(serviceOrder, i);
serviceState = _CFNetDiagnosticCopyNetworkStatusPassivelyInterfaceSpecific(store, serviceID, description);
if(serviceState == kCFNetDiagnosticConnectionDown) {
retval = kCFNetDiagnosticConnectionDown;
if (description) {
*description = CFCopyLocalizedStringFromTableInBundle( CFSTR("CONNECTION_DOWN"),
NULL,
CFBundleGetBundleWithIdentifier(CFSTR("com.apple.CFNetwork")),
"This computer's Internet connect appears to be offline.");
}
} else if (serviceState == kCFNetDiagnosticConnectionIndeterminate) {
retval = kCFNetDiagnosticConnectionIndeterminate;
if (description) {
*description = CFCopyLocalizedStringFromTableInBundle( CFSTR("CONNECTION_INDETERMINATE"),
NULL,
CFBundleGetBundleWithIdentifier(CFSTR("com.apple.CFNetwork")),
"This computer's Internet may be offline.");
}
} else if (serviceState == kCFNetDiagnosticConnectionUp) {
retval = kCFNetDiagnosticConnectionUp;
if (description) {
*description = CFCopyLocalizedStringFromTableInBundle( CFSTR("CONNECTION_UP"),
NULL,
CFBundleGetBundleWithIdentifier(CFSTR("com.apple.CFNetwork")),
"This computer's Internet may be online.");
}
break;
} else {
}
}
CFRelease(serviceOrder);
}
}
CFRelease(store);
}
return retval;
}