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