]> git.saurik.com Git - apple/configd.git/commitdiff
configd-888.51.2.tar.gz macos-10124 macos-10125 macos-10126 v888.51.2 v888.60.2
authorApple <opensource@apple.com>
Wed, 29 Mar 2017 20:14:01 +0000 (20:14 +0000)
committerApple <opensource@apple.com>
Wed, 29 Mar 2017 20:14:01 +0000 (20:14 +0000)
47 files changed:
Plugins/IPMonitor/configAgentDefines.h
Plugins/IPMonitor/controller.m
Plugins/IPMonitor/dns-configuration.c
Plugins/IPMonitor/ip_plugin.c
Plugins/IPMonitor/ip_plugin.h
Plugins/InterfaceNamer/ifnamer.c
Plugins/PreferencesMonitor/prefsmon.c
Plugins/common/InterfaceNamerControlPrefs.c [new file with mode: 0644]
Plugins/common/InterfaceNamerControlPrefs.h [new file with mode: 0644]
SCMonitor/monitor.c
SystemConfiguration.fproj/BondConfiguration.c
SystemConfiguration.fproj/BridgeConfiguration.c
SystemConfiguration.fproj/SCNetworkConfigurationInternal.h
SystemConfiguration.fproj/SCNetworkInterface.c
SystemConfiguration.fproj/SCNetworkMigration.c
SystemConfiguration.fproj/SCNetworkReachability.c
SystemConfiguration.fproj/SCNetworkSet.c
SystemConfiguration.fproj/SCPrivate.h
SystemConfiguration.fproj/SCProxies.c
SystemConfiguration.fproj/SCSchemaDefinitions.c
SystemConfiguration.fproj/SCSchemaDefinitions.h
SystemConfiguration.fproj/SCSchemaDefinitionsPrivate.h
SystemConfiguration.fproj/VLANConfiguration.c
SystemConfiguration.fproj/genSCPreferences.c
config-agent-info/config_agent_info.h
configd.xcodeproj/project.pbxproj
get-mobility-info
get-network-info
sctest/Makefile [new file with mode: 0644]
sctest/SCTest.h [new file with mode: 0644]
sctest/SCTest.m [new file with mode: 0644]
sctest/SCTestConfigAgents.m [new file with mode: 0644]
sctest/SCTestDynamicStore.m [new file with mode: 0644]
sctest/SCTestOptions.h [new file with mode: 0644]
sctest/SCTestOptions.m [new file with mode: 0644]
sctest/SCTestPreferences.m [new file with mode: 0644]
sctest/SCTestReachability.m [new file with mode: 0644]
sctest/SCTestUnitTest.m [new file with mode: 0644]
sctest/SCTestUtils.h [new file with mode: 0644]
sctest/SCTestUtils.m [new file with mode: 0644]
sctest/genSCTestOptions.c [new file with mode: 0644]
sctest/main.m [new file with mode: 0644]
sctest/npt_configd.plist [new file with mode: 0644]
sctest/sctest-entitlements.plist [new file with mode: 0644]
scutil.tproj/prefs.c
scutil.tproj/prefs.h
scutil.tproj/scutil.c

index 1d31c94f0219183f26d7673314fc40f345b53b74..d2c61c5c240721e0c9d1bbec020424f86d4d0001 100644 (file)
 #ifndef CONFIGAGENTDEFINES_H
 #define CONFIGAGENTDEFINES_H
 
-#define kConfigAgentDomain                      "SystemConfig"
+#import "config_agent_info.h"
+
 #define kConfigAgentType                        "AgentType"
 #define kConfigAgentTypeGeneric                 "ConfigAgent"
-#define kConfigAgentTypeProxy                   "ProxyAgent"
-#define kConfigAgentTypeDNS                     "DNSAgent"
 
 #define kConfigAgentTypeDNSMulticast            kConfigAgentTypeDNS "(m)"
 #define kConfigAgentTypeDNSPrivate              kConfigAgentTypeDNS "(p)"
