]> git.saurik.com Git - apple/configd.git/blobdiff - scutil.tproj/tests.c
configd-802.20.7.tar.gz
[apple/configd.git] / scutil.tproj / tests.c
index 333d2079c151433031cec60904afafc296e7705a..a243962365ad1e3fd33e67d4c13706f7a0267f60 100644 (file)
@@ -1,15 +1,15 @@
 /*
- * Copyright (c) 2000, 2001, 2003-2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000, 2001, 2003-2015 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,
@@ -17,7 +17,7 @@
  * 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 "scutil.h"
+#include "prefs.h"
 #include "tests.h"
 
+#include <netdb.h>
+#include <netdb_async.h>
+#include <notify.h>
 #include <sys/time.h>
 #include <net/if.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
+#define my_log(__level, fmt, ...)      SCPrint(TRUE, stdout, CFSTR(fmt "\n"), ## __VA_ARGS__)
+
 #include <dnsinfo.h>
+#include "dnsinfo_internal.h"
+#include <network_information.h>
+#include "network_information_priv.h"
+#include "SCNetworkReachabilityInternal.h"
+#include <CommonCrypto/CommonDigest.h>
 
-__private_extern__
-void
-do_checkReachability(int argc, char **argv)
+
+static Boolean resolver_bypass;
+
+
+static CF_RETURNS_RETAINED CFMutableDictionaryRef
+_setupReachabilityOptions(int argc, char **argv, const char *interface)
+{
+       int                     i;
+       CFMutableDictionaryRef  options;
+
+       options = CFDictionaryCreateMutable(NULL,
+                                           0,
+                                           &kCFTypeDictionaryKeyCallBacks,
+                                           &kCFTypeDictionaryValueCallBacks);
+
+       for (i = 0; i < argc; i++) {
+               if (strcasecmp(argv[i], "interface") == 0) {
+                       if (++i >= argc) {
+                               SCPrint(TRUE, stderr, CFSTR("No interface\n"));
+                               CFRelease(options);
+                               exit(1);
+                       }
+
+                       interface = argv[i];
+                       continue;
+               }
+
+
+               if (strcasecmp(argv[i], "server") == 0) {
+                       CFDictionarySetValue(options,
+                                            kSCNetworkReachabilityOptionServerBypass,
+                                            kCFBooleanFalse);
+                       continue;
+               } else if (strcasecmp(argv[i], "no-server") == 0) {
+                       CFDictionarySetValue(options,
+                                            kSCNetworkReachabilityOptionServerBypass,
+                                            kCFBooleanTrue);
+                       continue;
+               }
+
+               if (strcasecmp(argv[i], "no-connection-on-demand") == 0) {
+                       CFDictionarySetValue(options,
+                                            kSCNetworkReachabilityOptionConnectionOnDemandBypass,
+                                            kCFBooleanTrue);
+                       continue;
+               }
+
+               if (strcasecmp(argv[i], "no-resolve") == 0) {
+                       CFDictionarySetValue(options,
+                                            kSCNetworkReachabilityOptionResolverBypass,
+                                            kCFBooleanTrue);
+                       resolver_bypass = TRUE;
+                       continue;
+               }
+
+               if (strcasecmp(argv[i], "ptr") == 0) {
+                       CFDictionarySetValue(options,
+                                            kSCNetworkReachabilityOptionPTRAddress,
+                                            kCFBooleanTrue);
+                       continue;
+               }
+
+               if (strlen(argv[i]) == 0) {
+                       continue;
+               }
+
+               SCPrint(TRUE, stderr, CFSTR("Unrecognized option: %s\n"), argv[i]);
+               CFRelease(options);
+               exit(1);
+       }
+
+       if (interface != NULL) {
+               CFStringRef     str;
+
+               if (if_nametoindex(interface) == 0) {
+                       SCPrint(TRUE, stderr, CFSTR("No interface: %s\n"), interface);
+                       exit(1);
+               }
+
+               str  = CFStringCreateWithCString(NULL, interface, kCFStringEncodingASCII);
+               CFDictionarySetValue(options, kSCNetworkReachabilityOptionInterface, str);
+               CFRelease(str);
+       }
+
+       if (CFDictionaryGetCount(options) == 0) {
+               CFRelease(options);
+               options = NULL;
+       }
+
+       return options;
+}
+
+
+static SCNetworkReachabilityRef
+_setupReachability(int argc, char **argv, SCNetworkReachabilityContext *context)
 {
-       SCNetworkConnectionFlags        flags   = 0;
-       SCNetworkReachabilityRef        target  = NULL;
+       const char                      *ip_address     = argv[0];
+       char                            *ip_addressN    = NULL;
+       const char                      *interface;
+       CFMutableDictionaryRef          options         = NULL;
+       const char                      *remote_address = NULL;
+       char                            *remote_addressN= NULL;
+       const char                      *remote_interface;
+       struct sockaddr_in              sin;
+       struct sockaddr_in6             sin6;
+       SCNetworkReachabilityRef        target          = NULL;
 
-       if (argc == 1) {
-               struct sockaddr_in      sin;
-               struct sockaddr_in6     sin6;
+       bzero(&sin, sizeof(sin));
+       sin.sin_len    = sizeof(sin);
+       sin.sin_family = AF_INET;
 
-               bzero(&sin, sizeof(sin));
-               sin.sin_len         = sizeof(sin);
-               sin.sin_family      = AF_INET;
+       bzero(&sin6, sizeof(sin6));
+       sin6.sin6_len    = sizeof(sin6);
+       sin6.sin6_family = AF_INET6;
+
+       interface = strchr(ip_address, '%');
+       if (interface != NULL) {
+               ip_addressN = strdup(ip_address);
+               ip_addressN[interface - ip_address] = '\0';
+               ip_address = ip_addressN;
+               interface++;
+       }
+
+       if ((argc > 1) && (strlen(argv[1]) > 0)) {
+               remote_address = argv[1];
+
+               remote_interface = strchr(remote_address, '%');
+               if (remote_interface != NULL) {
+                       remote_addressN = strdup(remote_address);
+                       remote_addressN[remote_interface - remote_address] = '\0';
+                       remote_address = remote_addressN;
+                       remote_interface++;
+               }
+       }
 
-               bzero(&sin6, sizeof(sin6));
-               sin6.sin6_len = sizeof(sin6);
-               sin6.sin6_family = AF_INET6;
+       if (inet_aton(ip_address, &sin.sin_addr) == 1) {
+               struct sockaddr_in      r_sin;
 
-               if (inet_aton(argv[0], &sin.sin_addr) == 1) {
-                       target = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&sin);
-               } else if (inet_pton(AF_INET6, argv[0], &sin6.sin6_addr) == 1) {
-                       char    *p;
+               if (argc > 1) {
+                       bzero(&r_sin, sizeof(r_sin));
+                       r_sin.sin_len    = sizeof(r_sin);
+                       r_sin.sin_family = AF_INET;
+               }
 
-                       p = strchr(argv[0], '%');
-                       if (p != NULL) {
-                               sin6.sin6_scope_id = if_nametoindex(p + 1);
+               if ((argc == 1)
+                   || (remote_address == NULL)
+                   || (inet_aton(remote_address, &r_sin.sin_addr) == 0)) {
+                       if (argc > 2) {
+                               options = _setupReachabilityOptions(argc - 2, argv + 2, interface);
                        }
+                       if (options == NULL) {
+                               target = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&sin);
+                               if (context != NULL) {
+                                       context->info = "by address";
+                               }
+                       } else {
+                               CFDataRef       data;
+
+                               data = CFDataCreate(NULL, (const UInt8 *)&sin, sizeof(sin));
+                               CFDictionarySetValue(options, kSCNetworkReachabilityOptionRemoteAddress, data);
+                               CFRelease(data);
 
-                       target = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&sin6);
+                               if (context != NULL) {
+                                       if (CFDictionaryContainsKey(options,
+                                                                   kSCNetworkReachabilityOptionInterface)) {
+                                               if (CFDictionaryGetCount(options) == 2) {
+                                                       context->info = "by address w/scope";
+                                               } else {
+                                                       context->info = "by address w/scope and options";
+                                               }
+                                       } else {
+                                               context->info = "by address w/options";
+                                       }
+                               }
+                       }
                } else {
-                       target = SCNetworkReachabilityCreateWithName(NULL, argv[0]);
+                       if (remote_interface != NULL) {
+                               if ((interface != NULL) && (strcmp(interface, remote_interface) != 0)) {
+                                       SCPrint(TRUE, stderr,
+                                               CFSTR("Interface mismatch \"%s\" != \"%s\"\n"),
+                                               interface,
+                                               remote_interface);
+                                       exit(1);
+                               }
+
+                               interface = remote_interface;
+                       }
+
+                       options = _setupReachabilityOptions(argc - 2, argv + 2, interface);
+                       if (options == NULL) {
+                               target = SCNetworkReachabilityCreateWithAddressPair(NULL,
+                                                                                   (struct sockaddr *)&sin,
+                                                                                   (struct sockaddr *)&r_sin);
+                               if (context != NULL) {
+                                       context->info = "by address pair";
+                               }
+                       } else {
+                               CFDataRef       data;
+
+                               data = CFDataCreate(NULL, (const UInt8 *)&sin, sizeof(sin));
+                               CFDictionarySetValue(options, kSCNetworkReachabilityOptionLocalAddress, data);
+                               CFRelease(data);
+                               data = CFDataCreate(NULL, (const UInt8 *)&r_sin, sizeof(r_sin));
+                               CFDictionarySetValue(options, kSCNetworkReachabilityOptionRemoteAddress, data);
+                               CFRelease(data);
+
+                               if (context != NULL) {
+                                       if (CFDictionaryContainsKey(options,
+                                                                   kSCNetworkReachabilityOptionInterface)) {
+                                               if (CFDictionaryGetCount(options) == 3) {
+                                                       context->info = "by address pair w/scope";
+                                               } else {
+                                                       context->info = "by address pair w/scope and options";
+                                               }
+                                       } else {
+                                               context->info = "by address pair w/options";
+                                       }
+                               }
+                       }
                }
-       } else /* if (argc == 2) */ {
-               struct sockaddr_in      l_sin;
-               struct sockaddr_in      r_sin;
+       } else if (inet_pton(AF_INET6, ip_address, &sin6.sin6_addr) == 1) {
+               struct sockaddr_in6     r_sin6;
 
-               bzero(&l_sin, sizeof(l_sin));
-               l_sin.sin_len         = sizeof(l_sin);
-               l_sin.sin_family      = AF_INET;
-               if (inet_aton(argv[0], &l_sin.sin_addr) == 0) {
-                       SCPrint(TRUE, stderr, CFSTR("Could not interpret address \"%s\"\n"), argv[0]);
-                       exit(1);
+               if (interface != NULL) {
+                       sin6.sin6_scope_id = if_nametoindex(interface);
                }
 
-               bzero(&r_sin, sizeof(r_sin));
-               r_sin.sin_len         = sizeof(r_sin);
-               r_sin.sin_family      = AF_INET;
-               if (inet_aton(argv[1], &r_sin.sin_addr) == 0) {
-                       SCPrint(TRUE, stderr, CFSTR("Could not interpret address \"%s\"\n"), argv[1]);
-                       exit(1);
+               if (argc > 1) {
+                       bzero(&r_sin6, sizeof(r_sin6));
+                       r_sin6.sin6_len    = sizeof(r_sin6);
+                       r_sin6.sin6_family = AF_INET6;
+               }
+
+               if ((argc == 1)
+                   || (remote_address == NULL)
+                   || (inet_pton(AF_INET6, remote_address, &r_sin6.sin6_addr) == 0)) {
+                       if (argc > 2) {
+                               options = _setupReachabilityOptions(argc - 2, argv + 2, NULL);
+                       }
+                       if (options == NULL) {
+                               target = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&sin6);
+                               if (context != NULL) {
+                                       context->info = "by (v6) address";
+                               }
+                       } else {
+                               CFDataRef       data;
+
+                               data = CFDataCreate(NULL, (const UInt8 *)&sin6, sizeof(sin6));
+                               CFDictionarySetValue(options, kSCNetworkReachabilityOptionRemoteAddress, data);
+                               CFRelease(data);
+
+                               if (context != NULL) {
+                                       context->info = "by (v6) address w/options";
+                               }
+                       }
+               } else {
+                       if (remote_interface != NULL) {
+                               r_sin6.sin6_scope_id = if_nametoindex(remote_interface);
+
+                               if ((interface != NULL) && (strcmp(interface, remote_interface) != 0)) {
+                                       SCPrint(TRUE, stderr,
+                                               CFSTR("Interface mismatch \"%s\" != \"%s\"\n"),
+                                               interface,
+                                               remote_interface);
+                                       exit(1);
+                               }
+                       }
+
+                       options = _setupReachabilityOptions(argc - 2, argv + 2, NULL);
+                       if (options == NULL) {
+                               target = SCNetworkReachabilityCreateWithAddressPair(NULL,
+                                                                                   (struct sockaddr *)&sin6,
+                                                                                   (struct sockaddr *)&r_sin6);
+                               if (context != NULL) {
+                                       context->info = "by (v6) address pair";
+                               }
+                       } else {
+                               CFDataRef       data;
+
+                               data = CFDataCreate(NULL, (const UInt8 *)&sin6, sizeof(sin6));
+                               CFDictionarySetValue(options, kSCNetworkReachabilityOptionLocalAddress, data);
+                               CFRelease(data);
+                               data = CFDataCreate(NULL, (const UInt8 *)&r_sin6, sizeof(r_sin6));
+                               CFDictionarySetValue(options, kSCNetworkReachabilityOptionRemoteAddress, data);
+                               CFRelease(data);
+
+                               if (context != NULL) {
+                                       context->info = "by (v6) address pair w/options";
+                               }
+                       }
                }
+       } else {
+               if (argc == 1) {
+                       target = SCNetworkReachabilityCreateWithName(NULL, argv[0]);
+                       if (context != NULL) {
+                               context->info = "by name";
+                       }
+               } else {
+                       options = _setupReachabilityOptions(argc - 1, argv + 1, NULL);
+                       if (options == NULL) {
+                               target = SCNetworkReachabilityCreateWithName(NULL, argv[0]);
+                               if (context != NULL) {
+                                       context->info = "by name";
+                               }
+                       } else {
+                               CFStringRef     str;
+
+                               str  = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
+                               CFDictionarySetValue(options, kSCNetworkReachabilityOptionNodeName, str);
+                               CFRelease(str);
 
-               target = SCNetworkReachabilityCreateWithAddressPair(NULL,
-                                                                   (struct sockaddr *)&l_sin,
-                                                                   (struct sockaddr *)&r_sin);
+                               if (context != NULL) {
+                                       context->info = "by name w/options";
+                               }
+                       }
+               }
        }
 
-       if (!target) {
-               SCPrint(TRUE, stderr, CFSTR("  Could not determine status: %s\n"), SCErrorString(SCError()));
-               exit(1);
+       if (ip_addressN != NULL) {
+               free(ip_addressN);
+       }
+
+       if (remote_addressN != NULL) {
+               free(remote_addressN);
+       }
+
+       if ((target == NULL) && (options != NULL)) {
+               if (CFDictionaryContainsKey(options, kSCNetworkReachabilityOptionPTRAddress)) {
+                       CFDataRef       address;
+
+                       address = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionRemoteAddress);
+                       if (address == NULL) {
+                               SCPrint(TRUE, stderr, CFSTR("No address\n"));
+                               exit(1);
+                       }
+                       CFDictionarySetValue(options, kSCNetworkReachabilityOptionPTRAddress, address);
+                       CFDictionaryRemoveValue(options, kSCNetworkReachabilityOptionRemoteAddress);
+
+                       if (context != NULL) {
+                               CFIndex n       = CFDictionaryGetCount(options);
+
+                               if (n == 1) {
+                                       context->info = "by PTR";
+                               } else if (CFDictionaryContainsKey(options,
+                                                                  kSCNetworkReachabilityOptionInterface)) {
+                                       if (n == 2) {
+                                               context->info = "by PTR w/scope";
+                                       } else {
+                                               context->info = "by PTR w/scope and options";
+                                       }
+                               } else {
+                                       context->info = "by PTR w/options";
+                               }
+                       }
+               }
+
+               target = SCNetworkReachabilityCreateWithOptions(NULL, options);
+               CFRelease(options);
+       }
+
+       return target;
+}
+
+
+static void
+_printReachability(SCNetworkReachabilityRef target)
+{
+       SCNetworkReachabilityFlags      flags;
+       Boolean                         ok;
+       CFStringRef                     str;
+
+       ok = SCNetworkReachabilityGetFlags(target, &flags);
+       if (!ok) {
+               SCPrint(TRUE, stderr, CFSTR("    could not determine reachability, %s\n"), SCErrorString(SCError()));
+               return;
+       }
+
+       str = __SCNetworkReachabilityCopyFlags(flags, CFSTR("flags = "), _sc_debug);
+       SCPrint(TRUE, stdout, CFSTR("%@\n"), str);
+       CFRelease(str);
+
+       if (resolver_bypass && _sc_debug) {
+               int     if_index;
+
+               if_index = SCNetworkReachabilityGetInterfaceIndex(target);
+               SCPrint(TRUE, stdout, CFSTR("interface index = %d\n"), if_index);
        }
 
-       if (!SCNetworkReachabilityGetFlags(target, &flags)) {
+       return;
+}
+
+
+__private_extern__
+void
+do_checkReachability(int argc, char **argv)
+{
+       SCNetworkReachabilityRef        target;
+
+       target = _setupReachability(argc, argv, NULL);
+       if (target == NULL) {
                SCPrint(TRUE, stderr, CFSTR("  Could not determine status: %s\n"), SCErrorString(SCError()));
                exit(1);
        }
 
-       SCPrint(_sc_debug, stdout, CFSTR("flags = 0x%x"), flags);
+       _printReachability(target);
+       CFRelease(target);
+       exit(0);
+}
+
+
+static void
+_printNWIFlags(nwi_ifstate_flags flags)
+{
+       flags &= NWI_IFSTATE_FLAGS_MASK;
+       if (flags == 0) {
+               return;
+       }
+
+       SCPrint(TRUE, stdout, CFSTR(" ("));
+       if (flags & NWI_IFSTATE_FLAGS_HAS_IPV4) {
+               SCPrint(TRUE, stdout, CFSTR("IPv4"));
+               flags &= ~NWI_IFSTATE_FLAGS_HAS_IPV4;
+               SCPrint(flags != 0, stdout, CFSTR(","));
+       }
+       if (flags & NWI_IFSTATE_FLAGS_HAS_IPV6) {
+               SCPrint(TRUE, stdout, CFSTR("IPv6"));
+               flags &= ~NWI_IFSTATE_FLAGS_HAS_IPV6;
+               SCPrint(flags != 0, stdout, CFSTR(","));
+       }
+       if (flags & NWI_IFSTATE_FLAGS_HAS_DNS) {
+               SCPrint(TRUE, stdout, CFSTR("DNS"));
+               flags &= ~NWI_IFSTATE_FLAGS_HAS_DNS;
+               SCPrint(flags != 0, stdout, CFSTR(","));
+       }
+       if (flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) {
+               SCPrint(TRUE, stdout, CFSTR("NOT-IN-LIST"));
+               flags &= ~NWI_IFSTATE_FLAGS_NOT_IN_LIST;
+               SCPrint(flags != 0, stdout, CFSTR(","));
+       }
+       if (flags & NWI_IFSTATE_FLAGS_HAS_SIGNATURE) {
+               SCPrint(TRUE, stdout, CFSTR("SIGNATURE"));
+               flags &= ~NWI_IFSTATE_FLAGS_HAS_SIGNATURE;
+               SCPrint(flags != 0, stdout, CFSTR(","));
+       }
+       if (flags & NWI_IFSTATE_FLAGS_NOT_IN_IFLIST) {
+               SCPrint(TRUE, stdout, CFSTR("NOT-IN-IFLIST"));
+               flags &= ~NWI_IFSTATE_FLAGS_NOT_IN_IFLIST;
+               SCPrint(flags != 0, stdout, CFSTR(","));
+       }
        if (flags != 0) {
-               SCPrint(_sc_debug, stdout, CFSTR(" ("));
-               if (flags & kSCNetworkFlagsReachable) {
-                       SCPrint(TRUE, stdout, CFSTR("Reachable"));
-                       flags &= ~kSCNetworkFlagsReachable;
-                       SCPrint(flags != 0, stdout, CFSTR(","));
-               }
-               if (flags & kSCNetworkFlagsTransientConnection) {
-                       SCPrint(TRUE, stdout, CFSTR("Transient Connection"));
-                       flags &= ~kSCNetworkFlagsTransientConnection;
-                       SCPrint(flags != 0, stdout, CFSTR(","));
-               }
-               if (flags & kSCNetworkFlagsConnectionRequired) {
-                       SCPrint(TRUE, stdout, CFSTR("Connection Required"));
-                       flags &= ~kSCNetworkFlagsConnectionRequired;
-                       SCPrint(flags != 0, stdout, CFSTR(","));
-               }
-               if (flags & kSCNetworkFlagsConnectionAutomatic) {
-                       SCPrint(TRUE, stdout, CFSTR("Connection Automatic"));
-                       flags &= ~kSCNetworkFlagsConnectionAutomatic;
-                       SCPrint(flags != 0, stdout, CFSTR(","));
+               SCPrint(TRUE, stdout, CFSTR("%p"), (void *)flags);
+       }
+       SCPrint(TRUE, stdout, CFSTR(")"));
+
+       return;
+}
+
+
+static void
+_printNWIInfo(nwi_ifstate_t ifstate)
+{
+       nwi_ifstate_flags               ifstate_flags;
+       SCNetworkReachabilityFlags      reach_flags = nwi_ifstate_get_reachability_flags(ifstate);
+       const uint8_t                   *signature;
+       int                             signature_length;
+       CFStringRef                     str;
+       const struct sockaddr           *vpn_addr = nwi_ifstate_get_vpn_server(ifstate);
+
+       ifstate_flags = nwi_ifstate_get_flags(ifstate);
+       if (_sc_debug) {
+               ifstate_flags |= ifstate->flags;
+       }
+
+       SCPrint(TRUE, stdout,
+               CFSTR(" %7s : flags %p"),
+               nwi_ifstate_get_ifname(ifstate),
+               (void *)ifstate_flags);
+       _printNWIFlags(ifstate_flags);
+
+       str = __SCNetworkReachabilityCopyFlags(reach_flags, CFSTR("           reach "), TRUE);
+       SCPrint(TRUE, stdout, CFSTR("\n%@"), str);
+       CFRelease(str);
+
+       if (vpn_addr != NULL) {
+               char vpn_ntopbuf[INET6_ADDRSTRLEN];
+
+               _SC_sockaddr_to_string(vpn_addr, vpn_ntopbuf, sizeof(vpn_ntopbuf));
+               SCPrint(TRUE, stdout, CFSTR("\n           VPN server: %s"), vpn_ntopbuf);
+       }
+
+       signature = nwi_ifstate_get_signature(ifstate, AF_UNSPEC, &signature_length);
+       if (signature != NULL) {
+               CFDataRef       digest  = NULL;
+
+               digest = CFDataCreate(NULL, signature, CC_SHA1_DIGEST_LENGTH);
+               SCPrint(TRUE, stdout, CFSTR("\n           Signature Hash: %@"), digest);
+               CFRelease(digest);
+       } else {
+               SCPrint(TRUE, stdout, CFSTR("\n           Signature Hash: <empty>"));
+       }
+
+       SCPrint(TRUE, stdout, CFSTR("\n           generation %llu\n"),
+               nwi_ifstate_get_generation(ifstate));
+
+       return;
+}
+
+
+static void
+_printNWIReachInfo(nwi_state_t state, int af)
+{
+       uint32_t        reach_flags;
+       CFStringRef     str;
+
+       reach_flags = nwi_state_get_reachability_flags(state, af);
+
+       str = __SCNetworkReachabilityCopyFlags(reach_flags, CFSTR("   REACH : flags "), TRUE);
+       SCPrint(TRUE, stdout, CFSTR("\n%@\n"), str);
+       CFRelease(str);
+
+       return;
+}
+
+
+static void
+do_printNWI(int argc, char **argv, nwi_state_t state)
+{
+       unsigned int    count;
+       nwi_ifstate_t   ifstate;
+
+       if (state == NULL) {
+               SCPrint(TRUE, stdout, CFSTR("No network information\n"));
+               return;
+       }
+
+       if (argc > 0) {
+               ifstate = nwi_state_get_ifstate(state, argv[0]);
+               if (ifstate != NULL) {
+                       nwi_ifstate_t   alias;
+
+                       _printNWIInfo(ifstate);
+
+                       alias = nwi_ifstate_get_alias(ifstate,
+                                                     ifstate->af == AF_INET ? AF_INET6 : AF_INET);
+                       if (alias != NULL) {
+                               SCPrint(TRUE, stdout, CFSTR("\n"));
+                               _printNWIInfo(alias);
+                       }
+               } else {
+                       SCPrint(TRUE, stdout, CFSTR("No network information (for %s)\n"), argv[0]);
                }
-               if (flags & kSCNetworkFlagsInterventionRequired) {
-                       SCPrint(TRUE, stdout, CFSTR("Intervention Required"));
-                       flags &= ~kSCNetworkFlagsInterventionRequired;
-                       SCPrint(flags != 0, stdout, CFSTR(","));
+               return;
+       }
+
+       SCPrint(TRUE, stdout, CFSTR("Network information (generation %llu)"),
+               nwi_state_get_generation(state));
+
+       SCPrint(TRUE, stdout, CFSTR("\nIPv4 network interface information\n"));
+
+       ifstate = nwi_state_get_first_ifstate(state, AF_INET);
+       if (ifstate == NULL) {
+               SCPrint(TRUE, stdout, CFSTR("   No IPv4 states found\n"));
+       } else {
+               while (ifstate != NULL) {
+                       _printNWIInfo(ifstate);
+                       ifstate = nwi_ifstate_get_next(ifstate, AF_INET);
                }
-               if (flags & kSCNetworkFlagsIsLocalAddress) {
-                       SCPrint(TRUE, stdout, CFSTR("Local Address"));
-                       flags &= ~kSCNetworkFlagsIsLocalAddress;
-                       SCPrint(flags != 0, stdout, CFSTR(","));
+       }
+       _printNWIReachInfo(state, AF_INET);
+
+       SCPrint(TRUE, stdout, CFSTR("\nIPv6 network interface information\n"));
+
+       ifstate = nwi_state_get_first_ifstate(state, AF_INET6);
+       if (ifstate == NULL) {
+               SCPrint(TRUE, stdout, CFSTR("   No IPv6 states found\n"));
+       } else {
+               while (ifstate != NULL) {
+                       _printNWIInfo(ifstate);
+                       ifstate = nwi_ifstate_get_next(ifstate, AF_INET6);
                }
-               if (flags & kSCNetworkFlagsIsDirect) {
-                       SCPrint(TRUE, stdout, CFSTR("Directly Reachable Address"));
-                       flags &= ~kSCNetworkFlagsIsDirect;
-                       SCPrint(flags != 0, stdout, CFSTR(","));
+       }
+       _printNWIReachInfo(state, AF_INET6);
+
+       count = nwi_state_get_interface_names(state, NULL, 0);
+       if (count > 0) {
+               const char *    names[count];
+
+               count = nwi_state_get_interface_names(state, names, count);
+               if (count > 0) {
+                       int i;
+
+                       printf("\nNetwork interfaces:");
+                       for (i = 0; i < count; i++) {
+                               printf(" %s", names[i]);
+                       }
+                       printf("\n");
                }
-               SCPrint(_sc_debug, stdout, CFSTR(")"));
+       }
+       return;
+}
+
+
+__private_extern__
+void
+do_showNWI(int argc, char **argv)
+{
+       nwi_state_t     state;
+
+       state = nwi_state_copy();
+       do_printNWI(argc, argv, state);
+       if (state != NULL) {
+               nwi_state_release(state);
        } else {
-               SCPrint(_sc_debug, stdout, CFSTR(" ("));
-               SCPrint(TRUE, stdout, CFSTR("Not Reachable"));
-               SCPrint(_sc_debug, stdout, CFSTR(")"));
+               exit(1);
        }
-       SCPrint(TRUE, stdout, CFSTR("\n"));
+
        exit(0);
 }
 
 
 __private_extern__
 void
-do_showDNSConfiguration(int argc, char **argv)
+do_watchNWI(int argc, char **argv)
 {
-       dns_config_t    *dns_config;
+       nwi_state_t     state;
+       int             status;
+       int             token;
 
-       dns_config = dns_configuration_copy();
-       if (dns_config) {
-               int     n;
+       state = nwi_state_copy();
+       do_printNWI(argc, argv, state);
+       if (state != NULL) {
+               nwi_state_release(state);
+       }
 
-               SCPrint(TRUE, stdout, CFSTR("DNS configuration\n"));
+       status = notify_register_dispatch(nwi_state_get_notify_key(),
+                                         &token,
+                                         dispatch_get_main_queue(),
+                                         ^(int token){
+                                                 nwi_state_t           state;
+                                                 struct tm             tm_now;
+                                                 struct timeval        tv_now;
 
-               for (n = 0; n < dns_config->n_resolver; n++) {
-                       int             i;
-                       dns_resolver_t  *resolver       = dns_config->resolver[n];
+                                                 (void)gettimeofday(&tv_now, NULL);
+                                                 (void)localtime_r(&tv_now.tv_sec, &tm_now);
+                                                 SCPrint(TRUE, stdout, CFSTR("\n*** %2d:%02d:%02d.%03d\n\n"),
+                                                         tm_now.tm_hour,
+                                                         tm_now.tm_min,
+                                                         tm_now.tm_sec,
+                                                         tv_now.tv_usec / 1000);
 
-                       SCPrint(TRUE, stdout, CFSTR("\nresolver #%d\n"), n + 1);
+                                                 state = nwi_state_copy();
+                                                 do_printNWI(argc, argv, state);
+                                                 if (state != NULL) {
+                                                         nwi_state_release(state);
+                                                 }
+                                         });
+       if (status != NOTIFY_STATUS_OK) {
+               SC_log(LOG_INFO, "notify_register_dispatch() failed for nwi changes, status=%u", status);
+               exit(1);
+       }
 
-                       if (resolver->domain != NULL) {
-                               SCPrint(TRUE, stdout, CFSTR("  domain : %s\n"), resolver->domain);
-                       }
+       CFRunLoopRun();
+       exit(0);
+}
 
-                       for (i = 0; i < resolver->n_search; i++) {
-                               SCPrint(TRUE, stdout, CFSTR("  search domain[%d] : %s\n"), i, resolver->search[i]);
-                       }
 
-                       for (i = 0; i < resolver->n_nameserver; i++) {
-                               char    buf[128];
+static void
+callout(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info)
+{
+       static int      n = 3;
+       struct tm       tm_now;
+       struct timeval  tv_now;
 
-                               _SC_sockaddr_to_string(resolver->nameserver[i], buf, sizeof(buf));
-                               SCPrint(TRUE, stdout, CFSTR("  nameserver[%d] : %s\n"), i, buf);
-                       }
+       (void)gettimeofday(&tv_now, NULL);
+       (void)localtime_r(&tv_now.tv_sec, &tm_now);
 
-                       for (i = 0; i < resolver->n_sortaddr; i++) {
-                               SCPrint(TRUE, stdout, CFSTR("  sortaddr[%d] : %s/%s\n"),
-                                       i,
-                                       inet_ntoa(resolver->sortaddr[i]->address),
-                                       inet_ntoa(resolver->sortaddr[i]->mask));
-                       }
+       SCPrint(TRUE, stdout, CFSTR("\n*** %2d:%02d:%02d.%03d\n\n"),
+               tm_now.tm_hour,
+               tm_now.tm_min,
+               tm_now.tm_sec,
+               tv_now.tv_usec / 1000);
+       SCPrint(TRUE, stdout, CFSTR("%2d: callback w/flags=0x%08x (info=\"%s\")\n"), n++, flags, (char *)info);
+       SCPrint(TRUE, stdout, CFSTR("    %@\n"), target);
+       _printReachability(target);
+       SCPrint(TRUE, stdout, CFSTR("\n"));
+       return;
+}
 
-                       if (resolver->options != NULL) {
-                               SCPrint(TRUE, stdout, CFSTR("  options : %s\n"), resolver->options);
-                       }
 
-                       if (resolver->port != 0) {
-                               SCPrint(TRUE, stdout, CFSTR("  port    : %hd\n"), resolver->port);
-                       }
+__private_extern__
+void
+do_watchReachability(int argc, char **argv)
+{
+       SCNetworkReachabilityContext    context = { 0, NULL, NULL, NULL, NULL };
+       SCNetworkReachabilityRef        target;
+       SCNetworkReachabilityRef        target_async;
 
-                       if (resolver->timeout != 0) {
-                               SCPrint(TRUE, stdout, CFSTR("  timeout : %d\n"), resolver->timeout);
-                       }
+       target = _setupReachability(argc, argv, NULL);
+       if (target == NULL) {
+               SCPrint(TRUE, stderr, CFSTR("  Could not determine status: %s\n"), SCErrorString(SCError()));
+               exit(1);
+       }
 
-                       if (resolver->search_order != 0) {
-                               SCPrint(TRUE, stdout, CFSTR("  order   : %d\n"), resolver->search_order);
-                       }
+       target_async = _setupReachability(argc, argv, &context);
+       if (target_async == NULL) {
+               SCPrint(TRUE, stderr, CFSTR("  Could not determine status: %s\n"), SCErrorString(SCError()));
+               exit(1);
+       }
+
+       // Normally, we don't want to make any calls to SCNetworkReachabilityGetFlags()
+       // until after the "target" has been scheduled on a run loop.  Otherwise, we'll
+       // end up making a synchronous DNS request and that's not what we want.
+       //
+       // To test the case were an application first calls SCNetworkReachabilityGetFlags()
+       // we provide the "CHECK_REACHABILITY_BEFORE_SCHEDULING" environment variable.
+       if (getenv("CHECK_REACHABILITY_BEFORE_SCHEDULING") != NULL) {
+               CFRelease(target_async);
+               target_async = CFRetain(target);
+       }
+
+       // Direct check of reachability
+       SCPrint(TRUE, stdout, CFSTR(" 0: direct\n"));
+       SCPrint(TRUE, stdout, CFSTR("   %@\n"), target);
+       _printReachability(target);
+       CFRelease(target);
+       SCPrint(TRUE, stdout, CFSTR("\n"));
+
+       // schedule the target
+       SCPrint(TRUE, stdout, CFSTR(" 1: start\n"));
+       SCPrint(TRUE, stdout, CFSTR("   %@\n"), target_async);
+//     _printReachability(target_async);
+       SCPrint(TRUE, stdout, CFSTR("\n"));
+
+       if (!SCNetworkReachabilitySetCallback(target_async, callout, &context)) {
+               printf("SCNetworkReachabilitySetCallback() failed: %s\n", SCErrorString(SCError()));
+               exit(1);
+       }
+
+       if (doDispatch) {
+               if (!SCNetworkReachabilitySetDispatchQueue(target_async, dispatch_get_main_queue())) {
+                       printf("SCNetworkReachabilitySetDispatchQueue() failed: %s\n", SCErrorString(SCError()));
+                       exit(1);
+               }
+       } else {
+               if (!SCNetworkReachabilityScheduleWithRunLoop(target_async, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
+                       printf("SCNetworkReachabilityScheduleWithRunLoop() failed: %s\n", SCErrorString(SCError()));
+                       exit(1);
                }
+       }
+
+       // Note: now that we are scheduled on a run loop we can call SCNetworkReachabilityGetFlags()
+       //       to get the current status.  For "names", a DNS lookup has already been initiated.
+       SCPrint(TRUE, stdout, CFSTR(" 2: on %s\n"), doDispatch ? "dispatch queue" : "runloop");
+       SCPrint(TRUE, stdout, CFSTR("   %@\n"), target_async);
+       _printReachability(target_async);
+       SCPrint(TRUE, stdout, CFSTR("\n"));
+
+       CFRunLoopRun();
+       exit(0);
+}
+
+
+static void
+do_printDNSConfiguration(int argc, char **argv, dns_config_t *dns_config)
+{
+       int     _sc_log_save;
+
+       if (dns_config == NULL) {
+               SCPrint(TRUE, stdout, CFSTR("No DNS configuration available\n"));
+               return;
+       }
+
+       _sc_log_save = _sc_log;
+       _sc_log = FALSE;
+       _dns_configuration_log(dns_config, _sc_debug);
+       _sc_log = _sc_log_save;
+
+       if (_sc_debug) {
+               SCPrint(TRUE, stdout, CFSTR("\ngeneration = %llu\n"), dns_config->generation);
+       }
 
+       return;
+}
+
+
+__private_extern__
+void
+do_showDNSConfiguration(int argc, char **argv)
+{
+       dns_config_t    *dns_config;
+
+       dns_config = dns_configuration_copy();
+       do_printDNSConfiguration(argc, argv, dns_config);
+       if (dns_config != NULL) {
                dns_configuration_free(dns_config);
        } else {
-               SCPrint(TRUE, stdout, CFSTR("No DNS configuration available\n"));
+               exit(1);
        }
 
        exit(0);
 }
 
 
+__private_extern__
+void
+do_watchDNSConfiguration(int argc, char **argv)
+{
+       dns_config_t    *dns_config;
+       int             status;
+       int             token;
+
+       dns_config = dns_configuration_copy();
+       do_printDNSConfiguration(argc, argv, dns_config);
+       if (dns_config != NULL) {
+               dns_configuration_free(dns_config);
+       }
+
+       status = notify_register_dispatch(dns_configuration_notify_key(),
+                                         &token,
+                                         dispatch_get_main_queue(),
+                                         ^(int token){
+                                                 dns_config_t          *dns_config;
+                                                 struct tm             tm_now;
+                                                 struct timeval        tv_now;
+
+                                                 (void)gettimeofday(&tv_now, NULL);
+                                                 (void)localtime_r(&tv_now.tv_sec, &tm_now);
+                                                 SCPrint(TRUE, stdout, CFSTR("\n*** %2d:%02d:%02d.%03d\n\n"),
+                                                         tm_now.tm_hour,
+                                                         tm_now.tm_min,
+                                                         tm_now.tm_sec,
+                                                         tv_now.tv_usec / 1000);
+
+                                                 dns_config = dns_configuration_copy();
+                                                 do_printDNSConfiguration(argc, argv, dns_config);
+                                                 if (dns_config != NULL) {
+                                                         dns_configuration_free(dns_config);
+                                                 }
+                                         });
+       if (status != NOTIFY_STATUS_OK) {
+               SC_log(LOG_INFO, "notify_register_dispatch() failed for DNS configuration changes, status=%u", status);
+               exit(1);
+       }
+
+       CFRunLoopRun();
+       exit(0);
+}
+
+
+static void
+showProxy(CFDictionaryRef proxy)
+{
+       CFMutableDictionaryRef  cleaned = NULL;
+
+       if (!_sc_debug) {
+               cleaned = CFDictionaryCreateMutableCopy(NULL, 0, proxy);
+               CFDictionaryRemoveValue(cleaned, kSCPropNetProxiesScoped);
+               CFDictionaryRemoveValue(cleaned, kSCPropNetProxiesServices);
+               CFDictionaryRemoveValue(cleaned, kSCPropNetProxiesSupplemental);
+               proxy = cleaned;
+       }
+
+       SCPrint(TRUE, stdout, CFSTR("%@\n"), proxy);
+       if (cleaned != NULL) CFRelease(cleaned);
+       return;
+}
+
+
 __private_extern__
 void
 do_showProxyConfiguration(int argc, char **argv)
 {
-       CFDictionaryRef proxies;
+       CFMutableDictionaryRef  options = NULL;
+       CFDictionaryRef         proxies;
+
+       if (getenv("BYPASS_GLOBAL_PROXY") != NULL) {
+               options = CFDictionaryCreateMutable(NULL, 0,
+                                                   &kCFTypeDictionaryKeyCallBacks,
+                                                   &kCFTypeDictionaryValueCallBacks);
+               CFDictionaryAddValue(options, kSCProxiesNoGlobal, kCFBooleanTrue);
+       }
+
+       proxies = SCDynamicStoreCopyProxiesWithOptions(NULL, options);
+
+       if (options != NULL) {
+               CFRelease(options);
+       }
 
-       proxies = SCDynamicStoreCopyProxies(NULL);
        if (proxies != NULL) {
-               SCPrint(TRUE, stdout, CFSTR("%@\n"), proxies);
+               CFStringRef     interface       = NULL;
+               CFStringRef     server          = NULL;
+
+               while (argc > 0) {
+                       if (strcasecmp(argv[0], "interface") == 0) {
+                               argv++;
+                               argc--;
+
+                               if (argc < 1) {
+                                       SCPrint(TRUE, stderr, CFSTR("No interface\n"));
+                                       exit(1);
+                               }
+
+                               if (if_nametoindex(argv[0]) == 0) {
+                                       SCPrint(TRUE, stderr, CFSTR("No interface: %s\n"), argv[0]);
+                                       exit(1);
+                               }
+
+                               interface = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
+                               argv++;
+                               argc--;
+                       } else {
+                               if (server != NULL) {
+                                       CFRelease(server);
+                               }
+
+                               server = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
+                               argv++;
+                               argc--;
+                       }
+               }
+
+               if ((server != NULL) || (interface != NULL)) {
+                       CFArrayRef      matching;
+
+                       matching = SCNetworkProxiesCopyMatching(proxies, server, interface);
+                       if (matching != NULL) {
+                               CFIndex i;
+                               CFIndex n;
+
+                               if (server != NULL) {
+                                       if (interface != NULL) {
+                                               SCPrint(TRUE, stdout,
+                                                       CFSTR("server = %@, interface = %@\n"),
+                                                       server,
+                                                       interface);
+                                       } else {
+                                               SCPrint(TRUE, stdout,
+                                                       CFSTR("server = %@\n"),
+                                                       server);
+                                       }
+                               } else {
+                                       SCPrint(TRUE, stdout,
+                                               CFSTR("interface = %@\n"),
+                                               interface);
+                               }
+
+                               n = CFArrayGetCount(matching);
+                               for (i = 0; i < n; i++) {
+                                       CFDictionaryRef proxy;
+
+                                       proxy = CFArrayGetValueAtIndex(matching, i);
+                                       SCPrint(TRUE, stdout, CFSTR("\nproxy #%ld\n"), i + 1);
+                                       showProxy(proxy);
+                               }
+
+                               CFRelease(matching);
+                       } else {
+                               SCPrint(TRUE, stdout, CFSTR("No matching proxy configurations\n"));
+                       }
+               } else {
+                       showProxy(proxies);
+               }
+
+               if (interface != NULL) CFRelease(interface);
+               if (server != NULL) CFRelease(server);
+               CFRelease(proxies);
        } else {
                SCPrint(TRUE, stdout, CFSTR("No proxy configuration available\n"));
        }
@@ -253,10 +1038,122 @@ do_snapshot(int argc, char **argv)
        if (!SCDynamicStoreSnapshot(store)) {
                SCPrint(TRUE, stdout, CFSTR("  %s\n"), SCErrorString(SCError()));
        }
+
        return;
 }
 
 
+__private_extern__
+void
+do_renew(char *if_name)
+{
+       CFArrayRef      services;
+       Boolean         ok      = FALSE;
+
+       if ((if_name == NULL) || (strlen(if_name) == 0)) {
+               SCPrint(TRUE, stderr, CFSTR("No interface name\n"));
+               exit(1);
+       }
+
+       if (getenv("ATTEMPT_DHCP_RENEW_WITH_SCDYNAMICSTORE") != NULL) {
+               CFArrayRef      interfaces;
+
+               interfaces = SCNetworkInterfaceCopyAll();
+               if (interfaces != NULL) {
+                       CFIndex         i;
+                       CFStringRef     match_name;
+                       CFIndex         n;
+
+                       match_name = CFStringCreateWithCString(NULL, if_name, kCFStringEncodingASCII);
+                       assert(match_name != NULL);
+
+                       n = CFArrayGetCount(interfaces);
+                       for (i = 0; i < n; i++) {
+                               CFStringRef             bsd_name;
+                               SCNetworkInterfaceRef   interface;
+
+                               interface = CFArrayGetValueAtIndex(interfaces, i);
+                               bsd_name = SCNetworkInterfaceGetBSDName(interface);
+                               if (_SC_CFEqual(bsd_name, match_name)) {
+                                       // if match
+                                       ok = SCNetworkInterfaceForceConfigurationRefresh(interface);
+                                       if (!ok) {
+                                               int     status;
+
+                                               status = SCError();
+                                               if (status != kSCStatusAccessError) {
+                                                       SCPrint(TRUE, stderr, CFSTR("%s\n"), SCErrorString(status));
+                                                       exit(1);
+                                               }
+
+                                               // ... and if can't write the SCDynamicStore, try w/prefs
+                                       }
+
+                                       break;
+                               }
+                       }
+
+                       CFRelease(match_name);
+                       CFRelease(interfaces);
+               }
+
+               if (ok) {
+                       exit(0);
+               }
+       }
+
+       do_prefs_init();        /* initialization */
+       do_prefs_open(0, NULL); /* open default prefs */
+
+       services = SCNetworkServiceCopyAll(prefs);
+       if (services != NULL) {
+               CFIndex         i;
+               CFStringRef     match_name;
+               CFIndex         n;
+
+               match_name = CFStringCreateWithCString(NULL, if_name, kCFStringEncodingASCII);
+               assert(match_name != NULL);
+
+               n = CFArrayGetCount(services);
+               for (i = 0; i < n; i++) {
+                       CFStringRef             bsd_name;
+                       SCNetworkInterfaceRef   interface;
+                       SCNetworkServiceRef     service;
+
+                       service = CFArrayGetValueAtIndex(services, i);
+                       interface = SCNetworkServiceGetInterface(service);
+                       if (interface == NULL) {
+                               // if no interface
+                               continue;
+                       }
+
+                       bsd_name = SCNetworkInterfaceGetBSDName(interface);
+                       if (_SC_CFEqual(bsd_name, match_name)) {
+                               // if match
+                               ok = SCNetworkInterfaceForceConfigurationRefresh(interface);
+                               if (!ok) {
+                                       SCPrint(TRUE, stderr, CFSTR("%s\n"), SCErrorString(SCError()));
+                                       exit(1);
+                               }
+
+                               break;
+                       }
+               }
+
+               CFRelease(match_name);
+               CFRelease(services);
+       }
+
+       if (!ok) {
+               SCPrint(TRUE, stderr, CFSTR("No interface\n"));
+               exit(1);
+       }
+
+       _prefs_close();
+       exit(0);
+}
+
+
 static void
 waitKeyFound()
 {
@@ -278,6 +1175,7 @@ do_wait(char *waitKey, int timeout)
        struct itimerval        itv;
        CFStringRef             key;
        CFMutableArrayRef       keys;
+       Boolean                 ok;
 
        store = SCDynamicStoreCreate(NULL, CFSTR("scutil (wait)"), waitKeyFound, NULL);
        if (store == NULL) {
@@ -286,11 +1184,13 @@ do_wait(char *waitKey, int timeout)
                exit(1);
        }
 
-       keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
        key  = CFStringCreateWithCString(NULL, waitKey, kCFStringEncodingUTF8);
-       CFArrayAppendValue(keys, key);
 
-       if (!SCDynamicStoreSetNotificationKeys(store, keys, NULL)) {
+       keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+       CFArrayAppendValue(keys, key);
+       ok = SCDynamicStoreSetNotificationKeys(store, keys, NULL);
+       CFRelease(keys);
+       if (!ok) {
                SCPrint(TRUE, stderr,
                        CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s\n"), SCErrorString(SCError()));
                exit(1);
@@ -326,17 +1226,36 @@ do_wait(char *waitKey, int timeout)
        CFRunLoopRun();
 }
 
-#ifdef TEST_DNS_CONFIGURATION_COPY
+#ifdef TEST_DNS_CONFIGURATION
 
+Boolean                        doDispatch      = FALSE;
 CFRunLoopSourceRef     notifyRls       = NULL;
 SCDynamicStoreRef      store           = NULL;
 CFPropertyListRef      value           = NULL;
-       
+
 int
 main(int argc, char **argv)
 {
+       dns_config_t    *dns_config;
+
+fprintf(stdout, "copy configuration\n");
+       dns_config = dns_configuration_copy();
+       if (dns_config != NULL) {
+
+fprintf(stdout, "sleeping for 120 seconds\n");
+sleep(120);
+
+fprintf(stdout, "sending ack\n");
+               _dns_configuration_ack(dns_config, "TEST_DNS_CONFIGURATION");
+
+fprintf(stdout, "sleeping for 120 seconds\n");
+sleep(120);
+
+               dns_configuration_free(dns_config);
+       }
+
        do_showDNSConfiguration(argc, argv);
        exit(0);
 }
 
-#endif // TEST_DNS_CONFIGURATION_COPY
+#endif // TEST_DNS_CONFIGURATION