- switch (address->sa_family) {
- case AF_INET :
- /* 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
- */
- /* 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 :
- /* 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 :
- break;
- }
-
- if (bcmp(addr1, addr2, len) == 0) {
- statusMessage = "isReachable (is interface address)";
- reach_info->flags |= kSCNetworkReachabilityFlagsIsLocalAddress;
- }
- }
-
- if (!(info->rtm->rtm_flags & RTF_GATEWAY) &&
- (info->rti_info[RTAX_GATEWAY] != NULL) &&
- (info->rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) &&
- !(ifr->ifr_flags & IFF_POINTOPOINT)) {
- reach_info->flags |= kSCNetworkReachabilityFlagsIsDirect;
- }
-
- bzero(if_name, IFNAMSIZ);
- bcopy(info->sdl->sdl_data,
- 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) {
- SCLog(TRUE, LOG_INFO, CFSTR("%s status = %s"), log_prefix, statusMessage);
- SCLog(TRUE, LOG_INFO, CFSTR("%s device = %s (%hu)"), log_prefix, if_name, info->sdl->sdl_index);
- SCLog(TRUE, LOG_INFO, CFSTR("%s sdl_type = 0x%x"), log_prefix, info->sdl->sdl_type);
- SCLog(TRUE, LOG_INFO, CFSTR("%s ifr_flags = 0x%04hx"), log_prefix, ifr->ifr_flags);
- SCLog(TRUE, LOG_INFO, CFSTR("%s rtm_flags = 0x%08x"), log_prefix, info->rtm->rtm_flags);
- }
-
- done :
- if (isock != -1) (void)close(isock);
- return ret;
-}
-
-
-static Boolean
-checkAddress(ReachabilityStoreInfoRef store_info,
- const struct sockaddr *address,
- unsigned int if_index,
- ReachabilityInfo *reach_info,
- const char *log_prefix)
-{
- route_info info;
- struct ifreq ifr;
- char if_name[IFNAMSIZ];
- nwi_ifstate_t ifstate;
- nwi_state_t nwi_state;
- int ret;
- int sc_status = kSCStatusReachabilityUnknown;
-
- _reach_set(reach_info, &NOT_REACHABLE, reach_info->cycle, if_index, NULL);
-
- nwi_state = nwi_state_copy();
-
- if (address != NULL) {
- ret = checkAddress_route(address,
- if_index,
- if_name,
- &ifr,
- reach_info,
- &info,
- &sc_status,
- log_prefix);
- } else {
- /* special case: check only for available paths off the system */
- ret = EHOSTUNREACH;
- }
-
- if (ret == 0) {
- const struct sockaddr *vpn_server_address;
-
- sc_status = kSCStatusOK;
-
- ifstate = nwi_state_get_ifstate(nwi_state, if_name);
- if (ifstate == NULL) {
- goto done;
- }
-
- reach_info->flags |= nwi_ifstate_get_reachability_flags(ifstate);
-
-
- vpn_server_address = nwi_ifstate_get_vpn_server(ifstate);
- if (vpn_server_address != NULL) {
- char dst_if_name[IFNAMSIZ];
- route_info dst_info;
-
- ret = route_get(vpn_server_address, 0, &dst_info);
- if (ret != 0) {
- goto done;
- }
-
- bzero(&dst_if_name, sizeof(dst_if_name));
- bcopy(dst_info.sdl->sdl_data,
- dst_if_name,
- (dst_info.sdl->sdl_nlen <= IFNAMSIZ) ? dst_info.sdl->sdl_nlen : IFNAMSIZ);
- if (bcmp(if_name, dst_if_name, sizeof(if_name)) != 0) {
- nwi_ifstate_t ifstate;
-
- ifstate = nwi_state_get_ifstate(nwi_state, dst_if_name);
- if (ifstate != NULL) {
- reach_info->flags |= nwi_ifstate_get_reachability_flags(ifstate);
- }
- }
- }
- } else if (ret == EHOSTUNREACH) {
- if (if_index == 0) {
- int af;
-
- // if not "scoped" request
- af = (address != NULL) ? address->sa_family : AF_UNSPEC;
- reach_info->flags |= nwi_state_get_reachability_flags(nwi_state, af);
- sc_status = kSCStatusOK;
- } else {
- // if "scoped" request
- sc_status = kSCStatusNoKey;
- }
- }
-
- done:
-
- if (reach_info->flags == 0) {
- SCLog(_sc_debug, LOG_INFO, CFSTR("%s cannot be reached"), log_prefix);
- }
-
- if (nwi_state != NULL) {
- nwi_state_release(nwi_state);
- }
-
- if ((sc_status != kSCStatusOK) && (sc_status != kSCStatusNoKey)) {
- _SCErrorSet(sc_status);
- return FALSE;
- }
-
- return TRUE;
-}
-
-
-#pragma mark -
-#pragma mark SCNetworkReachability APIs
-
-
-static __inline__ CFTypeRef
-isA_SCNetworkReachability(CFTypeRef obj)
-{
- return (isA_CFType(obj, SCNetworkReachabilityGetTypeID()));
-}
-
-
-static Boolean
-addr_to_PTR_name(const struct sockaddr *sa, char *name, size_t name_len)
-{
- int n;
-
- switch (sa->sa_family) {
- case AF_INET : {
- union {
- in_addr_t s_addr;
- unsigned char b[4];
- } rev;
- /* ALIGN: assuming sa is aligned, then cast ok. */
- struct sockaddr_in *sin = (struct sockaddr_in *)(void *)sa;
-
- /*
- * build "PTR" query name
- * NNN.NNN.NNN.NNN.in-addr.arpa.
- */
- rev.s_addr = sin->sin_addr.s_addr;
- n = snprintf(name, name_len, "%u.%u.%u.%u.in-addr.arpa.",
- rev.b[3],
- rev.b[2],
- rev.b[1],
- rev.b[0]);
- if ((n == -1) || (n >= name_len)) {
- return FALSE;
- }
-
- break;
- }
-
- case AF_INET6 : {
- int i;
- int s = 0;
- /* ALIGN: assume sa is aligned, cast ok. */
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)(void *)sa;
- size_t x = name_len;
-
- /*
- * build IPv6 "nibble" PTR query name (RFC 1886, RFC 3152)
- * N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.ip6.arpa.
- */
- for (i = sizeof(sin6->sin6_addr) - 1; i >= 0; i--) {
- n = snprintf(&name[s], x, "%x.%x.",
- ( sin6->sin6_addr.s6_addr[i] & 0xf),
- ((sin6->sin6_addr.s6_addr[i] >> 4) & 0xf));
- if ((n == -1) || (n >= x)) {
- return FALSE;
- }
-
- s += n;
- x -= n;
- }
-
- n = snprintf(&name[s], x, "ip6.arpa.");
- if ((n == -1) || (n >= x)) {
- return FALSE;
- }
-
- break;
- }
-
- default :
- return FALSE;
- }
-
- return TRUE;
-}
-
-
-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 : {
- char buf[64];
-
- if (targetPrivate->localAddress != NULL) {
- _SC_sockaddr_to_string(targetPrivate->localAddress, buf, sizeof(buf));
- CFStringAppendFormat(str, NULL, CFSTR("local address = %s"),
- buf);
- }
-
- if (targetPrivate->remoteAddress != NULL) {
- _SC_sockaddr_to_string(targetPrivate->remoteAddress, buf, sizeof(buf));
- CFStringAppendFormat(str, NULL, CFSTR("%s%saddress = %s"),
- targetPrivate->localAddress ? ", " : "",
- (targetPrivate->type == reachabilityTypeAddressPair) ? "remote " : "",
- buf);
- }
- break;
- }
- case reachabilityTypeName : {
- CFStringAppendFormat(str, NULL, CFSTR("name = %s"), targetPrivate->name);
- break;
- }
- case reachabilityTypePTR : {
- CFStringAppendFormat(str, NULL, CFSTR("ptr = %s"), targetPrivate->name);
- 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 = %u%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 (isReachabilityTypeName(targetPrivate->type)) {
- if (targetPrivate->dnsActive) {
- CFStringAppendFormat(result, NULL, CFSTR(" (DNS query active)"));
- } else if (targetPrivate->serverActive &&
- (targetPrivate->info.flags & kSCNetworkReachabilityFlagsFirstResolvePending)) {
- CFStringAppendFormat(result, NULL, CFSTR(" (server query active)"));
- } else if ((targetPrivate->resolvedAddresses != NULL) || (targetPrivate->resolvedError != NETDB_SUCCESS)) {
- if (targetPrivate->resolvedAddresses != NULL) {
- if (isA_CFArray(targetPrivate->resolvedAddresses)) {
- CFIndex i;
- CFIndex n = CFArrayGetCount(targetPrivate->resolvedAddresses);
-
- CFStringAppendFormat(result, NULL, CFSTR(" ("));
- for (i = 0; i < n; i++) {
- CFDataRef address;
-
- CFStringAppendFormat(result, NULL, CFSTR("%s"),
- i > 0 ? ", " : "");
-
- address = CFArrayGetValueAtIndex(targetPrivate->resolvedAddresses, i);
- if (isA_CFData(address)) {
- char buf[64];
- struct sockaddr *sa;
-
- sa = (struct sockaddr *)CFDataGetBytePtr(address);
- _SC_sockaddr_to_string(sa, buf, sizeof(buf));
- CFStringAppendFormat(result, NULL, CFSTR("%s"), buf);
- } else {
- CFStringAppendFormat(result, NULL, CFSTR("%@"), address);
- }
- }
- CFStringAppendFormat(result, NULL, CFSTR(")"));
- } else if (CFEqual(targetPrivate->resolvedAddresses, kCFNull)) {
- CFStringAppendFormat(result, NULL, CFSTR(" (%s)"),
- gai_strerror(targetPrivate->resolvedError));
- } else {
- CFStringAppendFormat(result, NULL, CFSTR(" (no addresses)"));
- }
- } else {
- CFStringAppendFormat(result, NULL, CFSTR(" (%s)"),
- gai_strerror(targetPrivate->resolvedError));
- }
- }
- if (targetPrivate->dnsFlags != 0) {
- CFStringAppendFormat(result, NULL, CFSTR(", " DNS_FLAGS_FORMAT),
- DNS_FLAGS_VALUES(targetPrivate));
- }
- }
-
- if (targetPrivate->onDemandBypass) {
- CFStringAppendFormat(result, NULL, CFSTR(", !ondemand"));
- }
-
-
- if (targetPrivate->resolverBypass) {
- CFStringAppendFormat(result, NULL, CFSTR(", !resolve"));
- }
-
-
- // add flags
- if (targetPrivate->scheduled) {
- str = _SCNetworkReachabilityCopyTargetFlags(target);
- CFStringAppendFormat(result, NULL, CFSTR(", %@"), str);
- CFRelease(str);
- }
-
- CFStringAppendFormat(result, NULL, CFSTR("}"));
-
- return result;
-}
-
-
-static void
-__SCNetworkReachabilityDeallocate(CFTypeRef cf)
-{
- SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)cf;
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%srelease"),
- targetPrivate->log_prefix);
-
- /* disconnect from the reachability server */
-
- if (targetPrivate->serverActive) {
- __SCNetworkReachabilityServer_targetRemove(target);
- }
-
- /* release resources */
-
- pthread_mutex_destroy(&targetPrivate->lock);
-
- if (targetPrivate->name != NULL)
- CFAllocatorDeallocate(NULL, (void *)targetPrivate->name);
-
- if (targetPrivate->resolvedAddresses != NULL)
- CFRelease(targetPrivate->resolvedAddresses);
-
- if (targetPrivate->localAddress != NULL) {
- if (targetPrivate->localAddress == targetPrivate->remoteAddress) {
- targetPrivate->remoteAddress = NULL;
- }
- CFAllocatorDeallocate(NULL, (void *)targetPrivate->localAddress);
- }
-
- if (targetPrivate->remoteAddress != NULL)
- CFAllocatorDeallocate(NULL, (void *)targetPrivate->remoteAddress);
-
- if (targetPrivate->rlsContext.release != NULL) {
- (*targetPrivate->rlsContext.release)(targetPrivate->rlsContext.info);
- }
-
- if (targetPrivate->onDemandName != NULL) {
- CFRelease(targetPrivate->onDemandName);
- }
-
- if (targetPrivate->onDemandRemoteAddress != NULL) {
- CFRelease(targetPrivate->onDemandRemoteAddress);
- }
-
- if (targetPrivate->onDemandServer != NULL) {
- CFRelease(targetPrivate->onDemandServer);
- }
-
- if (targetPrivate->onDemandServiceID != NULL) {
- CFRelease(targetPrivate->onDemandServiceID);
- }
-
- 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);
- }
-
- if (targetPrivate->nePolicyResult) {
- free(targetPrivate->nePolicyResult);
- }
-
- return;
-}
-
-
-static void
-__SCNetworkReachabilityInitialize(void)
-{
- __kSCNetworkReachabilityTypeID = _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass);
-
- // provide a way to enable SCNetworkReachability logging without
- // having to set _sc_debug=1.
- if ((getenv("REACH_LOGGING") != NULL) ||
- (CFPreferencesGetAppBooleanValue(CFSTR("com.apple.SCNetworkReachability.debug"),
- kCFPreferencesCurrentApplication,
- NULL))) {
- _sc_debug = TRUE;
- }
-
- // set per-process "bypass" of the SCNetworkReachability server
- if (getenv("REACH_SERVER_BYPASS") != NULL) {
- D_serverBypass = TRUE;
- }
-
-
- 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("SCNetworkReachability.concurrent",
- DISPATCH_QUEUE_CONCURRENT);
- });
-
- return q;
-}
-
-
-/*
- * __SCNetworkReachabilityUpdateConcurrent
- *
- * Calls reachUpdate()
- * - 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()
- */
-__private_extern__
-void
-__SCNetworkReachabilityUpdateConcurrent(SCNetworkReachabilityRef target)
-{
- Boolean changed;
- unsigned int n;
- dispatch_queue_t queue;
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- changed = reachUpdate((void *)target);
- if (!changed) {
- return;
- }
-
- n = _SC_ATOMIC_INC(&targetPrivate->pending);
- if (n > 0) {
- // if we already have a notification pending
- return;
- }
-
- MUTEX_LOCK(&targetPrivate->lock);
-
- queue = targetPrivate->dispatchQueue;
- if (queue != NULL) {
- dispatch_group_t group;
-
- dispatch_retain(queue);
-
- group = targetPrivate->dispatchGroup;
- dispatch_group_enter(group);
-
- MUTEX_UNLOCK(&targetPrivate->lock);
-
- dispatch_sync(queue, ^{
- reachPerform((void *)target);
- dispatch_group_leave(group);
- });
-
- dispatch_release(queue);
- } else {
- if (targetPrivate->rls != NULL) {
- CFRunLoopSourceSignal(targetPrivate->rls);
- _SC_signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
- }
-
- MUTEX_UNLOCK(&targetPrivate->lock);
- }
-
- return;
-}
-
-
-/*
- * __SCNetworkReachabilityUpdate
- *
- * Calls reachUpdate() [indirectly]
- * - caller can be holding the target lock
- * - caller can be running on any dispatch queue
- */
-__private_extern__
-void
-__SCNetworkReachabilityUpdate(SCNetworkReachabilityRef target)
-{
- CFRetain(target);
- dispatch_async(__SCNetworkReachability_concurrent_queue(), ^{
- __SCNetworkReachabilityUpdateConcurrent(target);
- CFRelease(target);
- });
-
- return;
-}
-
-
-static SCNetworkReachabilityPrivateRef
-__SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator)
-{
- SCNetworkReachabilityPrivateRef targetPrivate;
- uint32_t size;
-
- /* initialize runtime */
- pthread_once(&initialized, __SCNetworkReachabilityInitialize);
-
- /* allocate target */
- size = sizeof(SCNetworkReachabilityPrivate) - sizeof(CFRuntimeBase);
- targetPrivate = (SCNetworkReachabilityPrivateRef)_CFRuntimeCreateInstance(allocator,
- __kSCNetworkReachabilityTypeID,
- size,
- NULL);
- if (targetPrivate == NULL) {
- return NULL;
- }
-
- bzero((void *)targetPrivate + sizeof(CFRuntimeBase), size);
-
- MUTEX_INIT(&targetPrivate->lock);
-
- targetPrivate->cycle = 1;
- targetPrivate->last_notify = NOT_REPORTED;
- targetPrivate->serverBypass = D_serverBypass;
-
-
-
- targetPrivate->log_prefix[0] = '\0';
- if (_sc_log > 0) {
- snprintf(targetPrivate->log_prefix,
- sizeof(targetPrivate->log_prefix),
- "[%p] ",
- targetPrivate);
- }
-
- return targetPrivate;
-}
-
-
-
-
-static const struct sockaddr *
-is_valid_address(const struct sockaddr *address)
-{
- const struct sockaddr *valid = NULL;
- static Boolean warned = FALSE;
-
- if ((address != NULL) &&
- (address->sa_len <= sizeof(struct sockaddr_storage))) {
- switch (address->sa_family) {
- case AF_INET :
- if (address->sa_len >= sizeof(struct sockaddr_in)) {
- valid = address;
- } else {
- if (!warned) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu"),
- address->sa_len,
- sizeof(struct sockaddr_in));
- warned = TRUE;
- }
- }
- break;
- case AF_INET6 :
- if (address->sa_len >= sizeof(struct sockaddr_in6)) {
- valid = address;
- } else if (!warned) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu"),
- address->sa_len,
- sizeof(struct sockaddr_in6));
- warned = TRUE;
- }
- break;
- default :
- if (!warned) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with invalid address family %d"),
- address->sa_family);
- warned = TRUE;
- }
- }
- }
-
- return valid;
-}
-
-
-
-
-
-
-SCNetworkReachabilityRef
-SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator,
- const struct sockaddr *address)
-{
- SCNetworkReachabilityPrivateRef targetPrivate;
-
- address = is_valid_address(address);
- if (address == NULL) {
- _SCErrorSet(kSCStatusInvalidArgument);
- return NULL;
- }
-
- targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
- if (targetPrivate == NULL) {
- return NULL;
- }
-
- targetPrivate->type = reachabilityTypeAddress;
- targetPrivate->remoteAddress = CFAllocatorAllocate(NULL, address->sa_len, 0);
- bcopy(address, targetPrivate->remoteAddress, address->sa_len);
-
-
-
- SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%s%s %@"),
- targetPrivate->log_prefix,
- DEBUG_REACHABILITY_TYPE_ADDRESS,
- targetPrivate);
-
- return (SCNetworkReachabilityRef)targetPrivate;
-}
-
-
-static Boolean
-is_same_address(const struct sockaddr *a, const struct sockaddr *b)
-{
- const void *a_addr;
- const void *b_addr;
- size_t len;
-
- if ((a == NULL) ||
- (b == NULL) ||
- (a->sa_family != b->sa_family) ||
- (a->sa_len != b->sa_len )) {
- return FALSE;
- }
-
- switch (a->sa_family) {
- case AF_INET : {
- struct sockaddr_in *a_sin = (struct sockaddr_in *)(void *)a;
- struct sockaddr_in *b_sin = (struct sockaddr_in *)(void *)b;
-
- /* ALIGN: assuming a (and b) are aligned, then cast ok. */
- a_addr = &a_sin->sin_addr;
- b_addr = &b_sin->sin_addr;
- len = sizeof(struct in_addr);
- break;
- }
-
- case AF_INET6 : {
- struct sockaddr_in6 *a_sin6 = (struct sockaddr_in6 *)(void *)a;
- struct sockaddr_in6 *b_sin6 = (struct sockaddr_in6 *)(void *)b;
-
- if (a_sin6->sin6_scope_id != b_sin6->sin6_scope_id) {
- return FALSE;
- }
-
- a_addr = &a_sin6->sin6_addr;
- b_addr = &b_sin6->sin6_addr;
- len = sizeof(struct in6_addr);
- break;
- }
-
- default :
- a_addr = a;
- b_addr = b;
- len = a->sa_len;
- break;
- }
-
- return (bcmp(a_addr, b_addr, len) == 0);
-}
-
-
-SCNetworkReachabilityRef
-SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator,
- const struct sockaddr *localAddress,
- const struct sockaddr *remoteAddress)
-{
- SCNetworkReachabilityPrivateRef targetPrivate;
-
- if ((localAddress == NULL) && (remoteAddress == NULL)) {
- _SCErrorSet(kSCStatusInvalidArgument);
- return NULL;
- }
-
- if (localAddress != NULL) {
- localAddress = is_valid_address(localAddress);
- if (localAddress == NULL) {
- _SCErrorSet(kSCStatusInvalidArgument);
- return NULL;
- }
- }
-
- if (remoteAddress != NULL) {
- remoteAddress = is_valid_address(remoteAddress);
- if (remoteAddress == NULL) {
- _SCErrorSet(kSCStatusInvalidArgument);
- return NULL;
- }
- }
-
- targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
- if (targetPrivate == NULL) {
- return NULL;
- }
-
- targetPrivate->type = reachabilityTypeAddressPair;
-
- if (localAddress != NULL) {
- targetPrivate->localAddress = CFAllocatorAllocate(NULL, localAddress->sa_len, 0);
- bcopy(localAddress, targetPrivate->localAddress, localAddress->sa_len);
- }
-
- if (remoteAddress != NULL) {
- if (is_same_address(localAddress, remoteAddress)) {
- targetPrivate->remoteAddress = targetPrivate->localAddress;
- } else {
- targetPrivate->remoteAddress = CFAllocatorAllocate(NULL, remoteAddress->sa_len, 0);
- bcopy(remoteAddress, targetPrivate->remoteAddress, remoteAddress->sa_len);
- }
- }
-
-
-
- SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%s%s %@"),
- targetPrivate->log_prefix,
- DEBUG_REACHABILITY_TYPE_ADDRESSPAIR,
- targetPrivate);
-
- return (SCNetworkReachabilityRef)targetPrivate;
-}
-
-
-SCNetworkReachabilityRef
-SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator,
- const char *nodename)
-{
- union {
- struct sockaddr sa;
- struct sockaddr_in sin;
- struct sockaddr_in6 sin6;
- } addr;
- size_t nodenameLen;
- SCNetworkReachabilityPrivateRef targetPrivate;
-
- if (nodename == NULL) {
- _SCErrorSet(kSCStatusInvalidArgument);
- return NULL;
- }
-
- nodenameLen = strlen(nodename);
- if (nodenameLen == 0) {
- _SCErrorSet(kSCStatusInvalidArgument);
- return NULL;
- }
-
- if (nodename[nodenameLen - 1] == '.') {
- int dots;
- size_t i;
-
- // trim trailing "."s
- do {
- --nodenameLen;
- } while ((nodenameLen > 0) && (nodename[nodenameLen - 1] == '.'));
-
- if (nodenameLen == 0) {
- // if only trailing "."s
- _SCErrorSet(kSCStatusInvalidArgument);
- return NULL;
- }
-
- // count the remaining "."s
- dots = 0;
- for (i = 0; i < nodenameLen; i++) {
- if (nodename[i] == '.') dots++;
- }
-
- if (dots == 0) {
- // if only a single-label, add back the FQDN "."
- nodenameLen++;
- }
- }
-
- 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);
- if (targetPrivate == NULL) {
- return NULL;
- }
-
- targetPrivate->type = reachabilityTypeName;
-
- targetPrivate->name = CFAllocatorAllocate(NULL, nodenameLen + 1, 0);
- strlcpy((char *)targetPrivate->name, nodename, nodenameLen + 1);
-
- targetPrivate->needResolve = TRUE;
- targetPrivate->info.flags |= kSCNetworkReachabilityFlagsFirstResolvePending;
- targetPrivate->serverInfo.flags |= kSCNetworkReachabilityFlagsFirstResolvePending;
-
- {
- /* make sure AppLayerVPN only is in client mode */
- CFDictionaryRef appLayerVPNProperties;
-
- appLayerVPNProperties = VPNAppLayerCopyCurrentAppProperties();
- if (appLayerVPNProperties != NULL) {
- targetPrivate->serverBypassForVPN = TRUE;
- targetPrivate->serverBypass = YES;
- CFRelease(appLayerVPNProperties);
- }
- }
-
- SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%s%s %@"),
- targetPrivate->log_prefix,
- DEBUG_REACHABILITY_TYPE_NAME,
- targetPrivate);
-
- return (SCNetworkReachabilityRef)targetPrivate;
-}
-
-
-static SCNetworkReachabilityRef
-__SCNetworkReachabilityCreateWithPtr(CFAllocatorRef allocator,
- const char *ptrName,
- const struct sockaddr *ptrAddress)
-{
- SCNetworkReachabilityRef target;
- SCNetworkReachabilityPrivateRef targetPrivate;
-
- target = SCNetworkReachabilityCreateWithName(NULL, ptrName);
- if (target == NULL) {
- return NULL;
- }
-
- targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- // change type
- targetPrivate->type = reachabilityTypePTR;
-
- // and keep the address
- targetPrivate->remoteAddress = CFAllocatorAllocate(NULL, ptrAddress->sa_len, 0);
- bcopy(ptrAddress, targetPrivate->remoteAddress, ptrAddress->sa_len);
-
- return target;
-}
-
-
-
-
-SCNetworkReachabilityRef
-SCNetworkReachabilityCreateWithOptions(CFAllocatorRef allocator,
- CFDictionaryRef options)
-{
- const struct sockaddr *addr_l = NULL;
- const struct sockaddr *addr_p = NULL;
- const struct sockaddr *addr_r = NULL;
- CFDataRef data;
- CFStringRef interface = NULL;
- CFStringRef nodename;
- CFBooleanRef onDemandBypass;
- CFBooleanRef resolverBypass;
- CFBooleanRef serverBypass;
- SCNetworkReachabilityRef target;
- SCNetworkReachabilityPrivateRef targetPrivate;
-
- if (!isA_CFDictionary(options)) {
- _SCErrorSet(kSCStatusInvalidArgument);
- return NULL;
- }
-
- nodename = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionNodeName);
- if ((nodename != NULL) &&
- (!isA_CFString(nodename) || (CFStringGetLength(nodename) == 0))) {
- _SCErrorSet(kSCStatusInvalidArgument);
- return NULL;
- }
- data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionLocalAddress);
- if (data != NULL) {
- if (!isA_CFData(data) || (CFDataGetLength(data) < sizeof(struct sockaddr_in))) {
- _SCErrorSet(kSCStatusInvalidArgument);
- return NULL;
- }
- addr_l = (const struct sockaddr *)CFDataGetBytePtr(data);
- }
- data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionPTRAddress);
- if (data != NULL) {
- if (!isA_CFData(data) || (CFDataGetLength(data) < sizeof(struct sockaddr_in))) {
- _SCErrorSet(kSCStatusInvalidArgument);
- return NULL;
- }
- addr_p = (const struct sockaddr *)CFDataGetBytePtr(data);
- }
- data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionRemoteAddress);
- if (data != NULL) {
- if (!isA_CFData(data) || (CFDataGetLength(data) < sizeof(struct sockaddr_in))) {
- _SCErrorSet(kSCStatusInvalidArgument);
- return NULL;
- }
- addr_r = (const struct sockaddr *)CFDataGetBytePtr(data);
- }
- interface = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionInterface);
- if ((interface != NULL) &&
- (!isA_CFString(interface) || (CFStringGetLength(interface) == 0))) {
- _SCErrorSet(kSCStatusInvalidArgument);
- return NULL;
- }
- 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;
- }
-
-
- serverBypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionServerBypass);
- if ((serverBypass != NULL) && !isA_CFBoolean(serverBypass)) {
- _SCErrorSet(kSCStatusInvalidArgument);
- return NULL;
- }
-
-
- if (nodename != NULL) {
- const char *name;
-
- if ((addr_l != NULL) || (addr_r != NULL) || (addr_p != NULL)) {
- // can't have both a nodename and an address
- _SCErrorSet(kSCStatusInvalidArgument);
- return NULL;
- }
-
- name = _SC_cfstring_to_cstring(nodename, NULL, 0, kCFStringEncodingUTF8);
- target = SCNetworkReachabilityCreateWithName(allocator, name);
- CFAllocatorDeallocate(NULL, (void *)name);
- } else if (addr_p != NULL) {
- char name[MAXHOSTNAMELEN];
-
- if ((addr_l != NULL) || // can't have PTR and target address
- (addr_r != NULL) || // can't have PTR and target address
- !addr_to_PTR_name(addr_p, name, sizeof(name))) { // can't convert PTR
- _SCErrorSet(kSCStatusInvalidArgument);
- return NULL;
- }
-
- target = __SCNetworkReachabilityCreateWithPtr(NULL, name, addr_p);
- } else {
- if ((addr_l != NULL) && (addr_r != NULL)) {
- target = SCNetworkReachabilityCreateWithAddressPair(NULL, addr_l, addr_r);
- } else if (addr_r != NULL) {
- target = SCNetworkReachabilityCreateWithAddress(NULL, addr_r);
- } else if (addr_l != NULL) {
- target = SCNetworkReachabilityCreateWithAddressPair(NULL, addr_l, NULL);
- } else {
- _SCErrorSet(kSCStatusInvalidArgument);
- return NULL;
- }
- }
- if (target == NULL) {
- return NULL;
- }
-
- targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- if (interface != NULL) {
- if ((_SC_cfstring_to_cstring(interface,
- targetPrivate->if_name,
- sizeof(targetPrivate->if_name),
- kCFStringEncodingASCII) == NULL) ||
- ((targetPrivate->if_index = if_nametoindex(targetPrivate->if_name)) == 0)) {
- CFRelease(targetPrivate);
- _SCErrorSet(kSCStatusInvalidArgument);
- return NULL;
- }
- }
-
-
- if (onDemandBypass != NULL) {
- targetPrivate->onDemandBypass = CFBooleanGetValue(onDemandBypass);
- }
-
- if (resolverBypass != NULL) {
- targetPrivate->resolverBypass = CFBooleanGetValue(resolverBypass);
- }
-
- /* if by name, make sure client-only VPN types stay in client mode */
- if (serverBypass != NULL && targetPrivate->serverBypassForVPN == FALSE) {
- targetPrivate->serverBypass = CFBooleanGetValue(serverBypass);
- }
-
-
- if (_sc_debug && (_sc_log > 0)) {
- const char *opt = "???";
-
- switch (targetPrivate->type) {
- case reachabilityTypeAddress :
- opt = DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS;
- break;
- case reachabilityTypeAddressPair :
- opt = DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS;
- break;
- case reachabilityTypeName :
- opt = DEBUG_REACHABILITY_TYPE_NAME_OPTIONS;
- break;
- case reachabilityTypePTR :
- opt = DEBUG_REACHABILITY_TYPE_PTR_OPTIONS;
- break;
- }
-
- SCLog(TRUE, LOG_INFO, CFSTR("%s%s %@"),
- targetPrivate->log_prefix,
- opt,
- targetPrivate);
- }
-
- return (SCNetworkReachabilityRef)targetPrivate;
-}
-
-
-static SCNetworkReachabilityRef
-__SCNetworkReachabilityCreateCopy(SCNetworkReachabilityRef target)
-{
- SCNetworkReachabilityRef clone = NULL;
- SCNetworkReachabilityPrivateRef clonePrivate;
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- switch (targetPrivate->type) {
- case reachabilityTypeAddress :
- clone = SCNetworkReachabilityCreateWithAddress(NULL,
- targetPrivate->remoteAddress);
- break;
- case reachabilityTypeAddressPair :
- clone = SCNetworkReachabilityCreateWithAddressPair(NULL,
- targetPrivate->localAddress,
- targetPrivate->remoteAddress);
- break;
- case reachabilityTypeName :
- clone = SCNetworkReachabilityCreateWithName(NULL,
- targetPrivate->name);
- break;
- case reachabilityTypePTR :
- clone = __SCNetworkReachabilityCreateWithPtr(NULL,
- targetPrivate->name,
- targetPrivate->remoteAddress);
- break;
- }
- if (clone == NULL) {
- return NULL;
- }
-
- clonePrivate = (SCNetworkReachabilityPrivateRef)clone;
-
- clonePrivate->quiet = TRUE;
-
- clonePrivate->if_index = targetPrivate->if_index;
- bcopy(targetPrivate->if_name, clonePrivate->if_name, sizeof(clonePrivate->if_name));
-
- clonePrivate->onDemandBypass = targetPrivate->onDemandBypass;
-
-
- clonePrivate->serverBypass = targetPrivate->serverBypass;
-
- clonePrivate->resolverBypass = targetPrivate->resolverBypass;
-
-
- if (_sc_debug && (_sc_log > 0)) {
- const char *opt = "???";
-
- switch (clonePrivate->type) {
- case reachabilityTypeAddress :
- opt = DEBUG_REACHABILITY_TYPE_ADDRESS_CLONE;
- break;
- case reachabilityTypeAddressPair :
- opt = DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_CLONE;
- break;
- case reachabilityTypeName :
- opt = DEBUG_REACHABILITY_TYPE_NAME_CLONE;
- break;
- case reachabilityTypePTR :
- opt = DEBUG_REACHABILITY_TYPE_PTR_CLONE;
- break;
- }
-
- SCLog(TRUE, LOG_INFO, CFSTR("%s%s %p %@"),
- clonePrivate->log_prefix,
- opt,
- targetPrivate,
- clone);
- }
-
- return clone;
-}
-
-
-CFTypeID
-SCNetworkReachabilityGetTypeID(void)
-{
- pthread_once(&initialized, __SCNetworkReachabilityInitialize); /* initialize runtime */
- return __kSCNetworkReachabilityTypeID;
-}
-
-
-CFArrayRef /* CFArray[CFData], where each CFData is a (struct sockaddr *) */
-SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target,
- int *error_num)
-{
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- if (!isA_SCNetworkReachability(target)) {
- _SCErrorSet(kSCStatusInvalidArgument);
- return NULL;
- }
-
- if (!isReachabilityTypeName(targetPrivate->type)) {
- _SCErrorSet(kSCStatusInvalidArgument);
- return NULL;
- }
-
- if (error_num) {
- *error_num = targetPrivate->resolvedError;
- }
-
- if (targetPrivate->resolvedAddresses != NULL) {
- if (isA_CFArray(targetPrivate->resolvedAddresses)) {
- return CFRetain(targetPrivate->resolvedAddresses);
- } else {
- /* if status is known but no resolved addresses to return */
- _SCErrorSet(kSCStatusOK);
- return NULL;
- }
- }
-
- _SCErrorSet(kSCStatusReachabilityUnknown);
- return NULL;
-}
-
-
-static void
-__SCNetworkReachabilitySetResolvedError(SCNetworkReachabilityRef target,
- int32_t status)
-{
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- MUTEX_ASSERT_HELD(&targetPrivate->lock);
-
- __mark_operation_end(target,
- FALSE, // if successful query
- dns_query_async, // async
- &targetPrivate->dnsQueryStart, // start time
- &targetPrivate->dnsQueryEnd); // end time
-
- if (targetPrivate->resolvedAddresses != NULL) {
- CFRelease(targetPrivate->resolvedAddresses);
- targetPrivate->resolvedAddresses = NULL;
- }
-
- SCLog(_sc_debug, LOG_INFO, CFSTR("%scould not be resolved: %s"),
- targetPrivate->log_prefix,
- gai_strerror(status));
-
- /* save the error associated with the attempt to resolve the name */
- targetPrivate->resolvedAddresses = CFRetain(kCFNull);
- targetPrivate->resolvedError = status;
- targetPrivate->needResolve = FALSE;
-
- return;
-}
-
-
-/*
- * rankReachability()
- * Not reachable == 0
- * Connection Required == 1
- * Reachable == 2
- */
-static int
-rankReachability(SCNetworkReachabilityFlags flags)
-{
- int rank = 0;
-
- if (flags & kSCNetworkReachabilityFlagsReachable) rank = 2;
- if (flags & kSCNetworkReachabilityFlagsConnectionRequired) rank = 1;
- return rank;
-}
-
-
-#pragma mark -
-#pragma mark DNS name resolution
-
-
-static void
-update_resolver_reachability(ReachabilityStoreInfoRef store_info,
- dns_resolver_t *resolver,
- SCNetworkReachabilityFlags *flags,
- Boolean *haveDNS,
- uint32_t *resolver_if_index,
- const char *log_prefix)
-{
- if (resolver_if_index) *resolver_if_index = 0;
-
- if (resolver->n_nameserver > 0) {
- *flags = (SCNetworkReachabilityFlags)resolver->reach_flags;
- if (resolver_if_index != NULL) {
- *resolver_if_index = resolver->if_index;
- }
- *haveDNS = TRUE;
- } else {
- *flags = kSCNetworkReachabilityFlagsReachable;
- *haveDNS = FALSE;
- }
-
- return;
-}
-
-
-static Boolean
-check_matching_resolvers(ReachabilityStoreInfoRef store_info,
- dns_config_t *dns_config,
- const char *fqdn,
- unsigned int if_index,
- SCNetworkReachabilityFlags *flags,
- Boolean *haveDNS,
- uint32_t *resolver_if_index,
- int *dns_config_index,
- const char *log_prefix)
-{
- int i;
- Boolean matched = FALSE;
- const char *name = fqdn;
- int32_t n_resolvers;
- dns_resolver_t **resolvers;
-
- if (if_index == 0) {
- n_resolvers = dns_config->n_resolver;
- resolvers = dns_config->resolver;
- } else {
- n_resolvers = dns_config->n_scoped_resolver;
- 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)) {
- size_t len;
-
- /*
- * check if the provided name (or sub-component)
- * matches one of our resolver configurations.
- */
- len = strlen(name);
- for (i = 0; i < n_resolvers; i++) {
- char *domain;
- dns_resolver_t *resolver;
-
- resolver = resolvers[i];
- if ((if_index != 0) && (if_index != resolver->if_index)) {
- continue;
- }
-
- domain = resolver->domain;
- if (domain != NULL && (len == strlen(domain))) {
- if (strcasecmp(name, domain) == 0) {
- /*
- * if name matches domain
- */
- matched = TRUE;
- update_resolver_reachability(store_info,
- resolver,
- flags,
- haveDNS,
- resolver_if_index,
- log_prefix);
- if (dns_config_index != NULL) *dns_config_index = i;
- break;
- }
- }
- }
-
- if (!matched) {
- /*
- * we have not found a matching resolver, try
- * a less qualified domain
- */
- name = strchr(name, '.');
- if ((name != NULL) && (*name != '\0')) {
- name++;
- } else {
- name = NULL;
- }
- }
- }
-
- return matched;
-}
-
-
-static dns_resolver_t *
-get_default_resolver(dns_config_t *dns_config, unsigned int if_index)
-{
- int i;
- int32_t n_resolvers;
- dns_resolver_t *resolver = NULL;
- dns_resolver_t **resolvers;
-
- if (if_index == 0) {
- n_resolvers = dns_config->n_resolver;
- resolvers = dns_config->resolver;
- } else {
- n_resolvers = dns_config->n_scoped_resolver;
- resolvers = dns_config->scoped_resolver;
- }
-
- for (i = 0; i < n_resolvers; i++) {
- if ((if_index != 0) && (if_index != resolvers[i]->if_index)) {
- continue;
- }
-
- if (((if_index == 0) && (i == 0)) ||
- ((if_index != 0) && (resolver == NULL))) {
- // if this is the first (aka default) resolver
- resolver = resolvers[i];
- } else if ((resolvers[i]->domain == NULL) &&
- (resolvers[i]->search_order < resolver->search_order)) {
- // if this is a default resolver with a lower search order
- resolver = resolvers[i];
- }
- }
-
- return resolver;
-}
-
-
-static dns_configuration_t *
-dns_configuration_retain()
-{
- dns_configuration_t *config;
-
- pthread_mutex_lock(&dns_lock);
-
- if (dns_configuration != NULL) {
- Boolean refresh = TRUE;
-
- if (dns_token_valid) {
- int check = 0;
- uint32_t status;
-
- /*
- * check if the global [DNS] configuration snapshot needs
- * to be updated
- */
- status = notify_check(dns_token, &check);
- if (status != NOTIFY_STATUS_OK) {
- SCLog(TRUE, LOG_INFO, CFSTR("notify_check() failed, status=%u"), status);
- } else if (check == 0) {
- // if the snapshot does not need to be refreshed
- refresh = FALSE;
- }
- }
-
- if (refresh) {
- if (dns_configuration->refs == 0) {
- dns_configuration_free(dns_configuration->config);
- CFAllocatorDeallocate(NULL, dns_configuration);
- }
- dns_configuration = NULL;
- }
- }
-
- if (dns_configuration == NULL) {
- dns_config_t *new_config;
-
- new_config = dns_configuration_copy();
- if (new_config != NULL) {
- dns_configuration = CFAllocatorAllocate(NULL, sizeof(dns_configuration_t), 0);
- dns_configuration->config = new_config;
- dns_configuration->refs = 0;
- }
- }
-
- if (dns_configuration != NULL) {
- dns_configuration->refs++;
- }
-
- config = dns_configuration;
- pthread_mutex_unlock(&dns_lock);
- return config;
-}
-
-
-static void
-dns_configuration_release(dns_configuration_t *config)
-{
- pthread_mutex_lock(&dns_lock);
-
- config->refs--;
- if (config->refs == 0) {
- if (!dns_token_valid && (config == dns_configuration)) {
- dns_configuration = NULL;
- }
-
- if (config != dns_configuration) {
- dns_configuration_free(config->config);
- CFAllocatorDeallocate(NULL, config);
- }
- }
-
- pthread_mutex_unlock(&dns_lock);
- return;
-}
-
-
-static Boolean
-dns_configuration_watch()
-{
- int dns_check = 0;
- const char *dns_key;
- Boolean ok = FALSE;
- uint32_t status;
-
- pthread_mutex_lock(&dns_lock);
-
- dns_key = dns_configuration_notify_key();
- if (dns_key == NULL) {
- SCLog(TRUE, LOG_INFO, CFSTR("dns_configuration_notify_key() failed"));
- goto done;
- }
-
- status = notify_register_check(dns_key, &dns_token);
- if (status == NOTIFY_STATUS_OK) {
- dns_token_valid = TRUE;
- } else {
- SCLog(TRUE, LOG_INFO, CFSTR("notify_register_check() failed, status=%u"), status);
- goto done;
- }
-
- status = notify_check(dns_token, &dns_check);
- if (status != NOTIFY_STATUS_OK) {
- SCLog(TRUE, LOG_INFO, CFSTR("notify_check() failed, status=%u"), status);
- (void)notify_cancel(dns_token);
- dns_token_valid = FALSE;
- goto done;
- }
-
- ok = TRUE;
-
- done :
-
- pthread_mutex_unlock(&dns_lock);
- return ok;
-}
-
-
-static void
-dns_configuration_unwatch()
-{
- pthread_mutex_lock(&dns_lock);
-
- (void)notify_cancel(dns_token);
- dns_token_valid = FALSE;
-
- if ((dns_configuration != NULL) && (dns_configuration->refs == 0)) {
- dns_configuration_free(dns_configuration->config);
- CFAllocatorDeallocate(NULL, dns_configuration);
- dns_configuration = NULL;
- }
-
- pthread_mutex_unlock(&dns_lock);
- return;
-}
-
-
-static void
-_SC_R_updateResolverReachability(ReachabilityStoreInfoRef store_info,
- SCNetworkReachabilityFlags *flags,
- Boolean *haveDNS,
- const char *nodename,
- unsigned int if_index,
- uint32_t *resolver_if_index,
- int *dns_config_index,
- const char *log_prefix
- )
-{
- dns_resolver_t *default_resolver;
- dns_configuration_t *dns;
- Boolean found = FALSE;
- char *fqdn = (char *)nodename;
- int i;
- Boolean isFQDN = FALSE;
- size_t len;
- const int ndots = 1;
- 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
- * be consulted to resolve the specified nodename we need to
- * check the availability of ALL name servers. We can only
- * proceed if we know that our query can be answered.
- */
-
- *flags = kSCNetworkReachabilityFlagsReachable;
- *haveDNS = FALSE;
-
- len = (nodename != NULL) ? strlen(nodename) : 0;
- if (len == 0) {
- // if no nodename, return not reachable
- *flags = 0;
- return;
- }
-
- dns = dns_configuration_retain();
- if (dns == NULL) {
- // if error
- SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS: no configuration"), log_prefix);
- goto done;
- }
-
- default_resolver = get_default_resolver(dns->config, if_index);
- if (default_resolver == NULL) {
- // if no resolver configuration
- SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS: no resolvers"), log_prefix);
- goto done;
- }
-
- if (fqdn[len - 1] == '.') {
- isFQDN = TRUE;
-
- // trim trailing '.''s
- while ((len > 0) && (fqdn[len-1] == '.')) {
- if (fqdn == nodename) {
- fqdn = strdup(nodename);
- assert(fqdn != nodename);
- }
- fqdn[--len] = '\0';
- }
- }
-
- /*
- * check if the provided name matches a supplemental domain
- */
- 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 we did not match a supplemental domain name and if the
- * provided name has enough "."s then the first query will be
- * directed to the default resolver.
- */
- char *cp;
- int dots;
-
- dots = 0;
- for (cp = fqdn; *cp != '\0'; cp++) {
- if (*cp == '.') dots++;
- }
-
- /* Per KB: HT4845 */
- if (dots >= ndots) {
- useDefault = TRUE;
- }
- }
-
- if (!found && !isFQDN && !useDefault && (dns->config->n_resolver > 1)) {
- /*
- * FQDN not specified, try matching w/search domains
- */
- if (default_resolver->n_search > 0) {
- for (i = 0; !found && (i < default_resolver->n_search); i++) {
- int ret;
- char *search_fqdn = NULL;
-
- ret = asprintf(&search_fqdn, "%s.%s", fqdn, default_resolver->search[i]);
- if (ret == -1) {
- continue;
- }
-
- // try the provided name with the search domain appended
- found = check_matching_resolvers(store_info,
- dns->config,
- search_fqdn,
- if_index,
- flags,
- haveDNS,
- resolver_if_index,
- dns_config_index,
- log_prefix);
- free(search_fqdn);
- }
- } else if (default_resolver->domain != NULL) {
- char *dp;
- int domain_parts = 0;
-
- // count domain parts
- for (dp = default_resolver->domain; *dp != '\0'; dp++) {
- if (*dp == '.') {
- domain_parts++;
- }
- }
-
- // remove trailing dots
- for (dp--; (dp >= default_resolver->domain) && (*dp == '.'); dp--) {
- *dp = '\0';
- domain_parts--;
- }
-
- if (dp >= default_resolver->domain) {
- // dots are separators, bump # of components
- domain_parts++;
- }
-
- dp = default_resolver->domain;
- for (i = LOCALDOMAINPARTS; !found && (i <= (domain_parts - ndots)); i++) {
- int ret;
- char *search_fqdn = NULL;
-
- ret = asprintf(&search_fqdn, "%s.%s", fqdn, dp);
- if (ret == -1) {
- continue;
- }
-
- // try the provided name with the [default] domain appended
- found = check_matching_resolvers(store_info,
- dns->config,
- search_fqdn,
- if_index,
- flags,
- haveDNS,
- resolver_if_index,
- dns_config_index,
- log_prefix);
- free(search_fqdn);
-
- // move to the next component of the [default] domain
- dp = strchr(dp, '.') + 1;
- }
- }
- }
-
- if (!found) {
- // update the reachability of the default resolver
- update_resolver_reachability(store_info,
- default_resolver,
- flags,
- haveDNS,
- resolver_if_index,
- log_prefix);
- if (dns_config_index != NULL) *dns_config_index = 0;
- }
-
- done :
-
- if (fqdn != nodename) free(fqdn);
-
- if (dns != NULL) {
- dns_configuration_release(dns);
- }
-
- return;
-}
-
-
-Boolean
-__SC_checkResolverReachabilityInternal(SCDynamicStoreRef *storeP,
- SCNetworkReachabilityFlags *flags,
- Boolean *haveDNS,
- const char *nodename,
- 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;
- }
-
- _SC_R_updateResolverReachability(&store_info,
- flags,
- haveDNS,
- nodename,
- 0,
- resolver_if_index,
- dns_config_index,
- "");
-
- done :
-
- ReachabilityStoreInfo_free(&store_info);
- return ok;
-}
-
-
-/*
- * _SC_checkResolverReachabilityByAddress()
- *
- * Given an IP address, determine whether a reverse DNS query can be issued
- * using the current network configuration.
- */
-Boolean
-_SC_checkResolverReachabilityByAddress(SCDynamicStoreRef *storeP,
- SCNetworkReachabilityFlags *flags,
- Boolean *haveDNS,
- struct sockaddr *sa)
-{
- Boolean ok;
- char ptr_name[128];
- ReachabilityStoreInfo store_info;
-
- ReachabilityStoreInfo_init(&store_info);
- ok = ReachabilityStoreInfo_update(&store_info, storeP, AF_UNSPEC);
- if (!ok) {
- goto done;
- }
-
- /*
- * Ideally, we would have an API that given a local IP
- * address would return the DNS server(s) that would field
- * a given PTR query. Fortunately, we do have an SPI which
- * which will provide this information given a "name" so we
- * take the address, convert it into the inverse query name,
- * and find out which servers should be consulted.
- */
- ok = addr_to_PTR_name(sa, ptr_name, sizeof(ptr_name));
- if (!ok) {
- goto done;
- }
-
- _SC_R_updateResolverReachability(&store_info, flags, haveDNS, ptr_name, 0, NULL, NULL, "");
-
- done :
-
- ReachabilityStoreInfo_free(&store_info);
- return ok;
-}
-
-
-#pragma mark -
-#pragma mark DNSServiceGetAddrInfo support
-
-
-/*
- * DNS query handling
- *
- * Notes :
- *
- * 1. We have a "contract" with mDNSResponder that for EVERY network
- * or DNS configuration change that should warrant our [re-]starting
- * a query, mDNSResponder will acknowledge the latest DNS configuration.
- *
- * 2. IPMonitor also posts a notification AFTER every network or DNS
- * configuration change.
- *
- * 3. We use IPMonitor's "trailing edge" as a signal to restart any
- * by-name queries.
- */
-
-
-// Note: protected by _hn_target_queue()
-static int dns_refresh_token;
-static Boolean dns_refresh_token_valid = FALSE;
-
-
-/*
- * dns_refresh_handler
- *
- * Called to notify/update all SCNetworkReachability by-name targets of
- * a network/DNS change. The change should [re-]start a DNS query to
- * resolve the name.
- * - should be exec'd on the _hn_target_queue()
- */
-static void
-dns_refresh_handler()
-{
- CFArrayRef changes;
- CFStringRef key;
- __block SCDynamicStoreRef store = NULL;
-
- dispatch_sync(_hn_target_queue(), ^{
- if (dns_refresh_token_valid && (hn_store != NULL)) {
- store = CFRetain(hn_store);
- }
- });
-
- if (store == NULL) {
- return;
- }
-
- key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
- kSCDynamicStoreDomainState,
- kSCEntNetDNS);
- changes = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks);
- __SCNetworkReachabilityHandleChanges(store, changes, NULL);
- CFRelease(changes);
- CFRelease(key);
-
- CFRelease(store);
- return;
-}
-
-
-/*
- * dns_refresh_enable
- *
- * Called to monitor for network/DNS changes that should restart a DNS query.
- * - caller must be running on the _hn_target_queue()
- */
-static Boolean
-dns_refresh_enable(dispatch_queue_t q)
-{
- uint32_t status;
-
- status = notify_register_dispatch(_SC_NOTIFY_NETWORK_CHANGE,
- &dns_refresh_token,
- q,
- ^(int token){
- dns_refresh_handler();
- });
- if (status != NOTIFY_STATUS_OK) {
- SCLog(TRUE, LOG_INFO, CFSTR("notify_register_dispatch() failed, status=%u"), status);
- return FALSE;
- }
-
- dns_refresh_token_valid = TRUE;
-
- return TRUE;
-}
-
-
-/*
- * dns_refresh_disable
- *
- * Called to stop monitoring for network/DNS changes
- * - caller must be running on the _hn_target_queue()
- */
-static void
-dns_refresh_disable()
-{
- (void)notify_cancel(dns_refresh_token);
- dns_refresh_token_valid = FALSE;
- return;
-}
-
-
-#pragma mark -
-#pragma mark [m]DNS Queries
-
-
-static void
-dequeueDNSQuery(SCNetworkReachabilityRef target);
-
-
-static dispatch_queue_t
-_dns_queue()
-{
- static dispatch_once_t once;
- static dispatch_queue_t q;
-
- dispatch_once(&once, ^{
- q = dispatch_queue_create("SCNetworkReachability.DNSService", NULL);
- });
-
- return q;
-}
-
-
-/*
- * _dns_complete
- */
-static __inline__ Boolean
-_dns_complete(SCNetworkReachabilityRef target)
-{
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- if ((targetPrivate->dnsHaveV4 && targetPrivate->dnsHaveV6) ||
- targetPrivate->dnsHavePTR ||
- targetPrivate->dnsHaveError ||
- targetPrivate->dnsHaveTimeout) {
- return TRUE;
- }
-
- return FALSE;
-}
-
-
-/*
- * _dns_notify
- *
- * Called to push out a target's DNS changes
- * - caller must be running on the _dns_queue()
- */
-static void
-_dns_notify(const void *value, void *context)
-{
- SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)value;
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- MUTEX_LOCK(&targetPrivate->lock);
-
- if (_dns_complete(target)) {
- __mark_operation_end(target,
- (targetPrivate->resolvedError == NETDB_SUCCESS), // if successful query
- dns_query_mdns, // [m]DNS query
- &targetPrivate->dnsQueryStart, // start time
- &targetPrivate->dnsQueryEnd); // end time
-
- // update target info
- if (targetPrivate->resolvedAddresses != NULL) {
- CFRelease(targetPrivate->resolvedAddresses);
- }
- targetPrivate->resolvedAddresses = targetPrivate->dnsAddresses;
- targetPrivate->dnsAddresses = NULL;
-
- targetPrivate->resolvedError = targetPrivate->dnsError;
- targetPrivate->dnsError = NETDB_SUCCESS;
-
- dequeueDNSQuery(target);
-
- targetPrivate->needResolve = FALSE;
-
- if (targetPrivate->scheduled) {
- __SCNetworkReachabilityUpdate(target);
- }
- }
-
- MUTEX_UNLOCK(&targetPrivate->lock);
- return;
-}
-
-
-typedef enum {
- MARK_NONE,
- MARK_ERROR,
- MARK_TIMEOUT,
- MARK_HAVE_V4,
- MARK_HAVE_V6,
- MARK_HAVE_PTR,
-} _dns_mark_t;
-
-
-/*
- * _dns_mark
- */
-static __inline__ void
-_dns_mark(SCNetworkReachabilityRef target, _dns_mark_t mark)
-{
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- switch (mark) {
- case MARK_NONE :
- break;
- case MARK_ERROR :
- targetPrivate->dnsHaveError = TRUE;
- break;
- case MARK_TIMEOUT :
- targetPrivate->dnsHaveTimeout = TRUE;
- break;
- case MARK_HAVE_V4 :
- targetPrivate->dnsHaveV4 = TRUE;
- break;
- case MARK_HAVE_V6 :
- targetPrivate->dnsHaveV6 = TRUE;
- break;
- case MARK_HAVE_PTR :
- targetPrivate->dnsHavePTR = TRUE;
- break;
- }
-
- return;
-}
-
-
-/*
- * _dns_callback
- *
- * Called to process [m]DNS query updates
- * - caller must be running on the _dns_queue()
- */
-static void
-_dns_callback(DNSServiceRef sdRef,
- DNSServiceFlags flags,
- DNSServiceErrorType errorCode,
- _dns_mark_t dnsMark,
- CFTypeRef dnsAddress, // CFData(struct sockaddr) or CFString(ptr_name)
- void *context)
-{
- int failures = 0;
- Boolean restart = FALSE;
- SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)context;
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- MUTEX_LOCK(&targetPrivate->lock);
-
- if (sdRef != targetPrivate->dnsTarget) {
- // if this DNSServiceRef is no longer associated with the target
- MUTEX_UNLOCK(&targetPrivate->lock);
- return;
- }
-
- switch (errorCode) {
- case kDNSServiceErr_NoError :
- if (dnsAddress != NULL) {
- CFMutableArrayRef addresses;
- CFIndex i;
-
- _dns_mark(target, dnsMark);
-
- if (targetPrivate->dnsAddresses != NULL) {
- if (isA_CFArray(targetPrivate->dnsAddresses)) {
- addresses = CFArrayCreateMutableCopy(NULL, 0, targetPrivate->dnsAddresses);
- } else {
- addresses = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
- }
-
- CFRelease(targetPrivate->dnsAddresses);
- targetPrivate->dnsAddresses = NULL;
- } else {
- addresses = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
- }
-
- i = CFArrayGetFirstIndexOfValue(addresses,
- CFRangeMake(0, CFArrayGetCount(addresses)),
- dnsAddress);
- if (flags & kDNSServiceFlagsAdd) {
- // add address
- if (i == kCFNotFound) {
- CFArrayAppendValue(addresses, dnsAddress);
- }
-#ifdef HANDLE_RMV_REQUESTS
- } else {
- // remove address
- if (i != kCFNotFound) {
- CFArrayRemoveValueAtIndex(addresses, i);
- }
-#endif // HANDLE_RMV_REQUESTS
- }
-
- if (CFArrayGetCount(addresses) > 0) {
- targetPrivate->dnsAddresses = addresses;
- targetPrivate->dnsError = NETDB_SUCCESS;
- } else {
- // if host not found
- targetPrivate->dnsAddresses = CFRetain(kCFNull);
- targetPrivate->dnsError = EAI_NONAME;
- CFRelease(addresses);
- }
-
- }
- break;
- case kDNSServiceErr_BadParam :
- _dns_mark(target, MARK_ERROR);
-
- if (targetPrivate->dnsAddresses != NULL) {
- CFRelease(targetPrivate->dnsAddresses);
- }
- targetPrivate->dnsAddresses = CFRetain(kCFNull);
- targetPrivate->dnsError = EAI_NONAME;
- break;
- case kDNSServiceErr_NoSuchRecord :
- _dns_mark(target, dnsMark);
-
- if (targetPrivate->dnsAddresses == NULL) {
- targetPrivate->dnsAddresses = CFRetain(kCFNull);
- targetPrivate->dnsError = EAI_NONAME;
- }
- break;
- case kDNSServiceErr_Timeout :
- _dns_mark(target, MARK_TIMEOUT);
-
- if (targetPrivate->dnsAddresses == NULL) {
- targetPrivate->dnsAddresses = CFRetain(kCFNull);
- targetPrivate->dnsError = EAI_NONAME;
- }
- break;
- default :
- SCLog(TRUE, LOG_ERR,
- CFSTR("%sSCNetworkReachability _dns_callback w/error=%d (n=%d)"),
- targetPrivate->log_prefix,
- errorCode,
- targetPrivate->dnsFailures + 1);
- // fall through
- case kDNSServiceErr_ServiceNotRunning :
- _dns_mark(target, MARK_ERROR);
-
- // bump per-target failure count
- failures = ++targetPrivate->dnsFailures;
-
- // Check to see if we've seen too many failures for this target
- if (failures > 2) {
- // if so, there's little point in retrying over
- // and over again so let's just return an error
- // and move on.
- if (targetPrivate->dnsAddresses != NULL) {
- CFRelease(targetPrivate->dnsAddresses);
- }
- targetPrivate->dnsAddresses = CFRetain(kCFNull);
- targetPrivate->dnsError = EAI_NONAME;
- } else if (targetPrivate->dnsGeneration == dnsGeneration) {
- // if not, then "mDNSResponder" crashed or some
- // other/unexpected error occurred. In this
- // case, we'll try again with a clean slate and
- // restart all requests.
- if (dnsMain != NULL) {
- DNSServiceRefDeallocate(dnsMain);
- dnsMain = NULL;
- dnsCount = 0;
- dnsGeneration++;
- restart = TRUE;
- }
- }
- break;
- }
-
- // update DNS failure count (and [re-]set to zero if we're OK)
- targetPrivate->dnsFailures = failures;
-
- MUTEX_UNLOCK(&targetPrivate->lock);
-
- if (restart) {
- SCLog(TRUE, LOG_DEBUG,
- CFSTR("%sreconnecting SCNetworkReachability w/\"mDNSResponder\" (%d)"),
- targetPrivate->log_prefix,
- dnsGeneration);
-
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 500 * NSEC_PER_MSEC),
- _hn_changes_queue(),
- ^{
- dns_refresh_handler();
- });
-
- // and flush the dnsUpdated queue as any DNS results we may have
- // accumulated are no longer valid.
- if (dnsUpdated != NULL) {
- CFRelease(dnsUpdated);
- dnsUpdated = NULL;
- }
- return;
- }
-
- if (targetPrivate->dnsHaveTimeout) {
- targetPrivate->dnsNoAddressesSinceLastTimeout = TRUE;
- } else if (targetPrivate->dnsNoAddressesSinceLastTimeout &&
- isA_CFArray(targetPrivate->dnsAddresses) &&
- CFArrayGetCount(targetPrivate->dnsAddresses) > 0)
- {
- targetPrivate->dnsNoAddressesSinceLastTimeout = FALSE;
- }
-
- // 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 (dnsUpdated == NULL) {
- dnsUpdated = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
- }
- CFSetAddValue(dnsUpdated, target);
-
- if (!(flags & kDNSServiceFlagsMoreComing)) {
- CFSetApplyFunction(dnsUpdated, _dns_notify, NULL);
- CFRelease(dnsUpdated);
- dnsUpdated = NULL;
- }
-
- return;
-}
-
-
-/*
- * _dns_getaddrinfo_callback
- *
- * Called to process [m]DNS query updates
- * - caller must be running on the _dns_queue()
- */
-static void
-_dns_getaddrinfo_callback(DNSServiceRef sdRef,
- DNSServiceFlags flags,
- uint32_t interfaceIndex,
- DNSServiceErrorType errorCode,
- const char *hostname,
- const struct sockaddr *address,
- uint32_t ttl,
- void *context)
-{
- CFDataRef dnsAddress = NULL;
- _dns_mark_t dnsMark = MARK_NONE;
-
- if (address != NULL) {
- switch (errorCode) {
- case kDNSServiceErr_NoError :
- dnsAddress = CFDataCreate(NULL, (void *)address, address->sa_len);
- // ... and fall through
- case kDNSServiceErr_NoSuchRecord :
- switch (address->sa_family) {
- case AF_INET :
- dnsMark = MARK_HAVE_V4;
- break;
- case AF_INET6 :
- dnsMark = MARK_HAVE_V6;
- break;
- }
- break;
- default :
- break;
- }
- }
-
- _dns_callback(sdRef, flags, errorCode, dnsMark, dnsAddress, context);
-
- if (dnsAddress != NULL) {
- CFRelease(dnsAddress);
- }
-
- return;
-}
-
-
-static CFStringRef
-_dns_copy_domain_name(const uint8_t *rdata, uint16_t rdlen)
-{
- CFMutableStringRef domain;
- const uint8_t *label;
- uint8_t label_len;
-
- domain = CFStringCreateMutable(NULL, 0);
-
- label = rdata;
- label_len = *(label++);
- while (label_len != 0) {
- while (label_len-- > 0) {
- uint8_t byte = *label++;
-
- if ((byte == '.') || (byte == '\\')) {
- // if escape needed
- CFStringAppendFormat(domain, NULL, CFSTR("\\%c"), byte);
- } else if (byte <= ' ') {
- CFStringAppendFormat(domain, NULL, CFSTR("\\%c%c%c"),
- '0' + (byte / 100),
- '0' + ((byte / 10) % 10),
- '0' + (byte % 10));
- } else {
- CFStringAppendFormat(domain, NULL, CFSTR("%c"), byte);
- }
- }
-
- label_len = *(label++);
- if (label_len != 0) {
- CFStringAppendFormat(domain, NULL, CFSTR("."));
- }
- }
-
- return domain;
-}
-
-
-static void
-_dns_queryrecord_callback(DNSServiceRef sdRef,
- DNSServiceFlags flags,
- uint32_t interfaceIndex,
- DNSServiceErrorType errorCode,
- const char *fullname,
- uint16_t rrtype,
- uint16_t rrclass,
- uint16_t rdlen,
- const void *rdata,
- uint32_t ttl,
- void *context)
-{
- _dns_mark_t dnsMark = MARK_NONE;
- CFStringRef dnsPTRName = NULL;
- SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)context;
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- // for now, we only support using DNSServiceQueryRecord for PTR queries
- assert(targetPrivate->type == reachabilityTypePTR);
-
- if (rdata != NULL) {
- switch (errorCode) {
- case kDNSServiceErr_NoError :
- if (rrtype == kDNSServiceType_PTR) {
- dnsPTRName = _dns_copy_domain_name(rdata, rdlen);
- }
- // ... and fall through
- case kDNSServiceErr_NoSuchRecord :
- dnsMark = MARK_HAVE_PTR;
- break;
- default :
- break;
- }
- }
-
- _dns_callback(sdRef, flags, errorCode, dnsMark, dnsPTRName, context);
-
- if (dnsPTRName != NULL) {
- CFRelease(dnsPTRName);
- }
-
- return;
-}
-
-
-static Boolean
-enqueueDNSQuery(SCNetworkReachabilityRef target)
-{
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- MUTEX_ASSERT_HELD(&targetPrivate->lock);
-
- // clear DNS flags, mark the query active
- targetPrivate->dnsFlags = 0;
- targetPrivate->dnsActive = TRUE;
-
- // track the DNS resolution time
- __mark_operation_start(&targetPrivate->dnsQueryStart, &targetPrivate->dnsQueryEnd);
-
- CFRetain(target);
- dispatch_async(_dns_queue(), ^{
- DNSServiceErrorType err;
- const char *fcn = "???";
- DNSServiceRef sdRef = NULL;
-
- if (targetPrivate->dnsTarget != NULL) {
- // if already running
- CFRelease(target);
- return;
- }
-
- // if needed, start interacting with "mDNSResponder"
- if (dnsMain == NULL) {
- err = DNSServiceCreateConnection(&dnsMain);
- if (err != kDNSServiceErr_NoError) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("%sDNSServiceCreateConnection(&dnsMain) failed, error = %d"),
- targetPrivate->log_prefix,
- err);
- goto done;
- }
-
- err = DNSServiceSetDispatchQueue(dnsMain, _dns_queue());
- if (err != kDNSServiceErr_NoError) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("%sDNSServiceSetDispatchQueue() failed, error = %d"),
- targetPrivate->log_prefix,
- err);
- DNSServiceRefDeallocate(dnsMain);
- dnsMain = NULL;
- dnsGeneration++;
- goto done;
- }
- }
-
- // start a query for this target
- sdRef = dnsMain;
-
- switch (targetPrivate->type) {
- case reachabilityTypeName :
- fcn = "DNSServiceGetAddrInfo";
- err = DNSServiceGetAddrInfo(&sdRef, // sdRef
- kDNSServiceFlagsReturnIntermediates // flags
- | kDNSServiceFlagsShareConnection
- | kDNSServiceFlagsSuppressUnusable
- | kDNSServiceFlagsTimeout,
- targetPrivate->if_index, // interfaceIndex
- 0, // protocol
- targetPrivate->name, // hostname
- _dns_getaddrinfo_callback, // callback
- (void *)target); // context
- break;
- case reachabilityTypePTR :
- fcn = "DNSServiceQueryRecord";
- err = DNSServiceQueryRecord(&sdRef, // sdRef
- kDNSServiceFlagsReturnIntermediates // flags
- | kDNSServiceFlagsShareConnection
- | kDNSServiceFlagsSuppressUnusable
- | kDNSServiceFlagsTimeout,
- targetPrivate->if_index, // interfaceIndex
- targetPrivate->name, // fullname
- kDNSServiceType_PTR, // rrtype
- kDNSServiceClass_IN, // rrclass
- _dns_queryrecord_callback, // callback
- (void *)target); // context
- break;
- default :
- err = kDNSServiceErr_Unknown;
- break;
- }
-
- switch (err) {
- case kDNSServiceErr_NoError :
- dnsCount++;
- break;
-
- default :
- SCLog(TRUE, LOG_ERR,
- CFSTR("%s%s() failed, error = %d (%d)"),
- targetPrivate->log_prefix,
- fcn,
- err,
- dnsCount);
- // fall through
-
- case kDNSServiceErr_BadParam :
- if (dnsCount == 0) {
- // if this was the first request
- DNSServiceRefDeallocate(dnsMain);
- dnsMain = NULL;
- dnsGeneration++;
- }
- sdRef = NULL;
- break;
- }
-
- done :
-
- MUTEX_LOCK(&targetPrivate->lock);
-
- if (err == kDNSServiceErr_NoError) {
- targetPrivate->dnsGeneration = dnsGeneration;
- targetPrivate->dnsTarget = sdRef;
- } else {
- targetPrivate->dnsActive = FALSE;
-
- // queue up the returned error
- dispatch_async(_dns_queue(), ^{
- _dns_callback(NULL, // sdRef
- 0, // flags
- err, // errorCode
- MARK_ERROR, // dnsMark
- NULL, // dnsAddress
- (void *)target); // context
- CFRelease(target);
- });
- }
-
- MUTEX_UNLOCK(&targetPrivate->lock);
-
- return;
- });
-
- return TRUE;
-}
-
-
-static void
-dequeueDNSQuery(SCNetworkReachabilityRef target)
-{
- DNSServiceRef sdRef;
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- MUTEX_ASSERT_HELD(&targetPrivate->lock);
-
- // terminate the [target] [m]DNS query
- sdRef = targetPrivate->dnsTarget;
- targetPrivate->dnsTarget = NULL;
-
- // mark the query NOT active
- targetPrivate->dnsActive = FALSE;
-
- // don't do anything if the sdRef is not valid
- if (sdRef != NULL) {
- int generation;
-
- generation = targetPrivate->dnsGeneration;
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3LL * NSEC_PER_SEC),
- _dns_queue(),
- ^{
- if (generation == dnsGeneration) {
- // if we're pointing to the same DNSService
- // generation as the main/active session
- // deallocate per-target query
- DNSServiceRefDeallocate(sdRef);
- dnsCount--;
- if (dnsCount == 0) {
- // if no more queries active
- DNSServiceRefDeallocate(dnsMain);
- dnsMain = NULL;
- dnsGeneration++;
- }
- }
-
- CFRelease(target);
- });
- }
-
- if (targetPrivate->dnsAddresses != NULL) {
- CFRelease(targetPrivate->dnsAddresses);
- targetPrivate->dnsAddresses = NULL;
- }
- targetPrivate->dnsError = NETDB_SUCCESS;
-
- return;
-}
-
-
-#pragma mark -
-#pragma mark Synchronous DNS query support
-
-
-#define SYNC_DNS_QUERY_TIMEOUT_NSEC 35 * NSEC_PER_SEC // 35s
-
-
-static void
-sync_DNS_query_callback(SCNetworkReachabilityRef clone,
- SCNetworkReachabilityFlags cloneFlags,
- void *info)
-{
- dispatch_semaphore_t s = (dispatch_semaphore_t)info;
-
- dispatch_semaphore_signal(s);
- return;
-}
-
-
-static void
-sync_DNS_query(SCNetworkReachabilityRef target)
-{
- SCNetworkReachabilityRef clone;
- SCNetworkReachabilityPrivateRef clonePrivate;
- SCNetworkReachabilityContext context = { 0, NULL, NULL, NULL, NULL };
- dispatch_queue_t q;
- long ret;
- dispatch_semaphore_t s;
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- clone = __SCNetworkReachabilityCreateCopy(target);
- if (clone == NULL) {
- return;
- }
- clonePrivate = (SCNetworkReachabilityPrivateRef)clone;
-
- q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- s = dispatch_semaphore_create(0);
-
- // start async query
- context.info = (void *)s;
- SCNetworkReachabilitySetCallback(clone, sync_DNS_query_callback, &context);
- SCNetworkReachabilitySetDispatchQueue(clone, q);
-
- // wait for reply (or timeout)
- ret = dispatch_semaphore_wait(s, dispatch_time(DISPATCH_TIME_NOW,
- SYNC_DNS_QUERY_TIMEOUT_NSEC));
- if (ret != 0) {
- dispatch_sync(_dns_queue(), ^{
- // mark as both a timeout *and* an error
- _dns_mark(clone, MARK_TIMEOUT);
- _dns_mark(clone, MARK_ERROR);
-
- __mark_operation_end(clone,
- FALSE, // if successful query
- dns_query_mdns_timeout, // [m]DNS query
- &clonePrivate->dnsQueryStart, // start time
- &clonePrivate->dnsQueryEnd); // end time
-
- MUTEX_LOCK(&clonePrivate->lock);
-
- // update target info with what's available
- if (clonePrivate->resolvedAddresses != NULL) {
- CFRelease(clonePrivate->resolvedAddresses);
- clonePrivate->resolvedAddresses = NULL;
- }
- if ((clonePrivate->dnsAddresses != NULL) &&
- isA_CFArray(clonePrivate->dnsAddresses) &&
- (CFArrayGetCount(clonePrivate->dnsAddresses) > 0)) {
- clonePrivate->resolvedAddresses = CFArrayCreateMutableCopy(NULL,
- 0,
- clonePrivate->dnsAddresses);
- }
- if (clonePrivate->resolvedAddresses != NULL) {
- // if timeout w/partial results
- clonePrivate->resolvedError = NETDB_SUCCESS;
- } else {
- // if timeout w/no results
- clonePrivate->resolvedAddresses = CFRetain(kCFNull);
- clonePrivate->resolvedError = EAI_NONAME;
- }
-
- MUTEX_UNLOCK(&clonePrivate->lock);
- });
- }
-
- // cancel request
- SCNetworkReachabilitySetDispatchQueue(clone, NULL);
- SCNetworkReachabilitySetCallback(clone, NULL, NULL);
-
- // transfer reply
- if (clonePrivate->resolvedAddresses != NULL) CFRetain(clonePrivate->resolvedAddresses);
- if (targetPrivate->resolvedAddresses != NULL) CFRelease(targetPrivate->resolvedAddresses);
- targetPrivate->resolvedAddresses = clonePrivate->resolvedAddresses;
- targetPrivate->resolvedError = clonePrivate->resolvedError;
- targetPrivate->resolverFlags = clonePrivate->resolverFlags;
- targetPrivate->cycle = clonePrivate->cycle;
- targetPrivate->dnsFlags = clonePrivate->dnsFlags;
- memcpy(&targetPrivate->info, &clonePrivate->info, sizeof(ReachabilityInfo));
- memcpy(&targetPrivate->last_notify, &clonePrivate->last_notify, sizeof(ReachabilityInfo));
-
- CFRelease(clone);
- dispatch_release(s);
-
- return;
-}
-
-
-#pragma mark -
-#pragma mark Network Information support
-
-
-// Note: protected by _hn_target_queue()
-static int network_changed_token;
-static Boolean network_changed_token_valid = FALSE;
-
-
-/*
- * nwi_refresh_handler
- *
- * Called to notify/update network changed events
- * - should be exec'd on the _hn_changes_queue()
- */
-static void
-nwi_refresh_handler()
-{
- CFArrayRef changes;
- CFStringRef key;
- __block SCDynamicStoreRef store = NULL;
-
- dispatch_sync(_hn_target_queue(), ^{
- if (network_changed_token_valid && (hn_store != NULL)) {
- store = CFRetain(hn_store);
- }
- });
-
- if (store == NULL) {
- return;
- }
-
- // Fake a network change.
- key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
- kSCDynamicStoreDomainState,
- kSCEntNetIPv4);
- changes = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks);
- __SCNetworkReachabilityHandleChanges(store, changes, NULL);
- CFRelease(changes);
- CFRelease(key);
-
- CFRelease(store);
- return;
-}
-
-
-/*
- * nwi_refresh_enable
- *
- * Called to monitor for network changes.
- * - caller must be running on the _hn_target_queue()
- * - passed in queue should be _hn_changes_queue()
- */
-static Boolean
-nwi_refresh_enable(dispatch_queue_t q)
-{
- uint32_t status;
-
- status = notify_register_dispatch(_SC_NOTIFY_NETWORK_CHANGE_NWI, // trailing nwi_state_get_notify_key()
- &network_changed_token,
- q,
- ^(int token){
- nwi_refresh_handler();
- });
- if (status != NOTIFY_STATUS_OK) {
- SCLog(TRUE, LOG_INFO, CFSTR("notify_register_dispatch() failed for network changes, status=%u"), status);
- return FALSE;
- }
-
- network_changed_token_valid = TRUE;
-
- return TRUE;
-}
-
-
-/*
- * nwi_refresh_disable
- *
- * Called to stop monitoring for network changes
- * - caller must be running on the _hn_target_queue()
- */
-static void
-nwi_refresh_disable()
-{
- if (network_changed_token_valid) {
- (void)notify_cancel(network_changed_token);
- network_changed_token_valid = FALSE;
- }
-
- return;
-}
-
-
-#pragma mark -
-#pragma mark Sleep/wake support
-
-
-#if !TARGET_OS_IPHONE
-
-// Note: protected by _hn_target_queue()
-static IOPMConnection power_changed_connection = NULL;
-static const CFStringRef power_changed_key = CFSTR("*** EARLY WAKE ***");
-
-
-/*
- * power_refresh_handler
- *
- * Called to notify/update power capability changed events
- * - should be exec'd on the _hn_changes_queue()
- */
-static void
-power_refresh_handler(void *param,
- IOPMConnection connection,
- IOPMConnectionMessageToken token,
- IOPMSystemPowerStateCapabilities capabilities)
-{
- Boolean change;
- IOReturn ret;
- __block SCDynamicStoreRef store = NULL;
-
- dispatch_sync(_hn_target_queue(), ^{
- if ((power_changed_connection != NULL) && (hn_store != NULL)) {
- store = CFRetain(hn_store);
- }
- });
-
- if (store == NULL) {
- return;
- }
-
- // check for [relevant] changes
- change = ((power_capabilities ^ capabilities) & POWER_CAPABILITIES_NETWORK) != 0;
-
- // update capabilities
- power_capabilities = capabilities;
-
- if (change) {
- CFArrayRef changes;
-
- // fake a network change.
- changes = CFArrayCreate(NULL, (const void **)&power_changed_key, 1, &kCFTypeArrayCallBacks);
- __SCNetworkReachabilityHandleChanges(store, changes, NULL);
- CFRelease(changes);
- }
-
- ret = IOPMConnectionAcknowledgeEvent(connection, token);
- if (ret != kIOReturnSuccess) {
- SCLog(TRUE, LOG_ERR, CFSTR("IOPMConnectionAcknowledgeEvent failed, 0x%08x"), ret);
- }
-
- CFRelease(store);
- return;
-}
-
-
-/*
- * power_refresh_enable
- *
- * Called to monitor power changes.
- * - caller must be running on the _hn_target_queue()
- * - passed in queue should be _hn_changes_queue()
- */
-static Boolean
-power_refresh_enable(dispatch_queue_t q)
-{
- IOPMConnection connection = NULL;
- IOReturn ret;
-
- ret = IOPMConnectionCreate(CFSTR("com.apple.SCNetworkReachability"),
- kIOPMEarlyWakeNotification | kIOPMSleepWakeInterest,
- &connection);
- if (ret != kIOReturnSuccess) {
- SCLog(TRUE, LOG_ERR, CFSTR("IOPMConnectionCreate failed, 0x%08x"), ret);
- goto failed;
- }
-
- ret = IOPMConnectionSetNotification(connection, NULL, power_refresh_handler);
- if (ret != kIOReturnSuccess) {
- SCLog(TRUE, LOG_ERR, CFSTR("IOPMConnectionSetNotification failed, 0x%08x"), ret);
- goto failed;
- }
-
- power_changed_connection = connection;
- IOPMConnectionSetDispatchQueue(connection, q);
- power_capabilities = IOPMConnectionGetSystemCapabilities();
-
- return TRUE;
-
- failed:
-
- if (connection != NULL) {
- IOPMConnectionRelease(connection);
- }
-
- return FALSE;
-}
-
-
-static void
-power_refresh_disable()
-{
- if (power_changed_connection != NULL) {
- IOPMConnectionSetDispatchQueue(power_changed_connection, NULL);
- IOPMConnectionRelease(power_changed_connection);
- power_changed_connection = NULL;
- }
-
- return;
-}
-
-#endif // !TARGET_OS_IPHONE
-
-
-
-
-
-
-#pragma mark -
-#pragma mark OnDemand
-
-
-SCNetworkServiceRef
-SCNetworkReachabilityCopyOnDemandService(SCNetworkReachabilityRef target,
- CFDictionaryRef *userOptions)
-{
- SCNetworkServiceRef service = NULL;
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- if (!isA_SCNetworkReachability(target)) {
- _SCErrorSet(kSCStatusInvalidArgument);
- return NULL;
- }
-
- if (targetPrivate->onDemandServiceID != NULL) {
- service = _SCNetworkServiceCopyActive(NULL, targetPrivate->onDemandServiceID);
- }
-
- if (userOptions != NULL) {
- if (targetPrivate->onDemandName != NULL) {
- *userOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
- CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, targetPrivate->onDemandName);
- } else {
- *userOptions = NULL;
- }
- }
-
- return service;
-}
-
-
-
-
-static void
-__SCNetworkReachabilityOnDemandCheckCallback(SCNetworkReachabilityRef onDemandServer,
- SCNetworkReachabilityFlags onDemandFlags,
- void *info)
-{
- SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- MUTEX_LOCK(&targetPrivate->lock);
-
- if (!targetPrivate->scheduled) {
- // if not currently scheduled
- MUTEX_UNLOCK(&targetPrivate->lock);
- return;
- }
-
- SCLog(_sc_debug, LOG_INFO, CFSTR("%sOnDemand \"server\" status changed (now 0x%08x)"),
- targetPrivate->log_prefix,
- onDemandFlags);
-
- if (targetPrivate->type == reachabilityTypeName) {
- // make sure that we resolve the name again
- targetPrivate->needResolve = TRUE;
- }
-
- __SCNetworkReachabilityUpdate(target);
-
- MUTEX_UNLOCK(&targetPrivate->lock);
-
- return;
-}
-
-
-static Boolean
-__SCNetworkReachabilityOnDemandCheck(ReachabilityStoreInfoRef store_info,
- SCNetworkReachabilityRef target,
- Boolean onDemandRetry,
- SCNetworkReachabilityFlags *flags)
-{
- SCNetworkConnectionRef connection = NULL;
- SCNetworkConnectionType connectionType = kSCNetworkConnectionTypeUnknown;
- Boolean isAppLayerVPN = FALSE;
- Boolean isOnDemandService = FALSE;
- Boolean ok = FALSE;
- CFStringRef onDemandRemoteAddress = NULL;
- CFStringRef onDemandServiceID = NULL;
- SCNetworkConnectionStatus onDemandStatus = kSCNetworkConnectionInvalid;
- CFMutableDictionaryRef selectOptions = NULL;
- Boolean success = FALSE;
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- MUTEX_ASSERT_HELD(&targetPrivate->lock);
-
- if (targetPrivate->onDemandName == NULL) {
- targetPrivate->onDemandName = CFStringCreateWithCString(NULL, targetPrivate->name, kCFStringEncodingUTF8);
- }
-
- /*
- * check if an OnDemand VPN configuration matches the name.
- */
-
- connection = SCNetworkConnectionCreate(kCFAllocatorDefault, NULL, NULL);
- if (connection == NULL) {
- goto done;
- }
-
- /* set select options */
- selectOptions = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
- if (selectOptions == NULL) {
- goto done;
- }
-
- CFDictionaryAddValue(selectOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, targetPrivate->onDemandName);
- CFDictionaryAddValue(selectOptions, kSCNetworkConnectionSelectionOptionOnDemandRetry, onDemandRetry ? kCFBooleanTrue : kCFBooleanFalse);
- CFDictionaryAddValue(selectOptions, kSCNetworkConnectionSelectionOptionNoUserPrefs, kCFBooleanTrue);
-
- /* select service. May be On Demand or App Layer VPN */
- if (!SCNetworkConnectionSelectServiceWithOptions(connection, selectOptions)) {
- goto done;
- }
-
- /* get reachability flags (of VPN server) */
- (void) SCNetworkConnectionGetReachabilityInfo(connection, flags, NULL);
-
- connectionType = SCNetworkConnectionGetType(connection);
- if (connectionType == kSCNetworkConnectionTypeAppLayerVPN) {
- isAppLayerVPN = TRUE;
- }
-
- /* get on-demand info */
- onDemandServiceID = SCNetworkConnectionCopyServiceID(connection);
- if (SCNetworkConnectionCopyOnDemandInfo(connection, &onDemandRemoteAddress, &onDemandStatus)) {
- if (onDemandRemoteAddress != NULL) {
- isOnDemandService = TRUE;
- ok = TRUE;
- }
- }
-
- /* handle non-OnDemand App Layer VPN */
- if (isAppLayerVPN && !isOnDemandService) {
- SCLog(_sc_debug, LOG_INFO, CFSTR("%s status * = 0x%08x (App Layer VPN)"),
- targetPrivate->log_prefix,
- *flags);
- if (*flags & kSCNetworkReachabilityFlagsReachable) {
- // if VPN "server" is reachable
-
- if (!(*flags & kSCNetworkReachabilityFlagsTransientConnection)) {
- // start w/clean flags if not already layered on a transient network
- *flags = kSCNetworkReachabilityFlagsReachable;
- }
-
- *flags |= kSCNetworkReachabilityFlagsTransientConnection;
- if (onDemandStatus != kSCNetworkConnectionConnected) {
- *flags |= kSCNetworkReachabilityFlagsConnectionRequired;
- }
-
- SCLog(_sc_debug, LOG_INFO, CFSTR("%s status = isReachable%s"),
- (onDemandStatus != kSCNetworkConnectionConnected)
- ? " (after App Layer connect)" : "",
- targetPrivate->log_prefix);
- }
-
- success = TRUE;
- goto done;
- }
-
- if (!_SC_CFEqual(targetPrivate->onDemandRemoteAddress, onDemandRemoteAddress) ||
- !_SC_CFEqual(targetPrivate->onDemandServiceID, onDemandServiceID)) {
- if (targetPrivate->onDemandRemoteAddress != NULL) {
- CFRelease(targetPrivate->onDemandRemoteAddress);
- targetPrivate->onDemandRemoteAddress = NULL;
- }
-
- if (targetPrivate->onDemandServer != NULL) {
- SCNetworkReachabilitySetCallback(targetPrivate->onDemandServer, NULL, NULL);
- if (targetPrivate->dispatchQueue != NULL) {
- // unschedule
- __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, NULL, NULL, TRUE);
- } else if (targetPrivate->rls != NULL) {
- CFIndex i;
- CFIndex n;
-
- // unschedule
- 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);
-
- __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, rl, rlMode, TRUE);
- }
- }
-
- CFRelease(targetPrivate->onDemandServer);
- targetPrivate->onDemandServer = NULL;
- }
-
- if (targetPrivate->onDemandServiceID != NULL) {
- CFRelease(targetPrivate->onDemandServiceID);
- targetPrivate->onDemandServiceID = NULL;
- }
- }
-
- if (ok) {
- if (onDemandStatus != kSCNetworkConnectionConnected) {
- /*
- * if we have a VPN configuration matching the name *and* we need to
- * bring the VPN up. Combine our flags with those of the VPN server.
- */
- if (targetPrivate->onDemandServer == NULL) {
- SCNetworkReachabilityPrivateRef demandPrivate;
- CFMutableDictionaryRef options;
-
- options = CFDictionaryCreateMutable(NULL,
- 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks);
- CFDictionarySetValue(options, kSCNetworkReachabilityOptionNodeName, onDemandRemoteAddress);
- CFDictionarySetValue(options, kSCNetworkReachabilityOptionConnectionOnDemandBypass, kCFBooleanTrue);
- if (targetPrivate->serverBypass) {
- CFDictionarySetValue(options, kSCNetworkReachabilityOptionServerBypass, kCFBooleanTrue);
- }
- targetPrivate->onDemandServer = SCNetworkReachabilityCreateWithOptions(NULL, options);
- CFRelease(options);
-
- // indent OnDemand target
- demandPrivate = (SCNetworkReachabilityPrivateRef)targetPrivate->onDemandServer;
- strlcat(demandPrivate->log_prefix, ".... ", sizeof(demandPrivate->log_prefix));
-
- if (targetPrivate->scheduled) {
- SCNetworkReachabilityContext context = { 0, NULL, CFRetain, CFRelease, CFCopyDescription };
-
- context.info = (void *)target;
- SCNetworkReachabilitySetCallback(targetPrivate->onDemandServer,
- __SCNetworkReachabilityOnDemandCheckCallback,
- &context);
-
- // schedule server reachability to match that of the target
- if (targetPrivate->dispatchQueue != NULL) {
- __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate->onDemandServer, NULL, NULL, targetPrivate->dispatchQueue, TRUE);
- } else {
- CFIndex i;
- CFIndex n;
-
- 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);
-
- __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate->onDemandServer, rl, rlMode, NULL, TRUE);
- }
- }
- }
- }
-
- SCLog(_sc_debug, LOG_INFO, CFSTR("%s status * = 0x%08x"),
- targetPrivate->log_prefix,
- *flags);
-
-
- if ((*flags & kSCNetworkReachabilityFlagsReachable) && !(*flags & kSCNetworkReachabilityFlagsConnectionRequired)) {
- // if VPN "server" is [still] reachable
-
- if (!(*flags & kSCNetworkReachabilityFlagsTransientConnection)) {
- // start w/clean flags if not already layered on a transient network
- *flags = kSCNetworkReachabilityFlagsReachable;
- }
-
- *flags |= kSCNetworkReachabilityFlagsTransientConnection;
- *flags |= kSCNetworkReachabilityFlagsConnectionRequired;
- *flags |= kSCNetworkReachabilityFlagsConnectionOnDemand;
-
- // set 'InterventionRequired' if the OnDemand connection is paused
- if (SCNetworkConnectionIsOnDemandSuspended(connection)) {
- *flags |= kSCNetworkReachabilityFlagsInterventionRequired;
- }
-
- if (_sc_debug) {
- SCLog(TRUE, LOG_INFO, CFSTR("%s service * = %@"),
- targetPrivate->log_prefix,
- onDemandServiceID);
- SCLog(TRUE, LOG_INFO, CFSTR("%s status = isReachable (after OnDemand connect)"),
- targetPrivate->log_prefix);
- }
-
- success = TRUE;
- }
- }
-
- if (onDemandRemoteAddress != NULL) {
- if (targetPrivate->onDemandRemoteAddress == NULL) {
- targetPrivate->onDemandRemoteAddress = CFRetain(onDemandRemoteAddress);
- }
- }
-
- if (onDemandServiceID != NULL) {
- if (targetPrivate->onDemandServiceID == NULL) {
- targetPrivate->onDemandServiceID = CFRetain(onDemandServiceID);
- }
- }
- }
-
- done:
-
- if (onDemandServiceID != NULL) {
- CFRelease(onDemandServiceID);
- }
- if (onDemandRemoteAddress != NULL) {
- CFRelease(onDemandRemoteAddress);
- }
- if (connection != NULL) {
- CFRelease(connection);
- }
- if (selectOptions != NULL) {
- CFRelease(selectOptions);
- }
- return success;
-}
-
-
-/*
- * OnDemand configuration handling
- *
- * Notes :
- *
- * 1. We have a "contract" with mDNSResponder that for EVERY network
- * or DNS configuration change that should warrant our [re-]starting
- * a query, mDNSResponder will acknowledge the latest DNS configuration.
- *
- * 2. IPMonitor also posts a notification AFTER every network or DNS
- * configuration change.
- *
- * 3. We use IPMonitor's "trailing edge" as a signal to restart any
- * by-name queries.
- */
-
-
-// Note: protected by _hn_target_queue()
-static int onDemand_refresh_token;
-static Boolean onDemand_refresh_token_valid = FALSE;
-
-
-/*
- * onDemand_refresh_handler
- *
- * Called to notify/update all SCNetworkReachability targets of
- * OnDemand changes.
- * - should be exec'd on the _hn_changes_queue()
- */
-static void
-onDemand_refresh_handler()
-{
- CFArrayRef changes;
- CFStringRef key;
- __block SCDynamicStoreRef store = NULL;
-
- dispatch_sync(_hn_target_queue(), ^{
- if (onDemand_refresh_token_valid && (hn_store != NULL)) {
- store = CFRetain(hn_store);
- }
- });
-
- if (store == NULL) {
- return;
- }
-
- key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
- kSCDynamicStoreDomainState,
- kSCEntNetOnDemand);
- changes = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks);
- __SCNetworkReachabilityHandleChanges(store, changes, NULL);
- CFRelease(changes);
- CFRelease(key);
-
- CFRelease(store);
- return;
-}
-
-
-/*
- * onDemand_refresh_enable
- *
- * Called to monitor for OnDemand changes.
- * - caller must be running on the _hn_target_queue()
- */
-static Boolean
-onDemand_refresh_enable(dispatch_queue_t q)
-{
- uint32_t status;
-
- status = notify_register_dispatch(kSCNETWORKCONNECTION_ONDEMAND_NOTIFY_KEY,
- &onDemand_refresh_token,
- q,
- ^(int token){
- onDemand_refresh_handler();
- });
- if (status != NOTIFY_STATUS_OK) {
- SCLog(TRUE, LOG_INFO, CFSTR("notify_register_dispatch() failed, status=%u"), status);
- return FALSE;
- }
-
- onDemand_refresh_token_valid = TRUE;
-
- return TRUE;
-}
-
-
-/*
- * onDemand_refresh_disable
- *
- * Called to stop monitoring for OnDemand changes
- * - caller must be running on the _hn_target_queue()
- */
-static void
-onDemand_refresh_disable()
-{
- (void)notify_cancel(onDemand_refresh_token);
- onDemand_refresh_token_valid = FALSE;
- return;
-}
-
-
-
-
-#pragma mark -
-#pragma mark Reachability Flags
-
-
-static Boolean
-__SCNetworkReachabilityGetFlags(ReachabilityStoreInfoRef store_info,
- SCNetworkReachabilityRef target,
- ReachabilityInfo *reach_info,
- Boolean async)
-{
- CFMutableArrayRef addresses = NULL;
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
- ReachabilityInfo my_info = NOT_REACHABLE;
- Boolean ok = TRUE;
-
- MUTEX_ASSERT_HELD(&targetPrivate->lock);
-
- _reach_set(reach_info, &NOT_REACHABLE, reach_info->cycle, targetPrivate->if_index, targetPrivate->if_name);
-
- if (!isA_SCNetworkReachability(target)) {
- _SCErrorSet(kSCStatusInvalidArgument);
- return FALSE;
- }
-
-#if TARGET_OS_IPHONE
- if (isReachabilityTypeName(targetPrivate->type) &&
- !async &&
- pthread_is_threaded_np() &&
- pthread_main_np()) {
- SCLog(TRUE, LOG_WARNING, CFSTR("Warning: sync SCNetworkReachability (by-name) query on main thread"));
- }
-#endif // TARGET_OS_IPHONE
-
- 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->serverInfo.cycle,
- targetPrivate->if_index,
- targetPrivate->if_name);
- goto done;
- }
- }
-
-
- switch (targetPrivate->type) {
- case reachabilityTypeAddress :
- case reachabilityTypeAddressPair : {
- /*
- * Check "local" address
- */
- if (targetPrivate->localAddress != NULL) {
- /*
- * Check "local" address
- */
- ok = checkAddress(store_info,
- targetPrivate->localAddress,
- targetPrivate->if_index,
- &my_info,
- targetPrivate->log_prefix);
- if (!ok) {
- goto done2; /* not today */
- }
-
- if (!(my_info.flags & kSCNetworkReachabilityFlagsIsLocalAddress)) {
- goto done2; /* not reachable, non-"local" address */
- }
- }
-
- /*
- * Check "remote" address
- */
- if ((targetPrivate->remoteAddress != NULL) &&
- (targetPrivate->localAddress != targetPrivate->remoteAddress)) {
- /*
- * in cases where we have different "local" and "remote" addresses
- * we need to re-initialize the to-be-returned flags.
- */
- my_info = NOT_REACHABLE;
-
- /*
- * Check "remote" address
- */
- ok = checkAddress(store_info,
- targetPrivate->remoteAddress,
- targetPrivate->if_index,
- &my_info,
- targetPrivate->log_prefix);
- if (!ok) {
- goto done2; /* not today */
- }
- }
-
- break;
-
- }
-
- case reachabilityTypeName :
- case reachabilityTypePTR : {
- int error;
- int ns_dns_config = -1;
- SCNetworkReachabilityFlags ns_flags = 0;
- uint32_t ns_if_index = 0;
-
- addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error);
- if ((addresses != NULL) || (error != NETDB_SUCCESS)) {
- /* if resolved or an error had been detected */
- if (!async) {
- /* if not an async request */
- goto checkResolvedAddresses;
- } else if (targetPrivate->dnsActive) {
- /* if [m]DNS query active */
- goto checkResolvedAddresses;
- } else if (!targetPrivate->needResolve) {
- /*
- * if this is an async request (i.e. someone is watching the reachability
- * of this target), if no query active, and if no query is needed
- */
- goto checkResolvedAddresses;
- }
- }
-
- if (!targetPrivate->onDemandBypass) {
- Boolean onDemand;
- SCNetworkReachabilityFlags onDemandFlags = 0;
-
- /*
- * before we attempt our initial DNS query, check if there is
- * an OnDemand configuration that we should be using.
- */
- onDemand = __SCNetworkReachabilityOnDemandCheck(store_info, target, FALSE, &onDemandFlags);
- if (onDemand) {
- /* if OnDemand connection is needed */
- my_info.flags = onDemandFlags;
- goto done;
- }
- }
-
- targetPrivate->dnsBlocked = FALSE;
-
- /* update the reachability of the DNS servers */
- _SC_R_updateResolverReachability(store_info,
- &ns_flags,
- &targetPrivate->haveDNS,
- targetPrivate->name,
- targetPrivate->if_index,
- &ns_if_index,
- &ns_dns_config,
- targetPrivate->log_prefix);
-
-
- // save resolver reachability flags
- targetPrivate->resolverFlags = ns_flags;
-
- if (rankReachability(ns_flags) < 2) {
- /*
- * if DNS servers are not (or are no longer) reachable, set
- * flags based on the availability of configured (but not
- * active) services.
- */
-
- SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS server(s) not available"),
- targetPrivate->log_prefix);
-
- if (!targetPrivate->dnsBlocked) {
- ok = checkAddress(store_info,
- NULL,
- targetPrivate->if_index,
- &my_info,
- targetPrivate->log_prefix);
- if (!ok) {
- SCLog(_sc_debug, LOG_INFO, CFSTR("%sNo available networks"),
- targetPrivate->log_prefix);
- goto done2;
- }
- } else {
- // if not checking "available" networks
- my_info.flags = ns_flags;
- my_info.if_index = ns_if_index;
- }
-
- if (async && targetPrivate->scheduled) {
- /*
- * return "host not found", set flags appropriately,
- * and schedule notification.
- */
- __SCNetworkReachabilitySetResolvedError(target, EAI_NONAME);
- my_info.flags |= (targetPrivate->info.flags & kSCNetworkReachabilityFlagsFirstResolvePending);
-
- SCLog(_sc_debug, LOG_INFO, CFSTR("%sno DNS servers are reachable"),
- targetPrivate->log_prefix);
- __SCNetworkReachabilityUpdate(target);
- }
-
- break;
- }
-
- if (targetPrivate->resolverBypass) {
- if (targetPrivate->haveDNS) {
- /*
- * if we are not resolving the name, and if we have
- * one or more DNS resolvers, then return flags that
- * reflect the reachability of the resolvers (and
- * not the actual name).
- */
- 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->dnsActive) {
- /* if [m]DNS query active */
- if (_sc_debug && !targetPrivate->quiet) {
- SCLog(TRUE, LOG_INFO,
- CFSTR("%swaiting for DNS reply"),
- targetPrivate->log_prefix);
- }
- if ((addresses != NULL) || (error != NETDB_SUCCESS)) {
- /* updated reachability based on the previous reply */
- goto checkResolvedAddresses;
- }
- break;
- }
-
- SCLog(_sc_debug, LOG_INFO,
- CFSTR("%sstart DNS query for name = %s"),
- targetPrivate->log_prefix,
- targetPrivate->name);
-
- /*
- * initiate an DNS query w/DNSServiceGetAddrInfo
- */
- enqueueDNSQuery(target);
- break;
- }
-
- SCLog(_sc_debug, LOG_INFO,
- CFSTR("%scheckName(%s)"),
- targetPrivate->log_prefix,
- targetPrivate->name);
-
- /*
- * OK, all of the DNS name servers are available. Let's
- * resolve the nodename into an address.
- */
- sync_DNS_query(target);
-
- if (!(targetPrivate->dnsHaveTimeout && targetPrivate->dnsHaveError)) {
- // if target reach info is valid
- memcpy(reach_info, &targetPrivate->info, sizeof(ReachabilityInfo));
- goto done2;
- }
-
- if (addresses != NULL) CFRelease(addresses);
- addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error);
-
- checkResolvedAddresses :
-
- /*
- * We first assume that the requested host is NOT available.
- * Then, check each address for accessibility and return the
- * best status available.
- */
- my_info = NOT_REACHABLE;
-
- if ((targetPrivate->type == reachabilityTypeName) && isA_CFArray(addresses)) {
- CFIndex i;
- CFIndex n = CFArrayGetCount(addresses);
- struct sockaddr *sa;
-
- for (i = 0; i < n; i++) {
- ReachabilityInfo ns_info = NOT_REACHABLE;
-
- sa = (struct sockaddr *)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses, i));
-
- ok = checkAddress(store_info,
- sa,
- targetPrivate->if_index,
- &ns_info,
- targetPrivate->log_prefix);
- if (!ok) {
- goto done2; /* not today */
- }
-
- if (rankReachability(ns_info.flags) > rankReachability(my_info.flags)) {
- /* return the best case result */
- my_info = ns_info;
- if (rankReachability(my_info.flags) == 2) {
- /* can't get any better than REACHABLE */
- break;
- }
- }
- }
-
- if (_sc_debug) {
- for (i++; i < n; i++) {
- sa = (struct sockaddr *)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses, i));
- log_address("skipAddress",
- sa,
- targetPrivate->if_index,
- targetPrivate->log_prefix);
- }
- }
- } else if ((targetPrivate->type == reachabilityTypePTR) && isA_CFArray(addresses)) {
- CFIndex i;
- CFIndex n = CFArrayGetCount(addresses);
-
- my_info = NOT_REACHABLE;
-
- for (i = 0; i < n; i++) {
- if (i == 0) {
- my_info.flags = kSCNetworkReachabilityFlagsReachable;
- }
-
- if (_sc_debug) {
- CFStringRef ptrName;
-
- ptrName = CFArrayGetValueAtIndex(addresses, i);
- SCLog(TRUE, LOG_INFO, CFSTR("%sPTR name(%@)"),
- targetPrivate->log_prefix,
- ptrName);
- }
- }
- } else {
- if ((error == EAI_NONAME)
-#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
- || (error == EAI_NODATA)
-#endif
- ) {
- /*
- * the target host name could not be resolved
- */
- if (!targetPrivate->onDemandBypass) {
- Boolean onDemand;
- SCNetworkReachabilityFlags onDemandFlags = 0;
-
- /*
- * our initial DNS query failed, check again to see if there
- * there is an OnDemand configuration that we should be using.
- */
- onDemand = __SCNetworkReachabilityOnDemandCheck(store_info, target, TRUE, &onDemandFlags);
- if (onDemand) {
- /* if OnDemand connection is needed */
- my_info.flags = onDemandFlags;
- goto done;
- }
- }
-
-
- if (!targetPrivate->haveDNS) {
- /*
- * No DNS servers are defined. Set flags based on
- * the availability of configured (but not active)
- * services.
- */
- ok = checkAddress(store_info,
- NULL,
- targetPrivate->if_index,
- &my_info,
- targetPrivate->log_prefix);
- if (!ok) {
- goto done2; /* not today */
- }
-
- if ((my_info.flags & kSCNetworkReachabilityFlagsReachable) &&
- (my_info.flags & kSCNetworkReachabilityFlagsConnectionRequired)) {
- /*
- * Since we might pick up a set of DNS servers when this connection
- * is established, don't reply with a "HOST NOT FOUND" error just yet.
- */
- break;
- }
-
- /* Host not found, not reachable! */
- my_info = NOT_REACHABLE;
- }
- }
- }
-
- break;
- }
- }
-
- done:
-
-
- _reach_set(reach_info, &my_info, targetPrivate->cycle, targetPrivate->if_index, targetPrivate->if_name);
-
- done2 :
-
- if (addresses != NULL) CFRelease(addresses);
- return ok;
-}
-
-int
-SCNetworkReachabilityGetInterfaceIndex(SCNetworkReachabilityRef target)