index 12f4997dda83a70b17d811782fe1a9e2e3471237..2a7e06f12d8b79e07239e301198e6d6e2083527f 100644 (file)
@@ -241,19 +241,15 @@ typedef struct resolverList {
 
 - (NSData *)dataForProxyArray:(CFArrayRef)proxy_array_for_data
 {
-       NSData *data = [NSPropertyListSerialization dataWithPropertyList:(__bridge id _Nonnull)(proxy_array_for_data)
-                                                                 format:NSPropertyListBinaryFormat_v1_0
-                                                                options:0
-                                                                  error:nil];
-
-       return data;
+       CFDataRef data = NULL;
+       (void)_SCSerialize(proxy_array_for_data, &data, NULL, NULL);
+       return (__bridge_transfer NSData *)data;
 }
 
 - (NSData *)dataForProxyDictionary:(CFDictionaryRef)domain_proxy
 {
        NSData  *       data = nil;
        CFMutableDictionaryRef domain_proxy_dict;
-       CFArrayRef      domain_proxy_array;
 
        if (domain_proxy == NULL) {
                SC_log(LOG_NOTICE, "Invalid domain proxy dict");
@@ -263,12 +259,9 @@ typedef struct resolverList {
        domain_proxy_dict = CFDictionaryCreateMutableCopy(NULL, 0, domain_proxy);
        CFDictionaryRemoveValue(domain_proxy_dict, kSCPropNetProxiesSupplementalMatchDomain);
 
-       domain_proxy_array = CFArrayCreate(NULL, (const void **)&domain_proxy_dict, 1, &kCFTypeArrayCallBacks);
+       data = (__bridge_transfer NSData *)(SCNetworkProxiesCreateProxyAgentData(domain_proxy_dict));
        CFRelease(domain_proxy_dict);
 
-       data = [self dataForProxyArray:domain_proxy_array];
-       CFRelease(domain_proxy_array);
-
        return data;
 }
 
index 6fff6a0cc604cb00e6fc33685c4c9eadcc58074e..cb922442426d96c2e87d39b519a206dd0cfadbfd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
@@ -263,20 +263,29 @@ add_supplemental(CFMutableArrayRef        resolvers,
        CFIndex         i;
        CFIndex         n_domains;
        CFArrayRef      orders;
+       CFArrayRef      servers;
 
        domains = CFDictionaryGetValue(dns, kSCPropNetDNSSupplementalMatchDomains);
        n_domains = isA_CFArray(domains) ? CFArrayGetCount(domains) : 0;
        if (n_domains == 0) {
+               // if no supplemental match domains
                return;
        }
 
        orders = CFDictionaryGetValue(dns, kSCPropNetDNSSupplementalMatchOrders);
        if (orders != NULL) {
                if (!isA_CFArray(orders) || (n_domains != CFArrayGetCount(orders))) {
+                       // if supplemental match orders... but too many/not enough
                        return;
                }
        }
 
+       servers = CFDictionaryGetValue(dns, kSCPropNetDNSServerAddresses);
+       if (!isA_CFArray(servers) || (CFArrayGetCount(servers) == 0)) {
+               // if no DNS server addresses
+               return;
+       }
+
        /*
         * yes, this is a "supplemental" resolver configuration, expand
         * the match domains and add each to the resolvers list.
@@ -894,6 +903,7 @@ add_scoped_resolvers(CFMutableArrayRef      scoped,
                CFArrayRef              searchDomains;
                CFDictionaryRef         service;
                CFStringRef             serviceID;
+               CFArrayRef              servers;
 
                serviceID = CFArrayGetValueAtIndex(order, i);
                service = CFDictionaryGetValue(services, serviceID);
@@ -908,6 +918,12 @@ add_scoped_resolvers(CFMutableArrayRef     scoped,
                        continue;
                }
 
+               servers = CFDictionaryGetValue(dns, kSCPropNetDNSServerAddresses);
+               if (!isA_CFArray(servers) || (CFArrayGetCount(servers) == 0)) {
+                       // if no DNS server addresses
+                       continue;
+               }
+
                interface = CFDictionaryGetValue(dns, kSCPropInterfaceName);
                if ((interface == NULL) || CFEqual(interface, CFSTR("*"))) {
                        // if no [scoped] interface or supplemental configuration w/match-all
@@ -1528,6 +1544,16 @@ dns_configuration_set(CFDictionaryRef   defaultResolver,
 
        // add the "default" resolver
 
+       if (defaultResolver != NULL) {
+               CFArrayRef      servers;
+
+               servers = CFDictionaryGetValue(defaultResolver, kSCPropNetDNSServerAddresses);
+               if (!isA_CFArray(servers) || (CFArrayGetCount(servers) == 0)) {
+                       // if no DNS server addresses
+                       defaultResolver = NULL;
+               }
+       }
+
        add_default_resolver(resolvers, defaultResolver, &myOrderAdded, &mySearchDomains);
 
        // collect (and add) any "multicast" resolver configurations
index 4270658a26a3106389b0173469e81740a4b68a57..413217f18029a8011cb8c0e2f1ea9a5b7a44e45a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2016 Apple Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2017 Apple Inc.  All Rights Reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
@@ -3797,30 +3797,47 @@ parse_component(CFStringRef key, CFStringRef prefix)
     return (comp);
 }
 
-__private_extern__ boolean_t
-service_contains_protocol(CFDictionaryRef service, int af)
+
+static boolean_t
+entity_routes_protocol(CFDictionaryRef entity_dict)
 {
-    boolean_t          contains_protocol = FALSE;
-    CFStringRef                entity;
     RouteListRef       routes;
-    CFDictionaryRef    dict;
 
-    entity = (af == AF_INET) ? kSCEntNetIPv4 : kSCEntNetIPv6;
-    dict = CFDictionaryGetValue(service, entity);
-    if (dict == NULL) {
-       goto done;
-    }
-    routes = ipdict_get_routelist(dict);
+    routes = ipdict_get_routelist(entity_dict);
     if (routes == NULL) {
-       goto done;
+       // if no routes
+       return FALSE;
+    }
+
+    if ((routes->flags & kRouteListFlagsHasDefault) == 0) {
+       // if service has no default route
+       return FALSE;
     }
+
     if ((routes->flags & kRouteListFlagsExcludeNWI) != 0) {
-       goto done;
+       // if service should be excluded from NWI
+       return FALSE;
     }
-    contains_protocol = TRUE;
 
- done:
-    return (contains_protocol);
+    return TRUE;
+}
+
+
+__private_extern__ boolean_t
+service_contains_protocol(CFDictionaryRef service_dict, int af)
+{
+    boolean_t          contains_protocol;
+    CFStringRef                entity;
+    CFDictionaryRef    entity_dict;
+
+    entity = (af == AF_INET) ? kSCEntNetIPv4 : kSCEntNetIPv6;
+    entity_dict = CFDictionaryGetValue(service_dict, entity);
+    if (entity_dict == NULL) {
+       return FALSE;
+    }
+
+    contains_protocol = entity_routes_protocol(entity_dict);
+    return contains_protocol;
 }
 
 
@@ -4598,9 +4615,8 @@ order_dns_servers(CFArrayRef servers, ProtocolFlags active_protos)
            if (((proto == kProtocolFlagsIPv4) && (v4_n == 1)) ||
                ((proto == kProtocolFlagsIPv6) && (v6_n == 1))) {
                /* if we now have the 1st server address of another protocol */
-               favor_v4 = (sa_dst_compare_no_stats((struct sockaddr *)&v4_dns1,
-                                                   (struct sockaddr *)&v6_dns1,
-                                                   0) >= 0);
+               favor_v4 = (sa_dst_compare_no_dependencies((struct sockaddr *)&v4_dns1,
+                                                          (struct sockaddr *)&v6_dns1) >= 0);
 #ifdef TEST_DNS_ORDER
                char v4_buf[INET_ADDRSTRLEN];
                char v6_buf[INET6_ADDRSTRLEN];
@@ -4754,7 +4770,7 @@ get_dns_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
     }
 
     ipv4 = service_dict_get(serviceID, kSCEntNetIPv4);
-    if (ipv4 != NULL) {
+    if (entity_routes_protocol(ipv4)) {
        if (get_service_setup_entity(info, serviceID, kSCEntNetIPv4) != NULL) {
            have_setup = TRUE;
        }
@@ -4763,10 +4779,9 @@ get_dns_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
     }
 
     ipv6 = service_dict_get(serviceID, kSCEntNetIPv6);
-    if (ipv6 != NULL) {
-       if (!have_setup
-           && (get_service_setup_entity(info, serviceID, kSCEntNetIPv6)
-               != NULL)) {
+    if (entity_routes_protocol(ipv6)) {
+       if (!have_setup &&
+           (get_service_setup_entity(info, serviceID, kSCEntNetIPv6) != NULL)) {
            have_setup = TRUE;
        }
        active_protos |= kProtocolFlagsIPv6;
@@ -5014,12 +5029,12 @@ get_proxies_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
        goto done;
     }
     ipv4 = service_dict_get(serviceID, kSCEntNetIPv4);
-    if (ipdict_get_routelist(ipv4) != NULL) {
+    if (entity_routes_protocol(ipv4)) {
        active_protos |= kProtocolFlagsIPv4;
        interface = ipdict_get_ifname(ipv4);
     }
     ipv6 = service_dict_get(serviceID, kSCEntNetIPv6);
-    if (ipdict_get_routelist(ipv6) != NULL) {
+    if (entity_routes_protocol(ipv6)) {
        active_protos |= kProtocolFlagsIPv6;
        if (interface == NULL) {
            interface = ipdict_get_ifname(ipv6);
index 85e47278fd048271227474c5b6a984dc0a1b2ca0..7fdb1b09a625d1eba24c80c57dd270451c9fef9d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2016 Apple Inc.  All Rights Reserved.
+ * Copyright (c) 2012-2017 Apple Inc.  All Rights Reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
@@ -74,10 +74,10 @@ const char *
 my_if_indextoname(unsigned int idx, char if_name[IFNAMSIZ]);
 
 boolean_t
-service_contains_protocol(CFDictionaryRef service, int af);
+service_contains_protocol(CFDictionaryRef service_dict, int af);
 
 boolean_t
-service_is_scoped_only(CFDictionaryRef service);
+service_is_scoped_only(CFDictionaryRef service_dict);
 
 boolean_t
 check_if_service_expensive(CFStringRef serviceID);
index 82ae9858cc0c8726949b789f32ca162e36ad91d9..68c9942e5d9fe64fead3804317a17eae13dcded0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2001-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
 #include <SystemConfiguration/SCPrivate.h>
 #include <SystemConfiguration/SCValidation.h>
 #include "plugin_shared.h"
+#if    !TARGET_OS_IPHONE
+#include "InterfaceNamerControlPrefs.h"
+#endif // !TARGET_OS_IPHONE
 
 #include <IOKit/IOKitLib.h>
 #include <IOKit/IOKitLibPrivate.h>
+#include <IOKit/IOKitKeysPrivate.h>
 #include <IOKit/IOBSD.h>
 #include <IOKit/IOMessage.h>
 #include <IOKit/network/IONetworkController.h>
@@ -1466,6 +1470,96 @@ builtinCount(CFArrayRef if_list, CFIndex last, CFNumberRef if_type)
     return n;
 }
 
+#if    !TARGET_OS_IPHONE
+static boolean_t
+blockNewInterfaces()
+{
+    static boolean_t       allow       = TRUE;
+    static dispatch_once_t  once;
+
+    dispatch_once(&once, ^{
+       allow = InterfaceNamerControlPrefsAllowNewInterfaces();
+    });
+
+    return !allow;
+}
+
+
+static boolean_t
+isConsoleLocked()
+{
+    CFArrayRef         console_sessions;
+    boolean_t          locked              = FALSE;
+    io_registry_entry_t        root;
+
+    root = IORegistryGetRootEntry(kIOMasterPortDefault);
+    console_sessions = IORegistryEntryCreateCFProperty(root,
+                                                      CFSTR(kIOConsoleUsersKey),
+                                                      NULL,
+                                                      0);
+    if (isA_CFArray(console_sessions)) {
+       CFIndex n;
+
+       n = CFArrayGetCount(console_sessions);
+       for (CFIndex i = 0; i < n; i++) {
+           CFBooleanRef        isLocked;
+           CFBooleanRef        isLoginDone;
+           CFBooleanRef        onConsole;
+           CFDictionaryRef     session;
+
+           session = CFArrayGetValueAtIndex(console_sessions, i);
+           if (!isA_CFDictionary(session)) {
+               // if not dictionary
+               continue;
+           }
+
+           if (!CFDictionaryGetValueIfPresent(session,
+                                              CFSTR(kIOConsoleSessionOnConsoleKey),
+                                              (const void **)&onConsole) ||
+               !isA_CFBoolean(onConsole) ||
+               !CFBooleanGetValue(onConsole)) {
+               // if not "on console" session
+               continue;
+           }
+
+           if ((n > 1) &&
+               CFDictionaryGetValueIfPresent(session,
+                                             CFSTR(kIOConsoleSessionLoginDoneKey),
+                                             (const void **)&isLoginDone) &&
+               isA_CFBoolean(isLoginDone) &&
+               !CFBooleanGetValue(isLoginDone)) {
+               // if @ loginwindow
+               SC_log(LOG_INFO, "multiple sessions, console @ loginwindow");
+               locked = TRUE;
+               goto done;
+           }
+
+           if (CFDictionaryGetValueIfPresent(session,
+                                             CFSTR(kIOConsoleSessionScreenIsLockedKey),
+                                             (const void **)&isLocked) &&
+               isA_CFBoolean(isLocked) &&
+               CFBooleanGetValue(isLocked)) {
+               // if screen locked
+               SC_log(LOG_INFO, "console screen locked");
+               locked = TRUE;
+               goto done;
+           }
+       }
+    }
+
+    SC_log(LOG_INFO, "console not locked");
+
+  done :
+
+    if (console_sessions != NULL) {
+       CFRelease(console_sessions);
+    }
+    IOObjectRelease(root);
+
+    return locked;
+}
+#endif // !TARGET_OS_IPHONE
+
 static __inline__ boolean_t
 isQuiet(void)
 {
@@ -1537,6 +1631,26 @@ nameInterfaces(CFMutableArrayRef if_list)
                                                 if_list,
                                                 i + 1,
                                                 is_builtin ? kCFBooleanTrue : kCFBooleanFalse);
+
+#if    !TARGET_OS_IPHONE
+               if (!is_builtin &&
+                   (dbdict != NULL) &&
+                   blockNewInterfaces() &&
+                   !_SCNetworkInterfaceIsApplePreconfigured(interface) &&
+                   isConsoleLocked()) {
+                   CFDataRef       addr;
+
+                   // if new (but matching) interface and console locked, ignore
+                   SC_log(LOG_NOTICE, "Console locked, network interface* ignored");
+                   SC_log(LOG_INFO, "  path = %@", path);
+                   addr = _SCNetworkInterfaceGetHardwareAddress(interface);
+                   if (addr != NULL) {
+                       SC_log(LOG_INFO, "  addr = %@", addr);
+                   }
+                   continue;
+               }
+#endif // !TARGET_OS_IPHONE
+
                if (dbdict != NULL) {
                    unit = CFDictionaryGetValue(dbdict, CFSTR(kIOInterfaceUnit));
                    CFRetain(unit);
@@ -1579,6 +1693,25 @@ nameInterfaces(CFMutableArrayRef if_list)
                    }
                }
 
+#if    !TARGET_OS_IPHONE
+               if (!is_builtin &&
+                   (unit == NULL) &&
+                   blockNewInterfaces() &&
+                   !_SCNetworkInterfaceIsApplePreconfigured(interface) &&
+                   isConsoleLocked()) {
+                   CFDataRef       addr;
+
+                   // if new interface and console locked, ignore
+                   SC_log(LOG_NOTICE, "Console locked, network interface ignored");
+                   SC_log(LOG_INFO, "  path = %@", path);
+                   addr = _SCNetworkInterfaceGetHardwareAddress(interface);
+                   if (addr != NULL) {
+                       SC_log(LOG_INFO, "  addr = %@", addr);
+                   }
+                   continue;
+               }
+#endif // !TARGET_OS_IPHONE
+
                if (unit == NULL) {
                    // not built-in (or built-in unit not available), allocate from
                    // the non-reserved slots
index 8a091e2dffedd76f63af03cdb80666509a20cd98..52e7fe910200a04953fff3f3d672aa2279e3be28 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2008, 2010, 2012-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2008, 2010, 2012-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
@@ -60,7 +60,8 @@ static SCDynamicStoreRef      store                   = NULL;
 /* InterfaceNamer[.plugin] monitoring globals */
 Boolean                                haveConfiguration       = FALSE;
 static CFStringRef             namerKey                = NULL;
-static CFArrayRef              preconfigured           = NULL;
+static CFMutableArrayRef       preconfigured_names     = NULL;         // of CFStringRef (BSD name)
+static CFMutableArrayRef       preconfigured_interfaces= NULL;         // of SCNetworkInterfaceRef
 
 /* KernelEventMonitor[.plugin] monitoring globals */
 static CFStringRef             interfacesKey           = NULL;
@@ -408,40 +409,20 @@ static void
 storeCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
 {
        CFDictionaryRef dict;
-       CFArrayRef      interfaces      = NULL;
        Boolean         quiet           = FALSE;
        Boolean         timeout         = FALSE;
        Boolean         updated         = FALSE;
 
-       /*
-        * Capture/process KernelEventMonitor[.bundle] info
-        * 1. get list of active network interfaces
-        */
-       dict = SCDynamicStoreCopyValue(store, interfacesKey);
-       if (dict != NULL) {
-               if (isA_CFDictionary(dict)) {
-                       interfaces = CFDictionaryGetValue(dict, kSCPropNetInterfaces);
-                       interfaces = isA_CFArray(interfaces);
-                       if (interfaces != NULL) {
-                               CFRetain(interfaces);
-                       }
-               }
-
-               CFRelease(dict);
-       }
-
        /*
         * Capture/process InterfaceNamer[.bundle] info
         * 1. check if IORegistry "quiet", "timeout"
-        * 2. get list of named pre-configured interfaces
-        * 3. merge list of active interfaces (from KEV) with the
-        *    list of preconfigured interfaces.
+        * 2. update list of named pre-configured interfaces
         */
        dict = SCDynamicStoreCopyValue(store, namerKey);
        if (dict != NULL) {
                if (isA_CFDictionary(dict)) {
-                       CFArrayRef              cur_preconfigured;
-                       CFMutableArrayRef       new_preconfigured       = NULL;
+                       CFIndex         n;
+                       CFArrayRef      preconfigured;
 
                        if (CFDictionaryContainsKey(dict, kInterfaceNamerKey_Quiet)) {
                                quiet = TRUE;
@@ -450,56 +431,82 @@ storeCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
                                timeout = TRUE;
                        }
 
-                       cur_preconfigured = CFDictionaryGetValue(dict, kInterfaceNamerKey_PreConfiguredInterfaces);
-                       cur_preconfigured = isA_CFArray(cur_preconfigured);
-                       if ((cur_preconfigured != NULL) && (interfaces != NULL)) {
-                               CFIndex         i;
-                               CFIndex         n;
-                               CFRange         r       = CFRangeMake(0, CFArrayGetCount(interfaces));
+                       preconfigured = CFDictionaryGetValue(dict, kInterfaceNamerKey_PreConfiguredInterfaces);
+                       preconfigured = isA_CFArray(preconfigured);
 
-                               n = CFArrayGetCount(cur_preconfigured);
-                               for (i = 0; i < n; i++) {
-                                       CFStringRef     bsdName;
+                       n = (preconfigured != NULL) ? CFArrayGetCount(preconfigured) : 0;
+                       for (CFIndex i = 0; i < n; i++) {
+                               CFStringRef             bsdName  = CFArrayGetValueAtIndex(preconfigured, i);
+                               SCNetworkInterfaceRef   interface;
+                               CFRange                 range;
 
-                                       bsdName = CFArrayGetValueAtIndex(cur_preconfigured, i);
-                                       if (!CFArrayContainsValue(interfaces, r, bsdName)) {
-                                               // if interface not currently active
-                                               continue;
+                               range.location = 0;
+                               range.length   = (preconfigured_names != NULL) ? CFArrayGetCount(preconfigured_names) : 0;
+                               if ((range.length > 0) &&
+                                   CFArrayContainsValue(preconfigured_names, range, bsdName)) {
+                                       // if we already know about this interface
+                                       continue;
+                               }
+
+                               for (int retry = 0; retry < 10; retry++) {
+                                       if (retry != 0) {
+                                               // add short delay (before retry)
+                                               usleep(20 * 1000);      // 20ms
+                                       }
+
+                                       interface = _SCNetworkInterfaceCreateWithBSDName(NULL, bsdName, kIncludeNoVirtualInterfaces);
+                                       if (interface == NULL) {
+                                               SC_log(LOG_ERR, "could not create network interface for %@", bsdName);
+                                       } else if (_SCNetworkInterfaceGetIOPath(interface) == NULL) {
+                                               SC_log(LOG_ERR, "could not get IOPath for %@", bsdName);
+                                               CFRelease(interface);
+                                               interface = NULL;
                                        }
 
-                                       if (new_preconfigured == NULL) {
-                                               new_preconfigured = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+                                       if (interface != NULL) {
+                                               // if we have an interface
+                                               break;
                                        }
-                                       CFArrayAppendValue(new_preconfigured, bsdName);
                                }
-                       }
 
-                       if (!_SC_CFEqual(preconfigured, new_preconfigured)) {
-                               SC_log(LOG_INFO, "pre-configured interface list changed");
+                               if (interface == NULL) {
+                                       // if SCNetworkInterface not [currently] available
+                                       continue;
+                               }
 
-                               if (preconfigured != NULL) {
-                                       CFRelease(preconfigured);
+                               // keep track of the interface name (quicker than having to iterate the list
+                               // of SCNetworkInterfaces, extract the name, and compare).
+                               if (preconfigured_names == NULL) {
+                                       preconfigured_names = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
                                }
-                               if (new_preconfigured != NULL) {
-                                       CFRetain(new_preconfigured);
+                               CFArrayAppendValue(preconfigured_names, bsdName);
+
+                               if (preconfigured_interfaces == NULL) {
+                                       preconfigured_interfaces = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
                                }
-                               preconfigured = new_preconfigured;
+                               CFArrayAppendValue(preconfigured_interfaces, interface);
+                               CFRelease(interface);
 
                                updated = TRUE;
                        }
 
-                       if (new_preconfigured != NULL) {
-                               CFRelease(new_preconfigured);
+                       if (updated) {
+                               CFStringRef     interfaces      = CFSTR("<empty>");
+
+                               // report [new] pre-configured interfaces
+                               if (preconfigured_names != NULL) {
+                                       interfaces = CFStringCreateByCombiningStrings(NULL, preconfigured_names, CFSTR(","));
+                               } else {
+                                       CFRetain(interfaces);
+                               }
+                               SC_log(LOG_INFO, "pre-configured interface list changed: %@", interfaces);
+                               CFRelease(interfaces);
                        }
                }
 
                CFRelease(dict);
        }
 
-       if (interfaces != NULL) {
-               CFRelease(interfaces);
-       }
-
        if (!haveConfiguration && (quiet || timeout)) {
                static int      logged  = 0;
 
@@ -736,7 +743,7 @@ updatePreConfiguredConfiguration(SCPreferencesRef prefs)
        SCNetworkSetRef set;
        Boolean         updated = FALSE;
 
-       range.length = (preconfigured != NULL) ? CFArrayGetCount(preconfigured) : 0;
+       range.length = (preconfigured_names != NULL) ? CFArrayGetCount(preconfigured_names) : 0;
        if (range.length == 0) {
                // if no [preconfigured] interfaces
                return;
@@ -748,10 +755,10 @@ updatePreConfiguredConfiguration(SCPreferencesRef prefs)
                CFArrayRef      services;
 
                /*
-                * Check for (and remove) and network services associated with
+                * Check for (and remove) any network services associated with
                 * a pre-configured interface from the prefs.
                 */
-               services = SCNetworkSetCopyServices(set);
+               services = SCNetworkServiceCopyAll(prefs);
                if (services != NULL) {
                        CFIndex         n;
 
@@ -775,14 +782,18 @@ updatePreConfiguredConfiguration(SCPreferencesRef prefs)
                                        continue;
                                }
 
-                               if (!CFArrayContainsValue(preconfigured, range, bsdName)) {
+                               if (!CFArrayContainsValue(preconfigured_names, range, bsdName)) {
                                        // if not preconfigured
                                        continue;
                                }
 
                                // remove [preconfigured] network service from the prefs
                                SC_log(LOG_NOTICE, "removing network service for %@", bsdName);
-                               SCNetworkServiceRemove(service);
+                               ok = SCNetworkServiceRemove(service);
+                               if (!ok) {
+                                       SC_log(LOG_ERR, "SCNetworkServiceRemove() failed: %s",
+                                              SCErrorString(SCError()));
+                               }
                                updated = TRUE;
                        }
 
@@ -794,7 +805,7 @@ updatePreConfiguredConfiguration(SCPreferencesRef prefs)
                        ok = SCPreferencesCommitChanges(prefs);
                        if (!ok) {
                                if (SCError() != EROFS) {
-                                       SC_log(LOG_NOTICE, "SCPreferencesCommitChanges() failed: %s",
+                                       SC_log(LOG_ERR, "SCPreferencesCommitChanges() failed: %s",
                                               SCErrorString(SCError()));
                                }
                        }
@@ -803,28 +814,18 @@ updatePreConfiguredConfiguration(SCPreferencesRef prefs)
                /*
                 * Now, add a new network service for each pre-configured interface
                 */
+               range.length = (preconfigured_interfaces != NULL) ? CFArrayGetCount(preconfigured_interfaces) : 0;
                for (CFIndex i = 0; i < range.length; i++) {
                        CFStringRef             bsdName;
-                       SCNetworkInterfaceRef   interface;
+                       SCNetworkInterfaceRef   interface       = CFArrayGetValueAtIndex(preconfigured_interfaces, i);
                        SCNetworkServiceRef     service;
 
-                       bsdName = CFArrayGetValueAtIndex(preconfigured, i);
-                       interface = _SCNetworkInterfaceCreateWithBSDName(NULL, bsdName, kIncludeNoVirtualInterfaces);
-                       if (interface == NULL) {
-                               SC_log(LOG_ERR, "could not create network interface for %@", bsdName);
-                               continue;
-                       }
-
-                       if (_SCNetworkInterfaceGetIOPath(interface) == NULL) {
-                               // if no [real] interface exists
-                               CFRelease(interface);
-                               continue;
-                       }
-
+                       bsdName = SCNetworkInterfaceGetBSDName(interface);
                        ok = SCNetworkSetEstablishDefaultInterfaceConfiguration(set, interface);
-                       CFRelease(interface);
                        if (!ok) {
-                               SC_log(LOG_ERR, "could not create network service for %@", bsdName);
+                               SC_log(LOG_ERR, "could not establish network service for %@: %s",
+                                      bsdName,
+                                      SCErrorString(SCError()));
                                continue;
                        }
 
diff --git a/Plugins/common/InterfaceNamerControlPrefs.c b/Plugins/common/InterfaceNamerControlPrefs.c
new file mode 100644 (file)
index 0000000..c559026
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2017 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@
+ */
+
+/*
+ * InterfaceNamerControlPrefs.c
+ * - definitions for accessing InterfaceNamer control preferences
+ */
+
+/*
+ * Modification History
+ *
+ * January 12, 2017    Allan Nathanson (ajn@apple.com)
+ * - created
+ */
+
+#include <TargetConditionals.h>
+#include <SystemConfiguration/SystemConfiguration.h>
+#include <SystemConfiguration/SCPrivate.h>
+#include <SystemConfiguration/scprefs_observer.h>
+#include "InterfaceNamerControlPrefs.h"
+
+os_log_t       __log_InterfaceNamer();
+
+/*
+ * kInterfaceNamerControlPrefsID
+ * - identifies the InterfaceNamer preferences file that contains 'AllowNewInterfaces'
+ */
+#define kInterfaceNamerControlPrefsIDStr       "com.apple.InterfaceNamer.control.plist"
+#define kInterfaceNamerControlPrefsID          CFSTR(kInterfaceNamerControlPrefsIDStr)
+
+/*
+ * kAllowNewInterfaces
+ * - indicates whether InterfaceNamer is allowed to create new interfaces
+ *   while the screen is locked or not
+ */
+#define kAllowNewInterfaces                    CFSTR("AllowNewInterfaces")
+
+static SCPreferencesRef                                S_prefs;
+static InterfaceNamerControlPrefsCallBack      S_callback;
+
+static SCPreferencesRef
+InterfaceNamerControlPrefsGet(void)
+{
+       if (S_prefs == NULL) {
+               InterfaceNamerControlPrefsInit(NULL, NULL);
+       }
+
+       return (S_prefs);
+}
+
+static void
+prefs_changed(void * arg)
+{
+#pragma unused(arg)
+       os_activity_t   activity;
+
+       activity = os_activity_create("processing InterfaceNamer preference change",
+                                     OS_ACTIVITY_CURRENT,
+                                     OS_ACTIVITY_FLAG_DEFAULT);
+       os_activity_scope(activity);
+
+       /* get the current value */
+       if (S_callback != NULL) {
+               (*S_callback)(S_prefs);
+       }
+
+       os_release(activity);
+
+       return;
+}
+
+#if    TARGET_OS_IPHONE
+/*
+ * kInterfaceNamerControlManangedPrefsID
+ * - identifies the location of the managed preferences file
+ */
+#define kManagedPrefsDirStr                    "/Library/Managed Preferences/mobile/"
+#define kInterfaceNamerControlManagedPrefsID   CFSTR(kManagedPrefsDirStr       \
+                                                       kInterfaceNamerControlPrefsIDStr)
+static SCPreferencesRef                                S_managed_prefs;
+
+static SCPreferencesRef
+InterfaceNamerControlManagedPrefsGet(void)
+{
+       if (S_managed_prefs == NULL) {
+               CFMutableDictionaryRef  options;
+
+               options = CFDictionaryCreateMutable(NULL,
+                                                   0,
+                                                   &kCFTypeDictionaryKeyCallBacks,
+                                                   &kCFTypeDictionaryValueCallBacks);
+               CFDictionarySetValue(options, kSCPreferencesOptionRemoveWhenEmpty, kCFBooleanTrue);
+               S_managed_prefs = SCPreferencesCreateWithOptions(NULL,
+                                                                CFSTR("InterfaceNamerControlPrefs"),
+                                                                kInterfaceNamerControlManagedPrefsID,
+                                                                NULL,
+                                                                options);
+               CFRelease(options);
+       }
+       return (S_managed_prefs);
+}
+
+static void
+enable_prefs_observer(CFRunLoopRef runloop)
+{
+       CFRunLoopSourceContext  context;
+       dispatch_queue_t        queue;
+       CFRunLoopSourceRef      source;
+
+       bzero(&context, sizeof(context));
+       context.perform = prefs_changed;
+       source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
+       CFRunLoopAddSource(runloop, source, kCFRunLoopCommonModes);
+       queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+       _scprefs_observer_watch(scprefs_observer_type_global,
+                               kInterfaceNamerControlPrefsIDStr,
+                               queue,
+                               ^{
+               if (source != NULL) {
+                       CFRunLoopSourceSignal(source);
+                       if (runloop != NULL) {
+                               CFRunLoopWakeUp(runloop);
+                       }
+               };
+       });
+       return;
+}
+
+#else  /* TARGET_OS_IPHONE */
+
+static void
+enable_prefs_observer(CFRunLoopRef runloop)
+{
+#pragma unused(runloop)
+       return;
+}
+
+#endif /* TARGET_OS_IPHONE */
+
+static void
+InterfaceNamerControlPrefsChanged(SCPreferencesRef             prefs,
+                                 SCPreferencesNotification     type,
+                                 void                          *info)
+{
+#pragma unused(prefs)
+#pragma unused(type)
+#pragma unused(info)
+    prefs_changed(NULL);
+    return;
+}
+
+__private_extern__ SCPreferencesRef
+InterfaceNamerControlPrefsInit(CFRunLoopRef                            runloop,
+                              InterfaceNamerControlPrefsCallBack       callback)
+{
+       CFMutableDictionaryRef  options;
+
+       options = CFDictionaryCreateMutable(NULL,
+                                           0,
+                                           &kCFTypeDictionaryKeyCallBacks,
+                                           &kCFTypeDictionaryValueCallBacks);
+       CFDictionarySetValue(options, kSCPreferencesOptionRemoveWhenEmpty, kCFBooleanTrue);
+       S_prefs = SCPreferencesCreateWithOptions(NULL,
+                                                CFSTR("InterfaceNamerControlPrefs"),
+                                                kInterfaceNamerControlPrefsID,
+                                                NULL,
+                                                options);
+       CFRelease(options);
+
+       if ((runloop != NULL) && (callback != NULL)) {
+               S_callback = callback;
+               if (!SCPreferencesSetCallback(S_prefs, InterfaceNamerControlPrefsChanged, NULL)) {
+                       SC_log(LOG_NOTICE, "SCPreferencesSetCallBack() failed: %s", SCErrorString(SCError()));
+                       goto done;
+               }
+
+               if (!SCPreferencesScheduleWithRunLoop(S_prefs, runloop, kCFRunLoopCommonModes)) {
+                       SC_log(LOG_NOTICE, "SCPreferencesScheduleWithRunLoop() failed: %s", SCErrorString(SCError()));
+                       (void) SCPreferencesSetCallback(S_prefs, NULL, NULL);
+               }
+
+               enable_prefs_observer(runloop);
+       }
+
+    done :
+       return (S_prefs);
+}
+
+static Boolean
+InterfaceNamerControlPrefsSave(void)
+{
+       Boolean saved   = FALSE;
+
+       if (S_prefs != NULL) {
+               saved = SCPreferencesCommitChanges(S_prefs);
+               SCPreferencesSynchronize(S_prefs);
+       }
+       return (saved);
+}
+
+static CFBooleanRef
+prefs_get_boolean(CFStringRef key)
+{
+       CFBooleanRef            b       = NULL;
+       SCPreferencesRef        prefs;
+
+#if    TARGET_OS_IPHONE
+       prefs = InterfaceNamerControlManagedPrefsGet();
+       if (prefs != NULL) {
+               b = SCPreferencesGetValue(prefs, key);
+               b = isA_CFBoolean(b);
+               if (b != NULL) {
+                       return (b);
+               }
+       }
+#endif /* TARGET_OS_IPHONE */
+
+       prefs = InterfaceNamerControlPrefsGet();
+       if (prefs != NULL) {
+               b = SCPreferencesGetValue(prefs, key);
+               b = isA_CFBoolean(b);
+       }
+       return (b);
+}
+
+static void
+prefs_set_boolean(CFStringRef key, CFBooleanRef b)
+{
+       SCPreferencesRef        prefs;
+
+       prefs = InterfaceNamerControlPrefsGet();
+       if (prefs != NULL) {
+               if (isA_CFBoolean(b) == NULL) {
+                       SCPreferencesRemoveValue(prefs, key);
+               }
+               else {
+                       SCPreferencesSetValue(prefs, key, b);
+               }
+       }
+       return;
+}
+
+static void
+InterfaceNamerControlPrefsRefresh(void)
+{
+       if (S_prefs != NULL) {
+               SCPreferencesSynchronize(S_prefs);
+       }
+#if    TARGET_OS_IPHONE
+       if (S_managed_prefs != NULL) {
+               SCPreferencesSynchronize(S_managed_prefs);
+       }
+#endif /* TARGET_OS_IPHONE */
+       return;
+}
+
+/**
+ ** Get
+ **/
+__private_extern__ Boolean
+InterfaceNamerControlPrefsAllowNewInterfaces(void)
+{
+       CFBooleanRef    b;
+       Boolean         allow   = FALSE;
+
+       b = prefs_get_boolean(kAllowNewInterfaces);
+       if (b != NULL) {
+               allow = CFBooleanGetValue(b);
+       }
+       /* flush the backing store */
+       InterfaceNamerControlPrefsRefresh();
+       return (allow);
+}
+
+/**
+ ** Set
+ **/
+__private_extern__ Boolean
+InterfaceNamerControlPrefsSetAllowNewInterfaces(Boolean allow)
+{
+       if (allow) {
+               prefs_set_boolean(kAllowNewInterfaces, kCFBooleanTrue);
+       } else {
+               prefs_set_boolean(kAllowNewInterfaces, NULL);
+       }
+       return (InterfaceNamerControlPrefsSave());
+}
diff --git a/Plugins/common/InterfaceNamerControlPrefs.h b/Plugins/common/InterfaceNamerControlPrefs.h
new file mode 100644 (file)
index 0000000..51daaeb
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2017 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@
+ */
+
+#ifndef _INTERFACENAMERCONTROLPREFS_H
+#define _INTERFACENAMERCONTROLPREFS_H
+
+/*
+ * InterfaceNamerControlPrefs.h
+ * - definitions for accessing InterfaceNamer control preferences
+ */
+
+/*
+ * Modification History
+ *
+ * January 12, 2017    Allan Nathanson (ajn@apple.com)
+ * - created
+ */
+
+typedef void (*InterfaceNamerControlPrefsCallBack)(SCPreferencesRef prefs);
+
+SCPreferencesRef
+InterfaceNamerControlPrefsInit (CFRunLoopRef                           runloop,
+                                InterfaceNamerControlPrefsCallBack     callback);
+
+Boolean
+InterfaceNamerControlPrefsAllowNewInterfaces   (void);
+
+Boolean
+InterfaceNamerControlPrefsSetAllowNewInterfaces        (Boolean                verbose);
+
+#endif /* _INTERFACENAMERCONTROLPREFS_H */
index 678ab935944de60fc11909261f4dca65a9c468d1..d0d43f622b40fcb516cf7ff54057d4932004baf2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2007-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
@@ -38,6 +38,7 @@
 #include <SystemConfiguration/SCPrivate.h>
 
 #include <IOKit/IOKitLib.h>
+#include <IOKit/IOKitKeysPrivate.h>
 #include <IOKit/IOMessage.h>
 #include <ApplicationServices/ApplicationServices.h>
 #include "UserEventAgentInterface.h"
@@ -549,6 +550,67 @@ notify_configure(MyType *myInstance)
 }
 
 
+#pragma mark -
+
+static Boolean
+onConsole()
+{
+       CFArrayRef              console_sessions;
+       Boolean                 on              = FALSE;
+       io_registry_entry_t     root;
+       uid_t                   uid             = geteuid();
+
+       root = IORegistryGetRootEntry(kIOMasterPortDefault);
+       console_sessions = IORegistryEntryCreateCFProperty(root,
+                                                          CFSTR(kIOConsoleUsersKey),
+                                                          NULL,
+                                                          0);
+       if (console_sessions != NULL) {
+               CFIndex n;
+
+               n = isA_CFArray(console_sessions) ? CFArrayGetCount(console_sessions) : 0;
+               for (CFIndex i = 0; i < n; i++) {
+                       CFBooleanRef    bVal;
+                       CFDictionaryRef session;
+                       uint64_t        sessionUID;
+                       CFNumberRef     val;
+
+                       session = CFArrayGetValueAtIndex(console_sessions, i);
+                       if (!isA_CFDictionary(session)) {
+                               // if not dictionary
+                               continue;
+                       }
+
+                       if (!CFDictionaryGetValueIfPresent(session,
+                                                          CFSTR(kIOConsoleSessionUIDKey),
+                                                          (const void **)&val) ||
+                           !isA_CFNumber(val) ||
+                           !CFNumberGetValue(val, kCFNumberSInt64Type, (void *)&sessionUID) ||
+                           (uid != sessionUID)) {
+                               // if not my session
+                               continue;
+                       }
+
+                       if (CFDictionaryGetValueIfPresent(session,
+                                                         CFSTR(kIOConsoleSessionOnConsoleKey),
+                                                         (const void **)&bVal) &&
+                           isA_CFBoolean(bVal) &&
+                           CFBooleanGetValue(bVal)) {
+                               // if "on console" session
+                               on = TRUE;
+                       }
+
+                       break;
+               }
+
+               CFRelease(console_sessions);
+       }
+       IOObjectRelease(root);
+
+       return on;
+}
+
+
 #pragma mark -
 
 
@@ -567,6 +629,10 @@ updateInterfaceList(MyType *myInstance)
        SCPreferencesRef        prefs;
        SCNetworkSetRef         set             = NULL;
 
+       if (!onConsole()) {
+               return;
+       }
+
        prefs = SCPreferencesCreate(NULL, CFSTR("SCMonitor"), NULL);
        if (prefs == NULL) {
                return;
index 2b8cc8312fdae90a557c0186119043d6240b966d..aaf33be8e27aa9c1b70b69d1bdc606efd773da5f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
@@ -364,7 +364,7 @@ SCBondInterfaceCopyAvailableMemberInterfaces(SCPreferencesRef prefs)
        }
 
        // identify available interfaces
-       interfaces = __SCNetworkInterfaceCopyAll_IONetworkInterface();
+       interfaces = __SCNetworkInterfaceCopyAll_IONetworkInterface(FALSE);
        if (interfaces != NULL) {
                CFIndex i;
                CFIndex n;
index 6abafabe33e469df9dd151a79be7775da81f8291..3d984a0746388cbc343a95069689eecb145f56e3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2009-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
@@ -350,7 +350,7 @@ SCBridgeInterfaceCopyAvailableMemberInterfaces(SCPreferencesRef prefs)
        }
 
        // identify available interfaces
-       interfaces = __SCNetworkInterfaceCopyAll_IONetworkInterface();
+       interfaces = __SCNetworkInterfaceCopyAll_IONetworkInterface(FALSE);
        if (interfaces != NULL) {
                CFIndex i;
                CFIndex n;
index fbc4a1e279da8858ef89c000d6967db4ab18aa72..8bce2fd009b8c58794ab6556fe610faa8a30fe2f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
@@ -211,7 +211,7 @@ Boolean
 __SCNetworkInterfaceMatchesName        (CFStringRef name, CFStringRef key);
 
 CFArrayRef
-__SCNetworkInterfaceCopyAll_IONetworkInterface (void);
+__SCNetworkInterfaceCopyAll_IONetworkInterface (Boolean                keep_pre_configured);
 
 /*!
  @function __SCNetworkInterfaceCopyStorageEntity
index 20791faf5da8493f21e6414f0932ba01fcd6e030..cdbbaeb74dba9498a5794b7d5e89284e9ca9f8b0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
@@ -1600,21 +1600,27 @@ processUSBInterface(SCNetworkInterfacePrivateRef        interfacePrivate,
 {
 #if    !TARGET_OS_SIMULATOR
        // capture USB info
-       interfacePrivate->usb.name = IORegistryEntrySearchCFProperty(interface,
-                                                                    kIOServicePlane,
-                                                                    CFSTR(kUSBProductString),
-                                                                    NULL,
-                                                                    kIORegistryIterateRecursively | kIORegistryIterateParents);
-       interfacePrivate->usb.vid  = IORegistryEntrySearchCFProperty(interface,
-                                                                    kIOServicePlane,
-                                                                    CFSTR(kUSBVendorID),
-                                                                    NULL,
-                                                                    kIORegistryIterateRecursively | kIORegistryIterateParents);
-       interfacePrivate->usb.pid  = IORegistryEntrySearchCFProperty(interface,
-                                                                    kIOServicePlane,
-                                                                    CFSTR(kUSBProductID),
-                                                                    NULL,
-                                                                    kIORegistryIterateRecursively | kIORegistryIterateParents);
+       if (interfacePrivate->usb.name == NULL) {
+               interfacePrivate->usb.name = IORegistryEntrySearchCFProperty(interface,
+                                                                            kIOServicePlane,
+                                                                            CFSTR(kUSBProductString),
+                                                                            NULL,
+                                                                            kIORegistryIterateRecursively | kIORegistryIterateParents);
+       }
+       if (interfacePrivate->usb.vid == NULL) {
+               interfacePrivate->usb.vid  = IORegistryEntrySearchCFProperty(interface,
+                                                                            kIOServicePlane,
+                                                                            CFSTR(kUSBVendorID),
+                                                                            NULL,
+                                                                            kIORegistryIterateRecursively | kIORegistryIterateParents);
+       }
+       if (interfacePrivate->usb.pid == NULL) {
+               interfacePrivate->usb.pid  = IORegistryEntrySearchCFProperty(interface,
+                                                                            kIOServicePlane,
+                                                                            CFSTR(kUSBProductID),
+                                                                            NULL,
+                                                                            kIORegistryIterateRecursively | kIORegistryIterateParents);
+       }
 #endif // !TARGET_OS_SIMULATOR
 
        return;
@@ -1738,7 +1744,7 @@ processNetworkInterface(SCNetworkInterfacePrivateRef      interfacePrivate,
            CFNumberGetValue(num, kCFNumberIntType, &ift)) {
                interfacePrivate->type = CFRetain(num);
        } else {
-               SC_log(LOG_INFO, "no interface type");
+               SC_log(LOG_INFO, "no interface type: %@", interface_dict);
                return FALSE;
        }
 
@@ -4401,7 +4407,7 @@ _SCNetworkInterfaceCreateWithEntity(CFAllocatorRef                allocator,
 
 __private_extern__
 CFArrayRef
-__SCNetworkInterfaceCopyAll_IONetworkInterface(void)
+__SCNetworkInterfaceCopyAll_IONetworkInterface(Boolean keep_pre_configured)
 {
        CFDictionaryRef         matching;
        CFArrayRef              new_interfaces;
@@ -4412,7 +4418,7 @@ __SCNetworkInterfaceCopyAll_IONetworkInterface(void)
        new_interfaces = findMatchingInterfaces(matching,
                                                processNetworkInterface,
                                                kSCNetworkInterfaceHiddenInterfaceKey,
-                                               FALSE);
+                                               keep_pre_configured);
        CFRelease(matching);
 
        return new_interfaces;
@@ -4611,7 +4617,7 @@ _SCNetworkInterfaceCopyAllWithPreferences(SCPreferencesRef prefs)
        all_interfaces = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
 
        // get Ethernet, Firewire, Thunderbolt, and AirPort interfaces
-       new_interfaces = __SCNetworkInterfaceCopyAll_IONetworkInterface();
+       new_interfaces = __SCNetworkInterfaceCopyAll_IONetworkInterface(FALSE);
        if (new_interfaces != NULL) {
                add_interfaces(all_interfaces, new_interfaces);
                CFRelease(new_interfaces);
index 411c7e790f28def3f74c9e87b7e1e2e580023e7b..3d4c53fbd0acf92abd820f630f88c2fb98d2d11e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2014-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
@@ -354,9 +354,7 @@ __SCNetworkCreateDefaultNIPrefs(CFStringRef prefsID)
        SCPreferencesRef ni_prefs;
        CFComparisonResult res;
 
-
-       networkInterfaces = __SCNetworkInterfaceCopyAll_IONetworkInterface();
-
+       networkInterfaces = __SCNetworkInterfaceCopyAll_IONetworkInterface(TRUE);
        if (networkInterfaces == NULL) {
                SC_log(LOG_NOTICE, "networkInterfaces is NULL");
                return NULL;
@@ -515,11 +513,12 @@ _SCNetworkConfigurationPerformMigration(CFURLRef sourceDir, CFURLRef currentDir,
        else {
                migrationComplete = _SCNetworkConfigurationMigrateConfiguration(sourceDirConfig, targetDirConfig);
        }
-       SC_log(LOG_NOTICE, "Migration %s", migrationComplete ? "complete" : "failed");
        if (migrationComplete) {
+               SC_log(LOG_NOTICE, "Migration complete");
                paths = _SCNetworkConfigurationCopyMigrationPaths(NULL);
-       }
-       else {
+       } else {
+               SC_log(LOG_NOTICE, "Migration failed: %s", SCErrorString(SCError()));
+
                // If migration fails, then remove configuration files from target config if they are
                // copied from the current directory
                if (removeTargetOnFailure) {
@@ -2061,6 +2060,13 @@ done:
        if (currentSourceSet != NULL) {
                CFRelease(currentSourceSet);
        }
+
+       if (setMapping != NULL) {
+               SC_log(LOG_NOTICE, "Set mapping: %@", setMapping);
+       } else {
+               SC_log(LOG_INFO, "Set mapping: NULL");
+       }
+
        return setMapping;
 }
 
@@ -2235,6 +2241,13 @@ done:
        if (targetSCNetworkServicesMutable != NULL) {
                CFRelease(targetSCNetworkServicesMutable);
        }
+
+       if (serviceMapping != NULL) {
+               SC_log(LOG_NOTICE, "Service mapping: %@", serviceMapping);
+       } else {
+               SC_log(LOG_INFO, "Service mapping: NULL");
+       }
+
        return serviceMapping;
 }
 
@@ -3446,12 +3459,9 @@ _SCNetworkConfigurationMigrateConfiguration(CFURLRef sourceDir, CFURLRef targetD
                }
                SC_log(LOG_DEBUG, "BSD Name Mapping: %@", bsdNameMapping);
                serviceMapping = _SCNetworkMigrationCreateServiceMappingUsingBSDMapping(sourcePrefs, targetPrefs, bsdNameMapping);
-
-               if (isA_CFDictionary(serviceMapping) == NULL) {
-                       SC_log(LOG_INFO, "Service mapping is NULL");
+               if (serviceMapping == NULL) {
                        goto done;
                }
-               SC_log(LOG_NOTICE, "Service mapping: %@", serviceMapping);
 
                setMapping = _SCNetworkMigrationCreateSetMapping(sourcePrefs, targetPrefs);
                sourceServiceSetMapping = _SCNetworkMigrationCreateServiceSetMapping(sourcePrefs);
index 76afb7f1b2c72b6a8c263e3d4d1fa54a6253c418..4553e0e713cf286011809600c29d25003f229e9b 100644 (file)
@@ -752,6 +752,8 @@ SCNetworkReachabilityCreateWithOptions(CFAllocatorRef       allocator,
        SCNetworkReachabilityPrivateRef targetPrivate;
        unsigned int                    if_index = 0;
        char                            if_name[IFNAMSIZ];
+       CFDataRef                       sourceAppAuditToken     = NULL;
+       CFStringRef                     sourceAppBundleID       = NULL;
 
        if (!isA_CFDictionary(options)) {
                _SCErrorSet(kSCStatusInvalidArgument);
@@ -799,6 +801,23 @@ SCNetworkReachabilityCreateWithOptions(CFAllocatorRef      allocator,
                _SCErrorSet(kSCStatusInvalidArgument);
                return NULL;
        }
+       sourceAppAuditToken =
+               CFDictionaryGetValue(options, kSCNetworkReachabilityOptionSourceAppAuditToken);
+       if ((sourceAppAuditToken != NULL) &&
+               (!isA_CFData(sourceAppAuditToken) ||
+               (CFDataGetLength(sourceAppAuditToken) != sizeof(audit_token_t)))) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+       sourceAppBundleID =
+               CFDictionaryGetValue(options, kSCNetworkReachabilityOptionSourceAppBundleIdentifier);
+       if ((sourceAppBundleID != NULL) &&
+               (!isA_CFString(sourceAppBundleID) ||
+               (CFStringGetLength(sourceAppBundleID) == 0))) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
 
        if (nodename != NULL) {
                const char      *name;
@@ -866,6 +885,28 @@ SCNetworkReachabilityCreateWithOptions(CFAllocatorRef      allocator,
                haveOpt = TRUE;
        }
 
+       if (sourceAppAuditToken != NULL) {
+               audit_token_t atoken;
+               CFDataGetBytes(sourceAppAuditToken,
+                               CFRangeMake(0, CFDataGetLength(sourceAppAuditToken)),
+                               (UInt8 *)&atoken);
+               nw_parameters_set_source_application(targetPrivate->parameters, atoken);
+               haveOpt = TRUE;
+       } else if (sourceAppBundleID != NULL) {
+               char *cBundleID = _SC_cfstring_to_cstring(sourceAppBundleID,
+                                                               NULL,
+                                                               0,
+                                                               kCFStringEncodingUTF8);
+               if (cBundleID != NULL) {
+                       nw_parameters_set_source_application_by_bundle_id(targetPrivate->parameters,
+                                                                         cBundleID);
+                       CFAllocatorDeallocate(NULL, (void *)cBundleID);
+               } else {
+                       SC_log(LOG_WARNING, "failed to convert %@ to a C string", sourceAppBundleID);
+               }
+               haveOpt = TRUE;
+       }
+
        if (haveOpt) {
                const char      *opt    = "???";
 
@@ -1309,10 +1350,13 @@ SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef          target,
        pathEvaluator = nw_path_create_evaluator_for_endpoint(endpoint, targetPrivate->parameters);
        path = nw_path_evaluator_copy_path(pathEvaluator);
 
-       crazyIvanPath = __SCNetworkReachabilityCreateCrazyIvan46Path(path, endpoint, targetPrivate->parameters, FALSE);
-       if (NULL != crazyIvanPath) {
-               network_release(path);
-               path = crazyIvanPath;
+       if (isReachabilityTypeAddress(targetPrivate->type)) {
+               crazyIvanPath = __SCNetworkReachabilityCreateCrazyIvan46Path(path, endpoint,
+                                                                            targetPrivate->parameters, FALSE);
+               if (NULL != crazyIvanPath) {
+                       network_release(path);
+                       path = crazyIvanPath;
+               }
        }
 
        *flags = __SCNetworkReachabilityGetFlagsFromPath(path, 0, nw_resolver_status_invalid, NULL, FALSE, 0);
@@ -1629,11 +1673,22 @@ __SCNetworkReachabilityRestartResolver(SCNetworkReachabilityPrivateRef targetPri
        if (targetPrivate &&
            !targetPrivate->resolverBypass &&
            isReachabilityTypeName(targetPrivate->type)) {
+               nw_resolver_t resolver;
                CFRetain(targetPrivate);
                if (NULL != targetPrivate->resolver) {
                        nw_resolver_cancel(targetPrivate->resolver);
                }
-               nw_resolver_t resolver = nw_resolver_create_with_endpoint(__SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate), targetPrivate->lastPathParameters ? targetPrivate->lastPathParameters : targetPrivate->parameters);
+               if (targetPrivate->lastPath != NULL) {
+                       resolver = nw_resolver_create_with_path(targetPrivate->lastPath);
+               } else {
+                       resolver = nw_resolver_create_with_endpoint(__SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate), targetPrivate->lastPathParameters ? targetPrivate->lastPathParameters : targetPrivate->parameters);
+               }
+               if (resolver == NULL) {
+                       SC_log(LOG_ERR, "%sfailed to create a nw_resolver", targetPrivate->log_prefix);
+                       targetPrivate->resolver = NULL;
+                       CFRelease(targetPrivate);
+                       return;
+               }
                targetPrivate->resolver = resolver;
                nw_resolver_set_cancel_handler(resolver, ^(void) {
                        MUTEX_LOCK(&targetPrivate->lock);
@@ -1743,11 +1798,13 @@ __SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityPrivateRef targetPr
                network_release(targetPrivate->lastPath);
                targetPrivate->lastPath = nw_path_evaluator_copy_path(pathEvaluator);
 
-               crazyIvanPath = __SCNetworkReachabilityCreateCrazyIvan46Path(targetPrivate->lastPath, endpoint,
-                                                                            targetPrivate->parameters, FALSE);
-               if (NULL != crazyIvanPath) {
-                       network_release(targetPrivate->lastPath);
-                       targetPrivate->lastPath = crazyIvanPath;
+               if (isReachabilityTypeAddress(targetPrivate->type)) {
+                       crazyIvanPath = __SCNetworkReachabilityCreateCrazyIvan46Path(targetPrivate->lastPath, endpoint,
+                                                                                    targetPrivate->parameters, FALSE);
+                       if (NULL != crazyIvanPath) {
+                               network_release(targetPrivate->lastPath);
+                               targetPrivate->lastPath = crazyIvanPath;
+                       }
                }
 
                network_release(targetPrivate->lastPathParameters);
@@ -1781,13 +1838,16 @@ __SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityPrivateRef targetPr
                                network_release(targetPrivate->lastPath);
                                targetPrivate->lastPath = network_retain(path);
 
-                               crazyIvanPath = __SCNetworkReachabilityCreateCrazyIvan46Path(targetPrivate->lastPath,
+                               if (isReachabilityTypeAddress(targetPrivate->type)) {
+                                       crazyIvanPath =
+                                               __SCNetworkReachabilityCreateCrazyIvan46Path(targetPrivate->lastPath,
                                                                                             endpoint,
                                                                                             targetPrivate->parameters,
                                                                                             TRUE);
-                               if (NULL != crazyIvanPath) {
-                                       network_release(targetPrivate->lastPath);
-                                       targetPrivate->lastPath = crazyIvanPath;
+                                       if (NULL != crazyIvanPath) {
+                                               network_release(targetPrivate->lastPath);
+                                               targetPrivate->lastPath = crazyIvanPath;
+                                       }
                                }
 
                                if (targetPrivate->lastResolverStatus == nw_resolver_status_complete) {
index a9ae4e4ea1348337e6bf810e20655a22e228e9c6..60b30ab6a1134ad2673394a13425fde70649d9b3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
@@ -1728,7 +1728,7 @@ __SCNetworkSetEstablishDefaultConfigurationForInterfaces(SCNetworkSetRef set, CF
 
                                service = SCNetworkServiceCreate(setPrivate->prefs, interface);
                                if (service == NULL) {
-                                       SC_log(LOG_INFO, "could not create service for \"%@\": %s",
+                                       SC_log(LOG_ERR, "could not create service for \"%@\": %s",
                                               SCNetworkInterfaceGetLocalizedDisplayName(interface),
                                               SCErrorString(SCError()));
                                        ok = FALSE;
@@ -1737,7 +1737,7 @@ __SCNetworkSetEstablishDefaultConfigurationForInterfaces(SCNetworkSetRef set, CF
 
                                ok = SCNetworkServiceEstablishDefaultConfiguration(service);
                                if (!ok) {
-                                       SC_log(LOG_INFO, "could not estabish default configuration for \"%@\": %s",
+                                       SC_log(LOG_ERR, "could not estabish default configuration for \"%@\": %s",
                                               SCNetworkInterfaceGetLocalizedDisplayName(interface),
                                               SCErrorString(SCError()));
                                        SCNetworkServiceRemove(service);
@@ -1747,7 +1747,7 @@ __SCNetworkSetEstablishDefaultConfigurationForInterfaces(SCNetworkSetRef set, CF
 
                                ok = SCNetworkSetAddService(set, service);
                                if (!ok) {
-                                       SC_log(LOG_INFO, "could not add service for \"%@\": %s",
+                                       SC_log(LOG_ERR, "could not add service for \"%@\": %s",
                                               SCNetworkInterfaceGetLocalizedDisplayName(interface),
                                               SCErrorString(SCError()));
                                        SCNetworkServiceRemove(service);
index 94b2c9c759d1715c682a225c9762b1fa1a7238df..53f2234f9969e467e8cd8637f7f653b4a48304f5 100644 (file)
@@ -179,6 +179,7 @@ extern int  _sc_log;        /* 0 if SC messages should be written to stdout/stderr,
 
 
 
+
 /*!
        @group
  */
@@ -709,6 +710,18 @@ SCNetworkProxiesCopyMatchingWithOptions            (CFDictionaryRef        globalConfiguration,
 
 extern const CFStringRef       kSCProxiesNoGlobal;
 
+/*!
+ @function SCNetworkProxiesCreateProxyAgentData
+ @discussion
+
+       @param proxyConfig A dictionary representing a proxy configuration
+       @result returns a CFData representing a proxy configuration. This data is readable by all
+ "config-agents" (Agents with domain as "SystemConfig") via config_agent_copy_proxy_information().
+ You must release the returned value.
+ */
+CFDataRef
+SCNetworkProxiesCreateProxyAgentData(CFDictionaryRef proxyConfig)      __OSX_AVAILABLE_STARTING(__MAC_10_12,__IPHONE_10_0/*SPI*/);
+
 /*!
  @function SCDynamicStoreCopyProxiesWithOptions
  @discussion
@@ -730,7 +743,6 @@ extern const CFStringRef    kSCProxiesNoGlobal;
 CFDictionaryRef
 SCDynamicStoreCopyProxiesWithOptions(SCDynamicStoreRef store, CFDictionaryRef options) __OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_7_0/*SPI*/);
 
-
 #pragma mark -
 #pragma mark Reachability
 
index 4c6a60b1b0324d9b2921fe75e5650037cf0bc105..043a0faaf9cb2fe34753ec2127963130c5fb5e63 100644 (file)
@@ -680,6 +680,25 @@ _SCNetworkProxiesCopyMatchingInternal(CFDictionaryRef      globalConfiguration,
        return proxies;
 }
 
+CFDataRef
+SCNetworkProxiesCreateProxyAgentData(CFDictionaryRef proxyConfig)
+{
+       CFDataRef result = NULL;
+       CFArrayRef newProxy = NULL;
+
+       if (!isA_CFDictionary(proxyConfig)) {
+               SC_log(LOG_ERR, "Invalid proxy configuration");
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
+       newProxy = CFArrayCreate(NULL, (const void **)&proxyConfig, 1, &kCFTypeArrayCallBacks);
+       (void)_SCSerialize(newProxy, &result, NULL, NULL);
+       CFRelease(newProxy);
+
+       return result;
+}
+
 CFArrayRef
 SCNetworkProxiesCopyMatching(CFDictionaryRef   globalConfiguration,
                             CFStringRef        server,
index a357970b252991f1ee6998ee211831b77b4e6ec5..ac7e2be33ca60d750008665e95ea6fedb2cfd902 100644 (file)
@@ -213,6 +213,7 @@ const CFStringRef kSCValNetIPv6ConfigMethodManual                  = CFSTR("Manu
 const CFStringRef kSCValNetIPv6ConfigMethodRouterAdvertisement     = CFSTR("RouterAdvertisement");
 const CFStringRef kSCValNetIPv6ConfigMethod6to4                    = CFSTR("6to4");
 const CFStringRef kSCPropNetIPv6AdditionalRoutes                   = CFSTR("AdditionalRoutes");
+const CFStringRef kSCPropNetIPv6EnableCGA                          = CFSTR("EnableCGA");
 const CFStringRef kSCPropNetIPv6ExcludedRoutes                     = CFSTR("ExcludedRoutes");
 const CFStringRef kSCPropNetIPv6IncludedRoutes                     = CFSTR("IncludedRoutes");
 const CFStringRef kSCPropNetIPv6RouteDestinationAddress            = CFSTR("DestinationAddress");
index 9f8bd6dbf1707c649f2d8d61fd3ad00741b8b8fb..d6714c482186f9726789df1cbeb648a41dacef3a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
index 24542327b8113f3d10a7fce61d193e2d5c402b96..1efd836430dea1382f06a66717cb23bb4610f024 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
  * kSCEntNetIPv6 Entity Keys
  *
  *   kSCPropNetIPv6AdditionalRoutes                     "AdditionalRoutes"             CFArray[CFDictionary]
+ *   kSCPropNetIPv6EnableCGA                            "EnableCGA"                    CFNumber (0 or 1)
  *   kSCPropNetIPv6ExcludedRoutes                       "ExcludedRoutes"               CFArray[CFDictionary]
  *   kSCPropNetIPv6IncludedRoutes                       "IncludedRoutes"               CFArray[CFDictionary]
  *
@@ -720,6 +721,13 @@ extern const CFStringRef kSCPropNetIPv4ARPResolvedIPAddress                 __OS
 extern const CFStringRef kSCPropNetIPv6AdditionalRoutes                     __OSX_AVAILABLE_STARTING(__MAC_10_10,__IPHONE_8_0/*SPI*/);
 #define kSCPropNetIPv6AdditionalRoutes kSCPropNetIPv6AdditionalRoutes
 
+/*!
+  @const kSCPropNetIPv6EnableCGA
+  @discussion Value is a CFNumber (0 or 1)
+ */
+extern const CFStringRef kSCPropNetIPv6EnableCGA                            __OSX_AVAILABLE_STARTING(__MAC_10_12,__IPHONE_10_0/*SPI*/);
+#define kSCPropNetIPv6EnableCGA kSCPropNetIPv6EnableCGA
+
 /*!
   @const kSCPropNetIPv6ExcludedRoutes
   @discussion Value is a CFArray[CFDictionary]
index 8acf6a168d32385e828b3ae579e6bad6fd9b3cc7..d0d9c3016027ba4700700b820e40c43c81914f95 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2013, 2015, 2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2003-2013, 2015-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
@@ -311,7 +311,7 @@ SCVLANInterfaceCopyAvailablePhysicalInterfaces()
        }
 
        // add real interfaces that aren't part of a bond or bridge
-       interfaces = __SCNetworkInterfaceCopyAll_IONetworkInterface();
+       interfaces = __SCNetworkInterfaceCopyAll_IONetworkInterface(FALSE);
        if (interfaces != NULL) {
                addAvailableInterfaces(available, interfaces, excluded);
                CFRelease(interfaces);
index dfe409a3cfffaa3fd365c50b470e0d9a852c58ee..b01957ba0b99974736ea65ab55180bf77002bd56 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
@@ -62,7 +62,7 @@
 
 char copyright_string[] =
 "/*\n"
-" * Copyright (c) 2000-2016 Apple Inc. All rights reserved.\n"
+" * Copyright (c) 2000-2017 Apple Inc. All rights reserved.\n"
 " *\n"
 " * @APPLE_LICENSE_HEADER_START@\n"
 " *\n"
@@ -207,6 +207,7 @@ typedef enum {
 #define CCP                    "CCP"
 #define CELLULAR               "Cellular"
 #define CERTIFICATE            "Certificate"
+#define CGA                    "CGA"
 #define CHAP                   "CHAP"
 #define COMM                   "Comm"
 #define COMPATIBLE             "Compatible"
@@ -843,6 +844,7 @@ static schemaDefinition names[] = {
 
   { GROUP_PRIVATE, NETPROP IPV6, KEY_PREFIX NETENT IPV6 " Entity Keys", NULL, NULL },
     { SC_10_10_IPHONE_8_0_PRIVATE, NETPROP IPV6, ADDITIONAL ROUTES, NULL, CFARRAY_CFDICTIONARY },
+    { SC_10_12_IPHONE_10_0_PRIVATE, NETPROP IPV6, ENABLE CGA, NULL, CFNUMBER_BOOL },
     { SC_10_7_IPHONE_4_0_PRIVATE, NETPROP IPV6, EXCLUDED ROUTES, NULL, CFARRAY_CFDICTIONARY },
     { SC_10_7_IPHONE_4_0_PRIVATE, NETPROP IPV6, INCLUDED ROUTES, NULL, CFARRAY_CFDICTIONARY },
     { COMMENT_PRIVATE, "", NULL, NULL, NULL },
index 7671c6c2c6fd3213b5c71188d7de3efcb852195c..9b2fa45f266f4e78a8cce00e10bc8a421a3d2e0f 100644 (file)
 
 __BEGIN_DECLS
 
+#define kConfigAgentDomain                      "SystemConfig"
+
+#define kConfigAgentTypeProxy                   "ProxyAgent"
+#define kConfigAgentTypeDNS                     "DNSAgent"
+
 /*     
        Returns true for agent with type DNSAgent and domain SystemConfig
  */
index 0c0c3dfdd279d0cd9099672205ae82c134e4ec06..ddf7c4ac390dbea05d50fc0ce0ccc938a0716353 100644 (file)
@@ -48,6 +48,7 @@
                                1558480607550D470046C2E9 /* PBXTargetDependency */,
                                1558480807550D470046C2E9 /* PBXTargetDependency */,
                                D6DDAC3D147A24BC00A2E902 /* PBXTargetDependency */,
+                               72C12CB11D6EA2CA000EE61C /* PBXTargetDependency */,
                                150ECB300D0042DA0065E94D /* PBXTargetDependency */,
                        );
                        name = configd_executables;
                                158317660CFB80D5006F62B9 /* PBXTargetDependency */,
                                157434210D4A8166002ACA73 /* PBXTargetDependency */,
                                1574341F0D4A815E002ACA73 /* PBXTargetDependency */,
+                               7271EA341D7660980055B1AA /* PBXTargetDependency */,
                        );
                        name = "configd_executables-Embedded";
                        productName = configd_executables;
                1565D85018B847590097062B /* SCNetworkMigration.c in Sources */ = {isa = PBXBuildFile; fileRef = 55A3DB9D183C2A8200ED3DB7 /* SCNetworkMigration.c */; };
                1565D85118B847F20097062B /* SCNetworkMigration.c in Sources */ = {isa = PBXBuildFile; fileRef = 55A3DB9D183C2A8200ED3DB7 /* SCNetworkMigration.c */; };
                156BD6BC07E0DFA9008698FF /* SCPreferencesSetSpecificPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 156BD6BB07E0DFA9008698FF /* SCPreferencesSetSpecificPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; };
-               1572AA8C1D8235390021E093 /* plugin_shared.h in Headers */ = {isa = PBXBuildFile; fileRef = 1572AA8B1D8234500021E093 /* plugin_shared.h */; };
                1572AA8D1D8235940021E093 /* plugin_shared.h in Headers */ = {isa = PBXBuildFile; fileRef = 1572AA8B1D8234500021E093 /* plugin_shared.h */; };
                1572AA8E1D8235A40021E093 /* plugin_shared.h in Headers */ = {isa = PBXBuildFile; fileRef = 1572AA8B1D8234500021E093 /* plugin_shared.h */; };
                1572AA8F1D82375A0021E093 /* plugin_shared.h in Headers */ = {isa = PBXBuildFile; fileRef = 1572AA8B1D8234500021E093 /* plugin_shared.h */; };
                157A85290D56C91100B6F1A0 /* linkconfig.c in Sources */ = {isa = PBXBuildFile; fileRef = 159D53C107528B36004F8947 /* linkconfig.c */; };
                157A853F0D56C96F00B6F1A0 /* prefsmon.c in Sources */ = {isa = PBXBuildFile; fileRef = 159D53C307528B36004F8947 /* prefsmon.c */; };
                157A88890A470D0F003A4256 /* SCSchemaDefinitionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 157A88880A470D0F003A4256 /* SCSchemaDefinitionsPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               1581BCD21E28673600F69B1E /* InterfaceNamerControlPrefs.h in Headers */ = {isa = PBXBuildFile; fileRef = 15FB1F891E27E9A000B4F809 /* InterfaceNamerControlPrefs.h */; };
+               1581BCD31E28679A00F69B1E /* InterfaceNamerControlPrefs.h in Headers */ = {isa = PBXBuildFile; fileRef = 15FB1F891E27E9A000B4F809 /* InterfaceNamerControlPrefs.h */; };
+               1581BCD41E2867A300F69B1E /* IPMonitorControlPrefs.h in Headers */ = {isa = PBXBuildFile; fileRef = F9A3780F16A4846E00C57CDC /* IPMonitorControlPrefs.h */; };
+               1581BCD51E2867A500F69B1E /* IPMonitorControlPrefs.h in Headers */ = {isa = PBXBuildFile; fileRef = F9A3780F16A4846E00C57CDC /* IPMonitorControlPrefs.h */; };
+               1581BCD61E2867AF00F69B1E /* IPMonitorControlPrefs.h in Headers */ = {isa = PBXBuildFile; fileRef = F9A3780F16A4846E00C57CDC /* IPMonitorControlPrefs.h */; };
+               1581BCD71E2867B200F69B1E /* IPMonitorControlPrefs.h in Headers */ = {isa = PBXBuildFile; fileRef = F9A3780F16A4846E00C57CDC /* IPMonitorControlPrefs.h */; };
+               1581BCD81E2867BA00F69B1E /* IPMonitorControlPrefs.h in Headers */ = {isa = PBXBuildFile; fileRef = F9A3780F16A4846E00C57CDC /* IPMonitorControlPrefs.h */; };
+               1581BCD91E2867C100F69B1E /* IPMonitorControlPrefs.h in Headers */ = {isa = PBXBuildFile; fileRef = F9A3780F16A4846E00C57CDC /* IPMonitorControlPrefs.h */; };
+               1581BCDD1E286E0000F69B1E /* InterfaceNamerControlPrefs.h in Headers */ = {isa = PBXBuildFile; fileRef = 15FB1F891E27E9A000B4F809 /* InterfaceNamerControlPrefs.h */; };
                158317250CFB80A1006F62B9 /* configd.h in Headers */ = {isa = PBXBuildFile; fileRef = 15CB69CF05C0722B0099E85F /* configd.h */; };
                158317260CFB80A1006F62B9 /* _SCD.h in Headers */ = {isa = PBXBuildFile; fileRef = 15CB69D105C0722B0099E85F /* _SCD.h */; };
                158317270CFB80A1006F62B9 /* configd_server.h in Headers */ = {isa = PBXBuildFile; fileRef = 15CB69D305C0722B0099E85F /* configd_server.h */; };
                15A5A2610D5B94190087BDA0 /* SCNetworkSignature.c in Sources */ = {isa = PBXBuildFile; fileRef = F95B8A420B03E07A00993BA3 /* SCNetworkSignature.c */; };
                15A5A2630D5B94190087BDA0 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15CB6A6F05C0722B0099E85F /* CoreFoundation.framework */; };
                15A6F7C40A4B266D00B907EA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 15A6F7C20A4B266D00B907EA /* Localizable.strings */; };
+               15A9BDA81D8DEA67007024DB /* plugin_shared.h in Headers */ = {isa = PBXBuildFile; fileRef = 1572AA8B1D8234500021E093 /* plugin_shared.h */; };
                15AAA7F4108E310700C2A607 /* VPNTunnelPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 15AAA7F1108E310700C2A607 /* VPNTunnelPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; };
                15AAA7F5108E310700C2A607 /* VPNTunnel.h in Headers */ = {isa = PBXBuildFile; fileRef = 15AAA7F2108E310700C2A607 /* VPNTunnel.h */; settings = {ATTRIBUTES = (Private, ); }; };
                15AAA7F6108E310700C2A607 /* VPNTunnel.c in Sources */ = {isa = PBXBuildFile; fileRef = 15AAA7F3108E310700C2A607 /* VPNTunnel.c */; };
                15E1B05416EBAE3C00E5F06F /* scprefs_observer.c in Sources */ = {isa = PBXBuildFile; fileRef = D61AAEAD1522C99C0066B003 /* scprefs_observer.c */; };
                15E1B05516EBAE3C00E5F06F /* IPMonitorControlPrefs.c in Sources */ = {isa = PBXBuildFile; fileRef = F9A3780E16A4846E00C57CDC /* IPMonitorControlPrefs.c */; };
                15F21618110F823500E89CF7 /* libbsm.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 15BAA32207F0699A00D9EC95 /* libbsm.dylib */; };
+               15FB1F8A1E27EA8700B4F809 /* InterfaceNamerControlPrefs.c in Sources */ = {isa = PBXBuildFile; fileRef = 15FB1F881E27E9A000B4F809 /* InterfaceNamerControlPrefs.c */; };
+               15FB1F8B1E27EA8900B4F809 /* InterfaceNamerControlPrefs.c in Sources */ = {isa = PBXBuildFile; fileRef = 15FB1F881E27E9A000B4F809 /* InterfaceNamerControlPrefs.c */; };
                15FBB54C17D6834C0035D752 /* libCrashReporterClient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 15FBB54B17D6834C0035D752 /* libCrashReporterClient.a */; };
                15FC130B0CCEA59E0013872C /* monitor.c in Sources */ = {isa = PBXBuildFile; fileRef = 15FC130A0CCEA59E0013872C /* monitor.c */; };
                15FC13180CCF74740013872C /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15CB6A6F05C0722B0099E85F /* CoreFoundation.framework */; };
                7214BCE41BEB392300A8F056 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 728015961BE16B6C009F4F60 /* NetworkExtension.framework */; };
                72499BA41AC9B7AB0090C49F /* get-network-info in Resources */ = {isa = PBXBuildFile; fileRef = 72499BA31AC9B7AB0090C49F /* get-network-info */; };
                72499BA51AC9B7AB0090C49F /* get-network-info in Resources */ = {isa = PBXBuildFile; fileRef = 72499BA31AC9B7AB0090C49F /* get-network-info */; };
+               72573D291D667372004975AD /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 72573D281D667372004975AD /* main.m */; };
+               72573D2E1D6673B6004975AD /* SCTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 72573D2D1D6673B6004975AD /* SCTest.m */; };
+               72573D321D667686004975AD /* SCTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 72573D311D667686004975AD /* SCTestUtils.m */; };
+               72573D351D6680AA004975AD /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72573D331D66800C004975AD /* SystemConfiguration.framework */; };
+               72573D3A1D6692BA004975AD /* SCTestOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 72573D391D6692BA004975AD /* SCTestOptions.m */; };
+               72573D3C1D6695B4004975AD /* SCTestDynamicStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 72573D3B1D6695B4004975AD /* SCTestDynamicStore.m */; };
+               72573D3E1D669AA6004975AD /* SCTestPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = 72573D3D1D669AA6004975AD /* SCTestPreferences.m */; };
+               72573D401D67B2BE004975AD /* SCTestUnitTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 72573D3F1D67B2BE004975AD /* SCTestUnitTest.m */; };
+               72573D421D6B798A004975AD /* SCTestConfigAgents.m in Sources */ = {isa = PBXBuildFile; fileRef = 72573D411D6B7989004975AD /* SCTestConfigAgents.m */; };
+               72573D441D6BA051004975AD /* Network.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 728015951BE16B6C009F4F60 /* Network.framework */; };
+               72573D451D6BA976004975AD /* libnetwork.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 90507AAF1CE2F55B0067D16B /* libnetwork.dylib */; };
                725CB7551BF439C6000C05A8 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 725CB7541BF439C6000C05A8 /* Foundation.framework */; };
                725CB7561BF439D2000C05A8 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 725CB7541BF439C6000C05A8 /* Foundation.framework */; };
                725CB7581BF514F2000C05A8 /* configAgentDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 725CB7571BF51476000C05A8 /* configAgentDefines.h */; };
                7264C14614731A1F004FD76D /* CaptiveNetwork.h in Headers */ = {isa = PBXBuildFile; fileRef = 15A1FF3010597F17004C9CC9 /* CaptiveNetwork.h */; settings = {ATTRIBUTES = (Public, ); }; };
                726DB2F41BEA80E5001B2C6C /* config_agent_info.c in Sources */ = {isa = PBXBuildFile; fileRef = 726DB2F11BEA80E5001B2C6C /* config_agent_info.c */; };
                726DB2F61BEA80E5001B2C6C /* config_agent_info.h in Headers */ = {isa = PBXBuildFile; fileRef = 726DB2F21BEA80E5001B2C6C /* config_agent_info.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               7271EA1E1D76600B0055B1AA /* SCTestDynamicStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 72573D3B1D6695B4004975AD /* SCTestDynamicStore.m */; };
+               7271EA1F1D76600B0055B1AA /* SCTestOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 72573D391D6692BA004975AD /* SCTestOptions.m */; };
+               7271EA201D76600B0055B1AA /* SCTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 72573D311D667686004975AD /* SCTestUtils.m */; };
+               7271EA211D76600B0055B1AA /* SCTestUnitTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 72573D3F1D67B2BE004975AD /* SCTestUnitTest.m */; };
+               7271EA221D76600B0055B1AA /* SCTestReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 728E0E951D70229A00E0613A /* SCTestReachability.m */; };
+               7271EA231D76600B0055B1AA /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 72573D281D667372004975AD /* main.m */; };
+               7271EA241D76600B0055B1AA /* SCTestConfigAgents.m in Sources */ = {isa = PBXBuildFile; fileRef = 72573D411D6B7989004975AD /* SCTestConfigAgents.m */; };
+               7271EA251D76600B0055B1AA /* SCTestPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = 72573D3D1D669AA6004975AD /* SCTestPreferences.m */; };
+               7271EA261D76600B0055B1AA /* SCTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 72573D2D1D6673B6004975AD /* SCTest.m */; };
+               7271EA281D76600B0055B1AA /* libnetwork.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 90507AAF1CE2F55B0067D16B /* libnetwork.dylib */; };
+               7271EA291D76600B0055B1AA /* Network.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 728015951BE16B6C009F4F60 /* Network.framework */; };
+               7271EA2A1D76600B0055B1AA /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72573D331D66800C004975AD /* SystemConfiguration.framework */; };
+               7271EA2B1D76600B0055B1AA /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 728015961BE16B6C009F4F60 /* NetworkExtension.framework */; };
+               7271EA2D1D76600B0055B1AA /* npt_configd.plist in npt_configd.plist */ = {isa = PBXBuildFile; fileRef = 72C12CAA1D6E9ED4000EE61C /* npt_configd.plist */; };
                727AF25419138699009AB153 /* VPNAppLayerPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = B0A88CA616397A1200A60B3A /* VPNAppLayerPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; };
                727AF255191386A0009AB153 /* VPNFlow.h in Headers */ = {isa = PBXBuildFile; fileRef = C4CDB8111631933400819B44 /* VPNFlow.h */; settings = {ATTRIBUTES = (Private, ); }; };
                727AF257191386DA009AB153 /* VPNTunnel.h in Headers */ = {isa = PBXBuildFile; fileRef = 15AAA7F2108E310700C2A607 /* VPNTunnel.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7280159E1BE1812B009F4F60 /* proxyAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = 7280157F1BE16833009F4F60 /* proxyAgent.h */; };
                728CEAFF1BEA951A00F13F92 /* config_agent_info.c in Sources */ = {isa = PBXBuildFile; fileRef = 726DB2F11BEA80E5001B2C6C /* config_agent_info.c */; };
                728CEB001BEA993100F13F92 /* config_agent_info.h in Headers */ = {isa = PBXBuildFile; fileRef = 726DB2F21BEA80E5001B2C6C /* config_agent_info.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               728E0E961D70229A00E0613A /* SCTestReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 728E0E951D70229A00E0613A /* SCTestReachability.m */; };
