]> git.saurik.com Git - apple/configd.git/blobdiff - scutil.tproj/nc.c
configd-596.15.tar.gz
[apple/configd.git] / scutil.tproj / nc.c
index 48806d0794cc485bdfd44ab4164c93b02dcaf848..76ba1deb45d566c2e367a8e45c1bef8ee76dcc41 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2011 Apple Inc. All rights reserved.
+ * Copyright (c) 2010-2013 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
  * - initial revision
  * February 8, 2011                    Kevin Wells <kcw@apple.com>
  * - added "select" command
+ * January 2012                                Kevin Wells <kcw@apple.com>
+ * - added arguments to "start" command to pass authentication credentials
+ * - "show" now takes a service name as an alternative to a service ID
+ * - fixes a bug whereby "IPv4" was being displayed as a subtype to IPsec services
+ * - improved format of "list" output
+ * - general cleanup of error messages and some variable names
  */
 
 
 #include "nc.h"
 #include "prefs.h"
 
+#include <SystemConfiguration/VPNConfiguration.h>
+
+#if    TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
+#include <MobileInstallation/MobileInstallation.h>
+#endif // TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
+
 #include <sys/time.h>
 
+CFStringRef                    username        = NULL;
+CFStringRef                    password        = NULL;
+CFStringRef                    sharedsecret    = NULL;
+
+static Boolean                 ondemandwatch   = FALSE;
+static CFStringRef             ondemand_nodename = NULL;
 
-static SCNetworkConnectionRef  connectionRef   = NULL;
+static SCNetworkConnectionRef  connection      = NULL;
 static int                     n_callback      = 0;
 
 
