]> git.saurik.com Git - apple/configd.git/blobdiff - SystemConfiguration.fproj/reachability/SCNetworkReachabilityServer_server.c
configd-453.16.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / reachability / SCNetworkReachabilityServer_server.c
diff --git a/SystemConfiguration.fproj/reachability/SCNetworkReachabilityServer_server.c b/SystemConfiguration.fproj/reachability/SCNetworkReachabilityServer_server.c
new file mode 100644 (file)
index 0000000..27753af
--- /dev/null
@@ -0,0 +1,2033 @@
+/*
+ * Copyright (c) 2011, 2012 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"
+
+#ifdef HAVE_REACHABILITY_SERVER
+
+#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 "rb.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);
+               dispatch_queue_set_width(q, 32);
+       });
+
+       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 (targetPrivate->type == reachabilityTypeName) {
+               if (isA_CFArray(targetPrivate->resolvedAddress)) {
+                       xpc_object_t    addresses;
+                       CFIndex         i;
+                       CFIndex         n;
+
+                       addresses = xpc_array_create(NULL, 0);
+
+                       n = CFArrayGetCount(targetPrivate->resolvedAddress);
+                       for (i = 0; i < n; i++) {
+                               CFDataRef       address;
+
+                               address = CFArrayGetValueAtIndex(targetPrivate->resolvedAddress, i);
+                               xpc_array_set_data(addresses,
+                                                  XPC_ARRAY_APPEND,
+                                                  CFDataGetBytePtr(address),
+                                                  CFDataGetLength(address));
+                       }
+
+                       xpc_dictionary_set_value(reply,
+                                                REACH_STATUS_RESOLVED_ADDRESS,
+                                                addresses);
+                       xpc_release(addresses);
+               }
+               xpc_dictionary_set_int64(reply,
+                                        REACH_STATUS_RESOLVED_ADDRESS_ERROR,
+                                        targetPrivate->resolvedAddressError);
+       }
+
+       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=%d"),
+                                     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);
+                       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);
+                       return;
+               }
+
+               CFDictionaryRemoveValue(targetPrivate->serverWatchers, key);
+               xpc_release(connection);
+               CFRelease(key);
+
+               n = CFDictionaryGetCount(targetPrivate->serverWatchers);
+
+               if (S_debug) {
+                       SCLog(TRUE, LOG_INFO,
+                             CFSTR("<%p>   target %p: watcher removed, c=0x%0llx, n=%d"),
+                             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
+                       (void) _SC_ATOMIC_CMPXCHG(&targetPrivate->serverInfoValid, TRUE, FALSE);
+               }
+       });
+
+       return ok;
+}
+
+
+#pragma mark -
+#pragma mark Reachability [RBT] client support
+
+
+typedef struct {
+       struct rb_node          rbn;
+       xpc_connection_t        connection;
+       pid_t                   pid;
+       const char              *proc_name;
+       CFMutableDictionaryRef  targets;        // target_id --> SCNetworkReachabilityRef
+} reach_client_t;
+
+
+#define RBNODE_TO_REACH_CLIENT(node) \
+       ((reach_client_t *)((uintptr_t)node - offsetof(reach_client_t, rbn)))
+
+
+static int
+_rbt_compare_transaction_nodes(const struct rb_node *n1, const struct rb_node *n2)
+{
+       uint64_t        a = (uintptr_t)RBNODE_TO_REACH_CLIENT(n1)->connection;
+       uint64_t        b = (uintptr_t)RBNODE_TO_REACH_CLIENT(n2)->connection;
+
+       return (a - b);
+}
+
+
+static int
+_rbt_compare_transaction_key(const struct rb_node *n1, const void *key)
+{
+       uint64_t        a = (uintptr_t)RBNODE_TO_REACH_CLIENT(n1)->connection;
+       uint64_t        b = *(uintptr_t *)key;
+
+       return (a - b);
+}
+
+
+static struct rb_tree *
+_reach_clients_rbt()
+{
+       static dispatch_once_t          once;
+       static const struct rb_tree_ops ops = {
+               .rbto_compare_nodes     = _rbt_compare_transaction_nodes,
+               .rbto_compare_key       = _rbt_compare_transaction_key,
+       };
+       static struct rb_tree           rbtree;
+
+       dispatch_once(&once, ^{
+               rb_tree_init(&rbtree, &ops);
+       });
+
+       return &rbtree;
+}
+
+
+static dispatch_queue_t
+_reach_connection_queue()
+{
+       static dispatch_once_t  once;
+       static dispatch_queue_t q;
+
+       dispatch_once(&once, ^{
+               q = dispatch_queue_create(REACH_SERVICE_NAME ".connection", 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;
+                       const void *    watchers_q[32];
+                       const void **   watchers        = watchers_q;
+
+                       if (n > sizeof(watchers_q)/sizeof(watchers[0])) {
+                               watchers = CFAllocatorAllocate(NULL, n * sizeof(CFDataRef), 0);
+                       }
+                       CFDictionaryGetKeysAndValues(targetPrivate->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);
+                       }
+               }
+       }
+
+       // 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)
+{
+       struct rb_tree  *rbtree = _reach_clients_rbt();
+       struct rb_node  *rbn;
+
+       rbn = rb_tree_find_node(rbtree, &connection);
+       if (rbn != NULL) {
+               reach_client_t  *client;
+
+               client = RBNODE_TO_REACH_CLIENT(rbn);
+
+               // remove any remaining target references (for this client)
+               my_CFDictionaryApplyFunction(client->targets,
+                                            _reach_client_remove_target,
+                                            (void *)connection);
+
+               rb_tree_remove_node(rbtree, rbn);
+               _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
+
+/*
+ * _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;
+       }
+
+       /*
+        * 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("  flags are now \"valid\""));
+               }
+       }
+
+       // 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_connection_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 char                              *serv;
+       const struct sockaddr                   *localAddress;
+       struct sockaddr_storage                 localAddress0;
+       const struct sockaddr                   *remoteAddress;
+       struct sockaddr_storage                 remoteAddress0;
+       const struct addrinfo                   *hints;
+       int64_t                                 if_index;
+       const char                              *if_name        = NULL;
+       bool                                    onDemandBypass  = FALSE;
+       uint64_t                                target_id;
+
+
+       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;
+       bool                                    resolverBypass  = FALSE;
+       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, name, strlen(name));
+       }
+
+       serv = xpc_dictionary_get_string(request, REACH_TARGET_SERV);
+       if (serv != NULL) {
+               CC_SHA1_Update(&ctx, serv, strlen(serv));
+       }
+
+       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, &localAddress0, 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, &remoteAddress0, len);
+               } else {
+                       xpc_dictionary_set_string(reply,
+                                                 REACH_REQUEST_REPLY_DETAIL,
+                                                 "remote address: size error");
+                       goto done;
+               }
+       }
+
+       hints = xpc_dictionary_get_data(request, REACH_TARGET_HINTS, &len);
+       if (hints != NULL) {
+               if (len == sizeof(struct addrinfo)) {
+                       CC_SHA1_Update(&ctx, hints, len);
+               } else {
+                       xpc_dictionary_set_string(reply,
+                                                 REACH_REQUEST_REPLY_DETAIL,
+                                                 "hints: 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, if_name, strlen(if_name));
+               }
+       }
+
+       onDemandBypass = xpc_dictionary_get_bool(request, REACH_TARGET_ONDEMAND_BYPASS);
+       if (onDemandBypass) {
+               CC_SHA1_Update(&ctx, &onDemandBypass, sizeof(onDemandBypass));
+       }
+
+       resolverBypass = xpc_dictionary_get_bool(request, REACH_TARGET_RESOLVER_BYPASS);
+       if (resolverBypass) {
+               CC_SHA1_Update(&ctx, &resolverBypass, sizeof(resolverBypass));
+       }
+
+
+       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 (serv != NULL) {
+                               str = CFStringCreateWithCString(NULL, serv, kCFStringEncodingUTF8);
+                               CFDictionarySetValue(options, kSCNetworkReachabilityOptionServName, 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 (hints != NULL) {
+                               data = CFDataCreate(NULL, (const UInt8 *)hints, sizeof(struct addrinfo));
+                               CFDictionarySetValue(options, kSCNetworkReachabilityOptionHints, 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;
+                       }
+
+                       // because the interface name may not (no longer) be valid we set
+                       // this after we've created the SCNetworkReachabilty object
+                       if ((if_index != 0) && (if_name != NULL)) {
+                               SCNetworkReachabilityPrivateRef targetPrivate;
+
+                               targetPrivate = (SCNetworkReachabilityPrivateRef)target;
+                               targetPrivate->if_index = 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) {
+               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 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);
+
+                       if (S_debug) {
+                               CFStringRef     str;
+
+                               str = _SCNetworkReachabilityCopyTargetFlags(target);
+                               SCLog(TRUE, LOG_INFO,
+                                     CFSTR("<%p>   reply [scheduled], %@"),
+                                     client->connection,
+                                     str);
+                               CFRelease(str);
+                       }
+               } else {
+                       /*
+                        * The client has NOT "scheduled" this target.  As
+                        * such, we know that this is a sync query and that
+                        * 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->serverQueryActive) == 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()));
+                                               }
+
+                                               // flags are now available
+                                               n = _SC_ATOMIC_ZERO(&targetPrivate->serverQueryActive);
+                                               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)
+{
+       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> 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
+       ok = _target_watcher_remove(target, client->connection, target_id);
+       if (ok) {
+               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)
+{
+       FILE                            *f              = (FILE *)context;
+       static reach_client_t           no_client       = {
+               .pid = 0,
+               .proc_name = "?",
+       };
+       struct rb_node                  *rbn;
+       reach_client_t                  *rbt_client;
+       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);
+
+       rbn = rb_tree_find_node(_reach_clients_rbt(), &watcher_key->connection);
+       if (rbn == NULL) {
+               rbn = &no_client.rbn;
+       }
+
+       rbt_client = RBNODE_TO_REACH_CLIENT(rbn);
+
+       SCPrint(TRUE, f,
+               CFSTR("      connection = %p, target(c) = 0x%0llx, command = %s, pid = %d, changes = %u\n"),
+               watcher_key->connection,
+               watcher_key->target_id,
+               rbt_client->proc_name,
+               rbt_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, ^{
+               SCPrint(TRUE, f, CFSTR("\n  digest : %@\n"), digest);
+               SCPrint(TRUE, f, CFSTR("    %@\n"), target);
+               SCPrint(TRUE, f, CFSTR("    valid = %s, active = %u, refs = %u\n"),
+                       targetPrivate->serverInfoValid ? "Y" : "N",
+                       targetPrivate->serverQueryActive,
+                       targetPrivate->serverReferences);
+
+               SCPrint(TRUE, f, CFSTR("    network %d.%3.3d"),
+                       targetPrivate->last_network.tv_sec,
+                       targetPrivate->last_network.tv_usec / 1000);
+#if    !TARGET_OS_IPHONE
+               SCPrint(TRUE, f, CFSTR(", power %d.%3.3d"),
+                       targetPrivate->last_power.tv_sec,
+                       targetPrivate->last_power.tv_usec / 1000);
+#endif // !TARGET_OS_IPHONE
+               if (targetPrivate->type == reachabilityTypeName) {
+                       SCPrint(TRUE, f, CFSTR(", DNS %d.%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 %d.%3.3d / reply %d.%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 %d.%3.3d"),
+                               targetPrivate->last_push.tv_sec,
+                               targetPrivate->last_push.tv_usec / 1000);
+               }
+               SCPrint(TRUE, f, CFSTR("\n"));
+
+               if (targetPrivate->serverWatchers != NULL) {
+                       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;
+       struct rb_node          *rbn;
+       struct rb_tree          *rbt;
+       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
+
+       SCPrint(TRUE, f, CFSTR("Clients :\n"));
+       rbt = _reach_clients_rbt();
+       rbn = rb_tree_iterate(rbt, NULL, RB_DIR_RIGHT);
+       if (rbn != NULL) {
+               while (rbn != NULL) {
+                       reach_client_t  *rbt_client;
+
+                       rbt_client = RBNODE_TO_REACH_CLIENT(rbn);
+                       SCPrint(TRUE, f,
+                               CFSTR("\n  connection = %p, client = %p, command = %s, pid = %d\n"),
+                               rbt_client->connection,
+                               rbt_client,
+                               rbt_client->proc_name != NULL ? rbt_client->proc_name : "?",
+                               rbt_client->pid);
+                       my_CFDictionaryApplyFunction(rbt_client->targets,
+                                                    _snapshot_target,
+                                                    f);
+
+                       rbn = rb_tree_iterate(rbt, rbn, RB_DIR_LEFT);
+               }
+       } 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 : %d"),
+                             client->connection,
+                             op);
+                       break;
+       }
+
+       return;
+}
+
+
+static void
+process_new_connection(xpc_connection_t connection)
+{
+       if (S_debug) {
+               SCLog(TRUE, LOG_INFO, CFSTR("<%p> new reach client, pid=%d"),
+                     connection,
+                     xpc_connection_get_pid(connection));
+       }
+
+       dispatch_sync(_reach_connection_queue(), ^{
+               reach_client_t  *client;
+
+               client = _reach_client_create(connection);
+               if (client == NULL || !rb_tree_insert_node(_reach_clients_rbt(), &client->rbn)) {
+                       __builtin_trap();
+               }
+       });
+
+       xpc_connection_set_event_handler(connection, ^(xpc_object_t xobj) {
+               xpc_type_t      type;
+
+               type = xpc_get_type(xobj);
+               if (type == XPC_TYPE_DICTIONARY) {
+                       dispatch_sync(_reach_connection_queue(), ^{
+                               struct rb_node  *rbn;
+
+                               rbn = rb_tree_find_node(_reach_clients_rbt(), &connection);
+                               if (rbn != NULL) {
+                                       reach_client_t  *client;
+
+                                       // process the request
+                                       client = RBNODE_TO_REACH_CLIENT(rbn);
+                                       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);
+                               }
+
+                               xpc_retain(connection);
+                               dispatch_async(_reach_connection_queue(), ^{
+                                       _reach_client_remove(connection);
+                                       xpc_release(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: %d : %s"),
+                                     connection,
+                                     xpc_connection_get_pid(connection),
+                                     xobj,
+                                     desc);
+                       }
+
+               }  else {
+                       SCLog(TRUE, LOG_ERR,
+                             CFSTR("<%p:%d> unknown event type : %x"),
+                             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;
+       dispatch_queue_t        reach_server_q;
+
+       S_debug = bundleVerbose;
+
+       /*
+        * create a dictionary mapping SCNetworkReachability [CFData] digests
+        * to SCNetworkReachability objects.
+        */
+       reach_digest_map = CFDictionaryCreateMutable(NULL,
+                                                    0,
+                                                    &kCFTypeDictionaryKeyCallBacks,
+                                                    &kCFTypeDictionaryValueCallBacks);
+
+       /*
+        * create dispatch queue for processing SCNetworkReachability
+        * service requests
+        */
+       reach_server_q = dispatch_queue_create(REACH_SERVICE_NAME, NULL);
+
+       // create XPC listener
+       name = getenv("REACH_SERVER");
+       if (name == NULL) {
+               name = REACH_SERVICE_NAME;
+       }
+       connection = xpc_connection_create_mach_service(name,
+                                                       reach_server_q,
+                                                       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: %d : %s"),
+                                     event,
+                                     desc);
+                       }
+
+               } else {
+                       SCLog(TRUE, LOG_ERR,
+                             CFSTR("reach server: unknown event type : %x"),
+                             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 */
+
+#endif // HAVE_REACHABILITY_SERVER