+               728E0E971D70348D00E0613A /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 728015961BE16B6C009F4F60 /* NetworkExtension.framework */; };
                72B43728113C7BFC00EBF1B6 /* nc.h in Headers */ = {isa = PBXBuildFile; fileRef = 72B43726113C7BFC00EBF1B6 /* nc.h */; };
                72B43729113C7BFC00EBF1B6 /* nc.c in Sources */ = {isa = PBXBuildFile; fileRef = 72B43727113C7BFC00EBF1B6 /* nc.c */; };
                72B4372A113C7BFC00EBF1B6 /* nc.h in Headers */ = {isa = PBXBuildFile; fileRef = 72B43726113C7BFC00EBF1B6 /* nc.h */; };
                72B4372B113C7BFC00EBF1B6 /* nc.c in Sources */ = {isa = PBXBuildFile; fileRef = 72B43727113C7BFC00EBF1B6 /* nc.c */; };
+               72C12CAB1D6E9F45000EE61C /* npt_configd.plist in npt_configd.plist */ = {isa = PBXBuildFile; fileRef = 72C12CAA1D6E9ED4000EE61C /* npt_configd.plist */; };
                72D3E6611AE6EA3A00DB4C69 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72D3E6601AE6EA3A00DB4C69 /* main.swift */; };
                72D3E66C1AE6EAF600DB4C69 /* test-objC.m in Sources */ = {isa = PBXBuildFile; fileRef = 72D3E66B1AE6EAF600DB4C69 /* test-objC.m */; };
                90507AB01CE2F55B0067D16B /* libnetwork.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 90507AAF1CE2F55B0067D16B /* libnetwork.dylib */; };
                        remoteGlobalIDString = 15DAD63F07591A1A0084A6ED;
                        remoteInfo = SystemConfiguration.framework;
                };
+               7271EA331D7660980055B1AA /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 15CB6A7705C0722B0099E85F /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 7271EA1B1D76600B0055B1AA;
+                       remoteInfo = "sctest-Embedded";
+               };
+               72C12CB01D6EA2CA000EE61C /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 15CB6A7705C0722B0099E85F /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 72573D251D667372004975AD;
+                       remoteInfo = sctest;
+               };
                72C4A47F1BE44D19009D570E /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = 15CB6A7705C0722B0099E85F /* Project object */;
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                };
+               72573D241D667372004975AD /* npt_configd.plist */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 2147483647;
+                       dstPath = /AppleInternal/CoreOS/BATS/npt_tests;
+                       dstSubfolderSpec = 0;
+                       files = (
+                               72C12CAB1D6E9F45000EE61C /* npt_configd.plist in npt_configd.plist */,
+                       );
+                       name = npt_configd.plist;
+                       runOnlyForDeploymentPostprocessing = 1;
+               };
+               7271EA2C1D76600B0055B1AA /* npt_configd.plist */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 2147483647;
+                       dstPath = /AppleInternal/CoreOS/BATS/npt_tests;
+                       dstSubfolderSpec = 0;
+                       files = (
+                               7271EA2D1D76600B0055B1AA /* npt_configd.plist in npt_configd.plist */,
+                       );
+                       name = npt_configd.plist;
+                       runOnlyForDeploymentPostprocessing = 1;
+               };
                72D3E65C1AE6EA3900DB4C69 /* CopyFiles */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 2147483647;
                15DC346E0711D49400A3311C /* net_set.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = net_set.h; sourceTree = "<group>"; };
                15E1B05916EBAE3C00E5F06F /* libIPMonitor_sim.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libIPMonitor_sim.a; sourceTree = BUILT_PRODUCTS_DIR; };
                15E1B06116EBAE7800E5F06F /* IPMonitor.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = IPMonitor.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
+               15FB1F881E27E9A000B4F809 /* InterfaceNamerControlPrefs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = InterfaceNamerControlPrefs.c; sourceTree = "<group>"; };
+               15FB1F891E27E9A000B4F809 /* InterfaceNamerControlPrefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InterfaceNamerControlPrefs.h; sourceTree = "<group>"; };
                15FBB54B17D6834C0035D752 /* libCrashReporterClient.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libCrashReporterClient.a; path = /usr/local/lib/libCrashReporterClient.a; sourceTree = "<absolute>"; };
                15FBB54E17D7899C0035D752 /* Info-EmbeddedSimulator.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-EmbeddedSimulator.plist"; sourceTree = "<group>"; };
                15FBB55017D78A780035D752 /* update-mach-services */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "update-mach-services"; sourceTree = "<group>"; };
                55A3DB9D183C2A8200ED3DB7 /* SCNetworkMigration.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SCNetworkMigration.c; sourceTree = "<group>"; };
                720985431C580D9F00966D30 /* network_config_agent_info_priv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = network_config_agent_info_priv.h; path = nwi/network_config_agent_info_priv.h; sourceTree = "<group>"; };
                72499BA31AC9B7AB0090C49F /* get-network-info */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "get-network-info"; sourceTree = SOURCE_ROOT; };
