]>
Commit | Line | Data |
---|---|---|
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 | ||
52 | CFStringRef username = NULL; | |
53 | CFStringRef password = NULL; | |
54 | CFStringRef sharedsecret = NULL; | |
55 | ||
56 | static Boolean ondemandwatch = FALSE; | |
57 | static CFStringRef ondemand_nodename = NULL; | |
58 | ||
59 | static SCNetworkConnectionRef connection = NULL; | |
60 | static int n_callback = 0; | |
61 | ||
62 | ||
63 | /* ----------------------------------------------------------------------------- | |
64 | ----------------------------------------------------------------------------- */ | |
65 | static void | |
66 | my_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 | ----------------------------------------------------------------------------- */ | |
78 | static void | |
79 | nc_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 | ----------------------------------------------------------------------------- */ | |
93 | static SCNetworkServiceRef | |
94 | nc_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 | ||
140 | done : | |
141 | ||
142 | if (selected != NULL) CFRetain(selected); | |
143 | if (services != NULL) CFRelease(services); | |
144 | return selected; | |
145 | } | |
146 | ||
147 | /* ----------------------------------------------------------------------------- | |
148 | ----------------------------------------------------------------------------- */ | |
149 | static SCNetworkServiceRef | |
150 | nc_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 | ----------------------------------------------------------------------------- */ | |
171 | static char * | |
172 | nc_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 | ||
189 | static void | |
190 | nc_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 | ||
230 | static void | |
231 | nc_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 | ||
257 | static void | |
258 | nc_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 | ----------------------------------------------------------------------------- */ | |
320 | static void | |
321 | nc_release_connection() | |
322 | { | |
323 | my_CFRelease(&connection); | |
324 | } | |
325 | ||
326 | /* ----------------------------------------------------------------------------- | |
327 | ----------------------------------------------------------------------------- */ | |
328 | static void | |
329 | nc_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 | ----------------------------------------------------------------------------- */ | |
407 | static void | |
408 | nc_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 | ----------------------------------------------------------------------------- */ | |
423 | static void | |
424 | nc_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 | ----------------------------------------------------------------------------- */ | |
436 | static void | |
437 | nc_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 | ----------------------------------------------------------------------------- */ | |
449 | static void | |
450 | nc_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 | ||
463 | static void | |
464 | nc_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 | ----------------------------------------------------------------------------- */ | |
498 | static void | |
499 | nc_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 | ----------------------------------------------------------------------------- */ | |
521 | static void | |
522 | checkOnDemandHost(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 | ||
562 | static void | |
563 | nc_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 | ||
600 | static void | |
601 | nc_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; | |
669 | done: | |
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 | ----------------------------------------------------------------------------- */ | |
680 | CFStringRef | |
681 | copy_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 | ||
699 | CFStringRef | |
700 | copy_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 | ||
715 | done: | |
716 | my_CFRelease(&service_connection); | |
717 | return output; | |
718 | } | |
719 | ||
720 | static void | |
721 | nc_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 | ----------------------------------------------------------------------------- */ | |
769 | static void | |
770 | nc_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 | ----------------------------------------------------------------------------- */ | |
795 | static Boolean | |
796 | nc_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 | ||
835 | done: | |
836 | return success; | |
837 | } | |
838 | ||
839 | /* Turns a service ID or name into a vendor type, or preserves type */ | |
840 | static CFStringRef | |
841 | nc_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 | ||
868 | done : | |
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 | |
877 | static const CFStringRef PREF_PREFIX = CFSTR("VPN-"); | |
878 | static const CFStringRef PREF_SUFFIX = CFSTR(".plist"); | |
879 | static void | |
880 | nc_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 | ||
936 | done: | |
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 | ----------------------------------------------------------------------------- */ | |
951 | static void | |
952 | nc_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 | ||
979 | done: | |
980 | my_CFRelease(&vendorType); | |
981 | exit(exit_code); | |
982 | } | |
983 | ||
984 | ||
985 | #if TARGET_OS_EMBEDDED | |
986 | static void | |
987 | nc_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 | |
1017 | static void | |
1018 | nc_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 | } | |
1063 | done: | |
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 | ----------------------------------------------------------------------------- */ | |
1075 | static void | |
1076 | nc_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); | |
1149 | skipURL: | |
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 | ||
1178 | done: | |
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 | ----------------------------------------------------------------------------- */ | |
1192 | static void | |
1193 | nc_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; | |
1231 | done: | |
1232 | my_CFRelease(&service); | |
1233 | my_CFRelease(¤t_set); | |
1234 | _prefs_close(); | |
1235 | exit(exit_code); | |
1236 | } | |
1237 | ||
1238 | /* ----------------------------------------------------------------------------- | |
1239 | ----------------------------------------------------------------------------- */ | |
1240 | static void | |
1241 | nc_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 | ----------------------------------------------------------------------------- */ | |
1305 | typedef void (*nc_func) (int argc, char **argv); | |
1306 | ||
1307 | static 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 | ----------------------------------------------------------------------------- */ | |
1333 | int | |
1334 | find_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 | ----------------------------------------------------------------------------- */ | |
1350 | void | |
1351 | do_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 |