@@ -56,19 +74,18 @@ my_CFRelease(void *t)
 }
 
 /* -----------------------------------------------------------------------------
------------------------------------------------------------------------------ */
-static CFStringRef
-nc_copy_serviceID(int argc, char **argv)
-{
-       CFStringRef             serviceIDRef    = NULL;
-
-       if (argc == 0) {
-               serviceIDRef = _copyStringFromSTDIN();
-       } else {
-               serviceIDRef = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
+ ----------------------------------------------------------------------------- */
+static void
+nc_get_service_type_and_subtype(SCNetworkServiceRef service, CFStringRef *iftype, CFStringRef *ifsubtype) {
+       SCNetworkInterfaceRef interface = SCNetworkServiceGetInterface(service);
+       SCNetworkInterfaceRef child = SCNetworkInterfaceGetInterface(interface);
+
+       *iftype = SCNetworkInterfaceGetInterfaceType(interface);
+       *ifsubtype = NULL;
+       if (CFEqual(*iftype, kSCNetworkInterfaceTypePPP) ||
+           CFEqual(*iftype, kSCNetworkInterfaceTypeVPN)) {
+           *ifsubtype = (child != NULL) ? SCNetworkInterfaceGetInterfaceType(child) : NULL;
        }
-
-       return serviceIDRef;
 }
 
 /* -----------------------------------------------------------------------------
@@ -114,19 +131,40 @@ nc_copy_service(SCNetworkSetRef set, CFStringRef identifier)
                        } else {
                                // if multiple services match
                                selected = NULL;
-                               SCPrint(TRUE, stdout, CFSTR("multiple services match\n"));
+                               SCPrint(TRUE, stderr, CFSTR("Multiple services match\n"));
                                goto done;
                        }
                }
        }
 
-    done :
+done :
 
        if (selected != NULL) CFRetain(selected);
        if (services != NULL) CFRelease(services);
        return selected;
 }
 
+/* -----------------------------------------------------------------------------
+ ----------------------------------------------------------------------------- */
+static SCNetworkServiceRef
+nc_copy_service_from_arguments(int argc, char **argv, SCNetworkSetRef set) {
+       CFStringRef             serviceID       = NULL;
+       SCNetworkServiceRef     service         = NULL;
+
+       if (argc == 0) {
+               serviceID = _copyStringFromSTDIN(CFSTR("Service"), NULL);
+       } else {
+               serviceID = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
+       }
+       if (serviceID == NULL) {
+               SCPrint(TRUE, stderr, CFSTR("No service ID specified\n"));
+               return NULL;
+       }
+       service = nc_copy_service(set, serviceID);
+       my_CFRelease(&serviceID);
+       return service;
+}
+
 
 /* -----------------------------------------------------------------------------
 ----------------------------------------------------------------------------- */
@@ -194,18 +232,8 @@ nc_create_connection(int argc, char **argv, Boolean exit_on_failure)
 {
        SCNetworkConnectionContext      context = { 0, &n_callback, NULL, NULL, NULL };
        SCNetworkServiceRef             service;
-       CFStringRef                     serviceIDRef;
-
-       serviceIDRef = nc_copy_serviceID(argc, argv);
-       if (serviceIDRef == NULL) {
-               SCPrint(TRUE, stderr, CFSTR("No service identifier\n"));
-               if (exit_on_failure)
-                       exit(1);
-               return;
-       }
 
-       service = nc_copy_service(NULL, serviceIDRef);
-       CFRelease(serviceIDRef);
+       service = nc_copy_service_from_arguments(argc, argv, NULL);
        if (service == NULL) {
                SCPrint(TRUE, stderr, CFSTR("No service\n"));
                if (exit_on_failure)
@@ -213,21 +241,86 @@ nc_create_connection(int argc, char **argv, Boolean exit_on_failure)
                return;
        }
 
-       connectionRef = SCNetworkConnectionCreateWithService(NULL, service, nc_callback, &context);
-       if (connectionRef == NULL) {
-               SCPrint(TRUE, stderr, CFSTR("nc_create_connection SCNetworkConnectionCreateWithServiceID() failed to create connectionRef: %s\n"), SCErrorString(SCError()));
+       connection = SCNetworkConnectionCreateWithService(NULL, service, nc_callback, &context);
+       CFRelease(service);
+       if (connection == NULL) {
+               SCPrint(TRUE, stderr, CFSTR("Could not create connection: %s\n"), SCErrorString(SCError()));
                if (exit_on_failure)
                        exit(1);
                return;
        }
 }
 
+/* -----------------------------------------------------------------------------
+ ----------------------------------------------------------------------------- */
+
+static void
+nc_trigger(int argc, char **argv)
+{
+       Boolean         background      = FALSE;
+       int             i;
+       CFStringRef     hostName        = NULL;
+       int             port            = 80;
+
+       for (i = 0; i < 3 && i < argc; i++) {
+               /* Parse host name. Must be first arg. */
+               if (i == 0) {
+                       hostName = CFStringCreateWithCString(NULL, argv[i], kCFStringEncodingUTF8);
+                       continue;
+               }
+
+               /* Check for optional background flag */
+               if (strcmp(argv[i], "background") == 0) {
+                       background = TRUE;
+                       continue;
+               }
+
+               /* Parse optional port number */
+               CFStringRef str = CFStringCreateWithCString(NULL, argv[i], kCFStringEncodingUTF8);
+               if (str) {
+                       int num = CFStringGetIntValue(str);
+                       if (num) {
+                               port = num;
+                       }
+                       my_CFRelease(&str);
+               }
+       }
+
+       if (hostName) {
+               CFReadStreamRef         readStream      = NULL;
+               CFWriteStreamRef        writeStream     = NULL;
+
+               CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, hostName, port, &readStream, &writeStream);
+
+               if (background) {
+                       CFReadStreamSetProperty(readStream, CFSTR("kCFStreamNetworkServiceType"), CFSTR("kCFStreamNetworkServiceTypeBackground"));
+                       CFWriteStreamSetProperty(writeStream, CFSTR("kCFStreamNetworkServiceType"), CFSTR("kCFStreamNetworkServiceTypeBackground"));
+               }
+
+               if (readStream && writeStream) {
+                       CFReadStreamOpen(readStream);
+                       CFWriteStreamOpen(writeStream);
+                       SCPrint(TRUE, stdout, CFSTR("Opened stream to %@, port %d%s\n"), hostName, port, background ? ", background traffic class" : "");
+                       sleep(1);
+               }
+
+               my_CFRelease(&readStream);
+               my_CFRelease(&writeStream);
+       } else {
+               SCPrint(TRUE, stderr, CFSTR("Invalid or missing host name\n"));
+       }
+
+       my_CFRelease(&hostName);
+
+       exit(0);
+}
+
 /* -----------------------------------------------------------------------------
 ----------------------------------------------------------------------------- */
 static void
 nc_release_connection()
 {
-       my_CFRelease(&connectionRef);
+       my_CFRelease(&connection);
 }
 
 /* -----------------------------------------------------------------------------
@@ -235,10 +328,76 @@ nc_release_connection()
 static void
 nc_start(int argc, char **argv)
 {
+       CFMutableDictionaryRef          userOptions = NULL;
+       CFStringRef                     iftype = NULL;
+       CFStringRef                     ifsubtype = NULL;
+       SCNetworkServiceRef             service = NULL;
+
        nc_create_connection(argc, argv, TRUE);
 
-       SCNetworkConnectionStart(connectionRef, 0, TRUE);
+       service = SCNetworkConnectionGetService(connection);
+       nc_get_service_type_and_subtype(service, &iftype, &ifsubtype);
+
+       userOptions = CFDictionaryCreateMutable(NULL, 0,
+                                               &kCFTypeDictionaryKeyCallBacks,
+                                               &kCFTypeDictionaryValueCallBacks);
+
+       Boolean isL2TP = (CFEqual(iftype, kSCEntNetPPP) &&
+                         (ifsubtype != NULL) && CFEqual(ifsubtype, kSCValNetInterfaceSubTypeL2TP));
+
+       if (CFEqual(iftype, kSCEntNetPPP)) {
+               CFMutableDictionaryRef pppEntity  = CFDictionaryCreateMutable(NULL, 0,
+                                                                          &kCFTypeDictionaryKeyCallBacks,
+                                                                          &kCFTypeDictionaryValueCallBacks);
+
+               if (username != NULL) {
+                       CFDictionarySetValue(pppEntity, kSCPropNetPPPAuthName, username);
+               }
+               if (password != NULL) {
+                       CFDictionarySetValue(pppEntity, kSCPropNetPPPAuthPassword, password);
+               }
+               CFDictionarySetValue(userOptions, kSCEntNetPPP, pppEntity);
+               my_CFRelease(&pppEntity);
+       }
+       if (CFEqual(iftype, kSCEntNetIPSec) || isL2TP) {
+               CFMutableDictionaryRef ipsecEntity  = CFDictionaryCreateMutable(NULL, 0,
+                                                                          &kCFTypeDictionaryKeyCallBacks,
+                                                                          &kCFTypeDictionaryValueCallBacks);
+               if (!isL2TP) {
+                       if (username != NULL) {
+                               CFDictionarySetValue(ipsecEntity, kSCPropNetIPSecXAuthName, username);
+                       }
+                       if (password != NULL) {
+                               CFDictionarySetValue(ipsecEntity, kSCPropNetIPSecXAuthPassword, password);
+                       }
+               }
+               if (sharedsecret != NULL) {
+                       CFDictionarySetValue(ipsecEntity, kSCPropNetIPSecSharedSecret, sharedsecret);
+               }
+               CFDictionarySetValue(userOptions, kSCEntNetIPSec, ipsecEntity);
+               my_CFRelease(&ipsecEntity);
+       }
+       if (CFEqual(iftype, kSCEntNetVPN)) {
+               CFMutableDictionaryRef vpnEntity  = CFDictionaryCreateMutable(NULL, 0,
+                                                                          &kCFTypeDictionaryKeyCallBacks,
+                                                                          &kCFTypeDictionaryValueCallBacks);
+               if (username != NULL) {
+                       CFDictionarySetValue(vpnEntity, kSCPropNetVPNAuthName, username);
+               }
+               if (password != NULL) {
+                       CFDictionarySetValue(vpnEntity, kSCPropNetVPNAuthPassword, password);
+               }
+               CFDictionarySetValue(userOptions, kSCEntNetVPN, vpnEntity);
+               my_CFRelease(&vpnEntity);
+       }
+       // If it doesn't match any VPN type, fail silently
+
+       if (!SCNetworkConnectionStart(connection, userOptions, TRUE)) {
+               SCPrint(TRUE, stderr, CFSTR("Could not start connection: %s\n"), SCErrorString(SCError()));
+               exit(1);
+       };
 
+       CFRelease(userOptions);
        nc_release_connection();
        exit(0);
 }
@@ -250,7 +409,10 @@ nc_stop(int argc, char **argv)
 {
        nc_create_connection(argc, argv, TRUE);
 
-       SCNetworkConnectionStop(connectionRef, TRUE);
+       if (!SCNetworkConnectionStop(connection, TRUE)) {
+               SCPrint(TRUE, stderr, CFSTR("Could not stop connection: %s\n"), SCErrorString(SCError()));
+               exit(1);
+       };
 
        nc_release_connection();
        exit(0);
@@ -263,7 +425,7 @@ nc_suspend(int argc, char **argv)
 {
        nc_create_connection(argc, argv, TRUE);
 
-       SCNetworkConnectionSuspend(connectionRef);
+       SCNetworkConnectionSuspend(connection);
 
        nc_release_connection();
        exit(0);
@@ -276,7 +438,7 @@ nc_resume(int argc, char **argv)
 {
        nc_create_connection(argc, argv, TRUE);
 
-       SCNetworkConnectionResume(connectionRef);
+       SCNetworkConnectionResume(connection);
 
        nc_release_connection();
        exit(0);
@@ -291,8 +453,8 @@ nc_status(int argc, char **argv)
 
        nc_create_connection(argc, argv, TRUE);
 
-       status = SCNetworkConnectionGetStatus(connectionRef);
-       nc_callback(connectionRef, status, NULL);
+       status = SCNetworkConnectionGetStatus(connection);
+       nc_callback(connection, status, NULL);
 
        nc_release_connection();
        exit(0);
@@ -305,21 +467,21 @@ nc_watch(int argc, char **argv)
 
        nc_create_connection(argc, argv, TRUE);
 
-       status = SCNetworkConnectionGetStatus(connectionRef);
+       status = SCNetworkConnectionGetStatus(connection);
 
        // report initial status
        n_callback = 0;
-       nc_callback(connectionRef, status, &n_callback);
+       nc_callback(connection, status, &n_callback);
 
        // setup watcher
        if (doDispatch) {
-               if (!SCNetworkConnectionSetDispatchQueue(connectionRef, dispatch_get_current_queue())) {
-                       printf("SCNetworkConnectionSetDispatchQueue() failed: %s\n", SCErrorString(SCError()));
+               if (!SCNetworkConnectionSetDispatchQueue(connection, dispatch_get_main_queue())) {
+                       SCPrint(TRUE, stderr, CFSTR("Unable to schedule watch process: %s\n"), SCErrorString(SCError()));
                        exit(1);
                }
        } else {
-               if (!SCNetworkConnectionScheduleWithRunLoop(connectionRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
-                       printf("SCNetworkConnectinScheduleWithRunLoop() failed: %s\n", SCErrorString(SCError()));
+               if (!SCNetworkConnectionScheduleWithRunLoop(connection, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
+                       SCPrint(TRUE, stderr, CFSTR("Unable to schedule watch process: %s\n"), SCErrorString(SCError()));
                        exit(1);
                }
        }
@@ -340,7 +502,7 @@ nc_statistics(int argc, char **argv)
 
        nc_create_connection(argc, argv, TRUE);
 
-       stats_dict = SCNetworkConnectionCopyStatistics(connectionRef);
+       stats_dict = SCNetworkConnectionCopyStatistics(connection);
 
        if (stats_dict) {
                SCPrint(TRUE, stdout, CFSTR("%@\n"), stats_dict);
@@ -356,6 +518,85 @@ nc_statistics(int argc, char **argv)
 
 /* -----------------------------------------------------------------------------
 ----------------------------------------------------------------------------- */
+static void
+checkOnDemandHost(SCDynamicStoreRef store, CFStringRef nodeName, Boolean retry)
+{
+       Boolean                         ok;
+       CFStringRef                     connectionServiceID     = NULL;
+       SCNetworkConnectionStatus       connectionStatus        = 0;
+       CFStringRef                     vpnRemoteAddress        = NULL;
+
+       SCPrint(TRUE, stdout, CFSTR("OnDemand host/domain check (%sretry)\n"), retry ? "" : "no ");
+
+       ok = __SCNetworkConnectionCopyOnDemandInfoWithName(&store,
+                                                          nodeName,
+                                                          retry,
+                                                          &connectionServiceID,
+                                                          &connectionStatus,
+                                                          &vpnRemoteAddress);
+
+       if (ok) {
+               SCPrint(TRUE, stdout, CFSTR("  serviceID      = %@\n"), connectionServiceID);
+               SCPrint(TRUE, stdout, CFSTR("  remote address = %@\n"), vpnRemoteAddress);
+       } else if (SCError() != kSCStatusOK) {
+               SCPrint(TRUE, stdout, CFSTR("%sretry\n"), retry ? "" : "no ");
+               SCPrint(TRUE, stdout,
+                       CFSTR("  Unable to copy OnDemand information for connection: %s\n"),
+                       SCErrorString(SCError()));
+       } else {
+               SCPrint(TRUE, stdout, CFSTR("  no match\n"));
+       }
+
+       if (connectionServiceID != NULL) {
+               CFRelease(connectionServiceID);
+               connectionServiceID = NULL;
+       }
+       if (vpnRemoteAddress != NULL) {
+               CFRelease(vpnRemoteAddress);
+               vpnRemoteAddress = NULL;
+       }
+
+       return;
+}
+
+static void
+nc_ondemand_callback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
+{
+       CFStringRef             key             = NULL;
+       CFDictionaryRef         ondemand_dict   = NULL;
+       struct tm               tm_now;
+       struct timeval          tv_now;
+
+       if (CFArrayGetCount(changedKeys) < 1) {
+               return;
+       }
+
+       (void)gettimeofday(&tv_now, NULL);
+       (void)localtime_r(&tv_now.tv_sec, &tm_now);
+
+       SCPrint(TRUE, stdout, CFSTR("\n*** %2d:%02d:%02d.%03d\n\n"),
+               tm_now.tm_hour,
+               tm_now.tm_min,
+               tm_now.tm_sec,
+               tv_now.tv_usec / 1000);
+
+       if (ondemand_nodename) {
+               checkOnDemandHost(store, ondemand_nodename, FALSE);
+               checkOnDemandHost(store, ondemand_nodename, TRUE);
+       } else {
+               key = CFArrayGetValueAtIndex(changedKeys, 0);
+
+               ondemand_dict = SCDynamicStoreCopyValue(store, key);
+               if (ondemand_dict) {
+                       SCPrint(TRUE, stdout, CFSTR("%@ %@\n"), kSCEntNetOnDemand, ondemand_dict);
+               } else {
+                       SCPrint(TRUE, stdout, CFSTR("%@ not configured\n"), kSCEntNetOnDemand);
+               }
+
+               my_CFRelease(&ondemand_dict);
+       }
+}
+
 static void
 nc_ondemand(int argc, char **argv)
 {
@@ -364,23 +605,64 @@ nc_ondemand(int argc, char **argv)
        CFDictionaryRef         ondemand_dict   = NULL;
        SCDynamicStoreRef       store;
 
-       store = SCDynamicStoreCreate(NULL, CFSTR("scutil --nc"), NULL, NULL);
+       store = SCDynamicStoreCreate(NULL, CFSTR("scutil --nc"), nc_ondemand_callback, NULL);
        if (store == NULL) {
-               SCPrint(TRUE, stderr, CFSTR("do_nc_ondemand SCDynamicStoreCreate() failed: %s\n"), SCErrorString(SCError()));
+               SCPrint(TRUE, stderr, CFSTR("Unable to create dynamic store: %s\n"), SCErrorString(SCError()));
                goto done;
        }
 
-       key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetOnDemand);
-       if (key == NULL) {
-               SCPrint(TRUE, stderr, CFSTR("do_nc_ondemand SCDynamicStoreKeyCreateNetworkGlobalEntity() failed: %s\n"), SCErrorString(SCError()));
+       if (argc == 1) {
+#if    !TARGET_IPHONE_SIMULATOR
+               if (strcmp("--refresh", argv[0]) == 0) {
+                       SCNetworkConnectionRef  connection      = NULL;
+
+                       connection = SCNetworkConnectionCreate(kCFAllocatorDefault, NULL, NULL);
+                       if (connection && SCNetworkConnectionRefreshOnDemandState(connection)) {
+                               exit_code = 0;
+                       }
+
+                       if (exit_code) {
+                               SCPrint(TRUE, stderr, CFSTR("Unable to refresh OnDemand state: %s\n"), SCErrorString(SCError()));
+                       }
+
+                       my_CFRelease(&connection);
+                       goto done;
+               }
+#endif // !TARGET_IPHONE_SIMULATOR
+
+               ondemand_nodename = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
+       } else if (argc != 0) {
+               SCPrint(TRUE, stderr, CFSTR("Usage: scutil --nc ondemand [-W] [hostname]\n"
+                                           "       scutil --nc ondemand -- --refresh\n"));
                goto done;
        }
 
-       ondemand_dict = SCDynamicStoreCopyValue(store, key);
-       if (ondemand_dict) {
-               SCPrint(TRUE, stdout, CFSTR("%@ %@\n"), kSCEntNetOnDemand, ondemand_dict);
+       key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetOnDemand);
+
+       if (ondemand_nodename) {
+               checkOnDemandHost(store, ondemand_nodename, FALSE);
+               checkOnDemandHost(store, ondemand_nodename, TRUE);
        } else {
-               SCPrint(TRUE, stdout, CFSTR("%@ not configured\n"), kSCEntNetOnDemand);
+               ondemand_dict = SCDynamicStoreCopyValue(store, key);
+               if (ondemand_dict) {
+                       SCPrint(TRUE, stdout, CFSTR("%@ %@\n"), kSCEntNetOnDemand, ondemand_dict);
+               } else {
+                       SCPrint(TRUE, stdout, CFSTR("%@ not configured\n"), kSCEntNetOnDemand);
+               }
+       }
+
+       if (ondemandwatch) {
+               CFMutableArrayRef       keys    = NULL;
+
+               keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+               CFArrayAppendValue(keys, key);
+               SCDynamicStoreSetNotificationKeys(store, keys, NULL);
+
+               my_CFRelease(&keys);
+
+               SCDynamicStoreSetDispatchQueue(store, dispatch_get_main_queue());
+
+               CFRunLoopRun();
        }
 
        exit_code = 0;
@@ -388,36 +670,97 @@ done:
        my_CFRelease(&ondemand_dict);
        my_CFRelease(&key);
        my_CFRelease(&store);
+       my_CFRelease(&ondemand_nodename);
        exit(exit_code);
 }
 
-/* -----------------------------------------------------------------------------
- Given a string 'key' and a string prefix 'prefix',
- return the next component in the slash '/' separated
- key.  If no slash follows the prefix, return NULL.
 
- Examples:
- 1. key = "a/b/c" prefix = "a/"    returns "b"
- 2. key = "a/b/c" prefix = "a/b/"  returns NULL
------------------------------------------------------------------------------ */
-CFStringRef parse_component(CFStringRef key, CFStringRef prefix)
+/* -----------------------------------------------------------------------------
+ ----------------------------------------------------------------------------- */
+CFStringRef
+copy_padded_string(CFStringRef original, int width, CFStringRef prefix, CFStringRef suffix)
 {
-       CFMutableStringRef      comp;
-       CFRange                 range;
+       CFMutableStringRef      padded;
 
-       if (!CFStringHasPrefix(key, prefix))
-               return NULL;
+       padded = CFStringCreateMutable(NULL, 0);
+       if (prefix != NULL) {
+               CFStringAppend(padded, prefix);
+       }
+       if (original != NULL) {
+               CFStringAppend(padded, original);
+       }
+       if (suffix != NULL) {
+               CFStringAppend(padded, suffix);
+       }
+       CFStringPad(padded, CFSTR(" "), MAX(CFStringGetLength(original), width), 0);
+       return padded;
+}
 
-       comp = CFStringCreateMutableCopy(NULL, 0, key);
-       CFStringDelete(comp, CFRangeMake(0, CFStringGetLength(prefix)));
-       range = CFStringFind(comp, CFSTR("/"), 0);
-       if (range.location == kCFNotFound) {
-               CFRelease(comp);
-               return NULL;
+CFStringRef
+copy_VPN_status(SCNetworkServiceRef service)
+{
+       CFStringRef output = NULL;
+       SCNetworkConnectionStatus status = kSCNetworkConnectionInvalid;
+       SCNetworkConnectionRef service_connection = NULL;
+
+       /* Only calculate status is the service is enabled. Default is invalid. */
+       if (SCNetworkServiceGetEnabled(service)) {
+               service_connection = SCNetworkConnectionCreateWithService(NULL, service, NULL, NULL);
+               if (service_connection == NULL) goto done;
+               status = SCNetworkConnectionGetStatus(service_connection);
        }
-       range.length = CFStringGetLength(comp) - range.location;
-       CFStringDelete(comp, range);
-       return comp;
+
+       output = CFStringCreateWithCString(NULL, nc_status_string(status), kCFStringEncodingUTF8);
+
+done:
+       my_CFRelease(&service_connection);
+       return output;
+}
+
+static void
+nc_print_VPN_service(SCNetworkServiceRef service)
+{
+       SCNetworkInterfaceRef interface = NULL;
+       CFStringRef display_name = NULL;
+       CFStringRef display_name_padded = NULL;
+       CFStringRef service_id = NULL;
+       CFStringRef service_name = NULL;
+       CFStringRef service_name_padded = NULL;
+       CFStringRef service_status = NULL;
+       CFStringRef service_status_padded = NULL;
+       CFStringRef sub_type = NULL;
+       CFStringRef type = NULL;
+
+       nc_get_service_type_and_subtype(service, &type, &sub_type);
+
+       service_name = SCNetworkServiceGetName(service);
+       service_name_padded = copy_padded_string(service_name, 32, CFSTR("\""), CFSTR("\""));
+
+       service_id = SCNetworkServiceGetServiceID(service);
+
+       interface = SCNetworkServiceGetInterface(service);
+       display_name = SCNetworkInterfaceGetLocalizedDisplayName(interface);
+       display_name_padded = copy_padded_string(display_name, 18, NULL, NULL);
+
+       service_status = copy_VPN_status(service);
+       service_status_padded = copy_padded_string(service_status, 16, CFSTR("("), CFSTR(")"));
+
+       SCPrint(TRUE,
+               stdout,
+               CFSTR("%@ %@ %@ %@ %@ [%@%@%@]\n"),
+               SCNetworkServiceGetEnabled(service) ? CFSTR("*") : CFSTR(" "),
+               service_status_padded,
+               service_id,
+               display_name_padded,
+               service_name_padded,
+               type,
+               (sub_type == NULL) ? CFSTR("") : CFSTR(":"),
+               (sub_type == NULL) ? CFSTR("") : sub_type);
+
+       CFRelease(display_name_padded);
+       CFRelease(service_name_padded);
+       CFRelease(service_status_padded);
+       my_CFRelease(&service_status);
 }
 
 
@@ -427,208 +770,400 @@ static void
 nc_list(int argc, char **argv)
 {
        int                     count;
-       int                     exit_code       = 1;
        int                     i;
-       CFStringRef             key             = NULL;
-       CFMutableDictionaryRef  names           = NULL;
        CFArrayRef              services        = NULL;
-       CFStringRef             setup           = NULL;
-       SCDynamicStoreRef       store;
 
-       store = SCDynamicStoreCreate(NULL, CFSTR("scutil --nc"), NULL, NULL);
-       if (store == NULL) {
-               SCPrint(TRUE, stderr, CFSTR("nc_list SCDynamicStoreCreate() failed: %s\n"), SCErrorString(SCError()));
-               goto done;
-       }
-       key = SCDynamicStoreKeyCreateNetworkServiceEntity(0, kSCDynamicStoreDomainSetup, kSCCompAnyRegex, kSCEntNetInterface);
-       if (key == NULL ) {
-               SCPrint(TRUE, stderr, CFSTR("nc_list SCDynamicStoreKeyCreateNetworkServiceEntity() failed to create key string\n"));
-               goto done;
-       }
-       setup = SCDynamicStoreKeyCreate(0, CFSTR("%@/%@/%@/"), kSCDynamicStoreDomainSetup, kSCCompNetwork, kSCCompService);
-       if (setup == NULL) {
-               SCPrint(TRUE, stderr, CFSTR("nc_list SCDynamicStoreKeyCreate() failed to create setup string\n"));
-               goto done;
-       }
-       names = CFDictionaryCreateMutable(NULL,
-                                         0,
-                                         &kCFTypeDictionaryKeyCallBacks,
-                                         &kCFTypeDictionaryValueCallBacks);
-       if (names == NULL) {
-               SCPrint(TRUE, stderr, CFSTR("nc_list CFDictionaryCreateMutable() failed to create names dictionary\n"));
-               goto done;
-       }
+       SCPrint(TRUE, stdout, CFSTR("Available network connection services in the current set (*=enabled):\n"));
        services = SCNetworkConnectionCopyAvailableServices(NULL);
        if (services != NULL) {
                count = CFArrayGetCount(services);
 
                for (i = 0; i < count; i++) {
                        SCNetworkServiceRef     service;
-                       CFStringRef             serviceID;
-                       CFStringRef             serviceName;
 
                        service = CFArrayGetValueAtIndex(services, i);
-                       serviceID = SCNetworkServiceGetServiceID(service);
-                       serviceName = SCNetworkServiceGetName(service);
-                       if (serviceName != NULL) {
-                               CFDictionarySetValue(names, serviceID, serviceName);
-                       }
+                       nc_print_VPN_service(service);
+               }
+
+       }
+       my_CFRelease(&services);
+       exit(0);
+}
+
+/* -----------------------------------------------------------------------------
+ ----------------------------------------------------------------------------- */
+static Boolean
+nc_enable_vpntype(CFStringRef vpnType)
+{
+       Boolean                 is_enabled = FALSE;
+       Boolean                 success = FALSE;
+
+       if (vpnType == NULL) {
+               SCPrint(TRUE, stderr, CFSTR("No VPN type provided\n"));
+               goto done;
+       }
+
+       is_enabled = VPNConfigurationIsVPNTypeEnabled(vpnType);
+
+       if (is_enabled) {
+               SCPrint(TRUE, stdout, CFSTR("VPN is already enabled\n"));
+       } else {
+#if    !TARGET_OS_IPHONE
+               AuthorizationRef        authorization;
+
+               authorization = _prefs_AuthorizationCreate();
+               if ((authorization == NULL) ||
+                   !VPNConfigurationSetAuthorization(authorization)) {
+                       SCPrint(TRUE, stderr, CFSTR("VPNConfigurationSetAuthorization failed: %s\n"), SCErrorString(SCError()));
+                       goto done;
                }
+#endif // !TARGET_OS_IPHONE
+
+               if (!VPNConfigurationEnableVPNType(vpnType)) {
+                       SCPrint(TRUE, stderr, CFSTR("VPN could not be enabled: %s\n"), SCErrorString(SCError()));
+                       goto done;
+               }
+
+#if    !TARGET_OS_IPHONE
+               _prefs_AuthorizationFree(authorization);
+#endif // !TARGET_OS_IPHONE
 
-               CFRelease(services);
+               SCPrint(TRUE, stdout, CFSTR("VPN enabled\n"));
        }
+       success = TRUE;
+
+done:
+       return success;
+}
+
+/* Turns a service ID or name into a vendor type, or preserves type */
+static CFStringRef
+nc_copy_vendor_type (CFStringRef input)
+{
+       SCNetworkInterfaceRef   child;
+       SCNetworkInterfaceRef   interface;
+       CFStringRef             output_name     = input;
+       SCNetworkServiceRef     service         = NULL;
+       CFStringRef             type;
 
-       services = SCDynamicStoreCopyKeyList(store, key);
-       if (services == NULL ) {
-               SCPrint(TRUE, stderr, CFSTR("nc_list SCDynamicStoreCopyKeyList() failed: %s\n"), SCErrorString(SCError()));
+       if (input == NULL) {
                goto done;
        }
 
-       count = CFArrayGetCount(services);
-       for (i = 0; i < count; i++) {
-               CFStringRef serviceID;
+       service = nc_copy_service(NULL, input);
+       if (service != NULL) {
+               interface = SCNetworkServiceGetInterface(service);
+               child = SCNetworkInterfaceGetInterface(interface);
+               type = SCNetworkInterfaceGetInterfaceType(interface);
 
-               serviceID = parse_component(CFArrayGetValueAtIndex(services, i), setup);
-               if (serviceID) {
-                       CFStringRef     iftype;
-                       CFStringRef     ifsubtype;
-                       CFStringRef     interface_key   = NULL;
-                       CFDictionaryRef interface_dict  = NULL;
-                       CFStringRef     service_name;
+               /* Must be of type VPN */
+               if (!CFEqual(type, kSCNetworkInterfaceTypeVPN)) {
+                       output_name = NULL;
+                       goto done;
+               }
+               output_name = SCNetworkInterfaceGetInterfaceType(child);
+               goto done;
+       }
 
-                       interface_key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, serviceID, kSCEntNetInterface);
-                       if (!interface_key)  {
-                               SCPrint(TRUE, stderr, CFSTR("nc_list SCDynamicStoreKeyCreateNetworkServiceEntity() failed to interface key string\n"));
-                               goto endloop;
-                       }
+done :
+       if (output_name != NULL) CFRetain(output_name);
+       my_CFRelease(&service);
+       return output_name;
+}
 
-                       interface_dict = SCDynamicStoreCopyValue(store, interface_key);
-                       if (!interface_dict) {
-                               SCPrint(TRUE, stderr, CFSTR("nc_list SCDynamicStoreCopyValue() to copy interface dictionary: %s\n"), SCErrorString(SCError()));
-                               goto endloop;
-                       }
+/* -----------------------------------------------------------------------------
+ ----------------------------------------------------------------------------- */
+#if !TARGET_OS_IPHONE
+static const CFStringRef PREF_PREFIX                       = CFSTR("VPN-");
+static const CFStringRef PREF_SUFFIX                       = CFSTR(".plist");
+static void
+nc_set_application_url(CFStringRef subtype, CFStringRef directory)
+{
+       CFURLRef        directory_url           = NULL;
+       CFDataRef       directory_url_data      = NULL;
+       CFStringRef     vpnprefpath             = NULL;
+       char           *path                    = NULL;
+       CFIndex         path_len                = 0;
 
-                       iftype = CFDictionaryGetValue(interface_dict, kSCPropNetInterfaceType);
-                       if (!iftype) {
-                               // is that an error condition ???
-                               goto endloop;
-                       }
+       if (subtype == NULL || directory == NULL) {
+               goto done;
+       }
+
+       directory_url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
+                                                     directory,
+                                                     kCFURLPOSIXPathStyle,
+                                                     FALSE);
+       if (directory_url == NULL) {
+               SCPrint(TRUE, stderr, CFSTR("CFURLCreateWithFileSystemPath failed\n"));
+               goto done;
+       }
 
-                       if (!CFEqual(iftype, kSCEntNetPPP) &&
-                               !CFEqual(iftype, kSCEntNetIPSec) &&
-                               !CFEqual(iftype, kSCEntNetVPN))
-                               goto endloop;
+       directory_url_data = CFURLCreateBookmarkData(NULL, directory_url, 0, 0, 0, 0);
+       if (directory_url_data == NULL) {
+               SCPrint(TRUE, stderr, CFSTR("CFURLCreateBookmarkData failed\n"));
+               goto done;
+       }
+
+       vpnprefpath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@%@"), PREF_PREFIX, subtype, PREF_SUFFIX );
+       if (vpnprefpath == NULL) {
+               SCPrint(TRUE, stderr, CFSTR("CFStringCreateWithFormat failed\n"));
+               goto done;
+       }
+
+       path_len = CFStringGetLength(vpnprefpath) + 1;
+       path = malloc(path_len);
+       if (path == NULL) {
+               goto done;
+       }
+
+       if (!CFStringGetCString(vpnprefpath, path, path_len, kCFStringEncodingASCII)) {
+               SCPrint(TRUE, stderr, CFSTR("CFStringGetCString failed\n"));
+               goto done;
+       }
+
+       do_prefs_init();                /* initialization */
+       do_prefs_open(1, &path);        /* open prefs */
+
+       if (!SCPreferencesSetValue(prefs, CFSTR("ApplicationURL"), directory_url_data)) {
+               SCPrint(TRUE, stderr,
+                       CFSTR("SCPreferencesSetValue ApplicationURL failed, %s\n"),
+                       SCErrorString(SCError()));
+               goto done;
+       }
+
+       _prefs_save();
+
+done:
+       my_CFRelease(&directory_url);
+       my_CFRelease(&directory_url_data);
+       my_CFRelease(&vpnprefpath);
+       if (path) {
+               free(path);
+       }
+       _prefs_close();
 
-                       ifsubtype = CFDictionaryGetValue(interface_dict, kSCPropNetInterfaceSubType);
+       exit(0);
+}
+#endif
 
-                       service_name = CFDictionaryGetValue(names, serviceID);
+/* -----------------------------------------------------------------------------
+ ----------------------------------------------------------------------------- */
+static void
+nc_enablevpn(int argc, char **argv)
+{
+       CFStringRef             argument = NULL;
+       CFStringRef             vendorType = NULL;
+       int                     exit_code = 1;
 
-                       SCPrint(TRUE, stdout, CFSTR("[%@%@%@] %@%s%@\n"),
-                               iftype ? iftype : CFSTR("?"),
-                               ifsubtype ? CFSTR("/") : CFSTR(""),
-                               ifsubtype ? ifsubtype : CFSTR(""),
-                               serviceID,
-                               service_name ? " : " : "",
-                               service_name ? service_name : CFSTR(""));
+       if (argc == 0) {
+               SCPrint(TRUE, stderr, CFSTR("No service type or ID\n"));
+       } else {
+               argument = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
+               vendorType = nc_copy_vendor_type(argument);
+               my_CFRelease(&argument);
 
-                   endloop:
-                       my_CFRelease(&interface_key);
-                       my_CFRelease(&interface_dict);
-                       my_CFRelease(&serviceID);
+               if (!nc_enable_vpntype(vendorType)) {
+                       goto done;
                }
+#if !TARGET_OS_IPHONE
+               if (argc >= 2) {
+                       argument = CFStringCreateWithCString(NULL, argv[1], kCFStringEncodingUTF8);
+                       nc_set_application_url(vendorType, argument);
+                       my_CFRelease(&argument);
+               }
+#endif
        }
 
        exit_code = 0;
+
 done:
-       my_CFRelease(&services);
-       my_CFRelease(&names);
-       my_CFRelease(&setup);
-       my_CFRelease(&key);
-       my_CFRelease(&store);
+       my_CFRelease(&vendorType);
        exit(exit_code);
 }
 
+
+#if TARGET_OS_EMBEDDED
+static void
+nc_print_VPN_app_info(CFStringRef appInfo, CFDictionaryRef appInfoDict)
+{
+       CFStringRef appName = NULL;
+       Boolean isEnabled = FALSE;
+       CFStringRef paddedAppInfo = NULL;
+       CFStringRef paddedAppName = NULL;
+
+       if (appInfo == NULL) {
+               return;
+       }
+
+       isEnabled = VPNConfigurationIsVPNTypeEnabled(appInfo);
+
+       CFDictionaryGetValueIfPresent(appInfoDict, CFSTR("CFBundleDisplayName"), (const void **)&appName);
+       paddedAppName = copy_padded_string((appName == NULL) ? CFSTR("") : appName, 12, NULL, NULL);
+       paddedAppInfo = copy_padded_string(appInfo, 30, NULL, NULL);
+
+       SCPrint(TRUE, stdout, CFSTR("%@ %@ [%@]\n"),
+               isEnabled ? CFSTR("(Enabled) ") : CFSTR("(Disabled)"),
+               paddedAppName,
+               appInfo);
+
+       my_CFRelease(&paddedAppName);
+       my_CFRelease(&paddedAppInfo);
+}
+#endif
+
+/* -----------------------------------------------------------------------------
+ ----------------------------------------------------------------------------- */
+#if    TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
+static void
+nc_listvpn(int argc, char **argv)
+{
+
+       CFDictionaryRef         appDict = NULL;
+       CFArrayRef              appinfo = NULL;
+       int                     i, j, count, subtypecount;
+       const void * *          keys = NULL;
+       CFMutableDictionaryRef optionsDict = NULL;
+       const void * *          values = NULL;
+       CFStringRef             vpntype = NULL;
+
+       optionsDict = CFDictionaryCreateMutable(NULL, 0,
+                                               &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+       CFDictionarySetValue(optionsDict, kLookupApplicationTypeKey, kApplicationTypeUser);
+       CFDictionarySetValue(optionsDict, kLookupAttributeKey, CFSTR("UIVPNPlugin"));
+
+       appDict = MobileInstallationLookup(optionsDict);
+       if (!isA_CFDictionary(appDict))
+               goto done;
+
+       count = CFDictionaryGetCount(appDict);
+       if (count > 0) {
+               keys = (const void * *)malloc(sizeof(CFTypeRef) * count);
+               values = (const void * *)malloc(sizeof(CFTypeRef) * count);
+
+               CFDictionaryGetKeysAndValues(appDict, keys, values);
+               for (i=0; i<count; i++) {
+                       appinfo = CFDictionaryGetValue(values[i], CFSTR("UIVPNPlugin"));
+                       if (appinfo) {
+
+
+
+                               if (isA_CFString(appinfo)) {
+                                       nc_print_VPN_app_info((CFStringRef)appinfo, (CFDictionaryRef)values[i]);
+                               }
+                               else if (isA_CFArray(appinfo)) {
+                                       subtypecount = CFArrayGetCount((CFArrayRef)appinfo);
+                                       for(j=0; j<subtypecount; j++) {
+                                               vpntype = (CFStringRef)CFArrayGetValueAtIndex((CFArrayRef)appinfo, j);
+                                               nc_print_VPN_app_info(vpntype, (CFDictionaryRef)values[i]);
+                                       }
+                               }
+                       }
+               }
+       }
+done:
+       if (keys) free(keys);
+       if (values) free(values);
+       my_CFRelease(&optionsDict);
+       my_CFRelease(&appDict);
+
+       exit(0);
+}
+#endif // TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
+
 /* -----------------------------------------------------------------------------
 ----------------------------------------------------------------------------- */
 static void
 nc_show(int argc, char **argv)
 {
-       SCDynamicStoreRef       store = NULL;
-       int                     exit_code = 1;
-       CFStringRef             setup = NULL;
-       CFStringRef             serviceIDRef = NULL;
-       CFArrayRef              services = NULL;
-       CFStringRef             iftype = NULL;
-       CFStringRef             ifsubtype = NULL;
-       CFStringRef             interface_key = NULL;
-       CFDictionaryRef         interface_dict = NULL;
-       CFStringRef             type_entity_key = NULL;
-       CFStringRef             subtype_entity_key = NULL;
-       CFDictionaryRef         type_entity_dict = NULL;
-       CFDictionaryRef         subtype_entity_dict = NULL;
-
-       serviceIDRef = nc_copy_serviceID(argc, argv);
-       if (serviceIDRef == NULL) {
-               SCPrint(TRUE, stderr, CFSTR("No service ID\n"));
-               goto done;
+       SCNetworkServiceRef     service                 = NULL;
+       SCDynamicStoreRef       store                   = NULL;
+       int                     exit_code               = 1;
+       CFStringRef             serviceID               = NULL;
+       CFStringRef             iftype                  = NULL;
+       CFStringRef             ifsubtype               = NULL;
+       CFStringRef             type_entity_key         = NULL;
+       CFStringRef             subtype_entity_key      = NULL;
+       CFDictionaryRef         type_entity_dict        = NULL;
+       CFDictionaryRef         subtype_entity_dict     = NULL;
+       CFStringRef             vpnprefpath             = NULL;
+#if !TARGET_OS_IPHONE
+       CFDataRef               bookmarkData            = NULL;
+       CFURLRef                directory               = NULL;
+       Boolean                 isStale                 = FALSE;
+       char                    *path                   = NULL;
+       CFIndex                 path_len                = 0;
+#endif
+
+       service = nc_copy_service_from_arguments(argc, argv, NULL);
+       if (service == NULL) {
+               SCPrint(TRUE, stderr, CFSTR("No service\n"));
+               exit(exit_code);
        }
 
-       store = SCDynamicStoreCreate(NULL, CFSTR("scutil --nc"), NULL, NULL);
-       if (store == NULL) {
-               SCPrint(TRUE, stderr, CFSTR("nc_show SCDynamicStoreCreate() failed: %s\n"), SCErrorString(SCError()));
+       serviceID = SCNetworkServiceGetServiceID(service);
+
+       nc_get_service_type_and_subtype(service, &iftype, &ifsubtype);
+
+       if (!CFEqual(iftype, kSCEntNetPPP) &&
+           !CFEqual(iftype, kSCEntNetIPSec) &&
+           !CFEqual(iftype, kSCEntNetVPN)) {
+               SCPrint(TRUE, stderr, CFSTR("Not a connection oriented service: %@\n"), serviceID);
                goto done;
        }
 
-       interface_key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, serviceIDRef, kSCEntNetInterface);
-       if (!interface_key) {
-               SCPrint(TRUE, stderr, CFSTR("nc_show SCDynamicStoreKeyCreateNetworkServiceEntity() failed to create interface key\n"));
-               goto done;
+       type_entity_key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, serviceID, iftype);
+
+       nc_print_VPN_service(service);
+
+#if !TARGET_OS_IPHONE
+       vpnprefpath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@%@"), PREF_PREFIX, ifsubtype, PREF_SUFFIX);
+       if (vpnprefpath == NULL) {
+               goto skipURL;
        }
 
-       interface_dict = SCDynamicStoreCopyValue(store, interface_key);
-       if (!interface_dict) {
-               SCPrint(TRUE, stdout, CFSTR("Interface dictionary missing for service ID : %@\n"), serviceIDRef);
-               goto done;
+       path_len = CFStringGetLength(vpnprefpath) + 1;
+       path = malloc(path_len);
+       if (path == NULL) {
+               goto skipURL;
        }
 
-       iftype = CFDictionaryGetValue(interface_dict, kSCPropNetInterfaceType);
-       if (!iftype) {
-               SCPrint(TRUE, stdout, CFSTR("Interface Type missing for service ID : %@\n"), serviceIDRef);
+       if (!CFStringGetCString(vpnprefpath, path, path_len, kCFStringEncodingASCII)) {
+               SCPrint(TRUE, stderr, CFSTR("CFStringGetCString failed\n"));
                goto done;
        }
 
-       if (!CFEqual(iftype, kSCEntNetPPP) &&
-               !CFEqual(iftype, kSCEntNetIPSec) &&
-               !CFEqual(iftype, kSCEntNetVPN)) {
-               SCPrint(TRUE, stdout, CFSTR("Interface Type [%@] invalid for service ID : %@\n"), iftype, serviceIDRef);
-               goto done;
+       do_prefs_init();                /* initialization */
+       do_prefs_open(1, &path);        /* open prefs */
+
+       bookmarkData = SCPreferencesGetValue(prefs, CFSTR("ApplicationURL"));
+       if (bookmarkData == NULL) {
+               goto skipURL;
        }
 
-       ifsubtype = CFDictionaryGetValue(interface_dict, kSCPropNetInterfaceSubType);
-       SCPrint(TRUE, stdout, CFSTR("[%@%@%@] %@\n"),
-               iftype ? iftype : CFSTR("?"),
-               ifsubtype ? CFSTR("/") : CFSTR(""),
-               ifsubtype ? ifsubtype : CFSTR(""),
-               serviceIDRef);
+       directory = CFURLCreateByResolvingBookmarkData(kCFAllocatorDefault, bookmarkData, 0, NULL, NULL, &isStale, NULL);
+       if (directory == NULL) {
+               goto skipURL;
+       }
+
+       SCPrint(TRUE, stdout, CFSTR("ApplicationURL: %@\n"), directory);
+skipURL:
+#endif
 
-       type_entity_key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, serviceIDRef, iftype);
-       if (!type_entity_key) {
-               SCPrint(TRUE, stderr, CFSTR("nc_show SCDynamicStoreKeyCreateNetworkServiceEntity() failed to create type entity key\n"));
+       store = SCDynamicStoreCreate(NULL, CFSTR("scutil --nc"), NULL, NULL);
+       if (store == NULL) {
+               SCPrint(TRUE, stderr, CFSTR("Unable to create dynamic store: %s\n"), SCErrorString(SCError()));
                goto done;
        }
        type_entity_dict = SCDynamicStoreCopyValue(store, type_entity_key);
+
        if (!type_entity_dict) {
-               SCPrint(TRUE, stdout, CFSTR("%@ dictionary missing for service ID : %@\n"), iftype, serviceIDRef);
+               SCPrint(TRUE, stderr, CFSTR("No \"%@\" configuration available\n"), iftype);
        } else {
                SCPrint(TRUE, stdout, CFSTR("%@ %@\n"), iftype, type_entity_dict);
        }
 
        if (ifsubtype) {
-               subtype_entity_key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, serviceIDRef, ifsubtype);
-               if (!subtype_entity_key) {
-                       SCPrint(TRUE, stderr, CFSTR("nc_show SCDynamicStoreKeyCreateNetworkServiceEntity() failed to create subtype entity key\n"));
-                       goto done;
-               }
+               subtype_entity_key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, serviceID, ifsubtype);
                subtype_entity_dict = SCDynamicStoreCopyValue(store, subtype_entity_key);
                if (!subtype_entity_dict) {
                        //
@@ -641,17 +1176,14 @@ nc_show(int argc, char **argv)
        exit_code = 0;
 
 done:
-       my_CFRelease(&serviceIDRef);
-       my_CFRelease(&interface_key);
-       my_CFRelease(&interface_dict);
        my_CFRelease(&type_entity_key);
        my_CFRelease(&type_entity_dict);
        my_CFRelease(&subtype_entity_key);
        my_CFRelease(&subtype_entity_dict);
-       my_CFRelease(&services);
-       my_CFRelease(&setup);
        my_CFRelease(&store);
-
+       my_CFRelease(&service);
+       my_CFRelease(&vpnprefpath);
+       _prefs_close();
        exit(exit_code);
 }
 
@@ -663,40 +1195,33 @@ nc_select(int argc, char **argv)
        SCNetworkSetRef         current_set;
        int                     exit_code       = 1;
        SCNetworkServiceRef     service         = NULL;
-       CFStringRef             service_id;
        Boolean                 status;
 
-       service_id = nc_copy_serviceID(argc, argv);
-       if (service_id == NULL) {
-               SCPrint(TRUE, stderr, CFSTR("No service identifier\n"));
-               exit(exit_code);
-       }
-
        do_prefs_init();        /* initialization */
        do_prefs_open(0, NULL); /* open default prefs */
 
        current_set = SCNetworkSetCopyCurrent(prefs);
        if (current_set == NULL) {
-               SCPrint(TRUE, stdout, CFSTR("nc_select SCNetworkSetCopyCurrent() failed: %s\n"), SCErrorString(SCError()));
+               SCPrint(TRUE, stderr, CFSTR("No current location\n"), SCErrorString(SCError()));
                goto done;
        }
 
-       service = nc_copy_service(current_set, service_id);
+       service = nc_copy_service_from_arguments(argc, argv, current_set);
        if (service == NULL) {
-               SCPrint(TRUE, stdout, CFSTR("No service\n"));
+               SCPrint(TRUE, stderr, CFSTR("No service\n"));
                goto done;
        }
 
 #if !TARGET_OS_IPHONE
        status = SCNetworkServiceSetEnabled(service, TRUE);
        if (!status) {
-               SCPrint(TRUE, stdout, CFSTR("nc_select SCNetworkServiceSetEnabled() failed: %s\n"), SCErrorString(SCError()));
+               SCPrint(TRUE, stderr, CFSTR("Unable to enable service: %s\n"), SCErrorString(SCError()));
                goto done;
        }
 #else
        status = SCNetworkSetSetSelectedVPNService(current_set, service);
        if (!status) {
-               SCPrint(TRUE, stdout, CFSTR("nc_select SCNetworkSetSetSelectedVPNService() failed: %s\n"), SCErrorString(SCError()));
+               SCPrint(TRUE, stderr, CFSTR("Unable to select service: %s\n"), SCErrorString(SCError()));
                goto done;
        }
 #endif
@@ -704,13 +1229,77 @@ nc_select(int argc, char **argv)
        _prefs_save();
        exit_code = 0;
 done:
-
-       my_CFRelease(&service_id);
+       my_CFRelease(&service);
        my_CFRelease(&current_set);
        _prefs_close();
        exit(exit_code);
 }
 
+/* -----------------------------------------------------------------------------
+ ----------------------------------------------------------------------------- */
+static void
+nc_help(int argc, char **argv)
+{
+       SCPrint(TRUE, stderr, CFSTR("Valid commands for scutil --nc (VPN connections)\n"));
+       SCPrint(TRUE, stderr, CFSTR("Usage: scutil --nc [command]\n"));
+       SCPrint(TRUE, stderr, CFSTR("\n"));
+       SCPrint(TRUE, stderr, CFSTR("\tlist\n"));
+       SCPrint(TRUE, stderr, CFSTR("\t\tList available network connection services in the current set\n"));
+       SCPrint(TRUE, stderr, CFSTR("\n"));
+       SCPrint(TRUE, stderr, CFSTR("\tstatus <service>\n"));
+       SCPrint(TRUE, stderr, CFSTR("\t\tIndicate whether a given service is connected, as well as extended status information for the service\n"));
+       SCPrint(TRUE, stderr, CFSTR("\n"));
+       SCPrint(TRUE, stderr, CFSTR("\tshow <service>\n"));
+       SCPrint(TRUE, stderr, CFSTR("\t\tDisplay configuration information for a given service\n"));
+       SCPrint(TRUE, stderr, CFSTR("\n"));
+       SCPrint(TRUE, stderr, CFSTR("\tstatistics <service>\n"));
+       SCPrint(TRUE, stderr, CFSTR("\t\tProvide statistics on bytes, packets, and errors for a given service\n"));
+       SCPrint(TRUE, stderr, CFSTR("\n"));
+       SCPrint(TRUE, stderr, CFSTR("\tselect <service>\n"));
+       SCPrint(TRUE, stderr, CFSTR("\t\tMake the given service active in the current set. This allows it to be started\n"));
+       SCPrint(TRUE, stderr, CFSTR("\n"));
+       SCPrint(TRUE, stderr, CFSTR("\tstart <service> [--user user] [--password password] [--secret secret]\n"));
+       SCPrint(TRUE, stderr, CFSTR("\t\tStart a given service. Can take optional arguments for user, password, and secret\n"));
+       SCPrint(TRUE, stderr, CFSTR("\n"));
+       SCPrint(TRUE, stderr, CFSTR("\tstop <service>\n"));
+       SCPrint(TRUE, stderr, CFSTR("\t\tStop a given service\n"));
+       SCPrint(TRUE, stderr, CFSTR("\n"));
+       SCPrint(TRUE, stderr, CFSTR("\tsuspend <service>\n"));
+       SCPrint(TRUE, stderr, CFSTR("\t\tSuspend a given service (PPP, Modem on Hold)\n"));
+       SCPrint(TRUE, stderr, CFSTR("\n"));
+       SCPrint(TRUE, stderr, CFSTR("\tresume <service>\n"));
+       SCPrint(TRUE, stderr, CFSTR("\t\tResume a given service (PPP, Modem on Hold)\n"));
+       SCPrint(TRUE, stderr, CFSTR("\n"));
+       SCPrint(TRUE, stderr, CFSTR("\tondemand [-W] [hostname]\n"));
+       SCPrint(TRUE, stderr, CFSTR("\tondemand -- --refresh\n"));
+       SCPrint(TRUE, stderr, CFSTR("\t\tDisplay VPN on-demand information\n"));
+       SCPrint(TRUE, stderr, CFSTR("\n"));
+       SCPrint(TRUE, stderr, CFSTR("\ttrigger <hostname> [background] [port]\n"));
+       SCPrint(TRUE, stderr, CFSTR("\t\tTrigger VPN on-demand with specified hostname, and optional port and background flag\n"));
+       SCPrint(TRUE, stderr, CFSTR("\n"));
+#if TARGET_OS_EMBEDDED
+       SCPrint(TRUE, stderr, CFSTR("\tlistvpn\n"));
+       SCPrint(TRUE, stderr, CFSTR("\t\tDisplay the installed VPN applications\n"));
+       SCPrint(TRUE, stderr, CFSTR("\n"));
+#endif
+#if !TARGET_OS_IPHONE
+       SCPrint(TRUE, stderr, CFSTR("\tenablevpn <service or vpn type> [path]\n"));
+       SCPrint(TRUE, stderr, CFSTR("\t\tEnables the given VPN application type. Takes either a service or VPN type. Pass a path to set ApplicationURL\n"));
+       SCPrint(TRUE, stderr, CFSTR("\n"));
+#else
+       SCPrint(TRUE, stderr, CFSTR("\tenablevpn <service or vpn type>\n"));
+       SCPrint(TRUE, stderr, CFSTR("\t\tEnables the given VPN application type. Takes either a service or VPN type\n"));
+       SCPrint(TRUE, stderr, CFSTR("\n"));
+#endif
+       SCPrint(TRUE, stderr, CFSTR("\tdisablevpn <service or vpn type>\n"));
+       SCPrint(TRUE, stderr, CFSTR("\t\tDisables the given VPN application type. Takes either a service or VPN type\n"));
+       SCPrint(TRUE, stderr, CFSTR("\n"));
+       SCPrint(TRUE, stderr, CFSTR("\thelp\n"));
+       SCPrint(TRUE, stderr, CFSTR("\t\tDisplay available commands for --nc\n"));
+       SCPrint(TRUE, stderr, CFSTR("\n"));
+       exit(0);
+}
+
 /* -----------------------------------------------------------------------------
 ----------------------------------------------------------------------------- */
 typedef void (*nc_func) (int argc, char **argv);
@@ -719,7 +1308,12 @@ static const struct {
        char            *cmd;
        nc_func         func;
 } nc_cmds[] = {
+       { "enablevpn",          nc_enablevpn    },
+       { "help",               nc_help         },
        { "list",               nc_list         },
+#if    TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
+       { "listvpn",            nc_listvpn      },
+#endif // TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
        { "ondemand",           nc_ondemand     },
        { "resume",             nc_resume       },
        { "select",             nc_select       },
@@ -729,6 +1323,7 @@ static const struct {
        { "status",             nc_status       },
        { "stop",               nc_stop         },
        { "suspend",            nc_suspend      },
+       { "trigger",            nc_trigger      },
 };
 #define        N_NC_CMNDS      (sizeof(nc_cmds) / sizeof(nc_cmds[0]))
 
@@ -762,8 +1357,12 @@ do_nc_cmd(char *cmd, int argc, char **argv, Boolean watch)
                nc_func func;
 
                func = nc_cmds[i].func;
-               if (watch && (func == nc_status)) {
-                       func = nc_watch;
+               if (watch) {
+                       if (func == nc_status) {
+                               func = nc_watch;
+                       } else if (func == nc_ondemand) {
+                               ondemandwatch = TRUE;
+                       }
                }
                (*func)(argc, argv);
        }