+               72573D261D667372004975AD /* sctest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = sctest; sourceTree = BUILT_PRODUCTS_DIR; };
+               72573D281D667372004975AD /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+               72573D2D1D6673B6004975AD /* SCTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCTest.m; sourceTree = "<group>"; };
+               72573D2F1D6673C6004975AD /* SCTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SCTest.h; sourceTree = "<group>"; };
+               72573D301D6675AF004975AD /* SCTestUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SCTestUtils.h; sourceTree = "<group>"; };
+               72573D311D667686004975AD /* SCTestUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCTestUtils.m; sourceTree = "<group>"; };
+               72573D331D66800C004975AD /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
+               72573D361D668B3C004975AD /* genSCTestOptions.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = genSCTestOptions.c; path = sctest/genSCTestOptions.c; sourceTree = SOURCE_ROOT; };
+               72573D381D6692BA004975AD /* SCTestOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCTestOptions.h; sourceTree = "<group>"; };
+               72573D391D6692BA004975AD /* SCTestOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCTestOptions.m; sourceTree = "<group>"; };
+               72573D3B1D6695B4004975AD /* SCTestDynamicStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCTestDynamicStore.m; sourceTree = "<group>"; };
+               72573D3D1D669AA6004975AD /* SCTestPreferences.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCTestPreferences.m; sourceTree = "<group>"; };
+               72573D3F1D67B2BE004975AD /* SCTestUnitTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCTestUnitTest.m; sourceTree = "<group>"; };
+               72573D411D6B7989004975AD /* SCTestConfigAgents.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCTestConfigAgents.m; sourceTree = "<group>"; };
+               72573D431D6B9E72004975AD /* sctest-entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "sctest-entitlements.plist"; path = "sctest/sctest-entitlements.plist"; sourceTree = SOURCE_ROOT; };
                725CB7541BF439C6000C05A8 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
                725CB7571BF51476000C05A8 /* configAgentDefines.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = configAgentDefines.h; sourceTree = "<group>"; };
                725E53D51A92D2A5009997E1 /* com.apple.networking.IPMonitor */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = com.apple.networking.IPMonitor; sourceTree = "<group>"; };
                726DB2F11BEA80E5001B2C6C /* config_agent_info.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = config_agent_info.c; path = "config-agent-info/config_agent_info.c"; sourceTree = "<group>"; };
                726DB2F21BEA80E5001B2C6C /* config_agent_info.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = config_agent_info.h; path = "config-agent-info/config_agent_info.h"; sourceTree = "<group>"; };
+               7271EA321D76600B0055B1AA /* sctest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = sctest; sourceTree = BUILT_PRODUCTS_DIR; };
                728015751BE16833009F4F60 /* agent-monitor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = "agent-monitor.m"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
                728015781BE16833009F4F60 /* configAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = configAgent.h; sourceTree = "<group>"; };
                728015791BE16833009F4F60 /* configAgent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = configAgent.m; sourceTree = "<group>"; };
                728015931BE1697E009F4F60 /* agent-monitor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "agent-monitor.h"; sourceTree = "<group>"; };
                728015951BE16B6C009F4F60 /* Network.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Network.framework; path = System/Library/PrivateFrameworks/Network.framework; sourceTree = SDKROOT; };
                728015961BE16B6C009F4F60 /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; };
+               728E0E951D70229A00E0613A /* SCTestReachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCTestReachability.m; sourceTree = "<group>"; };
                72B43726113C7BFC00EBF1B6 /* nc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nc.h; sourceTree = "<group>"; };
                72B43727113C7BFC00EBF1B6 /* nc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nc.c; sourceTree = "<group>"; };
+               72C12CAA1D6E9ED4000EE61C /* npt_configd.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = npt_configd.plist; sourceTree = "<group>"; };
                72D3E6591AE6E8A900DB4C69 /* Modules */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Modules; path = SystemConfiguration.fproj/Modules; sourceTree = SOURCE_ROOT; };
                72D3E65E1AE6EA3A00DB4C69 /* SCTest-Swift */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "SCTest-Swift"; sourceTree = BUILT_PRODUCTS_DIR; };
                72D3E6601AE6EA3A00DB4C69 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               72573D231D667372004975AD /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               72573D451D6BA976004975AD /* libnetwork.dylib in Frameworks */,
+                               72573D441D6BA051004975AD /* Network.framework in Frameworks */,
+                               72573D351D6680AA004975AD /* SystemConfiguration.framework in Frameworks */,
+                               728E0E971D70348D00E0613A /* NetworkExtension.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               7271EA271D76600B0055B1AA /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               7271EA281D76600B0055B1AA /* libnetwork.dylib in Frameworks */,
+                               7271EA291D76600B0055B1AA /* Network.framework in Frameworks */,
+                               7271EA2A1D76600B0055B1AA /* SystemConfiguration.framework in Frameworks */,
+                               7271EA2B1D76600B0055B1AA /* NetworkExtension.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                72D3E65B1AE6EA3900DB4C69 /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                159D53C907528B36004F8947 /* common */ = {
                        isa = PBXGroup;
                        children = (
-                               F9A3780E16A4846E00C57CDC /* IPMonitorControlPrefs.c */,
+                               15FB1F891E27E9A000B4F809 /* InterfaceNamerControlPrefs.h */,
+                               15FB1F881E27E9A000B4F809 /* InterfaceNamerControlPrefs.c */,
                                F9A3780F16A4846E00C57CDC /* IPMonitorControlPrefs.h */,
+                               F9A3780E16A4846E00C57CDC /* IPMonitorControlPrefs.c */,
                                159D53CA07528B36004F8947 /* cache.c */,
                                159D53CB07528B36004F8947 /* cache.h */,
                                1572AA8B1D8234500021E093 /* plugin_shared.h */,
                                15CB6A6E05C0722B0099E85F /* External Frameworks and Libraries */,
                                72D3E65F1AE6EA3A00DB4C69 /* SCTest-Swift */,
                                72D3E66A1AE6EAF600DB4C69 /* SCTest-ObjC */,
+                               72573D271D667372004975AD /* sctest */,
                                15CB690F05C0722B0099E85F /* Products */,
                                90507AAE1CE2F55B0067D16B /* Frameworks */,
                        );
                                155F49931C864F3700E47D08 /* QoSMarking.bundle */,
                                155F499C1C864F4E00E47D08 /* libQoSMarking.a */,
                                155F49A21C864F5400E47D08 /* QoSMarking.bundle */,
+                               72573D261D667372004975AD /* sctest */,
+                               7271EA321D76600B0055B1AA /* sctest */,
                        );
                        name = Products;
                        sourceTree = "<group>";
                        name = "Supporting Files";
                        sourceTree = "<group>";
                };
+               72573D271D667372004975AD /* sctest */ = {
+                       isa = PBXGroup;
+                       children = (
+                               72573D361D668B3C004975AD /* genSCTestOptions.c */,
+                               72573D381D6692BA004975AD /* SCTestOptions.h */,
+                               72573D391D6692BA004975AD /* SCTestOptions.m */,
+                               72573D301D6675AF004975AD /* SCTestUtils.h */,
+                               72573D311D667686004975AD /* SCTestUtils.m */,
+                               72573D2F1D6673C6004975AD /* SCTest.h */,
+                               72573D281D667372004975AD /* main.m */,
+                               72573D2D1D6673B6004975AD /* SCTest.m */,
+                               72573D3B1D6695B4004975AD /* SCTestDynamicStore.m */,
+                               72573D3D1D669AA6004975AD /* SCTestPreferences.m */,
+                               72573D411D6B7989004975AD /* SCTestConfigAgents.m */,
+                               728E0E951D70229A00E0613A /* SCTestReachability.m */,
+                               72573D3F1D67B2BE004975AD /* SCTestUnitTest.m */,
+                               72573D431D6B9E72004975AD /* sctest-entitlements.plist */,
+                               72C12CAA1D6E9ED4000EE61C /* npt_configd.plist */,
+                       );
+                       path = sctest;
+                       sourceTree = "<group>";
+               };
                725E53D41A92D289009997E1 /* Simulator */ = {
                        isa = PBXGroup;
                        children = (
                90507AAE1CE2F55B0067D16B /* Frameworks */ = {
                        isa = PBXGroup;
                        children = (
+                               72573D331D66800C004975AD /* SystemConfiguration.framework */,
                                90507AB11CE2F5720067D16B /* libnetwork.dylib */,
                                90507AAF1CE2F55B0067D16B /* libnetwork.dylib */,
                        );
                                155847520754FDCD0046C2E9 /* net_service.h in Headers */,
                                155847530754FDCD0046C2E9 /* net_set.h in Headers */,
                                72B43728113C7BFC00EBF1B6 /* nc.h in Headers */,
+                               1581BCD31E28679A00F69B1E /* InterfaceNamerControlPrefs.h in Headers */,
+                               1581BCD51E2867A500F69B1E /* IPMonitorControlPrefs.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                15732ABA16EA511900F3AC4C /* net_service.h in Headers */,
                                15732ABB16EA511900F3AC4C /* net_set.h in Headers */,
                                15732ABC16EA511900F3AC4C /* nc.h in Headers */,
+                               1581BCD91E2867C100F69B1E /* IPMonitorControlPrefs.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                157433FD0D4A8137002ACA73 /* net_service.h in Headers */,
                                157433FE0D4A8137002ACA73 /* net_set.h in Headers */,
                                72B4372A113C7BFC00EBF1B6 /* nc.h in Headers */,
+                               1581BCDD1E286E0000F69B1E /* InterfaceNamerControlPrefs.h in Headers */,
+                               1581BCD71E2867B200F69B1E /* IPMonitorControlPrefs.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                153ACCAC14E322D5005029A5 /* network_information_server.h in Headers */,
                                720A4C0A1C585C7D007436B8 /* configAgent.h in Headers */,
                                1596A7B514EDB73D00798C39 /* libSystemConfiguration_server.h in Headers */,
+                               1581BCD61E2867AF00F69B1E /* IPMonitorControlPrefs.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        isa = PBXHeadersBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               1572AA8C1D8235390021E093 /* plugin_shared.h in Headers */,
+                               15A9BDA81D8DEA67007024DB /* plugin_shared.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        buildActionMask = 2147483647;
                        files = (
                                1572AA8F1D82375A0021E093 /* plugin_shared.h in Headers */,
+                               1581BCD21E28673600F69B1E /* InterfaceNamerControlPrefs.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                7280159D1BE1812B009F4F60 /* dnsAgent.h in Headers */,
                                7280159E1BE1812B009F4F60 /* proxyAgent.h in Headers */,
                                155D223B0AF13A7300D52ED0 /* dns-configuration.h in Headers */,
+                               1581BCD41E2867A300F69B1E /* IPMonitorControlPrefs.h in Headers */,
                                15D48EC00F67061700B4711E /* dnsinfo_create.h in Headers */,
                                725CB7581BF514F2000C05A8 /* configAgentDefines.h in Headers */,
                                E4F211D7137B0AF200BBB915 /* network_state_information_priv.h in Headers */,
                                15E1B04516EBAE3C00E5F06F /* network_state_information_priv.h in Headers */,
                                15E1B04616EBAE3C00E5F06F /* proxy-configuration.h in Headers */,
                                15E1B04816EBAE3C00E5F06F /* network_information_server.h in Headers */,
+                               1581BCD81E2867BA00F69B1E /* IPMonitorControlPrefs.h in Headers */,
                                15E1B04916EBAE3C00E5F06F /* libSystemConfiguration_server.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        productReference = 15FD72C90754DA7E001CC321 /* PreferencesMonitor.bundle */;
                        productType = "com.apple.product-type.bundle";
                };
+               72573D251D667372004975AD /* sctest */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 72573D2A1D667372004975AD /* Build configuration list for PBXNativeTarget "sctest" */;
+                       buildPhases = (
+                               72573D221D667372004975AD /* Sources */,
+                               72573D231D667372004975AD /* Frameworks */,
+                               72573D241D667372004975AD /* npt_configd.plist */,
+                               72C12CB21D6FEFBE000EE61C /* ShellScript */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = sctest;
+                       productName = sctest;
+                       productReference = 72573D261D667372004975AD /* sctest */;
+                       productType = "com.apple.product-type.tool";
+               };
+               7271EA1B1D76600B0055B1AA /* sctest-Embedded */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 7271EA2F1D76600B0055B1AA /* Build configuration list for PBXNativeTarget "sctest-Embedded" */;
+                       buildPhases = (
+                               7271EA1D1D76600B0055B1AA /* Sources */,
+                               7271EA271D76600B0055B1AA /* Frameworks */,
+                               7271EA2C1D76600B0055B1AA /* npt_configd.plist */,
+                               7271EA2E1D76600B0055B1AA /* Fix plist ownership */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = "sctest-Embedded";
+                       productName = sctest;
+                       productReference = 7271EA321D76600B0055B1AA /* sctest */;
+                       productType = "com.apple.product-type.tool";
+               };
                72D3E65D1AE6EA3900DB4C69 /* SCTest-Swift */ = {
                        isa = PBXNativeTarget;
                        buildConfigurationList = 72D3E6621AE6EA3A00DB4C69 /* Build configuration list for PBXNativeTarget "SCTest-Swift" */;
                        attributes = {
                                LastUpgradeCheck = 0800;
                                TargetAttributes = {
+                                       72573D251D667372004975AD = {
+                                               CreatedOnToolsVersion = 8.0;
+                                               ProvisioningStyle = Automatic;
+                                       };
                                        72D3E65D1AE6EA3900DB4C69 = {
                                                CreatedOnToolsVersion = 7.0;
                                        };
                                15E83104167F9AF600FD51EC /* EVERYTHING */,
                                72D3E65D1AE6EA3900DB4C69 /* SCTest-Swift */,
                                72D3E6681AE6EAF600DB4C69 /* SCTest-ObjC */,
+                               72573D251D667372004975AD /* sctest */,
+                               7271EA1B1D76600B0055B1AA /* sctest-Embedded */,
                        );
                };
 /* End PBXProject section */
                        shellScript = "if [ -x ${SCRIPT_INPUT_FILE_0} ]; then\n\t${SCRIPT_INPUT_FILE_0} com.apple.configd_sim.plist\nfi";
                        showEnvVarsInLog = 0;
                };
+               7271EA2E1D76600B0055B1AA /* Fix plist ownership */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       buildActionMask = 8;
+                       files = (
+                       );
+                       inputPaths = (
+                       );
+                       name = "Fix plist ownership";
+                       outputPaths = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 1;
+                       shellPath = /bin/sh;
+                       shellScript = "if [ ${UID} -eq 0 ]; then\n\tchown 0:0 \"${DSTROOT}/AppleInternal/CoreOS/BATS/npt_tests/npt_configd.plist\"\nfi\n\nexit 0";
+               };
+               72C12CB21D6FEFBE000EE61C /* ShellScript */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       buildActionMask = 8;
+                       files = (
+                       );
+                       inputPaths = (
+                       );
+                       outputPaths = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 1;
+                       shellPath = /bin/sh;
+                       shellScript = "if [ ${UID} -eq 0 ]; then\n\tchown 0:0 \"${DSTROOT}/AppleInternal/CoreOS/BATS/npt_tests/npt_configd.plist\"\nfi\n\nexit 0";
+               };
 /* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
                                155847600754FDCD0046C2E9 /* net_service.c in Sources */,
                                155847610754FDCD0046C2E9 /* net_set.c in Sources */,
                                72B43729113C7BFC00EBF1B6 /* nc.c in Sources */,
+                               15FB1F8B1E27EA8900B4F809 /* InterfaceNamerControlPrefs.c in Sources */,
                                F9B50FF316A4CBB200CA274E /* IPMonitorControlPrefs.c in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        buildActionMask = 2147483647;
                        files = (
                                159D541607528DF1004F8947 /* ifnamer.c in Sources */,
+                               15FB1F8A1E27EA8700B4F809 /* InterfaceNamerControlPrefs.c in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                15D48EBF0F67061600B4711E /* dnsinfo_create.c in Sources */,
                                1522FCFB0FA7FE4B00B24128 /* dnsinfo_flatfile.c in Sources */,
                                150BEC1814CA24F900237116 /* dnsinfo_server.c in Sources */,
-                               F9B7AE6A186211D300C78D18 /* IPMonitorControlServer.c in Sources */,
                                159D541707528E05004F8947 /* ip_plugin.c in Sources */,
                                7280158D1BE16861009F4F60 /* dnsAgent.m in Sources */,
                                E49173E1137C4E4F0000089F /* network_state_information_priv.c in Sources */,
                                7280158C1BE1685D009F4F60 /* controller.m in Sources */,
                                D61AAEAF1522C99C0066B003 /* scprefs_observer.c in Sources */,
                                F9A3781016A4847700C57CDC /* IPMonitorControlPrefs.c in Sources */,
+                               F9B7AE6A186211D300C78D18 /* IPMonitorControlServer.c in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               72573D221D667372004975AD /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               72573D3C1D6695B4004975AD /* SCTestDynamicStore.m in Sources */,
+                               72573D3A1D6692BA004975AD /* SCTestOptions.m in Sources */,
+                               72573D321D667686004975AD /* SCTestUtils.m in Sources */,
+                               72573D401D67B2BE004975AD /* SCTestUnitTest.m in Sources */,
+                               728E0E961D70229A00E0613A /* SCTestReachability.m in Sources */,
+                               72573D291D667372004975AD /* main.m in Sources */,
+                               72573D421D6B798A004975AD /* SCTestConfigAgents.m in Sources */,
+                               72573D3E1D669AA6004975AD /* SCTestPreferences.m in Sources */,
+                               72573D2E1D6673B6004975AD /* SCTest.m in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               7271EA1D1D76600B0055B1AA /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               7271EA1E1D76600B0055B1AA /* SCTestDynamicStore.m in Sources */,
+                               7271EA1F1D76600B0055B1AA /* SCTestOptions.m in Sources */,
+                               7271EA201D76600B0055B1AA /* SCTestUtils.m in Sources */,
+                               7271EA211D76600B0055B1AA /* SCTestUnitTest.m in Sources */,
+                               7271EA221D76600B0055B1AA /* SCTestReachability.m in Sources */,
+                               7271EA231D76600B0055B1AA /* main.m in Sources */,
+                               7271EA241D76600B0055B1AA /* SCTestConfigAgents.m in Sources */,
+                               7271EA251D76600B0055B1AA /* SCTestPreferences.m in Sources */,
+                               7271EA261D76600B0055B1AA /* SCTest.m in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                72D3E65A1AE6EA3900DB4C69 /* Sources */ = {
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        target = 15DAD63F07591A1A0084A6ED /* SystemConfiguration.framework */;
                        targetProxy = 723050331AE6F29D004AC149 /* PBXContainerItemProxy */;
                };
+               7271EA341D7660980055B1AA /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = 7271EA1B1D76600B0055B1AA /* sctest-Embedded */;
+                       targetProxy = 7271EA331D7660980055B1AA /* PBXContainerItemProxy */;
+               };
+               72C12CB11D6EA2CA000EE61C /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = 72573D251D667372004975AD /* sctest */;
+                       targetProxy = 72C12CB01D6EA2CA000EE61C /* PBXContainerItemProxy */;
+               };
                72C4A4801BE44D19009D570E /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        target = 155847430754FDCD0046C2E9 /* scutil */;
                        };
                        name = Release;
                };
+               72573D2B1D667372004975AD /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CLANG_ENABLE_MODULES = NO;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CODE_SIGN_ENTITLEMENTS = "sctest/sctest-entitlements.plist";
+                               CODE_SIGN_IDENTITY = "-";
+                               FRAMEWORK_SEARCH_PATHS = "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks";
+                               HEADER_SEARCH_PATHS = "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/Frameworks/System.framework/PrivateHeaders";
+                               INSTALL_PATH = /usr/local/bin;
+                               MTL_ENABLE_DEBUG_INFO = YES;
+                               PLIST_FILE_OUTPUT_FORMAT = "same-as-input";
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                       };
+                       name = Debug;
+               };
+               72573D2C1D667372004975AD /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CLANG_ENABLE_MODULES = NO;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CODE_SIGN_ENTITLEMENTS = "sctest/sctest-entitlements.plist";
+                               CODE_SIGN_IDENTITY = "-";
+                               FRAMEWORK_SEARCH_PATHS = "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks";
+                               HEADER_SEARCH_PATHS = "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/Frameworks/System.framework/PrivateHeaders";
+                               INSTALL_PATH = /usr/local/bin;
+                               MTL_ENABLE_DEBUG_INFO = NO;
+                               PLIST_FILE_OUTPUT_FORMAT = "same-as-input";
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                       };
+                       name = Release;
+               };
+               7271EA301D76600B0055B1AA /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CLANG_ENABLE_MODULES = NO;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CODE_SIGN_ENTITLEMENTS = "sctest/sctest-entitlements.plist";
+                               CODE_SIGN_IDENTITY = "-";
+                               FRAMEWORK_SEARCH_PATHS = "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks";
+                               HEADER_SEARCH_PATHS = "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/Frameworks/System.framework/PrivateHeaders";
+                               INSTALL_PATH = /usr/local/bin;
+                               MTL_ENABLE_DEBUG_INFO = YES;
+                               PLIST_FILE_OUTPUT_FORMAT = "same-as-input";
+                               PRODUCT_NAME = sctest;
+                               SDKROOT = iphoneos.internal;
+                               SUPPORTED_PLATFORMS = iphoneos;
+                       };
+                       name = Debug;
+               };
+               7271EA311D76600B0055B1AA /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CLANG_ENABLE_MODULES = NO;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CODE_SIGN_ENTITLEMENTS = "sctest/sctest-entitlements.plist";
+                               CODE_SIGN_IDENTITY = "-";
+                               FRAMEWORK_SEARCH_PATHS = "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks";
+                               HEADER_SEARCH_PATHS = "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/Frameworks/System.framework/PrivateHeaders";
+                               INSTALL_PATH = /usr/local/bin;
+                               MTL_ENABLE_DEBUG_INFO = NO;
+                               PLIST_FILE_OUTPUT_FORMAT = "same-as-input";
+                               PRODUCT_NAME = sctest;
+                               SDKROOT = iphoneos.internal;
+                               SUPPORTED_PLATFORMS = iphoneos;
+                       };
+                       name = Release;
+               };
                72D3E6631AE6EA3A00DB4C69 /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
