#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCPrivate.h>
#include <SystemConfiguration/SCValidation.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <netdb.h>
#include <resolv.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include "ppp.h"
static int
inet_atonCF(CFStringRef cfStr, struct in_addr *addr)
{
char cStr[sizeof("255.255.255.255")];
if (!CFStringGetCString(cfStr, cStr, sizeof(cStr), kCFStringEncodingMacRoman)) {
return 0;
}
return inet_aton(cStr, addr);
}
static CFStringRef
parse_component(CFStringRef key, CFStringRef prefix)
{
CFMutableStringRef comp;
CFRange range;
if (CFStringHasPrefix(key, prefix) == FALSE) {
return NULL;
}
comp = CFStringCreateMutableCopy(NULL, 0, key);
CFStringDelete(comp, CFRangeMake(0, CFStringGetLength(prefix)));
range = CFStringFind(comp, CFSTR("/"), 0);
if (range.location == kCFNotFound) {
return comp;
}
range.length = CFStringGetLength(comp) - range.location;
CFStringDelete(comp, range);
return comp;
}
typedef struct {
CFMutableDictionaryRef aDict;
CFStringRef aPrefix;
CFMutableDictionaryRef cDict;
CFStringRef cPrefix;
CFMutableDictionaryRef iDict;
CFStringRef iPrefix;
CFMutableArrayRef order;
} initContext, *initContextRef;
static void
collectInfo(const void *key, const void *value, void *context)
{
initContextRef info = (initContextRef)context;
CFStringRef interface;
CFStringRef interfaceKey;
CFStringRef service;
CFStringRef serviceKey;
if (!isA_CFString(key) || !isA_CFDictionary(value)) {
return;
}
service = parse_component((CFStringRef)key, info->cPrefix);
if (service) {
serviceKey = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
service,
kSCEntNetIPv4);
if (CFEqual((CFStringRef)key, serviceKey)) {
CFMutableDictionaryRef dict;
dict = CFDictionaryCreateMutableCopy(NULL, 0, (CFDictionaryRef)value);
CFDictionaryAddValue(info->cDict, service, dict);
CFRelease(dict);
}
CFRelease(serviceKey);
if (!CFArrayContainsValue(info->order, CFRangeMake(0, CFArrayGetCount(info->order)), service)) {
CFArrayAppendValue(info->order, service);
}
CFRelease(service);
return;
}
service = parse_component((CFStringRef)key, info->aPrefix);
if (service) {
serviceKey = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainState,
service,
kSCEntNetIPv4);
if (CFEqual((CFStringRef)key, serviceKey)) {
CFMutableDictionaryRef dict;
dict = CFDictionaryCreateMutableCopy(NULL, 0, (CFDictionaryRef)value);
CFDictionaryAddValue(info->aDict, service, dict);
CFRelease(dict);
}
CFRelease(serviceKey);
if (!CFArrayContainsValue(info->order, CFRangeMake(0, CFArrayGetCount(info->order)), service)) {
CFArrayAppendValue(info->order, service);
}
CFRelease(service);
return;
}
interface = parse_component((CFStringRef)key, info->iPrefix);
if (interface) {
interfaceKey = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainState,
interface,
kSCEntNetIPv4);
if (CFEqual((CFStringRef)key, interfaceKey)) {
CFMutableDictionaryRef dict;
dict = CFDictionaryCreateMutableCopy(NULL, 0, (CFDictionaryRef)value);
CFDictionaryAddValue(info->iDict, interface, dict);
CFRelease(dict);
}
CFRelease(interfaceKey);
CFRelease(interface);
return;
}
return;
}
static void
collectExtraInfo(const void *key, const void *value, void *context)
{
CFStringRef interfaceKey;
initContextRef info = (initContextRef)context;
CFMutableDictionaryRef dict;
Boolean match;
CFStringRef pppKey;
CFStringRef service;
if (!isA_CFString(key) || !isA_CFDictionary(value)) {
return;
}
service = parse_component((CFStringRef)key, info->cPrefix);
if (!service) {
return;
}
dict = (CFMutableDictionaryRef)CFDictionaryGetValue(info->cDict, service);
if (!dict) {
goto done;
}
interfaceKey = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
service,
kSCEntNetInterface);
match = CFEqual((CFStringRef)key, interfaceKey);
CFRelease(interfaceKey);
if (match) {
CFStringRef interface;
interface = CFDictionaryGetValue((CFDictionaryRef)value,
kSCPropNetInterfaceType);
if (isA_CFString(interface)) {
CFDictionaryAddValue(dict, kSCPropNetInterfaceType, interface);
CFDictionarySetValue(info->cDict, service, dict);
}
goto done;
}
pppKey = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
service,
kSCEntNetPPP);
match = CFEqual((CFStringRef)key, pppKey);
CFRelease(pppKey);
if (match) {
CFNumberRef dialOnDemand;
dialOnDemand = CFDictionaryGetValue((CFDictionaryRef)value,
kSCPropNetPPPDialOnDemand);
if (isA_CFNumber(dialOnDemand)) {
CFDictionaryAddValue(dict, kSCPropNetPPPDialOnDemand, dialOnDemand);
CFDictionarySetValue(info->cDict, service, dict);
}
goto done;
}
done :
CFRelease(service);
return;
}
static void
removeKnownAddresses(const void *key, const void *value, void *context)
{
CFMutableDictionaryRef ifDict;
CFStringRef ifName;
CFMutableDictionaryRef interfaces = (CFMutableDictionaryRef)context;
CFMutableDictionaryRef serviceDict = (CFMutableDictionaryRef)value;
Boolean updated = FALSE;
CFIndex i;
CFArrayRef iAddrs;
CFArrayRef iDests;
CFArrayRef iMasks;
CFIndex n;
CFMutableArrayRef nAddrs = NULL;
CFMutableArrayRef nDests = NULL;
CFMutableArrayRef nMasks = NULL;
CFIndex s;
CFArrayRef sAddrs;
CFArrayRef sDests;
CFArrayRef sMasks;
ifName = CFDictionaryGetValue(serviceDict, kSCPropInterfaceName);
if (!ifName) {
return;
}
ifDict = (CFMutableDictionaryRef)CFDictionaryGetValue(interfaces, ifName);
if (!ifDict) {
return;
}
sAddrs = isA_CFArray(CFDictionaryGetValue(serviceDict,
kSCPropNetIPv4Addresses));
sDests = isA_CFArray(CFDictionaryGetValue(serviceDict,
kSCPropNetIPv4DestAddresses));
sMasks = isA_CFArray(CFDictionaryGetValue(serviceDict,
kSCPropNetIPv4SubnetMasks));
if (!sAddrs || ((n = CFArrayGetCount(sAddrs)) == 0)) {
return;
}
if (((sMasks == NULL) && (sDests == NULL)) ||
((sMasks != NULL) && (sDests != NULL))) {
return;
}
if (sMasks && (n != CFArrayGetCount(sMasks))) {
return;
}
if (sDests && (n != CFArrayGetCount(sDests))) {
return;
}
iAddrs = isA_CFArray(CFDictionaryGetValue(ifDict,
kSCPropNetIPv4Addresses));
iDests = isA_CFArray(CFDictionaryGetValue(ifDict,
kSCPropNetIPv4DestAddresses));
iMasks = isA_CFArray(CFDictionaryGetValue(ifDict,
kSCPropNetIPv4SubnetMasks));
if (((iMasks == NULL) && (iDests == NULL)) ||
((iMasks != NULL) && (iDests != NULL))) {
return;
}
if (!iAddrs || ((i = CFArrayGetCount(iAddrs)) == 0)) {
return;
}
if (iMasks && (i != CFArrayGetCount(iMasks))) {
return;
}
if (iDests && (i != CFArrayGetCount(iDests))) {
return;
}
if (((sMasks == NULL) && (iMasks != NULL)) ||
((sDests == NULL) && (iDests != NULL))) {
return;
}
nAddrs = CFArrayCreateMutableCopy(NULL, 0, iAddrs);
if (iMasks) nMasks = CFArrayCreateMutableCopy(NULL, 0, iMasks);
if (iDests) nDests = CFArrayCreateMutableCopy(NULL, 0, iDests);
for (s=0; s<n; s++) {
i = CFArrayGetCount(nAddrs);
while (--i >= 0) {
if (sMasks &&
CFEqual(CFArrayGetValueAtIndex(sAddrs, s),
CFArrayGetValueAtIndex(nAddrs, i)) &&
CFEqual(CFArrayGetValueAtIndex(sMasks, s),
CFArrayGetValueAtIndex(nMasks, i))
) {
CFArrayRemoveValueAtIndex(nAddrs, i);
CFArrayRemoveValueAtIndex(nMasks, i);
updated = TRUE;
} else if (sDests &&
CFEqual(CFArrayGetValueAtIndex(sAddrs, s),
CFArrayGetValueAtIndex(nAddrs, i)) &&
CFEqual(CFArrayGetValueAtIndex(sDests, s),
CFArrayGetValueAtIndex(nDests, i))
) {
CFArrayRemoveValueAtIndex(nAddrs, i);
CFArrayRemoveValueAtIndex(nDests, i);
updated = TRUE;
}
}
}
if (updated) {
if (nAddrs) {
CFDictionarySetValue(ifDict,
kSCPropNetIPv4Addresses,
nAddrs);
}
if (nMasks) {
CFDictionarySetValue(ifDict,
kSCPropNetIPv4SubnetMasks,
nMasks);
} else {
CFDictionarySetValue(ifDict,
kSCPropNetIPv4DestAddresses,
nDests);
}
CFDictionarySetValue(interfaces, ifName, ifDict);
}
CFRelease(nAddrs);
if (nMasks) CFRelease(nMasks);
if (nDests) CFRelease(nDests);
return;
}
static void
addUnknownService(const void *key, const void *value, void *context)
{
CFArrayRef addrs;
CFMutableDictionaryRef ifDict = (CFMutableDictionaryRef)value;
initContextRef info = (initContextRef)context;
CFStringRef service;
CFUUIDRef uuid;
addrs = CFDictionaryGetValue(ifDict, kSCPropNetIPv4Addresses);
if (!addrs || (CFArrayGetCount(addrs) == 0)) {
return;
}
CFDictionaryAddValue(ifDict, kSCPropInterfaceName, (CFStringRef)key);
uuid = CFUUIDCreate(NULL);
service = CFUUIDCreateString(NULL, uuid);
CFDictionaryAddValue(info->aDict, service, ifDict);
CFArrayAppendValue(info->order, service);
CFRelease(service);
CFRelease(uuid);
return;
}
static Boolean
getAddresses(CFDictionaryRef iDict,
CFIndex *nAddrs,
CFArrayRef *addrs,
CFArrayRef *masks,
CFArrayRef *dests)
{
*addrs = isA_CFArray(CFDictionaryGetValue(iDict,
kSCPropNetIPv4Addresses));
*masks = isA_CFArray(CFDictionaryGetValue(iDict,
kSCPropNetIPv4SubnetMasks));
*dests = isA_CFArray(CFDictionaryGetValue(iDict,
kSCPropNetIPv4DestAddresses));
if ((*addrs == NULL) ||
((*nAddrs = CFArrayGetCount(*addrs)) == 0)) {
_SCErrorSet(kSCStatusReachabilityUnknown);
return FALSE;
}
if (((*masks == NULL) && (*dests == NULL)) ||
((*masks != NULL) && (*dests != NULL))) {
_SCErrorSet(kSCStatusReachabilityUnknown);
return FALSE;
}
if (*masks && (*nAddrs != CFArrayGetCount(*masks))) {
_SCErrorSet(kSCStatusReachabilityUnknown);
return FALSE;
}
if (*dests && (*nAddrs != CFArrayGetCount(*dests))) {
_SCErrorSet(kSCStatusReachabilityUnknown);
return FALSE;
}
return TRUE;
}
static Boolean
checkAddress(SCDynamicStoreRef store,
const struct sockaddr *address,
const int addrlen,
CFDictionaryRef config,
CFDictionaryRef active,
CFArrayRef serviceOrder,
struct in_addr *defaultRoute,
SCNetworkConnectionFlags *flags)
{
CFIndex aCnt;
CFStringRef aType = NULL;
CFDictionaryRef cDict = NULL;
CFIndex i;
CFStringRef key = NULL;
int pppRef = -1;
int sc_status = kSCStatusReachabilityUnknown;
char *statusMessage = NULL;
if (!address || !flags) {
sc_status = kSCStatusInvalidArgument;
goto done;
}
*flags = 0;
if (address->sa_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in *)address;
SCLog(_sc_debug, LOG_INFO, CFSTR("checkAddress(%s)"), inet_ntoa(sin->sin_addr));
aCnt = CFArrayGetCount(serviceOrder);
for (i=0; i<aCnt; i++) {
CFDictionaryRef aDict;
CFArrayRef addrs;
CFArrayRef dests;
CFIndex j;
CFArrayRef masks;
CFIndex nAddrs = 0;
key = CFArrayGetValueAtIndex(serviceOrder, i);
aDict = CFDictionaryGetValue(active, key);
if (!aDict ||
!getAddresses(aDict, &nAddrs, &addrs, &masks, &dests)) {
continue;
}
for (j=0; j<nAddrs; j++) {
struct in_addr ifAddr;
if (inet_atonCF(CFArrayGetValueAtIndex(addrs, j),
&ifAddr) == 0) {
break;
}
if (masks) {
struct in_addr ifMask;
if (inet_atonCF(CFArrayGetValueAtIndex(masks, j),
&ifMask) == 0) {
break;
}
if ((ntohl(ifAddr.s_addr) & ntohl(ifMask.s_addr)) ==
(ntohl(sin->sin_addr.s_addr) & ntohl(ifMask.s_addr))) {
statusMessage = "isReachable (my subnet)";
*flags |= kSCNetworkFlagsReachable;
goto checkInterface;
}
} else {
struct in_addr destAddr;
if (inet_atonCF(CFArrayGetValueAtIndex(dests, j),
&destAddr) == 0) {
break;
}
if (ntohl(sin->sin_addr.s_addr) == ntohl(ifAddr.s_addr)) {
statusMessage = "isReachable (my local address)";
*flags |= kSCNetworkFlagsReachable;
goto checkInterface;
}
if (ntohl(sin->sin_addr.s_addr) == ntohl(destAddr.s_addr)) {
statusMessage = "isReachable (my remote address)";
*flags |= kSCNetworkFlagsReachable;
goto checkInterface;
}
}
}
}
for (i=0; i<aCnt; i++) {
CFDictionaryRef aDict;
CFArrayRef addrs;
CFArrayRef dests;
CFIndex j;
CFArrayRef masks;
CFIndex nAddrs = 0;
key = CFArrayGetValueAtIndex(serviceOrder, i);
aDict = CFDictionaryGetValue(active, key);
if (!sin->sin_addr.s_addr ||
!defaultRoute ||
!aDict ||
!getAddresses(aDict, &nAddrs, &addrs, &masks, &dests)) {
continue;
}
for (j=0; defaultRoute && j<nAddrs; j++) {
if (masks) {
struct in_addr ifAddr;
struct in_addr ifMask;
if (inet_atonCF(CFArrayGetValueAtIndex(addrs, j),
&ifAddr) == 0) {
break;
}
if (inet_atonCF(CFArrayGetValueAtIndex(masks, j),
&ifMask) == 0) {
break;
}
if ((ntohl(ifAddr.s_addr) & ntohl(ifMask.s_addr)) ==
(ntohl(defaultRoute->s_addr) & ntohl(ifMask.s_addr))) {
statusMessage = "isReachable via default route (my subnet)";
*flags |= kSCNetworkFlagsReachable;
goto checkInterface;
}
} else {
struct in_addr destAddr;
if (inet_atonCF(CFArrayGetValueAtIndex(dests, j),
&destAddr) == 0) {
break;
}
if (ntohl(destAddr.s_addr) == ntohl(defaultRoute->s_addr)) {
statusMessage = "isReachable via default route (my remote address)";
*flags |= kSCNetworkFlagsReachable;
goto checkInterface;
}
}
}
}
for (i=0; i<aCnt; i++) {
key = CFArrayGetValueAtIndex(serviceOrder, i);
if (CFDictionaryContainsKey(active, key)) {
continue;
}
cDict = CFDictionaryGetValue(config, key);
if (!cDict) {
continue;
}
aType = CFDictionaryGetValue(cDict, kSCPropNetInterfaceType);
if (!aType || !CFEqual(aType, kSCValNetInterfaceTypePPP)) {
sc_status = kSCStatusOK;
goto done;
}
statusMessage = "is configured w/dynamic addressing";
*flags |= kSCNetworkFlagsTransientConnection;
*flags |= kSCNetworkFlagsReachable;
*flags |= kSCNetworkFlagsConnectionRequired;
if (_sc_debug) {
SCLog(TRUE, LOG_INFO, CFSTR(" status = %s"), statusMessage);
SCLog(TRUE, LOG_INFO, CFSTR(" service id = %@"), key);
}
sc_status = kSCStatusOK;
goto done;
}
SCLog(_sc_debug, LOG_INFO, CFSTR(" cannot be reached"));
sc_status = kSCStatusOK;
goto done;
} else {
SCLog(_sc_verbose, LOG_ERR,
CFSTR("checkAddress(): unexpected address family %d"),
address->sa_family);
sc_status = kSCStatusInvalidArgument;
goto done;
}
goto done;
checkInterface :
if (_sc_debug) {
CFDictionaryRef aDict;
CFStringRef interface = NULL;
aDict = CFDictionaryGetValue(active, key);
if (aDict) {
interface = CFDictionaryGetValue(aDict, kSCPropInterfaceName);
}
SCLog(TRUE, LOG_INFO, CFSTR(" status = %s"), statusMessage);
SCLog(TRUE, LOG_INFO, CFSTR(" service id = %@"), key);
SCLog(TRUE, LOG_INFO, CFSTR(" device = %@"), interface ? interface : CFSTR("?"));
}
sc_status = kSCStatusOK;
{
CFNumberRef num;
CFDictionaryRef cDict;
cDict = CFDictionaryGetValue(config, key);
if (cDict) {
aType = CFDictionaryGetValue(cDict, kSCPropNetInterfaceType);
}
if (!aType || !CFEqual(aType, kSCValNetInterfaceTypePPP)) {
goto done;
}
num = CFDictionaryGetValue(cDict, kSCPropNetPPPDialOnDemand);
if (num) {
int dialOnDemand;
CFNumberGetValue(num, kCFNumberIntType, &dialOnDemand);
if (dialOnDemand != 0) {
*flags |= kSCNetworkFlagsConnectionAutomatic;
}
}
}
*flags |= kSCNetworkFlagsTransientConnection;
{
u_int32_t pppLink;
struct ppp_status *pppLinkStatus;
int pppStatus;
pppStatus = PPPInit(&pppRef);
if (pppStatus != 0) {
SCLog(_sc_debug, LOG_DEBUG, CFSTR(" PPPInit() failed: status=%d"), pppStatus);
sc_status = kSCStatusReachabilityUnknown;
goto done;
}
pppStatus = PPPGetLinkByServiceID(pppRef, key, &pppLink);
if (pppStatus != 0) {
SCLog(_sc_debug, LOG_DEBUG, CFSTR(" PPPGetLinkByServiceID() failed: status=%d"), pppStatus);
sc_status = kSCStatusReachabilityUnknown;
goto done;
}
pppStatus = PPPStatus(pppRef, pppLink, &pppLinkStatus);
if (pppStatus != 0) {
SCLog(_sc_debug, LOG_DEBUG, CFSTR(" PPPStatus() failed: status=%d"), pppStatus);
sc_status = kSCStatusReachabilityUnknown;
goto done;
}
#ifdef DEBUG
SCLog(_sc_debug, LOG_DEBUG, CFSTR(" PPP link status = %d"), pppLinkStatus->status);
#endif
switch (pppLinkStatus->status) {
case PPP_RUNNING :
break;
case PPP_IDLE :
SCLog(_sc_debug, LOG_INFO, CFSTR(" PPP link idle, dial-on-traffic to connect"));
*flags |= kSCNetworkFlagsReachable;
*flags |= kSCNetworkFlagsConnectionRequired;
sc_status = kSCStatusOK;
break;
default :
SCLog(_sc_debug, LOG_INFO, CFSTR(" PPP link, connection in progress"));
*flags |= kSCNetworkFlagsReachable;
*flags |= kSCNetworkFlagsConnectionRequired;
sc_status = kSCStatusOK;
break;
}
CFAllocatorDeallocate(NULL, pppLinkStatus);
}
goto done;
done :
if (pppRef != -1) (void) PPPDispose(pppRef);
if (sc_status != kSCStatusOK) {
_SCErrorSet(sc_status);
return FALSE;
}
return TRUE;
}
static void
_CheckReachabilityInit(SCDynamicStoreRef store,
CFDictionaryRef *config,
CFDictionaryRef *active,
CFArrayRef *serviceOrder,
struct in_addr **defaultRoute)
{
CFMutableDictionaryRef activeDict;
CFMutableDictionaryRef configDict;
initContext context;
CFDictionaryRef dict;
CFMutableDictionaryRef interfaces;
CFMutableArrayRef keys;
CFMutableArrayRef orderArray;
CFDictionaryRef orderDict;
CFStringRef orderKey;
CFStringRef pattern;
CFMutableArrayRef patterns;
CFStringRef routeKey;
CFDictionaryRef routeDict;
configDict = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
*config = configDict;
activeDict = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
*active = activeDict;
orderArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
*serviceOrder = orderArray;
*defaultRoute = NULL;
interfaces = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
orderKey = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCEntNetIPv4);
CFArrayAppendValue(keys, orderKey);
routeKey = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
kSCDynamicStoreDomainState,
kSCEntNetIPv4);
CFArrayAppendValue(keys, routeKey);
pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCCompAnyRegex,
kSCEntNetIPv4);
CFArrayAppendValue(patterns, pattern);
CFRelease(pattern);
pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCCompAnyRegex,
kSCEntNetInterface);
CFArrayAppendValue(patterns, pattern);
CFRelease(pattern);
pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCCompAnyRegex,
kSCEntNetPPP);
CFArrayAppendValue(patterns, pattern);
CFRelease(pattern);
pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainState,
kSCCompAnyRegex,
kSCEntNetIPv4);
CFArrayAppendValue(patterns, pattern);
CFRelease(pattern);
pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainState,
kSCCompAnyRegex,
kSCEntNetIPv4);
CFArrayAppendValue(patterns, pattern);
CFRelease(pattern);
dict = SCDynamicStoreCopyMultiple(store, keys, patterns);
CFRelease(keys);
CFRelease(patterns);
if (!dict) {
goto done;
}
orderDict = CFDictionaryGetValue(dict, orderKey);
if (isA_CFDictionary(orderDict)) {
CFArrayRef array;
array = (CFMutableArrayRef)CFDictionaryGetValue(orderDict, kSCPropNetServiceOrder);
if (isA_CFArray(array)) {
CFArrayAppendArray(orderArray,
array,
CFRangeMake(0, CFArrayGetCount(array)));
}
}
routeDict = CFDictionaryGetValue(dict, routeKey);
if (isA_CFDictionary(routeDict)) {
CFStringRef addr;
addr = CFDictionaryGetValue(routeDict, kSCPropNetIPv4Router);
if (isA_CFString(addr)) {
struct in_addr *route;
route = CFAllocatorAllocate(NULL, sizeof(struct in_addr), 0);
if (inet_atonCF(addr, route) == 0) {
CFAllocatorDeallocate(NULL, route);
route = NULL;
} else {
*defaultRoute = route;
}
}
}
context.cDict = configDict;
context.cPrefix = SCDynamicStoreKeyCreate(NULL,
CFSTR("%@/%@/%@/"),
kSCDynamicStoreDomainSetup,
kSCCompNetwork,
kSCCompService);
context.aDict = activeDict;
context.aPrefix = SCDynamicStoreKeyCreate(NULL,
CFSTR("%@/%@/%@/"),
kSCDynamicStoreDomainState,
kSCCompNetwork,
kSCCompService);
context.iDict = interfaces;
context.iPrefix = SCDynamicStoreKeyCreate(NULL,
CFSTR("%@/%@/%@/"),
kSCDynamicStoreDomainState,
kSCCompNetwork,
kSCCompInterface);
context.order = orderArray;
CFDictionaryApplyFunction(dict, collectInfo, &context);
CFDictionaryApplyFunction(dict, collectExtraInfo, &context);
CFDictionaryApplyFunction(activeDict, removeKnownAddresses, interfaces);
CFDictionaryApplyFunction(interfaces, addUnknownService, &context);
CFRelease(context.cPrefix);
CFRelease(context.aPrefix);
CFRelease(context.iPrefix);
CFRelease(dict);
done :
CFRelease(interfaces);
CFRelease(orderKey);
CFRelease(routeKey);
#ifdef DEBUG
SCLog(_sc_debug, LOG_NOTICE, CFSTR("config = %@"), *config);
SCLog(_sc_debug, LOG_NOTICE, CFSTR("active = %@"), *active);
SCLog(_sc_debug, LOG_NOTICE, CFSTR("serviceOrder = %@"), *serviceOrder);
SCLog(_sc_debug, LOG_NOTICE, CFSTR("defaultRoute = %s"), *defaultRoute?inet_ntoa(**defaultRoute):"None");
#endif
return;
}
static void
_CheckReachabilityFree(CFDictionaryRef config,
CFDictionaryRef active,
CFArrayRef serviceOrder,
struct in_addr *defaultRoute)
{
if (config) CFRelease(config);
if (active) CFRelease(active);
if (serviceOrder) CFRelease(serviceOrder);
if (defaultRoute) CFAllocatorDeallocate(NULL, defaultRoute);
return;
}
Boolean
SCNetworkCheckReachabilityByAddress(const struct sockaddr *address,
const int addrlen,
SCNetworkConnectionFlags *flags)
{
CFDictionaryRef active = NULL;
CFDictionaryRef config = NULL;
struct in_addr *defaultRoute = NULL;
Boolean ok;
CFArrayRef serviceOrder = NULL;
SCDynamicStoreRef store = NULL;
*flags = 0;
if (address->sa_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in *)address;
if (sin->sin_addr.s_addr == 0) {
SCLog(_sc_debug, LOG_INFO, CFSTR("checkAddress(0.0.0.0)"));
SCLog(_sc_debug, LOG_INFO, CFSTR(" status = isReachable (this host)"));
*flags |= kSCNetworkFlagsReachable;
return TRUE;
}
}
store = SCDynamicStoreCreate(NULL,
CFSTR("SCNetworkCheckReachabilityByAddress"),
NULL,
NULL);
if (!store) {
SCLog(_sc_verbose, LOG_INFO, CFSTR("SCDynamicStoreCreate() failed"));
return FALSE;
}
_CheckReachabilityInit(store, &config, &active, &serviceOrder, &defaultRoute);
ok = checkAddress(store,
address,
addrlen,
config,
active,
serviceOrder,
defaultRoute,
flags);
_CheckReachabilityFree(config, active, serviceOrder, defaultRoute);
CFRelease(store);
return ok;
}
static int
rankReachability(int flags)
{
int rank = 0;
if (flags & kSCNetworkFlagsReachable) rank = 2;
if (flags & kSCNetworkFlagsConnectionRequired) rank = 1;
return rank;
}
Boolean
SCNetworkCheckReachabilityByName(const char *nodename,
SCNetworkConnectionFlags *flags)
{
CFDictionaryRef active = NULL;
CFDictionaryRef config = NULL;
struct in_addr *defaultRoute = NULL;
struct hostent *h;
Boolean haveDNS = FALSE;
int i;
Boolean ok = TRUE;
struct addrinfo *res = NULL;
struct addrinfo *resP;
CFArrayRef serviceOrder = NULL;
SCDynamicStoreRef store = NULL;
store = SCDynamicStoreCreate(NULL,
CFSTR("SCNetworkCheckReachabilityByName"),
NULL,
NULL);
if (!store) {
SCLog(_sc_verbose, LOG_INFO, CFSTR("SCDynamicStoreCreate() failed"));
return FALSE;
}
_CheckReachabilityInit(store, &config, &active, &serviceOrder, &defaultRoute);
*flags = kSCNetworkFlagsReachable;
res_init();
for (i=0; i<_res.nscount; i++) {
SCNetworkConnectionFlags ns_flags = 0;
if (_res.nsaddr_list[i].sin_addr.s_addr == 0) {
continue;
}
haveDNS = TRUE;
if (_res.nsaddr_list[i].sin_len == 0) {
_res.nsaddr_list[i].sin_len = sizeof(_res.nsaddr_list[i]);
}
ok = checkAddress(store,
(struct sockaddr *)&_res.nsaddr_list[i],
_res.nsaddr_list[i].sin_len,
config,
active,
serviceOrder,
defaultRoute,
&ns_flags);
if (!ok) {
break;
}
if (rankReachability(ns_flags) < rankReachability(*flags)) {
*flags = ns_flags;
}
}
if (!ok || (rankReachability(*flags) < 2)) {
goto done;
}
SCLog(_sc_debug, LOG_INFO, CFSTR("check DNS for \"%s\""), nodename);
*flags = 0;
i = getaddrinfo(nodename, NULL, NULL, &res);
if (i != 0) {
SCLog(_sc_verbose, LOG_ERR,
CFSTR("getaddrinfo() failed: %s"),
gai_strerror(i));
goto done;
}
for (resP=res; resP!=NULL; resP=resP->ai_next) {
SCNetworkConnectionFlags ns_flags = 0;
if (resP->ai_addr->sa_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in *)resP->ai_addr;
if (sin->sin_addr.s_addr == 0) {
SCLog(_sc_debug, LOG_INFO, CFSTR("checkAddress(0.0.0.0)"));
SCLog(_sc_debug, LOG_INFO, CFSTR(" status = isReachable (this host)"));
*flags |= kSCNetworkFlagsReachable;
break;
}
}
ok = checkAddress(store,
resP->ai_addr,
resP->ai_addrlen,
config,
active,
serviceOrder,
defaultRoute,
&ns_flags);
if (!ok) {
break;
}
if (rankReachability(ns_flags) > rankReachability(*flags)) {
*flags = ns_flags;
if (rankReachability(*flags) == 2) {
break;
}
}
}
if (res) {
goto done;
}
#ifdef DEBUG
SCLog(_sc_debug,
LOG_INFO,
CFSTR("getaddrinfo() returned no addresses, try gethostbyname()"));
#endif
h = gethostbyname(nodename);
if (h && h->h_length) {
struct in_addr **s = (struct in_addr **)h->h_addr_list;
while (*s) {
SCNetworkConnectionFlags ns_flags = 0;
struct sockaddr_in sa;
bzero(&sa, sizeof(sa));
sa.sin_len = sizeof(sa);
sa.sin_family = AF_INET;
sa.sin_addr = **s;
if (sa.sin_addr.s_addr == 0) {
SCLog(_sc_debug, LOG_INFO, CFSTR("checkAddress(0.0.0.0)"));
SCLog(_sc_debug, LOG_INFO, CFSTR(" status = isReachable (this host)"));
*flags |= kSCNetworkFlagsReachable;
break;
}
ok = checkAddress(store,
(struct sockaddr *)&sa,
sizeof(sa),
config,
active,
serviceOrder,
defaultRoute,
&ns_flags);
if (!ok) {
break;
}
if (rankReachability(ns_flags) > rankReachability(*flags)) {
*flags = ns_flags;
if (rankReachability(*flags) == 2) {
break;
}
}
s++;
}
} else {
char *msg;
switch(h_errno) {
case NETDB_INTERNAL :
msg = strerror(errno);
break;
case HOST_NOT_FOUND :
msg = "Host not found.";
if (!haveDNS) {
struct sockaddr_in sa;
bzero(&sa, sizeof(sa));
sa.sin_len = sizeof(sa);
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = 0;
ok = checkAddress(store,
(struct sockaddr *)&sa,
sizeof(sa),
config,
active,
serviceOrder,
defaultRoute,
flags);
if (ok &&
(*flags & kSCNetworkFlagsReachable) &&
(*flags & kSCNetworkFlagsConnectionRequired)) {
goto done;
}
*flags = 0;
}
break;
case TRY_AGAIN :
msg = "Try again.";
break;
case NO_RECOVERY :
msg = "No recovery.";
break;
case NO_DATA :
msg = "No data available.";
break;
default :
msg = "Unknown";
break;
}
SCLog(_sc_debug, LOG_INFO, CFSTR("gethostbyname() failed: %s"), msg);
}
done :
_CheckReachabilityFree(config, active, serviceOrder, defaultRoute);
if (store) CFRelease(store);
if (res) freeaddrinfo(res);
return ok;
}