/*
- * Copyright (c) 2003-2011 Apple Inc. All rights reserved.
+ * Copyright (c) 2003-2012 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
/*
* Modification History
*
+ * April 12, 2011 Allan Nathanson <ajn@apple.com>
+ * - add SCNetworkReachability "server"
+ *
* March 31, 2004 Allan Nathanson <ajn@apple.com>
* - use [SC] DNS configuration information
*
#include <TargetConditionals.h>
#include <sys/cdefs.h>
#include <dispatch/dispatch.h>
+#include <dispatch/private.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFRuntime.h>
#include <SystemConfiguration/SystemConfiguration.h>
#define s6_addr16 __u6_addr.__u6_addr16
#endif
+#include "SCNetworkReachabilityInternal.h"
+
#include <ppp/ppp_msg.h>
#if !TARGET_IPHONE_SIMULATOR
+#define DEBUG_REACHABILITY_TYPE_NAME "create w/name"
+#define DEBUG_REACHABILITY_TYPE_NAME_OPTIONS " + options"
+
+#define DEBUG_REACHABILITY_TYPE_ADDRESS "create w/address"
+#define DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS " + options"
+
+#define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR "create w/address pair"
+#define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS " + options"
+
+
+static pthread_mutexattr_t lock_attr;
+
+#define MUTEX_INIT(m) { \
+ int _lock_ = (pthread_mutex_init(m, &lock_attr) == 0); \
+ assert(_lock_); \
+}
+
+#define MUTEX_LOCK(m) { \
+ int _lock_ = (pthread_mutex_lock(m) == 0); \
+ assert(_lock_); \
+}
+
+#define MUTEX_UNLOCK(m) { \
+ int _unlock_ = (pthread_mutex_unlock(m) == 0); \
+ assert(_unlock_); \
+}
+
+#define MUTEX_ASSERT_HELD(m) { \
+ int _locked_ = (pthread_mutex_lock(m) == EDEADLK); \
+ assert(_locked_); \
+}
+
+
#ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
/* Libinfo SPI */
mach_port_t
#endif /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */
-#define kSCNetworkReachabilityFlagsFirstResolvePending (1<<31)
+#define SCNETWORKREACHABILITY_TRIGGER_KEY CFSTR("com.apple.SCNetworkReachability:FORCE-CHANGE")
-#define N_QUICK 64
-
-
-typedef enum { NO = 0, YES, UNKNOWN } lazyBoolean;
+// how long (minimum time, us) to wait before retrying DNS query after EAI_NONAME
+#define EAI_NONAME_RETRY_DELAY_USEC 250000 // 250ms
+// how long (maximum time, us) after DNS configuration change we accept EAI_NONAME
+// without question.
+#define EAI_NONAME_RETRY_LIMIT_USEC 2500000 // 2.5s
-typedef enum {
- reachabilityTypeAddress,
- reachabilityTypeAddressPair,
- reachabilityTypeName
-} addressType;
+// how long (maximum time, ns) to wait for a long-lived-query callback before
+// we assume EAI_NONAME.
+#define LLQ_TIMEOUT_NSEC 30 * NSEC_PER_SEC // 30s
-// how long (minimum time, us) to wait before retrying DNS query after EAI_NONAME
-#define EAI_NONAME_RETRY_DELAY_USEC 250000
-// how long (maximum time, us) after DNS configuration change we accept EAI_NONAME
-// without question.
-#define EAI_NONAME_RETRY_LIMIT_USEC 2500000
+#define N_QUICK 64
static CFStringRef __SCNetworkReachabilityCopyDescription (CFTypeRef cf);
static void __SCNetworkReachabilityDeallocate (CFTypeRef cf);
-static void rlsPerform(void *info);
+static void reachPerform (void *info);
static Boolean
Boolean onDemand);
-typedef struct {
- SCNetworkReachabilityFlags flags;
- unsigned int if_index;
- Boolean sleeping;
-} ReachabilityInfo;
-
-
-typedef struct {
-
- /* base CFType information */
- CFRuntimeBase cfBase;
-
- /* lock */
- pthread_mutex_t lock;
-
- /* address type */
- addressType type;
-
- /* target host name */
- const char *name;
- const char *serv;
- struct addrinfo hints;
- Boolean needResolve;
- CFArrayRef resolvedAddress; /* CFArray[CFData] */
- int resolvedAddressError;
-
- /* [scoped routing] interface constraints */
- unsigned int if_index;
- char if_name[IFNAMSIZ];
-
- /* local & remote addresses */
- struct sockaddr *localAddress;
- struct sockaddr *remoteAddress;
-
- /* current reachability flags */
- ReachabilityInfo info;
- ReachabilityInfo last_notify;
-
- /* run loop source, callout, context, rl scheduling info */
- Boolean scheduled;
- CFRunLoopSourceRef rls;
- SCNetworkReachabilityCallBack rlsFunction;
- SCNetworkReachabilityContext rlsContext;
- CFMutableArrayRef rlList;
-
- dispatch_queue_t dispatchQueue; // SCNetworkReachabilitySetDispatchQueue
- dispatch_queue_t asyncDNSQueue;
- dispatch_source_t asyncDNSSource;
-
- /* [async] DNS query info */
- Boolean haveDNS;
- mach_port_t dnsMP;
- CFMachPortRef dnsPort;
- CFRunLoopSourceRef dnsRLS;
- struct timeval dnsQueryStart;
- struct timeval dnsQueryEnd;
- dispatch_source_t dnsRetry; // != NULL if DNS retry request queued
- int dnsRetryCount; // number of retry attempts
-
- /* [async] processing info */
- struct timeval last_dns;
-
- /* on demand info */
- Boolean onDemandBypass;
- CFStringRef onDemandName;
- CFStringRef onDemandRemoteAddress;
- SCNetworkReachabilityRef onDemandServer;
- CFStringRef onDemandServiceID;
-
-
- /* logging */
- char log_prefix[32];
-
-} SCNetworkReachabilityPrivate, *SCNetworkReachabilityPrivateRef;
-
-
static CFTypeID __kSCNetworkReachabilityTypeID = _kCFRuntimeNotATypeID;
static pthread_once_t initialized = PTHREAD_ONCE_INIT;
-static const ReachabilityInfo NOT_REACHABLE = { 0, 0, FALSE };
-static const ReachabilityInfo NOT_REPORTED = { 0xFFFFFFFF, 0, FALSE };
+static const ReachabilityInfo NOT_REACHABLE = { 0, 0, 0, { 0 }, FALSE };
+static const ReachabilityInfo NOT_REPORTED = { 0, 0xFFFFFFFF, 0, { 0 }, FALSE };
static int rtm_seq = 0;
+static const struct addrinfo HINTS_DEFAULT = {
+#ifdef AI_PARALLEL
+ .ai_flags = AI_ADDRCONFIG | AI_PARALLEL,
+#else // AI_PARALLEL
+ .ai_flags = AI_ADDRCONFIG,
+#endif // AI_PARALLEL
+};
+
+
static const struct timeval TIME_ZERO = { 0, 0 };
+static Boolean D_llqBypass = FALSE;
+static int llqCount = 0;
+static DNSServiceRef llqMain = NULL;
+static CFMutableSetRef llqUpdated = NULL;
+
+
+#ifdef HAVE_REACHABILITY_SERVER
+static Boolean D_serverBypass = FALSE;
+#endif // HAVE_REACHABILITY_SERVER
+
+
#if !TARGET_OS_IPHONE
/*
* Power capabilities (sleep/wake)
* host "something has changed" notifications
*/
-static pthread_mutex_t hn_lock = PTHREAD_MUTEX_INITIALIZER;
static SCDynamicStoreRef hn_store = NULL;
static dispatch_queue_t hn_dispatchQueue = NULL;
static CFMutableSetRef hn_targets = NULL;
static Boolean dns_token_valid = FALSE;
-static __inline__ CFTypeRef
-isA_SCNetworkReachability(CFTypeRef obj)
-{
- return (isA_CFType(obj, SCNetworkReachabilityGetTypeID()));
-}
+typedef enum {
+ dns_query_sync,
+ dns_query_async,
+ dns_query_llq
+} dns_query_type;
static void
static void
__dns_query_end(SCNetworkReachabilityRef target,
Boolean found,
- Boolean async,
+ dns_query_type query_type,
struct timeval *dnsQueryStart,
struct timeval *dnsQueryEnd)
{
struct timeval dnsQueryElapsed;
+ Boolean firstQuery;
SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
+ // report initial or updated query time
+ firstQuery = !timerisset(dnsQueryEnd);
+
(void) gettimeofday(dnsQueryEnd, NULL);
if (!_sc_debug) {
return;
}
- if (dnsQueryStart->tv_sec == 0) {
+ if (!timerisset(dnsQueryStart)) {
return;
}
timersub(dnsQueryEnd, dnsQueryStart, &dnsQueryElapsed);
- SCLog(TRUE, LOG_INFO,
- CFSTR("%s%ssync DNS complete%s (query time = %d.%3.3d)"),
- targetPrivate->log_prefix,
- async ? "a" : "",
- found ? "" : ", host not found",
- dnsQueryElapsed.tv_sec,
- dnsQueryElapsed.tv_usec / 1000);
+ switch (query_type) {
+
+// #define QUERY_TIME__FMT "%d.%3.3d"
+// #define QUERY_TIME__DIV 1000
+
+ #define QUERY_TIME__FMT "%d.%6.6d"
+ #define QUERY_TIME__DIV 1
+
+ case dns_query_sync :
+ SCLog(TRUE, LOG_INFO,
+ CFSTR("%ssync DNS complete%s (query time = " QUERY_TIME__FMT ")"),
+ targetPrivate->log_prefix,
+ found ? "" : ", host not found",
+ dnsQueryElapsed.tv_sec,
+ dnsQueryElapsed.tv_usec / QUERY_TIME__DIV);
+ break;
+ case dns_query_async :
+ SCLog(TRUE, LOG_INFO,
+ CFSTR("%sasync DNS complete%s (query time = " QUERY_TIME__FMT ")"),
+ targetPrivate->log_prefix,
+ found ? "" : ", host not found",
+ dnsQueryElapsed.tv_sec,
+ dnsQueryElapsed.tv_usec / QUERY_TIME__DIV);
+ break;
+ case dns_query_llq :
+ SCLog(TRUE, LOG_INFO,
+ CFSTR("%sDNS updated%s (%s = " QUERY_TIME__FMT ")"),
+ targetPrivate->log_prefix,
+ found ? "" : ", host not found",
+ firstQuery ? "query time" : "updated after",
+ dnsQueryElapsed.tv_sec,
+ dnsQueryElapsed.tv_usec / QUERY_TIME__DIV);
+ break;
+ }
return;
}
static __inline__ Boolean
-__reach_equal(ReachabilityInfo *r1, ReachabilityInfo *r2)
+__reach_changed(ReachabilityInfo *r1, ReachabilityInfo *r2)
{
if (r1->flags != r2->flags) {
// if the reachability flags changed
- return FALSE;
+ return TRUE;
}
if (r1->if_index != r2->if_index) {
// if the target interface changed
- return FALSE;
+ return TRUE;
}
if ((r1->sleeping != r2->sleeping) && !r2->sleeping) {
// if our sleep/wake status changed and if we
// are no longer sleeping
- return FALSE;
+ return TRUE;
}
- return TRUE;
+ return FALSE;
+}
+
+
+static __inline__ void
+_reach_set(ReachabilityInfo *dst, const ReachabilityInfo *src, uint64_t cycle)
+{
+ memcpy(dst, src, sizeof(ReachabilityInfo));
+ dst->cycle = cycle;
+
+ return;
}
+#pragma mark -
+#pragma mark SCDynamicStore info
+
+
typedef struct {
SCDynamicStoreRef store;
- Boolean storeAdded;
CFStringRef entity;
CFDictionaryRef dict;
CFIndex n;
} ReachabilityStoreInfo, *ReachabilityStoreInfoRef;
+static ReachabilityStoreInfo S_storeInfo = { 0 };
+static Boolean S_storeInfoActive = FALSE;
+
+
+static dispatch_queue_t
+_storeInfo_queue()
+{
+ static dispatch_once_t once;
+ static dispatch_queue_t q;
+
+ dispatch_once(&once, ^{
+ q = dispatch_queue_create("SCNetworkReachabilty.storeInfo", NULL);
+ });
+
+ return q;
+}
+
+
static void
-initReachabilityStoreInfo(ReachabilityStoreInfoRef store_info)
+ReachabilityStoreInfo_copy(ReachabilityStoreInfoRef src,
+ ReachabilityStoreInfoRef dst)
{
- bzero(store_info, sizeof(ReachabilityStoreInfo));
+ if (src->dict != NULL) {
+ dst->store = src->store;
+ CFRetain(dst->store);
+
+ dst->dict = src->dict;
+ CFRetain(dst->dict);
+
+ dst->n = src->n;
+ if (dst->n > 0) {
+ if (dst->n <= (CFIndex)(sizeof(dst->keys_q) / sizeof(CFTypeRef))) {
+ dst->keys = dst->keys_q;
+ dst->values = dst->values_q;
+ } else {
+ dst->keys = CFAllocatorAllocate(NULL, dst->n * sizeof(CFTypeRef), 0);
+ dst->values = CFAllocatorAllocate(NULL, dst->n * sizeof(CFTypeRef), 0);
+ }
+ memcpy(dst->keys, src->keys, dst->n * sizeof(CFTypeRef));
+ memcpy(dst->values, src->values, dst->n * sizeof(CFTypeRef));
+ }
+ }
+
return;
}
-static Boolean
-updateReachabilityStoreInfo(ReachabilityStoreInfoRef store_info,
- SCDynamicStoreRef *storeP,
- sa_family_t sa_family)
+static void
+ReachabilityStoreInfo_enable(Boolean enable)
{
- CFStringRef pattern;
- CFMutableArrayRef patterns;
+ dispatch_sync(_storeInfo_queue(), ^{
+ S_storeInfoActive = enable;
+ });
- switch (sa_family) {
- case AF_UNSPEC :
- store_info->entity = NULL;
- break;
- case AF_INET :
- store_info->entity = kSCEntNetIPv4;
- break;
- case AF_INET6 :
- store_info->entity = kSCEntNetIPv6;
- break;
- default :
- return FALSE;
+ return;
+}
+
+
+static void
+ReachabilityStoreInfo_free(ReachabilityStoreInfoRef store_info)
+{
+ if ((store_info->n > 0) && (store_info->keys != store_info->keys_q)) {
+ CFAllocatorDeallocate(NULL, store_info->keys);
+ store_info->keys = NULL;
+
+ CFAllocatorDeallocate(NULL, store_info->values);
+ store_info->values = NULL;
}
+ store_info->n = 0;
if (store_info->dict != NULL) {
- // if info already available
- return TRUE;
+ CFRelease(store_info->dict);
+ store_info->dict = NULL;
}
- if (store_info->store == NULL) {
- store_info->store = (storeP != NULL) ? *storeP : NULL;
- if (store_info->store == NULL) {
- store_info->store = SCDynamicStoreCreate(NULL, CFSTR("SCNetworkReachability"), NULL, NULL);
- if (store_info->store == NULL) {
- SCLog(TRUE, LOG_ERR, CFSTR("updateReachabilityStoreInfo SCDynamicStoreCreate() failed"));
- return FALSE;
- }
+ if (store_info->store != NULL) {
+ CFRelease(store_info->store);
+ store_info->store = NULL;
+ }
- if (storeP != NULL) {
- /// pass back the allocated SCDynamicStoreRef
- *storeP = store_info->store;
- } else {
- // this one is ours
- store_info->storeAdded = TRUE;
+ return;
+}
+
+
+static void
+ReachabilityStoreInfo_init(ReachabilityStoreInfoRef store_info)
+{
+ dispatch_sync(_storeInfo_queue(), ^{
+ bzero(store_info, sizeof(ReachabilityStoreInfo));
+
+ if (S_storeInfoActive && (S_storeInfo.dict != NULL)) {
+ ReachabilityStoreInfo_copy(&S_storeInfo, store_info);
+ }
+ });
+
+ return;
+}
+
+
+static void
+ReachabilityStoreInfo_save(ReachabilityStoreInfoRef store_info)
+{
+ dispatch_sync(_storeInfo_queue(), ^{
+ if ((store_info == NULL) ||
+ !_SC_CFEqual(store_info->dict, S_storeInfo.dict)) {
+ // free any old info
+ ReachabilityStoreInfo_free(&S_storeInfo);
+
+ // save new info
+ if (S_storeInfoActive &&
+ (store_info != NULL) &&
+ (store_info->dict != NULL)) {
+ ReachabilityStoreInfo_copy(store_info, &S_storeInfo);
}
}
- }
+ });
- if (sa_family == AF_UNSPEC) {
- // if the address family was not specified than
- // all we wanted, for now, was to establish the
- // SCDynamicStore session
- return TRUE;
- }
+ return;
+}
+
+
+static Boolean
+ReachabilityStoreInfo_fill(ReachabilityStoreInfoRef store_info)
+{
+ CFStringRef pattern;
+ CFMutableArrayRef patterns;
patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
}
-static void
-freeReachabilityStoreInfo(ReachabilityStoreInfoRef store_info)
+static Boolean
+ReachabilityStoreInfo_update(ReachabilityStoreInfoRef store_info,
+ SCDynamicStoreRef *storeP,
+ sa_family_t sa_family)
{
- if ((store_info->n > 0) && (store_info->keys != store_info->keys_q)) {
- CFAllocatorDeallocate(NULL, store_info->keys);
- store_info->keys = NULL;
+ __block Boolean ok = TRUE;
- CFAllocatorDeallocate(NULL, store_info->values);
- store_info->values = NULL;
+ switch (sa_family) {
+ case AF_UNSPEC :
+ store_info->entity = NULL;
+ break;
+ case AF_INET :
+ store_info->entity = kSCEntNetIPv4;
+ break;
+ case AF_INET6 :
+ store_info->entity = kSCEntNetIPv6;
+ break;
+ default :
+ return FALSE;
}
if (store_info->dict != NULL) {
- CFRelease(store_info->dict);
- store_info->dict = NULL;
+ // if info already available
+ return TRUE;
}
- if (store_info->storeAdded && (store_info->store != NULL)) {
- CFRelease(store_info->store);
- store_info->store = NULL;
- }
+ dispatch_sync(_storeInfo_queue(), ^{
+ if (S_storeInfoActive && (S_storeInfo.dict != NULL)) {
+ // free any info
+ ReachabilityStoreInfo_free(store_info);
- return;
+ // copy the shared/available info
+ ReachabilityStoreInfo_copy(&S_storeInfo, store_info);
+ }
+
+ if (store_info->store == NULL) {
+ store_info->store = (storeP != NULL) ? *storeP : NULL;
+ if (store_info->store != NULL) {
+ // keep a reference to the passed in SCDynamicStore
+ CFRetain(store_info->store);
+ } else {
+ store_info->store = SCDynamicStoreCreate(NULL, CFSTR("SCNetworkReachability"), NULL, NULL);
+ if (store_info->store == NULL) {
+ SCLog(TRUE, LOG_ERR, CFSTR("ReachabilityStoreInfo_update SCDynamicStoreCreate() failed"));
+ return;
+ }
+
+ if (storeP != NULL) {
+ // and pass back a reference
+ *storeP = store_info->store;
+ CFRetain(*storeP);
+ }
+ }
+ }
+
+ if (sa_family == AF_UNSPEC) {
+ // if the address family was not specified than
+ // all we wanted, for now, was to establish the
+ // SCDynamicStore session
+ return;
+ }
+
+ if (store_info->dict != NULL) {
+ // or we have picked up the shared info
+ return;
+ }
+
+ ok = ReachabilityStoreInfo_fill(store_info);
+ if (!ok) {
+ return;
+ }
+
+ if (!_SC_CFEqual(store_info->dict, S_storeInfo.dict)) {
+ // free any old info
+ ReachabilityStoreInfo_free(&S_storeInfo);
+
+ // save new info
+ if (S_storeInfoActive &&
+ (store_info->dict != NULL)) {
+ ReachabilityStoreInfo_copy(store_info, &S_storeInfo);
+ }
+ }
+ });
+
+ return ok;
}
+#pragma mark -
+#pragma mark PPP info
+
+
static int
updatePPPStatus(ReachabilityStoreInfoRef store_info,
const struct sockaddr *sa,
CFStringRef ppp_if;
int sc_status = kSCStatusNoKey;
- if (!updateReachabilityStoreInfo(store_info, NULL, sa->sa_family)) {
+ if (!ReachabilityStoreInfo_update(store_info, NULL, sa->sa_family)) {
return kSCStatusReachabilityUnknown;
}
CFIndex i;
int sc_status = kSCStatusNoKey;
- if (!updateReachabilityStoreInfo(store_info,
+ if (!ReachabilityStoreInfo_update(store_info,
NULL,
(sa != NULL) ? sa->sa_family : AF_INET)) {
return kSCStatusReachabilityUnknown;
}
+#pragma mark -
+#pragma mark VPN info
+
+
#if !TARGET_IPHONE_SIMULATOR
static int
updateVPNStatus(ReachabilityStoreInfoRef store_info,
CFStringRef vpn_if;
int sc_status = kSCStatusNoKey;
- if (!updateReachabilityStoreInfo(store_info, NULL, sa->sa_family)) {
+ if (!ReachabilityStoreInfo_update(store_info, NULL, sa->sa_family)) {
return kSCStatusReachabilityUnknown;
}
CFIndex i;
int sc_status = kSCStatusNoKey;
- if (!updateReachabilityStoreInfo(store_info,
+ if (!ReachabilityStoreInfo_update(store_info,
NULL,
(sa != NULL) ? sa->sa_family : AF_INET)) {
return kSCStatusReachabilityUnknown;
#endif // !TARGET_IPHONE_SIMULATOR
+#pragma mark -
+#pragma mark IPSec info
+
+
static int
updateIPSecStatus(ReachabilityStoreInfoRef store_info,
const struct sockaddr *sa,
CFStringRef ipsec_if;
int sc_status = kSCStatusNoKey;
- if (!updateReachabilityStoreInfo(store_info, NULL, sa->sa_family)) {
+ if (!ReachabilityStoreInfo_update(store_info, NULL, sa->sa_family)) {
return kSCStatusReachabilityUnknown;
}
+#pragma mark -
+#pragma mark Reachability engine
+
+
#define ROUNDUP(a, size) \
(((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
typedef struct {
- char buf[BUFLEN];
+ union {
+ char bytes[BUFLEN];
+ struct rt_msghdr rtm;
+ } buf;
int error;
struct sockaddr *rti_info[RTAX_MAX];
struct rt_msghdr *rtm;
bzero(info, sizeof(*info));
- info->rtm = (struct rt_msghdr *)&info->buf;
+ info->rtm = &info->buf.rtm;
info->rtm->rtm_msglen = sizeof(struct rt_msghdr);
info->rtm->rtm_version = RTM_VERSION;
#ifdef RTM_GET_SILENT
case AF_INET6: {
struct sockaddr_in6 *sin6;
- sin6 = (struct sockaddr_in6 *)address;
+ /* ALIGN: caller ensures that the address is aligned */
+ sin6 = (struct sockaddr_in6 *)(void *)address;
if ((IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) ||
IN6_IS_ADDR_MC_LINKLOCAL(&sin6->sin6_addr)) &&
(sin6->sin6_scope_id != 0)) {
while (TRUE) {
int n;
- n = read(rsock, (void *)&info->buf, sizeof(info->buf));
+ n = read(rsock, &info->buf, sizeof(info->buf));
if (n == -1) {
int error = errno;
return EINVAL;
}
- info->sdl = (struct sockaddr_dl *) info->rti_info[RTAX_IFP];
+ /* ALIGN: accessors are retrieving byte values, cast ok. */
+ info->sdl = (struct sockaddr_dl *)(void *) info->rti_info[RTAX_IFP];
if ((info->sdl->sdl_nlen == 0) || (info->sdl->sdl_nlen > IFNAMSIZ)) {
/* no interface name */
return EHOSTUNREACH;
char *statusMessage = NULL;
struct sockaddr_in v4mapped;
- *reach_info = NOT_REACHABLE;
+ _reach_set(reach_info, &NOT_REACHABLE, reach_info->cycle);
if (address == NULL) {
/* special case: check only for available paths off the system */
}
if (address->sa_family == AF_INET6) {
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)address;
+ /* ALIGN: sin6_addr accessed aligned, cast ok. */
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)(void *)address;
if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
bzero(&v4mapped, sizeof(v4mapped));
switch (address->sa_family) {
case AF_INET :
- addr1 = &((struct sockaddr_in *)address)->sin_addr;
- addr2 = &((struct sockaddr_in *)info.rti_info[RTAX_IFA])->sin_addr;
+ /* ALIGN: cast ok, because only bcmp is used. */
+ addr1 = &((struct sockaddr_in *)(void *)address)->sin_addr;
+ addr2 = &((struct sockaddr_in *)(void *)info.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) {
+ /* ALIGN: sin_addr should be aligned, cast ok. */
+ if (((struct sockaddr_in *)(void *)address)->sin_addr.s_addr == 0) {
statusMessage = "isReachable (this host)";
reach_info->flags |= kSCNetworkReachabilityFlagsIsLocalAddress;
}
break;
case AF_INET6 :
- addr1 = &((struct sockaddr_in6 *)address)->sin6_addr;
- addr2 = &((struct sockaddr_in6 *)info.rti_info[RTAX_IFA])->sin6_addr;
+ /* ALIGN: cast ok, because only bcmp is used. */
+ addr1 = &((struct sockaddr_in6 *)(void *)address)->sin6_addr;
+ addr2 = &((struct sockaddr_in6 *)(void *)info.rti_info[RTAX_IFA])->sin6_addr;
len = sizeof(struct in6_addr);
break;
default :
if_name,
(info.sdl->sdl_nlen <= IFNAMSIZ) ? info.sdl->sdl_nlen : IFNAMSIZ);
+ strlcpy(reach_info->if_name, if_name, sizeof(reach_info->if_name));
reach_info->if_index = info.sdl->sdl_index;
if (_sc_debug) {
#pragma mark SCNetworkReachability APIs
-static CFStringRef
-__SCNetworkReachabilityCopyDescription(CFTypeRef cf)
+static __inline__ CFTypeRef
+isA_SCNetworkReachability(CFTypeRef obj)
{
- CFAllocatorRef allocator = CFGetAllocator(cf);
- CFMutableStringRef result;
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)cf;
+ return (isA_CFType(obj, SCNetworkReachabilityGetTypeID()));
+}
- result = CFStringCreateMutable(allocator, 0);
- CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkReachability %p [%p]> {"), cf, allocator);
+
+CFStringRef
+_SCNetworkReachabilityCopyTargetDescription(SCNetworkReachabilityRef target)
+{
+ CFAllocatorRef allocator = CFGetAllocator(target);
+ CFMutableStringRef str;
+ SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
+
+ str = CFStringCreateMutable(allocator, 0);
switch (targetPrivate->type) {
case reachabilityTypeAddress :
case reachabilityTypeAddressPair : {
if (targetPrivate->localAddress != NULL) {
_SC_sockaddr_to_string(targetPrivate->localAddress, buf, sizeof(buf));
- CFStringAppendFormat(result, NULL, CFSTR("local address = %s"),
+ CFStringAppendFormat(str, NULL, CFSTR("local address = %s"),
buf);
}
if (targetPrivate->remoteAddress != NULL) {
_SC_sockaddr_to_string(targetPrivate->remoteAddress, buf, sizeof(buf));
- CFStringAppendFormat(result, NULL, CFSTR("%s%saddress = %s"),
+ CFStringAppendFormat(str, NULL, CFSTR("%s%saddress = %s"),
targetPrivate->localAddress ? ", " : "",
(targetPrivate->type == reachabilityTypeAddressPair) ? "remote " : "",
buf);
}
case reachabilityTypeName : {
if ((targetPrivate->name != NULL)) {
- CFStringAppendFormat(result, NULL, CFSTR("name = %s"), targetPrivate->name);
+ CFStringAppendFormat(str, NULL, CFSTR("name = %s"), targetPrivate->name);
}
if ((targetPrivate->serv != NULL)) {
- CFStringAppendFormat(result, NULL, CFSTR("%sserv = %s"),
+ CFStringAppendFormat(str, NULL, CFSTR("%sserv = %s"),
targetPrivate->name != NULL ? ", " : "",
targetPrivate->serv);
}
- if ((targetPrivate->resolvedAddress != NULL) || (targetPrivate->resolvedAddressError != NETDB_SUCCESS)) {
- if (targetPrivate->resolvedAddress != NULL) {
- if (isA_CFArray(targetPrivate->resolvedAddress)) {
- CFIndex i;
- CFIndex n = CFArrayGetCount(targetPrivate->resolvedAddress);
-
- CFStringAppendFormat(result, NULL, CFSTR(" ("));
- for (i = 0; i < n; i++) {
- CFDataRef address;
- char buf[64];
- struct sockaddr *sa;
-
- address = CFArrayGetValueAtIndex(targetPrivate->resolvedAddress, i);
- sa = (struct sockaddr *)CFDataGetBytePtr(address);
- _SC_sockaddr_to_string(sa, buf, sizeof(buf));
- CFStringAppendFormat(result, NULL, CFSTR("%s%s"),
- i > 0 ? ", " : "",
- buf);
- }
- CFStringAppendFormat(result, NULL, CFSTR(")"));
- } else {
- CFStringAppendFormat(result, NULL, CFSTR(" (no addresses)"));
+ break;
+ }
+ }
+
+ return str;
+}
+
+
+CFStringRef
+_SCNetworkReachabilityCopyTargetFlags(SCNetworkReachabilityRef target)
+{
+ CFAllocatorRef allocator = CFGetAllocator(target);
+ CFStringRef str;
+ SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
+
+ str = CFStringCreateWithFormat(allocator,
+ NULL,
+ CFSTR("flags = 0x%08x, if_index = %hu%s"),
+ targetPrivate->info.flags,
+ targetPrivate->info.if_index,
+ targetPrivate->info.sleeping ? ", z" : "");
+ return str;
+}
+
+
+static CFStringRef
+__SCNetworkReachabilityCopyDescription(CFTypeRef cf)
+{
+ CFAllocatorRef allocator = CFGetAllocator(cf);
+ CFMutableStringRef result;
+ CFStringRef str;
+ SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)cf;
+ SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
+
+ result = CFStringCreateMutable(allocator, 0);
+ CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkReachability %p [%p]> {"), cf, allocator);
+
+ // add target description
+ str = _SCNetworkReachabilityCopyTargetDescription(target);
+ CFStringAppend(result, str);
+ CFRelease(str);
+
+ // add additional "name" info
+ if (targetPrivate->type == reachabilityTypeName) {
+ if (targetPrivate->dnsMP != MACH_PORT_NULL) {
+ CFStringAppendFormat(result, NULL, CFSTR(" (DNS query active)"));
+ } else if (targetPrivate->dnsRetry != NULL) {
+ CFStringAppendFormat(result, NULL, CFSTR(" (DNS retry queued)"));
+ } else if ((targetPrivate->resolvedAddress != NULL) || (targetPrivate->resolvedAddressError != NETDB_SUCCESS)) {
+ if (targetPrivate->resolvedAddress != NULL) {
+ if (isA_CFArray(targetPrivate->resolvedAddress)) {
+ CFIndex i;
+ CFIndex n = CFArrayGetCount(targetPrivate->resolvedAddress);
+
+ CFStringAppendFormat(result, NULL, CFSTR(" ("));
+ for (i = 0; i < n; i++) {
+ CFDataRef address;
+ char buf[64];
+ struct sockaddr *sa;
+
+ address = CFArrayGetValueAtIndex(targetPrivate->resolvedAddress, i);
+ sa = (struct sockaddr *)CFDataGetBytePtr(address);
+ _SC_sockaddr_to_string(sa, buf, sizeof(buf));
+ CFStringAppendFormat(result, NULL, CFSTR("%s%s"),
+ i > 0 ? ", " : "",
+ buf);
}
- } else {
- CFStringAppendFormat(result, NULL, CFSTR(" (%s)"),
+ } else if (CFEqual(targetPrivate->resolvedAddress, kCFNull)) {
+ CFStringAppendFormat(result, NULL, CFSTR(" (%s"),
gai_strerror(targetPrivate->resolvedAddressError));
+ } else {
+ CFStringAppendFormat(result, NULL, CFSTR(" (no addresses"));
}
- } else if (targetPrivate->dnsPort != NULL) {
- CFStringAppendFormat(result, NULL, CFSTR(" (DNS query active)"));
+ } else {
+ CFStringAppendFormat(result, NULL, CFSTR(" (%s"),
+ gai_strerror(targetPrivate->resolvedAddressError));
}
- break;
+ if (targetPrivate->llqActive) {
+ CFStringAppendFormat(result, NULL, CFSTR("), DNS llq active"));
+ } else {
+ CFStringAppendFormat(result, NULL, CFSTR(")"));
+ }
+ } else if (targetPrivate->llqActive) {
+ CFStringAppendFormat(result, NULL, CFSTR(" (DNS llq active)"));
}
}
+
+ // add flags
if (targetPrivate->scheduled) {
- CFStringAppendFormat(result,
- NULL,
- CFSTR(", flags = 0x%08x, if_index = %hu"),
- targetPrivate->info.flags,
- targetPrivate->info.if_index);
+ str = _SCNetworkReachabilityCopyTargetFlags(target);
+ CFStringAppendFormat(result, NULL, CFSTR(", %@"), str);
+ CFRelease(str);
}
+
CFStringAppendFormat(result, NULL, CFSTR("}"));
return result;
static void
__SCNetworkReachabilityDeallocate(CFTypeRef cf)
{
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)cf;
+ SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)cf;
+ SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%srelease"),
targetPrivate->log_prefix);
+#ifdef HAVE_REACHABILITY_SERVER
+ /* disconnect from the reachability server */
+
+ if (targetPrivate->serverActive) {
+ __SCNetworkReachabilityServer_targetRemove(target);
+ }
+#endif // HAVE_REACHABILITY_SERVER
+
/* release resources */
pthread_mutex_destroy(&targetPrivate->lock);
CFRelease(targetPrivate->onDemandServiceID);
}
+#ifdef HAVE_REACHABILITY_SERVER
+ if (targetPrivate->serverDigest != NULL) {
+ CFRelease(targetPrivate->serverDigest);
+ }
+
+ if (targetPrivate->serverGroup != NULL) {
+ dispatch_release(targetPrivate->serverGroup);
+ }
+
+ if (targetPrivate->serverQueue != NULL) {
+ dispatch_release(targetPrivate->serverQueue);
+ }
+
+ if (targetPrivate->serverWatchers != NULL) {
+ CFRelease(targetPrivate->serverWatchers);
+ }
+#endif // HAVE_REACHABILITY_SERVER
+
return;
}
_sc_debug = TRUE;
}
+ // set per-process "bypass" of the SCNetworkReachability server
+ if (getenv("LONG_LIVED_QUERY_BYPASS") != NULL) {
+ D_llqBypass = TRUE;
+ }
+
+#ifdef HAVE_REACHABILITY_SERVER
+ // set per-process "bypass" of the SCNetworkReachability server
+ if (getenv("REACH_SERVER_BYPASS") != NULL) {
+ D_serverBypass = TRUE;
+ }
+#endif // HAVE_REACHABILITY_SERVER
+
+ pthread_mutexattr_init(&lock_attr);
+ pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_ERRORCHECK);
+
return;
}
+__private_extern__
+dispatch_queue_t
+__SCNetworkReachability_concurrent_queue()
+{
+ static dispatch_once_t once;
+ static dispatch_queue_t q;
+
+ dispatch_once(&once, ^{
+ q = dispatch_queue_create("SCNetworkReachabilty.concurrent",
+ DISPATCH_QUEUE_CONCURRENT);
+ dispatch_queue_set_width(q, 32);
+ });
+
+ return q;
+}
+
+
/*
- * __SCNetworkReachabilityPerformInline
+ * __SCNetworkReachabilityPerformInlineNoLock
*
- * Calls rlsPerform()
+ * Calls reachPerform()
* - caller must be holding a reference to the target
* - caller must *not* be holding the target lock
+ * - caller must be running on the __SCNetworkReachability_concurrent_queue()
*/
static __inline__ void
-__SCNetworkReachabilityPerformInline(SCNetworkReachabilityRef target, Boolean needResolve)
+__SCNetworkReachabilityPerformInlineNoLock(SCNetworkReachabilityRef target, Boolean needResolve)
{
dispatch_queue_t queue;
SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
- pthread_mutex_lock(&targetPrivate->lock);
+ MUTEX_LOCK(&targetPrivate->lock);
if (needResolve) {
// allow the DNS query to be [re-]started
queue = targetPrivate->dispatchQueue;
if (queue != NULL) {
+ dispatch_group_t group;
+
dispatch_retain(queue);
- pthread_mutex_unlock(&targetPrivate->lock);
+ group = targetPrivate->dispatchGroup;
+ dispatch_group_enter(group);
+
+ MUTEX_UNLOCK(&targetPrivate->lock);
dispatch_sync(queue, ^{
- rlsPerform((void *)target);
+ reachPerform((void *)target);
+ dispatch_group_leave(group);
dispatch_release(queue);
});
} else {
_SC_signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
}
- pthread_mutex_unlock(&targetPrivate->lock);
+ MUTEX_UNLOCK(&targetPrivate->lock);
+ }
+
+ return;
+}
+
+
+#ifdef HAVE_REACHABILITY_SERVER
+/*
+ * __SCNetworkReachabilityPerformNoLock
+ *
+ * Calls reachPerform()
+ * - caller must *not* be holding the target lock
+ * - caller must *not* running on the __SCNetworkReachability_concurrent_queue()
+ */
+__private_extern__
+void
+__SCNetworkReachabilityPerformNoLock(SCNetworkReachabilityRef target)
+{
+ CFRetain(target);
+ dispatch_async(__SCNetworkReachability_concurrent_queue(), ^{
+ __SCNetworkReachabilityPerformInlineNoLock(target, FALSE);
+ CFRelease(target);
+ });
+
+ return;
+}
+#endif // HAVE_REACHABILITY_SERVER
+
+
+/*
+ * __SCNetworkReachabilityPerformConcurrent
+ *
+ * Calls reachPerform()
+ * - caller must be holding the target lock
+ * - caller running on the __SCNetworkReachability_concurrent_queue()
+ */
+static __inline__ void
+__SCNetworkReachabilityPerformConcurrent(SCNetworkReachabilityRef target)
+{
+ dispatch_queue_t queue;
+ SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
+
+ MUTEX_ASSERT_HELD(&targetPrivate->lock);
+
+ queue = targetPrivate->dispatchQueue;
+ if (queue != NULL) {
+ dispatch_retain(queue);
+ CFRetain(target);
+ dispatch_group_async(targetPrivate->dispatchGroup, queue, ^{
+ reachPerform((void *)target);
+ CFRelease(target);
+ dispatch_release(queue);
+ });
+ } else {
+ if (targetPrivate->rls != NULL) {
+ CFRunLoopSourceSignal(targetPrivate->rls);
+ _SC_signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
+ }
}
return;
}
+/*
+ * __SCNetworkReachabilityPerform
+ *
+ * Calls reachPerform()
+ * - caller must be holding the target lock
+ * - caller not running on the __SCNetworkReachability_concurrent_queue()
+ */
static void
__SCNetworkReachabilityPerform(SCNetworkReachabilityRef target)
{
+ dispatch_queue_t queue;
SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
- if (targetPrivate->dispatchQueue != NULL) {
+ MUTEX_ASSERT_HELD(&targetPrivate->lock);
+
+ queue = targetPrivate->dispatchQueue;
+ if (queue != NULL) {
+ dispatch_retain(queue);
CFRetain(target);
- dispatch_async(targetPrivate->dispatchQueue,
- ^{
- rlsPerform((void *)target);
- CFRelease(target);
- });
+ dispatch_group_async(targetPrivate->dispatchGroup, __SCNetworkReachability_concurrent_queue(), ^{
+ dispatch_sync(queue, ^{
+ reachPerform((void *)target);
+ CFRelease(target);
+ dispatch_release(queue);
+ });
+ });
} else if (targetPrivate->rls != NULL) {
CFRunLoopSourceSignal(targetPrivate->rls);
_SC_signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
return;
}
+
static SCNetworkReachabilityPrivateRef
__SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator)
{
return NULL;
}
- pthread_mutex_init(&targetPrivate->lock, NULL);
+ MUTEX_INIT(&targetPrivate->lock);
targetPrivate->name = NULL;
targetPrivate->serv = NULL;
- bzero(&targetPrivate->hints, sizeof(targetPrivate->hints));
- targetPrivate->hints.ai_flags = AI_ADDRCONFIG;
-#ifdef AI_PARALLEL
- targetPrivate->hints.ai_flags |= AI_PARALLEL;
-#endif /* AI_PARALLEL */
-
+ targetPrivate->hints = HINTS_DEFAULT;
targetPrivate->needResolve = FALSE;
targetPrivate->resolvedAddress = NULL;
targetPrivate->resolvedAddressError = NETDB_SUCCESS;
targetPrivate->localAddress = NULL;
targetPrivate->remoteAddress = NULL;
+ targetPrivate->cycle = 1;
targetPrivate->info = NOT_REACHABLE;
targetPrivate->last_notify = NOT_REPORTED;
targetPrivate->dnsMP = MACH_PORT_NULL;
targetPrivate->dnsPort = NULL;
targetPrivate->dnsRLS = NULL;
+ targetPrivate->dnsSource = NULL;
targetPrivate->dnsQueryStart = TIME_ZERO;
targetPrivate->dnsQueryEnd = TIME_ZERO;
targetPrivate->dnsRetry = NULL;
targetPrivate->dnsRetryCount = 0;
targetPrivate->last_dns = TIME_ZERO;
+ targetPrivate->last_network = TIME_ZERO;
+#if !TARGET_OS_IPHONE
+ targetPrivate->last_power = TIME_ZERO;
+#endif // !TARGET_OS_IPHONE
+ targetPrivate->last_push = TIME_ZERO;
targetPrivate->onDemandBypass = FALSE;
targetPrivate->onDemandName = NULL;
targetPrivate->onDemandServiceID = NULL;
+ targetPrivate->llqActive = FALSE;
+ targetPrivate->llqBypass = D_llqBypass;
+ targetPrivate->llqTarget = NULL;
+ targetPrivate->llqTimer = NULL;
+
+#ifdef HAVE_REACHABILITY_SERVER
+ targetPrivate->serverActive = FALSE;
+ targetPrivate->serverBypass = D_serverBypass;
+ targetPrivate->serverScheduled = FALSE;
+ targetPrivate->serverInfo = NOT_REACHABLE;
+
+ targetPrivate->serverDigest = NULL;
+ targetPrivate->serverGroup = NULL;
+ targetPrivate->serverInfoValid = FALSE;
+ targetPrivate->serverQueryActive = 0;
+ targetPrivate->serverQueue = NULL;
+ targetPrivate->serverReferences = 0;
+ targetPrivate->serverWatchers = NULL;
+#endif // HAVE_REACHABILITY_SERVER
+
targetPrivate->log_prefix[0] = '\0';
if (_sc_log > 0) {
snprintf(targetPrivate->log_prefix,
targetPrivate->remoteAddress = CFAllocatorAllocate(NULL, address->sa_len, 0);
bcopy(address, targetPrivate->remoteAddress, address->sa_len);
- SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%screate w/address %@"),
+ SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%s%s %@"),
targetPrivate->log_prefix,
+ DEBUG_REACHABILITY_TYPE_ADDRESS,
targetPrivate);
return (SCNetworkReachabilityRef)targetPrivate;
bcopy(remoteAddress, targetPrivate->remoteAddress, remoteAddress->sa_len);
}
- SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%screate w/address pair %@"),
+ SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%s%s %@"),
targetPrivate->log_prefix,
+ DEBUG_REACHABILITY_TYPE_ADDRESSPAIR,
targetPrivate);
return (SCNetworkReachabilityRef)targetPrivate;
SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator,
const char *nodename)
{
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ } addr;
int nodenameLen;
- struct sockaddr_in sin;
- struct sockaddr_in6 sin6;
SCNetworkReachabilityPrivateRef targetPrivate;
if (nodename == NULL) {
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);
+ if (_SC_string_to_sockaddr(nodename, AF_UNSPEC, (void *)&addr, sizeof(addr)) != NULL) {
+ /* if this "nodename" is really an IP[v6] address in disguise */
+ return SCNetworkReachabilityCreateWithAddress(allocator, &addr.sa);
}
targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
targetPrivate->needResolve = TRUE;
targetPrivate->info.flags |= kSCNetworkReachabilityFlagsFirstResolvePending;
+#ifdef HAVE_REACHABILITY_SERVER
+ targetPrivate->serverInfo.flags |= kSCNetworkReachabilityFlagsFirstResolvePending;
+#endif // HAVE_REACHABILITY_SERVER
- SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%screate w/name %@"),
+ SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%s%s %@"),
targetPrivate->log_prefix,
+ DEBUG_REACHABILITY_TYPE_NAME,
targetPrivate);
return (SCNetworkReachabilityRef)targetPrivate;
{
const struct sockaddr *addr_l = NULL;
const struct sockaddr *addr_r = NULL;
- CFBooleanRef bypass;
CFDataRef data;
struct addrinfo *hints = NULL;
CFStringRef interface = NULL;
+ CFBooleanRef llqBypass;
CFStringRef nodename;
+ CFBooleanRef onDemandBypass;
+ CFBooleanRef resolverBypass;
+#ifdef HAVE_REACHABILITY_SERVER
+ CFBooleanRef serverBypass;
+#endif // HAVE_REACHABILITY_SERVER
CFStringRef servname;
SCNetworkReachabilityRef target;
SCNetworkReachabilityPrivateRef targetPrivate;
return NULL;
}
- hints = (struct addrinfo *)CFDataGetBytePtr(data);
+ /* ALIGN: CF aligns to >8 byte boundries */
+ hints = (struct addrinfo *)(void *)CFDataGetBytePtr(data);
if ((hints->ai_addrlen != 0) ||
(hints->ai_addr != NULL) ||
(hints->ai_canonname != NULL) ||
_SCErrorSet(kSCStatusInvalidArgument);
return NULL;
}
- bypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionConnectionOnDemandByPass);
- if ((bypass != NULL) && !isA_CFBoolean(bypass)) {
+ onDemandBypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionConnectionOnDemandBypass);
+ if ((onDemandBypass != NULL) && !isA_CFBoolean(onDemandBypass)) {
+ _SCErrorSet(kSCStatusInvalidArgument);
+ return NULL;
+ }
+ resolverBypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionResolverBypass);
+ if ((resolverBypass != NULL) && !isA_CFBoolean(resolverBypass)) {
_SCErrorSet(kSCStatusInvalidArgument);
return NULL;
}
+ llqBypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionLongLivedQueryBypass);
+ if ((llqBypass != NULL) && !isA_CFBoolean(llqBypass)) {
+ _SCErrorSet(kSCStatusInvalidArgument);
+ return NULL;
+ }
+
+#ifdef HAVE_REACHABILITY_SERVER
+ serverBypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionServerBypass);
+ if ((serverBypass != NULL) && !isA_CFBoolean(serverBypass)) {
+ _SCErrorSet(kSCStatusInvalidArgument);
+ return NULL;
+ }
+#endif // HAVE_REACHABILITY_SERVER
+
if ((nodename != NULL) || (servname != NULL)) {
const char *name;
}
- if (bypass != NULL) {
- targetPrivate->onDemandBypass = CFBooleanGetValue(bypass);
+ if (llqBypass != NULL) {
+ targetPrivate->llqBypass = CFBooleanGetValue(llqBypass);
}
- SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%s + options %@"),
- targetPrivate->log_prefix,
- targetPrivate);
+ if (onDemandBypass != NULL) {
+ targetPrivate->onDemandBypass = CFBooleanGetValue(onDemandBypass);
+ }
+
+ if (resolverBypass != NULL) {
+ targetPrivate->resolverBypass = CFBooleanGetValue(resolverBypass);
+ }
+
+#ifdef HAVE_REACHABILITY_SERVER
+ if (serverBypass != NULL) {
+ targetPrivate->serverBypass = CFBooleanGetValue(serverBypass);
+ }
+#endif // HAVE_REACHABILITY_SERVER
+
+ if (_sc_debug && (_sc_log > 0)) {
+ const char *opt;
+
+ switch (targetPrivate->type) {
+ case reachabilityTypeName :
+ opt = DEBUG_REACHABILITY_TYPE_NAME_OPTIONS;
+ break;
+ case reachabilityTypeAddress :
+ opt = DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS;
+ break;
+ case reachabilityTypeAddressPair :
+ opt = DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS;
+ break;
+ default :
+ opt = "???";
+ break;
+ }
+
+ SCLog(TRUE, LOG_INFO, CFSTR("%s%s %@"),
+ targetPrivate->log_prefix,
+ opt,
+ targetPrivate);
+ }
return (SCNetworkReachabilityRef)targetPrivate;
}
struct addrinfo *resP;
SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
+ MUTEX_ASSERT_HELD(&targetPrivate->lock);
+
if (targetPrivate->resolvedAddress != NULL) {
CFRelease(targetPrivate->resolvedAddress);
targetPrivate->resolvedAddress = NULL;
__dns_query_end(target,
((status == 0) && (res != NULL)), // if successful query
- TRUE, // async
+ dns_query_async, // async
&targetPrivate->dnsQueryStart, // start time
&targetPrivate->dnsQueryEnd); // end time
static void
-processAsyncDNSReply(mach_port_t mp, void *msg, SCNetworkReachabilityRef target);
+getaddrinfo_async_handleCFReply(CFMachPortRef port, void *msg, CFIndex size, void *info);
-static void
-getaddrinfo_async_handleCFReply(CFMachPortRef port, void *msg, CFIndex size, void *info)
+static Boolean
+enqueueAsyncDNSQuery_dispatch(SCNetworkReachabilityRef target)
{
- mach_port_t mp = CFMachPortGetPort(port);
- SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
+ mach_port_t mp;
+ dispatch_source_t source;
+ SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
- processAsyncDNSReply(mp, msg, target);
- return;
-}
+ MUTEX_ASSERT_HELD(&targetPrivate->lock);
+ mp = targetPrivate->dnsMP;
-static boolean_t
-SCNetworkReachabilityNotifyMIGCallback(mach_msg_header_t *message, mach_msg_header_t *reply)
-{
- mach_port_t mp = message->msgh_local_port;
- SCNetworkReachabilityRef target = dispatch_get_context(dispatch_get_current_queue());
+ // mach_port context <-- NULL (no msg received)
+ mach_port_set_context(mach_task_self(), mp, (mach_vm_address_t)(uintptr_t)NULL);
- processAsyncDNSReply(mp, message, target);
- reply->msgh_remote_port = MACH_PORT_NULL;
- return false;
+ // create dispatch source to handle DNS reply
+ source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV,
+ mp,
+ 0,
+ __SCNetworkReachability_concurrent_queue());
+ if (source == NULL) {
+ SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkReachability dispatch_source_create() failed"));
+ return FALSE;
+ }
+
+ //
+ // We created the dispatch_source to listen for (and process) the mach IPC
+ // reply to our async DNS query. Because the source handler runs asychronously
+ // we need to ensure that we're holding a reference to the target. Here, we take
+ // a reference and setup the dispatch_source finalizer to drop it.
+ //
+ CFRetain(target);
+ dispatch_set_context(source, (void *)target);
+ dispatch_set_finalizer_f(source, (dispatch_function_t)CFRelease);
+
+ dispatch_source_set_event_handler(source, ^{
+ mach_msg_size_t msg_size = 8192;
+ const mach_msg_options_t options = MACH_RCV_MSG
+ | MACH_RCV_LARGE
+ | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_CTX)
+ | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0);
+
+ while (TRUE) {
+ kern_return_t kr;
+ mach_msg_header_t *msg = (mach_msg_header_t *)malloc(msg_size);
+
+ kr = mach_msg(msg, /* msg */
+ options, /* options */
+ 0, /* send_size */
+ msg_size, /* rcv_size */
+ mp, /* rcv_name */
+ MACH_MSG_TIMEOUT_NONE, /* timeout */
+ MACH_PORT_NULL); /* notify */
+ if (kr == KERN_SUCCESS) {
+ // mach_port context <-- msg
+ mach_port_set_context(mach_task_self(),
+ mp,
+ (mach_vm_address_t)(uintptr_t)msg);
+ } else if (kr == MACH_RCV_TOO_LARGE) {
+ msg_size *= 2;
+ free(msg);
+ continue;
+ } else {
+ SCLog(TRUE, LOG_ERR,
+ CFSTR("SCNetworkReachability async DNS handler, kr=0x%x"),
+ kr);
+ free(msg);
+ }
+ break;
+ }
+
+ dispatch_source_cancel(source);
+ });
+
+ dispatch_source_set_cancel_handler(source, ^{
+#if !TARGET_OS_EMBEDDED
+ mach_vm_address_t context;
+#else // !TARGET_OS_EMBEDDED
+ mach_port_context_t context;
+#endif // !TARGET_OS_EMBEDDED
+ kern_return_t kr;
+ mach_port_t mp;
+
+ // get the [async DNS query] mach port
+ mp = (mach_port_t)dispatch_source_get_handle(source);
+
+ // check if we have a received message
+ kr = mach_port_get_context(mach_task_self(), mp, &context);
+ if (kr == KERN_SUCCESS) {
+ void *msg;
+
+ msg = (void *)(uintptr_t)context;
+ if (msg != NULL) {
+ MUTEX_LOCK(&targetPrivate->lock);
+ getaddrinfo_async_handle_reply(msg);
+ targetPrivate->dnsSource = NULL;
+ targetPrivate->dnsMP = MACH_PORT_NULL;
+ MUTEX_UNLOCK(&targetPrivate->lock);
+ free(msg);
+ } else {
+ getaddrinfo_async_cancel(mp);
+ }
+ }
+
+ dispatch_release(source);
+ });
+
+ targetPrivate->dnsSource = source;
+ dispatch_resume(source);
+
+ return TRUE;
}
static Boolean
-enqueueAsyncDNSQuery(SCNetworkReachabilityRef target, mach_port_t mp)
+enqueueAsyncDNSQuery_CF(SCNetworkReachabilityRef target)
{
- CFMachPortContext context = { 0
- , (void *)target
- , CFRetain
- , CFRelease
- , replyMPCopyDescription
- };
+ CFMachPortContext context = { 0
+ , (void *)target
+ , CFRetain
+ , CFRelease
+ , replyMPCopyDescription
+ };
+ CFIndex i;
+ mach_port_t mp;
+ CFIndex n;
SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
- targetPrivate->dnsMP = mp;
+ MUTEX_ASSERT_HELD(&targetPrivate->lock);
+
+ mp = targetPrivate->dnsMP;
+
targetPrivate->dnsPort = _SC_CFMachPortCreateWithPort("SCNetworkReachability",
mp,
getaddrinfo_async_handleCFReply,
&context);
- if (targetPrivate->dispatchQueue != NULL) {
- targetPrivate->asyncDNSQueue = dispatch_queue_create("com.apple.SCNetworkReachabilty.async_DNS_query", NULL);
- if (targetPrivate->asyncDNSQueue == NULL) {
- SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkReachability dispatch_queue_create() failed"));
- goto fail;
- }
- CFRetain(target); // Note: will be released when the dispatch queue is released
- dispatch_set_context(targetPrivate->asyncDNSQueue, (void *)target);
- dispatch_set_finalizer_f(targetPrivate->asyncDNSQueue, (dispatch_function_t)CFRelease);
-
- targetPrivate->asyncDNSSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV,
- mp,
- 0,
- targetPrivate->asyncDNSQueue);
- if (targetPrivate->asyncDNSSource == NULL) {
- SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkReachability dispatch_source_create() failed"));
- goto fail;
- }
- dispatch_source_set_event_handler(targetPrivate->asyncDNSSource, ^{
- dispatch_mig_server(targetPrivate->asyncDNSSource,
- sizeof(mach_msg_header_t),
- SCNetworkReachabilityNotifyMIGCallback);
- });
- dispatch_resume(targetPrivate->asyncDNSSource);
- } else if (targetPrivate->rls != NULL) {
- CFIndex i;
- CFIndex n;
+ if (targetPrivate->dnsPort == NULL) {
+ SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkReachability CFMachPortCreateWithPort() failed"));
+ goto fail;
+ }
- targetPrivate->dnsRLS = CFMachPortCreateRunLoopSource(NULL, targetPrivate->dnsPort, 0);
+ targetPrivate->dnsRLS = CFMachPortCreateRunLoopSource(NULL, targetPrivate->dnsPort, 0);
+ if (targetPrivate->dnsRLS == NULL) {
+ SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkReachability CFMachPortCreateRunLoopSource() failed"));
+ goto fail;
+ }
- n = CFArrayGetCount(targetPrivate->rlList);
- for (i = 0; i < n; i += 3) {
- CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(targetPrivate->rlList, i+1);
- CFStringRef rlMode = (CFStringRef) CFArrayGetValueAtIndex(targetPrivate->rlList, i+2);
+ n = CFArrayGetCount(targetPrivate->rlList);
+ for (i = 0; i < n; i += 3) {
+ CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(targetPrivate->rlList, i+1);
+ CFStringRef rlMode = (CFStringRef) CFArrayGetValueAtIndex(targetPrivate->rlList, i+2);
- CFRunLoopAddSource(rl, targetPrivate->dnsRLS, rlMode);
- }
+ CFRunLoopAddSource(rl, targetPrivate->dnsRLS, rlMode);
}
return TRUE;
fail :
- if (targetPrivate->asyncDNSSource != NULL) {
- dispatch_source_cancel(targetPrivate->asyncDNSSource);
- dispatch_release(targetPrivate->asyncDNSSource);
- targetPrivate->asyncDNSSource = NULL;
+ if (targetPrivate->dnsRLS != NULL) {
+ CFRunLoopSourceInvalidate(targetPrivate->dnsRLS);
+ CFRelease(targetPrivate->dnsRLS);
+ targetPrivate->dnsRLS = NULL;
}
- if (targetPrivate->asyncDNSQueue != NULL) {
- dispatch_release(targetPrivate->asyncDNSQueue);
- targetPrivate->asyncDNSQueue = NULL;
+ if (targetPrivate->dnsPort != NULL) {
+ CFMachPortInvalidate(targetPrivate->dnsPort);
+ CFRelease(targetPrivate->dnsPort);
+ targetPrivate->dnsPort = NULL;
}
- CFMachPortInvalidate(targetPrivate->dnsPort);
- CFRelease(targetPrivate->dnsPort);
- targetPrivate->dnsPort = NULL;
- targetPrivate->dnsMP = MACH_PORT_NULL;
-
- _SCErrorSet(kSCStatusFailed);
return FALSE;
}
-static void
-dequeueAsyncDNSQuery(SCNetworkReachabilityRef target)
+static Boolean
+enqueueAsyncDNSQuery(SCNetworkReachabilityRef target, mach_port_t mp)
{
+ Boolean ok = FALSE;
SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
- if (targetPrivate->asyncDNSSource != NULL) {
- dispatch_source_cancel(targetPrivate->asyncDNSSource);
- if (targetPrivate->asyncDNSQueue != dispatch_get_current_queue()) {
- // ensure the cancellation has completed
- pthread_mutex_unlock(&targetPrivate->lock);
- dispatch_sync(targetPrivate->asyncDNSQueue, ^{});
- pthread_mutex_lock(&targetPrivate->lock);
- }
+ MUTEX_ASSERT_HELD(&targetPrivate->lock);
+
+ targetPrivate->dnsMP = mp;
+
+ if (targetPrivate->dispatchQueue != NULL) {
+ ok = enqueueAsyncDNSQuery_dispatch(target);
+ } else if (targetPrivate->rls != NULL) {
+ ok = enqueueAsyncDNSQuery_CF(target);
}
- if (targetPrivate->asyncDNSSource != NULL) {
- dispatch_release(targetPrivate->asyncDNSSource);
- targetPrivate->asyncDNSSource = NULL;
+
+ if (!ok) {
+ targetPrivate->dnsMP = MACH_PORT_NULL;
+ _SCErrorSet(kSCStatusFailed);
+ return FALSE;
}
- if (targetPrivate->asyncDNSQueue != NULL) {
- dispatch_release(targetPrivate->asyncDNSQueue);
- targetPrivate->asyncDNSQueue = NULL;
+
+ return TRUE;
+}
+
+
+static void
+dequeueAsyncDNSQuery(SCNetworkReachabilityRef target, Boolean cancel)
+{
+ SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
+
+ MUTEX_ASSERT_HELD(&targetPrivate->lock);
+
+ if (targetPrivate->dnsPort != NULL) {
+ CFMachPortInvalidate(targetPrivate->dnsPort);
+ CFRelease(targetPrivate->dnsPort);
+ targetPrivate->dnsPort = NULL;
}
if (targetPrivate->dnsRLS != NULL) {
targetPrivate->dnsRLS = NULL;
}
- if (targetPrivate->dnsPort != NULL) {
- CFMachPortInvalidate(targetPrivate->dnsPort);
- CFRelease(targetPrivate->dnsPort);
- targetPrivate->dnsPort = NULL;
+ if (targetPrivate->dnsSource != NULL) {
+ dispatch_source_cancel(targetPrivate->dnsSource);
+ targetPrivate->dnsSource = NULL;
+ cancel = FALSE; // the cancellation handler does the work
+ }
+
+ if (targetPrivate->dnsMP != MACH_PORT_NULL) {
+ if (cancel) {
+ getaddrinfo_async_cancel(targetPrivate->dnsMP);
+ }
targetPrivate->dnsMP = MACH_PORT_NULL;
}
static void
-processAsyncDNSReply(mach_port_t mp, void *msg, SCNetworkReachabilityRef target)
+getaddrinfo_async_handleCFReply(CFMachPortRef port, void *msg, CFIndex size, void *info)
{
+ mach_port_t mp = CFMachPortGetPort(port);
int32_t status;
+ SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
- pthread_mutex_lock(&targetPrivate->lock);
+ MUTEX_LOCK(&targetPrivate->lock);
if (mp != targetPrivate->dnsMP) {
// we've received a callback on the async DNS port but since the
// associated CFMachPort doesn't match than the request must have
// already been cancelled.
SCLog(TRUE, LOG_ERR, CFSTR("processAsyncDNSReply(): mp != targetPrivate->dnsMP"));
- pthread_mutex_unlock(&targetPrivate->lock);
+ MUTEX_UNLOCK(&targetPrivate->lock);
return;
}
- dequeueAsyncDNSQuery(target);
+ dequeueAsyncDNSQuery(target, FALSE);
status = getaddrinfo_async_handle_reply(msg);
if ((status == 0) &&
(targetPrivate->resolvedAddress == NULL) && (targetPrivate->resolvedAddressError == NETDB_SUCCESS)) {
}
}
- pthread_mutex_unlock(&targetPrivate->lock);
+ MUTEX_UNLOCK(&targetPrivate->lock);
return;
}
dns_resolver_t *resolver,
SCNetworkReachabilityFlags *flags,
Boolean *haveDNS,
+ uint32_t *resolver_if_index,
const char *log_prefix)
{
- int i;
Boolean ok = TRUE;
- *flags = kSCNetworkReachabilityFlagsReachable;
- *haveDNS = FALSE;
+ if (resolver_if_index) *resolver_if_index = 0;
- for (i = 0; i < resolver->n_nameserver; i++) {
- struct sockaddr *address = resolver->nameserver[i];
- ReachabilityInfo ns_info;
+ if (resolver->n_nameserver > 0) {
+#if !TARGET_IPHONE_SIMULATOR
+ *flags = (SCNetworkReachabilityFlags)resolver->reach_flags;
+ if (resolver_if_index != NULL) {
+ *resolver_if_index = resolver->if_index;
+ }
+#else // !TARGET_IPHONE_SIMULATOR
+ int i;
- *haveDNS = TRUE;
+ *flags = kSCNetworkReachabilityFlagsReachable;
- if (address->sa_family != AF_INET) {
- /*
- * we need to skip non-IPv4 DNS server
- * addresses (at least until [3510431] has
- * been resolved).
- */
- continue;
- }
+ for (i = 0; i < resolver->n_nameserver; i++) {
+ struct sockaddr *address = resolver->nameserver[i];
+ ReachabilityInfo ns_info;
- ok = checkAddress(store_info, address, resolver->if_index, &ns_info, log_prefix);
- if (!ok) {
- /* not today */
- goto done;
- }
+ ok = checkAddress(store_info, address, resolver->if_index, &ns_info, log_prefix);
+ if (!ok) {
+ /* not today */
+ break;
+ }
- if (rankReachability(ns_info.flags) < rankReachability(*flags)) {
- /* return the worst case result */
- *flags = ns_info.flags;
+ if ((i == 0) ||
+ (rankReachability(ns_info.flags) < rankReachability(*flags))) {
+ /* return the worst case result */
+ *flags = ns_info.flags;
+ if (resolver_if_index != NULL) {
+ *resolver_if_index = ns_info.if_index;
+ }
+ }
}
+#endif // !TARGET_IPHONE_SIMULATOR
+ *haveDNS = TRUE;
+ } else {
+ *flags = kSCNetworkReachabilityFlagsReachable;
+ *haveDNS = FALSE;
}
- done :
-
return ok;
}
unsigned int if_index,
SCNetworkReachabilityFlags *flags,
Boolean *haveDNS,
+ uint32_t *resolver_if_index,
+ int *dns_config_index,
const char *log_prefix)
{
int i;
resolvers = dns_config->scoped_resolver;
}
+ /* In case we couldn't find a match, setting an index of -1
+ and resolver_if_index 0 */
+ if (dns_config_index != NULL) *dns_config_index = -1;
+ if (resolver_if_index != NULL) *resolver_if_index = 0;
+
while (!matched && (name != NULL)) {
int len;
* if name matches domain
*/
matched = TRUE;
- ok = check_resolver_reachability(store_info, resolver, flags, haveDNS, log_prefix);
+ ok = check_resolver_reachability(store_info, resolver, flags, haveDNS,
+ resolver_if_index, log_prefix);
if (!ok) {
/* not today */
return FALSE;
}
+ if (dns_config_index != NULL) *dns_config_index = i;
}
}
}
const char *nodename,
const char *servname,
unsigned int if_index,
- const char *log_prefix)
+ uint32_t *resolver_if_index,
+ int *dns_config_index,
+ const char *log_prefix
+ )
{
dns_resolver_t *default_resolver;
dns_configuration_t *dns;
Boolean ok = TRUE;
Boolean useDefault = FALSE;
+ if (resolver_if_index) *resolver_if_index = 0;
+ if (dns_config_index) *dns_config_index = -1;
+
/*
* We first assume that all of the configured DNS servers
* are available. Since we don't know which name server will
goto done;
}
- *flags = kSCNetworkReachabilityFlagsReachable;
-
if (fqdn[len - 1] == '.') {
isFQDN = TRUE;
/*
* check if the provided name matches a supplemental domain
*/
- found = check_matching_resolvers(store_info, dns->config, fqdn, if_index, flags, haveDNS, log_prefix);
+ found = check_matching_resolvers(store_info, dns->config, fqdn, if_index,
+ flags, haveDNS, resolver_if_index,
+ dns_config_index, log_prefix);
if (!found && !isFQDN) {
/*
if_index,
flags,
haveDNS,
+ resolver_if_index,
+ dns_config_index,
log_prefix);
free(search_fqdn);
}
if_index,
flags,
haveDNS,
+ resolver_if_index,
+ dns_config_index,
log_prefix);
free(search_fqdn);
/*
* check the reachability of the default resolver
*/
- ok = check_resolver_reachability(store_info, default_resolver, flags, haveDNS, log_prefix);
+ ok = check_resolver_reachability(store_info, default_resolver, flags, haveDNS,
+ resolver_if_index, log_prefix);
+ if (ok && dns_config_index != NULL) *dns_config_index = 0;
}
if (fqdn != nodename) free(fqdn);
Boolean ok;
ReachabilityStoreInfo store_info;
- initReachabilityStoreInfo(&store_info);
- ok = updateReachabilityStoreInfo(&store_info, storeP, AF_UNSPEC);
+ ReachabilityStoreInfo_init(&store_info);
+ ok = ReachabilityStoreInfo_update(&store_info, storeP, AF_UNSPEC);
if (!ok) {
goto done;
}
- ok = _SC_R_checkResolverReachability(&store_info, flags, haveDNS, nodename, servname, 0, "");
+ ok = _SC_R_checkResolverReachability(&store_info, flags, haveDNS, nodename,
+ servname, 0, NULL, NULL, "");
done :
- freeReachabilityStoreInfo(&store_info);
+ ReachabilityStoreInfo_free(&store_info);
return ok;
}
+Boolean
+__SC_checkResolverReachabilityInternal(SCDynamicStoreRef *storeP,
+ SCNetworkReachabilityFlags *flags,
+ Boolean *haveDNS,
+ const char *nodename,
+ const char *servname,
+ uint32_t *resolver_if_index,
+ int *dns_config_index)
+{
+ Boolean ok;
+ ReachabilityStoreInfo store_info;
+
+ ReachabilityStoreInfo_init(&store_info);
+ ok = ReachabilityStoreInfo_update(&store_info, storeP, AF_UNSPEC);
+ if (!ok) {
+ goto done;
+ }
+
+ ok = _SC_R_checkResolverReachability(&store_info, flags, haveDNS, nodename,
+ servname, 0, resolver_if_index, dns_config_index, "");
+
+ done :
+
+ ReachabilityStoreInfo_free(&store_info);
+ return ok;
+}
/*
* _SC_checkResolverReachabilityByAddress()
char ptr_name[128];
ReachabilityStoreInfo store_info;
- initReachabilityStoreInfo(&store_info);
- ok = updateReachabilityStoreInfo(&store_info, storeP, AF_UNSPEC);
+ ReachabilityStoreInfo_init(&store_info);
+ ok = ReachabilityStoreInfo_update(&store_info, storeP, AF_UNSPEC);
if (!ok) {
goto done;
}
in_addr_t s_addr;
unsigned char b[4];
} rev;
- struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+ /* ALIGN: assuming sa is aligned, then cast ok. */
+ struct sockaddr_in *sin = (struct sockaddr_in *)(void *)sa;
/*
* build "PTR" query name
case AF_INET6 : {
int s = 0;
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+ /* ALIGN: assume sa is aligned, cast ok. */
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)(void *)sa;
int x = sizeof(ptr_name);
int n;
goto done;
}
- ok = _SC_R_checkResolverReachability(&store_info, flags, haveDNS, ptr_name, NULL, 0, "");
+ ok = _SC_R_checkResolverReachability(&store_info, flags, haveDNS, ptr_name, NULL, 0, NULL, NULL, "");
done :
- freeReachabilityStoreInfo(&store_info);
+ ReachabilityStoreInfo_free(&store_info);
return ok;
}
static Boolean
-startAsyncDNSQuery(SCNetworkReachabilityRef target) {
+startAsyncDNSQuery(SCNetworkReachabilityRef target)
+{
int error = 0;
mach_port_t mp = MACH_PORT_NULL;
Boolean ok;
}
-#pragma mark -
+#pragma mark -
+
+
+static Boolean
+enqueueAsyncDNSRetry(SCNetworkReachabilityRef target)
+{
+ int64_t delay;
+ dispatch_source_t source;
+ SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
+
+ MUTEX_ASSERT_HELD(&targetPrivate->lock);
+
+ source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
+ 0,
+ 0,
+ __SCNetworkReachability_concurrent_queue());
+ if (source == NULL) {
+ SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkReachability retry dispatch_source_create() failed"));
+ return FALSE;
+ }
+
+ // retain the target ... and release it when the [timer] source is released
+ CFRetain(target);
+ dispatch_set_context(source, (void *)target);
+ dispatch_set_finalizer_f(source, (dispatch_function_t)CFRelease);
+
+ dispatch_source_set_event_handler(source, ^(void) {
+ __SCNetworkReachabilityPerformInlineNoLock(target, TRUE);
+ });
+
+ // start a one-shot timer
+ delay = targetPrivate->dnsRetryCount * EAI_NONAME_RETRY_DELAY_USEC * NSEC_PER_USEC;
+ dispatch_source_set_timer(source,
+ dispatch_time(DISPATCH_TIME_NOW, delay), // start
+ 0, // interval
+ 10 * NSEC_PER_MSEC); // leeway
+
+ targetPrivate->dnsRetry = source;
+ dispatch_resume(source);
+
+ return TRUE;
+}
+
+
+static void
+dequeueAsyncDNSRetry(SCNetworkReachabilityRef target)
+{
+ SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
+
+ MUTEX_ASSERT_HELD(&targetPrivate->lock);
+
+ if (targetPrivate->dnsRetry != NULL) {
+ dispatch_source_cancel(targetPrivate->dnsRetry);
+ dispatch_release(targetPrivate->dnsRetry);
+ targetPrivate->dnsRetry = NULL;
+ }
+
+ return;
+}
+
+
+#pragma mark -
+
+
+static dispatch_queue_t
+_llq_queue()
+{
+ static dispatch_once_t once;
+ static dispatch_queue_t q;
+
+ dispatch_once(&once, ^{
+ q = dispatch_queue_create("SCNetworkReachabilty.longLivedQueries", NULL);
+ });
+
+ return q;
+}
+
+
+/*
+ * _llq_notify
+ *
+ * Called to push out a target's DNS changes
+ * - caller must be running on the _llq_queue()
+ */
+static void
+_llq_notify(const void *value, void *context)
+{
+ SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)value;
+ SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
+
+ MUTEX_LOCK(&targetPrivate->lock);
+
+ __dns_query_end(target,
+ (targetPrivate->resolvedAddressError == NETDB_SUCCESS), // if successful query
+ dns_query_llq, // long-lived-query
+ &targetPrivate->dnsQueryStart, // start time
+ &targetPrivate->dnsQueryEnd); // end time
+
+ if (targetPrivate->scheduled) {
+ __SCNetworkReachabilityPerform(target);
+ }
+
+ // last long-lived-query end time is new start time
+ targetPrivate->dnsQueryStart = targetPrivate->dnsQueryEnd;
+
+ MUTEX_UNLOCK(&targetPrivate->lock);
+ return;
+}
+
+
+/*
+ * _llq_callback
+ *
+ * Called to process mDNSResponder long-lived-query updates
+ * - caller must be running on the _llq_queue()
+ */
+static void
+_llq_callback(DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *hostname,
+ const struct sockaddr *address,
+ uint32_t ttl,
+ void *context)
+{
+ SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)context;
+ SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
+
+ MUTEX_LOCK(&targetPrivate->lock);
+
+ if (targetPrivate->llqTimer != NULL) {
+ dispatch_source_cancel(targetPrivate->llqTimer);
+ dispatch_release(targetPrivate->llqTimer);
+ targetPrivate->llqTimer = NULL;
+ }
+
+ switch (errorCode) {
+ case kDNSServiceErr_NoError :
+ if (address != NULL) {
+ CFMutableArrayRef addresses;
+ CFDataRef llqAddress;
+
+ if (targetPrivate->resolvedAddress != NULL) {
+ if (isA_CFArray(targetPrivate->resolvedAddress)) {
+ addresses = CFArrayCreateMutableCopy(NULL, 0, targetPrivate->resolvedAddress);
+ } else {
+ addresses = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+ }
+
+ CFRelease(targetPrivate->resolvedAddress);
+ targetPrivate->resolvedAddress = NULL;
+ } else {
+ addresses = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+ }
+
+ llqAddress = CFDataCreate(NULL, (void *)address, address->sa_len);
+ if (flags & kDNSServiceFlagsAdd) {
+ // add address
+ CFArrayAppendValue(addresses, llqAddress);
+ } else {
+ CFIndex i;
+
+ // remove address
+ i = CFArrayGetFirstIndexOfValue(addresses,
+ CFRangeMake(0, CFArrayGetCount(addresses)),
+ llqAddress);
+ if (i != kCFNotFound) {
+ CFArrayRemoveValueAtIndex(addresses, i);
+ }
+ }
+ CFRelease(llqAddress);
+
+ if (CFArrayGetCount(addresses) > 0) {
+ targetPrivate->resolvedAddress = addresses;
+ targetPrivate->resolvedAddressError = NETDB_SUCCESS;
+ } else {
+ // if host not found
+ targetPrivate->resolvedAddress = CFRetain(kCFNull);
+ targetPrivate->resolvedAddressError = EAI_NONAME;
+ CFRelease(addresses);
+ }
+
+ targetPrivate->needResolve = FALSE;
+ }
+ break;
+ case kDNSServiceErr_NoSuchRecord :
+ if (address != NULL) {
+ // no IPv4/IPv6 address for name (NXDOMAIN)
+ if (targetPrivate->resolvedAddress == NULL) {
+ targetPrivate->resolvedAddress = CFRetain(kCFNull);
+ targetPrivate->resolvedAddressError = EAI_NONAME;
+ }
+ targetPrivate->needResolve = FALSE;
+ }
+ break;
+ case kDNSServiceErr_Timeout :
+ if (targetPrivate->resolvedAddress == NULL) {
+ targetPrivate->resolvedAddress = CFRetain(kCFNull);
+ targetPrivate->resolvedAddressError = EAI_NONAME;
+ }
+ targetPrivate->needResolve = FALSE;
+ break;
+ default :
+ SCLog(TRUE, LOG_ERR,
+ CFSTR("%sSCNetworkReachability _llq_callback w/error=%d"),
+ targetPrivate->log_prefix,
+ errorCode);
+ break;
+ }
+
+ MUTEX_UNLOCK(&targetPrivate->lock);
+
+ // the "more coming" flag applies to DNSService callouts for any/all
+ // hosts that are being watched so we need to keep track of the targets
+ // we have updated. When we [finally] have the last callout then we
+ // push our notifications for all of the updated targets.
+
+ if (llqUpdated == NULL) {
+ llqUpdated = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
+ }
+ CFSetAddValue(llqUpdated, target);
+
+ if (!(flags & kDNSServiceFlagsMoreComing)) {
+ CFSetApplyFunction(llqUpdated, _llq_notify, NULL);
+ CFRelease(llqUpdated);
+ llqUpdated = NULL;
+ }
+
+ return;
+}
+
+
+static Boolean
+enqueueLongLivedQuery(SCNetworkReachabilityRef target)
+{
+ SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
+
+ MUTEX_ASSERT_HELD(&targetPrivate->lock);
+
+ if (targetPrivate->serv != NULL) {
+ // if "serv" provided, can't use DNSServiceGetAddrInfo
+ return FALSE;
+ }
+
+ if (memcmp(&targetPrivate->hints, &HINTS_DEFAULT, sizeof(targetPrivate->hints)) != 0) {
+ // non-default "hints" provided, can't use DNSServiceGetAddrInfo
+ return FALSE;
+ }
+
+ // mark the long lived query active
+ targetPrivate->llqActive = TRUE;
+
+ // track the DNS resolution time
+ __dns_query_start(&targetPrivate->dnsQueryStart, &targetPrivate->dnsQueryEnd);
+
+ CFRetain(target);
+ dispatch_async(_llq_queue(), ^{
+ DNSServiceErrorType err;
+ dispatch_source_t source;
+
+ MUTEX_LOCK(&targetPrivate->lock);
+
+ if (targetPrivate->llqTarget != NULL) {
+ // if already running
+ MUTEX_UNLOCK(&targetPrivate->lock);
+ CFRelease(target);
+ return;
+ }
+
+ // if needed, start interacting with mDNSResponder
+
+ if (llqMain == NULL) {
+ err = DNSServiceCreateConnection(&llqMain);
+ if (err != kDNSServiceErr_NoError) {
+ SCLog(TRUE, LOG_ERR,
+ CFSTR("DNSServiceCreateConnection(&llqMain) failed, error = %d"),
+ err);
+
+ targetPrivate->llqActive = FALSE;
+
+ MUTEX_UNLOCK(&targetPrivate->lock);
+ CFRelease(target);
+ return;
+ }
+ err = DNSServiceSetDispatchQueue(llqMain, _llq_queue());
+ if (err != kDNSServiceErr_NoError) {
+ SCLog(TRUE, LOG_ERR,
+ CFSTR("DNSServiceSetDispatchQueue() failed, error = %d"),
+ err);
+ DNSServiceRefDeallocate(llqMain);
+ llqMain = NULL;
-static Boolean
-enqueueAsyncDNSRetry(SCNetworkReachabilityRef target)
-{
- int64_t delay;
- dispatch_source_t source;
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
+ targetPrivate->llqActive = FALSE;
- source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
- 0,
- 0,
- dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
- if (source == NULL) {
- SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkReachability retry dispatch_source_create() failed"));
- return FALSE;
- }
+ MUTEX_UNLOCK(&targetPrivate->lock);
+ CFRelease(target);
+ return;
+ }
+ }
- // retain the target ... and release it when the [timer] source is released
- CFRetain(target);
- dispatch_set_context(source, (void *)target);
- dispatch_set_finalizer_f(source, (dispatch_function_t)CFRelease);
+ // start a long-lived-query for this target
- dispatch_source_set_event_handler(source, ^(void) {
- __SCNetworkReachabilityPerformInline(target, TRUE);
- });
+ targetPrivate->llqTarget = llqMain;
+ err = DNSServiceGetAddrInfo(&targetPrivate->llqTarget, // sdRef
+ kDNSServiceFlagsReturnIntermediates // flags
+ | kDNSServiceFlagsShareConnection,
+ targetPrivate->if_index, // interfaceIndex
+ 0, // protocol
+ targetPrivate->name, // hostname
+ _llq_callback, // callback
+ (void *)target); // context
+ if (err != kDNSServiceErr_NoError) {
+ SCLog(TRUE, LOG_ERR,
+ CFSTR("DNSServiceGetAddrInfo() failed, error = %d"),
+ err);
+ targetPrivate->llqTarget = NULL;
+ if (llqCount == 0) {
+ // if this was the first request
+ DNSServiceRefDeallocate(llqMain);
+ llqMain = NULL;
+ }
- // start a one-shot timer
- delay = targetPrivate->dnsRetryCount * EAI_NONAME_RETRY_DELAY_USEC * NSEC_PER_USEC;
- dispatch_source_set_timer(source,
- dispatch_time(DISPATCH_TIME_NOW, delay), // start
- 0, // interval
- 10 * NSEC_PER_MSEC); // leeway
+ targetPrivate->llqActive = FALSE;
+
+ MUTEX_UNLOCK(&targetPrivate->lock);
+ CFRelease(target);
+ return;
+ }
+
+ llqCount++;
+
+ // if case we don't get any callbacks from our long-lived-query (this
+ // could happen if the DNS servers do not respond), we start a timer
+ // to ensure that we fire off at least one reachability callback.
+
+ source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
+ 0,
+ 0,
+ _llq_queue());
+ if (source != NULL) {
+ // retain the target ... and release it when the [timer] source is released
+ CFRetain(target);
+ dispatch_set_context(source, (void *)target);
+ dispatch_set_finalizer_f(source, (dispatch_function_t)CFRelease);
+
+ dispatch_source_set_event_handler(source, ^(void) {
+ _llq_callback(NULL, // sdRef
+ 0, // flags
+ 0, // interfaceIndex
+ kDNSServiceErr_Timeout, // errorCode
+ NULL, // hostname
+ NULL, // address
+ 0, // ttl
+ (void *)target); // context
+ });
+
+ dispatch_source_set_timer(source,
+ dispatch_time(DISPATCH_TIME_NOW,
+ LLQ_TIMEOUT_NSEC), // start
+ 0, // interval
+ 10 * NSEC_PER_MSEC); // leeway
+
+ targetPrivate->llqTimer = source;
+ dispatch_resume(source);
+ } else {
+ SCLog(TRUE, LOG_ERR,
+ CFSTR("SCNetworkReachability llq dispatch_source_create(no-reply) failed"));
+ }
- targetPrivate->dnsRetry = source;
- dispatch_resume(source);
+ MUTEX_UNLOCK(&targetPrivate->lock);
+ return;
+ });
return TRUE;
}
static void
-dequeueAsyncDNSRetry(SCNetworkReachabilityRef target)
+dequeueLongLivedQuery(SCNetworkReachabilityRef target)
{
+ DNSServiceRef sdRef;
SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
- if (targetPrivate->dnsRetry != NULL) {
- dispatch_source_cancel(targetPrivate->dnsRetry);
- dispatch_release(targetPrivate->dnsRetry);
- targetPrivate->dnsRetry = NULL;
+ MUTEX_ASSERT_HELD(&targetPrivate->lock);
+
+ // terminate the [target] llq timer
+ if (targetPrivate->llqTimer != NULL) {
+ dispatch_source_cancel(targetPrivate->llqTimer);
+ dispatch_release(targetPrivate->llqTimer);
+ targetPrivate->llqTimer = NULL;
+ }
+
+ // terminate the [target] long lived query
+ sdRef = targetPrivate->llqTarget;
+ targetPrivate->llqTarget = NULL;
+
+ // mark the long lived query NOT active
+ targetPrivate->llqActive = FALSE;
+
+ if (sdRef != NULL) {
+ dispatch_async(_llq_queue(), ^{
+ DNSServiceRefDeallocate(sdRef);
+ CFRelease(target);
+
+ llqCount--;
+ if (llqCount == 0) {
+ // if no more queries active
+ DNSServiceRefDeallocate(llqMain);
+ llqMain = NULL;
+ }
+ });
}
return;
SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
- pthread_mutex_lock(&targetPrivate->lock);
+ MUTEX_LOCK(&targetPrivate->lock);
if (!targetPrivate->scheduled) {
// if not currently scheduled
- pthread_mutex_unlock(&targetPrivate->lock);
+ MUTEX_UNLOCK(&targetPrivate->lock);
return;
}
targetPrivate->log_prefix);
__SCNetworkReachabilityPerform(target);
- pthread_mutex_unlock(&targetPrivate->lock);
+ MUTEX_UNLOCK(&targetPrivate->lock);
return;
}
SCDynamicStoreRef store;
SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
+ MUTEX_ASSERT_HELD(&targetPrivate->lock);
+
// SCLog(_sc_debug, LOG_INFO,
// CFSTR("%s__SCNetworkReachabilityOnDemandCheck %s"),
// targetPrivate->log_prefix,
&onDemandServiceID,
&onDemandStatus,
&onDemandRemoteAddress);
- if (store_info->store != store) {
+ if ((store_info->store == NULL) && (store != NULL)) {
+ // if an SCDynamicStore session was added, keep it
store_info->store = store;
- store_info->storeAdded = TRUE;
}
if (!_SC_CFEqual(targetPrivate->onDemandRemoteAddress, onDemandRemoteAddress) ||
!_SC_CFEqual(targetPrivate->onDemandServiceID, onDemandServiceID)) {
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(options, kSCNetworkReachabilityOptionNodeName, onDemandRemoteAddress);
- CFDictionarySetValue(options, kSCNetworkReachabilityOptionConnectionOnDemandByPass, kCFBooleanTrue);
+ CFDictionarySetValue(options, kSCNetworkReachabilityOptionConnectionOnDemandBypass, kCFBooleanTrue);
+#ifdef HAVE_REACHABILITY_SERVER
+ CFDictionarySetValue(options, kSCNetworkReachabilityOptionServerBypass, kCFBooleanTrue);
+#endif // HAVE_REACHABILITY_SERVER
targetPrivate->onDemandServer = SCNetworkReachabilityCreateWithOptions(NULL, options);
CFRelease(options);
ReachabilityInfo my_info = NOT_REACHABLE;
Boolean ok = TRUE;
- *reach_info = NOT_REACHABLE;
+ MUTEX_ASSERT_HELD(&targetPrivate->lock);
+
+ _reach_set(reach_info, &NOT_REACHABLE, reach_info->cycle);
if (!isA_SCNetworkReachability(target)) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
+#ifdef HAVE_REACHABILITY_SERVER
+ if (!targetPrivate->serverBypass) {
+ if (!targetPrivate->serverActive) {
+ ok = __SCNetworkReachabilityServer_targetAdd(target);
+ if (!ok) {
+ targetPrivate->serverBypass = TRUE;
+ }
+ }
+
+ if (targetPrivate->serverActive) {
+ ok = __SCNetworkReachabilityServer_targetStatus(target);
+ if (!ok) {
+ SCLog(TRUE, LOG_DEBUG,
+ CFSTR("__SCNetworkReachabilityGetFlags _targetStatus() failed"));
+ _SCErrorSet(kSCStatusFailed);
+ goto done;
+ }
+
+ targetPrivate->cycle = targetPrivate->serverInfo.cycle;
+ _reach_set(&my_info, &targetPrivate->serverInfo, targetPrivate->cycle);
+ goto done;
+ }
+ }
+#endif // HAVE_REACHABILITY_SERVER
+
switch (targetPrivate->type) {
case reachabilityTypeAddress :
case reachabilityTypeAddressPair : {
struct timeval dnsQueryEnd;
int error;
SCNetworkReachabilityFlags ns_flags;
+ uint32_t ns_if_index;
struct addrinfo *res;
addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error);
if (!async) {
/* if not an async request */
goto checkResolvedAddress;
+ } else if (targetPrivate->llqActive) {
+ /* if long-lived-query active */
+ goto checkResolvedAddress;
} else if ((targetPrivate->dnsMP == MACH_PORT_NULL) && !targetPrivate->needResolve) {
struct timeval elapsed;
const struct timeval retry_limit = { EAI_NONAME_RETRY_LIMIT_USEC / USEC_PER_SEC,
targetPrivate->serv != NULL ? targetPrivate->serv : "");
enqueueAsyncDNSRetry(target);
-
break;
}
}
targetPrivate->name,
targetPrivate->serv,
targetPrivate->if_index,
+ &ns_if_index,
+ NULL,
targetPrivate->log_prefix);
if (!ok) {
/* if we could not get DNS server info */
break;
}
+ if (targetPrivate->resolverBypass) {
+ /* if we are not resolving the name,
+ * set the flags of the resolvers */
+ my_info.flags = ns_flags;
+ my_info.if_index = ns_if_index;
+ break;
+ }
+
if (async) {
/* for async requests we return the last known status */
my_info = targetPrivate->info;
- if (targetPrivate->dnsPort != NULL) {
+ if (targetPrivate->dnsMP != MACH_PORT_NULL) {
/* if request already in progress */
SCLog(_sc_debug, LOG_INFO,
CFSTR("%swaiting for DNS reply"),
break;
}
+ if (targetPrivate->llqActive) {
+ /* if long-lived-query active */
+ SCLog(_sc_debug, LOG_INFO,
+ CFSTR("%swaiting for DNS updates"),
+ targetPrivate->log_prefix);
+ if ((addresses != NULL) || (error != NETDB_SUCCESS)) {
+ /* updated reachability based on the previous reply */
+ goto checkResolvedAddress;
+ }
+ break;
+ }
+
+ if (!targetPrivate->llqBypass) {
+ SCLog(_sc_debug, LOG_INFO,
+ CFSTR("%sstart long-lived DNS query for %s%s%s%s%s"),
+ targetPrivate->log_prefix,
+ targetPrivate->name != NULL ? "name = " : "",
+ targetPrivate->name != NULL ? targetPrivate->name : "",
+ targetPrivate->name != NULL && targetPrivate->serv != NULL ? ", " : "",
+ targetPrivate->serv != NULL ? "serv = " : "",
+ targetPrivate->serv != NULL ? targetPrivate->serv : "");
+
+ /*
+ * initiate an long-lived DNS query
+ */
+ if (enqueueLongLivedQuery(target)) {
+ /* request initiated */
+ break;
+ }
+ }
+
SCLog(_sc_debug, LOG_INFO,
CFSTR("%sstart DNS query for %s%s%s%s%s"),
targetPrivate->log_prefix,
/*
* initiate an async DNS query
*/
- if (!startAsyncDNSQuery(target)) {
- /* if we could not initiate the request, process error */
- goto checkResolvedAddress;
+ if (startAsyncDNSQuery(target)) {
+ /* request initiated */
+ break;
}
- /* request initiated */
- break;
+ /* if we could not initiate the request, process error */
+ goto checkResolvedAddress;
}
SCLog(_sc_debug, LOG_INFO,
__dns_query_end(target,
((error == 0) && (res != NULL)), // if successful query
- FALSE, // sync
+ dns_query_sync, // sync
&dnsQueryStart, // start time
&dnsQueryEnd); // end time
done:
- *reach_info = my_info;
+ _reach_set(reach_info, &my_info, targetPrivate->cycle);
error :
return ok;
}
+int
+SCNetworkReachabilityGetInterfaceIndex(SCNetworkReachabilityRef target)
+{
+ SCNetworkReachabilityFlags flags;
+ int if_index = -1;
+ Boolean ok = TRUE;
+ ReachabilityStoreInfo store_info;
+ SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
+
+ if (!isA_SCNetworkReachability(target)) {
+ _SCErrorSet(kSCStatusInvalidArgument);
+ return if_index;
+ }
+
+ ReachabilityStoreInfo_init(&store_info);
+
+ MUTEX_LOCK(&targetPrivate->lock);
+
+ if (targetPrivate->scheduled) {
+ // if being watched, return the last known (and what should be current) status
+ flags = targetPrivate->info.flags & ~kSCNetworkReachabilityFlagsFirstResolvePending;
+ goto done;
+ }
+
+
+ ok = __SCNetworkReachabilityGetFlags(&store_info, target, &targetPrivate->info, FALSE);
+ flags = targetPrivate->info.flags & ~kSCNetworkReachabilityFlagsFirstResolvePending;
+
+ done :
+
+ /* Only return the if_index if the connection is reachable not for reachable connection
+ * required etc ... */
+ if (ok && rankReachability(flags) == 2) {
+ if_index = targetPrivate->info.if_index;
+ }
+
+ MUTEX_UNLOCK(&targetPrivate->lock);
+ ReachabilityStoreInfo_free(&store_info);
+ return if_index;
+}
+
Boolean
SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target,
return FALSE;
}
- initReachabilityStoreInfo(&store_info);
- pthread_mutex_lock(&targetPrivate->lock);
+ ReachabilityStoreInfo_init(&store_info);
+
+ MUTEX_LOCK(&targetPrivate->lock);
if (targetPrivate->scheduled) {
// if being watched, return the last known (and what should be current) status
done :
- pthread_mutex_unlock(&targetPrivate->lock);
- freeReachabilityStoreInfo(&store_info);
+ MUTEX_UNLOCK(&targetPrivate->lock);
+ ReachabilityStoreInfo_free(&store_info);
return ok;
}
#endif // TARGET_OS_IPHONE
+ // SCDynamicStore key to force posting a reachability change
+ CFArrayAppendValue(keys, SCNETWORKREACHABILITY_TRIGGER_KEY);
+
(void)SCDynamicStoreSetNotificationKeys(store, keys, patterns);
CFRelease(keys);
CFRelease(patterns);
}
+static dispatch_queue_t
+_hn_queue()
+{
+ static dispatch_once_t once;
+ static dispatch_queue_t q;
+
+ dispatch_once(&once, ^{
+ q = dispatch_queue_create("SCNetworkReachabilty.changes", NULL);
+ });
+
+ return q;
+}
+
+
static void
__SCNetworkReachabilityHandleChanges(SCDynamicStoreRef store,
CFArrayRef changedKeys,
#endif // !TARGET_OS_IPHONE
Boolean dnsConfigChanged = FALSE;
CFIndex i;
+ Boolean forcedChange = FALSE;
CFStringRef key;
- CFIndex nChanges = CFArrayGetCount(changedKeys);
+ CFIndex nChanges;
+ CFIndex nGlobals = 0;
CFIndex nTargets;
+ Boolean networkConfigChanged = FALSE;
struct timeval now;
#if !TARGET_OS_IPHONE
Boolean powerStatusChanged = FALSE;
ReachabilityStoreInfo store_info;
const void * targets_q[N_QUICK];
const void ** targets = targets_q;
+ __block CFSetRef watchers = NULL;
+ nChanges = CFArrayGetCount(changedKeys);
if (nChanges == 0) {
/* if no changes */
return;
}
- pthread_mutex_lock(&hn_lock);
+ /* "something" changed, start fresh */
+ ReachabilityStoreInfo_save(NULL);
+
+ dispatch_sync(_hn_queue(), ^{
+ /* grab the currently watched targets */
+ if (hn_targets != NULL) {
+ watchers = CFSetCreateCopy(NULL, hn_targets);
+ }
+ });
- nTargets = (hn_targets != NULL) ? CFSetGetCount(hn_targets) : 0;
+ nTargets = (watchers != NULL) ? CFSetGetCount(watchers) : 0;
if (nTargets == 0) {
/* if no addresses being monitored */
goto done;
if (CFArrayContainsValue(changedKeys, CFRangeMake(0, nChanges), key)) {
CFNumberRef num;
+ nGlobals++;
+
num = SCDynamicStoreCopyValue(store, key);
if (num != NULL) {
if (isA_CFNumber(num) &&
kSCDynamicStoreDomainState,
kSCEntNetDNS);
if (CFArrayContainsValue(changedKeys, CFRangeMake(0, nChanges), key)) {
+ nGlobals++;
dnsConfigChanged = TRUE; /* the DNS server(s) have changed */
}
CFRelease(key);
+ if (CFArrayContainsValue(changedKeys, CFRangeMake(0, nChanges), SCNETWORKREACHABILITY_TRIGGER_KEY)) {
+ nGlobals++;
+ forcedChange = TRUE; /* an SCDynamicStore driven "network" change */
+ }
+
+ if (nChanges > nGlobals) {
+ networkConfigChanged = TRUE;
+ }
+
if (_sc_debug) {
unsigned int changes = 0;
static const char *change_strings[] = {
"DNS and power ",
"network, DNS, and power ",
- // with no "power" status change (including CPU "on")
+ // with "power" status change (including CPU "on")
"power* ",
"network and power* ",
"DNS and power* ",
if (cpuStatusChanged) {
changes += PWR;
}
- nChanges -= 1;
}
#endif // !TARGET_OS_IPHONE
#define DNS 2
if (dnsConfigChanged) {
changes |= DNS;
- nChanges -= 1;
}
#define NET 1
- if (nChanges > 0) {
+ if (networkConfigChanged) {
changes |= NET;
}
SCLog(TRUE, LOG_INFO,
- CFSTR("process %sconfiguration change"),
+ CFSTR("process %s%sconfiguration change"),
+ forcedChange ? "[forced] " : "",
change_strings[changes]);
}
- initReachabilityStoreInfo(&store_info);
+ ReachabilityStoreInfo_init(&store_info);
if (nTargets > (CFIndex)(sizeof(targets_q) / sizeof(CFTypeRef)))
targets = CFAllocatorAllocate(NULL, nTargets * sizeof(CFTypeRef), 0);
- CFSetGetValues(hn_targets, targets);
+ CFSetGetValues(watchers, targets);
for (i = 0; i < nTargets; i++) {
SCNetworkReachabilityRef target = targets[i];
SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
- pthread_mutex_lock(&targetPrivate->lock);
+ MUTEX_LOCK(&targetPrivate->lock);
if (dnsConfigChanged) {
targetPrivate->last_dns = now;
targetPrivate->dnsRetryCount = 0;
}
+ if (networkConfigChanged) {
+ targetPrivate->last_network = now;
+ }
+
+#if !TARGET_OS_IPHONE
+ if (powerStatusChanged) {
+ targetPrivate->last_power = now;
+ }
+#endif // !TARGET_OS_IPHONE
+
if (targetPrivate->type == reachabilityTypeName) {
Boolean dnsChanged = dnsConfigChanged;
Boolean ok;
/* check the reachability of the DNS servers */
- ok = updateReachabilityStoreInfo(&store_info, &store, AF_UNSPEC);
+ ok = ReachabilityStoreInfo_update(&store_info, &store, AF_UNSPEC);
if (ok) {
ok = _SC_R_checkResolverReachability(&store_info,
&ns_flags,
targetPrivate->name,
targetPrivate->serv,
targetPrivate->if_index,
+ NULL,
+ NULL,
targetPrivate->log_prefix);
}
}
if (dnsChanged) {
- if (targetPrivate->dnsPort != NULL) {
- mach_port_t mp = CFMachPortGetPort(targetPrivate->dnsPort);
-
+ if (targetPrivate->dnsMP != MACH_PORT_NULL) {
/* cancel the outstanding DNS query */
SCLog(_sc_debug, LOG_INFO,
CFSTR("%scancel DNS query for %s%s%s%s%s"),
targetPrivate->name != NULL && targetPrivate->serv != NULL ? ", " : "",
targetPrivate->serv != NULL ? "serv = " : "",
targetPrivate->serv != NULL ? targetPrivate->serv : "");
- dequeueAsyncDNSQuery(target);
- getaddrinfo_async_cancel(mp);
+ dequeueAsyncDNSQuery(target, TRUE);
}
if (targetPrivate->dnsRetry != NULL) {
}
}
- __SCNetworkReachabilityPerform(target);
+ if (forcedChange) {
+ targetPrivate->cycle++;
+ }
+
+ if (targetPrivate->scheduled) {
+ __SCNetworkReachabilityPerform(target);
+ }
- pthread_mutex_unlock(&targetPrivate->lock);
+ MUTEX_UNLOCK(&targetPrivate->lock);
}
if (targets != targets_q) CFAllocatorDeallocate(NULL, targets);
- freeReachabilityStoreInfo(&store_info);
+ ReachabilityStoreInfo_free(&store_info);
done :
- pthread_mutex_unlock(&hn_lock);
+ if (watchers != NULL) CFRelease(watchers);
return;
}
static void
-rlsPerform(void *info)
+reachPerform(void *info)
{
void *context_info;
void (*context_release)(const void *);
+ uint64_t cycle;
Boolean defer = FALSE;
+ Boolean forced;
Boolean ok;
ReachabilityInfo reach_info = NOT_REACHABLE;
SCNetworkReachabilityCallBack rlsFunction;
targetPrivate->log_prefix);
- pthread_mutex_lock(&targetPrivate->lock);
+ MUTEX_LOCK(&targetPrivate->lock);
if (targetPrivate->dnsRetry != NULL) {
// cancel DNS retry
if (!targetPrivate->scheduled) {
// if not currently scheduled
- pthread_mutex_unlock(&targetPrivate->lock);
+ MUTEX_UNLOCK(&targetPrivate->lock);
return;
}
/* update reachability, notify if status changed */
- initReachabilityStoreInfo(&store_info);
+ ReachabilityStoreInfo_init(&store_info);
ok = __SCNetworkReachabilityGetFlags(&store_info, target, &reach_info, TRUE);
- freeReachabilityStoreInfo(&store_info);
+ ReachabilityStoreInfo_free(&store_info);
if (!ok) {
/* if reachability status not available */
SCLog(_sc_debug, LOG_INFO, CFSTR("%flags not available"),
* the same or "better"
*/
defer = !darkWakeNotify(target);
- } else if (__reach_equal(&targetPrivate->last_notify, &reach_info)) {
+ } else if (!__reach_changed(&targetPrivate->last_notify, &reach_info)) {
/*
* if we have already posted this change
*/
}
#endif // !TARGET_OS_IPHONE
- if (__reach_equal(&targetPrivate->info, &reach_info)) {
+ cycle = targetPrivate->cycle;
+ forced = ((cycle != 0) && (targetPrivate->info.cycle != cycle));
+
+ if (!forced && !__reach_changed(&targetPrivate->info, &reach_info)) {
if (_sc_debug) {
if (targetPrivate->info.sleeping == reach_info.sleeping) {
SCLog(TRUE, LOG_INFO,
}
}
- pthread_mutex_unlock(&targetPrivate->lock);
+ MUTEX_UNLOCK(&targetPrivate->lock);
return;
}
SCLog(_sc_debug, LOG_INFO,
- CFSTR("%sflags/interface have changed (was 0x%08x/%hu%s, now 0x%08x/%hu%s)%s"),
+ CFSTR("%sflags/interface have changed (was 0x%08x/%hu%s, now 0x%08x/%hu%s)%s%s"),
targetPrivate->log_prefix,
targetPrivate->info.flags,
targetPrivate->info.if_index,
reach_info.flags,
reach_info.if_index,
reach_info.sleeping ? ", z" : "",
- defer ? ", deferred" : "");
+ defer ? ", deferred" : "",
+ forced ? ", forced" : "");
/* as needed, defer the notification */
if (defer) {
- pthread_mutex_unlock(&targetPrivate->lock);
+ MUTEX_UNLOCK(&targetPrivate->lock);
return;
}
/* update flags / interface */
- targetPrivate->info = reach_info;
+ _reach_set(&targetPrivate->info, &reach_info, cycle);
/* save last notification info */
- targetPrivate->last_notify = reach_info;
+ _reach_set(&targetPrivate->last_notify, &reach_info, cycle);
+
+ /* save last notification time */
+ (void)gettimeofday(&targetPrivate->last_push, NULL);
/* callout */
rlsFunction = targetPrivate->rlsFunction;
context_release = NULL;
}
- pthread_mutex_unlock(&targetPrivate->lock);
+ MUTEX_UNLOCK(&targetPrivate->lock);
if (rlsFunction != NULL) {
(*rlsFunction)(target,
{
SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
- pthread_mutex_lock(&targetPrivate->lock);
+ MUTEX_LOCK(&targetPrivate->lock);
if (targetPrivate->rlsContext.release != NULL) {
/* let go of the current context */
}
}
- pthread_mutex_unlock(&targetPrivate->lock);
+ MUTEX_UNLOCK(&targetPrivate->lock);
return TRUE;
}
{
SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
Boolean init = FALSE;
- Boolean ok = FALSE;
+ __block Boolean ok = FALSE;
- if (!onDemand) {
- pthread_mutex_lock(&hn_lock);
- }
- pthread_mutex_lock(&targetPrivate->lock);
+ MUTEX_LOCK(&targetPrivate->lock);
if ((targetPrivate->dispatchQueue != NULL) || // if we are already scheduled with a dispatch queue
((queue != NULL) && targetPrivate->scheduled)) { // if we are already scheduled on a CFRunLoop
goto done;
}
- /* schedule the SCNetworkReachability run loop source */
-
- if (!onDemand && (hn_store == NULL)) {
- /*
- * if we are not monitoring any hosts, start watching
- */
- if (!dns_configuration_watch()) {
- // if error
- _SCErrorSet(kSCStatusFailed);
- goto done;
+#ifdef HAVE_REACHABILITY_SERVER
+ if (!targetPrivate->serverBypass) {
+ if (!targetPrivate->serverActive) {
+ ok = __SCNetworkReachabilityServer_targetAdd(target);
+ if (!ok) {
+ targetPrivate->serverBypass = TRUE;
+ }
}
- hn_store = SCDynamicStoreCreate(NULL,
- CFSTR("SCNetworkReachability"),
- __SCNetworkReachabilityHandleChanges,
- NULL);
- if (hn_store == NULL) {
- SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreCreate() failed"));
- goto done;
- }
+ if (targetPrivate->serverActive) {
+ if (targetPrivate->scheduled) {
+ // if already scheduled
+ goto watch;
+ }
- __SCNetworkReachabilityReachabilitySetNotifications(hn_store);
+ ok = __SCNetworkReachabilityServer_targetSchedule(target);
+ if (!ok) {
+ SCLog(TRUE, LOG_DEBUG,
+ CFSTR("__SCNetworkReachabilityScheduleWithRunLoop _targetMonitor() failed"));
+ _SCErrorSet(kSCStatusFailed);
+ goto done;
+ }
- hn_dispatchQueue = dispatch_queue_create("com.apple.SCNetworkReachabilty.network_changes", NULL);
- if (hn_dispatchQueue == NULL) {
- SCLog(TRUE, LOG_ERR, CFSTR("__SCNetworkReachabilityScheduleWithRunLoop dispatch_queue_create() failed"));
- _SCErrorSet(kSCStatusFailed);
- CFRelease(hn_store);
- hn_store = NULL;
- goto done;
+ goto watch;
}
- CFRetain(hn_store); // Note: will be released when the dispatch queue is released
- dispatch_set_context(hn_dispatchQueue, (void *)hn_store);
- dispatch_set_finalizer_f(hn_dispatchQueue, (dispatch_function_t)CFRelease);
+ }
+#endif // HAVE_REACHABILITY_SERVER
- ok = SCDynamicStoreSetDispatchQueue(hn_store, hn_dispatchQueue);
- if (!ok) {
- SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreSetDispatchQueue() failed"));
- dispatch_release(hn_dispatchQueue);
- hn_dispatchQueue = NULL;
- CFRelease(hn_store);
- hn_store = NULL;
- goto done;
+ /* schedule the SCNetworkReachability did-something-change handler */
+
+ dispatch_sync(_hn_queue(), ^{
+ ok = FALSE;
+
+ if (!onDemand && (hn_store == NULL)) {
+ /*
+ * if we are not monitoring any hosts, start watching
+ */
+ if (!dns_configuration_watch()) {
+ // if error
+ _SCErrorSet(kSCStatusFailed);
+ return;
+ }
+
+ hn_store = SCDynamicStoreCreate(NULL,
+ CFSTR("SCNetworkReachability"),
+ __SCNetworkReachabilityHandleChanges,
+ NULL);
+ if (hn_store == NULL) {
+ SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreCreate() failed"));
+ dns_configuration_unwatch();
+ return;
+ }
+
+ __SCNetworkReachabilityReachabilitySetNotifications(hn_store);
+
+ hn_dispatchQueue = dispatch_queue_create("SCNetworkReachabilty.changes", NULL);
+ if (hn_dispatchQueue == NULL) {
+ SCLog(TRUE, LOG_ERR, CFSTR("__SCNetworkReachabilityScheduleWithRunLoop dispatch_queue_create() failed"));
+ CFRelease(hn_store);
+ hn_store = NULL;
+ dns_configuration_unwatch();
+ _SCErrorSet(kSCStatusFailed);
+ return;
+ }
+
+ ok = SCDynamicStoreSetDispatchQueue(hn_store, hn_dispatchQueue);
+ if (!ok) {
+ SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreSetDispatchQueue() failed"));
+ dispatch_release(hn_dispatchQueue);
+ hn_dispatchQueue = NULL;
+ CFRelease(hn_store);
+ hn_store = NULL;
+ dns_configuration_unwatch();
+ return;
+ }
+ hn_targets = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
+
+ ReachabilityStoreInfo_enable(TRUE);
}
- hn_targets = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
+
+ CFSetAddValue(hn_targets, target);
+
+ ok = TRUE;
+ });
+
+ if (!ok) {
+ goto done;
}
+#ifdef HAVE_REACHABILITY_SERVER
+ watch :
+#endif // HAVE_REACHABILITY_SERVER
+
if (!targetPrivate->scheduled) {
CFRunLoopSourceContext context = { 0 // version
, (void *)target // info
, CFHash // hash
, NULL // schedule
, NULL // cancel
- , rlsPerform // perform
+ , reachPerform // perform
};
if (runLoop != NULL) {
targetPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
}
- targetPrivate->scheduled = TRUE;
if (targetPrivate->type == reachabilityTypeName) {
+ /*
+ * we're now scheduled so let's ensure that we
+ * are starting with a clean slate before we
+ * resolve the name
+ */
+ if (targetPrivate->resolvedAddress != NULL) {
+ CFRelease(targetPrivate->resolvedAddress);
+ targetPrivate->resolvedAddress = NULL;
+ }
+ targetPrivate->resolvedAddressError = NETDB_SUCCESS;
targetPrivate->needResolve = TRUE;
+ _reach_set(&targetPrivate->info, &NOT_REACHABLE, targetPrivate->info.cycle);
+ targetPrivate->info.flags |= kSCNetworkReachabilityFlagsFirstResolvePending;
+#ifdef HAVE_REACHABILITY_SERVER
+ _reach_set(&targetPrivate->serverInfo, &NOT_REACHABLE, targetPrivate->serverInfo.cycle);
+ targetPrivate->serverInfo.flags |= kSCNetworkReachabilityFlagsFirstResolvePending;
+#endif // HAVE_REACHABILITY_SERVER
}
+
+ targetPrivate->scheduled = TRUE;
+
init = TRUE;
}
if (queue != NULL) {
+ // retain dispatch queue
+ dispatch_retain(queue);
targetPrivate->dispatchQueue = queue;
- dispatch_retain(targetPrivate->dispatchQueue);
+
+ //
+ // We've taken a reference to the client's dispatch_queue and we
+ // want to hold on to that reference until we've processed any/all
+ // notifications. To facilitate this we create a group, dispatch
+ // any notification blocks to via that group, and when the caller
+ // has told us to stop the notifications (unschedule) we wait for
+ // the group to empty and use the group's finalizer to release
+ // our reference to the client's queue.
+ //
+
+ // make sure that we have group to track any async requests
+ targetPrivate->dispatchGroup = dispatch_group_create();
+
+ // retain the target ... and release it when the group is released
+ CFRetain(target);
+ dispatch_set_context(targetPrivate->dispatchGroup, (void *)target);
+ dispatch_set_finalizer_f(targetPrivate->dispatchGroup, (dispatch_function_t)CFRelease);
} else {
if (!_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
/*
CFRunLoopAddSource(runLoop, targetPrivate->rls, runLoopMode);
if (targetPrivate->dnsRLS != NULL) {
- /* if we have an active async DNS query too */
+ // if we have an active async DNS query too
CFRunLoopAddSource(runLoop, targetPrivate->dnsRLS, runLoopMode);
}
}
_SC_schedule(target, runLoop, runLoopMode, targetPrivate->rlList);
}
- CFSetAddValue(hn_targets, target);
-
if (init) {
ReachabilityInfo reach_info = NOT_REACHABLE;
ReachabilityStoreInfo store_info;
* if we have yet to schedule SC notifications for this address
* - initialize current reachability status
*/
- initReachabilityStoreInfo(&store_info);
+ ReachabilityStoreInfo_init(&store_info);
if (__SCNetworkReachabilityGetFlags(&store_info, target, &reach_info, TRUE)) {
/*
* if reachability status available
* - set flags
* - schedule notification to report status via callback
*/
- targetPrivate->info = reach_info;
+#ifdef HAVE_REACHABILITY_SERVER
+ reach_info.flags |= (targetPrivate->info.flags & kSCNetworkReachabilityFlagsFirstResolvePending);
+#endif // HAVE_REACHABILITY_SERVER
+ _reach_set(&targetPrivate->info, &reach_info, targetPrivate->cycle);
__SCNetworkReachabilityPerform(target);
} else {
/* if reachability status not available, async lookup started */
- targetPrivate->info = NOT_REACHABLE;
+ _reach_set(&targetPrivate->info, &NOT_REACHABLE, targetPrivate->cycle);
+#ifdef HAVE_REACHABILITY_SERVER
+ _reach_set(&targetPrivate->serverInfo, &NOT_REACHABLE, targetPrivate->cycle);
+#endif // HAVE_REACHABILITY_SERVER
}
- freeReachabilityStoreInfo(&store_info);
+ ReachabilityStoreInfo_free(&store_info);
}
if (targetPrivate->onDemandServer != NULL) {
done :
- pthread_mutex_unlock(&targetPrivate->lock);
- if (!onDemand) {
- pthread_mutex_unlock(&hn_lock);
- }
+ MUTEX_UNLOCK(&targetPrivate->lock);
return ok;
}
CFStringRef runLoopMode,
Boolean onDemand)
{
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
+ dispatch_group_t drainGroup = NULL;
+ dispatch_queue_t drainQueue = NULL;
CFIndex n = 0;
Boolean ok = FALSE;
+ SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
- if (!onDemand) {
- pthread_mutex_lock(&hn_lock);
- }
- pthread_mutex_lock(&targetPrivate->lock);
+ // hold a reference while we unschedule
+ CFRetain(target);
+
+ MUTEX_LOCK(&targetPrivate->lock);
if (((runLoop == NULL) && (targetPrivate->dispatchQueue == NULL)) || // if we should be scheduled on a dispatch queue (but are not)
((runLoop != NULL) && (targetPrivate->dispatchQueue != NULL))) { // if we should be scheduled on a CFRunLoop (but are not)
goto done;
}
- // first, unschedule the target specific sources
+ // unschedule the target specific sources
if (targetPrivate->dispatchQueue != NULL) {
if (targetPrivate->onDemandServer != NULL) {
__SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, NULL, NULL, TRUE);
}
+
+ // save dispatchQueue, release reference when we've queue'd blocks complete, allow re-scheduling
+ drainGroup = targetPrivate->dispatchGroup;
+ targetPrivate->dispatchGroup = NULL;
+ drainQueue = targetPrivate->dispatchQueue;
+ targetPrivate->dispatchQueue = NULL;
} else {
if (!_SC_unschedule(target, runLoop, runLoopMode, targetPrivate->rlList, FALSE)) {
// if not currently scheduled
}
if (n == 0) {
+#ifdef HAVE_REACHABILITY_SERVER
+ //
+ // Cancel our request for server monitoring
+ //
+ if (targetPrivate->serverActive) {
+ ok = __SCNetworkReachabilityServer_targetUnschedule(target);
+ if (!ok) {
+ SCLog(TRUE, LOG_DEBUG,
+ CFSTR("__SCNetworkReachabilityUnscheduleFromRunLoop _targetMonitor() failed"));
+ _SCErrorSet(kSCStatusFailed);
+ }
+ }
+#endif // HAVE_REACHABILITY_SERVER
+
// if *all* notifications have been unscheduled
targetPrivate->scheduled = FALSE;
+ }
- if (!onDemand) {
- CFSetRemoveValue(hn_targets, target); // cleanup notification resources
- }
-
- if (targetPrivate->dnsPort != NULL) {
- mach_port_t mp = CFMachPortGetPort(targetPrivate->dnsPort);
+#ifdef HAVE_REACHABILITY_SERVER
+ if (targetPrivate->serverActive) {
+ goto unwatch;
+ }
+#endif // HAVE_REACHABILITY_SERVER
+ if (n == 0) {
+ if (targetPrivate->dnsMP != MACH_PORT_NULL) {
// if we have an active async DNS query
- dequeueAsyncDNSQuery(target);
- getaddrinfo_async_cancel(mp);
+ dequeueAsyncDNSQuery(target, TRUE);
}
if (targetPrivate->dnsRetry != NULL) {
// if we have an outstanding DNS retry
dequeueAsyncDNSRetry(target);
}
- }
- if (runLoop == NULL) {
- dispatch_release(targetPrivate->dispatchQueue);
- targetPrivate->dispatchQueue = NULL;
- }
+ if (targetPrivate->llqActive) {
+ // if we have a long-lived-query
+ dequeueLongLivedQuery(target);
+ }
- n = CFSetGetCount(hn_targets);
- if (n == 0) {
- // if we are no longer monitoring any targets
- SCDynamicStoreSetDispatchQueue(hn_store, NULL);
- dispatch_release(hn_dispatchQueue);
- hn_dispatchQueue = NULL;
- CFRelease(hn_store);
- hn_store = NULL;
- CFRelease(hn_targets);
- hn_targets = NULL;
+ dispatch_sync(_hn_queue(), ^{
+ CFSetRemoveValue(hn_targets, target);
- /*
- * until we start monitoring again, ensure that
- * any resources associated with tracking the
- * DNS configuration have been released.
- */
- dns_configuration_unwatch();
+ if (onDemand) {
+ return;
+ }
+
+ if (CFSetGetCount(hn_targets) > 0) {
+ return;
+ }
+
+ // if we are no longer monitoring any targets
+ SCDynamicStoreSetDispatchQueue(hn_store, NULL);
+ dispatch_release(hn_dispatchQueue);
+ hn_dispatchQueue = NULL;
+ CFRelease(hn_store);
+ hn_store = NULL;
+ CFRelease(hn_targets);
+ hn_targets = NULL;
+
+ ReachabilityStoreInfo_enable(FALSE);
+ ReachabilityStoreInfo_save(NULL);
+
+ /*
+ * until we start monitoring again, ensure that
+ * any resources associated with tracking the
+ * DNS configuration have been released.
+ */
+ dns_configuration_unwatch();
+ });
}
+#ifdef HAVE_REACHABILITY_SERVER
+ unwatch :
+#endif // HAVE_REACHABILITY_SERVER
+
SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%sunscheduled"),
targetPrivate->log_prefix);
done :
- pthread_mutex_unlock(&targetPrivate->lock);
- if (!onDemand) {
- pthread_mutex_unlock(&hn_lock);
+ MUTEX_UNLOCK(&targetPrivate->lock);
+
+ if (drainGroup != NULL) {
+ dispatch_group_notify(drainGroup, __SCNetworkReachability_concurrent_queue(), ^{
+ // release group/queue references
+ dispatch_release(drainQueue);
+ dispatch_release(drainGroup); // releases our target reference
+ });
}
+
+ // release our reference
+ CFRelease(target);
+
return ok;
}