+               72573D2A1D667372004975AD /* Build configuration list for PBXNativeTarget "sctest" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               72573D2B1D667372004975AD /* Debug */,
+                               72573D2C1D667372004975AD /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               7271EA2F1D76600B0055B1AA /* Build configuration list for PBXNativeTarget "sctest-Embedded" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               7271EA301D76600B0055B1AA /* Debug */,
+                               7271EA311D76600B0055B1AA /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
                72D3E6621AE6EA3A00DB4C69 /* Build configuration list for PBXNativeTarget "SCTest-Swift" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
index 6df85fd180d66ad377b296ac5fbd0ccb9175171d..db30d7b991607d257b60d770052449dc6beb2163 100755 (executable)
@@ -1,5 +1,5 @@
 #!/bin/sh
-# Copyright (c) 2004-2015 Apple Inc.
+# Copyright (c) 2004-2017 Apple Inc.
 #
 # get-mobility-info
 #
@@ -8,6 +8,30 @@
 
 PATH=/bin:/usr/bin:/sbin:/usr/sbin
 
+OUTDIR=""
+NO_PCAP=0
+NO_TAR=0
+
+while getopts f:PT OPTION ; do
+       case ${OPTION} in
+               f)
+                  OUTDIR="${OPTARG}"
+                  if [ ! -d "${OUTDIR}" ]; then
+                       echo "# ${PROGNAME}: \"${OUTDIR}\" is not a directory"
+                       exit 1
+                  fi
+                  ;;
+               P)
+                  NO_PCAP=1
+                  ;;
+               T)
+                  NO_TAR=1
+                  ;;
+               \?)
+                  ;;
+       esac
+done
+
 #
 # Disclaimer
 #
@@ -45,12 +69,15 @@ else
 fi
 
 OUT="mobility-info-`date +'%Y.%m.%d.%H%M%S'`"
-OUTDIR="/var/tmp"
-if [ -d ~/Desktop ]; then
-       OUTDIR=~/Desktop
-elif [ "`readlink /tmp`" = "private/var/tmp" ]; then
-       OUTDIR=/Library/Logs/CrashReporter
-       mkdir -p ${OUTDIR}
+
+if [ -z $OUTDIR ]; then
+       OUTDIR="/var/tmp"
+       if [ -d ~/Desktop ]; then
+               OUTDIR=~/Desktop
+       elif [ "`readlink /tmp`" = "private/var/tmp" ]; then
+               OUTDIR=/Library/Logs/CrashReporter
+               mkdir -p ${OUTDIR}
+       fi
 fi
 
 umask 077
@@ -61,18 +88,20 @@ if [ $? -ne 0 ]; then
        exit 1
 fi
 
-GZ_EXT=""
-GZ_OPT=""
-if [ -x /usr/bin/gzip ]; then
-       GZ_EXT=".gz"
-       GZ_OPT="-z"
-fi
+if [ $NO_TAR -eq 0 ]; then
+       GZ_EXT=""
+       GZ_OPT=""
+       if [ -x /usr/bin/gzip ]; then
+               GZ_EXT=".gz"
+               GZ_OPT="-z"
+       fi
 
-ARCHIVE=`mktemp -q "${OUTDIR}/${OUT}.tar${GZ_EXT}"`
-if [ $? -ne 0 ]; then
-       echo "Could not create snapshot archive"
-       rm -rf "${WORKDIR}"
-       exit 1
+       ARCHIVE=`mktemp -q "${OUTDIR}/${OUT}.tar${GZ_EXT}"`
+       if [ $? -ne 0 ]; then
+               echo "Could not create snapshot archive"
+               rm -rf "${WORKDIR}"
+               exit 1
+       fi
 fi
 
 cd "${WORKDIR}"
@@ -81,15 +110,22 @@ echo ""
 echo "Please wait, collecting information and statistics"
 echo ""
 
+#
+# collect packet capture with kernel ring buffer if available
+#
+if [ -x /usr/local/bin/netdiagnose -a ${NO_PCAP} -ne 1 ]; then
+       /usr/local/bin/netdiagnose -p "${WORKDIR}" start packetcapture          2>&1
+fi
+
 #
 # get-network-info
 #
 if [ -x /System/Library/Frameworks/SystemConfiguration.framework/Resources/get-network-info ]; then
-       /System/Library/Frameworks/SystemConfiguration.framework/Resources/get-network-info -s -c "${WORKDIR}"
+       /System/Library/Frameworks/SystemConfiguration.framework/Resources/get-network-info -s -c -P "${WORKDIR}"
 elif [ -x /System/Library/Frameworks/SystemConfiguration.framework/get-network-info ]; then
-       /System/Library/Frameworks/SystemConfiguration.framework/get-network-info -s -c "${WORKDIR}"
+       /System/Library/Frameworks/SystemConfiguration.framework/get-network-info -s -c -P "${WORKDIR}"
 elif [ -x /System/Library/PrivateFrameworks/SystemConfiguration.framework/get-network-info ]; then
-       /System/Library/PrivateFrameworks/SystemConfiguration.framework/get-network-info -s -c "${WORKDIR}"
+       /System/Library/PrivateFrameworks/SystemConfiguration.framework/get-network-info -s -c -P "${WORKDIR}"
 fi
 
 #
@@ -631,19 +667,38 @@ fi
 wait
 
 #
-# collect everything into a single archive
+# Stop the packet capture
 #
-cd "${WORKDIR}/.."
-tar -c ${GZ_OPT} -f "${ARCHIVE}" "${OUT}"
-rm -rf "${WORKDIR}"
+if [ -x /usr/local/bin/netdiagnose -a ${NO_PCAP} -ne 1 ]; then
+       /usr/local/bin/netdiagnose stop packetcapture           2>&1
+fi
 
-if [ ${UID} -eq 0 ]; then
-       if [ -n "${SUDO_UID}" -a -n "${SUDO_GID}" ]; then
-               if [ ${UID} -ne ${SUDO_UID} ]; then
-                       chown ${SUDO_UID}:${SUDO_GID} "${ARCHIVE}"
+if [ $NO_TAR -eq 0 ]; then
+       #
+       # collect everything into a single archive
+       #
+       cd "${WORKDIR}/.."
+       tar -c ${GZ_OPT} -f "${ARCHIVE}" "${OUT}"
+       rm -rf "${WORKDIR}"
+
+       if [ ${UID} -eq 0 ]; then
+               if [ -n "${SUDO_UID}" -a -n "${SUDO_GID}" ]; then
+                       if [ ${UID} -ne ${SUDO_UID} ]; then
+                               chown ${SUDO_UID}:${SUDO_GID} "${ARCHIVE}"
+                       fi
                fi
        fi
-fi
 
-echo "Network data collected to \"${ARCHIVE}\""
+       echo "Network data collected to \"${ARCHIVE}\""
+else
+       mv "${WORKDIR}" "${OUTDIR}"
 
+       if [ ${UID} -eq 0 ]; then
+               if [ -n "${SUDO_UID}" -a -n "${SUDO_GID}" ]; then
+                       if [ ${UID} -ne ${SUDO_UID} ]; then
+                               chown -R ${SUDO_UID}:${SUDO_GID} "${OUTDIR}/${OUT}"
+                       fi
+               fi
+       fi
+       echo "Network data collected to \"${OUTDIR}/${OUT}\""
+fi
index 81a822992bdf6e400b1a7cb12383d75ae3fe7063..bcf39c359d42a0ea7659b1c8826f67b8833e515e 100755 (executable)
@@ -25,6 +25,10 @@ process_opts () {
                           COLLECT_NDF_INFO="Y"
                           shift
                           ;;
+                  -P)
+                          COLLECT_PCAP="N"
+                          shift
+                          ;;
                    -s)
                           COLLECT_SENSITIVE_INFO="Y"
                           shift
@@ -560,11 +564,34 @@ run_lsof () {
 
 }
 
+start_pcap() {
+
+       #
+       # collect a packet capture if netdiagnose is available
+       #
+       if [ -x /usr/local/bin/netdiagnose ]; then
+               /usr/local/bin/netdiagnose -p "${REQUESTED_OUTDIR}" start sysdiagpcap                   2>&1
+
+               PCAP_STARTED=1
+       fi
+}
+
+stop_pcap () {
+       if [ ${PCAP_STARTED} -ne 0 ]; then
+               /usr/local/bin/netdiagnose stop sysdiagpcap                                             2>&1
+       fi
+}
+
 collect_ndf_info () {
        run_lsof
 }
 
 collect_sensitive_info () {
+
+       if [ "${COLLECT_PCAP}" == "Y" ]; then
+               start_pcap
+       fi
+
        collect_state_dump_sensitive
        run_ndp
        run_arp
@@ -595,6 +622,8 @@ collect_info () {
        if [ "${COLLECT_CONFIGURATION_FILES}" == "Y" ]; then
                collect_configuration_files
        fi
+
+       stop_pcap
 }
 
 # __COMMAND_ROUTINES_END__
@@ -606,6 +635,7 @@ usage () {
        echo "Usage: get-network-info [-c] [-n] [-s] <info-directory>"
        echo "          -c                  collects system configuration files"
        echo "          -n                  collects NDF information (lsof)"
+       echo "          -P                  do not collect a packet capture"
        echo "          -s                  collects sensitive information (ARP/NDP/mDNS cache)"
        echo "          <info-directory>    path to directory where all the information will be collected"
 
@@ -642,6 +672,9 @@ init_globals () {
        REQUESTED_OUTDIR=""
        COLLECT_SENSITIVE_INFO=""
        COLLECT_CONFIGURATION_FILES=""
+       COLLECT_PCAP="Y"
+       PCAP_STARTED=0
+
 }
 
 # __HELPER_ROUTINES_END__
@@ -649,7 +682,7 @@ init_globals () {
 #
 # __MAIN__
 #
-ARGS=`getopt cns $*`
+ARGS=`getopt cnPs $*`
 if [ $? != 0 ]; then
        usage
        exit 1
diff --git a/sctest/Makefile b/sctest/Makefile
new file mode 100644 (file)
index 0000000..bc47fa4
--- /dev/null
@@ -0,0 +1,14 @@
+#
+# Makefile for generating the SCTestOptions.[mh] files
+#
+
+all: SCTestOptions.h SCTestOptions.m
+
+/tmp/genSCTestOptions: genSCTestOptions.c Makefile
+       cc -g -o /tmp/genSCTestOptions genSCTestOptions.c
+
+SCTestOptions.h: /tmp/genSCTestOptions
+       /tmp/genSCTestOptions header > SCTestOptions.h
+
+SCTestOptions.m: /tmp/genSCTestOptions
+       /tmp/genSCTestOptions mfile > SCTestOptions.m
\ No newline at end of file
diff --git a/sctest/SCTest.h b/sctest/SCTest.h
new file mode 100644 (file)
index 0000000..b798d55
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016 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@
+ */
+
+#ifndef SCTest_h
+#define SCTest_h
+
+#import <Foundation/Foundation.h>
+#import "SCTestOptions.h"
+#import "SCTestUtils.h"
+
+@interface SCTest : NSObject
+
+@property (atomic, retain) NSDictionary *options;
+@property (atomic) CPUUsageInfo *globalCPU;
+@property (atomic) timerInfo *globalTimer;
+
+- (instancetype)initWithOptions:(NSDictionary *)options;
+- (void)start;
+- (void)cleanupAndExitWithErrorCode:(int)error;
+- (BOOL)unitTest;
+- (void)waitFor:(double)seconds;
+
++ (NSString *)command;
++ (NSString *)commandDescription;
+
+@end
+
+#endif /* SCTest_h */
diff --git a/sctest/SCTest.m b/sctest/SCTest.m
new file mode 100644 (file)
index 0000000..bc4ce1c
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2016 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@
+ */
+
+#import "SCTest.h"
+
+@implementation SCTest
+
+- (instancetype)initWithOptions:(NSDictionary *)options
+{
+       self = [super init];
+       if (self) {
+               self.options = options;
+               self.globalCPU = malloc(sizeof(CPUUsageInfo));
+               self.globalTimer = malloc(sizeof(timerInfo));
+       }
+       return self;
+}
+
+- (void)dealloc
+{
+       if (self.globalTimer != NULL) {
+               free(self.globalTimer);
+       }
+       if (self.globalCPU != NULL) {
+               free(self.globalCPU);
+       }
+}
+
++ (NSString *)command
+{
+       return @"sctest";
+}
+
++ (NSString *)commandDescription
+{
+       return @"This is a generic class";
+}
+
++ (NSString *)commandHelp
+{
+       return @"This is a generic help";
+}
+
+- (void)start
+{
+       return;
+}
+
+- (void)waitFor:(double)seconds
+{
+       dispatch_semaphore_t sem = dispatch_semaphore_create(0);
+       dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
+       dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 0);
+       dispatch_source_set_event_handler(timer, ^{
+               dispatch_semaphore_signal(sem);
+       });
+       dispatch_resume(timer);
+       dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
+}
+
+- (void)cleanupAndExitWithErrorCode:(int)error
+{
+       if (self.options[kSCTestGlobalOptionTime] != nil) {
+               timerEnd(self.globalTimer);
+               SCTestLog("Time: %@ s", createUsageStringForTimer(self.globalTimer));
+       }
+
+       if (self.options[kSCTestGlobalOptionCPU] != nil) {
+               cpuEnd(self.globalCPU);
+               SCTestLog("CPU: %@", createUsageStringForCPU(self.globalCPU));
+       }
+
+       if (self.options[kSCTestGlobalOptionWait] != nil) {
+               return;
+       }
+       exit(error);
+}
+
+- (BOOL)unitTest
+{
+       return YES;
+}
+
+@end
diff --git a/sctest/SCTestConfigAgents.m b/sctest/SCTestConfigAgents.m
new file mode 100644 (file)
index 0000000..122f258
--- /dev/null
@@ -0,0 +1,654 @@
+/*
+ * Copyright (c) 2016 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@
+ */
+
+#import "SCTest.h"
+#import "SCTestUtils.h"
+#import <Network/Network_Private.h>
+#import <CoreFoundation/CFXPCBridge.h>
+#import <config_agent_info.h>
+
+#define TEST_DOMAIN    "filemaker.com"
+#define PATH_QUIESCE_TIME 1.5 // seconds
+
+@interface SCTestConfigAgent : SCTest
+@property NSString *serviceID;
+@property NSString *proxyKey;
+@property NSString *dnsKey;
+@property NSArray<NSArray<NSDictionary *> *> *testProxy;
+@property NSArray<NWEndpoint *> *testDNS;
+@property SCDynamicStoreRef store;
+@property (copy) NSArray<NSArray<NSDictionary *> *> *pathProxy;
+@property (copy) NSArray<NWEndpoint *> *pathDNS;
+@end
+
+@implementation SCTestConfigAgent
+
+- (instancetype)initWithOptions:(NSDictionary *)options
+{
+       self = [super initWithOptions:options];
+       if (self) {
+               _serviceID = @"8F66B505-EAEF-4611-BD4D-C523FD9451F0";
+               _store = SCDynamicStoreCreate(kCFAllocatorDefault,
+                                               CFSTR("SCTest"),
+                                               NULL,
+                                               NULL);
+               _proxyKey = (__bridge_transfer NSString *)[self copyStateKeyWithServiceID:(__bridge CFStringRef)(self.serviceID) forEntity:kSCEntNetProxies];
+               _dnsKey = (__bridge_transfer NSString *)[self copyStateKeyWithServiceID:(__bridge CFStringRef)(self.serviceID) forEntity:kSCEntNetDNS];
+       }
+       return self;
+}
+
+- (void)dealloc
+{
+       if (self.store != NULL) {
+               CFRelease(self.store);
+               self.store = NULL;
+       }
+}
+
++ (NSString *)command
+{
+       return @"config_agent";
+}
+
++ (NSString *)commandDescription
+{
+       return @"Tests the Config Agent code path";
+}
+
+- (void)start
+{
+       if (self.options[kSCTestConfigAgentRemoveProxy]) {
+               [self removeFromSCDynamicStore:self.proxyKey];
+       }
+
+       if (self.options[kSCTestConfigAgentRemoveDNS]) {
+               [self removeFromSCDynamicStore:self.dnsKey];
+       }
+
+       NSDictionary *proxyConfig = [self parseProxyAgentOptions];
+       if (proxyConfig != nil) {
+               [self publishToSCDynamicStore:self.proxyKey value:proxyConfig];
+               self.testProxy = @[@[proxyConfig]];
+       }
+
+       NSDictionary *dnsConfig = [self parseDNSAgentOptions];
+       if (dnsConfig != nil) {
+               [self publishToSCDynamicStore:self.dnsKey value:dnsConfig];
+               self.testDNS = [self createDNSArray:dnsConfig];
+       }
+
+       [self cleanupAndExitWithErrorCode:0];
+}
+
+- (CFStringRef)copyStateKeyWithServiceID:(CFStringRef)serviceID
+                                forEntity:(CFStringRef)entity
+{
+       return SCDynamicStoreKeyCreateNetworkServiceEntity(kCFAllocatorDefault,
+                                                          kSCDynamicStoreDomainState,
+                                                          serviceID,
+                                                          entity);
+}
+
+- (NSArray<NWEndpoint *> *)createDNSArray:(NSDictionary *)dnsConfig
+{
+       NSArray<NSString *> *dnsServers;
+       NSMutableArray<NWEndpoint *> *dnsArray;
+
+       dnsServers = [dnsConfig objectForKey:(__bridge NSString *)kSCPropNetDNSServerAddresses];
+       if (dnsServers == nil || [dnsServers count] == 0) {
+               return nil;
+       }
+
+       dnsArray = [[NSMutableArray alloc] init];
+       for (NSString *server in dnsServers) {
+               NWEndpoint *endpoint = (NWEndpoint *)[NWAddressEndpoint endpointWithHostname:server port:@"0"];
+               [dnsArray addObject:endpoint];
+       }
+
+       return dnsArray;
+}
+
+- (void)publishToSCDynamicStore:(NSString *)key
+                         value:(NSDictionary *)value
+{
+
+       BOOL ok = SCDynamicStoreSetValue(self.store, (__bridge CFStringRef)key, (__bridge CFPropertyListRef _Nonnull)(value));
+       if (!ok) {
+               int error = SCError();
+               if (error == kSCStatusNoKey) {
+                       return;
+               }
+               SCTestLog("Could not set in SCDynamicStore: Error: %s", SCErrorString(error));
+               return;
+       }
+}
+
+- (void)removeFromSCDynamicStore:(NSString *)key
+{
+       BOOL ok = SCDynamicStoreRemoveValue(self.store, (__bridge CFStringRef)key);
+       if (!ok) {
+               int error = SCError();
+               if (error == kSCStatusNoKey) {
+                       return;
+               }
+               SCTestLog("Could not remove key: %@, Error: %s", key, SCErrorString(error));
+               return;
+       }
+}
+
+- (NSDictionary *)parseProxyAgentOptions
+{
+       NSMutableDictionary *proxyConfig = [[NSMutableDictionary alloc] init];
+
+#define NS_NUMBER(x) [NSNumber numberWithInt:x]
+
+#define SET_PROXY_CONFIG(proxyType)                                                                                                            \
+       do {                                                                                                                                    \
+               if (self.options[kSCTestConfigAgent ## proxyType ## Proxy] != nil) {                                                            \
+                       NSString *serverAndPortString = self.options[kSCTestConfigAgent ## proxyType ## Proxy];                                 \
+                       NSArray<NSString *> *serverAndPortArray = [serverAndPortString componentsSeparatedByString:@":"];                       \
+                       if ([serverAndPortArray count] != 2) {                                                                                  \
+                               SCTestLog("server address or port missing");                                                                    \
+                               ERR_EXIT;                                                                                                       \
+                       }                                                                                                                       \
+                       NSString *server = [serverAndPortArray objectAtIndex:0];                                                                \
+                       NSString *port = [serverAndPortArray objectAtIndex:1];                                                                  \
+                       [proxyConfig setObject:server forKey:(__bridge NSString *)kSCPropNetProxies ## proxyType ## Proxy];                     \
+                       [proxyConfig setObject:NS_NUMBER(port.intValue) forKey:(__bridge NSString *)kSCPropNetProxies ## proxyType ## Port];    \
+                       [proxyConfig setObject:NS_NUMBER(1) forKey:(__bridge NSString *)kSCPropNetProxies ## proxyType ## Enable];              \
+               }                                                                                                                               \
+       } while(0);
+
+       SET_PROXY_CONFIG(HTTP);
+       SET_PROXY_CONFIG(HTTPS);
+       SET_PROXY_CONFIG(FTP);
+       SET_PROXY_CONFIG(Gopher);
+       SET_PROXY_CONFIG(SOCKS);
+
+       if ([proxyConfig count] > 0) {
+               NSString *matchDomain = self.options[kSCTestConfigAgentProxyMatchDomain] ? self.options[kSCTestConfigAgentProxyMatchDomain] : @TEST_DOMAIN;
+               [proxyConfig setObject:@[matchDomain] forKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains];
+       } else {
+               proxyConfig = nil;
+       }
+
+       return proxyConfig;
+#undef SET_PROXY_CONFIG
+}
+
+- (NSDictionary *)parseDNSAgentOptions
+{
+       NSMutableDictionary *dnsConfig;
+       NSString *dnsServerString;
+       NSString *dnsDomainString;
+       NSArray<NSString *> *dnsServers;
+       NSArray<NSString *> *dnsDomains;
+
+       dnsConfig = [[NSMutableDictionary alloc] init];
+       dnsServerString = self.options[kSCTestConfigAgentDNSServers];
+       if (dnsServerString == nil) {
+               return nil;
+       }
+
+       dnsDomainString = self.options[kSCTestConfigAgentDNSDomains];
+       if (dnsDomainString == nil) {
+               dnsDomainString = @TEST_DOMAIN;
+       }
+
+       dnsServers = [dnsServerString componentsSeparatedByString:@","];
+       [dnsConfig setObject:dnsServers forKey:(__bridge NSString *)kSCPropNetDNSServerAddresses];
+
+       dnsDomains = [dnsDomainString componentsSeparatedByString:@","];
+       [dnsConfig setObject:dnsDomains forKey:(__bridge NSString *)kSCPropNetDNSSupplementalMatchDomains];
+
+       return dnsConfig;
+}
+
+- (void)cleanupAndExitWithErrorCode:(int)error
+{
+       CFRelease(self.store);
+       [super cleanupAndExitWithErrorCode:error];
+}
+
+- (BOOL)setup
+{
+       return YES;
+}
+
+- (BOOL)unitTest
+{
+       if(![self setup]) {
+               return NO;
+       }
+
+       BOOL allUnitTestsPassed = YES;
+       allUnitTestsPassed &= [self unitTestInstallProxy];
+       allUnitTestsPassed &= [self unitTestInstallProxyWithLargeConfig];
+       allUnitTestsPassed &= [self unitTestInstallProxyWithConflictingDomain];
+       allUnitTestsPassed &= [self unitTestInstallDNS];
+       allUnitTestsPassed &= [self unitTestInstallDNSWithConflictingDomain];
+
+       if(![self tearDown]) {
+               return NO;
+       }
+
+       return allUnitTestsPassed;
+}
+
+- (BOOL)unitTestInstallProxy
+{
+       BOOL success = NO;
+       SCTestConfigAgent *test;
+       NSDictionary *proxyConfig;
+       NSString *hostname;
+       NSNumber *port;
+       NWHostEndpoint *hostEndpoint;
+       NWPathEvaluator *pathEvaluator;
+       NSMutableDictionary *dict;
+
+       test = [[SCTestConfigAgent alloc] initWithOptions:self.options];
+       proxyConfig = [test parseProxyAgentOptions];
+       if (proxyConfig == nil) {
+               // Use default options
+               proxyConfig = @{(__bridge NSString *)kSCPropNetProxiesHTTPEnable:@(1),
+                               (__bridge NSString *)kSCPropNetProxiesHTTPPort:@(80),
+                               (__bridge NSString *)kSCPropNetProxiesHTTPProxy:@"10.10.10.100",
+                               (__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains:@[@TEST_DOMAIN],
+                               };
+       }
+
+       hostname = [[proxyConfig objectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains] objectAtIndex:0];
+       port = [proxyConfig objectForKey:(__bridge NSString *)kSCPropNetProxiesHTTPPort];
+       hostEndpoint = [NWHostEndpoint endpointWithHostname:hostname port:port.stringValue];
+       pathEvaluator = [[NWPathEvaluator alloc] initWithEndpoint:hostEndpoint parameters:NULL];
+       [pathEvaluator addObserver:test
+                       forKeyPath:@"path"
+                          options:NSKeyValueObservingOptionNew
+                          context:nil];
+
+       do {
+               [test publishToSCDynamicStore:test.proxyKey value:proxyConfig];
+               dict = [NSMutableDictionary dictionaryWithDictionary:proxyConfig];
+               [dict removeObjectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains];
+               test.testProxy = @[@[dict]];
+
+               // Wait for the path changes to quiesce
+               [test waitFor:PATH_QUIESCE_TIME];
+               if (![test.testProxy isEqualToArray:test.pathProxy]) {
+                       SCTestLog("test proxy and applied proxy do not match. Test: %@, Applied: %@", test.testProxy, test.pathProxy);
+                       break;
+               }
+
+               SCTestLog("Verified the configured proxy is the same as applied proxy");
+               [test removeFromSCDynamicStore:test.proxyKey];
+               test.testProxy = nil;
+               // Wait for the path changes to quiesce
+               [test waitFor:PATH_QUIESCE_TIME];
+               if (test.pathProxy != nil) {
+                       SCTestLog("proxy applied when there is no test proxy");
+                       break;
+               }
+
+               success = YES;
+       } while(0);
+
+       [pathEvaluator removeObserver:test
+                          forKeyPath:@"path"];
+
+       return success;
+}
+
+- (BOOL)unitTestInstallDNS
+{
+       BOOL success = NO;
+       SCTestConfigAgent *test;
+       NSDictionary *dnsConfig;
+       NSString *hostname;
+       NWHostEndpoint *hostEndpoint;
+       NWPathEvaluator *pathEvaluator;
+
+       test = [[SCTestConfigAgent alloc] initWithOptions:self.options];
+       dnsConfig = [test parseDNSAgentOptions];
+       if (dnsConfig == nil) {
+               dnsConfig = @{  (__bridge NSString *)kSCPropNetDNSServerAddresses:@[@"10.10.10.101", @"10.10.10.102", @"10.10.10.103"],
+                               (__bridge NSString *)kSCPropNetDNSSupplementalMatchDomains:@[@TEST_DOMAIN],
+                               };
+       }
+
+       hostname = [[dnsConfig objectForKey:(__bridge NSString *)kSCPropNetDNSSupplementalMatchDomains] objectAtIndex:0];
+       hostEndpoint = [NWHostEndpoint endpointWithHostname:hostname port:@"80"];
+       pathEvaluator = [[NWPathEvaluator alloc] initWithEndpoint:hostEndpoint parameters:NULL];
+       [pathEvaluator addObserver:test
+                       forKeyPath:@"path"
+                          options:NSKeyValueObservingOptionNew
+                          context:nil];
+
+       do {
+               [test publishToSCDynamicStore:test.dnsKey value:dnsConfig];
+               test.testDNS = [test createDNSArray:dnsConfig];
+
+               // Wait for the path changes to quiesce
+               [test waitFor:PATH_QUIESCE_TIME];
+               if (![test.testDNS isEqualToArray:test.pathDNS]) {
+                       SCTestLog("test DNS and applied DNS do not match. Test: %@, Applied: %@", test.testDNS, test.pathDNS);
+                       break;
+               }
+
+               [test removeFromSCDynamicStore:test.dnsKey];
+               test.testDNS = nil;
+               [test waitFor:PATH_QUIESCE_TIME];
+
+               SCTestLog("Verified that the configured DNS is same as applied DNS for a domain");
+               success = YES;
+       } while (0);
+
+       [pathEvaluator removeObserver:test
+                          forKeyPath:@"path"];
+
+       return success;
+}
+
+- (BOOL)unitTestInstallProxyWithLargeConfig
+{
+       BOOL success = NO;
+       SCTestConfigAgent *test;
+       NSString *str = @"0123456789";
+       NSMutableString *largeStr;
+       NSDictionary *proxyConfig;
+       NSString *hostname;
+       NSNumber *port;
+       NWHostEndpoint *hostEndpoint;
+       NWPathEvaluator *pathEvaluator;
+       NSMutableDictionary *dict;
+
+       test = [[SCTestConfigAgent alloc] initWithOptions:self.options];
+       largeStr = [[NSMutableString alloc] init];
+       for (int i = 0; i < 200; i++) {
+               [largeStr appendString:str];
+       }
+
+       // We imitate a proxy config worth 2K bytes.
+       proxyConfig = @{(__bridge NSString *)kSCPropNetProxiesHTTPEnable:@(1),
+                       (__bridge NSString *)kSCPropNetProxiesHTTPPort:@(80),
+                       (__bridge NSString *)kSCPropNetProxiesHTTPProxy:@"10.10.10.100",
+                       (__bridge NSString *)kSCPropNetProxiesProxyAutoConfigJavaScript:largeStr,
+                       (__bridge NSString *)kSCPropNetProxiesProxyAutoConfigEnable:@(1),
+                       (__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains:@[@TEST_DOMAIN],
+                       };
+
+       hostname = [[proxyConfig objectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains] objectAtIndex:0];
+       port = [proxyConfig objectForKey:(__bridge NSString *)kSCPropNetProxiesHTTPPort];
+       hostEndpoint = [NWHostEndpoint endpointWithHostname:hostname port:port.stringValue];
+       pathEvaluator = [[NWPathEvaluator alloc] initWithEndpoint:hostEndpoint parameters:NULL];
+       [pathEvaluator addObserver:test
+                       forKeyPath:@"path"
+                          options:NSKeyValueObservingOptionNew
+                          context:nil];
+
+       do {
+               [test publishToSCDynamicStore:test.proxyKey value:proxyConfig];
+               dict = [NSMutableDictionary dictionaryWithDictionary:proxyConfig];
+               [dict removeObjectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains];
+               test.testProxy = @[@[dict]];
+
+               // Wait for the path changes to quiesce
+               [test waitFor:PATH_QUIESCE_TIME];
+               if ([test.testProxy isEqualToArray:test.pathProxy]) {
+                       SCTestLog("applied proxy does not contain Out of Band Agent UUID");
+                       break;
+               }
+
+               // Now we verify that we are able to fetch the proxy configuration from configd
+               for (NSArray<NSDictionary *> *config in test.pathProxy) {
+                       xpc_object_t xpcConfig = _CFXPCCreateXPCObjectFromCFObject((__bridge CFArrayRef)config);
+                       xpc_object_t fetchedConfig = config_agent_update_proxy_information(xpcConfig);
+                       if (fetchedConfig != nil) {
+                               NSArray *nsConfig = (__bridge_transfer NSArray *)(_CFXPCCreateCFObjectFromXPCObject(fetchedConfig));
+                               test.pathProxy = @[nsConfig];
+                               break;
+                       }
+               }
+
+               if (![test.testProxy isEqualToArray:test.pathProxy]) {
+                       SCTestLog("Could not fetch proxy configuration from configd. Test: %@, Applied: %@", test.testProxy, test.pathProxy);
+                       break;
+               }
+
+               SCTestLog("Verified that the proxy configuration is successfully fetched from configd");
+               test.testProxy = nil;
+               [test removeFromSCDynamicStore:test.proxyKey];
+
+               // Wait for the path changes to quiesce
+               [test waitFor:PATH_QUIESCE_TIME];
+               if (test.pathProxy != nil) {
+                       SCTestLog("proxy applied when there is no test proxy");
+                       break;
+               }
+
+               success = YES;
+       } while (0);
+
+       [pathEvaluator removeObserver:test
+                          forKeyPath:@"path"];
+
+       return success;
+}
+
+- (BOOL)unitTestInstallDNSWithConflictingDomain
+{
+       BOOL success = NO;
+       SCTestConfigAgent *test;
+       NSDictionary *dnsConfig;
+       NSString *hostname;
+       NWHostEndpoint *hostEndpoint;
+       NWPathEvaluator *pathEvaluator;
+
+       test = [[SCTestConfigAgent alloc] initWithOptions:self.options];
+       dnsConfig = @{  (__bridge NSString *)kSCPropNetDNSServerAddresses:@[@"10.10.10.101", @"10.10.10.102", @"10.10.10.103"],
+                       (__bridge NSString *)kSCPropNetDNSSupplementalMatchDomains:@[@TEST_DOMAIN],
+                       };
+
+       hostname = [[dnsConfig objectForKey:(__bridge NSString *)kSCPropNetDNSSupplementalMatchDomains] objectAtIndex:0];
+       hostEndpoint = [NWHostEndpoint endpointWithHostname:hostname port:@"80"];
+       pathEvaluator = [[NWPathEvaluator alloc] initWithEndpoint:hostEndpoint parameters:NULL];
+       [pathEvaluator addObserver:test
+                       forKeyPath:@"path"
+                          options:NSKeyValueObservingOptionNew
+                          context:nil];
+
+       do {
+               NSDictionary *duplicateDnsConfig;
+               NSString *anotherFakeServiceID;
+               NSString *anotherDNSKey;
+               NSArray *array;
+               NSSet *testDNSSet;
+               NSSet *pathDNSSet;
+
+               [test publishToSCDynamicStore:test.dnsKey value:dnsConfig];
+               test.testDNS = [test createDNSArray:dnsConfig];
+
+               // Wait for the path changes to quiesce
+               [test waitFor:PATH_QUIESCE_TIME];
+               if (![test.testDNS isEqualToArray:test.pathDNS]) {
+                       SCTestLog("test DNS and applied DNS do not match. Test: %@, Applied: %@", test.testDNS, test.pathDNS);
+                       break;
+               }
+
+               // Now install the conflicting DNS configuration
+               duplicateDnsConfig = @{ (__bridge NSString *)kSCPropNetDNSServerAddresses:@[@"10.10.10.104", @"10.10.10.105", @"10.10.10.106"],
+                                       (__bridge NSString *)kSCPropNetDNSSupplementalMatchDomains:@[@TEST_DOMAIN],
+                                       };
+
+               anotherFakeServiceID = [NSUUID UUID].UUIDString;
+               anotherDNSKey = (__bridge_transfer NSString *)[self copyStateKeyWithServiceID:(__bridge CFStringRef)(anotherFakeServiceID) forEntity:kSCEntNetDNS];
+
+               [test publishToSCDynamicStore:anotherDNSKey value:duplicateDnsConfig];
+               array = [test.testDNS arrayByAddingObjectsFromArray:[test createDNSArray:duplicateDnsConfig]];
+               test.testDNS = array;
+
+               // Wait for the path changes to quiesce
+               [test waitFor:PATH_QUIESCE_TIME];
+
+               // Use NSSet for unordered comparison
+               testDNSSet = [NSSet setWithArray:test.testDNS];
+               pathDNSSet = [NSSet setWithArray:test.pathDNS];
+               success = [testDNSSet isEqualToSet:pathDNSSet];
+               [test removeFromSCDynamicStore:anotherDNSKey];
+               if (!success) {
+                       SCTestLog("test DNS and applied DNS for duplicate domains do not match. Test: %@, Applied: %@", test.testDNS, test.pathDNS);
+                       break;
+               }
+
+               [test removeFromSCDynamicStore:test.dnsKey];
+               test.testDNS = nil;
+               [test waitFor:PATH_QUIESCE_TIME];
+
+               SCTestLog("Verified that the configured DNS with duplicate domains is same as applied DNS for a domain");
+               success = YES;
+
+       } while (0);
+
+       [pathEvaluator removeObserver:test
+                          forKeyPath:@"path"];
+
+       return success;
+}
+
+- (BOOL)unitTestInstallProxyWithConflictingDomain
+{
+       BOOL success = NO;
+       SCTestConfigAgent *test;
+       NSDictionary *proxyConfig;
+       NSString *hostname;
+       NSNumber *port;
+       NWHostEndpoint *hostEndpoint;
+       NWPathEvaluator *pathEvaluator;
+
+       test = [[SCTestConfigAgent alloc] initWithOptions:self.options];
+       proxyConfig = @{(__bridge NSString *)kSCPropNetProxiesHTTPEnable:@(1),
+                       (__bridge NSString *)kSCPropNetProxiesHTTPPort:@(80),
+                       (__bridge NSString *)kSCPropNetProxiesHTTPProxy:@"10.10.10.100",
+                       (__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains:@[@TEST_DOMAIN],
+                       };
+
+       hostname = [[proxyConfig objectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains] objectAtIndex:0];
+       port = [proxyConfig objectForKey:(__bridge NSString *)kSCPropNetProxiesHTTPPort];
+       hostEndpoint = [NWHostEndpoint endpointWithHostname:hostname port:port.stringValue];
+       pathEvaluator = [[NWPathEvaluator alloc] initWithEndpoint:hostEndpoint parameters:NULL];
+       [pathEvaluator addObserver:test
+                       forKeyPath:@"path"
+                          options:NSKeyValueObservingOptionNew
+                          context:nil];
+
+       do {
+               NSMutableDictionary *dict;
+               NSMutableDictionary *dict2;
+               NSDictionary *duplicateProxyConfig;
+               NSString *anotherFakeServiceID;
+               NSString *anotherProxyKey;
+               NSSet *testProxySet;
+               NSSet *pathProxySet;
+
+               [test publishToSCDynamicStore:test.proxyKey value:proxyConfig];
+               dict = [NSMutableDictionary dictionaryWithDictionary:proxyConfig];
+               [dict removeObjectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains];
+               test.testProxy = @[@[dict]];
+
+               // Wait for the path changes to quiesce
+               [test waitFor:PATH_QUIESCE_TIME];
+               if (![test.testProxy isEqualToArray:test.pathProxy]) {
+                       SCTestLog("test proxy and applied proxy do not match. Test: %@, Applied: %@", test.testProxy, test.pathProxy);
+                       break;
+               }
+
+               // Now install the conflicting Proxy configuration
+               duplicateProxyConfig = @{(__bridge NSString *)kSCPropNetProxiesHTTPSEnable:@(1),
+                                                       (__bridge NSString *)kSCPropNetProxiesHTTPSPort:@(8080),
+                                                       (__bridge NSString *)kSCPropNetProxiesHTTPSProxy:@"10.10.10.101",
+                                                       (__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains:@[@TEST_DOMAIN],
+                                                       };
+               anotherFakeServiceID = [NSUUID UUID].UUIDString;
+               anotherProxyKey = (__bridge_transfer NSString *)[self copyStateKeyWithServiceID:(__bridge CFStringRef)(anotherFakeServiceID) forEntity:kSCEntNetProxies];
+
+               [test publishToSCDynamicStore:anotherProxyKey value:duplicateProxyConfig];;
+               dict2 = [NSMutableDictionary dictionaryWithDictionary:duplicateProxyConfig];
+               [dict2 removeObjectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains];
+               test.testProxy = @[@[dict],@[dict2]];
+
+               // Wait for the path changes to quiesce
+               [test waitFor:PATH_QUIESCE_TIME];
+
+               // Use NSSet for unordered comparison
+               testProxySet = [NSSet setWithArray:test.testProxy];
+               pathProxySet = [NSSet setWithArray:test.pathProxy];
+               success = [testProxySet isEqualToSet:pathProxySet];
+               [test removeFromSCDynamicStore:anotherProxyKey];
+               if (!success) {
+                       SCTestLog("test proxy and applied proxy for duplicate domains do not match. Test: %@, Applied: %@", test.testDNS, test.pathDNS);
+                       break;
+               }
+
+               SCTestLog("Verified the configured proxy with Duplicate domains is the same as applied proxy");
+
+               [test removeFromSCDynamicStore:test.proxyKey];
+               test.testProxy = nil;
+
+               // Wait for the path changes to quiesce
+               [test waitFor:PATH_QUIESCE_TIME];
+
+               if (test.pathProxy != nil) {
+                       SCTestLog("proxy applied when there is no test proxy");
+                       break;
+               }
+
+               success = YES;
+
+       } while(0);
+
+       [pathEvaluator removeObserver:test
+                          forKeyPath:@"path"];
+
+       return success;
+}
+
+- (BOOL)tearDown
+{
+       [self removeFromSCDynamicStore:self.proxyKey];
+       [self removeFromSCDynamicStore:self.dnsKey];
+       return YES;
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath
+                       ofObject:(id)object
+                       change:(NSDictionary *)change
+               context:(void *)context
+{
+       NWPathEvaluator *pathEvaluator = (NWPathEvaluator *)object;
+       if ([keyPath isEqualToString:@"path"]) {
+               self.pathProxy = pathEvaluator.path.proxySettings;
+               self.pathDNS = pathEvaluator.path.dnsServers;
+       }
+}
+
+@end
diff --git a/sctest/SCTestDynamicStore.m b/sctest/SCTestDynamicStore.m
new file mode 100644 (file)
index 0000000..6ece952
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+ * Copyright (c) 2016 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@
+ */
+
+#import "SCTest.h"
+#import "SCTestUtils.h"
+
+@interface SCTestDynamicStore : SCTest
+@property SCDynamicStoreRef store;
+@property dispatch_semaphore_t sem;
+@property int counter;
+@end
+
+@implementation SCTestDynamicStore
+
++ (NSString *)command
+{
+       return @"dynamic_store";
+}
+
++ (NSString *)commandDescription
+{
+       return @"Tests the SCDynamicStore code path";
+}
+
+- (instancetype)initWithOptions:(NSDictionary *)options
+{
+       self = [super initWithOptions:options];
+       if (self) {
+               _store = SCDynamicStoreCreate(kCFAllocatorDefault,
+                                                 CFSTR("SCTest"),
+                                                 NULL,
+                                                 NULL);
+               if (_store == NULL) {
+                       SCTestLog("Could not create session");
+                       ERR_EXIT;
+               }
+       }
+       return self;
+}
+
+- (void)dealloc
+{
+       if (self.store != NULL) {
+               CFRelease(self.store);
+               self.store = NULL;
+       }
+}
+
+- (void)start
+{
+       CFStringRef key;
+       if (self.options[kSCTestDynamicStoreOptionDNS]) {
+               key = SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault, kSCDynamicStoreDomainState, kSCEntNetDNS);
+               CFPropertyListRef value = SCDynamicStoreCopyValue(self.store, key);
+               SCTestLog("%@ : %@", key, value);
+               CFRelease(key);
+       }
+
+       if (self.options[kSCTestDynamicStoreOptionIPv4]) {
+               key = SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault, kSCDynamicStoreDomainState, kSCEntNetIPv4);
+               CFPropertyListRef value = SCDynamicStoreCopyValue(self.store, key);
+               SCTestLog("%@ : %@", key, value);
+               CFRelease(key);
+       }
+
+       if (self.options[kSCTestDynamicStoreOptionIPv6]) {
+               key = SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault, kSCDynamicStoreDomainState, kSCEntNetIPv6);
+               CFPropertyListRef value = SCDynamicStoreCopyValue(self.store, key);
+               SCTestLog("%@ : %@", key, value);
+               CFRelease(key);
+       }
+
+       if (self.options[kSCTestDynamicStoreOptionProxies]) {
+               key = SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault, kSCDynamicStoreDomainState, kSCEntNetProxies);
+               CFPropertyListRef value = SCDynamicStoreCopyValue(self.store, key);
+               SCTestLog("%@ : %@", key, value);
+               CFRelease(key);
+       }
+
+       [self cleanupAndExitWithErrorCode:0];
+}
+
+- (void)cleanupAndExitWithErrorCode:(int)error
+{
+       [super cleanupAndExitWithErrorCode:error];
+}
+
+- (BOOL)setup
+{
+       return YES;
+}
+
+- (BOOL)unitTest
+{
+       if(![self setup]) {
+               return NO;
+       }
+
+       BOOL allUnitTestsPassed = YES;
+       allUnitTestsPassed &= [self unitTestSetAndRemoveValue];
+       allUnitTestsPassed &= [self unitTestCopyMultiple];
+       allUnitTestsPassed &= [self unitTestSCDynamicStoreCallbackStress];
+       allUnitTestsPassed &= [self unitTestSCDynamicStoreSetMultipleStress];
+
+       if(![self tearDown]) {
+               return NO;
+       }
+
+       return allUnitTestsPassed;
+}
+
+- (BOOL)tearDown
+{
+       return YES;
+}
+
+- (BOOL)unitTestSetAndRemoveValue
+{
+       int iterations = 1000;
+       NSDictionary *bogusValue;
+       SCTestDynamicStore *test;
+
+       test = [[SCTestDynamicStore alloc] initWithOptions:self.options];
+       bogusValue = @{@"Pretty":@"Useless"};
+
+       for (int i = 0; i < iterations; i++) {
+               NSUUID *uuid = [NSUUID UUID];
+               CFStringRef key;
+               BOOL ok;
+
+               key = SCDynamicStoreKeyCreateNetworkServiceEntity(kCFAllocatorDefault,
+                                                                 CFSTR("SCTest"),
+                                                                 (__bridge CFStringRef)uuid.UUIDString,
+                                                                 kSCEntNetDNS);
+               ok = SCDynamicStoreSetValue(test.store, key, (__bridge CFDictionaryRef)bogusValue);
+               if (!ok) {
+                       SCTestLog("Failed to set value in SCDynamicStore. Error: %s", SCErrorString(SCError()));
+                       CFRelease(key);
+                       return NO;
+               }
+
+               ok = SCDynamicStoreRemoveValue(test.store, key);
+               if (!ok) {
+                       SCTestLog("Failed to remove value from SCDynamicStore. Error: %s", SCErrorString(SCError()));
+                       CFRelease(key);
+                       return NO;
+               }
+
+               CFRelease(key);
+       }
+
+       SCTestLog("Successfully completed setAndRemove unit test");
+       return YES;
+}
+
+- (BOOL)unitTestCopyMultiple
+{
+       NSString *pattern;
+       SCTestDynamicStore *test;
+       CFArrayRef keyList;
+       int iterations = 1000;
+
+       test = [[SCTestDynamicStore alloc] initWithOptions:self.options];
+       pattern = (__bridge_transfer NSString *)SCDynamicStoreKeyCreate(kCFAllocatorDefault,
+                                                                       CFSTR("%@/%@/%@/%@/%@"),
+                                                                       kSCDynamicStoreDomainState,
+                                                                       kSCCompNetwork,
+                                                                       kSCCompInterface,
+                                                                       kSCCompAnyRegex,
+                                                                       kSCCompAnyRegex);
+
+       keyList = SCDynamicStoreCopyKeyList(test.store, (__bridge CFStringRef)pattern);
+       for (int i = 0; i < iterations; i++) {
+               CFDictionaryRef value = SCDynamicStoreCopyMultiple(test.store, keyList, NULL);
+               if (value == NULL) {
+                       SCTestLog("Failed to copy multiple values from SCDynamicStore. Error: %s", SCErrorString(SCError()));
+                       CFRelease(keyList);
+                       return NO;
+               }
+               CFRelease(value);
+       }
+       CFRelease(keyList);
+
+       pattern = (__bridge_transfer NSString *)SCDynamicStoreKeyCreate(kCFAllocatorDefault,
+                                                                       CFSTR("%@/%@/%@/%@/%@"),
+                                                                       kSCDynamicStoreDomainSetup,
+                                                                       kSCCompNetwork,
+                                                                       kSCCompService,
+                                                                       kSCCompAnyRegex,
+                                                                       kSCCompAnyRegex);
+
+       keyList = SCDynamicStoreCopyKeyList(test.store, (__bridge CFStringRef)pattern);
+       for (int i = 0; i < iterations; i++) {
+               CFDictionaryRef value = SCDynamicStoreCopyMultiple(test.store, keyList, NULL);
+               if (value == NULL) {
+                       SCTestLog("Failed to copy multiple values from SCDynamicStore. Error: %s", SCErrorString(SCError()));
+                       CFRelease(keyList);
+                       return NO;
+               }
+               CFRelease(value);
+       }
+       CFRelease(keyList);
+
+       SCTestLog("Successfully completed copyMultiple unit test");
+       return YES;
+}
+
+void
+myTestCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *ctx)
+{
+       SCTestDynamicStore *test = (__bridge SCTestDynamicStore *)ctx;
+       test.counter++;
+       if (test.sem != NULL) {
+               dispatch_semaphore_signal(test.sem);
+       }
+}
+
+- (BOOL)unitTestSCDynamicStoreCallbackStress
+{
+       SCTestDynamicStore *test;
+       int iterations = 100;
+       NSString *testKey = @"State:/myTestKey";
+       BOOL ok;
+       dispatch_queue_t callbackQ;
+
+       test = [[SCTestDynamicStore alloc] initWithOptions:self.options];
+       if (test.store != NULL) {
+               CFRelease(test.store);
+               test.store = NULL;
+       }
+
+       SCDynamicStoreContext ctx = {0, (__bridge void * _Nullable)(test), CFRetain, CFRelease, NULL};
+       test.store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("SCTest"), myTestCallback, &ctx);
+
+       ok = SCDynamicStoreSetNotificationKeys(test.store, (__bridge CFArrayRef)@[testKey], NULL);
+       if (!ok) {
+               SCTestLog("Failed to set notification keys. Error: %s", SCErrorString(SCError()));
+               return NO;
+       }
+
+       callbackQ = dispatch_queue_create("SCTestDynamicStore callback queue", NULL);
+       ok = SCDynamicStoreSetDispatchQueue(test.store, callbackQ);
+       if (!ok) {
+               SCTestLog("Failed to set dispatch queue. Error: %s", SCErrorString(SCError()));
+               return NO;
+       }
+
+       for (int i = 0; i < iterations; i++) {
+               NSUUID *uuid = [NSUUID UUID];
+               ok = SCDynamicStoreSetValue(test.store, (__bridge CFStringRef)testKey, (__bridge CFStringRef)uuid.UUIDString);
+               if (!ok) {
+                       SCTestLog("Failed to set value. Error: %s", SCErrorString(SCError()));
+                       return NO;
+               }
+               // Perform a write to Dynamic Store every 10ms.
+               [test waitFor:0.01];
+       }
+
+       ok = SCDynamicStoreRemoveValue(test.store, (__bridge CFStringRef)testKey);
+       if (!ok) {
+               SCTestLog("Failed to remove value. Error: %s", SCErrorString(SCError()));
+               return NO;
+       }
+
+       if (test.counter < iterations) {
+               // Not all callbacks were received
+               SCTestLog("Not all SCDynamicStore callbacks were received (%d/%d). Something went wrong", test.counter, iterations);
+               return NO;
+       }
+
+       SCTestLog("Successfully completed SCDynamicStore callbacks unit test");
+       return YES;
+}
+
+- (BOOL)unitTestSCDynamicStoreSetMultipleStress
+{
+       SCTestDynamicStore *test;
+       int iterations = 100;
+       NSMutableArray *testKeyArray;
+       NSMutableDictionary *testSetDictionary;
+       int expectedCallbackCount = 0;
+       BOOL ok;
+       dispatch_queue_t callbackQ;
+       uint8_t waitTime = 1; // second
+
+       test = [[SCTestDynamicStore alloc] initWithOptions:self.options];
+       if (test.store != NULL) {
+               CFRelease(test.store);
+               test.store = NULL;
+       }
+
+       SCDynamicStoreContext ctx = {0, (__bridge void * _Nullable)(test), CFRetain, CFRelease, NULL};
+       test.store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("SCTest"), myTestCallback, &ctx);
+       test.sem = dispatch_semaphore_create(0);
+
+       testKeyArray = [[NSMutableArray alloc] init];
+
+       for (int i = 0; i < iterations; i++) {
+               NSUUID *uuid = [NSUUID UUID];
+               NSString *str = [NSString stringWithFormat:@"State:/%@", uuid];
+               [testKeyArray addObject:str];
+       }
+
+       testSetDictionary = [[NSMutableDictionary alloc] init];
+       for (NSString *key in testKeyArray) {
+               NSUUID *uuid = [NSUUID UUID];
+               [testSetDictionary setObject:@[uuid.UUIDString] forKey:key];
+       }
+
+       callbackQ = dispatch_queue_create("SCTestDynamicStore callback queue", NULL);
+       ok = SCDynamicStoreSetNotificationKeys(test.store, (__bridge CFArrayRef)testKeyArray, NULL);
+       if (!ok) {
+               SCTestLog("Failed to set notification keys. Error: %s", SCErrorString(SCError()));
+               return NO;
+       }
+
+       ok = SCDynamicStoreSetDispatchQueue(test.store, callbackQ);
+       if (!ok) {
+               SCTestLog("Failed to set dispatch queue. Error: %s", SCErrorString(SCError()));
+               return NO;
+       }
+
+       ok = SCDynamicStoreSetMultiple(test.store, (__bridge CFDictionaryRef)testSetDictionary, NULL, NULL);
+       if (!ok) {
+               SCTestLog("Failed to set multiple keys. Error: %s", SCErrorString(SCError()));
+               return NO;
+       } else {
+               expectedCallbackCount++; // One callback for set multiple
+       }
+
+       if(dispatch_semaphore_wait(test.sem, dispatch_time(DISPATCH_TIME_NOW, waitTime * NSEC_PER_SEC))) {
+               SCTestLog("Failed to get a callback when multiple keys are set");
+               return NO;
+       }
+
+       ok = SCDynamicStoreSetMultiple(test.store, NULL, NULL, (__bridge CFArrayRef)testKeyArray);
+       if (!ok) {
+               SCTestLog("Failed to set multiple keys. Error: %s", SCErrorString(SCError()));
+               return NO;
+       } else {
+               expectedCallbackCount++; // One callback for fake notification
+       }
+
+       if(dispatch_semaphore_wait(test.sem, dispatch_time(DISPATCH_TIME_NOW, waitTime * NSEC_PER_SEC))) {
+               SCTestLog("Failed to get a callback when multiple keys are notified");
+               return NO;
+       }
+
+       ok = SCDynamicStoreSetMultiple(test.store, NULL, (__bridge CFArrayRef)testKeyArray, NULL);
+       if (!ok) {
+               SCTestLog("Failed to set multiple keys. Error: %s", SCErrorString(SCError()));
+               return NO;
+       } else {
+               expectedCallbackCount++; // One callback for key removal
+       }
+
+       if(dispatch_semaphore_wait(test.sem, dispatch_time(DISPATCH_TIME_NOW, waitTime * NSEC_PER_SEC))) {
+               SCTestLog("Failed to get a callback when multiple keys are removed");
+               return NO;
+       }
+
+       if (test.counter < expectedCallbackCount) {
+               // Not all callbacks were received
+               SCTestLog("Not all SCDynamicStore callbacks were received (%d/%d). Something went wrong", test.counter, expectedCallbackCount);
+               return NO;
+       }
+
+       SCTestLog("Successfully completed SCDynamicStore set multiple unit test");
+       return YES;
+
+}
+
+
+@end
diff --git a/sctest/SCTestOptions.h b/sctest/SCTestOptions.h
new file mode 100644 (file)
index 0000000..665987c
--- /dev/null
@@ -0,0 +1,106 @@
+//
+// This file is automatically generated. DO NOT EDIT!
+// To add options, see genSCTestOptions.c
+//
+#import <Foundation/Foundation.h>
+#import <getopt.h>
+
+extern const NSString * const kSCTestGlobalOptionCPU;
+extern const NSString * const kSCTestGlobalOptionHelp;
+extern const NSString * const kSCTestGlobalOptionTime;
+extern const NSString * const kSCTestGlobalOptionVerbose;
+extern const NSString * const kSCTestGlobalOptionWait;
+extern const NSString * const kSCTestDynamicStoreOptionDNS;
+extern const NSString * const kSCTestDynamicStoreOptionIPv4;
+extern const NSString * const kSCTestDynamicStoreOptionIPv6;
+extern const NSString * const kSCTestDynamicStoreOptionProxies;
+extern const NSString * const kSCTestPreferencesServiceList;
+extern const NSString * const kSCTestPreferencesServiceOrder;
+extern const NSString * const kSCTestConfigAgentDNSDomains;
+extern const NSString * const kSCTestConfigAgentDNSServers;
+extern const NSString * const kSCTestConfigAgentRemoveDNS;
+extern const NSString * const kSCTestConfigAgentFTPProxy;
+extern const NSString * const kSCTestConfigAgentGopherProxy;
+extern const NSString * const kSCTestConfigAgentHTTPProxy;
+extern const NSString * const kSCTestConfigAgentHTTPSProxy;
+extern const NSString * const kSCTestConfigAgentProxyMatchDomain;
+extern const NSString * const kSCTestConfigAgentRemoveProxy;
+extern const NSString * const kSCTestConfigAgentSOCKSProxy;
+extern const NSString * const kSCTestReachabilityAddress;
+extern const NSString * const kSCTestReachabilityHost;
+extern const NSString * const kSCTestReachabilityInterface;
+extern const NSString * const kSCTestReachabilityWatch;
+extern const NSString * const kSCTestUnitTestCommand;
+extern const NSString * const kSCTestUnitTestListTests;
+extern const NSString * const kSCTestUnitTestTestMethod;
+extern const NSString * const kSCTestUnitTestTestMethodList;
+
+
+
+#define kSCTestOptionEntries \
+               {"cpu", 0, NULL, 0}, \
+               {"help", 0, NULL, 0}, \
+               {"time", 0, NULL, 0}, \
+               {"verbose", 0, NULL, 0}, \
+               {"wait", 0, NULL, 0}, \
+               {"dns", 0, NULL, 0}, \
+               {"ipv4", 0, NULL, 0}, \
+               {"ipv6", 0, NULL, 0}, \
+               {"proxies", 0, NULL, 0}, \
+               {"service_list", 0, NULL, 0}, \
+               {"service_order", 0, NULL, 0}, \
+               {"dns_domain", 1, NULL, 0}, \
+               {"dns_servers", 1, NULL, 0}, \
+               {"remove_dns", 0, NULL, 0}, \
+               {"ftp_proxy", 1, NULL, 0}, \
+               {"gopher_proxy", 1, NULL, 0}, \
+               {"http_proxy", 1, NULL, 0}, \
+               {"https_proxy", 1, NULL, 0}, \
+               {"proxy_match_domain", 1, NULL, 0}, \
+               {"remove_proxy", 0, NULL, 0}, \
+               {"socks_proxy", 1, NULL, 0}, \
+               {"address", 1, NULL, 0}, \
+               {"host", 1, NULL, 0}, \
+               {"interface", 1, NULL, 0}, \
+               {"watch", 0, NULL, 0}, \
+               {"command", 1, NULL, 0}, \
+               {"list_tests", 0, NULL, 0}, \
+               {"test_method", 1, NULL, 0}, \
+               {"test_method_list", 0, NULL, 0}, \
+
+#define kSCTestOptionHelp \
+               "\n============== global options =============\n"\
+               "-cpu                 : Prints the CPU usage after the test completes\n"\
+               "-help                : Prints this very useful help!\n"\
+               "-time                : Prints the time elapsed since the test was launched\n"\
+               "-verbose             : Enables verbose mode\n"\
+               "-wait                : Results in a wait for 'sctest'\n"\
+               "\n============== dynamic_store options =============\n"\
+               "-dns                 : Prints the global DNS information from the SCDynamicStore\n"\
+               "-ipv4                : Prints the global IPv4 information from the SCDynamicStore\n"\
+               "-ipv6                : Prints the global IPv6 information from the SCDynamicStore\n"\
+               "-proxies             : Prints the global Proxy information from the SCDynamicStore\n"\
+               "\n============== preferences options =============\n"\
+               "-service_list        : Prints the Network Services list from the preferences\n"\
+               "-service_order       : Prints the Network Service order from the preferences\n"\
+               "\n============== config_agent options =============\n"\
+               "-dns_domain          : Configures the DNS Servers for certain domains. A comma-separated list of domains can be specified. Default is 'apple.com'\n"\
+               "-dns_servers         : Configures the specified DNS Servers. A comma-separated list of IP Addresses can be specified\n"\
+               "-remove_dns          : Remove a dns configuration, previously configured via 'sctest'\n"\
+               "-ftp_proxy           : Add a proxy agent with FTP proxy. Format of the argument is 'server:port'\n"\
+               "-gopher_proxy        : Add a proxy agent with Gopher proxy. Format of the argument is 'server:port'\n"\
+               "-http_proxy          : Add a proxy agent with HTTP proxy. Format of the argument is 'server:port'\n"\
+               "-https_proxy         : Add a proxy agent with HTTPS proxy. Format of the argument is 'server:port'\n"\
+               "-proxy_match_domain  : Add a proxy agent for a match domain. If this option is not specified, 'apple.com' will be used\n"\
+               "-remove_proxy        : Remove a proxy configuration, previously configured via 'sctest'\n"\
+               "-socks_proxy         : Add a proxy agent with SOCKS proxy. Format of the argument is 'server:port'\n"\
+               "\n============== reachability options =============\n"\
+               "-address             : Determine reachability to this address\n"\
+               "-host                : Determine reachability to this host\n"\
+               "-interface           : Determine reachability when scoped to this interface\n"\
+               "-watch               : Watch for reachability changes\n"\
+               "\n============== unit_test options =============\n"\
+               "-command             : Run a unit test for a specific command. If this option is not specified, unit-tests for all commands will be run\n"\
+               "-list_tests          : List the test commands in a JSON format. This is for NPT compliance\n"\
+               "-test_method         : Runs a specific unit test. List can be obtained by using the 'test_method_list' option\n"\
+               "-test_method_list    : Lists all the unit tests. A specific one can be run using the 'test_method' option\n"\
diff --git a/sctest/SCTestOptions.m b/sctest/SCTestOptions.m
new file mode 100644 (file)
index 0000000..6b1d491
--- /dev/null
@@ -0,0 +1,35 @@
+//
+// This file is automatically generated. DO NOT EDIT!
+// To add options, see genSCTestOptions.c
+//
+#import "SCTestOptions.h"
+
+const NSString * const kSCTestGlobalOptionCPU                            = @"cpu_Str";
+const NSString * const kSCTestGlobalOptionHelp                           = @"help_Str";
+const NSString * const kSCTestGlobalOptionTime                           = @"time_Str";
+const NSString * const kSCTestGlobalOptionVerbose                        = @"verbose_Str";
+const NSString * const kSCTestGlobalOptionWait                           = @"wait_Str";
+const NSString * const kSCTestDynamicStoreOptionDNS                      = @"dns_Str";
+const NSString * const kSCTestDynamicStoreOptionIPv4                     = @"ipv4_Str";
+const NSString * const kSCTestDynamicStoreOptionIPv6                     = @"ipv6_Str";
+const NSString * const kSCTestDynamicStoreOptionProxies                  = @"proxies_Str";
+const NSString * const kSCTestPreferencesServiceList                     = @"service_list_Str";
+const NSString * const kSCTestPreferencesServiceOrder                    = @"service_order_Str";
+const NSString * const kSCTestConfigAgentDNSDomains                      = @"dns_domain_Str";
+const NSString * const kSCTestConfigAgentDNSServers                      = @"dns_servers_Str";
+const NSString * const kSCTestConfigAgentRemoveDNS                       = @"remove_dns_Str";
+const NSString * const kSCTestConfigAgentFTPProxy                        = @"ftp_proxy_Str";
+const NSString * const kSCTestConfigAgentGopherProxy                     = @"gopher_proxy_Str";
+const NSString * const kSCTestConfigAgentHTTPProxy                       = @"http_proxy_Str";
+const NSString * const kSCTestConfigAgentHTTPSProxy                      = @"https_proxy_Str";
+const NSString * const kSCTestConfigAgentProxyMatchDomain                = @"proxy_match_domain_Str";
+const NSString * const kSCTestConfigAgentRemoveProxy                     = @"remove_proxy_Str";
+const NSString * const kSCTestConfigAgentSOCKSProxy                      = @"socks_proxy_Str";
+const NSString * const kSCTestReachabilityAddress                        = @"address_Str";
+const NSString * const kSCTestReachabilityHost                           = @"host_Str";
+const NSString * const kSCTestReachabilityInterface                      = @"interface_Str";
+const NSString * const kSCTestReachabilityWatch                          = @"watch_Str";
+const NSString * const kSCTestUnitTestCommand                            = @"command_Str";
+const NSString * const kSCTestUnitTestListTests                          = @"list_tests_Str";
+const NSString * const kSCTestUnitTestTestMethod                         = @"test_method_Str";
+const NSString * const kSCTestUnitTestTestMethodList                     = @"test_method_list_Str";
diff --git a/sctest/SCTestPreferences.m b/sctest/SCTestPreferences.m
new file mode 100644 (file)
index 0000000..f444586
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 2016 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@
+ */
+
+#import "SCTest.h"
+#import "SCTestUtils.h"
+
+@interface SCTestPreferences : SCTest
+@property SCPreferencesRef prefs;
+@end
+
+@implementation SCTestPreferences
+
++ (NSString *)command
+{
+       return @"preferences";
+}
+
++ (NSString *)commandDescription
+{
+       return @"Tests the SCPreferences code path";
+}
+
+- (instancetype)initWithOptions:(NSDictionary *)options
+{
+       self = [super initWithOptions:options];
+       if (self) {
+               _prefs = SCPreferencesCreate(kCFAllocatorDefault, CFSTR("SCTest"), NULL);
+       }
+       return self;
+}
+
+- (void)dealloc
+{
+       if (self.prefs != NULL) {
+               CFRelease(self.prefs);
+               self.prefs = NULL;
+       }
+}
+
+- (void)start
+{
+       if (self.options[kSCTestPreferencesServiceList]) {
+               NSDictionary *services = (__bridge NSDictionary *)SCPreferencesGetValue(self.prefs, kSCPrefNetworkServices);
+               if (services != nil) {
+                       [self printNetworkServicesFromDict:services];
+               } else {
+                       SCTestLog("No services present!");
+               }
+       }
+
+       if (self.options[kSCTestPreferencesServiceOrder]) {
+               SCNetworkSetRef set = SCNetworkSetCopyCurrent(self.prefs);
+               NSArray *serviceID = (__bridge NSArray *)SCNetworkSetGetServiceOrder(set);
+               NSDictionary *services = (__bridge NSDictionary *)SCPreferencesGetValue(self.prefs, kSCPrefNetworkServices);
+               int counter = 1;
+               SCTestLog("Network service order");
+               for (NSString *key in serviceID) {
+                       NSDictionary *dict = [services objectForKey:key];
+                       SCTestLog("\n%d: %@\n\tUserDefinedName: %@", counter++, key, [dict objectForKey:(__bridge NSString *)kSCPropNetServiceUserDefinedName]);
+               }
+               CFRelease(set);
+       }
+
+       [self cleanupAndExitWithErrorCode:0];
+}
+
+- (void)printNetworkServicesFromDict:(NSDictionary *)serviceDict
+{
+       int counter = 1;
+       SCTestLog("Network Services");
+       for (NSString *key in serviceDict) {
+               NSDictionary *dict = [serviceDict objectForKey:key];
+               SCTestLog("\n%d: %@\n\tUserDefinedName: %@", counter++, key, [dict objectForKey:(__bridge NSString *)kSCPropNetServiceUserDefinedName]);
+       }
+}
+
+- (BOOL)unitTest
+{
+       BOOL allUnitTestsPassed = YES;
+       allUnitTestsPassed &= [self unitTestNetworkServicesSanity];
+       allUnitTestsPassed &= [self unitTestPreferencesAPI];
+       allUnitTestsPassed &= [self unitTestPreferencesSession];
+       return  allUnitTestsPassed;
+
+}
+
+- (BOOL)unitTestNetworkServicesSanity
+{
+       // We verify that every service has a unique name, an interface, an IPv4 config method and and IPv6 config method.
+       NSDictionary *services;
+       NSMutableArray *serviceNameArray;
+       SCTestPreferences *test;
+
+       test = [[SCTestPreferences alloc] initWithOptions:self.options];
+       services = (__bridge NSDictionary *)SCPreferencesGetValue(test.prefs, kSCPrefNetworkServices);
+       if (services == NULL) {
+               SCTestLog("No services present!");
+               return NO;
+       }
+
+       serviceNameArray = [[NSMutableArray alloc] init];
+       for (NSString *serviceID in services) {
+               NSDictionary *serviceDict;
+               NSString *serviceName;
+               NSDictionary *interfaceDict;
+               NSString *interfaceType;
+               NSDictionary *ipv4Dict;
+               NSDictionary *ipv6Dict;
+
+               serviceDict = [services objectForKey:serviceID];
+               if (![serviceDict isKindOfClass:[NSDictionary class]]) {
+                       SCTestLog("Service is not a dictionary");
+                       return NO;
+               }
+
+               serviceName = [serviceDict objectForKey:(__bridge NSString *)kSCPropNetServiceUserDefinedName];
+               if (serviceName != nil) {
+                       // Check if the name is unique
+                       BOOL namePresent = [serviceNameArray containsObject:serviceName];
+                       if (!namePresent) {
+                               [serviceNameArray addObject:serviceName];
+                       } else {
+                               SCTestLog("Duplicate services with name %@ exist", serviceName);
+                               return NO;
+                       }
+               } else {
+                       SCTestLog("Service ID %@ does not have a name", serviceID);
+                       return NO;
+               }
+
+               interfaceDict = [serviceDict objectForKey:(__bridge NSString *)kSCCompInterface];
+               if (interfaceDict == nil) {
+                       SCTestLog("Service %@ does not have an interface", serviceName);
+                       return NO;
+               }
+
+               interfaceType = [interfaceDict objectForKey:(__bridge NSString *)kSCPropNetInterfaceType];
+               if (interfaceType != nil && [interfaceType containsString:@"CommCenter"]) {
+                       // CommCenter services typically do not have an ipv4/v6 data OR config method. Skip such services.
+                       continue;
+               }
+
+               ipv4Dict = [serviceDict objectForKey:(__bridge NSString *)kSCEntNetIPv4];
+               ipv6Dict = [serviceDict objectForKey:(__bridge NSString *)kSCEntNetIPv6];
+
+               // Check that we have at least one IP config method
+               if (ipv4Dict == nil && ipv6Dict == nil) {
+                       SCTestLog("Service %@ does not have an IP dictionary", serviceName);
+                       return NO;
+               }
+
+               if ([ipv4Dict objectForKey:(__bridge NSString *)kSCPropNetIPv4ConfigMethod] == nil &&
+                       [ipv6Dict objectForKey:(__bridge NSString *)kSCPropNetIPv6ConfigMethod] == nil) {
+                       SCTestLog("Service %@ does not have an IP Config Method", serviceName);
+                       return NO;
+               }
+       }
+
+       SCTestLog("Verified that the Network Services have valid configurations");
+
+       return YES;
+}
+
+- (BOOL)unitTestPreferencesSession
+{
+       SCPreferencesRef prefs;
+       prefs = SCPreferencesCreate(kCFAllocatorDefault, CFSTR("SCTest"), NULL);
+       if (prefs == NULL) {
+               SCTestLog("Failed to create SCPreferences. Error: %s", SCErrorString(SCError()));
+               return NO;
+       } else {
+               CFRelease(prefs);
+       }
+
+       prefs = SCPreferencesCreateWithOptions(kCFAllocatorDefault, CFSTR("SCTest"), NULL, kSCPreferencesUseEntitlementAuthorization, NULL);
+       if (prefs == NULL) {
+               SCTestLog("Failed to create SCPreferences w/options. Error: %s", SCErrorString(SCError()));
+               return NO;
+       } else {
+               CFRelease(prefs);
+       }
+
+       prefs = SCPreferencesCreateWithAuthorization(kCFAllocatorDefault, CFSTR("SCTest"), NULL, kSCPreferencesUseEntitlementAuthorization);
+       if (prefs == NULL) {
+               SCTestLog("Failed to create SCPreferences w/options. Error: %s", SCErrorString(SCError()));
+               return NO;
+       } else {
+               CFRelease(prefs);
+       }
+
+       SCTestLog("Verified that the preferences session can be created");
+       return YES;
+}
+
+- (BOOL)unitTestPreferencesAPI
+{
+       BOOL ok = NO;
+       int iterations = 1000;
+       NSDictionary *prefsOptions;
+       NSMutableArray *keys;
+       NSMutableArray *values;
+       SCTestPreferences *test;
+       NSArray *keyList;
+
+       test = [[SCTestPreferences alloc] initWithOptions:self.options];
+       if (test.prefs != NULL) {
+               CFRelease(test.prefs);
+               test.prefs = NULL;
+       }
+
+       prefsOptions = @{(__bridge NSString *)kSCPreferencesOptionRemoveWhenEmpty:(__bridge NSNumber *)kCFBooleanTrue};
+       test.prefs = SCPreferencesCreateWithOptions(kCFAllocatorDefault,
+                                                       CFSTR("SCTest"),
+                                                       CFSTR("SCTestPreferences.plist"),
+                                                       kSCPreferencesUseEntitlementAuthorization,
+                                                       (__bridge CFDictionaryRef)prefsOptions);
+       if (test.prefs == NULL) {
+               SCTestLog("Failed to create a preferences session. Error: %s", SCErrorString(SCError()));
+               return NO;
+       }
+
+       keys = [[NSMutableArray alloc] init];
+       values = [[NSMutableArray alloc] init];
+       for (int i = 0; i < iterations; i++) {
+               NSUUID *uuidKey = [NSUUID UUID];
+               NSUUID *uuidValue = [NSUUID UUID];
+
+               ok = SCPreferencesLock(test.prefs, true);
+               if (!ok) {
+                       SCTestLog("Failed to get preferences lock. Error: %s", SCErrorString(SCError()));
+                       return NO;
+               }
+
+               ok = SCPreferencesSetValue(test.prefs, (__bridge CFStringRef)uuidKey.UUIDString, (__bridge CFStringRef)uuidValue.UUIDString);
+               if (!ok) {
+                       SCTestLog("Failed to set preferences value. Error: %s", SCErrorString(SCError()));
+                       return NO;
+               }
+
+               ok = SCPreferencesUnlock(test.prefs);
+               if (!ok) {
+                       SCTestLog("Failed to release preferences lock. Error: %s", SCErrorString(SCError()));
+                       return NO;
+               }
+
+               [keys addObject:uuidKey.UUIDString];
+               [values addObject:uuidValue.UUIDString];
+       }
+
+       ok = SCPreferencesCommitChanges(test.prefs);
+       if (!ok) {
+               SCTestLog("Failed to commit preferences. Error: %s", SCErrorString(SCError()));
+               return NO;
+       }
+
+       CFRelease(test.prefs);
+       test.prefs = SCPreferencesCreateWithOptions(kCFAllocatorDefault,
+                                                       CFSTR("SCTest"),
+                                                       CFSTR("SCTestPreferences.plist"),
+                                                       kSCPreferencesUseEntitlementAuthorization,
+                                                       (__bridge CFDictionaryRef)prefsOptions);
+       if (test.prefs == NULL) {
+               SCTestLog("Failed to create a preferences session. Error: %s", SCErrorString(SCError()));
+               return NO;
+       }
+
+       keyList = (__bridge_transfer NSArray *)SCPreferencesCopyKeyList(test.prefs);
+       if ([keyList count] < [keys count]) {
+               SCTestLog("Failed to copy all keys from preferences. Error: %s", SCErrorString(SCError()));
+               return NO;
+       }
+
+       for (NSString *key in keys) {
+               NSString *valueString = (__bridge NSString *)SCPreferencesGetValue(test.prefs, (__bridge CFStringRef)key);
+               if (!valueString) {
+                       SCTestLog("Failed to get value from preferences. Error: %s", SCErrorString(SCError()));
+                       return NO;
+               }
+
+               BOOL ok = [values containsObject:valueString];
+               if (!ok) {
+                       SCTestLog("Incorrect value fetched from preferences");
+                       return NO;
+               }
+       }
+
+       ok = SCPreferencesRemoveAllValues(test.prefs);
+       if (!ok) {
+               SCTestLog("Failed to remove values  preferences. Error: %s", SCErrorString(SCError()));
+               return NO;
+       }
+
+       ok = SCPreferencesCommitChanges(test.prefs);
+       if (!ok) {
+               SCTestLog("Failed to commit preferences. Error: %s", SCErrorString(SCError()));
+               return NO;
+       }
+
+       CFRelease(test.prefs);
+       test.prefs = SCPreferencesCreateWithOptions(kCFAllocatorDefault,
+                                                       CFSTR("SCTest"),
+                                                       CFSTR("SCTestPreferences.plist"),
+                                                       kSCPreferencesUseEntitlementAuthorization,
+                                                       (__bridge CFDictionaryRef)prefsOptions);
+       if (test.prefs == NULL) {
+               SCTestLog("Failed to create a preferences session. Error: %s", SCErrorString(SCError()));
+               return NO;
+       }
+
+       keyList = (__bridge_transfer NSArray *)SCPreferencesCopyKeyList(test.prefs);
+       if ([keyList count] > 0) {
+               SCTestLog("Failed to remove all keys from preferences. Error: %s", SCErrorString(SCError()));
+               return NO;
+       }
+
+       SCTestLog("Verified that SCPreferences APIs behave as expected");
+       return ok;
+}
+
+- (void)cleanupAndExitWithErrorCode:(int)error
+{
+       [super cleanupAndExitWithErrorCode:error];
+}
+
+@end
diff --git a/sctest/SCTestReachability.m b/sctest/SCTestReachability.m
new file mode 100644 (file)
index 0000000..c638a66
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * Copyright (c) 2016 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@
+ */
+
+#import "SCTest.h"
+#import "SCTestUtils.h"
+#import <arpa/inet.h>
+#import <NetworkExtension/NEPolicySession.h>
+#import <network_information.h>
+
+#define REACHABILITY_TEST_HOSTNAME "apple.com"
+
+@interface SCTestReachability : SCTest
+@property SCNetworkReachabilityRef target;
+@property dispatch_queue_t callbackQ;
+@end
+
+@implementation SCTestReachability
+
++ (NSString *)command
+{
+       return @"reachability";
+}
+
++ (NSString *)commandDescription
+{
+       return @"Tests the SCNetworkReachability code path";
+}
+
+- (instancetype)initWithOptions:(NSDictionary *)options
+{
+       self = [super initWithOptions:options];
+       if (self) {
+               NSMutableDictionary *reachOptions;
+               if (self.options[kSCTestReachabilityInterface] != nil) {
+                       reachOptions = [[NSMutableDictionary alloc] init];
+                       [reachOptions setObject:self.options[kSCTestReachabilityInterface] forKey:(__bridge NSString *)kSCNetworkReachabilityOptionInterface];
+                       if (self.options[kSCTestReachabilityHost] != nil) {
+                               [reachOptions setObject:self.options[kSCTestReachabilityHost] forKey:(__bridge NSString *)kSCNetworkReachabilityOptionNodeName];
+                       } else if (self.options[kSCTestReachabilityAddress] != nil) {
+                               NSData *data;
+                               NSString *addressString = self.options[kSCTestReachabilityAddress];
+                               struct sockaddr *sa = _SC_string_to_sockaddr(addressString.UTF8String, AF_INET, NULL, 0);
+                               if (sa == NULL) {
+                                       sa = _SC_string_to_sockaddr(addressString.UTF8String, AF_INET6, NULL, 0);
+                                       if (sa == NULL) {
+                                               SCTestLog("Invalid address");
+                                               ERR_EXIT;
+                                       }
+                               }
+                               data = [NSData dataWithBytes:sa length:(sa->sa_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)];
+                               [reachOptions setObject:data forKey:(__bridge NSString *)kSCNetworkReachabilityOptionRemoteAddress];
+                       }
+
+
+                       _target = SCNetworkReachabilityCreateWithOptions(kCFAllocatorDefault, (__bridge CFDictionaryRef)reachOptions);
+               } else if (self.options[kSCTestReachabilityHost]) {
+                       NSString *host = self.options[kSCTestReachabilityHost];
+                       _target = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, host.UTF8String);
+               } else if (self.options[kSCTestReachabilityAddress]) {
+                       NSString *addressString = self.options[kSCTestReachabilityAddress];
+                       struct sockaddr *sa = _SC_string_to_sockaddr(addressString.UTF8String, AF_INET, NULL, 0);
+                       if (sa == NULL) {
+                               sa = _SC_string_to_sockaddr(addressString.UTF8String, AF_INET6, NULL, 0);
+                               if (sa == NULL) {
+                                       SCTestLog("Invalid address");
+                                       ERR_EXIT;
+                               }
+                       }
+                       _target = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, sa);
+               }
+       }
+       return self;
+}
+
+- (void)dealloc
+{
+       if (self.target != NULL) {
+               CFRelease(self.target);
+               self.target = NULL;
+       }
+}
+
+void
+myReachabilityCallback(SCNetworkReachabilityRef        target, SCNetworkReachabilityFlags flags, void *info)
+{
+       struct tm       tm_now;
+       struct timeval  tv_now;
+       (void)gettimeofday(&tv_now, NULL);
+       (void)localtime_r(&tv_now.tv_sec, &tm_now);
+       SCTestLog("%2d:%02d:%02d.%03d Reachability changed: %#x", tm_now.tm_hour, tm_now.tm_min, tm_now.tm_sec, tv_now.tv_usec / 1000, flags);
+}
+
+- (void)start
+{
+       if (self.options[kSCTestReachabilityHost] != nil && self.options[kSCTestReachabilityAddress] != nil) {
+               SCTestLog("Please specify either a host or address");
+               ERR_EXIT;
+       }
+
+       if (self.options[kSCTestReachabilityWatch]) {
+               self.callbackQ = dispatch_queue_create("SCTestReachability callback queue", NULL);
+               SCNetworkReachabilitySetCallback(self.target, myReachabilityCallback, NULL);
+               SCNetworkReachabilitySetDispatchQueue(self.target, self.callbackQ);
+       } else {
+               SCNetworkReachabilityFlags flags;
+               SCNetworkReachabilityGetFlags(self.target, &flags);
+               SCTestLog("Reachability: %#x", flags);
+               [self cleanupAndExitWithErrorCode:0];
+       }
+}
+
+- (void)cleanupAndExitWithErrorCode:(int)error
+{
+       [super cleanupAndExitWithErrorCode:error];
+}
+
+- (NSString *)primaryInterfaceName
+{
+       const char *name;
+       NSString *nsName;
+       nwi_ifstate_t ifstate;
+       nwi_state_t nwi;
+
+       nwi = nwi_state_copy();
+       ifstate = nwi_state_get_first_ifstate(nwi, AF_INET);
+       if (ifstate == NULL) {
+               ifstate = nwi_state_get_first_ifstate(nwi, AF_INET6);
+               if (ifstate == NULL) {
+                       // May be we are in airplane mode
+                       SCTestLog("No primary interface found");
+                       return nil;
+               }
+       }
+
+       name = nwi_ifstate_get_ifname(ifstate);
+       nsName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
+       nwi_state_release(nwi);
+
+       return nsName;
+}
+
+- (BOOL)setup
+{
+       return YES;
+}
+
+- (BOOL)unitTest
+{
+       if(![self setup]) {
+               return NO;
+       }
+
+       BOOL allUnitTestsPassed = YES;
+
+       allUnitTestsPassed &= [self unitTestBasicReachabilityCheck];
+       allUnitTestsPassed &= [self unitTestReachabilityWithPolicy];
+       allUnitTestsPassed &= [self unitTestScopedReachabilityWithPolicy];
+
+       if(![self tearDown]) {
+               return NO;
+       }
+
+       return allUnitTestsPassed;
+}
+
+- (BOOL)unitTestBasicReachabilityCheck
+{
+       SCNetworkReachabilityFlags flags;
+       SCTestReachability *test;
+
+       test = [[SCTestReachability alloc] initWithOptions:self.options];
+       if ([test primaryInterfaceName] == nil) {
+               return YES;
+       }
+
+       if (test.target == NULL) {
+               NSDictionary *options = @{kSCTestReachabilityHost:@REACHABILITY_TEST_HOSTNAME};
+               test = [[SCTestReachability alloc] initWithOptions:options];
+       }
+
+
+       SCNetworkReachabilityGetFlags(test.target, &flags);
+       if (flags == 0) {
+               SCTestLog("Reachability reported not reachable");
+               return NO;
+       }
+
+       SCTestLog("Verified that basic reachability check succeeds");
+       return YES;
+}
+
+- (BOOL)unitTestReachabilityWithPolicy
+{
+       NEPolicyCondition *condition1;
+       NEPolicyCondition *condition2;
+       SCNetworkReachabilityFlags flags;
+       BOOL ok;
+       NEPolicy *policy;
+       NSUInteger policyID;
+       NEPolicyResult *result;
+       NEPolicySession *session;
+       SCTestReachability *test;
+
+       test = [[SCTestReachability alloc] initWithOptions:self.options];
+       if ([test primaryInterfaceName] == nil) {
+               return YES;
+       }
+
+       if (test.target == NULL) {
+               NSDictionary *options = @{kSCTestReachabilityHost:@REACHABILITY_TEST_HOSTNAME};
+               test = [[SCTestReachability alloc] initWithOptions:options];
+       }
+
+       SCNetworkReachabilityGetFlags(test.target, &flags);
+       if (flags == 0) {
+               SCTestLog("Reachability reported not reachable");
+               return NO;
+       }
+
+       session = [[NEPolicySession alloc] init];
+       if (session == nil) {
+               SCTestLog("Failed to create NEPolicySession");
+               return NO;
+       }
+
+       result = [NEPolicyResult drop];
+       condition1 = [NEPolicyCondition allInterfaces];
+       condition2 = [NEPolicyCondition effectivePID:getpid()];
+       policy = [[NEPolicy alloc ] initWithOrder:10 result:result conditions:@[condition1, condition2]];
+       policyID = [session addPolicy:policy];
+       if (policyID == 0) {
+               SCTestLog("Failed to add policy");
+               return NO;
+       }
+
+       ok = [session apply];
+       if (!ok) {
+               SCTestLog("Failed to apply policy");
+               return NO;
+       }
+
+       SCNetworkReachabilityGetFlags(test.target, &flags);
+       if (flags != 0) {
+               SCTestLog("Reachability reported as reachable, in presence of a drop policy");
+               return NO;
+       }
+
+       ok = [session removeAllPolicies];
+       if (!ok) {
+               SCTestLog("Failed to remove policies from session");
+               return NO;
+       }
+
+       ok = [session apply];
+       if (!ok) {
+               SCTestLog("Failed to apply policy");
+               return NO;
+       }
+
+       SCNetworkReachabilityGetFlags(test.target, &flags);
+       if (flags == 0) {
+               SCTestLog("Reachability reported as not reachable, in absence of a drop policy");
+               return NO;
+       }
+
+       SCTestLog("Verified that SCNetworkReachability reports reachability corresponding to the NECP Policies");
+       return YES;
+}
+
+- (BOOL)unitTestScopedReachabilityWithPolicy
+{
+       NEPolicyCondition *condition1;
+       NEPolicyCondition *condition2;
+       SCNetworkReachabilityFlags flags;
+       BOOL ok;
+       NEPolicy *policy;
+       NSUInteger policyID;
+       NSString *primaryInterface;
+       NEPolicyResult *result;
+       NEPolicySession *session;
+       SCTestReachability *test;
+
+       test = [[SCTestReachability alloc] initWithOptions:self.options];
+       primaryInterface = [test primaryInterfaceName];
+       if (primaryInterface == nil) {
+               return YES;
+       }
+
+       if (test.target == NULL) {
+               NSDictionary *options = @{kSCTestReachabilityHost:@REACHABILITY_TEST_HOSTNAME,
+                                         kSCTestReachabilityInterface:primaryInterface};
+               test = [[SCTestReachability alloc] initWithOptions:options];
+       }
+
+       SCNetworkReachabilityGetFlags(test.target, &flags);
+       if (flags == 0) {
+               SCTestLog("Reachability reported not reachable");
+               return NO;
+       }
+
+       session = [[NEPolicySession alloc] init];
+       if (session == nil) {
+               SCTestLog("Failed to create NEPolicySession");
+               return NO;
+       }
+
+       result = [NEPolicyResult drop];
+       condition1 = [NEPolicyCondition scopedInterface:primaryInterface];
+       condition2 = [NEPolicyCondition effectivePID:getpid()];
+       policy = [[NEPolicy alloc ] initWithOrder:10 result:result conditions:@[condition1, condition2]];
+       policyID = [session addPolicy:policy];
+       if (policyID == 0) {
+               SCTestLog("Failed to add policy");
+               return NO;
+       }
+
+       ok = [session apply];
+       if (!ok) {
+               SCTestLog("Failed to apply policy");
+               return NO;
+       }
+
+       SCNetworkReachabilityGetFlags(test.target, &flags);
+       if (flags != 0) {
+               SCTestLog("Reachability reported as reachable, in presence of a drop policy");
+               return NO;
+       }
+
+       ok = [session removeAllPolicies];
+       if (!ok) {
+               SCTestLog("Failed to remove policies from session");
+               return NO;
+       }
+
+       ok = [session apply];
+       if (!ok) {
+               SCTestLog("Failed to apply policy");
+               return NO;
+       }
+
+       SCNetworkReachabilityGetFlags(test.target, &flags);
+       if (flags == 0) {
+               SCTestLog("Reachability reported as not reachable, in absence of a drop policy");
+               return NO;
+       }
+
+       SCTestLog("Verified that SCNetworkReachability for scoped queries report reachability corresponding to the NECP Policies");
+       return YES;
+}
+
+- (BOOL)tearDown
+{
+       return YES;
+}
+
+@end
diff --git a/sctest/SCTestUnitTest.m b/sctest/SCTestUnitTest.m
new file mode 100644 (file)
index 0000000..3f771c2
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2016 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@
+ */
+
+#import "SCTest.h"
+#import "SCTestUtils.h"
+
+@interface SCTestUnitTest : SCTest
+@end
+
+@implementation SCTestUnitTest
+
++ (NSString *)command
+{
+       return @"unit_test";
+}
+
++ (NSString *)commandDescription
+{
+       return @"Runs the unit test for all commands";
+}
+
+- (void)listTests
+{
+       NSMutableDictionary *testDictionary;
+       NSMutableArray *testsArray;
+       NSString *thisClass;
+       NSArray<NSString *> *testClasses;
+       NSData *data;
+       NSString *jsonString;
+
+       testDictionary = [[NSMutableDictionary alloc] init];
+       [testDictionary setObject:@"SystemConfiguration Unit Tests" forKey:@"Name"];
+       [testDictionary setObject:@"These tests exercise 'configd' and the 'SystemConfiguration' framework" forKey:@"Description"];
+
+       testsArray = [[NSMutableArray alloc] init];
+       thisClass = NSStringFromClass([self class]);
+       testClasses = getTestClasses();
+       for (NSString *className in testClasses) {
+               Class testClass;
+               NSMutableDictionary *subTest;
+               NSArray *list;
+               NSMutableArray *subTestArray;
+
+               if ([className isEqualToString:thisClass] ) {
+                       continue;
+               }
+
+               testClass = NSClassFromString(className);
+               list = getUnitTestListForClass(testClass);
+
+               subTest = [[NSMutableDictionary alloc] init];
+               [subTest setObject:@NO forKey:@"RequiresTCPDUMP"];
+               [subTest setObject:@YES forKey:@"RequiresNetwork"];
+               [subTest setObject:@NO forKey:@"RequiresRoot"];
+               [subTest setObject:@NO forKey:@"RequiresPowermetrics"];
+               [subTest setObject:[testClass command] forKey:@"Name"];
+               [subTest setObject:[testClass commandDescription] forKey:@"Description"];
+               subTestArray = [[NSMutableArray alloc] init];
+               for (NSString *unitTest in list) {
+                       NSDictionary *testDict = @{@"Command":@[@"/usr/local/bin/sctest",
+                                                               @"unit_test",
+                                                               @"-test_method",
+                                                               unitTest],
+                                                       @"Name":[unitTest stringByReplacingOccurrencesOfString:@"unitTest" withString:@""],
+                                                       @"Description":@"Unit test"
+                                                       };
+                       [subTestArray addObject:testDict];
+               }
+               [subTest setObject:subTestArray forKey:@"SubTests"];
+               [testsArray addObject:subTest];
+       }
+       [testDictionary setObject:testsArray forKey:@"Tests"];
+       data = [NSJSONSerialization dataWithJSONObject:testDictionary
+                                               options:NSJSONWritingPrettyPrinted
+                                               error:nil];
+       jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+       //SCTestLog("%@", jsonString);
+       SCPrint(TRUE, stderr, CFSTR("%@"), jsonString);
+}
+
+- (void)start
+{
+       NSArray<NSString *> *testClasses;
+       NSString *thisClass = NSStringFromClass([self class]);;
+       testClasses = getTestClasses();
+       BOOL errorOccured = NO;
+
+       if (self.options[kSCTestUnitTestListTests]) {
+               [self listTests];
+       } else if (self.options[kSCTestUnitTestTestMethodList]) {
+               SCTestLog("List of unit tests:");
+               for (NSString *className in testClasses) {
+                       Class testClass;
+                       NSArray *list;
+
+                       if ([className isEqualToString:thisClass] ) {
+                               continue;
+                       }
+
+                       testClass = NSClassFromString(className);
+                       if (self.options[kSCTestUnitTestCommand] != nil) {
+                               if (![self.options[kSCTestUnitTestCommand] isEqualToString:[testClass command]]) {
+                                       // Run unit test only for a specific command
+                                       continue;
+                               }
+                       }
+
+                       SCTestLog("\n======= '%@' unit tests =======", [testClass command]);
+                       list = getUnitTestListForClass(testClass);
+                       for (NSString *unitTest in list) {
+                               SCTestLog("%@", unitTest);
+                       }
+               }
+
+               SCTestLog("\nEach of the unit tests can be run with the 'test_method' option\n");
+       } else if (self.options[kSCTestUnitTestTestMethod]) {
+               for (NSString *className in testClasses) {
+                       Class testClass;
+                       NSArray *list;
+
+                       if ([className isEqualToString:thisClass] ) {
+                               continue;
+                       }
+
+                       testClass = NSClassFromString(className);
+                       if (self.options[kSCTestUnitTestCommand] != nil) {
+                               if (![self.options[kSCTestUnitTestCommand] isEqualToString:[testClass command]]) {
+                                       // Run unit test only for a specific command
+                                       continue;
+                               }
+                       }
+
+                       list = getUnitTestListForClass(testClass);
+                       for (NSString *unitTest in list) {
+                               if ([unitTest isEqualToString:self.options[kSCTestUnitTestTestMethod]]) {
+                                       id obj = [(SCTest *)[testClass alloc] initWithOptions:self.options];
+
+                                       SCTestLog("Running unit test %@ ...", unitTest);
+
+                                       SEL methodSelector = NSSelectorFromString(unitTest);
+                                       Boolean retVal = false;
+                                       if ([obj respondsToSelector:methodSelector]) {
+                                               NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[obj methodSignatureForSelector:methodSelector]];
+                                               invocation.target = obj;
+                                               invocation.selector = methodSelector;
+                                               [invocation invoke];
+                                               [invocation getReturnValue:&retVal];
+                                       }
+
+                                       if (!retVal) {
+                                               SCTestLog("FAILED");
+                                               errorOccured = YES;
+                                       } else {
+                                               SCTestLog("PASSED");
+                                       }
+                                       break;
+                               }
+                       }
+               }
+       } else {
+               // This command runs unit tests for all commands.
+               for (NSString *className in testClasses) {
+                       Class testClass;
+                       id obj;
+
+                       if ([className isEqualToString:thisClass] ) {
+                               continue;
+                       }
+
+                       testClass = NSClassFromString(className);
+                       if (self.options[kSCTestUnitTestCommand] != nil) {
+                               if (![self.options[kSCTestUnitTestCommand] isEqualToString:[testClass command]]) {
+                                       // Run unit test only for a specific command
+                                       continue;
+                               }
+                       }
+
+                       obj = [(SCTest *)[testClass alloc] initWithOptions:self.options];
+                       if ([obj respondsToSelector:@selector(unitTest)]) {
+                               SCTestLog("\n*** Running unit test for \"%@\" command ***\n", [testClass command]);
+                               BOOL passed = [obj unitTest];
+                               if (!passed) {
+                                       SCTestLog("FAILED");
+                                       errorOccured = YES;
+                               }
+                       }
+               }
+       }
+
+       [self cleanupAndExitWithErrorCode:errorOccured];
+}
+
+- (void)cleanupAndExitWithErrorCode:(int)error
+{
+       [super cleanupAndExitWithErrorCode:error];
+}
+
+@end
diff --git a/sctest/SCTestUtils.h b/sctest/SCTestUtils.h
new file mode 100644 (file)
index 0000000..2b02800
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2016 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@
+ */
+
+#ifndef SCTestUtils_h
+#define SCTestUtils_h
+
+#import <Foundation/Foundation.h>
+#import <SystemConfiguration/SCPrivate.h>
+#import <objc/objc-runtime.h>
+
+#define SCTestLog(fmt, ...)    SCPrint(TRUE, stdout, CFSTR(fmt "\n"), ##__VA_ARGS__)
+#define ERR_EXIT               exit(1)
+
+typedef struct {
+       uint64_t user;
+       uint64_t sys;
+       uint64_t idle;
+} CPUUsageInfoInner;
+
+typedef struct {
+       CPUUsageInfoInner startCPU;
+       CPUUsageInfoInner endCPU;
+} CPUUsageInfo;
+
+typedef struct {
+       struct timespec startTime;
+       struct timespec endTime;
+} timerInfo;
+
+void timerStart(timerInfo *);
+void timerEnd(timerInfo *);
+NSString * createUsageStringForTimer(timerInfo *);
+
+void cpuStart(CPUUsageInfo *);
+void cpuEnd(CPUUsageInfo *);
+NSString * createUsageStringForCPU(CPUUsageInfo *cpu);
+
+NSArray<NSString *> *getTestClasses();
+NSArray<NSString *> *getUnitTestListForClass(Class base);
+NSDictionary *getOptionsDictionary(int argc, const char **argv);
+
+#endif /* SCTestUtils_h */
diff --git a/sctest/SCTestUtils.m b/sctest/SCTestUtils.m
new file mode 100644 (file)
index 0000000..d244eb2
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2016 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@
+ */
+
+#import <Foundation/Foundation.h>
+#import "SCTest.h"
+#import "SCTestUtils.h"
+#import <mach/mach_time.h>
+
+NSArray<NSString *> *
+getTestClasses()
+{
+       static NSMutableArray *subclassNames = nil;
+       Class base;
+       unsigned int classListCount;
+       Class *classList;
+
+       if (subclassNames != nil) {
+               return subclassNames;
+       }
+
+       base = [SCTest class];
+       classListCount = 0;
+       classList = objc_copyClassList(&classListCount);
+       subclassNames = [[NSMutableArray alloc] init];
+
+       if (classList) {
+               for (unsigned int i = 0; i < classListCount; i++) {
+                       Class superClass = class_getSuperclass(classList[i]);
+                       while (superClass && superClass != base) {
+                               superClass = class_getSuperclass(superClass);
+                       }
+
+                       if (superClass == base) {
+                               [subclassNames addObject:@(class_getName(classList[i]))];
+                       }
+               }
+               free(classList);
+       }
+
+       [subclassNames sortUsingComparator: ^(id obj1, id obj2) {
+               NSString *className1 = obj1;
+               NSString *className2 = obj2;
+               return [className1 compare:className2 options:NSCaseInsensitiveSearch];
+       }];
+
+       return subclassNames;
+}
+
+NSArray<NSString *> *
+getUnitTestListForClass(Class base)
+{
+       NSMutableArray<NSString *> *unitTestNames = nil;
+       unsigned int methodListCount = 0;
+       Method *methodList = NULL;
+
+       methodList = class_copyMethodList(base, &methodListCount);
+       if (methodList) {
+               unitTestNames = [[NSMutableArray alloc] initWithCapacity:methodListCount];
+               for (unsigned int i = 0; i < methodListCount; i++) {
+                       NSString *name = @(sel_getName(method_getName(methodList[i])));
+                       if ([name isEqualToString:@"unitTest"]) {
+                               continue;
+                       } else if (![name hasPrefix:@"unitTest"]) {
+                               continue;
+                       }
+
+                       [unitTestNames addObject:name];
+               }
+               free(methodList);
+       }
+
+       return unitTestNames;
+}
+
+NSDictionary *
+getOptionsDictionary(int argc, const char **argv)
+{
+       NSMutableDictionary *options;
+       NSNumberFormatter *numberFormatter;
+       int ch;
+       int i;
+       struct option entries[] = {
+               kSCTestOptionEntries
+       };
+
+       options = [NSMutableDictionary dictionary];
+       optind = 0;
+       optreset = 1;
+       numberFormatter = [[NSNumberFormatter alloc] init];
+
+       while ((ch = getopt_long_only(argc, (char * const *)argv, "", entries, &i)) == 0) {
+               struct option opt = entries[i];
+               NSString *optKey = [NSString stringWithFormat:@"%s_Str", opt.name]; // ... "_Str" suffix is standardized across all keys.
+               id optVal = nil;
+
+               if (opt.has_arg) {
+                       // Parse the optarg
+
+                       // Attempt string
+                       optVal = @(optarg);
+
+                       if (optVal == nil) {
+                               // Fall back to NSData
+                               // WARNING: Doesn't work if it contains '\0'
+                               optVal = [NSData dataWithBytes:optarg length:strlen(optarg)];
+                       } else {
+                               // Use NSNumber if the argument is a number
+                               NSNumber *number = [numberFormatter numberFromString:optVal];
+                               if (number) {
+                                       optVal = number;
+                               }
+                       }
+               } else {
+                       optVal = @YES;
+               }
+
+               // Handle multiple option instances
+               id existingValue = options[optKey];
+               if (existingValue) {
+                       if ([existingValue isKindOfClass:[NSMutableArray class]]) {
+                               [(NSMutableArray *)existingValue addObject:optVal];
+                               optVal = existingValue;
+                       } else if ([existingValue isKindOfClass:[NSArray class]]) {
+                               NSMutableArray *tempArray = [NSMutableArray arrayWithArray:existingValue];
+                               [tempArray addObject:optVal];
+                               optVal = tempArray;
+                       } else {
+                               optVal = @[existingValue, optVal];
+                       }
+               }
+
+               options[optKey] = optVal;
+       }
+
+       if (ch > 0) {
+               return nil;
+       }
+
+       return options;
+}
+
+static void
+cpu_routine(CPUUsageInfoInner *usage)
+{
+       host_name_port_t host;
+       host_cpu_load_info_data_t host_load;
+       mach_msg_type_number_t count;
+       kern_return_t kret;
+
+       if (usage == NULL) {
+               return;
+       }
+
+       host = mach_host_self();
+       count = HOST_CPU_LOAD_INFO_COUNT;
+       kret = host_statistics(host, HOST_CPU_LOAD_INFO, (host_info_t)&host_load, &count);
+       if (kret) {
+               return;
+       }
+
+       // ms per tick. 1 tick is 10 ms.
+       usage->user = ((uint64_t)host_load.cpu_ticks[CPU_STATE_USER]) * 10;
+       usage->sys = ((uint64_t)host_load.cpu_ticks[CPU_STATE_SYSTEM]) * 10;
+       usage->idle = ((uint64_t)host_load.cpu_ticks[CPU_STATE_IDLE]) * 10;
+}
+
+void
+cpuStart(CPUUsageInfo *cpu)
+{
+       cpu_routine(&cpu->startCPU);
+}
+
+void
+cpuEnd(CPUUsageInfo *cpu)
+{
+       cpu_routine(&cpu->endCPU);
+}
+
+NSString *
+createUsageStringForCPU(CPUUsageInfo *cpu)
+{
+       uint64_t userelapsed = cpu->endCPU.user - cpu->startCPU.user;
+       uint64_t systemelapsed = cpu->endCPU.sys - cpu->startCPU.sys;
+       uint64_t idleelapsed = cpu->endCPU.idle - cpu->startCPU.idle;
+       uint64_t totalelapsed = userelapsed + systemelapsed + idleelapsed;
+       double u = ((double)userelapsed * 100)/totalelapsed;
+       double s = ((double)systemelapsed * 100)/totalelapsed;
+       double i = ((double)idleelapsed * 100)/totalelapsed;
+
+       return [NSString stringWithFormat:@"%1.02f%% user %1.02f%% sys %1.02f%% idle", u, s, i];
+}
+
+static void
+timer_routine(struct timespec *ts)
+{
+       uint64_t diff;
+       static uint32_t orwl_timebase_numer = 0;
+       static uint32_t orwl_timebase_denom = 0;
+       static uint64_t orwl_timestart = 0;
+       if (orwl_timestart == 0) {
+               mach_timebase_info_data_t tb = { 0, 0 };
+               mach_timebase_info(&tb);
+               orwl_timebase_numer = tb.numer;
+               orwl_timebase_denom = tb.denom;
+               orwl_timestart = mach_absolute_time();
+       }
+       if (0 == orwl_timebase_denom) {
+               orwl_timebase_denom = 1;
+       }
+       diff = ((mach_absolute_time() - orwl_timestart) * orwl_timebase_numer) / orwl_timebase_denom;
+       ts->tv_sec = (size_t)(diff / NSEC_PER_SEC);
+       ts->tv_nsec = (size_t)(diff - (ts->tv_sec * NSEC_PER_SEC));
+}
+
+void
+timerStart(timerInfo *timer)
+{
+       timer_routine(&timer->startTime);
+}
+
+void
+timerEnd(timerInfo *timer)
+{
+       timer_routine(&timer->endTime);
+}
+
+NSString *
+createUsageStringForTimer(timerInfo *timer)
+{
+       double elapsed;
+       int64_t nsecs =  timer->endTime.tv_nsec - timer->startTime.tv_nsec;
+       uint64_t secs =  timer->endTime.tv_sec - timer->startTime.tv_sec;
+       if (nsecs < 0) {
+               nsecs += NSEC_PER_SEC;
+               secs -= 1;
+       }
+
+       elapsed = (double)secs + (double)nsecs / NSEC_PER_SEC;
+       return [NSString stringWithFormat:@"%f", elapsed];
+}
diff --git a/sctest/genSCTestOptions.c b/sctest/genSCTestOptions.c
new file mode 100644 (file)
index 0000000..dabf9f3
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2016 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <mach/boolean.h>
+#include <getopt.h>
+
+typedef struct {
+       const char *scope; // Either "global" or for a particular command
+       const char *optionString; // The option
+       const char *optionKey; // String representation of the key to be used to access this option's value from the "options" dictionary.
+       int hasArg; // no_argument, required_argument, optional_argument
+       const char *usageString; // help string
+} SCTestOption;
+
+//     Add the options here and then once finished, go to ${SRCROOT}/sctest/ in Terminal and type "make".
+//     The options should then be ready to use, through the "options" dictionary.
+SCTestOption testOptions[] = {
+       {"global", "cpu", "kSCTestGlobalOptionCPU", no_argument, "Prints the CPU usage after the test completes"},
+       {"global", "help", "kSCTestGlobalOptionHelp", no_argument, "Prints this very useful help!"},
+       {"global", "time", "kSCTestGlobalOptionTime", no_argument, "Prints the time elapsed since the test was launched"},
+       {"global", "verbose", "kSCTestGlobalOptionVerbose", no_argument, "Enables verbose mode"},
+       {"global", "wait", "kSCTestGlobalOptionWait", no_argument, "Results in a wait for 'sctest'"},
+
+       {"dynamic_store", "dns", "kSCTestDynamicStoreOptionDNS", no_argument, "Prints the global DNS information from the SCDynamicStore"},
+       {"dynamic_store", "ipv4", "kSCTestDynamicStoreOptionIPv4", no_argument, "Prints the global IPv4 information from the SCDynamicStore"},
+       {"dynamic_store", "ipv6", "kSCTestDynamicStoreOptionIPv6", no_argument, "Prints the global IPv6 information from the SCDynamicStore"},
+       {"dynamic_store", "proxies", "kSCTestDynamicStoreOptionProxies", no_argument, "Prints the global Proxy information from the SCDynamicStore"},
+
+       {"preferences", "service_list", "kSCTestPreferencesServiceList", no_argument, "Prints the Network Services list from the preferences"},
+       {"preferences", "service_order", "kSCTestPreferencesServiceOrder", no_argument, "Prints the Network Service order from the preferences"},
+
+       {"config_agent", "dns_domain", "kSCTestConfigAgentDNSDomains", required_argument, "Configures the DNS Servers for certain domains. A comma-separated list of domains can be specified. Default is 'apple.com'"},
+       {"config_agent", "dns_servers", "kSCTestConfigAgentDNSServers", required_argument, "Configures the specified DNS Servers. A comma-separated list of IP Addresses can be specified"},
+       {"config_agent", "remove_dns", "kSCTestConfigAgentRemoveDNS", no_argument, "Remove a dns configuration, previously configured via 'sctest'"},
+
+       {"config_agent", "ftp_proxy", "kSCTestConfigAgentFTPProxy", required_argument, "Add a proxy agent with FTP proxy. Format of the argument is 'server:port'"},
+       {"config_agent", "gopher_proxy", "kSCTestConfigAgentGopherProxy", required_argument, "Add a proxy agent with Gopher proxy. Format of the argument is 'server:port'"},
+       {"config_agent", "http_proxy", "kSCTestConfigAgentHTTPProxy", required_argument, "Add a proxy agent with HTTP proxy. Format of the argument is 'server:port'"},
+       {"config_agent", "https_proxy", "kSCTestConfigAgentHTTPSProxy", required_argument, "Add a proxy agent with HTTPS proxy. Format of the argument is 'server:port'"},
+       {"config_agent", "proxy_match_domain", "kSCTestConfigAgentProxyMatchDomain", required_argument, "Add a proxy agent for a match domain. If this option is not specified, 'apple.com' will be used"},
+       {"config_agent", "remove_proxy", "kSCTestConfigAgentRemoveProxy", no_argument, "Remove a proxy configuration, previously configured via 'sctest'"},
+       {"config_agent", "socks_proxy", "kSCTestConfigAgentSOCKSProxy", required_argument, "Add a proxy agent with SOCKS proxy. Format of the argument is 'server:port'"},
+
+       {"reachability", "address", "kSCTestReachabilityAddress", required_argument, "Determine reachability to this address"},
+       {"reachability", "host", "kSCTestReachabilityHost", required_argument, "Determine reachability to this host"},
+       {"reachability", "interface", "kSCTestReachabilityInterface", required_argument, "Determine reachability when scoped to this interface"},
+       {"reachability", "watch", "kSCTestReachabilityWatch", no_argument, "Watch for reachability changes"},
+
+       {"unit_test", "command", "kSCTestUnitTestCommand", required_argument, "Run a unit test for a specific command. If this option is not specified, unit-tests for all commands will be run"},
+       {"unit_test", "list_tests", "kSCTestUnitTestListTests", no_argument, "List the test commands in a JSON format. This is for NPT compliance"},
+       {"unit_test", "test_method", "kSCTestUnitTestTestMethod", required_argument, "Runs a specific unit test. List can be obtained by using the 'test_method_list' option"},
+       {"unit_test", "test_method_list", "kSCTestUnitTestTestMethodList", no_argument, "Lists all the unit tests. A specific one can be run using the 'test_method' option"},
+};
+
+static int testOptionsCount = (sizeof(testOptions) / sizeof(testOptions[0]));
+
+void
+printDeclarations()
+{
+       // Prints to SCTestOptions.h
+
+       const char *keyTemplate = "extern const NSString * const";
+       for (int i = 0; i < testOptionsCount; i++) {
+               char buffer[256] = {0};
+               snprintf(buffer, sizeof(buffer), "%s %s;", keyTemplate, testOptions[i].optionKey);
+               printf("%s\n", buffer);
+       }
+}
+
+void
+printOptionEntries()
+{
+       // Prints to SCTestOptions.h
+
+       const char *entryTemplate = "#define kSCTestOptionEntries";
+       printf("\n%s \\\n", entryTemplate);
+       for (int i = 0; i < testOptionsCount; i++) {
+               printf("\t\t{\"%s\", %d, NULL, 0}, \\", testOptions[i].optionString, testOptions[i].hasArg);
+               printf("\n");
+       }
+}
+
+void
+printUsage()
+{
+       // Prints to SCTestOptions.h
+
+       const char *usageTemplate = "#define kSCTestOptionHelp";
+       printf("\n%s \\\n", usageTemplate);
+       const char *last = "";
+       for (int i = 0; i < testOptionsCount; i++) {
+               if (strcmp(last, testOptions[i].scope)) {
+                       last = testOptions[i].scope;
+                       printf("\t\t\"\\n============== %s options =============\\n\"\\\n", testOptions[i].scope);
+               }
+
+               printf("\t\t\"-%-20s: %s\\n\"\\\n", testOptions[i].optionString, testOptions[i].usageString);
+       }
+}
+
+void
+printDefinitions()
+{
+       // Prints to SCTestOptions.m
+
+       const char *definitionTemplate = "const NSString * const";
+       for (int i = 0; i < testOptionsCount; i++) {
+               char buffer[256] = {0};
+               snprintf(buffer, sizeof(buffer), "%s %-50s= @\"%s_Str\";", definitionTemplate, testOptions[i].optionKey, testOptions[i].optionString);
+               printf("%s\n", buffer);
+       }
+}
+
+int
+main(int argc, char * argv[])
+{
+       char * type = "";
+
+       if (argc >= 2) {
+               type = argv[1];
+       }
+
+       if (strcmp(type, "header") == 0) {
+               // Preamble
+               printf("//\n");
+               printf("// This file is automatically generated. DO NOT EDIT!\n");
+               printf("// To add options, see genSCTestOptions.c\n");
+               printf("//\n");
+
+               // Import header files
+               printf("#import <Foundation/Foundation.h>\n");
+               printf("#import <getopt.h>");
+
+               printf("\n\n");
+
+               // Print the declarations
+               printDeclarations();
+
+               printf("\n\n");
+
+               // Print the option Entries
+               printOptionEntries();
+
+               // Print the usage
+               printUsage();
+       } else if (strcmp(type, "mfile") == 0) {
+               printf("//\n");
+               printf("// This file is automatically generated. DO NOT EDIT!\n");
+               printf("// To add options, see genSCTestOptions.c\n");
+               printf("//\n");
+
+               // Import header files
+               printf("#import \"SCTestOptions.h\"");
+
+               printf("\n\n");
+               printDefinitions();
+       }
+       return 0;
+}
diff --git a/sctest/main.m b/sctest/main.m
new file mode 100644 (file)
index 0000000..e868110
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2016 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@
+ */
+
+#import "SCTest.h"
+#import "SCTestUtils.h"
+
+static void
+usage()
+{
+       NSArray *testClasses = getTestClasses();
+       SCTestLog("\nUsage: sctest <command> <options>");
+       SCTestLog("\nCommands:");
+       for (NSString *testClassName in testClasses) {
+               Class testClass = NSClassFromString(testClassName);
+               SCTestLog("  %15s:   %s", [testClass command].UTF8String, [testClass commandDescription].UTF8String);
+       }
+
+       SCTestLog("\n\nOptions:");
+       SCTestLog(kSCTestOptionHelp "\n");
+
+       ERR_EXIT;
+}
+
+int main(int argc, const char * argv[]) {
+       @autoreleasepool {
+               NSString *testCommand;
+               NSArray<NSString *> *testClasses;
+               BOOL commandValid = NO;
+               NSDictionary *options;
+               Class testClass;
+               SCTest *testClassObject;
+
+               if (argc == 1) {
+                       usage();
+               }
+
+               testCommand = @(argv[1]);
+               // Check if the command is valid
+               testClasses = getTestClasses();
+               for (NSString *testClassName in testClasses) {
+                       Class testClass = NSClassFromString(testClassName);
+                       if ([[testClass command] isEqualToString:testCommand]) {
+                               commandValid = YES;
+                               break;
+                       }
+
+               }
+
+               if (!commandValid) {
+                       SCTestLog("Invalid command: %@", testCommand);
+                       usage();
+               }
+
+               // Create the options dictionary
+               options = getOptionsDictionary(argc, argv);
+               if (options == nil) {
+                       usage();
+               }
+
+               // Initialize the command
+               for (NSString *className in testClasses) {
+                       Class   commandClass = NSClassFromString(className);
+                       if ([testCommand isEqualToString:[commandClass command]]) {
+                               testClass = commandClass;
+                               break;
+                       }
+               }
+
+               testClassObject = [(SCTest *)[testClass alloc] initWithOptions:options];
+               if (testClassObject.options[kSCTestGlobalOptionCPU] != nil) {
+                       cpuStart(testClassObject.globalCPU);
+               }
+
+               if (testClassObject.options[kSCTestGlobalOptionTime] != nil) {
+                       timerStart(testClassObject.globalTimer);
+               }
+
+               [testClassObject start];
+
+               dispatch_main();
+       }
+
+       return 0;
+}
diff --git a/sctest/npt_configd.plist b/sctest/npt_configd.plist
new file mode 100644 (file)
index 0000000..5ece97d
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>ID</key>
+       <string>com.apple.npt.configd</string>
+       <key>CommandList</key>
+       <array>
+               <string>/usr/local/bin/sctest</string>
+               <string>unit_test</string>
+               <string>-list_tests</string>
+       </array>
+</dict>
+</plist>
diff --git a/sctest/sctest-entitlements.plist b/sctest/sctest-entitlements.plist
new file mode 100644 (file)
index 0000000..c07263d
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>com.apple.private.nehelper.privileged</key>
+       <true/>
+       <key>com.apple.private.necp.policies</key>
+       <true/>
+       <key>com.apple.SystemConfiguration.SCPreferences-write-access</key>
+       <array>
+               <string>SCTestPreferences.plist</string>
+       </array>
+       <key>com.apple.SystemConfiguration.SCDynamicStore-write-access</key>
+       <true/>
+</dict>
+</plist>
index 605cd2a07828183d06fbdb8adfa795174a706495..e2ab8b20f8a0a2dc9ba37692c2d7cb13bd7c80e4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2008, 2011-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2003-2008, 2011-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
@@ -28,6 +28,7 @@
  * - initial revision
  */
 
