/*
- * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2003-2005 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
- * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
- *
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
/*
* Modification History
*
+ * March 31, 2004 Allan Nathanson <ajn@apple.com>
+ * - use [SC] DNS configuration information
+ *
* January 19, 2003 Allan Nathanson <ajn@apple.com>
* - add advanced reachability APIs
*/
#include <CoreFoundation/CFRuntime.h>
#include <pthread.h>
+#include <notify.h>
+#include <dnsinfo.h>
#include <netinet/in.h>
#include <arpa/inet.h>
-#include <arpa/nameser.h>
#include <netdb.h>
#include <netdb_async.h>
#include <resolv.h>
#define s6_addr16 __u6_addr.__u6_addr16
#endif
-#include "ppp.h"
+#include <ppp/ppp_msg.h>
+
+
#define kSCNetworkFlagsFirstResolvePending (1<<31)
SCNetworkReachabilityContext rlsContext;
CFMutableArrayRef rlList;
- /* async DNS query info */
+ /* [async] DNS query info */
+ Boolean haveDNS;
CFMachPortRef dnsPort;
CFRunLoopSourceRef dnsRLS;
+ struct timeval dnsQueryStart;
} SCNetworkReachabilityPrivate, *SCNetworkReachabilityPrivateRef;
static pthread_once_t initialized = PTHREAD_ONCE_INIT;
-static Boolean needDNS = TRUE;
-/* host "something has changed" notifications */
+/*
+ * host "something has changed" notifications
+ */
+
static pthread_mutex_t hn_lock = PTHREAD_MUTEX_INITIALIZER;
static SCDynamicStoreRef hn_store = NULL;
static CFRunLoopSourceRef hn_storeRLS = NULL;
static CFMutableSetRef hn_targets = NULL;
-static __inline__ CFTypeRef
-isA_SCNetworkReachability(CFTypeRef obj)
-{
- return (isA_CFType(obj, SCNetworkReachabilityGetTypeID()));
-}
+/*
+ * DNS configuration
+ */
+typedef struct {
+ dns_config_t *config;
+ int refs;
+} dns_configuration_t;
-static void
-sockaddr_to_string(const struct sockaddr *address, char *buf, size_t bufLen)
-{
- bzero(buf, bufLen);
- switch (address->sa_family) {
- case AF_INET :
- (void)inet_ntop(((struct sockaddr_in *)address)->sin_family,
- &((struct sockaddr_in *)address)->sin_addr,
- buf,
- bufLen);
- break;
- case AF_INET6 : {
- (void)inet_ntop(((struct sockaddr_in6 *)address)->sin6_family,
- &((struct sockaddr_in6 *)address)->sin6_addr,
- buf,
- bufLen);
- if (((struct sockaddr_in6 *)address)->sin6_scope_id != 0) {
- int n;
-
- n = strlen(buf);
- if ((n+IF_NAMESIZE+1) <= (int)bufLen) {
- buf[n++] = '%';
- if_indextoname(((struct sockaddr_in6 *)address)->sin6_scope_id, &buf[n]);
- }
- }
- break;
- }
- case AF_LINK :
- if (((struct sockaddr_dl *)address)->sdl_len < bufLen) {
- bufLen = ((struct sockaddr_dl *)address)->sdl_len;
- } else {
- bufLen = bufLen - 1;
- }
- bcopy(((struct sockaddr_dl *)address)->sdl_data, buf, bufLen);
- break;
- default :
- snprintf(buf, bufLen, "unexpected address family %d", address->sa_family);
- break;
- }
+static pthread_mutex_t dns_lock = PTHREAD_MUTEX_INITIALIZER;
+static dns_configuration_t *dns_configuration = NULL;
+static int dns_token;
+static Boolean dns_token_valid = FALSE;
- return;
-}
-
-#ifndef CHECK_IPV6_REACHABILITY
-static char *
-__netdb_error(int error)
+static __inline__ CFTypeRef
+isA_SCNetworkReachability(CFTypeRef obj)
{
- char *msg;
-
- switch(error) {
- case NETDB_INTERNAL :
- msg = strerror(errno);
- break;
- case HOST_NOT_FOUND :
- msg = "Host not found.";
- 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;
- }
-
- return msg;
+ return (isA_CFType(obj, SCNetworkReachabilityGetTypeID()));
}
-#endif /* CHECK_IPV6_REACHABILITY */
static void
-__signalRunLoop(CFTypeRef obj, CFRunLoopSourceRef rls, CFArrayRef rlList)
+__log_query_time(Boolean found, Boolean async, struct timeval *start)
{
- CFRunLoopRef rl = NULL;
- CFRunLoopRef rl1 = NULL;
- CFIndex i;
- CFIndex n = CFArrayGetCount(rlList);
-
- if (n == 0) {
- return;
- }
-
- /* get first runLoop for this object */
- for (i = 0; i < n; i += 3) {
- if (!CFEqual(obj, CFArrayGetValueAtIndex(rlList, i))) {
- continue;
- }
+ struct timeval dnsQueryComplete;
+ struct timeval dnsQueryElapsed;
- rl1 = (CFRunLoopRef)CFArrayGetValueAtIndex(rlList, i+1);
- break;
- }
-
- if (!rl1) {
- /* if not scheduled */
+ if (!_sc_debug) {
return;
}
- /* check if we have another runLoop for this object */
- rl = rl1;
- for (i = i+3; i < n; i += 3) {
- CFRunLoopRef rl2;
-
- if (!CFEqual(obj, CFArrayGetValueAtIndex(rlList, i))) {
- continue;
- }
-
- rl2 = (CFRunLoopRef)CFArrayGetValueAtIndex(rlList, i+1);
- if (!CFEqual(rl1, rl2)) {
- /* we've got more than one runLoop */
- rl = NULL;
- break;
- }
- }
-
- if (rl) {
- /* if we only have one runLoop */
- CFRunLoopWakeUp(rl);
+ if (start->tv_sec == 0) {
return;
}
- /* more than one different runLoop, so we must pick one */
- for (i = 0; i < n; i+=3) {
- CFStringRef rlMode;
+ (void) gettimeofday(&dnsQueryComplete, NULL);
+ timersub(&dnsQueryComplete, start, &dnsQueryElapsed);
+ SCLog(TRUE, LOG_DEBUG,
+ CFSTR("%ssync DNS complete%s (query time = %d.%3.3d)"),
+ async ? "a" : "",
+ found ? "" : ", host not found",
+ dnsQueryElapsed.tv_sec,
+ dnsQueryElapsed.tv_usec / 1000);
- if (!CFEqual(obj, CFArrayGetValueAtIndex(rlList, i))) {
- continue;
- }
-
- rl = (CFRunLoopRef)CFArrayGetValueAtIndex(rlList, i+1);
- rlMode = CFRunLoopCopyCurrentMode(rl);
- if (rlMode && CFRunLoopIsWaiting(rl) && CFRunLoopContainsSource(rl, rls, rlMode)) {
- /* we've found a runLoop that's "ready" */
- CFRelease(rlMode);
- CFRunLoopWakeUp(rl);
- return;
- }
- if (rlMode) CFRelease(rlMode);
- }
-
- /* didn't choose one above, so choose first */
- CFRunLoopWakeUp(rl1);
return;
}
CFIndex n;
CFStringRef ppp_if;
int sc_status = kSCStatusReachabilityUnknown;
- SCDynamicStoreRef store = (storeP) ? *storeP : NULL;
+ SCDynamicStoreRef store = (storeP != NULL) ? *storeP : NULL;
const void * values_q[N_QUICK];
const void ** values = values_q;
goto done;
}
- if (!store) {
+ if (store == NULL) {
store = SCDynamicStoreCreate(NULL, CFSTR("SCNetworkReachability"), NULL, NULL);
- if (!store) {
- SCLog(_sc_verbose, LOG_INFO, CFSTR("SCDynamicStoreCreate() failed"));
+ if (store == NULL) {
+ SCLog(_sc_verbose, LOG_INFO, CFSTR("updatePPPStatus SCDynamicStoreCreate() failed"));
goto done;
}
}
dict = SCDynamicStoreCopyMultiple(store, NULL, patterns);
CFRelease(patterns);
}
- if (!dict) {
+ if (dict == NULL) {
/* if we could not access the dynamic store */
goto done;
}
done :
- if (dict) CFRelease(dict);
- if (storeP) *storeP = store;
+ if (dict != NULL) CFRelease(dict);
+ if (storeP != NULL) *storeP = store;
return sc_status;
}
static int
updatePPPAvailable(SCDynamicStoreRef *storeP,
- const struct sockaddr *sa,
- SCNetworkConnectionFlags *flags)
+ const struct sockaddr *sa,
+ SCNetworkConnectionFlags *flags)
{
CFDictionaryRef dict = NULL;
CFStringRef entity;
const void ** keys = keys_q;
CFIndex n;
int sc_status = kSCStatusReachabilityUnknown;
- SCDynamicStoreRef store = (storeP) ? *storeP : NULL;
+ SCDynamicStoreRef store = (storeP != NULL) ? *storeP : NULL;
const void * values_q[N_QUICK];
const void ** values = values_q;
- switch (sa->sa_family) {
- case AF_INET :
- entity = kSCEntNetIPv4;
- break;
- case AF_INET6 :
- entity = kSCEntNetIPv6;
- break;
- default :
- goto done;
+ if (sa == NULL) {
+ entity = kSCEntNetIPv4;
+ } else {
+ switch (sa->sa_family) {
+ case AF_INET :
+ entity = kSCEntNetIPv4;
+ break;
+ case AF_INET6 :
+ entity = kSCEntNetIPv6;
+ break;
+ default :
+ goto done;
+ }
}
- if (!store) {
+ if (store == NULL) {
store = SCDynamicStoreCreate(NULL, CFSTR("SCNetworkReachability"), NULL, NULL);
- if (!store) {
- SCLog(_sc_verbose, LOG_INFO, CFSTR("SCDynamicStoreCreate() failed"));
+ if (store == NULL) {
+ SCLog(_sc_debug, LOG_INFO, CFSTR(" status = unknown (could not access SCDynamicStore"));
goto done;
}
}
dict = SCDynamicStoreCopyMultiple(store, NULL, patterns);
CFRelease(patterns);
}
- if (!dict) {
+ if (dict == NULL) {
/* if we could not access the dynamic store */
goto done;
}
done :
- if (dict) CFRelease(dict);
- if (storeP) *storeP = store;
+ if (dict != NULL) CFRelease(dict);
+ if (storeP != NULL) *storeP = store;
return sc_status;
}
int sc_status = kSCStatusReachabilityUnknown;
struct sockaddr_dl *sdl;
int seq = (int)pthread_self();
- SCDynamicStoreRef store = (storeP) ? *storeP : NULL;
+ SCDynamicStoreRef store = (storeP != NULL) ? *storeP : NULL;
char *statusMessage = NULL;
#ifndef RTM_GET_SILENT
#warning Note: Using RTM_GET (and not RTM_GET_SILENT)
int sosize = 48 * 1024;
#endif
- if (!address || !flags) {
- sc_status = kSCStatusInvalidArgument;
- goto done;
+ *flags = 0;
+ if (if_index != NULL) {
+ *if_index = 0;
+ }
+
+ if (address == NULL) {
+ /* special case: check only for available paths off the system */
+ goto checkAvailable;
}
switch (address->sa_family) {
case AF_INET :
case AF_INET6 :
if (_sc_debug) {
- sockaddr_to_string(address, buf, sizeof(buf));
+ _SC_sockaddr_to_string(address, buf, sizeof(buf));
SCLog(TRUE, LOG_INFO, CFSTR("checkAddress(%s)"), buf);
}
break;
goto done;
}
- *flags = 0;
- if (if_index) {
- *if_index = 0;
- }
-
- if ((address->sa_family == AF_INET) && (((struct sockaddr_in *)address)->sin_addr.s_addr == 0)) {
- /* special case: check for available paths off the system */
- goto checkAvailable;
- }
-
bzero(&buf, sizeof(buf));
rtm = (struct rt_msghdr *)&buf;
for (i = 0; i < RTAX_MAX; i++) {
if (rti_info[i] != NULL) {
- sockaddr_to_string(rti_info[i], buf, sizeof(buf));
+ _SC_sockaddr_to_string(rti_info[i], buf, sizeof(buf));
SCLog(_sc_debug, LOG_DEBUG, CFSTR("%d: %s"), i, buf);
}
}
if ((rti_info[RTAX_IFP] == NULL) ||
(rti_info[RTAX_IFP]->sa_family != AF_LINK)) {
/* no interface info */
- goto done; // ???
+ goto done;
}
sdl = (struct sockaddr_dl *) rti_info[RTAX_IFP];
if ((sdl->sdl_nlen == 0) || (sdl->sdl_nlen > IFNAMSIZ)) {
/* no interface name */
- goto done; // ???
+ goto checkAvailable;
}
/* get the interface flags */
bzero(&ifr, sizeof(ifr));
- bcopy(sdl->sdl_data, ifr.ifr_name, sdl->sdl_len);
+ bcopy(sdl->sdl_data, ifr.ifr_name, sdl->sdl_nlen);
isock = socket(AF_INET, SOCK_DGRAM, 0);
if (isock < 0) {
addr1 = &((struct sockaddr_in *)address)->sin_addr;
addr2 = &((struct sockaddr_in *)rti_info[RTAX_IFA])->sin_addr;
len = sizeof(struct in_addr);
+
+ /*
+ * check if 0.0.0.0
+ */
+ if (((struct sockaddr_in *)address)->sin_addr.s_addr == 0) {
+ statusMessage = "isReachable (this host)";
+ *flags |= kSCNetworkFlagsIsLocalAddress;
+ }
break;
case AF_INET6 :
addr1 = &((struct sockaddr_in6 *)address)->sin6_addr;
}
}
- if (rti_info[RTAX_GATEWAY] && (rti_info[RTAX_GATEWAY]->sa_family == AF_LINK)) {
+ if (!(rtm->rtm_flags & RTF_GATEWAY) &&
+ (rti_info[RTAX_GATEWAY] != NULL) &&
+ (rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) &&
+ !(ifr.ifr_flags & IFF_POINTOPOINT)) {
*flags |= kSCNetworkFlagsIsDirect;
}
bzero(&if_name, sizeof(if_name));
bcopy(sdl->sdl_data,
if_name,
- (sdl->sdl_len <= IFNAMSIZ) ? sdl->sdl_len : IFNAMSIZ);
+ (sdl->sdl_nlen <= IFNAMSIZ) ? sdl->sdl_nlen : IFNAMSIZ);
- if (if_index) {
+ if (if_index != NULL) {
*if_index = sdl->sdl_index;
}
SCLog(_sc_debug, LOG_INFO, CFSTR(" cannot be reached"));
}
- if (storeP) *storeP = store;
+ if (storeP != NULL) *storeP = store;
if (sc_status != kSCStatusOK) {
_SCErrorSet(sc_status);
return FALSE;
}
-static Boolean
-checkAddressZero(SCDynamicStoreRef *storeP,
- SCNetworkConnectionFlags *flags,
- uint16_t *if_index)
-{
- Boolean ok;
- struct sockaddr_in sin;
-
- bzero(&sin, sizeof(sin));
- sin.sin_len = sizeof(sin);
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = 0;
-
- ok = checkAddress(storeP, (struct sockaddr *)&sin, flags, if_index);
-
- return ok;
-}
-
-
-static Boolean
-isAddressZero(struct sockaddr *sa, SCNetworkConnectionFlags *flags)
-{
- /*
- * Check if 0.0.0.0
- */
- if (sa->sa_family == AF_INET) {
- struct sockaddr_in *sin = (struct sockaddr_in *)sa;
-
- if (sin->sin_addr.s_addr == 0) {
- SCLog(_sc_debug, LOG_INFO, CFSTR("isAddressZero(0.0.0.0)"));
- SCLog(_sc_debug, LOG_INFO, CFSTR(" status = isReachable (this host)"));
- *flags |= kSCNetworkFlagsReachable;
- *flags |= kSCNetworkFlagsIsLocalAddress;
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-
static CFStringRef
__SCNetworkReachabilityCopyDescription(CFTypeRef cf)
{
case reachabilityTypeAddressPair : {
char buf[64];
- if (targetPrivate->localAddress) {
- sockaddr_to_string(targetPrivate->localAddress, buf, sizeof(buf));
+ if (targetPrivate->localAddress != NULL) {
+ _SC_sockaddr_to_string(targetPrivate->localAddress, buf, sizeof(buf));
CFStringAppendFormat(result, NULL, CFSTR("local address=%s"),
buf);
}
- if (targetPrivate->remoteAddress) {
- sockaddr_to_string(targetPrivate->remoteAddress, buf, sizeof(buf));
+ if (targetPrivate->remoteAddress != NULL) {
+ _SC_sockaddr_to_string(targetPrivate->remoteAddress, buf, sizeof(buf));
CFStringAppendFormat(result, NULL, CFSTR("%s%saddress=%s"),
targetPrivate->localAddress ? ", " : "",
(targetPrivate->type == reachabilityTypeAddressPair) ? "remote " : "",
case reachabilityTypeName : {
CFStringAppendFormat(result, NULL, CFSTR("name=%s"), targetPrivate->name);
if (targetPrivate->resolvedAddress || (targetPrivate->resolvedAddressError != NETDB_SUCCESS)) {
- if (targetPrivate->resolvedAddress) {
+ if (targetPrivate->resolvedAddress != NULL) {
if (isA_CFArray(targetPrivate->resolvedAddress)) {
CFIndex i;
CFIndex n = CFArrayGetCount(targetPrivate->resolvedAddress);
address = CFArrayGetValueAtIndex(targetPrivate->resolvedAddress, i);
sa = (struct sockaddr *)CFDataGetBytePtr(address);
- sockaddr_to_string(sa, buf, sizeof(buf));
+ _SC_sockaddr_to_string(sa, buf, sizeof(buf));
CFStringAppendFormat(result, NULL, CFSTR("%s%s"),
i > 0 ? ", " : "",
buf);
CFStringAppendFormat(result, NULL, CFSTR(" (no addresses)"));
}
} else {
-#ifdef CHECK_IPV6_REACHABILITY
CFStringAppendFormat(result, NULL, CFSTR(" (%s)"),
gai_strerror(targetPrivate->resolvedAddressError));
-#else /* CHECK_IPV6_REACHABILITY */
- CFStringAppendFormat(result, NULL, CFSTR(" (%s)"),
- __netdb_error(targetPrivate->resolvedAddressError));
-#endif /* CHECK_IPV6_REACHABILITY */
}
} else if (targetPrivate->dnsPort) {
CFStringAppendFormat(result, NULL, CFSTR(" (DNS query active)"));
break;
}
}
- if (targetPrivate->rls) {
+ if (targetPrivate->rls != NULL) {
CFStringAppendFormat(result,
NULL,
CFSTR(", flags=%8.8x, if_index=%hu"),
{
SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)cf;
- SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCNetworkReachabilityDeallocate:"));
-
/* release resources */
pthread_mutex_destroy(&targetPrivate->lock);
- if (targetPrivate->name)
+ if (targetPrivate->name != NULL)
CFAllocatorDeallocate(NULL, (void *)targetPrivate->name);
- if (targetPrivate->resolvedAddress)
+ if (targetPrivate->resolvedAddress != NULL)
CFRelease(targetPrivate->resolvedAddress);
- if (targetPrivate->localAddress)
+ if (targetPrivate->localAddress != NULL)
CFAllocatorDeallocate(NULL, (void *)targetPrivate->localAddress);
- if (targetPrivate->remoteAddress)
+ if (targetPrivate->remoteAddress != NULL)
CFAllocatorDeallocate(NULL, (void *)targetPrivate->remoteAddress);
- if (targetPrivate->rlsContext.release) {
- targetPrivate->rlsContext.release(targetPrivate->rlsContext.info);
+ if (targetPrivate->rlsContext.release != NULL) {
+ (*targetPrivate->rlsContext.release)(targetPrivate->rlsContext.info);
}
return;
/* initialize runtime */
pthread_once(&initialized, __SCNetworkReachabilityInitialize);
- SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCNetworkReachabilityCreatePrivate:"));
-
/* allocate target */
size = sizeof(SCNetworkReachabilityPrivate) - sizeof(CFRuntimeBase);
targetPrivate = (SCNetworkReachabilityPrivateRef)_CFRuntimeCreateInstance(allocator,
__kSCNetworkReachabilityTypeID,
size,
NULL);
- if (!targetPrivate) {
+ if (targetPrivate == NULL) {
return NULL;
}
targetPrivate->rlsContext.copyDescription = NULL;
targetPrivate->rlList = NULL;
+ targetPrivate->haveDNS = FALSE;
targetPrivate->dnsPort = NULL;
targetPrivate->dnsRLS = NULL;
{
SCNetworkReachabilityPrivateRef targetPrivate;
- if (!address ||
+ if ((address == NULL) ||
(address->sa_len == 0) ||
(address->sa_len > sizeof(struct sockaddr_storage))) {
_SCErrorSet(kSCStatusInvalidArgument);
}
targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
- if (!targetPrivate) {
+ if (targetPrivate == NULL) {
return NULL;
}
return NULL;
}
- if (localAddress) {
+ if (localAddress != NULL) {
if ((localAddress->sa_len == 0) ||
(localAddress->sa_len > sizeof(struct sockaddr_storage))) {
_SCErrorSet(kSCStatusInvalidArgument);
}
}
- if (remoteAddress) {
+ if (remoteAddress != NULL) {
if ((remoteAddress->sa_len == 0) ||
(remoteAddress->sa_len > sizeof(struct sockaddr_storage))) {
_SCErrorSet(kSCStatusInvalidArgument);
}
targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
- if (!targetPrivate) {
+ if (targetPrivate == NULL) {
return NULL;
}
targetPrivate->type = reachabilityTypeAddressPair;
- if (localAddress) {
+ if (localAddress != NULL) {
targetPrivate->localAddress = CFAllocatorAllocate(NULL, localAddress->sa_len, 0);
bcopy(localAddress, targetPrivate->localAddress, localAddress->sa_len);
}
- if (remoteAddress) {
+ if (remoteAddress != NULL) {
targetPrivate->remoteAddress = CFAllocatorAllocate(NULL, remoteAddress->sa_len, 0);
bcopy(remoteAddress, targetPrivate->remoteAddress, remoteAddress->sa_len);
}
SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator,
const char *nodename)
{
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
SCNetworkReachabilityPrivateRef targetPrivate;
- if (!nodename) {
+ if (nodename == NULL) {
_SCErrorSet(kSCStatusInvalidArgument);
return NULL;
}
+ /* check if this "nodename" is really an IP[v6] address in disguise */
+
+ bzero(&sin, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ if (inet_aton(nodename, &sin.sin_addr) == 1) {
+ /* if IPv4 address */
+ return SCNetworkReachabilityCreateWithAddress(allocator, (struct sockaddr *)&sin);
+ }
+
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_family = AF_INET6;
+ if (inet_pton(AF_INET6, nodename, &sin6.sin6_addr) == 1) {
+ /* if IPv6 address */
+ char *p;
+
+ p = strchr(nodename, '%');
+ if (p != NULL) {
+ sin6.sin6_scope_id = if_nametoindex(p+1);
+ }
+
+ return SCNetworkReachabilityCreateWithAddress(allocator, (struct sockaddr *)&sin6);
+ }
+
targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
- if (!targetPrivate) {
+ if (targetPrivate == NULL) {
return NULL;
}
}
if (targetPrivate->resolvedAddress || (targetPrivate->resolvedAddressError != NETDB_SUCCESS)) {
- if (targetPrivate->resolvedAddress) {
+ if (targetPrivate->resolvedAddress != NULL) {
return CFRetain(targetPrivate->resolvedAddress);
} else {
/* if status is known but no resolved addresses to return */
static void
-__SCNetworkReachabilitySetResolvedAddress(SCNetworkReachabilityRef target,
- CFArrayRef addresses,
- int error_num)
+__SCNetworkReachabilitySetResolvedAddress(int32_t status,
+ struct addrinfo *res,
+ SCNetworkReachabilityRef target)
{
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- if (targetPrivate->resolvedAddress) {
- CFRelease(targetPrivate->resolvedAddress);
- }
- targetPrivate->resolvedAddress = addresses ? CFRetain(addresses) : NULL;
- targetPrivate->resolvedAddressError = error_num;
- return;
-}
-
-
-#ifdef CHECK_IPV6_REACHABILITY
-static void
-__SCNetworkReachabilityCallbackSetResolvedAddress(int32_t status, struct addrinfo *res, void *context)
-{
- Boolean ok;
struct addrinfo *resP;
- SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)context;
SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
- ok = (status == 0) && (res != NULL);
+ if (targetPrivate->resolvedAddress != NULL) {
+ CFRelease(targetPrivate->resolvedAddress);
+ targetPrivate->resolvedAddress = NULL;
+ }
- SCLog(_sc_debug, LOG_DEBUG,
- CFSTR("process async DNS complete%s"),
- ok ? "" : ", host not found");
+ if ((status == 0) && (res != NULL)) {
- if (ok) {
CFMutableArrayRef addresses;
addresses = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
}
/* save the resolved address[es] */
- __SCNetworkReachabilitySetResolvedAddress(target, addresses, NETDB_SUCCESS);
- CFRelease(addresses);
+ targetPrivate->resolvedAddress = addresses;
+ targetPrivate->resolvedAddressError = NETDB_SUCCESS;
} else {
SCLog(_sc_debug, LOG_INFO, CFSTR("getaddrinfo() failed: %s"), gai_strerror(status));
/* save the error associated with the attempt to resolve the name */
- __SCNetworkReachabilitySetResolvedAddress(target, (CFArrayRef)kCFNull, status);
+ targetPrivate->resolvedAddress = CFRetain(kCFNull);
+ targetPrivate->resolvedAddressError = status;
}
if (res) freeaddrinfo(res);
- if (targetPrivate->rls) {
+ if (targetPrivate->rls != NULL) {
SCLog(_sc_debug, LOG_INFO, CFSTR("DNS request completed"));
CFRunLoopSourceSignal(targetPrivate->rls);
- __signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
+ _SC_signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
}
return;
}
-#else /* CHECK_IPV6_REACHABILITY */
-static void
-__SCNetworkReachabilityCallbackSetResolvedAddress(struct hostent *h, int error, void *context)
-{
- SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)context;
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- SCLog(_sc_debug, LOG_DEBUG,
- CFSTR("process async DNS complete%s"),
- (h == NULL) ? ", host not found" : "");
- if (h && h->h_length) {
- CFMutableArrayRef addresses;
- union {
- struct sockaddr sa;
- struct sockaddr_in sin;
- struct sockaddr_in6 sin6;
- struct sockaddr_storage ss;
- } addr;
- char **ha = h->h_addr_list;
- addresses = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
-
- bzero(&addr, sizeof(addr));
-
- while (*ha) {
- CFDataRef newAddress;
-
- switch (h->h_length) {
- case sizeof(struct in_addr) :
- addr.sin.sin_family = AF_INET;
- addr.sin.sin_len = sizeof(struct sockaddr_in);
- bcopy(*ha, &addr.sin.sin_addr, h->h_length);
- break;
- case sizeof(struct in6_addr) :
- addr.sin6.sin6_family = AF_INET6;
- addr.sin6.sin6_len = sizeof(struct sockaddr_in6);
- bcopy(*ha, &addr.sin6.sin6_addr, h->h_length);
- break;
- }
-
- newAddress = CFDataCreate(NULL, (void *)&addr, addr.sa.sa_len);
- CFArrayAppendValue(addresses, newAddress);
- CFRelease(newAddress);
-
- ha++;
- }
-
- /* save the resolved address[es] */
- __SCNetworkReachabilitySetResolvedAddress(target, addresses, NETDB_SUCCESS);
- CFRelease(addresses);
- } else {
- SCLog(_sc_debug, LOG_INFO, CFSTR("getipnodebyname() failed: %s"), __netdb_error(error));
-
- /* save the error associated with the attempt to resolve the name */
- __SCNetworkReachabilitySetResolvedAddress(target, (CFArrayRef)kCFNull, error);
- }
+static void
+__SCNetworkReachabilityCallbackSetResolvedAddress(int32_t status, struct addrinfo *res, void *context)
+{
+ SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)context;
+ SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
- if (h) freehostent(h);
-
- if (targetPrivate->rls) {
- SCLog(_sc_debug, LOG_INFO, CFSTR("DNS request completed"));
- CFRunLoopSourceSignal(targetPrivate->rls);
- __signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
- }
+ __log_query_time(((status == 0) && (res != NULL)), // if successful query
+ TRUE, // async
+ &targetPrivate->dnsQueryStart); // start time
+ __SCNetworkReachabilitySetResolvedAddress(status, res, target);
return;
}
-#endif /* CHECK_IPV6_REACHABILITY */
/*
}
-#ifdef CHECK_IPV6_REACHABILITY
static void
getaddrinfo_async_handleCFReply(CFMachPortRef port, void *msg, CFIndex size, void *info)
{
+ int32_t status;
SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
pthread_mutex_lock(&targetPrivate->lock);
- getaddrinfo_async_handle_reply(msg);
+ status = getaddrinfo_async_handle_reply(msg);
+ if ((status == 0) &&
+ (targetPrivate->resolvedAddress == NULL) && (targetPrivate->resolvedAddressError == NETDB_SUCCESS)) {
+ // if request has been re-queued
+ goto again;
+ }
if (port == targetPrivate->dnsPort) {
+ CFRunLoopSourceInvalidate(targetPrivate->dnsRLS);
CFRelease(targetPrivate->dnsRLS);
targetPrivate->dnsRLS = NULL;
CFRelease(targetPrivate->dnsPort);
targetPrivate->dnsPort = NULL;
}
+ again :
+
pthread_mutex_unlock(&targetPrivate->lock);
return;
}
-#else /* CHECK_IPV6_REACHABILITY */
-static void
-getipnodebyname_async_handleCFReply(CFMachPortRef port, void *msg, CFIndex size, void *info)
+
+
+static Boolean
+check_resolver_reachability(SCDynamicStoreRef *storeP,
+ dns_resolver_t *resolver,
+ SCNetworkConnectionFlags *flags,
+ Boolean *haveDNS)
{
- SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
+ int i;
+ Boolean ok = TRUE;
- pthread_mutex_lock(&targetPrivate->lock);
+ *flags = kSCNetworkFlagsReachable;
+ *haveDNS = FALSE;
- getipnodebyname_async_handleReply(msg);
+ for (i = 0; i < resolver->n_nameserver; i++) {
+ struct sockaddr *address = resolver->nameserver[i];
+ SCNetworkConnectionFlags ns_flags = 0;
- if (port == targetPrivate->dnsPort) {
- CFRelease(targetPrivate->dnsRLS);
- targetPrivate->dnsRLS = NULL;
- CFRelease(targetPrivate->dnsPort);
- targetPrivate->dnsPort = NULL;
+ *haveDNS = TRUE;
+
+ if (address->sa_family != AF_INET) {
+ /*
+ * we need to skip non-IPv4 DNS server
+ * addresses (at least until [3510431] has
+ * been resolved).
+ */
+ continue;
+ }
+
+ ok = checkAddress(storeP, address, &ns_flags, NULL);
+ if (!ok) {
+ /* not today */
+ goto done;
+ }
+
+ if (rankReachability(ns_flags) < rankReachability(*flags)) {
+ /* return the worst case result */
+ *flags = ns_flags;
+ }
}
- pthread_mutex_unlock(&targetPrivate->lock);
+ done :
+
+ return ok;
+}
+
+
+static Boolean
+check_matching_resolvers(SCDynamicStoreRef *storeP,
+ dns_config_t *dns_config,
+ const char *fqdn,
+ SCNetworkConnectionFlags *flags,
+ Boolean *haveDNS)
+{
+ int i;
+ Boolean matched = FALSE;
+ const char *name = fqdn;
+
+ while (!matched && (name != NULL)) {
+ int len;
+
+ /*
+ * check if the provided name (or sub-component)
+ * matches one of our resolver configurations.
+ */
+ len = strlen(name);
+ for (i = 0; i < dns_config->n_resolver; i++) {
+ char *domain;
+ dns_resolver_t *resolver;
+
+ resolver = dns_config->resolver[i];
+ domain = resolver->domain;
+ if (domain != NULL && (len == strlen(domain))) {
+ if (strcasecmp(name, domain) == 0) {
+ Boolean ok;
+
+ /*
+ * if name matches domain
+ */
+ matched = TRUE;
+ ok = check_resolver_reachability(storeP, resolver, flags, haveDNS);
+ if (!ok) {
+ /* not today */
+ return FALSE;
+ }
+ }
+ }
+ }
+
+ if (!matched) {
+ /*
+ * we have not found a matching resolver, try
+ * a less qualified domain
+ */
+ name = strchr(name, '.');
+ if ((name != NULL) && (*name != '\0')) {
+ name++;
+ } else {
+ name = NULL;
+ }
+ }
+ }
+
+ return matched;
+}
+
+
+static dns_configuration_t *
+dns_configuration_retain()
+{
+ pthread_mutex_lock(&dns_lock);
+
+ if ((dns_configuration != NULL) && dns_token_valid) {
+ int check = 0;
+ uint32_t status;
+
+ /*
+ * check if the global [DNS] configuration snapshot needs
+ * to be updated
+ */
+ status = notify_check(dns_token, &check);
+ if (status != NOTIFY_STATUS_OK) {
+ SCLog(TRUE, LOG_INFO, CFSTR("notify_check() failed, status=%lu"), status);
+ }
+
+ if ((status != NOTIFY_STATUS_OK) || (check != 0)) {
+ /*
+ * if the snapshot needs to be refreshed
+ */
+ if (dns_configuration->refs == 0) {
+ dns_configuration_free(dns_configuration->config);
+ CFAllocatorDeallocate(NULL, dns_configuration);
+ }
+ dns_configuration = NULL;
+ }
+ }
+
+ if (dns_configuration == NULL) {
+ dns_config_t *new_config;
+ new_config = dns_configuration_copy();
+ if (new_config != NULL) {
+ dns_configuration = CFAllocatorAllocate(NULL, sizeof(dns_configuration_t), 0);
+ dns_configuration->config = new_config;
+ dns_configuration->refs = 0;
+ }
+ }
+
+ if (dns_configuration != NULL) {
+ dns_configuration->refs++;
+ }
+
+ pthread_mutex_unlock(&dns_lock);
+ return dns_configuration;
+}
+
+
+static void
+dns_configuration_release(dns_configuration_t *config)
+{
+ pthread_mutex_lock(&dns_lock);
+
+ config->refs--;
+ if (config->refs == 0) {
+ if ((dns_configuration != config)) {
+ dns_configuration_free(config->config);
+ CFAllocatorDeallocate(NULL, config);
+ }
+ }
+
+ pthread_mutex_unlock(&dns_lock);
return;
}
-#endif /* CHECK_IPV6_REACHABILITY */
static Boolean
-checkResolverReachability(SCDynamicStoreRef *storeP,
- SCNetworkConnectionFlags *flags,
- Boolean *haveDNS)
+dns_configuration_watch()
{
- int i;
- Boolean ok = TRUE;
+ int dns_check = 0;
+ const char *dns_key;
+ Boolean ok = FALSE;
+ uint32_t status;
+
+ pthread_mutex_lock(&dns_lock);
+
+ dns_key = dns_configuration_notify_key();
+ if (dns_key == NULL) {
+ SCLog(TRUE, LOG_INFO, CFSTR("dns_configuration_notify_key() failed"));
+ goto done;
+ }
+
+ status = notify_register_check(dns_key, &dns_token);
+ if (status == NOTIFY_STATUS_OK) {
+ dns_token_valid = TRUE;
+ } else {
+ SCLog(TRUE, LOG_INFO, CFSTR("notify_register_check() failed, status=%lu"), status);
+ goto done;
+ }
+
+ status = notify_check(dns_token, &dns_check);
+ if (status != NOTIFY_STATUS_OK) {
+ SCLog(TRUE, LOG_INFO, CFSTR("notify_check() failed, status=%lu"), status);
+ (void)notify_cancel(dns_token);
+ dns_token_valid = FALSE;
+ goto done;
+ }
+
+ ok = TRUE;
+
+ done :
+
+ pthread_mutex_unlock(&dns_lock);
+ return ok;
+}
+
+
+static void
+dns_configuration_unwatch()
+{
+ pthread_mutex_lock(&dns_lock);
+
+ (void)notify_cancel(dns_token);
+ dns_token_valid = FALSE;
+
+ if ((dns_configuration != NULL) && (dns_configuration->refs == 0)) {
+ dns_configuration_free(dns_configuration->config);
+ CFAllocatorDeallocate(NULL, dns_configuration);
+ dns_configuration = NULL;
+ }
+
+ pthread_mutex_unlock(&dns_lock);
+ return;
+}
+
+
+Boolean
+_SC_checkResolverReachability(SCDynamicStoreRef *storeP,
+ SCNetworkConnectionFlags *flags,
+ Boolean *haveDNS,
+ const char * nodename)
+{
+ dns_resolver_t *default_resolver;
+ dns_configuration_t *dns;
+ Boolean found = FALSE;
+ char *fqdn = (char *)nodename;
+ int i;
+ Boolean isFQDN = FALSE;
+ uint32_t len;
+ Boolean ok = TRUE;
/*
* We first assume that all of the configured DNS servers
*flags = kSCNetworkFlagsReachable;
*haveDNS = FALSE;
- if (needDNS) {
- if (hn_store) {
- /* if we are actively watching at least one host */
- needDNS = FALSE;
- }
- res_init();
+ len = strlen(fqdn);
+ if (len == 0) {
+ // if no nodename, return not reachable
+ *flags = 0;
+ return ok;
}
- for (i = 0; i < _res.nscount; i++) {
- SCNetworkConnectionFlags ns_flags = 0;
+ dns = dns_configuration_retain();
+ if (dns == NULL) {
+ // if error
+ goto done;
+ }
- if (_res.nsaddr_list[i].sin_addr.s_addr == 0) {
- continue;
- }
+ if (dns->config->n_resolver == 0) {
+ // if no resolver configuration
+ goto done;
+ }
- *haveDNS = TRUE;
+ *flags = kSCNetworkFlagsReachable;
- if (_res.nsaddr_list[i].sin_len == 0) {
- _res.nsaddr_list[i].sin_len = sizeof(_res.nsaddr_list[i]);
- }
+ if (fqdn[len - 1] == '.') {
+ isFQDN = TRUE;
- ok = checkAddress(storeP, (struct sockaddr *)&_res.nsaddr_list[i], &ns_flags, NULL);
- if (!ok) {
- /* not today */
- break;
+ // trim trailing '.''s
+ while ((len > 0) && (fqdn[len-1] == '.')) {
+ if (fqdn == nodename) {
+ fqdn = strdup(nodename);
+ }
+ fqdn[--len] = '\0';
}
- if (rankReachability(ns_flags) < rankReachability(*flags)) {
- /* return the worst case result */
- *flags = ns_flags;
+ }
+
+ default_resolver = dns->config->resolver[0];
+
+ /*
+ * try the provided name
+ */
+ found = check_matching_resolvers(storeP, dns->config, fqdn, flags, haveDNS);
+ if (!found && !isFQDN && (dns->config->n_resolver > 1)) {
+ /*
+ * FQDN not specified, try w/search or default domain(s) too
+ */
+ if (default_resolver->n_search > 0) {
+ for (i = 0; !found && (i < default_resolver->n_search); i++) {
+ int ret;
+ char *search_fqdn = NULL;
+
+ ret = asprintf(&search_fqdn, "%s.%s", fqdn, default_resolver->search[i]);
+ if (ret == -1) {
+ continue;
+ }
+
+ // try the provided name with the search domain appended
+ found = check_matching_resolvers(storeP, dns->config, search_fqdn, flags, haveDNS);
+ free(search_fqdn);
+ }
+ } else if (default_resolver->domain != NULL) {
+ char *dp;
+ int domain_parts = 1;
+
+ for (dp = default_resolver->domain; *dp != '\0'; dp++) {
+ if (*dp == '.') {
+ domain_parts++;
+ }
+ }
+
+ dp = default_resolver->domain;
+ for (i = LOCALDOMAINPARTS; !found && (i <= domain_parts); i++) {
+ int ret;
+ char *search_fqdn = NULL;
+
+ ret = asprintf(&search_fqdn, "%s.%s", fqdn, dp);
+ if (ret == -1) {
+ continue;
+ }
+
+ // try the provided name with the [default] domain appended
+ found = check_matching_resolvers(storeP, dns->config, search_fqdn, flags, haveDNS);
+ free(search_fqdn);
+
+ // move to the next component of the [default] domain
+ dp = strchr(dp, '.') + 1;
+ }
}
}
- if (!*haveDNS) {
- /* if no DNS server addresses */
- *flags = 0;
+ if (!found) {
+ /*
+ * check the reachability of the default resolver
+ */
+ ok = check_resolver_reachability(storeP, default_resolver, flags, haveDNS);
+ }
+
+ if (fqdn != nodename) free(fqdn);
+
+ done :
+
+ if (dns != NULL) {
+ dns_configuration_release(dns);
}
return ok;
startAsyncDNSQuery(SCNetworkReachabilityRef target) {
CFMachPortContext context = { 0, (void *)target, CFRetain, CFRelease, CFCopyDescription };
int error;
-#ifdef CHECK_IPV6_REACHABILITY
struct addrinfo hints;
-#endif /* CHECK_IPV6_REACHABILITY */
CFIndex i;
CFIndex n;
mach_port_t port;
SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-#ifdef CHECK_IPV6_REACHABILITY
+ (void) gettimeofday(&targetPrivate->dnsQueryStart, NULL);
+
bzero(&hints, sizeof(hints));
hints.ai_flags = AI_ADDRCONFIG;
+#ifdef AI_PARALLEL
+ hints.ai_flags |= AI_PARALLEL;
+#endif /* AI_PARALLEL */
error = getaddrinfo_async_start(&port,
targetPrivate->name,
(void *)target);
if (error != 0) {
/* save the error associated with the attempt to resolve the name */
- __SCNetworkReachabilitySetResolvedAddress(target, (CFArrayRef)kCFNull, error);
+ __SCNetworkReachabilityCallbackSetResolvedAddress(error, NULL, (void *)target);
return FALSE;
}
getaddrinfo_async_handleCFReply,
&context,
NULL);
-#else /* CHECK_IPV6_REACHABILITY */
- port = getipnodebyname_async_start(targetPrivate->name,
- AF_INET,
- 0,
- &error,
- __SCNetworkReachabilityCallbackSetResolvedAddress,
- (void *)target);
- if (port == MACH_PORT_NULL) {
- /* save the error associated with the attempt to resolve the name */
- __SCNetworkReachabilitySetResolvedAddress(target, (CFArrayRef)kCFNull, error);
- return FALSE;
- }
-
- targetPrivate->dnsPort = CFMachPortCreateWithPort(NULL,
- port,
- getipnodebyname_async_handleCFReply,
- &context,
- NULL);
-#endif /* CHECK_IPV6_REACHABILITY */
-
targetPrivate->dnsRLS = CFMachPortCreateRunLoopSource(NULL, targetPrivate->dnsPort, 0);
n = CFArrayGetCount(targetPrivate->rlList);
Boolean ok = TRUE;
*flags = 0;
- if (if_index) {
+ if (if_index != NULL) {
*if_index = 0;
}
/*
* Check "local" address
*/
- if (targetPrivate->localAddress) {
- /*
- * Check if 0.0.0.0
- */
- if (isAddressZero(targetPrivate->localAddress, &my_flags)) {
- goto checkRemote;
- }
-
+ if (targetPrivate->localAddress != NULL) {
/*
* Check "local" address
*/
}
}
- checkRemote :
-
/*
* Check "remote" address
*/
- if (targetPrivate->remoteAddress) {
+ if (targetPrivate->remoteAddress != NULL) {
/*
* in cases where we have "local" and "remote" addresses
* we need to re-initialize the to-be-returned flags.
my_flags = 0;
my_index = 0;
- /*
- * Check if 0.0.0.0
- */
- if (isAddressZero(targetPrivate->remoteAddress, &my_flags)) {
- break;
- }
-
/*
* Check "remote" address
*/
}
case reachabilityTypeName : {
+ struct timeval dnsQueryStart;
int error;
-#ifndef CHECK_IPV6_REACHABILITY
- struct hostent *h;
-#endif /* CHECK_IPV6_REACHABILITY */
- Boolean haveDNS = FALSE;
-#ifdef CHECK_IPV6_REACHABILITY
struct addrinfo hints;
-#endif /* CHECK_IPV6_REACHABILITY */
SCNetworkConnectionFlags ns_flags;
-#ifdef CHECK_IPV6_REACHABILITY
struct addrinfo *res;
-#endif /* CHECK_IPV6_REACHABILITY */
addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error);
- if (addresses || (error != NETDB_SUCCESS)) {
+ if ((addresses != NULL) || (error != NETDB_SUCCESS)) {
/* if resolved or an error had been detected */
goto checkResolvedAddress;
}
/* check the reachability of the DNS servers */
- ok = checkResolverReachability(storeP, &ns_flags, &haveDNS);\
+ ok = _SC_checkResolverReachability(storeP,
+ &ns_flags,
+ &targetPrivate->haveDNS,
+ targetPrivate->name);
if (!ok) {
/* if we could not get DNS server info */
goto error;
* flags based on the availability of configured (but not
* active) services.
*/
- if (!checkAddressZero(storeP, &my_flags, &my_index)) {
+ if (!checkAddress(storeP, NULL, &my_flags, &my_index)) {
goto error;
}
- if (async && targetPrivate->rls) {
+ if (async && (targetPrivate->rls != NULL)) {
/*
- * return HOST_NOT_FOUND, set flags appropriately,
+ * return "host not found", set flags appropriately,
* and schedule notification.
*/
-#ifdef CHECK_IPV6_REACHABILITY
__SCNetworkReachabilityCallbackSetResolvedAddress(EAI_NODATA,
NULL,
(void *)target);
-#else /* CHECK_IPV6_REACHABILITY */
- __SCNetworkReachabilityCallbackSetResolvedAddress(NULL,
- HOST_NOT_FOUND,
- (void *)target);
-#endif /* CHECK_IPV6_REACHABILITY */
-
my_flags |= (targetPrivate->flags & kSCNetworkFlagsFirstResolvePending);
SCLog(_sc_debug, LOG_INFO, CFSTR("no DNS servers are reachable"));
CFRunLoopSourceSignal(targetPrivate->rls);
- __signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
+ _SC_signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
}
break;
}
* OK, all of the DNS name servers are available. Let's
* resolve the nodename into an address.
*/
-#ifdef CHECK_IPV6_REACHABILITY
+ if (_sc_debug) {
+ (void) gettimeofday(&dnsQueryStart, NULL);
+ }
+
bzero(&hints, sizeof(hints));
hints.ai_flags = AI_ADDRCONFIG;
+#ifdef AI_PARALLEL
+ hints.ai_flags |= AI_PARALLEL;
+#endif /* AI_PARALLEL */
error = getaddrinfo(targetPrivate->name, NULL, &hints, &res);
- __SCNetworkReachabilityCallbackSetResolvedAddress(error, res, (void *)target);
-#else /* CHECK_IPV6_REACHABILITY */
- h = getipnodebyname(targetPrivate->name, AF_INET, 0, &error);
- __SCNetworkReachabilityCallbackSetResolvedAddress(h, error, (void *)target);
-#endif /* CHECK_IPV6_REACHABILITY */
+
+ __log_query_time(((error == 0) && (res != NULL)),// if successful query
+ FALSE, // sync
+ &dnsQueryStart); // start time
+
+ __SCNetworkReachabilitySetResolvedAddress(error, res, target);
addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error);
struct sockaddr *sa;
sa = (struct sockaddr *)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses, i));
+
ok = checkAddress(storeP, sa, &ns_flags, &ns_if_index);
if (!ok) {
goto error; /* not today */
}
}
} else {
- if ((error == HOST_NOT_FOUND) && !haveDNS) {
+ if ((error == EAI_NODATA) && !targetPrivate->haveDNS) {
/*
* No DNS servers are defined. Set flags based on
* the availability of configured (but not active)
* services.
*/
- ok = checkAddressZero(storeP, &my_flags, &my_index);
+ ok = checkAddress(storeP, NULL, &my_flags, &my_index);
if (!ok) {
goto error; /* not today */
}
}
*flags = my_flags;
- if (if_index) {
+ if (if_index != NULL) {
*if_index = my_index;
}
error :
- if (addresses) CFRelease(addresses);
+ if (addresses != NULL) CFRelease(addresses);
return ok;
}
return FALSE;
}
- if (targetPrivate->rlList) {
- /* if being watched, return current (OK, last known) status */
+ if (targetPrivate->rlList != NULL) {
+ // if being watched, return the last known (and what should be current) status
*flags = targetPrivate->flags & ~kSCNetworkFlagsFirstResolvePending;
return TRUE;
}
+
ok = __SCNetworkReachabilityGetFlags(&store, target, flags, NULL, FALSE);
*flags &= ~kSCNetworkFlagsFirstResolvePending;
- if (store) CFRelease(store);
+ if (store != NULL) CFRelease(store);
return ok;
}
static void
__SCNetworkReachabilityReachabilitySetNotifications(SCDynamicStoreRef store)
{
+ CFStringRef dns_key;
CFStringRef key;
CFMutableArrayRef keys;
CFStringRef pattern;
keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
- /*
- * Setup:/Network/Global/IPv4 (for the ServiceOrder)
- */
+ // Setup:/Network/Global/IPv4 (for the ServiceOrder)
key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCEntNetIPv4);
CFArrayAppendValue(keys, key);
CFRelease(key);
- /*
- * State:/Network/Global/DNS
- */
- key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
- kSCDynamicStoreDomainState,
- kSCEntNetDNS);
+ dns_key = CFStringCreateWithCString(NULL,
+ dns_configuration_notify_key(),
+ kCFStringEncodingASCII);
+ key = CFStringCreateWithFormat(NULL, NULL, CFSTR("Notify:%@"), dns_key);
+ CFRelease(dns_key);
CFArrayAppendValue(keys, key);
CFRelease(key);
- /*
- * State:/Network/Global/IPv4
- */
+ // State:/Network/Global/IPv4 (default route)
key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
kSCDynamicStoreDomainState,
kSCEntNetIPv4);
CFArrayAppendValue(keys, key);
CFRelease(key);
- /* Setup: per-service IPv4 info */
+ // Setup: per-service IPv4 info
pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCCompAnyRegex,
CFArrayAppendValue(patterns, pattern);
CFRelease(pattern);
- /* Setup: per-service Interface info */
+ // Setup: per-service Interface info
pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCCompAnyRegex,
CFArrayAppendValue(patterns, pattern);
CFRelease(pattern);
- /* Setup: per-service PPP info */
+ // Setup: per-service PPP info (for kSCPropNetPPPDialOnDemand)
pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCCompAnyRegex,
CFArrayAppendValue(patterns, pattern);
CFRelease(pattern);
- /* State: per-service IPv4 info */
- pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
- kSCDynamicStoreDomainState,
- kSCCompAnyRegex,
- kSCEntNetIPv4);
- CFArrayAppendValue(patterns, pattern);
- CFRelease(pattern);
-
- /* State: per-interface IPv4 info */
+ // State: per-interface IPv4 info
pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainState,
kSCCompAnyRegex,
CFArrayAppendValue(patterns, pattern);
CFRelease(pattern);
- /* State: per-interface IPv6 info */
+ // State: per-interface IPv6 info
pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainState,
kSCCompAnyRegex,
CFArrayRef changedKeys,
void *info)
{
- Boolean dnsChanged = FALSE;
+ Boolean dnsConfigChanged = FALSE;
+ CFStringRef dnsKey;
CFIndex i;
CFStringRef key;
CFIndex nTargets;
const void * targets_q[N_QUICK];
- const void ** targets = targets_q;
+ const void ** targets = targets_q;
pthread_mutex_lock(&hn_lock);
SCLog(_sc_debug, LOG_INFO, CFSTR("process configuration change"));
- key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
- kSCDynamicStoreDomainState,
- kSCEntNetDNS);
+ dnsKey = CFStringCreateWithCString(NULL,
+ dns_configuration_notify_key(),
+ kCFStringEncodingASCII);
+ key = CFStringCreateWithFormat(NULL, NULL, CFSTR("Notify:%@"), dnsKey);
+ CFRelease(dnsKey);
if (CFArrayContainsValue(changedKeys,
CFRangeMake(0, CFArrayGetCount(changedKeys)),
key)) {
- dnsChanged = TRUE; /* the DNS server(s) have changed */
- needDNS = TRUE; /* ... and we need to res_init() on the next query */
- }
- CFRelease(key);
+ dnsConfigChanged = TRUE; /* the DNS server(s) have changed */
- if (!dnsChanged) {
- /*
- * if the DNS configuration didn't change we still need to
- * check that the DNS servers are accessible.
- */
- Boolean haveDNS = FALSE;
- SCNetworkConnectionFlags ns_flags;
- Boolean ok;
-
- /* check the reachability of the DNS servers */
- ok = checkResolverReachability(&store, &ns_flags, &haveDNS);\
- if (!ok || (rankReachability(ns_flags) < 2)) {
- /* if DNS servers are not reachable */
- dnsChanged = TRUE;
- }
}
+ CFRelease(key);
- SCLog(_sc_debug && dnsChanged, LOG_INFO, CFSTR(" DNS changed"));
+ SCLog(_sc_debug && dnsConfigChanged, LOG_INFO, CFSTR(" DNS configuration changed"));
if (nTargets > (CFIndex)(sizeof(targets_q) / sizeof(CFTypeRef)))
targets = CFAllocatorAllocate(NULL, nTargets * sizeof(CFTypeRef), 0);
pthread_mutex_lock(&targetPrivate->lock);
- if (dnsChanged) {
- if (targetPrivate->dnsPort) {
- /* cancel the outstanding DNS query */
-#ifdef CHECK_IPV6_REACHABILITY
- lu_async_call_cancel(CFMachPortGetPort(targetPrivate->dnsPort));
-#else /* CHECK_IPV6_REACHABILITY */
- getipnodebyname_async_cancel(CFMachPortGetPort(targetPrivate->dnsPort));
-#endif /* CHECK_IPV6_REACHABILITY */
- CFRelease(targetPrivate->dnsRLS);
- targetPrivate->dnsRLS = NULL;
- CFRelease(targetPrivate->dnsPort);
- targetPrivate->dnsPort = NULL;
+ if (targetPrivate->type == reachabilityTypeName) {
+ Boolean dnsChanged = dnsConfigChanged;
+
+ if (!dnsChanged) {
+ /*
+ * if the DNS configuration didn't change we still need to
+ * check that the DNS servers are accessible.
+ */
+ SCNetworkConnectionFlags ns_flags;
+ Boolean ok;
+
+ /* check the reachability of the DNS servers */
+ ok = _SC_checkResolverReachability(&store,
+ &ns_flags,
+ &targetPrivate->haveDNS,
+ targetPrivate->name);
+ if (!ok || (rankReachability(ns_flags) < 2)) {
+ /* if DNS servers are not reachable */
+ dnsChanged = TRUE;
+ }
}
- /* schedule request to resolve the name again */
- __SCNetworkReachabilitySetResolvedAddress(target, NULL, NETDB_SUCCESS);
+ if (dnsChanged) {
+ if (targetPrivate->dnsPort) {
+ /* cancel the outstanding DNS query */
+ lu_async_call_cancel(CFMachPortGetPort(targetPrivate->dnsPort));
+ CFRunLoopSourceInvalidate(targetPrivate->dnsRLS);
+ CFRelease(targetPrivate->dnsRLS);
+ targetPrivate->dnsRLS = NULL;
+ CFRelease(targetPrivate->dnsPort);
+ targetPrivate->dnsPort = NULL;
+ }
+
+ /* schedule request to resolve the name again */
+ if (targetPrivate->resolvedAddress != NULL) {
+ CFRelease(targetPrivate->resolvedAddress);
+ targetPrivate->resolvedAddress = NULL;
+ }
+ targetPrivate->resolvedAddress = NULL;
+ targetPrivate->resolvedAddressError = NETDB_SUCCESS;
+ }
}
CFRunLoopSourceSignal(targetPrivate->rls);
- __signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
+ _SC_signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
pthread_mutex_unlock(&targetPrivate->lock);
}
}
-static Boolean
-__isScheduled(CFTypeRef obj, CFRunLoopRef runLoop, CFStringRef runLoopMode, CFMutableArrayRef rlList)
-{
- CFIndex i;
- CFIndex n = CFArrayGetCount(rlList);
-
- for (i = 0; i < n; i += 3) {
- if (obj && !CFEqual(obj, CFArrayGetValueAtIndex(rlList, i))) {
- continue;
- }
- if (runLoop && !CFEqual(runLoop, CFArrayGetValueAtIndex(rlList, i+1))) {
- continue;
- }
- if (runLoopMode && !CFEqual(runLoopMode, CFArrayGetValueAtIndex(rlList, i+2))) {
- continue;
- }
- return TRUE;
- }
-
- return FALSE;
-}
-
-
-static void
-__schedule(CFTypeRef obj, CFRunLoopRef runLoop, CFStringRef runLoopMode, CFMutableArrayRef rlList)
-{
- CFArrayAppendValue(rlList, obj);
- CFArrayAppendValue(rlList, runLoop);
- CFArrayAppendValue(rlList, runLoopMode);
-
- return;
-}
-
-
-static Boolean
-__unschedule(CFTypeRef obj, CFRunLoopRef runLoop, CFStringRef runLoopMode, CFMutableArrayRef rlList, Boolean all)
-{
- CFIndex i = 0;
- Boolean found = FALSE;
- CFIndex n = CFArrayGetCount(rlList);
-
- while (i < n) {
- if (obj && !CFEqual(obj, CFArrayGetValueAtIndex(rlList, i))) {
- i += 3;
- continue;
- }
- if (runLoop && !CFEqual(runLoop, CFArrayGetValueAtIndex(rlList, i+1))) {
- i += 3;
- continue;
- }
- if (runLoopMode && !CFEqual(runLoopMode, CFArrayGetValueAtIndex(rlList, i+2))) {
- i += 3;
- continue;
- }
-
- found = TRUE;
-
- CFArrayRemoveValueAtIndex(rlList, i + 2);
- CFArrayRemoveValueAtIndex(rlList, i + 1);
- CFArrayRemoveValueAtIndex(rlList, i);
-
- if (!all) {
- return found;
- }
-
- n -= 3;
- }
-
- return found;
-}
-
-
static void
rlsPerform(void *info)
{
SCLog(_sc_debug, LOG_DEBUG, CFSTR("process reachability change"));
+
pthread_mutex_lock(&targetPrivate->lock);
/* update reachability, notify if status changed */
ok = __SCNetworkReachabilityGetFlags(&store, target, &flags, &if_index, TRUE);
- if (store) CFRelease(store);
+ if (store != NULL) CFRelease(store);
if (!ok) {
/* if reachability status not available */
flags = 0;
if ((targetPrivate->flags == flags) && (targetPrivate->if_index == if_index)) {
/* if reachability flags and interface have not changed */
pthread_mutex_unlock(&targetPrivate->lock);
- SCLog(_sc_debug, LOG_DEBUG, CFSTR("flags/interface match"));
+ SCLog(_sc_debug, LOG_DEBUG,
+ CFSTR("flags/interface match (now %8.8x/%hu)"),
+ flags, if_index);
return;
+ } else {
+ SCLog(_sc_debug, LOG_DEBUG,
+ CFSTR("flags/interface have changed (was %8.8x/%hu, now %8.8x/%hu)"),
+ targetPrivate->flags, targetPrivate->if_index,
+ flags, if_index);
}
/* update flags / interface */
/* callout */
rlsFunction = targetPrivate->rlsFunction;
- if (NULL != targetPrivate->rlsContext.retain) {
- context_info = (void *)targetPrivate->rlsContext.retain(targetPrivate->rlsContext.info);
+ if (targetPrivate->rlsContext.retain != NULL) {
+ context_info = (void *)(*targetPrivate->rlsContext.retain)(targetPrivate->rlsContext.info);
context_release = targetPrivate->rlsContext.release;
} else {
context_info = targetPrivate->rlsContext.info;
pthread_mutex_unlock(&targetPrivate->lock);
- if (rlsFunction) {
- SCLog(_sc_debug, LOG_DEBUG, CFSTR("flags/interface have changed"));
+ if (rlsFunction != NULL) {
(*rlsFunction)(target, flags, context_info);
}
- if (context_release) {
- context_release(context_info);
+ if (context_release != NULL) {
+ (*context_release)(context_info);
}
return;
pthread_mutex_lock(&targetPrivate->lock);
- if (targetPrivate->rlsContext.release) {
+ if (targetPrivate->rlsContext.release != NULL) {
/* let go of the current context */
- targetPrivate->rlsContext.release(targetPrivate->rlsContext.info);
+ (*targetPrivate->rlsContext.release)(targetPrivate->rlsContext.info);
}
targetPrivate->rlsFunction = callout;
targetPrivate->rlsContext.copyDescription = NULL;
if (context) {
bcopy(context, &targetPrivate->rlsContext, sizeof(SCNetworkReachabilityContext));
- if (context->retain) {
- targetPrivate->rlsContext.info = (void *)context->retain(context->info);
+ if (context->retain != NULL) {
+ targetPrivate->rlsContext.info = (void *)(*context->retain)(context->info);
}
}
pthread_mutex_lock(&hn_lock);
pthread_mutex_lock(&targetPrivate->lock);
- if (!hn_store) {
+ if (hn_store == NULL) {
/*
- * if we are not monitoring any hosts
+ * if we are not monitoring any hosts, start watching
*/
+ if (!dns_configuration_watch()) {
+ // if error
+ _SCErrorSet(kSCStatusFailed);
+ goto done;
+ }
+
hn_store = SCDynamicStoreCreate(NULL,
CFSTR("SCNetworkReachability"),
__SCNetworkReachabilityReachabilityHandleChanges,
NULL);
- if (!hn_store) {
+ if (hn_store == NULL) {
SCLog(_sc_verbose, LOG_INFO, CFSTR("SCDynamicStoreCreate() failed"));
goto done;
}
hn_targets = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
}
- if (!targetPrivate->rls) {
+ if (targetPrivate->rls == NULL) {
CFRunLoopSourceContext context = { 0 // version
, (void *)target // info
, CFRetain // retain
init = TRUE;
}
- if (!__isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
+ if (!_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
/*
* if we do not already have host notifications scheduled with
* this runLoop / runLoopMode
*/
CFRunLoopAddSource(runLoop, targetPrivate->rls, runLoopMode);
- if (targetPrivate->dnsRLS) {
+ if (targetPrivate->dnsRLS != NULL) {
/* if we have an active async DNS query too */
CFRunLoopAddSource(runLoop, targetPrivate->dnsRLS, runLoopMode);
}
}
- __schedule(target, runLoop, runLoopMode, targetPrivate->rlList);
+ _SC_schedule(target, runLoop, runLoopMode, targetPrivate->rlList);
/* schedule the SCNetworkReachability run loop source */
- if (!__isScheduled(NULL, runLoop, runLoopMode, hn_rlList)) {
+ if (!_SC_isScheduled(NULL, runLoop, runLoopMode, hn_rlList)) {
/*
* if we do not already have SC notifications scheduled with
* this runLoop / runLoopMode
CFRunLoopAddSource(runLoop, hn_storeRLS, runLoopMode);
}
- __schedule(target, runLoop, runLoopMode, hn_rlList);
+ _SC_schedule(target, runLoop, runLoopMode, hn_rlList);
CFSetAddValue(hn_targets, target);
if (init) {
targetPrivate->flags = flags;
targetPrivate->if_index = if_index;
CFRunLoopSourceSignal(targetPrivate->rls);
- __signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
+ _SC_signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
} else {
/* if reachability status not available, async lookup started */
targetPrivate->flags = 0;
targetPrivate->if_index = 0;
}
- if (store) CFRelease(store);
+ if (store != NULL) CFRelease(store);
}
ok = TRUE;
pthread_mutex_lock(&hn_lock);
pthread_mutex_lock(&targetPrivate->lock);
- if (!targetPrivate->rls) {
+ if (targetPrivate->rls == NULL) {
/* if not currently scheduled */
goto done;
}
- if (!__unschedule(NULL, runLoop, runLoopMode, targetPrivate->rlList, FALSE)) {
+ if (!_SC_unschedule(NULL, runLoop, runLoopMode, targetPrivate->rlList, FALSE)) {
/* if not currently scheduled */
goto done;
}
n = CFArrayGetCount(targetPrivate->rlList);
- if (n == 0 || !__isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
+ if (n == 0 || !_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
/*
* if this host is no longer scheduled for this runLoop / runLoopMode
*/
CFRunLoopRemoveSource(runLoop, targetPrivate->rls, runLoopMode);
- if (targetPrivate->dnsRLS) {
+ if (targetPrivate->dnsRLS != NULL) {
/* if we have an active async DNS query too */
CFRunLoopRemoveSource(runLoop, targetPrivate->dnsRLS, runLoopMode);
}
/*
* if this host is no longer scheduled
*/
- CFRelease(targetPrivate->rls); /* cleanup SCNetworkReachability resources */
+ CFRunLoopSourceInvalidate(targetPrivate->rls); /* cleanup SCNetworkReachability resources */
+ CFRelease(targetPrivate->rls);
targetPrivate->rls = NULL;
CFRelease(targetPrivate->rlList);
targetPrivate->rlList = NULL;
- CFSetRemoveValue(hn_targets, target); /* cleanup notification resources */
+ CFSetRemoveValue(hn_targets, target); /* cleanup notification resources */
if (targetPrivate->dnsPort) {
/* if we have an active async DNS query too */
-#ifdef CHECK_IPV6_REACHABILITY
lu_async_call_cancel(CFMachPortGetPort(targetPrivate->dnsPort));
-#else /* CHECK_IPV6_REACHABILITY */
- getipnodebyname_async_cancel(CFMachPortGetPort(targetPrivate->dnsPort));
-#endif /* CHECK_IPV6_REACHABILITY */
+ CFRunLoopSourceInvalidate(targetPrivate->dnsRLS);
CFRelease(targetPrivate->dnsRLS);
targetPrivate->dnsRLS = NULL;
CFRelease(targetPrivate->dnsPort);
}
}
- (void)__unschedule(target, runLoop, runLoopMode, hn_rlList, FALSE);
+ (void)_SC_unschedule(target, runLoop, runLoopMode, hn_rlList, FALSE);
n = CFArrayGetCount(hn_rlList);
- if (n == 0 || !__isScheduled(NULL, runLoop, runLoopMode, hn_rlList)) {
+ if (n == 0 || !_SC_isScheduled(NULL, runLoop, runLoopMode, hn_rlList)) {
/*
* if we no longer have any addresses scheduled for
* this runLoop / runLoopMode
* if we are no longer monitoring any addresses
*/
CFRelease(hn_targets);
+ hn_targets = NULL;
CFRelease(hn_rlList);
+ hn_rlList = NULL;
+ CFRunLoopSourceInvalidate(hn_storeRLS);
CFRelease(hn_storeRLS);
+ hn_storeRLS = NULL;
CFRelease(hn_store);
hn_store = NULL;
/*
* until we start monitoring again, ensure that
- * all subsequent reachability-by-name checks
- * call res_init()
+ * any resources associated with tracking the
+ * DNS configuration have been released.
*/
- needDNS = TRUE;
+ dns_configuration_unwatch();
}
}