+++ /dev/null
-/*
- * Copyright (c) 2011-2014 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include <CoreFoundation/CoreFoundation.h>
-#include <SystemConfiguration/SystemConfiguration.h>
-#include <SystemConfiguration/SCPrivate.h>
-#include "SCNetworkReachabilityInternal.h"
-
-#include <fcntl.h>
-#include <paths.h>
-#include <CommonCrypto/CommonDigest.h>
-#include <dispatch/dispatch.h>
-#include <dispatch/private.h>
-#include <xpc/xpc.h>
-#include <xpc/private.h>
-#include <sys/rbtree.h>
-
-
-#pragma mark -
-#pragma mark Globals
-
-
-/*
- * S_debug
- * A boolean that enables additional logging.
- */
-static boolean_t S_debug = FALSE;
-
-
-#pragma mark -
-#pragma mark Support functions
-
-
-static void
-log_xpc_object(const char *msg, xpc_object_t obj)
-{
- char *desc;
-
- desc = xpc_copy_description(obj);
- SCLog(S_debug, LOG_INFO, CFSTR("%s = %s"), msg, desc);
- free(desc);
-}
-
-
-static __inline__ void
-my_CFDictionaryApplyFunction(CFDictionaryRef theDict,
- CFDictionaryApplierFunction applier,
- void *context)
-{
- CFAllocatorRef myAllocator;
- CFDictionaryRef myDict;
-
- myAllocator = CFGetAllocator(theDict);
- myDict = CFDictionaryCreateCopy(myAllocator, theDict);
- CFDictionaryApplyFunction(myDict, applier, context);
- CFRelease(myDict);
- return;
-}
-
-
-#pragma mark -
-#pragma mark SCNetworkReachability target support
-
-
-static CFMutableDictionaryRef reach_digest_map;
-
-
-static dispatch_queue_t
-_server_concurrent_queue()
-{
- static dispatch_once_t once;
- static dispatch_queue_t q;
-
- dispatch_once(&once, ^{
- q = dispatch_queue_create(REACH_SERVICE_NAME ".concurrent",
- DISPATCH_QUEUE_CONCURRENT);
- });
-
- return q;
-}
-
-
-static dispatch_queue_t
-_server_digest_queue()
-{
- static dispatch_once_t once;
- static dispatch_queue_t q;
-
- dispatch_once(&once, ^{
- q = dispatch_queue_create(REACH_SERVICE_NAME ".digest", NULL);
- });
-
- return q;
-}
-
-
-static dispatch_group_t
-_target_group(SCNetworkReachabilityRef target)
-{
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- return targetPrivate->serverGroup;
-}
-
-
-static dispatch_queue_t
-_target_queue(SCNetworkReachabilityRef target)
-{
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- return targetPrivate->serverQueue;
-}
-
-
-#pragma mark -
-
-
-/*
- * _target_reference_add
- *
- * Note: use dispatch_sync(_server_digest_queue(), ^{ ... });
- */
-static void
-_target_reference_add(SCNetworkReachabilityRef target, CFDataRef digest, xpc_connection_t connection)
-{
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- // take a reference to the target
- CFRetain(target);
-
- // ensure that we have a dispatch group
- if (targetPrivate->serverGroup == NULL) {
- targetPrivate->serverGroup = dispatch_group_create();
- }
-
- // ensure that we have a dispatch queue
- if (targetPrivate->serverQueue == NULL) {
- char qname[256];
-
- snprintf(qname, sizeof(qname), "com.apple.SCNetworkReachability.%p.server", target);
- targetPrivate->serverQueue = dispatch_queue_create(qname, NULL);
- }
-
- // bump the reference count
- if (_SC_ATOMIC_INC(&targetPrivate->serverReferences) == 0) {
- // and maintain a digest-->target mapping
- targetPrivate->serverDigest = CFRetain(digest);
- CFDictionarySetValue(reach_digest_map, digest, target);
- }
-
- if (S_debug) {
- CFStringRef str;
-
- str = _SCNetworkReachabilityCopyTargetDescription(target);
- SCLog(TRUE, LOG_INFO,
- CFSTR("<%p> target %p: reference added (%@, %d)"),
- connection,
- target,
- str,
- targetPrivate->serverReferences);
- CFRelease(str);
- }
-
- return;
-}
-
-
-/*
- * _target_reference_remove
- *
- * Note: use dispatch_sync(_server_digest_queue(), ^{ ... });
- */
-static void
-_target_reference_remove(SCNetworkReachabilityRef target, xpc_connection_t connection)
-{
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- // drop the reference count
- if (_SC_ATOMIC_DEC(&targetPrivate->serverReferences) == 0) {
- /*
- * if that was the last reference, we no longer need to
- * keep the digest-->target mapping
- */
- CFDictionaryRemoveValue(reach_digest_map, targetPrivate->serverDigest);
- CFRelease(targetPrivate->serverDigest);
- targetPrivate->serverDigest = NULL;
- }
-
- if (S_debug) {
- SCLog(TRUE, LOG_INFO,
- CFSTR("<%p> target %p: reference removed (%d)"),
- connection,
- target,
- targetPrivate->serverReferences);
- }
-
- // release a reference to the target
- CFRelease(target);
-
- return;
-}
-
-
-#pragma mark -
-
-
-#define MUTEX_LOCK(m) { \
- int _lock_ = (pthread_mutex_lock(m) == 0); \
- assert(_lock_); \
-}
-
-#define MUTEX_UNLOCK(m) { \
- int _unlock_ = (pthread_mutex_unlock(m) == 0); \
- assert(_unlock_); \
-}
-
-
-static void
-_target_reply_add_reachability(SCNetworkReachabilityRef target,
- xpc_object_t reply)
-{
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- MUTEX_LOCK(&targetPrivate->lock);
-
- xpc_dictionary_set_uint64(reply,
- REACH_STATUS_CYCLE,
- targetPrivate->info.cycle);
- xpc_dictionary_set_uint64(reply,
- REACH_STATUS_FLAGS,
- targetPrivate->info.flags);
- xpc_dictionary_set_uint64(reply,
- REACH_STATUS_IF_INDEX,
- targetPrivate->info.if_index);
- xpc_dictionary_set_data (reply,
- REACH_STATUS_IF_NAME,
- targetPrivate->info.if_name,
- sizeof(targetPrivate->info.if_name));
- xpc_dictionary_set_bool (reply,
- REACH_STATUS_SLEEPING,
- targetPrivate->info.sleeping);
- if (isReachabilityTypeName(targetPrivate->type)) {
- if (isA_CFArray(targetPrivate->resolvedAddresses)) {
- xpc_object_t addresses;
- CFIndex i;
- CFIndex n;
-
- addresses = xpc_array_create(NULL, 0);
-
- n = CFArrayGetCount(targetPrivate->resolvedAddresses);
- for (i = 0; i < n; i++) {
- if (targetPrivate->type == reachabilityTypeName) {
- CFDataRef address;
-
- address = CFArrayGetValueAtIndex(targetPrivate->resolvedAddresses, i);
- xpc_array_set_data(addresses,
- XPC_ARRAY_APPEND,
- CFDataGetBytePtr(address),
- CFDataGetLength(address));
- } else if (targetPrivate->type == reachabilityTypePTR) {
- CFStringRef name;
- char str[MAXHOSTNAMELEN];
-
- name = CFArrayGetValueAtIndex(targetPrivate->resolvedAddresses, i);
- _SC_cfstring_to_cstring(name, str, sizeof(str), kCFStringEncodingUTF8);
- xpc_array_set_string(addresses, XPC_ARRAY_APPEND, str);
- }
- }
-
- xpc_dictionary_set_value(reply,
- REACH_STATUS_RESOLVED_ADDRESSES,
- addresses);
- xpc_release(addresses);
- }
- xpc_dictionary_set_int64(reply,
- REACH_STATUS_RESOLVED_ERROR,
- targetPrivate->resolvedError);
- xpc_dictionary_set_uint64(reply,
- REACH_STATUS_DNS_FLAGS,
- targetPrivate->dnsFlags);
-
- }
-
- MUTEX_UNLOCK(&targetPrivate->lock);
-
- return;
-}
-
-
-#pragma mark -
-
-
-typedef struct {
- xpc_connection_t connection;
- uint64_t target_id;
-} reach_watcher_key_t;
-
-typedef struct {
- unsigned int n_changes;
-} reach_watcher_val_t;
-
-
-static CFDataRef
-_target_watcher_key_create(xpc_connection_t connection,
- uint64_t target_id)
-{
- CFDataRef key;
- reach_watcher_key_t watcher_key;
-
- watcher_key.connection = connection;
- watcher_key.target_id = target_id;
-
- key = CFDataCreate(NULL, (UInt8 *)&watcher_key, sizeof(watcher_key));
- return key;
-}
-
-
-static Boolean
-_target_watcher_add(SCNetworkReachabilityRef target,
- xpc_connection_t connection,
- uint64_t target_id)
-{
- __block Boolean ok = TRUE;
- dispatch_queue_t q;
-
- q = _target_queue(target);
- dispatch_sync(q, ^{
- CFDataRef key;
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- if (targetPrivate->serverWatchers == NULL) {
- ok = SCNetworkReachabilitySetDispatchQueue(target, q);
- if (!ok) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("<%p> target %p: _watcher_add SCNetworkReachabilitySetDispatchQueue() failed: %s"),
- connection,
- target,
- SCErrorString(SCError()));
- return;
- }
-
- targetPrivate->serverWatchers = CFDictionaryCreateMutable(NULL,
- 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks);
- }
-
- xpc_retain(connection);
-
- key = _target_watcher_key_create(connection, target_id);
- if (CFDictionaryContainsKey(targetPrivate->serverWatchers, key)) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("<%p> target %p: watcher not added, c=0x%0llx, \"serverWatchers\" key exists"),
- connection,
- target,
- target_id);
- } else {
- CFDataRef val;
- static const reach_watcher_val_t watcher_val0 = { 0 };
-
- val = CFDataCreate(NULL, (UInt8 *)&watcher_val0, sizeof(watcher_val0));
- CFDictionaryAddValue(targetPrivate->serverWatchers, key, val);
- CFRelease(val);
-
- if (S_debug) {
- SCLog(TRUE, LOG_INFO,
- CFSTR("<%p> target %p: watcher added, c=0x%0llx, n=%ld"),
- connection,
- target,
- target_id,
- CFDictionaryGetCount(targetPrivate->serverWatchers));
- }
- }
- CFRelease(key);
- });
-
- return ok;
-}
-
-
-static Boolean
-_target_watcher_checkin(SCNetworkReachabilityRef target,
- xpc_connection_t connection,
- uint64_t target_id)
-{
- __block Boolean scheduled = FALSE;
-
- dispatch_sync(_target_queue(target), ^{
- CFDataRef key;
- unsigned int n;
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
- CFDataRef val;
- reach_watcher_val_t *watcher_val;
-
- if (targetPrivate->serverWatchers == NULL) {
- // if no watchers
- return;
- }
-
- key = _target_watcher_key_create(connection, target_id);
- val = CFDictionaryGetValue(targetPrivate->serverWatchers, key);
- CFRelease(key);
- if (val == NULL) {
- // if the target [for this client] was not scheduled
- return;
- }
-
- // indicate that the target was scheduled
- scheduled = TRUE;
-
- /*
- * and note that the reachability flags for this target have
- * been picked up by the client
- */
- /* ALIGN: CF aligns to at least >8 byte boundries */
- watcher_val = (reach_watcher_val_t *)(void *)CFDataGetBytePtr(val);
- n = _SC_ATOMIC_ZERO(&watcher_val->n_changes);
- if (S_debug && (n > 0)) {
- SCLog(TRUE, LOG_INFO,
- CFSTR("<%p> target %p: SCNetworkReachabilityGetFlags() after %d notification%s"),
- connection,
- target,
- n,
- (n == 1) ? "" : "s");
- }
- });
-
- return scheduled;
-}
-
-
-static Boolean
-_target_watcher_remove(SCNetworkReachabilityRef target,
- xpc_connection_t connection,
- uint64_t target_id)
-{
- __block Boolean ok = TRUE;
-
- dispatch_sync(_target_queue(target), ^{
- CFDataRef key;
- CFIndex n;
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- if (targetPrivate->serverWatchers == NULL) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("<%p> target %p: watcher not removed, c=0x%0llx, no \"serverWatchers\""),
- connection,
- target,
- target_id);
- ok = FALSE;
- return;
- }
-
- key = _target_watcher_key_create(connection, target_id);
- if (!CFDictionaryContainsKey(targetPrivate->serverWatchers, key)) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("<%p> target %p: watcher not removed, c=0x%0llx, no \"serverWatchers\" key"),
- connection,
- target,
- target_id);
- CFRelease(key);
- ok = FALSE;
- return;
- }
-
- CFDictionaryRemoveValue(targetPrivate->serverWatchers, key);
- CFRelease(key);
-
- n = CFDictionaryGetCount(targetPrivate->serverWatchers);
-
- if (S_debug) {
- SCLog(TRUE, LOG_INFO,
- CFSTR("<%p> target %p: watcher removed, c=0x%0llx, n=%ld"),
- connection,
- target, // server
- target_id, // client
- n);
- }
-
- if (n == 0) {
- CFRelease(targetPrivate->serverWatchers);
- targetPrivate->serverWatchers = NULL;
-
- ok = SCNetworkReachabilitySetDispatchQueue(target, NULL);
- if (!ok) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("<%p> target %p: _watcher_remove SCNetworkReachabilitySetDispatchQueue() failed: %s"),
- connection,
- target,
- SCErrorString(SCError()));
- return;
- }
-
- // no more watchers, flags are no longer valid
- if (_SC_ATOMIC_CMPXCHG(&targetPrivate->serverInfoValid, TRUE, FALSE)) {
- if (S_debug) {
- SCLog(TRUE, LOG_INFO, CFSTR("%s flags are no longer \"valid\""),
- targetPrivate->log_prefix);
- }
- }
- }
-
- xpc_release(connection);
- });
-
- return ok;
-}
-
-
-#pragma mark -
-#pragma mark Reachability [RBT] client support
-
-
-typedef struct {
- rb_node_t rbn;
- xpc_connection_t connection;
- pid_t pid;
- const char *proc_name;
- CFMutableDictionaryRef targets; // target_id --> SCNetworkReachabilityRef
-} reach_client_t;
-
-
-static int
-_rbt_compare_transaction_nodes(void *context, const void *n1, const void *n2)
-{
- uint64_t a = (uintptr_t)((reach_client_t *)n1)->connection;
- uint64_t b = (uintptr_t)((reach_client_t *)n2)->connection;
-
- if (a == b) {
- return 0;
- } else if (a < b) {
- return -1;
- } else {
- return 1;
- }
-}
-
-
-static int
-_rbt_compare_transaction_key(void *context, const void *n1, const void *key)
-{
- uint64_t a = (uintptr_t)((reach_client_t *)n1)->connection;
- uint64_t b = *(uintptr_t *)key;
-
- if (a == b) {
- return 0;
- } else if (a < b) {
- return -1;
- } else {
- return 1;
- }
-}
-
-
-static rb_tree_t *
-_reach_clients_rbt()
-{
- static dispatch_once_t once;
- static const rb_tree_ops_t ops = {
- .rbto_compare_nodes = _rbt_compare_transaction_nodes,
- .rbto_compare_key = _rbt_compare_transaction_key,
- .rbto_node_offset = offsetof(reach_client_t, rbn),
- .rbto_context = NULL
- };
- static rb_tree_t rbt;
-
- dispatch_once(&once, ^{
- rb_tree_init(&rbt, &ops);
- });
-
- return &rbt;
-}
-
-
-static dispatch_queue_t
-_reach_clients_rbt_queue()
-{
- static dispatch_once_t once;
- static dispatch_queue_t q;
-
- dispatch_once(&once, ^{
- q = dispatch_queue_create(REACH_SERVICE_NAME ".clients.rbt", NULL);
- });
-
- return q;
-}
-
-
-static reach_client_t *
-_reach_client_create(xpc_connection_t connection)
-{
- reach_client_t *client;
-
- client = calloc(1, sizeof(*client));
- client->connection = connection;
- client->pid = xpc_connection_get_pid(connection);
- client->proc_name = NULL;
- client->targets = CFDictionaryCreateMutable(NULL,
- 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks);
-
- return client;
-}
-
-
-static void
-_reach_client_release(reach_client_t *client)
-{
- if (client->proc_name != NULL) {
- free((void *)client->proc_name);
- }
- CFRelease(client->targets);
- free(client);
- return;
-}
-
-
-static void
-_reach_client_remove_target(const void *key, const void *value, void *context)
-{
- xpc_connection_t connection = (xpc_connection_t)context;
- SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)value;
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- // check if we have anyone watching this target
- if (targetPrivate->serverWatchers != NULL) {
- CFIndex n;
-
- n = CFDictionaryGetCount(targetPrivate->serverWatchers);
- if (n > 0) {
- CFIndex i;
- CFDictionaryRef serverWatchers;
- const void * watchers_q[32];
- const void ** watchers = watchers_q;
-
- serverWatchers = CFDictionaryCreateCopy(NULL, targetPrivate->serverWatchers);
-
- if (n > sizeof(watchers_q)/sizeof(watchers[0])) {
- watchers = CFAllocatorAllocate(NULL, n * sizeof(CFDataRef), 0);
- }
- CFDictionaryGetKeysAndValues(serverWatchers, watchers, NULL);
-
- for (i = 0; i < n; i++) {
- CFDataRef key;
- reach_watcher_key_t *watcher_key;
-
- key = (CFDataRef)watchers[i];
- /* ALIGN: CF aligns to >8 byte boundries */
- watcher_key = (reach_watcher_key_t *)(void *)CFDataGetBytePtr(key);
- if (watcher_key->connection == connection) {
- // remove watcher references for THIS connection
- _target_watcher_remove(target,
- watcher_key->connection,
- watcher_key->target_id);
- }
- }
-
- if (watchers != watchers_q) {
- CFAllocatorDeallocate(NULL, watchers);
- }
-
- CFRelease(serverWatchers);
- }
- }
-
- // remove our reference to this target
- dispatch_sync(_server_digest_queue(), ^{
- _target_reference_remove(target, connection);
- });
-
- return;
-}
-
-
-static void
-_reach_client_remove(xpc_connection_t connection)
-{
- uint64_t connection_id = (uintptr_t)connection;
-
- dispatch_sync(_reach_clients_rbt_queue(), ^{
- reach_client_t *client;
- rb_tree_t *rbt = _reach_clients_rbt();
-
- client = rb_tree_find_node(rbt, &connection_id);
- if (client != NULL) {
- // remove any remaining target references (for this client)
- my_CFDictionaryApplyFunction(client->targets,
- _reach_client_remove_target,
- (void *)connection);
-
- rb_tree_remove_node(rbt, client);
- _reach_client_release(client);
- } else {
- SCLog(TRUE, LOG_ERR,
- CFSTR("<%p> _reach_client_remove: unexpected client"),
- connection);
- }
- });
-
- return;
-}
-
-
-static __inline__ CFDataRef
-_client_target_key_create(uint64_t target_id)
-{
- CFDataRef target_key;
-
- target_key = CFDataCreate(NULL, (UInt8 *)&target_id, sizeof(target_id));
- return target_key;
-}
-
-
-static SCNetworkReachabilityRef
-_client_target_copy(reach_client_t *client, uint64_t target_id)
-{
- SCNetworkReachabilityRef target;
- CFDataRef target_key;
-
- target_key = _client_target_key_create(target_id);
- target = CFDictionaryGetValue(client->targets, target_key);
- CFRelease(target_key);
-
- if (target != NULL) {
- CFRetain(target);
- }
-
- return target;
-}
-
-
-static Boolean
-_client_target_set(reach_client_t *client, uint64_t target_id, SCNetworkReachabilityRef target)
-{
- Boolean added;
- CFDataRef target_key;
-
- target_key = _client_target_key_create(target_id);
- added = !CFDictionaryContainsKey(client->targets, target_key);
- if (added) {
- CFDictionarySetValue(client->targets, target_key, target);
- }
- CFRelease(target_key);
-
- return added;
-}
-
-
-static void
-_client_target_remove(reach_client_t *client, uint64_t target_id)
-{
- CFDataRef target_key;
-
- target_key = _client_target_key_create(target_id);
- CFDictionaryRemoveValue(client->targets, target_key);
- CFRelease(target_key);
-
- return;
-}
-
-
-#pragma mark -
-#pragma mark Reachability [XPC] server functions
-
-
-static dispatch_queue_t
-_reach_server_queue()
-{
- static dispatch_once_t once;
- static dispatch_queue_t q;
-
- dispatch_once(&once, ^{
- q = dispatch_queue_create(REACH_SERVICE_NAME, NULL);
- });
-
- return q;
-}
-
-
-/*
- * _reach_changed
- *
- * Note: should be exec'd on the target queue
- */
-static void
-_reach_changed(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info)
-{
- CFIndex i;
- CFIndex n;
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
- const void * watcher_keys_q[32];
- const void ** watcher_keys = watcher_keys_q;
- const void * watcher_vals_q[32];
- const void ** watcher_vals = watcher_vals_q;
-
- if (S_debug) {
- SCLog(TRUE, LOG_INFO,
- CFSTR("%sprocess reachability changed, flags = 0x%08x"),
- targetPrivate->log_prefix,
- flags);
- }
-
- if (targetPrivate->serverWatchers == NULL) {
- // if no watchers
- return;
- }
-
- n = CFDictionaryGetCount(targetPrivate->serverWatchers);
- if (n == 0) {
- // if no watchers
- return;
- }
-
- if (!isReachabilityTypeName(targetPrivate->type) || !targetPrivate->dnsNoAddressesSinceLastTimeout) {
- /*
- * Because we are actively watching for additional changes
- * we mark the flags as "valid"
- */
- if (_SC_ATOMIC_CMPXCHG(&targetPrivate->serverInfoValid, FALSE, TRUE)) {
- if (S_debug) {
- SCLog(TRUE, LOG_INFO, CFSTR("%s flags are now \"valid\""),
- targetPrivate->log_prefix);
- }
- }
- }
-
- // notify all of the watchers
- if (n > sizeof(watcher_keys_q)/sizeof(watcher_keys[0])) {
- watcher_keys = CFAllocatorAllocate(NULL, n * sizeof(CFDataRef), 0);
- watcher_vals = CFAllocatorAllocate(NULL, n * sizeof(CFDataRef), 0);
- }
-
- CFDictionaryGetKeysAndValues(targetPrivate->serverWatchers,
- watcher_keys,
- watcher_vals);
-
- for (i = 0; i < n; i++) {
- xpc_connection_t connection;
- CFDataRef key;
- uint64_t target_id;
- CFDataRef val;
- reach_watcher_key_t *watcher_key;
- reach_watcher_val_t *watcher_val;
-
- val = (CFDataRef)watcher_vals[i];
- /* ALIGN: CF aligns to >8 byte boundries */
- watcher_val = (reach_watcher_val_t *)(void *)CFDataGetBytePtr(val);
-
- if (_SC_ATOMIC_INC(&watcher_val->n_changes) > 0) {
- // if we've already sent a notification
- continue;
- }
-
- key = (CFDataRef)watcher_keys[i];
- /* ALIGN: CF aligns to >8 byte boundries */
- watcher_key = (reach_watcher_key_t *)(void *)CFDataGetBytePtr(key);
-
- connection = xpc_retain(watcher_key->connection);
- target_id = watcher_key->target_id;
- dispatch_async(_reach_server_queue(), ^{
- xpc_object_t reply;
-
- // create our [async] notification
- reply = xpc_dictionary_create(NULL, NULL, 0);
-
- // set notification
- xpc_dictionary_set_int64(reply,
- MESSAGE_NOTIFY,
- MESSAGE_REACHABILITY_STATUS);
-
- // set target ID
- xpc_dictionary_set_uint64(reply,
- REACH_CLIENT_TARGET_ID,
- target_id);
-
- log_xpc_object(" reply [async]", reply);
- xpc_connection_send_message(connection, reply);
-
- xpc_release(reply);
- xpc_release(connection);
- });
- }
-
- if (n > sizeof(watcher_keys_q)/sizeof(watcher_keys[0])) {
- CFAllocatorDeallocate(NULL, watcher_keys);
- CFAllocatorDeallocate(NULL, watcher_vals);
- }
-
- return;
-}
-
-
-static void
-sanitize_address(const struct sockaddr *from, struct sockaddr *to)
-{
- switch (from->sa_family) {
- case AF_INET : {
- /* ALIGN: cast okay, alignment not assumed. */
- struct sockaddr_in *from4 = (struct sockaddr_in *)(void *)from;
- struct sockaddr_in *to4 = (struct sockaddr_in *)(void *)to;
-
- bzero(to4, sizeof(*to4));
- to4->sin_len = sizeof(*to4);
- to4->sin_family = AF_INET;
- bcopy(&from4->sin_addr, &to4->sin_addr, sizeof(to4->sin_addr));
- break;
- }
-
- case AF_INET6 : {
- /* ALIGN: cast okay, alignment not assumed. */
- struct sockaddr_in6 *from6 = (struct sockaddr_in6 *)(void *)from;
- struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)(void *)to;
-
- bzero(to6, sizeof(*to6));
- to6->sin6_len = sizeof(*to6);
- to6->sin6_family = AF_INET6;
- bcopy(&from6->sin6_addr, &to6->sin6_addr, sizeof(to6->sin6_addr));
- to6->sin6_scope_id = from6->sin6_scope_id;
- break;
- }
-
- default:
- bcopy(from, to, from->sa_len);
- break;
- }
-
- return;
-}
-
-
-static void
-target_add(reach_client_t *client, xpc_object_t request)
-{
- const char *name;
- const struct sockaddr *localAddress;
- struct sockaddr_storage localAddress0;
- const struct sockaddr *remoteAddress;
- struct sockaddr_storage remoteAddress0;
- const struct sockaddr *ptrAddress;
- struct sockaddr_storage ptrAddress0;
- int64_t if_index;
- const char *if_name = NULL;
- bool onDemandBypass = FALSE;
- bool resolverBypass = FALSE;
- uint64_t target_id;
- uid_t uid = 0;
-
-
- unsigned char bytes[CC_SHA1_DIGEST_LENGTH];
- CC_SHA1_CTX ctx;
- CFDataRef digest = NULL;
- size_t len;
- xpc_connection_t remote;
- xpc_object_t reply;
- uint64_t status = REACH_REQUEST_REPLY_FAILED;
-
- Boolean added;
- __block Boolean ok = TRUE;
- __block SCNetworkReachabilityRef target = NULL;
-
- if (S_debug) {
- SCLog(TRUE, LOG_INFO,
- CFSTR("<%p> create reachability target"),
- client->connection);
-// log_xpc_object(" create", request);
- }
-
- remote = xpc_dictionary_get_remote_connection(request);
- reply = xpc_dictionary_create_reply(request);
- if (reply == NULL) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("<%p> target_add: xpc_dictionary_create_reply: failed"),
- client->connection);
- return;
- }
-
- target_id = xpc_dictionary_get_uint64(request, REACH_CLIENT_TARGET_ID);
- if (target_id == 0) {
- xpc_dictionary_set_string(reply,
- REACH_REQUEST_REPLY_DETAIL,
- "no target ID");
- goto done;
- }
-
- // create a "digest" of the [new] target
-
- CC_SHA1_Init(&ctx);
-
- name = xpc_dictionary_get_string(request, REACH_TARGET_NAME);
- if (name != NULL) {
- CC_SHA1_Update(&ctx, REACH_TARGET_NAME, sizeof(REACH_TARGET_NAME));
- CC_SHA1_Update(&ctx, name, (CC_LONG)strlen(name));
- }
-
- localAddress = xpc_dictionary_get_data(request, REACH_TARGET_LOCAL_ADDR, &len);
- if (localAddress != NULL) {
- if ((len == localAddress->sa_len) && (len <= sizeof(struct sockaddr_storage))) {
- sanitize_address(localAddress, (struct sockaddr *)&localAddress0);
- CC_SHA1_Update(&ctx, REACH_TARGET_LOCAL_ADDR, sizeof(REACH_TARGET_LOCAL_ADDR));
- CC_SHA1_Update(&ctx, &localAddress0, (CC_LONG)len);
- } else {
- xpc_dictionary_set_string(reply,
- REACH_REQUEST_REPLY_DETAIL,
- "local address: size error");
- goto done;
- }
- }
-
- remoteAddress = xpc_dictionary_get_data(request, REACH_TARGET_REMOTE_ADDR, &len);
- if (remoteAddress != NULL) {
- if ((len == remoteAddress->sa_len) && (len <= sizeof(struct sockaddr_storage))) {
- sanitize_address(remoteAddress, (struct sockaddr *)&remoteAddress0);
- CC_SHA1_Update(&ctx, REACH_TARGET_REMOTE_ADDR, sizeof(REACH_TARGET_REMOTE_ADDR));
- CC_SHA1_Update(&ctx, &remoteAddress0, (CC_LONG)len);
- } else {
- xpc_dictionary_set_string(reply,
- REACH_REQUEST_REPLY_DETAIL,
- "remote address: size error");
- goto done;
- }
- }
-
- ptrAddress = xpc_dictionary_get_data(request, REACH_TARGET_PTR_ADDR, &len);
- if (ptrAddress != NULL) {
- if ((len == ptrAddress->sa_len) && (len <= sizeof(struct sockaddr_storage))) {
- sanitize_address(ptrAddress, (struct sockaddr *)&ptrAddress0);
- CC_SHA1_Update(&ctx, REACH_TARGET_PTR_ADDR, sizeof(REACH_TARGET_PTR_ADDR));
- CC_SHA1_Update(&ctx, &ptrAddress0, (CC_LONG)len);
- } else {
- xpc_dictionary_set_string(reply,
- REACH_REQUEST_REPLY_DETAIL,
- "ptr address: size error");
- goto done;
- }
- }
-
- if_index = xpc_dictionary_get_int64(request, REACH_TARGET_IF_INDEX);
- if (if_index != 0) {
- if_name = xpc_dictionary_get_string(request, REACH_TARGET_IF_NAME);
- if (if_name != NULL) {
- CC_SHA1_Update(&ctx, REACH_TARGET_IF_NAME, sizeof(REACH_TARGET_IF_NAME));
- CC_SHA1_Update(&ctx, if_name, (CC_LONG)strlen(if_name));
- }
- }
-
-
- onDemandBypass = xpc_dictionary_get_bool(request, REACH_TARGET_ONDEMAND_BYPASS);
- if (onDemandBypass) {
- CC_SHA1_Update(&ctx, REACH_TARGET_ONDEMAND_BYPASS, sizeof(REACH_TARGET_ONDEMAND_BYPASS));
- CC_SHA1_Update(&ctx, &onDemandBypass, sizeof(onDemandBypass));
- }
-
- resolverBypass = xpc_dictionary_get_bool(request, REACH_TARGET_RESOLVER_BYPASS);
- if (resolverBypass) {
- CC_SHA1_Update(&ctx, REACH_TARGET_RESOLVER_BYPASS, sizeof(REACH_TARGET_RESOLVER_BYPASS));
- CC_SHA1_Update(&ctx, &resolverBypass, sizeof(resolverBypass));
- }
-
- // Grab UID from XPC connection
- uid = xpc_connection_get_euid(remote);
- CC_SHA1_Update(&ctx, &uid, sizeof(uid));
-
-
- CC_SHA1_Final(bytes, &ctx);
- digest = CFDataCreate(NULL, bytes, sizeof(bytes));
-
- /*
- * Check to see if we already have a SCNetworkReachability object
- * for this digest. If so, we'll share the existing target. If not,
- * create a new [shared] target.
- */
- dispatch_sync(_server_digest_queue(), ^{
- target = CFDictionaryGetValue(reach_digest_map, digest);
- if (target != NULL) {
- CFRetain(target);
- } else {
- CFDataRef data;
- CFMutableDictionaryRef options;
- CFStringRef str;
-
- options = CFDictionaryCreateMutable(NULL,
- 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks);
- if (name != NULL) {
- str = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8);
- CFDictionarySetValue(options, kSCNetworkReachabilityOptionNodeName, str);
- CFRelease(str);
- }
- if (localAddress != NULL) {
- data = CFDataCreate(NULL, (const UInt8 *)&localAddress0, localAddress0.ss_len);
- CFDictionarySetValue(options, kSCNetworkReachabilityOptionLocalAddress, data);
- CFRelease(data);
- }
- if (remoteAddress != NULL) {
- data = CFDataCreate(NULL, (const UInt8 *)&remoteAddress0, remoteAddress0.ss_len);
- CFDictionarySetValue(options, kSCNetworkReachabilityOptionRemoteAddress, data);
- CFRelease(data);
- }
- if (ptrAddress != NULL) {
- data = CFDataCreate(NULL, (const UInt8 *)&ptrAddress0, ptrAddress0.ss_len);
- CFDictionarySetValue(options, kSCNetworkReachabilityOptionPTRAddress, data);
- CFRelease(data);
- }
- if (onDemandBypass) {
- CFDictionarySetValue(options,
- kSCNetworkReachabilityOptionConnectionOnDemandBypass,
- kCFBooleanTrue);
- }
- if (resolverBypass) {
- CFDictionarySetValue(options,
- kSCNetworkReachabilityOptionResolverBypass,
- kCFBooleanTrue);
- }
- CFDictionarySetValue(options,
- kSCNetworkReachabilityOptionServerBypass,
- kCFBooleanTrue);
- target = SCNetworkReachabilityCreateWithOptions(NULL, options);
- CFRelease(options);
- if (target == NULL) {
- xpc_dictionary_set_string(reply,
- REACH_REQUEST_REPLY_DETAIL,
- "SCNetworkReachabilityCreateWithOptions failed");
- ok = FALSE;
- return;
- }
-
- // Set the UID on the target
- ((SCNetworkReachabilityPrivateRef)target)->uid = uid;
-
- // because the interface name may not (no longer) be valid we set
- // this after we've created the SCNetworkReachability object
- if ((if_index != 0) && (if_name != NULL)) {
- SCNetworkReachabilityPrivateRef targetPrivate;
-
- targetPrivate = (SCNetworkReachabilityPrivateRef)target;
- targetPrivate->if_index = (unsigned int)if_index;
- strlcpy(targetPrivate->if_name, if_name, sizeof(targetPrivate->if_name));
- }
-
-
- ok = SCNetworkReachabilitySetCallback(target, _reach_changed, NULL);
- if (!ok) {
- xpc_dictionary_set_string(reply,
- REACH_REQUEST_REPLY_DETAIL,
- "SCNetworkReachabilitySetCallback failed");
- CFRelease(target);
- target = NULL;
- return;
- }
- }
-
- // bump the number of references to this target
- _target_reference_add(target, digest, client->connection);
- });
-
- if (!ok) {
- goto done;
- }
-
- /*
- * add an association for the client's target_id to the [shared]
- * SCNetworkReachability object.
- */
- added = _client_target_set(client, target_id, target);
- if (!added) {
- // if we already had a reference to the target (e.g. reconnect)
- dispatch_sync(_server_digest_queue(), ^{
- _target_reference_remove(target, client->connection);
- });
- }
-
- status = REACH_REQUEST_REPLY_OK;
-
- done :
-
- xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, status);
-// log_xpc_object(" reply", reply);
- xpc_connection_send_message(remote, reply);
- xpc_release(reply);
-
- if (digest != NULL) CFRelease(digest);
- if (target != NULL) CFRelease(target);
- return;
-}
-
-
-static void
-target_remove(reach_client_t *client, xpc_object_t request)
-{
- xpc_connection_t remote;
- xpc_object_t reply;
- uint64_t status = REACH_REQUEST_REPLY_FAILED;
- SCNetworkReachabilityRef target = NULL;
- uint64_t target_id;
-
- if (S_debug) {
- SCLog(TRUE, LOG_INFO,
- CFSTR("<%p> remove reachability target"),
- client->connection);
-// log_xpc_object(" remove", request);
- }
-
- remote = xpc_dictionary_get_remote_connection(request);
- reply = xpc_dictionary_create_reply(request);
- if (reply == NULL) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("<%p> target_remove: xpc_dictionary_create_reply: failed"),
- client->connection);
- return;
- }
-
- target_id = xpc_dictionary_get_uint64(request, REACH_CLIENT_TARGET_ID);
- if (target_id == 0) {
- xpc_dictionary_set_string(reply,
- REACH_REQUEST_REPLY_DETAIL,
- "no target ID");
- goto done;
- }
-
- target = _client_target_copy(client, target_id);
- if (target == NULL) {
- xpc_dictionary_set_string(reply,
- REACH_REQUEST_REPLY_DETAIL,
- "no target");
- status = REACH_REQUEST_REPLY_UNKNOWN;
- goto done;
- }
-
- /*
- * remove the association from the client's target_id to the [shared]
- * SCNetworkReachability object.
- */
- _client_target_remove(client, target_id);
-
- // drop the number of references to this target
- dispatch_sync(_server_digest_queue(), ^{
- _target_reference_remove(target, client->connection);
- });
-
- status = REACH_REQUEST_REPLY_OK;
-
- done :
-
- xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, status);
-// log_xpc_object(" reply", reply);
- xpc_connection_send_message(remote, reply);
- xpc_release(reply);
-
- if (target != NULL) CFRelease(target);
- return;
-}
-
-
-static void
-target_schedule(reach_client_t *client, xpc_object_t request)
-{
- Boolean ok;
- xpc_connection_t remote;
- xpc_object_t reply;
- uint64_t status = REACH_REQUEST_REPLY_FAILED;
- SCNetworkReachabilityRef target = NULL;
- uint64_t target_id;
-
- if (S_debug) {
- SCLog(TRUE, LOG_INFO,
- CFSTR("<%p> schedule reachability target"),
- client->connection);
-// log_xpc_object(" schedule", request);
- }
-
- remote = xpc_dictionary_get_remote_connection(request);
- reply = xpc_dictionary_create_reply(request);
- if (reply == NULL) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("<%p> target_schedule: xpc_dictionary_create_reply: failed"),
- client->connection);
- return;
- }
-
- target_id = xpc_dictionary_get_uint64(request, REACH_CLIENT_TARGET_ID);
- if (target_id == 0) {
- xpc_dictionary_set_string(reply,
- REACH_REQUEST_REPLY_DETAIL,
- "no target ID");
- goto done;
- }
-
- target = _client_target_copy(client, target_id);
- if (target == NULL) {
- xpc_dictionary_set_string(reply,
- REACH_REQUEST_REPLY_DETAIL,
- "no target");
- status = REACH_REQUEST_REPLY_UNKNOWN;
- goto done;
- }
-
- // enable monitoring
- ok = _target_watcher_add(target, client->connection, target_id);
- if (!ok) {
- xpc_dictionary_set_string(reply,
- REACH_REQUEST_REPLY_DETAIL,
- "could not add watcher");
- goto done;
- }
-
- status = REACH_REQUEST_REPLY_OK;
-
- done :
-
- xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, status);
-// log_xpc_object(" reply", reply);
- xpc_connection_send_message(remote, reply);
- xpc_release(reply);
-
- if (target != NULL) CFRelease(target);
- return;
-}
-
-
-static void
-target_status(reach_client_t *client, xpc_object_t request)
-{
- xpc_connection_t remote;
- xpc_object_t reply;
- __block Boolean reply_now = TRUE;
- Boolean scheduled;
- uint64_t status = REACH_REQUEST_REPLY_FAILED;
- SCNetworkReachabilityRef target = NULL;
- uint64_t target_id;
-
- if(S_debug) {
- SCLog(TRUE, LOG_INFO,
- CFSTR("<%p> get status of reachability target"),
- client->connection);
-// log_xpc_object(" status", request);
- }
-
- remote = xpc_dictionary_get_remote_connection(request);
- reply = xpc_dictionary_create_reply(request);
- if (reply == NULL) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("<%p> target_status: xpc_dictionary_create_reply: failed"),
- client->connection);
- return;
- }
-
- target_id = xpc_dictionary_get_uint64(request, REACH_CLIENT_TARGET_ID);
- if (target_id == 0) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("<%p> target_status: no target"),
- client->connection);
- xpc_dictionary_set_string(reply,
- REACH_REQUEST_REPLY_DETAIL,
- "no target ID");
- goto done;
- }
-
- target = _client_target_copy(client, target_id);
- if (target == NULL) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("<%p> target_status: no target (0x%0llx)"),
- client->connection,
- target_id);
- xpc_dictionary_set_string(reply,
- REACH_REQUEST_REPLY_DETAIL,
- "no target");
- status = REACH_REQUEST_REPLY_UNKNOWN;
- goto done;
- }
-
- /*
- * Check to see if the target [for this client] had been "scheduled".
- *
- * If so, also mark that we've picked up the current reachability
- * flags and that any pending notifications have been processed.
- */
- scheduled = _target_watcher_checkin(target, client->connection, target_id);
-
- /*
- * return current reachability information to the caller
- */
- dispatch_sync(_target_queue(target), ^{
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- if (scheduled) {
- /*
- * The client "scheduled" this target. As such, we
- * know that this is an async query and that we only
- * need to return the "last known" flags.
- */
- _target_reply_add_reachability(target, reply);
-// log_xpc_object(" reply [scheduled]", reply);
-
- /*
- * ... and if it's not a "name" query then we can mark the
- * flags as valid.
- */
- if (!isReachabilityTypeName(targetPrivate->type)) {
- if (_SC_ATOMIC_CMPXCHG(&targetPrivate->serverInfoValid, FALSE, TRUE)) {
- if (S_debug) {
- SCLog(TRUE, LOG_INFO, CFSTR("%s flags are now \"valid\"."),
- targetPrivate->log_prefix);
- }
- }
- }
-
- if (S_debug) {
- CFStringRef str;
-
- str = _SCNetworkReachabilityCopyTargetFlags(target);
- SCLog(TRUE, LOG_INFO,
- CFSTR("<%p> reply [scheduled%s], %@"),
- client->connection,
- targetPrivate->serverInfoValid ? "/valid" : "",
- str);
- CFRelease(str);
- }
- } else {
- /*
- * The client has NOT "scheduled" this target. As
- * such, we know that this is a sync query and that
- * we must return "current" flags.
- */
- if (targetPrivate->scheduled && targetPrivate->serverInfoValid) {
- /*
- * The server target has been "scheduled" and we
- * have flags that are "current".
- */
- _target_reply_add_reachability(target, reply);
-// log_xpc_object(" reply [scheduled/valid]", reply);
-
- if (S_debug) {
- CFStringRef str;
-
- str = _SCNetworkReachabilityCopyTargetFlags(target);
- SCLog(TRUE, LOG_INFO,
- CFSTR("<%p> reply [scheduled/valid], %@"),
- client->connection,
- str);
- CFRelease(str);
- }
- } else {
- dispatch_group_t group;
-
- /*
- * The server target has NOT been "scheduled" or
- * we do not have "current" flags. This means that
- * we must query for the current information and
- * return the flags to the client when they are
- * available.
- */
-
- reply_now = FALSE;
-
- group = _target_group(target);
- if (_SC_ATOMIC_INC(&targetPrivate->serverSyncQueryActive) == 0) {
- CFRetain(target);
- dispatch_group_async(group, _server_concurrent_queue(), ^{
- SCNetworkReachabilityFlags flags;
- unsigned int n;
- Boolean ok;
-
- // query for the flags
- ok = SCNetworkReachabilityGetFlags(target, &flags);
- flags = targetPrivate->info.flags; // get the "raw" flags
- if (!ok) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("SCNetworkReachabilityGetFlags() [sync query] failed"
- "\n target = %@"
- "\n status = %s"),
- target,
- SCErrorString(SCError()));
- }
-
- /*
- * if we have current flags, if the target has since been
- * scheduled, and this is not a "name" query, then mark as
- * valid.
- */
- if (ok &&
- targetPrivate->scheduled &&
- !isReachabilityTypeName(targetPrivate->type)) {
- if (_SC_ATOMIC_CMPXCHG(&targetPrivate->serverInfoValid, FALSE, TRUE)) {
- if (S_debug) {
- SCLog(TRUE, LOG_INFO, CFSTR("%s flags are now \"valid\"!"),
- targetPrivate->log_prefix);
- }
- }
- }
-
- // sync query complete
- n = _SC_ATOMIC_ZERO(&targetPrivate->serverSyncQueryActive);
- if (S_debug) {
- SCLog(TRUE, LOG_INFO,
- CFSTR("%sSCNetworkReachabilityGetFlags() [sync query] complete, n = %d"),
- targetPrivate->log_prefix,
- n);
- }
-
- CFRelease(target);
- });
- }
-
- CFRetain(target);
- dispatch_group_notify(group, _target_queue(target), ^{
- // flags are now available
- _target_reply_add_reachability(target, reply);
- xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, REACH_REQUEST_REPLY_OK);
-// log_xpc_object(" reply [delayed]", reply);
-
- if (S_debug) {
- CFStringRef str;
-
- str = _SCNetworkReachabilityCopyTargetFlags(target);
- SCLog(TRUE, LOG_INFO,
- CFSTR("<%p> reply [delayed], %@"),
- client->connection,
- str);
- CFRelease(str);
- }
-
- xpc_connection_send_message(remote, reply);
- xpc_release(reply);
-
- CFRelease(target);
- });
- }
- }
- });
-
- status = REACH_REQUEST_REPLY_OK;
-
- done :
-
- if (reply_now) {
- xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, status);
-
- if (status != REACH_REQUEST_REPLY_OK) {
-// log_xpc_object(" reply [!]", reply);
-
- if (S_debug) {
- SCLog(TRUE, LOG_INFO,
- CFSTR("<%p> reply [!]"),
- client->connection);
- }
- }
-
- xpc_connection_send_message(remote, reply);
- xpc_release(reply);
- } else if (S_debug) {
- CFStringRef str;
-
- str = _SCNetworkReachabilityCopyTargetFlags(target);
- SCLog(TRUE, LOG_INFO,
- CFSTR("<%p> no reply [yet], %@"),
- client->connection,
- str);
- CFRelease(str);
- }
-
- if (target != NULL) CFRelease(target);
- return;
-}
-
-
-static void
-target_unschedule(reach_client_t *client, xpc_object_t request)
-{
- xpc_connection_t remote;
- xpc_object_t reply;
- uint64_t status = REACH_REQUEST_REPLY_FAILED;
- SCNetworkReachabilityRef target = NULL;
- uint64_t target_id;
-
- if (S_debug) {
- SCLog(TRUE, LOG_INFO,
- CFSTR("<%p> unschedule reachability target"),
- client->connection);
-// log_xpc_object(" unschedule", request);
- }
-
- remote = xpc_dictionary_get_remote_connection(request);
- reply = xpc_dictionary_create_reply(request);
- if (reply == NULL) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("<%p> target_unschedule: xpc_dictionary_create_reply: failed"),
- client->connection);
- return;
- }
-
- target_id = xpc_dictionary_get_uint64(request, REACH_CLIENT_TARGET_ID);
- if (target_id == 0) {
- xpc_dictionary_set_string(reply,
- REACH_REQUEST_REPLY_DETAIL,
- "no target ID");
- goto done;
- }
-
- target = _client_target_copy(client, target_id);
- if (target == NULL) {
- xpc_dictionary_set_string(reply,
- REACH_REQUEST_REPLY_DETAIL,
- "no target");
- status = REACH_REQUEST_REPLY_UNKNOWN;
- goto done;
- }
-
- // disable monitoring
- _target_watcher_remove(target, client->connection, target_id);
-
- status = REACH_REQUEST_REPLY_OK;
-
- done :
-
- xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, status);
-// log_xpc_object(" reply", reply);
- xpc_connection_send_message(remote, reply);
- xpc_release(reply);
-
- if (target != NULL) CFRelease(target);
- return;
-}
-
-
-#define SNAPSHOT_PATH_STATE _PATH_VARTMP "configd-reachability"
-
-
-static void
-_snapshot_digest_watcher(const void *key, const void *value, void *context)
-{
- __block reach_client_t *client = NULL;
- FILE *f = (FILE *)context;
- static reach_client_t no_client = {
- .pid = 0,
- .proc_name = "?",
- };
- reach_watcher_key_t *watcher_key;
- reach_watcher_val_t *watcher_val;
-
- /* ALIGN: CF aligns to >8 byte boundries */
- watcher_key = (reach_watcher_key_t *)(void *)CFDataGetBytePtr(key);
- watcher_val = (reach_watcher_val_t *)(void *)CFDataGetBytePtr(value);
-
- dispatch_sync(_reach_clients_rbt_queue(), ^{
- uint64_t connection_id = (uintptr_t)watcher_key->connection;
- rb_tree_t *rbt = _reach_clients_rbt();
-
- client = rb_tree_find_node(rbt, &connection_id);
- if (client == NULL) {
- client = &no_client;
- }
- });
-
- SCPrint(TRUE, f,
- CFSTR(" connection = %p, target(c) = 0x%0llx, command = %s, pid = %d, changes = %u\n"),
- watcher_key->connection,
- watcher_key->target_id,
- client->proc_name,
- client->pid,
- watcher_val->n_changes);
-
- return;
-}
-
-
-static void
-_snapshot_digest(const void *key, const void *value, void *context)
-{
- FILE *f = (FILE *)context;
- CFStringRef digest = (CFStringRef)key;
- dispatch_queue_t q;
- SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)value;
- SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
-
- q = _target_queue(target);
- dispatch_sync(q, ^{
- CFIndex nWatchers = 0;
-
- if (targetPrivate->serverWatchers != NULL) {
- nWatchers = CFDictionaryGetCount(targetPrivate->serverWatchers);
- }
-
- SCPrint(TRUE, f, CFSTR("\n digest : %@\n"), digest);
- SCPrint(TRUE, f, CFSTR(" %@\n"), target);
- SCPrint(TRUE, f, CFSTR(" valid = %s, async watchers = %ld, sync queries = %u, refs = %u\n"),
- targetPrivate->serverInfoValid ? "Y" : "N",
- nWatchers,
- targetPrivate->serverSyncQueryActive,
- targetPrivate->serverReferences);
-
- SCPrint(TRUE, f, CFSTR(" network %ld.%3.3d"),
- targetPrivate->last_network.tv_sec,
- targetPrivate->last_network.tv_usec / 1000);
-#if !TARGET_OS_IPHONE
- SCPrint(TRUE, f, CFSTR(", power %ld.%3.3d"),
- targetPrivate->last_power.tv_sec,
- targetPrivate->last_power.tv_usec / 1000);
-#endif // !TARGET_OS_IPHONE
- if (isReachabilityTypeName(targetPrivate->type)) {
- SCPrint(TRUE, f, CFSTR(", DNS %ld.%3.3d"),
- targetPrivate->last_dns.tv_sec,
- targetPrivate->last_dns.tv_usec / 1000);
- if (timerisset(&targetPrivate->dnsQueryEnd)) {
- struct timeval dnsQueryElapsed;
-
- timersub(&targetPrivate->dnsQueryEnd,
- &targetPrivate->dnsQueryStart,
- &dnsQueryElapsed);
- SCPrint(TRUE, f, CFSTR(" (query %ld.%3.3d / reply %ld.%3.3d)"),
- targetPrivate->dnsQueryStart.tv_sec,
- targetPrivate->dnsQueryStart.tv_usec / 1000,
- dnsQueryElapsed.tv_sec,
- dnsQueryElapsed.tv_usec / 1000);
- }
- }
- if (timerisset(&targetPrivate->last_push)) {
- SCPrint(TRUE, f, CFSTR(", last notify %ld.%3.3d"),
- targetPrivate->last_push.tv_sec,
- targetPrivate->last_push.tv_usec / 1000);
- }
- SCPrint(TRUE, f, CFSTR(", uid %d"),
- targetPrivate->uid);
- SCPrint(TRUE, f, CFSTR("\n"));
-
- if (nWatchers > 0) {
- CFDictionaryApplyFunction(targetPrivate->serverWatchers,
- _snapshot_digest_watcher,
- f);
- }
- });
-
- return;
-}
-
-
-static void
-_snapshot_target(const void *key, const void *value, void *context)
-{
- FILE *f = (FILE *)context;
- SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)value;
- uint64_t target_id;
- CFDataRef target_key = (CFDataRef)key;
-
- /* ALIGN: CF aligns > 8 byte boundries */
- target_id = *(uint64_t *)(void *)CFDataGetBytePtr(target_key);
-
- SCPrint(TRUE, f,
- CFSTR(" target(c) = 0x%0llx, target(s) = %@\n"),
- target_id,
- target);
-
- return;
-}
-
-
-static void
-_snapshot(reach_client_t *client, xpc_object_t request)
-{
- uid_t euid;
- FILE *f;
- int fd;
- Boolean ok = FALSE;
- xpc_connection_t remote;
- xpc_object_t reply;
-
- if (S_debug) {
- SCLog(TRUE, LOG_INFO,
- CFSTR("<%p> snapshot"),
- client->connection);
-// log_xpc_object(" create", request);
- }
-
- remote = xpc_dictionary_get_remote_connection(request);
- reply = xpc_dictionary_create_reply(request);
- if (reply == NULL) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("<%p> _snapshot: xpc_dictionary_create_reply: failed"),
- client->connection);
- return;
- }
-
- euid = xpc_connection_get_euid(remote);
- if (euid != 0) {
- xpc_dictionary_set_string(reply,
- REACH_REQUEST_REPLY_DETAIL,
- "Permission denied.");
- goto done;
- }
-
- // Save a snapshot of the SCNetworkReachability server "state"
-
- (void) unlink(SNAPSHOT_PATH_STATE);
- fd = open(SNAPSHOT_PATH_STATE, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
- if (fd == -1) {
- xpc_dictionary_set_string(reply,
- REACH_REQUEST_REPLY_DETAIL,
- "open: failed");
- goto done;
- }
- f = fdopen(fd, "w");
- if (f == NULL) {
- xpc_dictionary_set_string(reply,
- REACH_REQUEST_REPLY_DETAIL,
- "fdopen: failed");
- goto done;
- }
-
- // provide connection/client info
-
- dispatch_sync(_reach_clients_rbt_queue(), ^{
- rb_tree_t *rbt = _reach_clients_rbt();
-
- SCPrint(TRUE, f, CFSTR("Clients :\n"));
-
- if (rb_tree_count(rbt) > 0) {
- reach_client_t *client;
-
- RB_TREE_FOREACH(client, rbt) {
- SCPrint(TRUE, f,
- CFSTR("\n connection = %p, client = %p, command = %s, pid = %d\n"),
- client->connection,
- client,
- client->proc_name != NULL ? client->proc_name : "?",
- client->pid);
- my_CFDictionaryApplyFunction(client->targets,
- _snapshot_target,
- f);
- }
- } else {
- SCPrint(TRUE, f, CFSTR(" None.\n"));
- }
-
- SCPrint(TRUE, f, CFSTR("\n"));
- });
-
- // provide "digest" info
-
- SCPrint(TRUE, f, CFSTR("Digests :\n"));
- dispatch_sync(_server_digest_queue(), ^{
- if (reach_digest_map != NULL) {
- CFDictionaryApplyFunction(reach_digest_map,
- _snapshot_digest,
- f);
- }
- });
-
- (void) fclose(f);
-
- ok = TRUE;
-
- done :
-
- xpc_dictionary_set_int64(reply,
- REACH_REQUEST_REPLY,
- ok ? REACH_REQUEST_REPLY_OK : REACH_REQUEST_REPLY_FAILED);
-// log_xpc_object(" reply", reply);
- xpc_connection_send_message(remote, reply);
- xpc_release(reply);
-
- return;
-}
-
-
-static __inline__ void
-_extract_client_info(reach_client_t *client, xpc_object_t request)
-{
- // if available/needed, save the process name
- if (client->proc_name == NULL) {
- const char *proc_name;
-
- proc_name = xpc_dictionary_get_string(request, REACH_CLIENT_PROC_NAME);
- if (proc_name != NULL) {
- client->proc_name = strdup(proc_name);
- }
- }
-
- return;
-}
-
-
-static void
-process_request(reach_client_t *client, xpc_object_t request)
-{
- int64_t op;
-
- op = xpc_dictionary_get_int64(request, REACH_REQUEST);
- switch (op) {
- case REACH_REQUEST_CREATE :
- _extract_client_info(client, request);
- target_add(client, request);
- break;
- case REACH_REQUEST_REMOVE :
- target_remove(client, request);
- break;
- case REACH_REQUEST_STATUS :
- target_status(client, request);
- break;
- case REACH_REQUEST_SCHEDULE :
- target_schedule(client, request);
- break;
- case REACH_REQUEST_UNSCHEDULE :
- target_unschedule(client, request);
- break;
- case REACH_REQUEST_SNAPSHOT :
- _extract_client_info(client, request);
- _snapshot(client, request);
- break;
- default :
- SCLog(TRUE, LOG_ERR,
- CFSTR("<%p> unknown request : %lld"),
- client->connection,
- op);
- break;
- }
-
- return;
-}
-
-
-static void
-process_new_connection(xpc_connection_t connection)
-{
- reach_client_t *client;
-
- if (S_debug) {
- SCLog(TRUE, LOG_INFO, CFSTR("<%p> new reach client, pid=%d"),
- connection,
- xpc_connection_get_pid(connection));
- }
-
- client = _reach_client_create(connection);
- assert(client != NULL);
-
- dispatch_sync(_reach_clients_rbt_queue(), ^{
- rb_tree_t *rbt = _reach_clients_rbt();
-
- rb_tree_insert_node(rbt, client);
- });
-
- xpc_connection_set_target_queue(connection, _reach_server_queue());
-
- xpc_connection_set_event_handler(connection, ^(xpc_object_t xobj) {
- xpc_type_t type;
-
- type = xpc_get_type(xobj);
- if (type == XPC_TYPE_DICTIONARY) {
- __block reach_client_t *client = NULL;
-
- dispatch_sync(_reach_clients_rbt_queue(), ^{
- uint64_t connection_id = (uintptr_t)connection;
- rb_tree_t *rbt = _reach_clients_rbt();
-
- client = rb_tree_find_node(rbt, &connection_id);
- });
-
- if (client != NULL) {
- // process the request
- process_request(client, xobj);
- } else {
- char *desc;
-
- SCLog(TRUE, LOG_ERR,
- CFSTR("<%p:%d> unexpected SCNetworkReachability request"),
- connection,
- xpc_connection_get_pid(connection));
-
- desc = xpc_copy_description(xobj);
- SCLog(TRUE, LOG_ERR,
- CFSTR(" request = %s"),
- desc);
- free(desc);
-
- xpc_connection_cancel(connection);
- }
-
- } else if (type == XPC_TYPE_ERROR) {
- const char *desc;
-
- desc = xpc_dictionary_get_string(xobj, XPC_ERROR_KEY_DESCRIPTION);
- if (xobj == XPC_ERROR_CONNECTION_INVALID) {
- if (S_debug) {
- SCLog(TRUE, LOG_INFO,
- CFSTR("<%p:%d> %s"),
- connection,
- xpc_connection_get_pid(connection),
- desc);
- }
-
- _reach_client_remove(connection);
-
- } else if (xobj == XPC_ERROR_CONNECTION_INTERRUPTED) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("<%p:%d> %s"),
- connection,
- xpc_connection_get_pid(connection),
- desc);
-
- } else {
- SCLog(TRUE, LOG_ERR,
- CFSTR("<%p:%d> Connection error: %p : %s"),
- connection,
- xpc_connection_get_pid(connection),
- xobj,
- desc);
- }
-
- } else {
- SCLog(TRUE, LOG_ERR,
- CFSTR("<%p:%d> unknown event type : %p"),
- connection,
- xpc_connection_get_pid(connection),
- type);
- }
- });
-
- xpc_connection_resume(connection);
-
- return;
-}
-
-
-#pragma mark -
-#pragma mark Reachability server "main"
-
-
-__private_extern__
-void
-load_SCNetworkReachability(CFBundleRef bundle, Boolean bundleVerbose)
-{
- xpc_connection_t connection;
- const char *name;
-
- S_debug = bundleVerbose;
-
- /*
- * create a dictionary mapping SCNetworkReachability [CFData] digests
- * to SCNetworkReachability objects.
- */
- reach_digest_map = CFDictionaryCreateMutable(NULL,
- 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks);
-
- // create XPC listener
- name = getenv("REACH_SERVER");
- if (name == NULL) {
- name = REACH_SERVICE_NAME;
- }
- connection = xpc_connection_create_mach_service(name,
- _reach_server_queue(),
- XPC_CONNECTION_MACH_SERVICE_LISTENER);
-
- xpc_connection_set_event_handler(connection, ^(xpc_object_t event) {
- xpc_type_t type;
-
- type = xpc_get_type(event);
- if (type == XPC_TYPE_CONNECTION) {
- process_new_connection(event);
-
- } else if (type == XPC_TYPE_ERROR) {
- const char *desc;
-
- desc = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION);
- if (event == XPC_ERROR_CONNECTION_INVALID) {
- SCLog(TRUE, LOG_ERR, CFSTR("reach server: %s"), desc);
- xpc_release(connection);
- } else if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
- SCLog(TRUE, LOG_ERR, CFSTR("reach server: %s"), desc);
- } else {
- SCLog(TRUE, LOG_ERR,
- CFSTR("reach server: Connection error: %p : %s"),
- event,
- desc);
- }
-
- } else {
- SCLog(TRUE, LOG_ERR,
- CFSTR("reach server: unknown event type : %p"),
- type);
- }
- });
- xpc_connection_resume(connection);
-
- return;
-}
-
-#ifdef MAIN
-
-int
-main(int argc, char **argv)
-{
-// _sc_log = FALSE;
- _sc_verbose = (argc > 1) ? TRUE : FALSE;
- _sc_debug = TRUE;
-
- load_SCNetworkReachability(CFBundleGetMainBundle(), (argc > 1) ? TRUE : FALSE);
- CFRunLoopRun();
- /* not reached */
- exit(0);
- return 0;
-}
-
-#endif /* MAIN */