+#include <TargetConditionals.h>
 #include <dlfcn.h>
 #include <unistd.h>
 #include <sys/param.h>
@@ -979,6 +980,68 @@ on_off_str(Boolean on)
 
 /* -------------------- */
 
+#if    !TARGET_OS_IPHONE
+
+#include "InterfaceNamerControlPrefs.h"
+
+static void
+allow_new_interfaces_usage(void)
+{
+       fprintf(stderr, "usage: scutil --allow-new-interfaces [on|off|default]\n");
+       return;
+}
+
+__private_extern__
+void
+do_ifnamer(char * pref, int argc, char **argv)
+{
+       Boolean allow   = FALSE;
+
+       if (argc > 1) {
+               allow_new_interfaces_usage();
+               exit(1);
+       }
+
+       if (strcmp(pref, "allow-new-interfaces")) {
+               exit(0);
+       }
+
+       if (argc == 0) {
+               SCPrint(TRUE, stdout, CFSTR("AllowNewInterfaces is %s\n"),
+                       on_off_str(InterfaceNamerControlPrefsAllowNewInterfaces()));
+               exit(0);
+       }
+
+       if        ((strcasecmp(argv[0], "disable") == 0) ||
+                  (strcasecmp(argv[0], "no"     ) == 0) ||
+                  (strcasecmp(argv[0], "off"    ) == 0) ||
+                  (strcasecmp(argv[0], "0"      ) == 0)) {
+               allow = FALSE;
+       } else if ((strcasecmp(argv[0], "enable") == 0) ||
+                  (strcasecmp(argv[0], "yes"   ) == 0) ||
+                  (strcasecmp(argv[0], "on"   ) == 0) ||
+                  (strcasecmp(argv[0], "1"     ) == 0)) {
+               allow = TRUE;
+       } else if (strcasecmp(argv[0], "default") == 0) {
+               allow = FALSE;
+       } else {
+               allow_new_interfaces_usage();
+               exit(1);
+       }
+
+       if (!InterfaceNamerControlPrefsSetAllowNewInterfaces(allow)) {
+               SCPrint(TRUE, stderr, CFSTR("failed to set preferences\n"));
+               exit(2);
+       }
+
+       exit(0);
+       return;
+}
+
+#endif // !TARGET_OS_IPHONE
+
+/* -------------------- */
+
 #include "IPMonitorControlPrefs.h"
 
 __private_extern__
