#include <SystemConfiguration/SystemConfiguration.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;
}
static CFDictionaryRef
getServices(SCDSessionRef session)
{
CFArrayRef defined = NULL;
int i;
CFStringRef key;
CFStringRef prefix;
CFMutableDictionaryRef services;
SCDStatus status;
prefix = SCDKeyCreate(CFSTR("%@/%@/%@/"),
kSCCacheDomainSetup,
kSCCompNetwork,
kSCCompService);
services = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
key = SCDKeyCreateNetworkServiceEntity(kSCCacheDomainSetup,
kSCCompAnyRegex,
kSCEntNetIPv4);
status = SCDList(session, key, kSCDRegexKey, &defined);
CFRelease(key);
if (status != SCD_OK) {
goto done;
}
for (i = 0; i < CFArrayGetCount(defined); i++) {
CFDictionaryRef if_dict;
SCDHandleRef if_handle = NULL;
CFDictionaryRef ip_dict;
SCDHandleRef ip_handle = NULL;
boolean_t isPPP = FALSE;
CFDictionaryRef ppp_dict;
SCDHandleRef ppp_handle = NULL;
CFMutableDictionaryRef sDict = NULL;
CFStringRef sid = NULL;
key = CFArrayGetValueAtIndex(defined, i);
status = SCDGet(session, key, &ip_handle);
if (status != SCD_OK) {
goto nextService;
}
ip_dict = SCDHandleGetData(ip_handle);
sDict = CFDictionaryCreateMutableCopy(NULL, 0, ip_dict);
sid = parse_component(key, prefix);
if (sid == NULL) {
goto nextService;
}
key = SCDKeyCreateNetworkServiceEntity(kSCCacheDomainSetup,
sid,
kSCEntNetInterface);
status = SCDGet(session, key, &if_handle);
CFRelease(key);
if (status != SCD_OK) {
goto nextService;
}
if_dict = SCDHandleGetData(if_handle);
if (CFDictionaryGetValueIfPresent(if_dict,
kSCPropNetInterfaceType,
(void **)&key)) {
CFDictionaryAddValue(sDict, kSCPropNetInterfaceType, key);
isPPP = CFEqual(key, kSCValNetInterfaceTypePPP);
}
if (CFDictionaryGetValueIfPresent(if_dict,
kSCPropNetInterfaceSubType,
(void **)&key)) {
CFDictionaryAddValue(sDict, kSCPropNetInterfaceSubType, key);
}
if (isPPP) {
key = SCDKeyCreateNetworkServiceEntity(kSCCacheDomainSetup,
sid,
kSCEntNetPPP);
status = SCDGet(session, key, &ppp_handle);
CFRelease(key);
if (status != SCD_OK) {
goto nextService;
}
ppp_dict = SCDHandleGetData(ppp_handle);
if (CFDictionaryGetValueIfPresent(ppp_dict,
kSCPropNetPPPDialOnDemand,
(void **)&key)) {
CFDictionaryAddValue(sDict, kSCPropNetPPPDialOnDemand, key);
}
}
CFDictionaryAddValue(services, sid, sDict);
nextService:
if (sid) CFRelease(sid);
if (if_handle) SCDHandleRelease(if_handle);
if (ip_handle) SCDHandleRelease(ip_handle);
if (ppp_handle) SCDHandleRelease(ppp_handle);
if (sDict) CFRelease(sDict);
}
done:
if (defined) CFRelease(defined);
CFRelease(prefix);
return services;
}
static CFDictionaryRef
getInterfaces(SCDSessionRef session)
{
CFMutableArrayRef defined = NULL;
int i;
CFStringRef key;
CFMutableDictionaryRef interfaces;
CFStringRef prefix;
SCDStatus status;
prefix = SCDKeyCreate(CFSTR("%@/%@/%@/"),
kSCCacheDomainState,
kSCCompNetwork,
kSCCompInterface);
interfaces = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
key = SCDKeyCreateNetworkInterfaceEntity(kSCCacheDomainState,
kSCCompAnyRegex,
kSCEntNetIPv4);
status = SCDList(session, key, kSCDRegexKey, &defined);
CFRelease(key);
if (status != SCD_OK) {
goto done;
}
for (i=0; i<CFArrayGetCount(defined); i++) {
CFStringRef iid = NULL;
CFDictionaryRef ip_dict;
SCDHandleRef ip_handle = NULL;
key = CFArrayGetValueAtIndex(defined, i);
status = SCDGet(session, key, &ip_handle);
if (status != SCD_OK) {
goto nextIF;
}
ip_dict = SCDHandleGetData(ip_handle);
iid = parse_component(key, prefix);
if (iid == NULL) {
goto nextIF;
}
CFDictionaryAddValue(interfaces, iid, ip_dict);
nextIF :
if (iid) CFRelease(iid);
if (ip_handle) SCDHandleRelease(ip_handle);
}
done:
if (defined) CFRelease(defined);
CFRelease(prefix);
return interfaces;
}
static CFArrayRef
getInterfaceOrder(CFDictionaryRef interfaces,
CFArrayRef serviceOrder,
CFNumberRef pppOverridePrimary)
{
CFIndex i;
CFIndex iCnt;
CFMutableArrayRef iKeys;
void **keys;
CFMutableArrayRef order = NULL;
CFArrayRef tKeys;
order = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
iCnt = CFDictionaryGetCount(interfaces);
keys = CFAllocatorAllocate(NULL, iCnt * sizeof(CFStringRef), 0);
CFDictionaryGetKeysAndValues(interfaces, keys, NULL);
tKeys = CFArrayCreate(NULL, keys, iCnt, &kCFTypeArrayCallBacks);
CFAllocatorDeallocate(NULL, keys);
iKeys = CFArrayCreateMutableCopy(NULL, 0, tKeys);
CFRelease(tKeys);
for (i = 0; serviceOrder && i < CFArrayGetCount(serviceOrder); i++) {
CFIndex j;
CFStringRef oSID;
oSID = CFArrayGetValueAtIndex(serviceOrder, i);
for (j=0; j<CFArrayGetCount(iKeys); j++) {
CFDictionaryRef iDict;
CFStringRef iKey;
CFStringRef iSID;
CFArrayRef iSIDs;
CFIndex k;
boolean_t match = FALSE;
iKey = CFArrayGetValueAtIndex(iKeys, j);
iDict = CFDictionaryGetValue(interfaces, iKey);
iSIDs = CFDictionaryGetValue(iDict, kSCCachePropNetServiceIDs);
for (k = 0; iSIDs && k < CFArrayGetCount(iSIDs); k++) {
iSID = CFArrayGetValueAtIndex(iSIDs, k);
if (CFEqual(oSID, iSID)) {
match = TRUE;
break;
}
}
if (match) {
CFArrayAppendValue(order, iKey);
CFArrayRemoveValueAtIndex(iKeys, j);
break;
}
}
}
for (i = 0; i < CFArrayGetCount(iKeys); i++) {
CFStringRef iKey;
iKey = CFArrayGetValueAtIndex(iKeys, i);
CFArrayAppendValue(order, iKey);
}
CFRelease(iKeys);
return order;
}
static boolean_t
getAddresses(CFDictionaryRef iDict,
CFIndex *nAddrs,
CFArrayRef *addrs,
CFArrayRef *masks,
CFArrayRef *dests)
{
*addrs = CFDictionaryGetValue(iDict, kSCPropNetIPv4Addresses);
*masks = CFDictionaryGetValue(iDict, kSCPropNetIPv4SubnetMasks);
*dests = CFDictionaryGetValue(iDict, kSCPropNetIPv4DestAddresses);
if ((*addrs == NULL) ||
((*nAddrs = CFArrayGetCount(*addrs)) == 0)) {
return FALSE;
}
if ((*masks && *dests) ||
(*masks == NULL) && (*dests == NULL)) {
return FALSE;
}
if (*masks && (*nAddrs != CFArrayGetCount(*masks))) {
return FALSE;
}
if (*dests && (*nAddrs != CFArrayGetCount(*dests))) {
return FALSE;
}
return TRUE;
}
static SCNStatus
checkAddress(SCDSessionRef session,
const struct sockaddr *address,
const int addrlen,
CFDictionaryRef services,
CFDictionaryRef interfaces,
CFArrayRef interfaceOrder,
struct in_addr *defaultRoute,
int *flags,
const char **errorMessage)
{
CFIndex i;
struct ifreq ifr;
CFIndex iCnt;
CFStringRef iKey = NULL;
CFStringRef iType = NULL;
void **keys;
int pppRef = -1;
SCNStatus scn_status = SCN_REACHABLE_UNKNOWN;
CFIndex sCnt;
CFMutableArrayRef sKeys = NULL;
CFStringRef sID = NULL;
CFArrayRef sIDs = NULL;
CFArrayRef sList = NULL;
int sock = -1;
CFStringRef sKey = NULL;
CFDictionaryRef sDict = NULL;
CFArrayRef tKeys;
if (flags != NULL) {
*flags = 0;
}
if (address == NULL) {
return SCN_REACHABLE_NO;
}
sCnt = CFDictionaryGetCount(services);
keys = CFAllocatorAllocate(NULL, sCnt * sizeof(CFStringRef), 0);
CFDictionaryGetKeysAndValues(services, keys, NULL);
tKeys = CFArrayCreate(NULL, keys, sCnt, &kCFTypeArrayCallBacks);
CFAllocatorDeallocate(NULL, keys);
sKeys = CFArrayCreateMutableCopy(NULL, 0, tKeys);
CFRelease(tKeys);
if (address->sa_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in *)address;
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug))
SCDLog(LOG_INFO, CFSTR("checkAddress(%s)"), inet_ntoa(sin->sin_addr));
#endif
if (ntohl(sin->sin_addr.s_addr) == ntohl(INADDR_LOOPBACK)) {
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug))
SCDLog(LOG_INFO, CFSTR(" isReachable via loopback"));
#endif
scn_status = SCN_REACHABLE_YES;
goto done;
}
iCnt = CFArrayGetCount(interfaceOrder);
for (i=0; i<iCnt; i++) {
CFArrayRef addrs;
CFArrayRef dests;
CFDictionaryRef iDict;
CFIndex j;
CFArrayRef masks;
CFIndex nAddrs = 0;
iKey = CFArrayGetValueAtIndex(interfaceOrder, i);
iDict = CFDictionaryGetValue(interfaces, iKey);
sIDs = CFDictionaryGetValue(iDict, kSCCachePropNetServiceIDs);
for (j = 0; sIDs && j < CFArrayGetCount(sIDs); j++) {
CFIndex k;
CFStringRef sID;
sID = CFArrayGetValueAtIndex(sIDs, j);
k = CFArrayGetFirstIndexOfValue(sKeys,
CFRangeMake(0, CFArrayGetCount(sKeys)),
sID);
if (k != -1) {
CFArrayRemoveValueAtIndex(sKeys, k);
}
}
if (!getAddresses(iDict, &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))) {
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug))
SCDLog(LOG_INFO, CFSTR(" isReachable (my subnet)"));
#endif
scn_status = SCN_REACHABLE_YES;
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)) {
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug))
SCDLog(LOG_INFO, CFSTR(" isReachable (my local address)"));
#endif
scn_status = SCN_REACHABLE_YES;
goto checkInterface;
}
if (ntohl(sin->sin_addr.s_addr) == ntohl(destAddr.s_addr)) {
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug))
SCDLog(LOG_INFO, CFSTR(" isReachable (my remote address)"));
#endif
scn_status = SCN_REACHABLE_YES;
goto checkInterface;
}
}
}
}
for (i=0; i<iCnt; i++) {
CFArrayRef addrs;
CFArrayRef dests;
CFDictionaryRef iDict;
CFIndex j;
CFArrayRef masks;
CFIndex nAddrs = 0;
iKey = CFArrayGetValueAtIndex(interfaceOrder, i);
iDict = CFDictionaryGetValue(interfaces, iKey);
if (!getAddresses(iDict, &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))) {
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug))
SCDLog(LOG_INFO, CFSTR(" isReachable via default route (my subnet)"));
#endif
scn_status = SCN_REACHABLE_YES;
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)) {
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug))
SCDLog(LOG_INFO, CFSTR(" isReachable via default route (my remote address)"));
#endif
scn_status = SCN_REACHABLE_YES;
goto checkInterface;
}
}
}
}
sCnt = CFArrayGetCount(sKeys);
for (i=0; i<sCnt; i++) {
CFArrayRef addrs;
CFStringRef configMethod = NULL;
CFArrayRef dests;
CFIndex j;
CFArrayRef masks;
CFIndex nAddrs = 0;
sKey = CFArrayGetValueAtIndex(sKeys, i);
sDict = CFDictionaryGetValue(services, sKey);
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))) {
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug))
SCDLog(LOG_INFO, CFSTR(" is configured w/static info (my subnet)"));
#endif
goto checkService;
}
} 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)) {
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug))
SCDLog(LOG_INFO, CFSTR(" is configured w/static info (my local address)"));
#endif
goto checkService;
}
if (ntohl(sin->sin_addr.s_addr) == ntohl(destAddr.s_addr)) {
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug))
SCDLog(LOG_INFO, CFSTR(" is configured w/static info (my remote address)"));
#endif
goto checkService;
}
}
}
if (CFDictionaryGetValueIfPresent(sDict,
kSCPropNetIPv4ConfigMethod,
(void **)&configMethod) &&
!CFEqual(configMethod, kSCValNetIPv4ConfigMethodManual)) {
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug))
SCDLog(LOG_INFO, CFSTR(" is configured w/dynamic addressing"));
#endif
goto checkService;
}
}
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug))
SCDLog(LOG_INFO, CFSTR(" cannot be reached"));
#endif
scn_status = SCN_REACHABLE_NO;
goto done;
} else {
SCDSessionLog(session,
LOG_ERR,
CFSTR("checkAddress(): unexpected address family %d"),
address->sa_family);
if (errorMessage != NULL) {
*errorMessage = "unexpected address family";
}
goto done;
}
goto done;
checkInterface :
if (sIDs) {
CFNumberRef num;
CFDictionaryRef sDict;
sID = CFArrayGetValueAtIndex(sIDs, 0);
sDict = CFDictionaryGetValue(services, sID);
if (sDict) {
iType = CFDictionaryGetValue(sDict, kSCPropNetInterfaceType);
}
if (!iType) {
goto done;
}
if (!CFEqual(iType, kSCValNetInterfaceTypePPP)) {
goto done;
}
num = CFDictionaryGetValue(sDict, kSCPropNetPPPDialOnDemand);
if (num) {
int dialOnDemand;
CFNumberGetValue(num, kCFNumberIntType, &dialOnDemand);
if (flags && (dialOnDemand != 0)) {
*flags |= kSCNFlagsConnectionAutomatic;
}
}
} else if (!CFStringHasPrefix(iKey, CFSTR("ppp"))) {
goto done;
}
if (flags != NULL) {
*flags |= kSCNFlagsTransientConnection;
}
if (sID) {
u_int32_t pppLink;
struct ppp_status *pppLinkStatus;
int pppStatus;
pppStatus = PPPInit(&pppRef);
if (pppStatus != 0) {
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug))
SCDLog(LOG_DEBUG, CFSTR(" PPPInit() failed: status=%d"), pppStatus);
#endif
scn_status = SCN_REACHABLE_UNKNOWN;
if (errorMessage != NULL) {
*errorMessage = "PPPInit() failed";
}
goto done;
}
pppStatus = PPPGetLinkByServiceID(pppRef, sID, &pppLink);
if (pppStatus != 0) {
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug))
SCDLog(LOG_DEBUG, CFSTR(" PPPGetLinkByServiceID() failed: status=%d"), pppStatus);
#endif
scn_status = SCN_REACHABLE_UNKNOWN;
if (errorMessage != NULL) {
*errorMessage = "PPPGetLinkByServiceID() failed";
}
goto done;
}
pppStatus = PPPStatus(pppRef, pppLink, &pppLinkStatus);
if (pppStatus != 0) {
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug))
SCDLog(LOG_DEBUG, CFSTR(" PPPStatus() failed: status=%d"), pppStatus);
#endif
scn_status = SCN_REACHABLE_UNKNOWN;
if (errorMessage != NULL) {
*errorMessage = "PPPStatus() failed";
}
goto done;
}
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug))
SCDLog(LOG_DEBUG, CFSTR(" PPP link status = %d"), pppLinkStatus->status);
#endif
switch (pppLinkStatus->status) {
case PPP_RUNNING :
break;
case PPP_IDLE :
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug))
SCDLog(LOG_INFO, CFSTR(" PPP link idle, dial-on-traffic to connect"));
#endif
scn_status = SCN_REACHABLE_CONNECTION_REQUIRED;
break;
default :
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug))
SCDLog(LOG_INFO, CFSTR(" PPP link, connection in progress"));
#endif
scn_status = SCN_REACHABLE_CONNECTION_REQUIRED;
break;
}
CFAllocatorDeallocate(NULL, pppLinkStatus);
} else {
bzero(&ifr, sizeof(ifr));
if (!CFStringGetCString(iKey,
(char *)&ifr.ifr_name,
sizeof(ifr.ifr_name),
kCFStringEncodingMacRoman)) {
scn_status = SCN_REACHABLE_UNKNOWN;
if (errorMessage != NULL) {
*errorMessage = "could not convert interface name to C string";
}
goto done;
}
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == -1) {
scn_status = SCN_REACHABLE_UNKNOWN;
if (errorMessage != NULL) {
*errorMessage = strerror(errno);
}
goto done;
}
if (ioctl(sock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
scn_status = SCN_REACHABLE_UNKNOWN;
if (errorMessage != NULL) {
*errorMessage = strerror(errno);
}
goto done;
}
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug))
SCDLog(LOG_INFO, CFSTR(" flags for %s == 0x%hx"), ifr.ifr_name, ifr.ifr_flags);
#endif
if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
if ((ifr.ifr_flags & IFF_UP) == IFF_UP) {
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug))
SCDLog(LOG_INFO, CFSTR(" up & not running, dial-on-traffic to connect"));
#endif
scn_status = SCN_REACHABLE_CONNECTION_REQUIRED;
if (flags != NULL) {
*flags |= kSCNFlagsConnectionAutomatic;
}
} else {
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug))
SCDLog(LOG_INFO, CFSTR(" not up & running, connection required"));
#endif
scn_status = SCN_REACHABLE_CONNECTION_REQUIRED;
}
goto done;
}
}
goto done;
checkService :
if (sDict &&
CFDictionaryGetValueIfPresent(sDict,
kSCPropNetInterfaceType,
(void **)&iType) &&
!CFEqual(iType, kSCValNetInterfaceTypePPP)) {
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug))
SCDLog(LOG_INFO, CFSTR(" cannot be reached"));
#endif
scn_status = SCN_REACHABLE_NO;
goto done;
}
scn_status = SCN_REACHABLE_CONNECTION_REQUIRED;
if (flags != NULL) {
*flags |= kSCNFlagsTransientConnection;
}
done :
if (sKeys) CFRelease(sKeys);
if (sList) CFRelease(sList);
if (pppRef != -1) (void) PPPDispose(pppRef);
if (sock != -1) (void)close(sock);
return scn_status;
}
static void
_IsReachableInit(SCDSessionRef session,
CFDictionaryRef *services,
CFDictionaryRef *interfaces,
CFArrayRef *interfaceOrder,
struct in_addr **defaultRoute)
{
CFStringRef addr;
CFDictionaryRef dict;
CFStringRef key;
SCDHandleRef handle;
CFNumberRef pppOverridePrimary = NULL;
CFArrayRef serviceOrder = NULL;
struct in_addr *route = NULL;
SCDStatus status;
key = SCDKeyCreateNetworkGlobalEntity(kSCCacheDomainSetup, kSCEntNetIPv4);
status = SCDGet(session, key, &handle);
CFRelease(key);
switch (status) {
case SCD_OK :
dict = SCDHandleGetData(handle);
if ((CFDictionaryGetValueIfPresent(dict,
kSCPropNetServiceOrder,
(void **)&serviceOrder) == TRUE)) {
CFRetain(serviceOrder);
}
if ((CFDictionaryGetValueIfPresent(dict,
kSCPropNetPPPOverridePrimary,
(void **)&pppOverridePrimary) == TRUE)) {
CFRetain(pppOverridePrimary);
}
SCDHandleRelease(handle);
break;
case SCD_NOKEY :
break;
default :
SCDLog(LOG_ERR, CFSTR("SCDGet() failed: %s"), SCDError(status));
goto error;
}
key = SCDKeyCreateNetworkGlobalEntity(kSCCacheDomainState,
kSCEntNetIPv4);
status = SCDGet(session, key, &handle);
CFRelease(key);
switch (status) {
case SCD_OK :
dict = SCDHandleGetData(handle);
addr = CFDictionaryGetValue(dict, kSCPropNetIPv4Router);
if (addr == NULL) {
break;
}
route = CFAllocatorAllocate(NULL, sizeof(struct in_addr), 0);
if (inet_atonCF(addr, route) == 0) {
CFAllocatorDeallocate(NULL, route);
route = NULL;
break;
}
*defaultRoute = route;
break;
case SCD_NOKEY :
break;
default :
SCDSessionLog(session,
LOG_ERR,
CFSTR("SCDGet() failed: %s"),
SCDError(status));
goto error;
}
if (handle) {
SCDHandleRelease(handle);
handle = NULL;
}
*services = getServices (session);
*interfaces = getInterfaces(session);
*interfaceOrder = getInterfaceOrder(*interfaces,
serviceOrder,
pppOverridePrimary);
error :
if (serviceOrder) CFRelease(serviceOrder);
if (pppOverridePrimary) CFRelease(pppOverridePrimary);
#ifdef DEBUG
if (SCDOptionGet(session, kSCDOptionDebug)) {
SCDLog(LOG_NOTICE, CFSTR("interfaces = %@"), *interfaces);
SCDLog(LOG_NOTICE, CFSTR("services = %@"), *services);
SCDLog(LOG_NOTICE, CFSTR("interfaceOrder = %@"), *interfaceOrder);
SCDLog(LOG_NOTICE, CFSTR("defaultRoute = %s"), *defaultRoute?inet_ntoa(**defaultRoute):"None");
}
#endif
return;
}
static void
_IsReachableFree(CFDictionaryRef services,
CFDictionaryRef interfaces,
CFArrayRef interfaceOrder,
struct in_addr *defaultRoute)
{
if (services) CFRelease(services);
if (interfaces) CFRelease(interfaces);
if (interfaceOrder) CFRelease(interfaceOrder);
if (defaultRoute) CFAllocatorDeallocate(NULL, defaultRoute);
return;
}
SCNStatus
SCNIsReachableByAddress(const struct sockaddr *address,
const int addrlen,
int *flags,
const char **errorMessage)
{
struct in_addr *defaultRoute = NULL;
CFDictionaryRef interfaces = NULL;
CFArrayRef interfaceOrder = NULL;
CFDictionaryRef services = NULL;
SCDSessionRef session = NULL;
SCDStatus scd_status;
SCNStatus scn_status;
scd_status = SCDOpen(&session, CFSTR("SCNIsReachableByAddress"));
if (scd_status != SCD_OK) {
if (errorMessage != NULL) {
*errorMessage = SCDError(scd_status);
}
return SCN_REACHABLE_UNKNOWN;
}
_IsReachableInit(session, &services, &interfaces, &interfaceOrder, &defaultRoute);
scn_status = checkAddress(session,
address,
addrlen,
services,
interfaces,
interfaceOrder,
defaultRoute,
flags,
errorMessage);
_IsReachableFree(services, interfaces, interfaceOrder, defaultRoute);
(void) SCDClose(&session);
return scn_status;
}
SCNStatus
SCNIsReachableByName(const char *nodename,
int *flags,
const char **errorMessage)
{
struct in_addr *defaultRoute = NULL;
int i;
CFDictionaryRef interfaces = NULL;
CFArrayRef interfaceOrder = NULL;
SCDStatus scd_status = SCD_OK;
SCNStatus ns_status = SCN_REACHABLE_YES;
struct addrinfo *res = NULL;
struct addrinfo *resP;
CFDictionaryRef services = NULL;
SCDSessionRef session = NULL;
SCNStatus scn_status = SCN_REACHABLE_YES;
scd_status = SCDOpen(&session, CFSTR("SCNIsReachableByName"));
if (scd_status != SCD_OK) {
scn_status = SCN_REACHABLE_UNKNOWN;
if (errorMessage != NULL) {
*errorMessage = SCDError(scd_status);
}
goto done;
}
_IsReachableInit(session, &services, &interfaces, &interfaceOrder, &defaultRoute);
res_init();
for (i=0; i<_res.nscount; i++) {
ns_status = checkAddress(session,
(struct sockaddr *)&_res.nsaddr_list[i],
_res.nsaddr_list[i].sin_len,
services,
interfaces,
interfaceOrder,
defaultRoute,
flags,
errorMessage);
if (ns_status < scn_status) {
scn_status = ns_status;
if (ns_status == SCN_REACHABLE_UNKNOWN) {
break;
}
}
}
if (ns_status < SCN_REACHABLE_YES) {
goto done;
}
scn_status = SCN_REACHABLE_UNKNOWN;
i = getaddrinfo(nodename, NULL, NULL, &res);
if (i != 0) {
SCDSessionLog(session,
LOG_ERR,
CFSTR("getaddrinfo() failed: %s"),
gai_strerror(i));
goto done;
}
for (resP=res; resP!=NULL; resP=resP->ai_next) {
ns_status = checkAddress(session,
resP->ai_addr,
resP->ai_addrlen,
services,
interfaces,
interfaceOrder,
defaultRoute,
flags,
errorMessage);
if (ns_status > scn_status) {
scn_status = ns_status;
if (ns_status == SCN_REACHABLE_YES) {
break;
}
}
}
done :
_IsReachableFree(services, interfaces, interfaceOrder, defaultRoute);
if (session) (void)SCDClose(&session);
if (res) freeaddrinfo(res);
return scn_status;
}