1/*
2 * Copyright (c) 2010-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * Modification History
26 *
27 * March 1, 2010			Christophe Allie <callie@apple.com>
28 * - initial revision
29 * February 8, 2011			Kevin Wells <kcw@apple.com>
30 * - added "select" command
31 * January 2012				Kevin Wells <kcw@apple.com>
32 * - added arguments to "start" command to pass authentication credentials
33 * - "show" now takes a service name as an alternative to a service ID
34 * - fixes a bug whereby "IPv4" was being displayed as a subtype to IPsec services
35 * - improved format of "list" output
36 * - general cleanup of error messages and some variable names
37 */
38
39
40#include "scutil.h"
41#include "nc.h"
42#include "prefs.h"
43
44#include <SystemConfiguration/VPNConfiguration.h>
45
46#if	TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
47#include <MobileInstallation/MobileInstallation.h>
48#endif	// TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
49
50#include <sys/time.h>
51
52CFStringRef			username	= NULL;
53CFStringRef			password	= NULL;
54CFStringRef			sharedsecret	= NULL;
55
56static	Boolean			ondemandwatch	= FALSE;
57static	CFStringRef		ondemand_nodename = NULL;
58
59static	SCNetworkConnectionRef	connection	= NULL;
60static	int			n_callback	= 0;
61
62
63/* -----------------------------------------------------------------------------
64----------------------------------------------------------------------------- */
65static void
66my_CFRelease(void *t)
67{
68	void * * obj = (void * *)t;
69	if (obj && *obj) {
70		CFRelease(*obj);
71		*obj = NULL;
72	}
73	return;
74}
75
76/* -----------------------------------------------------------------------------
77 ----------------------------------------------------------------------------- */
78static void
79nc_get_service_type_and_subtype(SCNetworkServiceRef service, CFStringRef *iftype, CFStringRef *ifsubtype) {
80	SCNetworkInterfaceRef interface = SCNetworkServiceGetInterface(service);
81	SCNetworkInterfaceRef child = SCNetworkInterfaceGetInterface(interface);
82
83	*iftype = SCNetworkInterfaceGetInterfaceType(interface);
84	*ifsubtype = NULL;
85	if (CFEqual(*iftype, kSCNetworkInterfaceTypePPP) ||
86	    CFEqual(*iftype, kSCNetworkInterfaceTypeVPN)) {
87	    *ifsubtype = (child != NULL) ? SCNetworkInterfaceGetInterfaceType(child) : NULL;
88	}
89}
90
91/* -----------------------------------------------------------------------------
92 ----------------------------------------------------------------------------- */
93static SCNetworkServiceRef
94nc_copy_service(SCNetworkSetRef set, CFStringRef identifier)
95{
96	CFIndex			i;
97	CFIndex			n;
98	SCNetworkServiceRef	selected	= NULL;
99	CFArrayRef		services;
100
101	services = SCNetworkConnectionCopyAvailableServices(set);
102	if (services == NULL) {
103		goto done;
104	}
105
106	n = CFArrayGetCount(services);
107
108	// try to select the service by its serviceID
109	for (i = 0; i < n; i++) {
110		SCNetworkServiceRef	service		= NULL;
111		CFStringRef		serviceID;
112
113		service = CFArrayGetValueAtIndex(services, i);
114		serviceID = SCNetworkServiceGetServiceID(service);
115		if (CFEqual(identifier, serviceID)) {
116			selected = service;
117			goto done;
118		}
119	}
120
121	// try to select the service by service name
122	for (i = 0; i < n; i++) {
123		SCNetworkServiceRef	service		= NULL;
124		CFStringRef		serviceName;
125
126		service = CFArrayGetValueAtIndex(services, i);
127		serviceName = SCNetworkServiceGetName(service);
128		if ((serviceName != NULL) && CFEqual(identifier, serviceName)) {
129			if (selected == NULL) {
130				selected = service;
131			} else {
132				// if multiple services match
133				selected = NULL;
134				SCPrint(TRUE, stderr, CFSTR("Multiple services match\n"));
135				goto done;
136			}
137		}
138	}
139
140done :
141
142	if (selected != NULL) CFRetain(selected);
143	if (services != NULL) CFRelease(services);
144	return selected;
145}
146
147/* -----------------------------------------------------------------------------
148 ----------------------------------------------------------------------------- */
149static SCNetworkServiceRef
150nc_copy_service_from_arguments(int argc, char **argv, SCNetworkSetRef set) {
151	CFStringRef		serviceID	= NULL;
152	SCNetworkServiceRef	service		= NULL;
153
154	if (argc == 0) {
155		serviceID = _copyStringFromSTDIN(CFSTR("Service"), NULL);
156	} else {
157		serviceID = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
158	}
159	if (serviceID == NULL) {
160		SCPrint(TRUE, stderr, CFSTR("No service ID specified\n"));
161		return NULL;
162	}
163	service = nc_copy_service(set, serviceID);
164	my_CFRelease(&serviceID);
165	return service;
166}
167
168
169/* -----------------------------------------------------------------------------
170----------------------------------------------------------------------------- */
171static char *
172nc_status_string(SCNetworkConnectionStatus status)
173{
174	switch (status) {
175		case kSCNetworkConnectionInvalid:
176			return "Invalid";
177		case kSCNetworkConnectionDisconnected:
178			return "Disconnected";
179		case kSCNetworkConnectionConnecting:
180			return "Connecting";
181		case kSCNetworkConnectionConnected:
182			return "Connected";
183		case kSCNetworkConnectionDisconnecting:
184			return "Disconnecting";
185	}
186	return "Unknown";
187}
188
189static void
190nc_callback(SCNetworkConnectionRef connection, SCNetworkConnectionStatus status, void *info)
191{
192	int		*n		= (int *)info;
193	CFDictionaryRef	status_dict;
194
195	// report status
196	if (n != NULL) {
197		if (*n == 0) {
198			SCPrint(TRUE, stdout, CFSTR("Current status = "));
199		} else {
200			struct tm	tm_now;
201			struct timeval	tv_now;
202
203			(void)gettimeofday(&tv_now, NULL);
204			(void)localtime_r(&tv_now.tv_sec, &tm_now);
205
206			SCPrint(TRUE, stdout, CFSTR("\n*** %2d:%02d:%02d.%03d\n\n"),
207				tm_now.tm_hour,
208				tm_now.tm_min,
209				tm_now.tm_sec,
210				tv_now.tv_usec / 1000);
211			SCPrint(TRUE, stdout, CFSTR("Callback (%d) status = "), *n);
212		}
213		*n = *n + 1;
214	}
215	SCPrint(TRUE, stdout, CFSTR("%s%s%s\n"),
216		nc_status_string(status),
217		(status == kSCNetworkConnectionInvalid) ? ": "                     : "",
218		(status == kSCNetworkConnectionInvalid) ? SCErrorString(SCError()) : "");
219
220	// report extended status
221	status_dict = SCNetworkConnectionCopyExtendedStatus(connection);
222	if (status_dict) {
223		SCPrint(TRUE, stdout, CFSTR("Extended Status %@\n"), status_dict);
224		CFRelease(status_dict);
225	}
226
227	return;
228}
229
230static void
231nc_create_connection(int argc, char **argv, Boolean exit_on_failure)
232{
233	SCNetworkConnectionContext	context	= { 0, &n_callback, NULL, NULL, NULL };
234	SCNetworkServiceRef		service;
235
236	service = nc_copy_service_from_arguments(argc, argv, NULL);
237	if (service == NULL) {
238		SCPrint(TRUE, stderr, CFSTR("No service\n"));
239		if (exit_on_failure)
240			exit(1);
241		return;
242	}
243
244	connection = SCNetworkConnectionCreateWithService(NULL, service, nc_callback, &context);
245	CFRelease(service);
246	if (connection == NULL) {
247		SCPrint(TRUE, stderr, CFSTR("Could not create connection: %s\n"), SCErrorString(SCError()));
248		if (exit_on_failure)
249			exit(1);
250		return;
251	}
252}
253
254/* -----------------------------------------------------------------------------
255 ----------------------------------------------------------------------------- */
256
257static void
258nc_trigger(int argc, char **argv)
259{
260	Boolean		background	= FALSE;
261	int		i;
262	CFStringRef	hostName	= NULL;
263	int		port		= 80;
264
265	for (i = 0; i < 3 && i < argc; i++) {
266		/* Parse host name. Must be first arg. */
267		if (i == 0) {
268			hostName = CFStringCreateWithCString(NULL, argv[i], kCFStringEncodingUTF8);
269			continue;
270		}
271
272		/* Check for optional background flag */
273		if (strcmp(argv[i], "background") == 0) {
274			background = TRUE;
275			continue;
276		}
277
278		/* Parse optional port number */
279		CFStringRef str = CFStringCreateWithCString(NULL, argv[i], kCFStringEncodingUTF8);
280		if (str) {
281			int num = CFStringGetIntValue(str);
282			if (num) {
283				port = num;
284			}
285			my_CFRelease(&str);
286		}
287	}
288
289	if (hostName) {
290		CFReadStreamRef		readStream	= NULL;
291		CFWriteStreamRef	writeStream	= NULL;
292
293		CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, hostName, port, &readStream, &writeStream);
294
295		if (background) {
296			CFReadStreamSetProperty(readStream, CFSTR("kCFStreamNetworkServiceType"), CFSTR("kCFStreamNetworkServiceTypeBackground"));
297			CFWriteStreamSetProperty(writeStream, CFSTR("kCFStreamNetworkServiceType"), CFSTR("kCFStreamNetworkServiceTypeBackground"));
298		}
299
300		if (readStream && writeStream) {
301			CFReadStreamOpen(readStream);
302			CFWriteStreamOpen(writeStream);
303			SCPrint(TRUE, stdout, CFSTR("Opened stream to %@, port %d%s\n"), hostName, port, background ? ", background traffic class" : "");
304			sleep(1);
305		}
306
307		my_CFRelease(&readStream);
308		my_CFRelease(&writeStream);
309	} else {
310		SCPrint(TRUE, stderr, CFSTR("Invalid or missing host name\n"));
311	}
312
313	my_CFRelease(&hostName);
314
315	exit(0);
316}
317
318/* -----------------------------------------------------------------------------
319----------------------------------------------------------------------------- */
320static void
321nc_release_connection()
322{
323	my_CFRelease(&connection);
324}
325
326/* -----------------------------------------------------------------------------
327----------------------------------------------------------------------------- */
328static void
329nc_start(int argc, char **argv)
330{
331	CFMutableDictionaryRef		userOptions = NULL;
332	CFStringRef			iftype = NULL;
333	CFStringRef			ifsubtype = NULL;
334	SCNetworkServiceRef		service = NULL;
335
336	nc_create_connection(argc, argv, TRUE);
337
338	service = SCNetworkConnectionGetService(connection);
339	nc_get_service_type_and_subtype(service, &iftype, &ifsubtype);
340
341	userOptions = CFDictionaryCreateMutable(NULL, 0,
342						&kCFTypeDictionaryKeyCallBacks,
343						&kCFTypeDictionaryValueCallBacks);
344
345	Boolean isL2TP = (CFEqual(iftype, kSCEntNetPPP) &&
346			  (ifsubtype != NULL) && CFEqual(ifsubtype, kSCValNetInterfaceSubTypeL2TP));
347
348	if (CFEqual(iftype, kSCEntNetPPP)) {
349		CFMutableDictionaryRef pppEntity  = CFDictionaryCreateMutable(NULL, 0,
350									   &kCFTypeDictionaryKeyCallBacks,
351									   &kCFTypeDictionaryValueCallBacks);
352
353		if (username != NULL) {
354			CFDictionarySetValue(pppEntity, kSCPropNetPPPAuthName, username);
355		}
356		if (password != NULL) {
357			CFDictionarySetValue(pppEntity, kSCPropNetPPPAuthPassword, password);
358		}
359		CFDictionarySetValue(userOptions, kSCEntNetPPP, pppEntity);
360		my_CFRelease(&pppEntity);
361	}
362	if (CFEqual(iftype, kSCEntNetIPSec) || isL2TP) {
363		CFMutableDictionaryRef ipsecEntity  = CFDictionaryCreateMutable(NULL, 0,
364									   &kCFTypeDictionaryKeyCallBacks,
365									   &kCFTypeDictionaryValueCallBacks);
366		if (!isL2TP) {
367			if (username != NULL) {
368				CFDictionarySetValue(ipsecEntity, kSCPropNetIPSecXAuthName, username);
369			}
370			if (password != NULL) {
371				CFDictionarySetValue(ipsecEntity, kSCPropNetIPSecXAuthPassword, password);
372			}
373		}
374		if (sharedsecret != NULL) {
375			CFDictionarySetValue(ipsecEntity, kSCPropNetIPSecSharedSecret, sharedsecret);
376		}
377		CFDictionarySetValue(userOptions, kSCEntNetIPSec, ipsecEntity);
378		my_CFRelease(&ipsecEntity);
379	}
380	if (CFEqual(iftype, kSCEntNetVPN)) {
381		CFMutableDictionaryRef vpnEntity  = CFDictionaryCreateMutable(NULL, 0,
382									   &kCFTypeDictionaryKeyCallBacks,
383									   &kCFTypeDictionaryValueCallBacks);
384		if (username != NULL) {
385			CFDictionarySetValue(vpnEntity, kSCPropNetVPNAuthName, username);
386		}
387		if (password != NULL) {
388			CFDictionarySetValue(vpnEntity, kSCPropNetVPNAuthPassword, password);
389		}
390		CFDictionarySetValue(userOptions, kSCEntNetVPN, vpnEntity);
391		my_CFRelease(&vpnEntity);
392	}
393	// If it doesn't match any VPN type, fail silently
394
395	if (!SCNetworkConnectionStart(connection, userOptions, TRUE)) {
396		SCPrint(TRUE, stderr, CFSTR("Could not start connection: %s\n"), SCErrorString(SCError()));
397		exit(1);
398	};
399
400	CFRelease(userOptions);
401	nc_release_connection();
402	exit(0);
403}
404
405/* -----------------------------------------------------------------------------
406----------------------------------------------------------------------------- */
407static void
408nc_stop(int argc, char **argv)
409{
410	nc_create_connection(argc, argv, TRUE);
411
412	if (!SCNetworkConnectionStop(connection, TRUE)) {
413		SCPrint(TRUE, stderr, CFSTR("Could not stop connection: %s\n"), SCErrorString(SCError()));
414		exit(1);
415	};
416
417	nc_release_connection();
418	exit(0);
419}
420
421/* -----------------------------------------------------------------------------
422 ----------------------------------------------------------------------------- */
423static void
424nc_suspend(int argc, char **argv)
425{
426	nc_create_connection(argc, argv, TRUE);
427
428	SCNetworkConnectionSuspend(connection);
429
430	nc_release_connection();
431	exit(0);
432}
433
434/* -----------------------------------------------------------------------------
435 ----------------------------------------------------------------------------- */
436static void
437nc_resume(int argc, char **argv)
438{
439	nc_create_connection(argc, argv, TRUE);
440
441	SCNetworkConnectionResume(connection);
442
443	nc_release_connection();
444	exit(0);
445}
446
447/* -----------------------------------------------------------------------------
448----------------------------------------------------------------------------- */
449static void
450nc_status(int argc, char **argv)
451{
452	SCNetworkConnectionStatus	status;
453
454	nc_create_connection(argc, argv, TRUE);
455
456	status = SCNetworkConnectionGetStatus(connection);
457	nc_callback(connection, status, NULL);
458
459	nc_release_connection();
460	exit(0);
461}
462
463static void
464nc_watch(int argc, char **argv)
465{
466	SCNetworkConnectionStatus	status;
467
468	nc_create_connection(argc, argv, TRUE);
469
470	status = SCNetworkConnectionGetStatus(connection);
471
472	// report initial status
473	n_callback = 0;
474	nc_callback(connection, status, &n_callback);
475
476	// setup watcher
477	if (doDispatch) {
478		if (!SCNetworkConnectionSetDispatchQueue(connection, dispatch_get_main_queue())) {
479			SCPrint(TRUE, stderr, CFSTR("Unable to schedule watch process: %s\n"), SCErrorString(SCError()));
480			exit(1);
481		}
482	} else {
483		if (!SCNetworkConnectionScheduleWithRunLoop(connection, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
484			SCPrint(TRUE, stderr, CFSTR("Unable to schedule watch process: %s\n"), SCErrorString(SCError()));
485			exit(1);
486		}
487	}
488
489	// wait for changes
490	CFRunLoopRun();
491
492	nc_release_connection();
493	exit(0);
494}
495
496/* -----------------------------------------------------------------------------
497----------------------------------------------------------------------------- */
498static void
499nc_statistics(int argc, char **argv)
500{
501	CFDictionaryRef stats_dict;
502
503	nc_create_connection(argc, argv, TRUE);
504
505	stats_dict = SCNetworkConnectionCopyStatistics(connection);
506
507	if (stats_dict) {
508		SCPrint(TRUE, stdout, CFSTR("%@\n"), stats_dict);
509	} else {
510		SCPrint(TRUE, stdout, CFSTR("No statistics available\n"));
511	}
512
513	my_CFRelease(&stats_dict);
514
515	nc_release_connection();
516	exit(0);
517}
518
519/* -----------------------------------------------------------------------------
520----------------------------------------------------------------------------- */
521static void
522checkOnDemandHost(SCDynamicStoreRef store, CFStringRef nodeName, Boolean retry)
523{
524	Boolean				ok;
525	CFStringRef			connectionServiceID	= NULL;
526	SCNetworkConnectionStatus	connectionStatus	= 0;
527	CFStringRef			vpnRemoteAddress	= NULL;
528
529	SCPrint(TRUE, stdout, CFSTR("OnDemand host/domain check (%sretry)\n"), retry ? "" : "no ");
530
531	ok = __SCNetworkConnectionCopyOnDemandInfoWithName(&store,
532							   nodeName,
533							   retry,
534							   &connectionServiceID,
535							   &connectionStatus,
536							   &vpnRemoteAddress);
537
538	if (ok) {
539		SCPrint(TRUE, stdout, CFSTR("  serviceID      = %@\n"), connectionServiceID);
540		SCPrint(TRUE, stdout, CFSTR("  remote address = %@\n"), vpnRemoteAddress);
541	} else if (SCError() != kSCStatusOK) {
542		SCPrint(TRUE, stdout, CFSTR("%sretry\n"), retry ? "" : "no ");
543		SCPrint(TRUE, stdout,
544			CFSTR("  Unable to copy OnDemand information for connection: %s\n"),
545			SCErrorString(SCError()));
546	} else {
547		SCPrint(TRUE, stdout, CFSTR("  no match\n"));
548	}
549
550	if (connectionServiceID != NULL) {
551		CFRelease(connectionServiceID);
552		connectionServiceID = NULL;
553	}
554	if (vpnRemoteAddress != NULL) {
555		CFRelease(vpnRemoteAddress);
556		vpnRemoteAddress = NULL;
557	}
558
559	return;
560}
561
562static void
563nc_ondemand_callback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
564{
565	CFStringRef		key		= NULL;
566	CFDictionaryRef		ondemand_dict	= NULL;
567	struct tm		tm_now;
568	struct timeval		tv_now;
569
570	if (CFArrayGetCount(changedKeys) < 1) {
571		return;
572	}
573
574	(void)gettimeofday(&tv_now, NULL);
575	(void)localtime_r(&tv_now.tv_sec, &tm_now);
576
577	SCPrint(TRUE, stdout, CFSTR("\n*** %2d:%02d:%02d.%03d\n\n"),
578		tm_now.tm_hour,
579		tm_now.tm_min,
580		tm_now.tm_sec,
581		tv_now.tv_usec / 1000);
582
583	if (ondemand_nodename) {
584		checkOnDemandHost(store, ondemand_nodename, FALSE);
585		checkOnDemandHost(store, ondemand_nodename, TRUE);
586	} else {
587		key = CFArrayGetValueAtIndex(changedKeys, 0);
588
589		ondemand_dict = SCDynamicStoreCopyValue(store, key);
590		if (ondemand_dict) {
591			SCPrint(TRUE, stdout, CFSTR("%@ %@\n"), kSCEntNetOnDemand, ondemand_dict);
592		} else {
593			SCPrint(TRUE, stdout, CFSTR("%@ not configured\n"), kSCEntNetOnDemand);
594		}
595
596		my_CFRelease(&ondemand_dict);
597	}
598}
599
600static void
601nc_ondemand(int argc, char **argv)
602{
603	int			exit_code	= 1;
604	CFStringRef		key		= NULL;
605	CFDictionaryRef		ondemand_dict	= NULL;
606	SCDynamicStoreRef	store;
607
608	store = SCDynamicStoreCreate(NULL, CFSTR("scutil --nc"), nc_ondemand_callback, NULL);
609	if (store == NULL) {
610		SCPrint(TRUE, stderr, CFSTR("Unable to create dynamic store: %s\n"), SCErrorString(SCError()));
611		goto done;
612	}
613
614	if (argc == 1) {
615#if	!TARGET_IPHONE_SIMULATOR
616		if (strcmp("--refresh", argv[0]) == 0) {
617			SCNetworkConnectionRef	connection	= NULL;
618
619			connection = SCNetworkConnectionCreate(kCFAllocatorDefault, NULL, NULL);
620			if (connection && SCNetworkConnectionRefreshOnDemandState(connection)) {
621				exit_code = 0;
622			}
623
624			if (exit_code) {
625				SCPrint(TRUE, stderr, CFSTR("Unable to refresh OnDemand state: %s\n"), SCErrorString(SCError()));
626			}
627
628			my_CFRelease(&connection);
629			goto done;
630		}
631#endif	// !TARGET_IPHONE_SIMULATOR
632
633		ondemand_nodename = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
634	} else if (argc != 0) {
635		SCPrint(TRUE, stderr, CFSTR("Usage: scutil --nc ondemand [-W] [hostname]\n"
636					    "       scutil --nc ondemand -- --refresh\n"));
637		goto done;
638	}
639
640	key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetOnDemand);
641
642	if (ondemand_nodename) {
643		checkOnDemandHost(store, ondemand_nodename, FALSE);
644		checkOnDemandHost(store, ondemand_nodename, TRUE);
645	} else {
646		ondemand_dict = SCDynamicStoreCopyValue(store, key);
647		if (ondemand_dict) {
648			SCPrint(TRUE, stdout, CFSTR("%@ %@\n"), kSCEntNetOnDemand, ondemand_dict);
649		} else {
650			SCPrint(TRUE, stdout, CFSTR("%@ not configured\n"), kSCEntNetOnDemand);
651		}
652	}
653
654	if (ondemandwatch) {
655		CFMutableArrayRef	keys	= NULL;
656
657		keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
658		CFArrayAppendValue(keys, key);
659		SCDynamicStoreSetNotificationKeys(store, keys, NULL);
660
661		my_CFRelease(&keys);
662
663		SCDynamicStoreSetDispatchQueue(store, dispatch_get_main_queue());
664
665		CFRunLoopRun();
666	}
667
668	exit_code = 0;
669done:
670	my_CFRelease(&ondemand_dict);
671	my_CFRelease(&key);
672	my_CFRelease(&store);
673	my_CFRelease(&ondemand_nodename);
674	exit(exit_code);
675}
676
677
678/* -----------------------------------------------------------------------------
679 ----------------------------------------------------------------------------- */
680CFStringRef
681copy_padded_string(CFStringRef original, int width, CFStringRef prefix, CFStringRef suffix)
682{
683	CFMutableStringRef	padded;
684
685	padded = CFStringCreateMutable(NULL, 0);
686	if (prefix != NULL) {
687		CFStringAppend(padded, prefix);
688	}
689	if (original != NULL) {
690		CFStringAppend(padded, original);
691	}
692	if (suffix != NULL) {
693		CFStringAppend(padded, suffix);
694	}
695	CFStringPad(padded, CFSTR(" "), MAX(CFStringGetLength(original), width), 0);
696	return padded;
697}
698
699CFStringRef
700copy_VPN_status(SCNetworkServiceRef service)
701{
702	CFStringRef output = NULL;
703	SCNetworkConnectionStatus status = kSCNetworkConnectionInvalid;
704	SCNetworkConnectionRef service_connection = NULL;
705
706	/* Only calculate status is the service is enabled. Default is invalid. */
707	if (SCNetworkServiceGetEnabled(service)) {
708		service_connection = SCNetworkConnectionCreateWithService(NULL, service, NULL, NULL);
709		if (service_connection == NULL) goto done;
710		status = SCNetworkConnectionGetStatus(service_connection);
711	}
712
713	output = CFStringCreateWithCString(NULL, nc_status_string(status), kCFStringEncodingUTF8);
714
715done:
716	my_CFRelease(&service_connection);
717	return output;
718}
719
720static void
721nc_print_VPN_service(SCNetworkServiceRef service)
722{
723	SCNetworkInterfaceRef interface = NULL;
724	CFStringRef display_name = NULL;
725	CFStringRef display_name_padded = NULL;
726	CFStringRef service_id = NULL;
727	CFStringRef service_name = NULL;
728	CFStringRef service_name_padded = NULL;
729	CFStringRef service_status = NULL;
730	CFStringRef service_status_padded = NULL;
731	CFStringRef sub_type = NULL;
732	CFStringRef type = NULL;
733
734	nc_get_service_type_and_subtype(service, &type, &sub_type);
735
736	service_name = SCNetworkServiceGetName(service);
737	service_name_padded = copy_padded_string(service_name, 32, CFSTR("\""), CFSTR("\""));
738
739	service_id = SCNetworkServiceGetServiceID(service);
740
741	interface = SCNetworkServiceGetInterface(service);
742	display_name = SCNetworkInterfaceGetLocalizedDisplayName(interface);
743	display_name_padded = copy_padded_string(display_name, 18, NULL, NULL);
744
745	service_status = copy_VPN_status(service);
746	service_status_padded = copy_padded_string(service_status, 16, CFSTR("("), CFSTR(")"));
747
748	SCPrint(TRUE,
749		stdout,
750		CFSTR("%@ %@ %@ %@ %@ [%@%@%@]\n"),
751		SCNetworkServiceGetEnabled(service) ? CFSTR("*") : CFSTR(" "),
752		service_status_padded,
753		service_id,
754		display_name_padded,
755		service_name_padded,
756		type,
757		(sub_type == NULL) ? CFSTR("") : CFSTR(":"),
758		(sub_type == NULL) ? CFSTR("") : sub_type);
759
760	CFRelease(display_name_padded);
761	CFRelease(service_name_padded);
762	CFRelease(service_status_padded);
763	my_CFRelease(&service_status);
764}
765
766
767/* -----------------------------------------------------------------------------
768----------------------------------------------------------------------------- */
769static void
770nc_list(int argc, char **argv)
771{
772	int			count;
773	int			i;
774	CFArrayRef		services	= NULL;
775
776	SCPrint(TRUE, stdout, CFSTR("Available network connection services in the current set (*=enabled):\n"));
777	services = SCNetworkConnectionCopyAvailableServices(NULL);
778	if (services != NULL) {
779		count = CFArrayGetCount(services);
780
781		for (i = 0; i < count; i++) {
782			SCNetworkServiceRef	service;
783
784			service = CFArrayGetValueAtIndex(services, i);
785			nc_print_VPN_service(service);
786		}
787
788	}
789	my_CFRelease(&services);
790	exit(0);
791}
792
793/* -----------------------------------------------------------------------------
794 ----------------------------------------------------------------------------- */
795static Boolean
796nc_enable_vpntype(CFStringRef vpnType)
797{
798	Boolean			is_enabled = FALSE;
799	Boolean			success = FALSE;
800
801	if (vpnType == NULL) {
802		SCPrint(TRUE, stderr, CFSTR("No VPN type provided\n"));
803		goto done;
804	}
805
806	is_enabled = VPNConfigurationIsVPNTypeEnabled(vpnType);
807
808	if (is_enabled) {
809		SCPrint(TRUE, stdout, CFSTR("VPN is already enabled\n"));
810	} else {
811#if	!TARGET_OS_IPHONE
812		AuthorizationRef	authorization;
813
814		authorization = _prefs_AuthorizationCreate();
815		if ((authorization == NULL) ||
816		    !VPNConfigurationSetAuthorization(authorization)) {
817			SCPrint(TRUE, stderr, CFSTR("VPNConfigurationSetAuthorization failed: %s\n"), SCErrorString(SCError()));
818			goto done;
819		}
820#endif	// !TARGET_OS_IPHONE
821
822		if (!VPNConfigurationEnableVPNType(vpnType)) {
823			SCPrint(TRUE, stderr, CFSTR("VPN could not be enabled: %s\n"), SCErrorString(SCError()));
824			goto done;
825		}
826
827#if	!TARGET_OS_IPHONE
828		_prefs_AuthorizationFree(authorization);
829#endif	// !TARGET_OS_IPHONE
830
831		SCPrint(TRUE, stdout, CFSTR("VPN enabled\n"));
832	}
833	success = TRUE;
834
835done:
836	return success;
837}
838
839/* Turns a service ID or name into a vendor type, or preserves type */
840static CFStringRef
841nc_copy_vendor_type (CFStringRef input)
842{
843	SCNetworkInterfaceRef	child;
844	SCNetworkInterfaceRef	interface;
845	CFStringRef		output_name	= input;
846	SCNetworkServiceRef	service		= NULL;
847	CFStringRef		type;
848
849	if (input == NULL) {
850		goto done;
851	}
852
853	service = nc_copy_service(NULL, input);
854	if (service != NULL) {
855		interface = SCNetworkServiceGetInterface(service);
856		child = SCNetworkInterfaceGetInterface(interface);
857		type = SCNetworkInterfaceGetInterfaceType(interface);
858
859		/* Must be of type VPN */
860		if (!CFEqual(type, kSCNetworkInterfaceTypeVPN)) {
861			output_name = NULL;
862			goto done;
863		}
864		output_name = SCNetworkInterfaceGetInterfaceType(child);
865		goto done;
866	}
867
868done :
869	if (output_name != NULL) CFRetain(output_name);
870	my_CFRelease(&service);
871	return output_name;
872}
873
874/* -----------------------------------------------------------------------------
875 ----------------------------------------------------------------------------- */
876#if !TARGET_OS_IPHONE
877static const CFStringRef PREF_PREFIX                       = CFSTR("VPN-");
878static const CFStringRef PREF_SUFFIX                       = CFSTR(".plist");
879static void
880nc_set_application_url(CFStringRef subtype, CFStringRef directory)
881{
882	CFURLRef	directory_url		= NULL;
883	CFDataRef	directory_url_data	= NULL;
884	CFStringRef	vpnprefpath		= NULL;
885	char	       *path			= NULL;
886	CFIndex		path_len		= 0;
887
888	if (subtype == NULL || directory == NULL) {
889		goto done;
890	}
891
892	directory_url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
893						      directory,
894						      kCFURLPOSIXPathStyle,
895						      FALSE);
896	if (directory_url == NULL) {
897		SCPrint(TRUE, stderr, CFSTR("CFURLCreateWithFileSystemPath failed\n"));
898		goto done;
899	}
900
901	directory_url_data = CFURLCreateBookmarkData(NULL, directory_url, 0, 0, 0, 0);
902	if (directory_url_data == NULL) {
903		SCPrint(TRUE, stderr, CFSTR("CFURLCreateBookmarkData failed\n"));
904		goto done;
905	}
906
907	vpnprefpath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@%@"), PREF_PREFIX, subtype, PREF_SUFFIX );
908	if (vpnprefpath == NULL) {
909		SCPrint(TRUE, stderr, CFSTR("CFStringCreateWithFormat failed\n"));
910		goto done;
911	}
912
913	path_len = CFStringGetLength(vpnprefpath) + 1;
914	path = malloc(path_len);
915	if (path == NULL) {
916		goto done;
917	}
918
919	if (!CFStringGetCString(vpnprefpath, path, path_len, kCFStringEncodingASCII)) {
920		SCPrint(TRUE, stderr, CFSTR("CFStringGetCString failed\n"));
921		goto done;
922	}
923
924	do_prefs_init();		/* initialization */
925	do_prefs_open(1, &path);	/* open prefs */
926
927	if (!SCPreferencesSetValue(prefs, CFSTR("ApplicationURL"), directory_url_data)) {
928		SCPrint(TRUE, stderr,
929			CFSTR("SCPreferencesSetValue ApplicationURL failed, %s\n"),
930			SCErrorString(SCError()));
931		goto done;
932	}
933
934	_prefs_save();
935
936done:
937	my_CFRelease(&directory_url);
938	my_CFRelease(&directory_url_data);
939	my_CFRelease(&vpnprefpath);
940	if (path) {
941		free(path);
942	}
943	_prefs_close();
944
945	exit(0);
946}
947#endif
948
949/* -----------------------------------------------------------------------------
950 ----------------------------------------------------------------------------- */
951static void
952nc_enablevpn(int argc, char **argv)
953{
954	CFStringRef		argument = NULL;
955	CFStringRef		vendorType = NULL;
956	int			exit_code = 1;
957
958	if (argc == 0) {
959		SCPrint(TRUE, stderr, CFSTR("No service type or ID\n"));
960	} else {
961		argument = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
962		vendorType = nc_copy_vendor_type(argument);
963		my_CFRelease(&argument);
964
965		if (!nc_enable_vpntype(vendorType)) {
966			goto done;
967		}
968#if !TARGET_OS_IPHONE
969		if (argc >= 2) {
970			argument = CFStringCreateWithCString(NULL, argv[1], kCFStringEncodingUTF8);
971			nc_set_application_url(vendorType, argument);
972			my_CFRelease(&argument);
973		}
974#endif
975	}
976
977	exit_code = 0;
978
979done:
980	my_CFRelease(&vendorType);
981	exit(exit_code);
982}
983
984
985#if TARGET_OS_EMBEDDED
986static void
987nc_print_VPN_app_info(CFStringRef appInfo, CFDictionaryRef appInfoDict)
988{
989	CFStringRef appName = NULL;
990	Boolean isEnabled = FALSE;
991	CFStringRef paddedAppInfo = NULL;
992	CFStringRef paddedAppName = NULL;
993
994	if (appInfo == NULL) {
995		return;
996	}
997
998	isEnabled = VPNConfigurationIsVPNTypeEnabled(appInfo);
999
1000	CFDictionaryGetValueIfPresent(appInfoDict, CFSTR("CFBundleDisplayName"), (const void **)&appName);
1001	paddedAppName = copy_padded_string((appName == NULL) ? CFSTR("") : appName, 12, NULL, NULL);
1002	paddedAppInfo = copy_padded_string(appInfo, 30, NULL, NULL);
1003
1004	SCPrint(TRUE, stdout, CFSTR("%@ %@ [%@]\n"),
1005		isEnabled ? CFSTR("(Enabled) ") : CFSTR("(Disabled)"),
1006		paddedAppName,
1007		appInfo);
1008
1009	my_CFRelease(&paddedAppName);
1010	my_CFRelease(&paddedAppInfo);
1011}
1012#endif
1013
1014/* -----------------------------------------------------------------------------
1015 ----------------------------------------------------------------------------- */
1016#if	TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
1017static void
1018nc_listvpn(int argc, char **argv)
1019{
1020
1021	CFDictionaryRef		appDict = NULL;
1022	CFArrayRef		appinfo = NULL;
1023	int			i, j, count, subtypecount;
1024	const void * *		keys = NULL;
1025	CFMutableDictionaryRef optionsDict = NULL;
1026	const void * *		values = NULL;
1027	CFStringRef		vpntype = NULL;
1028
1029	optionsDict = CFDictionaryCreateMutable(NULL, 0,
1030						&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1031	CFDictionarySetValue(optionsDict, kLookupApplicationTypeKey, kApplicationTypeUser);
1032	CFDictionarySetValue(optionsDict, kLookupAttributeKey, CFSTR("UIVPNPlugin"));
1033
1034	appDict = MobileInstallationLookup(optionsDict);
1035	if (!isA_CFDictionary(appDict))
1036		goto done;
1037
1038	count = CFDictionaryGetCount(appDict);
1039	if (count > 0) {
1040		keys = (const void * *)malloc(sizeof(CFTypeRef) * count);
1041		values = (const void * *)malloc(sizeof(CFTypeRef) * count);
1042
1043		CFDictionaryGetKeysAndValues(appDict, keys, values);
1044		for (i=0; i<count; i++) {
1045			appinfo = CFDictionaryGetValue(values[i], CFSTR("UIVPNPlugin"));
1046			if (appinfo) {
1047
1048
1049
1050				if (isA_CFString(appinfo)) {
1051					nc_print_VPN_app_info((CFStringRef)appinfo, (CFDictionaryRef)values[i]);
1052				}
1053				else if (isA_CFArray(appinfo)) {
1054					subtypecount = CFArrayGetCount((CFArrayRef)appinfo);
1055					for(j=0; j<subtypecount; j++) {
1056						vpntype = (CFStringRef)CFArrayGetValueAtIndex((CFArrayRef)appinfo, j);
1057						nc_print_VPN_app_info(vpntype, (CFDictionaryRef)values[i]);
1058					}
1059				}
1060			}
1061		}
1062	}
1063done:
1064	if (keys) free(keys);
1065	if (values) free(values);
1066	my_CFRelease(&optionsDict);
1067	my_CFRelease(&appDict);
1068
1069	exit(0);
1070}
1071#endif	// TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
1072
1073/* -----------------------------------------------------------------------------
1074----------------------------------------------------------------------------- */
1075static void
1076nc_show(int argc, char **argv)
1077{
1078	SCNetworkServiceRef	service			= NULL;
1079	SCDynamicStoreRef	store			= NULL;
1080	int			exit_code		= 1;
1081	CFStringRef		serviceID		= NULL;
1082	CFStringRef		iftype			= NULL;
1083	CFStringRef		ifsubtype		= NULL;
1084	CFStringRef		type_entity_key		= NULL;
1085	CFStringRef		subtype_entity_key	= NULL;
1086	CFDictionaryRef		type_entity_dict	= NULL;
1087	CFDictionaryRef		subtype_entity_dict	= NULL;
1088	CFStringRef		vpnprefpath		= NULL;
1089#if !TARGET_OS_IPHONE
1090	CFDataRef		bookmarkData		= NULL;
1091	CFURLRef		directory		= NULL;
1092	Boolean			isStale			= FALSE;
1093	char			*path			= NULL;
1094	CFIndex			path_len		= 0;
1095#endif
1096
1097	service = nc_copy_service_from_arguments(argc, argv, NULL);
1098	if (service == NULL) {
1099		SCPrint(TRUE, stderr, CFSTR("No service\n"));
1100		exit(exit_code);
1101	}
1102
1103	serviceID = SCNetworkServiceGetServiceID(service);
1104
1105	nc_get_service_type_and_subtype(service, &iftype, &ifsubtype);
1106
1107	if (!CFEqual(iftype, kSCEntNetPPP) &&
1108	    !CFEqual(iftype, kSCEntNetIPSec) &&
1109	    !CFEqual(iftype, kSCEntNetVPN)) {
1110		SCPrint(TRUE, stderr, CFSTR("Not a connection oriented service: %@\n"), serviceID);
1111		goto done;
1112	}
1113
1114	type_entity_key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, serviceID, iftype);
1115
1116	nc_print_VPN_service(service);
1117
1118#if !TARGET_OS_IPHONE
1119	vpnprefpath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@%@"), PREF_PREFIX, ifsubtype, PREF_SUFFIX);
1120	if (vpnprefpath == NULL) {
1121		goto skipURL;
1122	}
1123
1124	path_len = CFStringGetLength(vpnprefpath) + 1;
1125	path = malloc(path_len);
1126	if (path == NULL) {
1127		goto skipURL;
1128	}
1129
1130	if (!CFStringGetCString(vpnprefpath, path, path_len, kCFStringEncodingASCII)) {
1131		SCPrint(TRUE, stderr, CFSTR("CFStringGetCString failed\n"));
1132		goto done;
1133	}
1134
1135	do_prefs_init();		/* initialization */
1136	do_prefs_open(1, &path);	/* open prefs */
1137
1138	bookmarkData = SCPreferencesGetValue(prefs, CFSTR("ApplicationURL"));
1139	if (bookmarkData == NULL) {
1140		goto skipURL;
1141	}
1142
1143	directory = CFURLCreateByResolvingBookmarkData(kCFAllocatorDefault, bookmarkData, 0, NULL, NULL, &isStale, NULL);
1144	if (directory == NULL) {
1145		goto skipURL;
1146	}
1147
1148	SCPrint(TRUE, stdout, CFSTR("ApplicationURL: %@\n"), directory);
1149skipURL:
1150#endif
1151
1152	store = SCDynamicStoreCreate(NULL, CFSTR("scutil --nc"), NULL, NULL);
1153	if (store == NULL) {
1154		SCPrint(TRUE, stderr, CFSTR("Unable to create dynamic store: %s\n"), SCErrorString(SCError()));
1155		goto done;
1156	}
1157	type_entity_dict = SCDynamicStoreCopyValue(store, type_entity_key);
1158
1159	if (!type_entity_dict) {
1160		SCPrint(TRUE, stderr, CFSTR("No \"%@\" configuration available\n"), iftype);
1161	} else {
1162		SCPrint(TRUE, stdout, CFSTR("%@ %@\n"), iftype, type_entity_dict);
1163	}
1164
1165	if (ifsubtype) {
1166		subtype_entity_key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, serviceID, ifsubtype);
1167		subtype_entity_dict = SCDynamicStoreCopyValue(store, subtype_entity_key);
1168		if (!subtype_entity_dict) {
1169			//
1170		}
1171		else {
1172			SCPrint(TRUE, stdout, CFSTR("%@ %@\n"), ifsubtype, subtype_entity_dict);
1173		}
1174	}
1175
1176	exit_code = 0;
1177
1178done:
1179	my_CFRelease(&type_entity_key);
1180	my_CFRelease(&type_entity_dict);
1181	my_CFRelease(&subtype_entity_key);
1182	my_CFRelease(&subtype_entity_dict);
1183	my_CFRelease(&store);
1184	my_CFRelease(&service);
1185	my_CFRelease(&vpnprefpath);
1186	_prefs_close();
1187	exit(exit_code);
1188}
1189
1190/* -----------------------------------------------------------------------------
1191 ----------------------------------------------------------------------------- */
1192static void
1193nc_select(int argc, char **argv)
1194{
1195	SCNetworkSetRef		current_set;
1196	int			exit_code	= 1;
1197	SCNetworkServiceRef	service		= NULL;
1198	Boolean			status;
1199
1200	do_prefs_init();	/* initialization */
1201	do_prefs_open(0, NULL);	/* open default prefs */
1202
1203	current_set = SCNetworkSetCopyCurrent(prefs);
1204	if (current_set == NULL) {
1205		SCPrint(TRUE, stderr, CFSTR("No current location\n"), SCErrorString(SCError()));
1206		goto done;
1207	}
1208
1209	service = nc_copy_service_from_arguments(argc, argv, current_set);
1210	if (service == NULL) {
1211		SCPrint(TRUE, stderr, CFSTR("No service\n"));
1212		goto done;
1213	}
1214
1215#if !TARGET_OS_IPHONE
1216	status = SCNetworkServiceSetEnabled(service, TRUE);
1217	if (!status) {
1218		SCPrint(TRUE, stderr, CFSTR("Unable to enable service: %s\n"), SCErrorString(SCError()));
1219		goto done;
1220	}
1221#else
1222	status = SCNetworkSetSetSelectedVPNService(current_set, service);
1223	if (!status) {
1224		SCPrint(TRUE, stderr, CFSTR("Unable to select service: %s\n"), SCErrorString(SCError()));
1225		goto done;
1226	}
1227#endif
1228
1229	_prefs_save();
1230	exit_code = 0;
1231done:
1232	my_CFRelease(&service);
1233	my_CFRelease(&current_set);
1234	_prefs_close();
1235	exit(exit_code);
1236}
1237
1238/* -----------------------------------------------------------------------------
1239 ----------------------------------------------------------------------------- */
1240static void
1241nc_help(int argc, char **argv)
1242{
1243	SCPrint(TRUE, stderr, CFSTR("Valid commands for scutil --nc (VPN connections)\n"));
1244	SCPrint(TRUE, stderr, CFSTR("Usage: scutil --nc [command]\n"));
1245	SCPrint(TRUE, stderr, CFSTR("\n"));
1246	SCPrint(TRUE, stderr, CFSTR("\tlist\n"));
1247	SCPrint(TRUE, stderr, CFSTR("\t\tList available network connection services in the current set\n"));
1248	SCPrint(TRUE, stderr, CFSTR("\n"));
1249	SCPrint(TRUE, stderr, CFSTR("\tstatus <service>\n"));
1250	SCPrint(TRUE, stderr, CFSTR("\t\tIndicate whether a given service is connected, as well as extended status information for the service\n"));
1251	SCPrint(TRUE, stderr, CFSTR("\n"));
1252	SCPrint(TRUE, stderr, CFSTR("\tshow <service>\n"));
1253	SCPrint(TRUE, stderr, CFSTR("\t\tDisplay configuration information for a given service\n"));
1254	SCPrint(TRUE, stderr, CFSTR("\n"));
1255	SCPrint(TRUE, stderr, CFSTR("\tstatistics <service>\n"));
1256	SCPrint(TRUE, stderr, CFSTR("\t\tProvide statistics on bytes, packets, and errors for a given service\n"));
1257	SCPrint(TRUE, stderr, CFSTR("\n"));
1258	SCPrint(TRUE, stderr, CFSTR("\tselect <service>\n"));
1259	SCPrint(TRUE, stderr, CFSTR("\t\tMake the given service active in the current set. This allows it to be started\n"));
1260	SCPrint(TRUE, stderr, CFSTR("\n"));
1261	SCPrint(TRUE, stderr, CFSTR("\tstart <service> [--user user] [--password password] [--secret secret]\n"));
1262	SCPrint(TRUE, stderr, CFSTR("\t\tStart a given service. Can take optional arguments for user, password, and secret\n"));
1263	SCPrint(TRUE, stderr, CFSTR("\n"));
1264	SCPrint(TRUE, stderr, CFSTR("\tstop <service>\n"));
1265	SCPrint(TRUE, stderr, CFSTR("\t\tStop a given service\n"));
1266	SCPrint(TRUE, stderr, CFSTR("\n"));
1267	SCPrint(TRUE, stderr, CFSTR("\tsuspend <service>\n"));
1268	SCPrint(TRUE, stderr, CFSTR("\t\tSuspend a given service (PPP, Modem on Hold)\n"));
1269	SCPrint(TRUE, stderr, CFSTR("\n"));
1270	SCPrint(TRUE, stderr, CFSTR("\tresume <service>\n"));
1271	SCPrint(TRUE, stderr, CFSTR("\t\tResume a given service (PPP, Modem on Hold)\n"));
1272	SCPrint(TRUE, stderr, CFSTR("\n"));
1273	SCPrint(TRUE, stderr, CFSTR("\tondemand [-W] [hostname]\n"));
1274	SCPrint(TRUE, stderr, CFSTR("\tondemand -- --refresh\n"));
1275	SCPrint(TRUE, stderr, CFSTR("\t\tDisplay VPN on-demand information\n"));
1276	SCPrint(TRUE, stderr, CFSTR("\n"));
1277	SCPrint(TRUE, stderr, CFSTR("\ttrigger <hostname> [background] [port]\n"));
1278	SCPrint(TRUE, stderr, CFSTR("\t\tTrigger VPN on-demand with specified hostname, and optional port and background flag\n"));
1279	SCPrint(TRUE, stderr, CFSTR("\n"));
1280#if TARGET_OS_EMBEDDED
1281	SCPrint(TRUE, stderr, CFSTR("\tlistvpn\n"));
1282	SCPrint(TRUE, stderr, CFSTR("\t\tDisplay the installed VPN applications\n"));
1283	SCPrint(TRUE, stderr, CFSTR("\n"));
1284#endif
1285#if !TARGET_OS_IPHONE
1286	SCPrint(TRUE, stderr, CFSTR("\tenablevpn <service or vpn type> [path]\n"));
1287	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"));
1288	SCPrint(TRUE, stderr, CFSTR("\n"));
1289#else
1290	SCPrint(TRUE, stderr, CFSTR("\tenablevpn <service or vpn type>\n"));
1291	SCPrint(TRUE, stderr, CFSTR("\t\tEnables the given VPN application type. Takes either a service or VPN type\n"));
1292	SCPrint(TRUE, stderr, CFSTR("\n"));
1293#endif
1294	SCPrint(TRUE, stderr, CFSTR("\tdisablevpn <service or vpn type>\n"));
1295	SCPrint(TRUE, stderr, CFSTR("\t\tDisables the given VPN application type. Takes either a service or VPN type\n"));
1296	SCPrint(TRUE, stderr, CFSTR("\n"));
1297	SCPrint(TRUE, stderr, CFSTR("\thelp\n"));
1298	SCPrint(TRUE, stderr, CFSTR("\t\tDisplay available commands for --nc\n"));
1299	SCPrint(TRUE, stderr, CFSTR("\n"));
1300	exit(0);
1301}
1302
1303/* -----------------------------------------------------------------------------
1304----------------------------------------------------------------------------- */
1305typedef void (*nc_func) (int argc, char **argv);
1306
1307static const struct {
1308	char		*cmd;
1309	nc_func		func;
1310} nc_cmds[] = {
1311	{ "enablevpn",		nc_enablevpn	},
1312	{ "help",		nc_help		},
1313	{ "list",		nc_list		},
1314#if	TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
1315	{ "listvpn",		nc_listvpn	},
1316#endif	// TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
1317	{ "ondemand",		nc_ondemand	},
1318	{ "resume",		nc_resume	},
1319	{ "select",		nc_select	},
1320	{ "show",		nc_show		},
1321	{ "start",		nc_start	},
1322	{ "statistics",		nc_statistics	},
1323	{ "status",		nc_status	},
1324	{ "stop",		nc_stop		},
1325	{ "suspend",		nc_suspend	},
1326	{ "trigger",		nc_trigger	},
1327};
1328#define	N_NC_CMNDS	(sizeof(nc_cmds) / sizeof(nc_cmds[0]))
1329
1330
1331/* -----------------------------------------------------------------------------
1332----------------------------------------------------------------------------- */
1333int
1334find_nc_cmd(char *cmd)
1335{
1336	int	i;
1337
1338	for (i = 0; i < (int)N_NC_CMNDS; i++) {
1339		if (strcmp(cmd, nc_cmds[i].cmd) == 0) {
1340			return i;
1341		}
1342	}
1343
1344	return -1;
1345}
1346
1347
1348/* -----------------------------------------------------------------------------
1349----------------------------------------------------------------------------- */
1350void
1351do_nc_cmd(char *cmd, int argc, char **argv, Boolean watch)
1352{
1353	int	i;
1354
1355	i = find_nc_cmd(cmd);
1356	if (i >= 0) {
1357		nc_func	func;
1358
1359		func = nc_cmds[i].func;
1360		if (watch) {
1361			if (func == nc_status) {
1362				func = nc_watch;
1363			} else if (func == nc_ondemand) {
1364				ondemandwatch = TRUE;
1365			}
1366		}
1367		(*func)(argc, argv);
1368	}
1369	return;
1370}
1371
1372