index fcf3bcb33d966d7c309b8f693b6e76cca3d8b7b5..b4f397a8df4ce2bb346f6cb4d38f9c61572dd53b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2005-2007, 2012, 2013, 2015, 2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2003, 2005-2007, 2012, 2013, 2015-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
@@ -31,6 +31,7 @@
 #ifndef _PREFS_H
 #define _PREFS_H
 
+#include <TargetConditionals.h>
 #include <sys/cdefs.h>
 #include <SystemConfiguration/SystemConfiguration.h>
 
@@ -70,6 +71,11 @@ void do_prefs_remove         (int argc, char **argv);
 
 void   do_log                  (char *pref, int argc, char **argv);
 void   do_disable_until_needed (int argc, char **argv);
+
+#if    !TARGET_OS_IPHONE
+void   do_ifnamer              (char *pref, int argc, char **argv);
+#endif // !TARGET_OS_IPHONE
+
 __END_DECLS
 
 #endif /* !_PREFS_H */
index cb15a5a9b3213bc0fcf334df916924eeee954d37..dda8ac210e90e448146ad19c0ecf71dca58993da 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
@@ -108,7 +108,10 @@ static const struct option longopts[] = {
        { "password",           required_argument,      NULL,   0       },
        { "secret",             required_argument,      NULL,   0       },
        { "log",                required_argument,      NULL,   0       },
-       { "disable-until-needed", no_argument,  NULL,   0       },
+#if    !TARGET_OS_IPHONE
+       { "allow-new-interfaces", no_argument,          NULL,   0       },
+#endif // !TARGET_OS_IPHONE
+       { "disable-until-needed", no_argument,          NULL,   0       },
        { NULL,                 0,                      NULL,   0       }
 };
 
@@ -365,6 +368,12 @@ usage(const char *command)
                SCPrint(TRUE, stderr, CFSTR("\tmanage secondary interface demand.\n"));
        }
 
+#if    !TARGET_OS_IPHONE
+       SCPrint(TRUE, stderr, CFSTR("\n"));
+       SCPrint(TRUE, stderr, CFSTR("   or: %s --allow-new-interfaces [off|on]\n"), command);
+       SCPrint(TRUE, stderr, CFSTR("\tmanage new interface creation with screen locked.\n"));
+#endif // !TARGET_OS_IPHONE
+
        if (getenv("ENABLE_EXPERIMENTAL_SCUTIL_COMMANDS")) {
                SCPrint(TRUE, stderr, CFSTR("\n"));
                SCPrint(TRUE, stderr, CFSTR("   or: %s --net\n"), command);
@@ -393,29 +402,32 @@ prompt(EditLine *el)
 int
 main(int argc, char * const argv[])
 {
-       Boolean                 disableUntilNeeded = FALSE;
-       Boolean                 doDNS   = FALSE;
-       Boolean                 doNet   = FALSE;
-       Boolean                 doNWI   = FALSE;
-       Boolean                 doPrefs = FALSE;
-       Boolean                 doProxy = FALSE;
-       Boolean                 doReach = FALSE;
-       Boolean                 doSnap  = FALSE;
-       char                    *error  = NULL;
-       char                    *get    = NULL;
-       char                    *log    = NULL;
+#if    !TARGET_OS_IPHONE
+       Boolean                 allowNewInterfaces      = FALSE;
+#endif // !TARGET_OS_IPHONE
+       Boolean                 disableUntilNeeded      = FALSE;
+       Boolean                 doDNS                   = FALSE;
+       Boolean                 doNet                   = FALSE;
+       Boolean                 doNWI                   = FALSE;
+       Boolean                 doPrefs                 = FALSE;
+       Boolean                 doProxy                 = FALSE;
+       Boolean                 doReach                 = FALSE;
+       Boolean                 doSnap                  = FALSE;
+       char                    *error                  = NULL;
+       char                    *get                    = NULL;
+       char                    *log                    = NULL;
        extern int              optind;
        int                     opt;
        int                     opti;
-       const char              *prog   = argv[0];
-       char                    *renew  = NULL;
-       char                    *set    = NULL;
-       char                    *nc_cmd = NULL;
+       const char              *prog                   = argv[0];
+       char                    *renew                  = NULL;
+       char                    *set                    = NULL;
+       char                    *nc_cmd                 = NULL;
        InputRef                src;
-       int                     timeout = 15;   /* default timeout (in seconds) */
-       char                    *wait   = NULL;
-       Boolean                 watch   = FALSE;
-       int                     xStore  = 0;    /* non dynamic store command line options */
+       int                     timeout                 = 15;   /* default timeout (in seconds) */
+       char                    *wait                   = NULL;
+       Boolean                 watch                   = FALSE;
+       int                     xStore                  = 0;    /* non dynamic store command line options */
 
        /* process any arguments */
 
@@ -486,6 +498,11 @@ main(int argc, char * const argv[])
                        } else if (strcmp(longopts[opti].name, "log") == 0) {
                                log = optarg;
                                xStore++;
+#if    !TARGET_OS_IPHONE
+                       } else if (strcmp(longopts[opti].name, "allow-new-interfaces") == 0) {
+                               allowNewInterfaces = TRUE;
+                               xStore++;
+#endif // !TARGET_OS_IPHONE
                        } else if (strcmp(longopts[opti].name, "disable-until-needed") == 0) {
                                disableUntilNeeded = TRUE;
                                xStore++;
@@ -620,11 +637,20 @@ main(int argc, char * const argv[])
                /* NOT REACHED */
        }
 
+#if    !TARGET_OS_IPHONE
+       /* allowNewInterfaces */
+       if (allowNewInterfaces) {
+               do_ifnamer("allow-new-interfaces", argc, (char * *)argv);
+               /* NOT REACHED */
+       }
+#endif // !TARGET_OS_IPHONE
+
        /* disableUntilNeeded */
        if (disableUntilNeeded) {
                do_disable_until_needed(argc, (char * *)argv);
                /* NOT REACHED */
        }
+
        /* network connection commands */
        if (nc_cmd) {
                if (find_nc_cmd(nc_cmd) < 0) {