]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCNetworkConnection.c
configd-802.40.13.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCNetworkConnection.c
1 /*
2 * Copyright (c) 2003-2015 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 * April 14, 2004 Christophe Allie <callie@apple.com>
28 * - use mach messages
29
30 * December 20, 2002 Christophe Allie <callie@apple.com>
31 * - initial revision
32 */
33
34
35 //#define DEBUG_MACH_PORT_ALLOCATIONS
36
37
38 #include <Availability.h>
39 #include <TargetConditionals.h>
40 #include <sys/cdefs.h>
41 #include <dispatch/dispatch.h>
42 #include <CoreFoundation/CoreFoundation.h>
43 #include <CoreFoundation/CFRuntime.h>
44 #include <CoreFoundation/CFXPCBridge.h>
45 #include <SystemConfiguration/SystemConfiguration.h>
46 #include <SystemConfiguration/SCPrivate.h>
47 #include <SystemConfiguration/SCValidation.h>
48 #include <SystemConfiguration/VPNAppLayerPrivate.h>
49 #include <SystemConfiguration/VPNTunnel.h>
50
51 #if !TARGET_OS_IPHONE
52 #include <Security/Security.h>
53 #include "dy_framework.h"
54 #endif // !TARGET_OS_IPHONE
55
56 #include <bootstrap.h>
57
58 #include <pthread.h>
59 #include <notify.h>
60 #include <netinet/in.h>
61 #include <arpa/inet.h>
62 #include <netdb.h>
63 #include <unistd.h>
64 #include <sys/ioctl.h>
65 #include <sys/socket.h>
66 #include <net/if.h>
67 #include <mach/mach.h>
68 #include <bsm/audit.h>
69 #include <bsm/libbsm.h>
70 #include <sandbox.h>
71 #include <sys/proc_info.h>
72 #include <libproc.h>
73
74 #include <ppp/ppp_msg.h>
75 #include "pppcontroller.h"
76 #include <ppp/pppcontroller_types.h>
77
78 #ifndef PPPCONTROLLER_SERVER_PRIV
79 #define PPPCONTROLLER_SERVER_PRIV PPPCONTROLLER_SERVER
80 #endif // !PPPCONTROLLER_SERVER_PRIV
81
82
83 #include "SCNetworkConnectionInternal.h"
84
85 static int debug = 0;
86 static pthread_once_t initialized = PTHREAD_ONCE_INIT;
87 static pthread_mutex_t scnc_lock = PTHREAD_MUTEX_INITIALIZER;
88 static mach_port_t scnc_server = MACH_PORT_NULL;
89 static char *scnc_server_name = NULL;
90
91
92 typedef struct {
93
94 /* base CFType information */
95 CFRuntimeBase cfBase;
96
97 /* lock */
98 pthread_mutex_t lock;
99
100 /* service */
101 SCNetworkServiceRef service;
102
103 /* client info (if we are proxying for another process */
104 mach_port_t client_audit_session;
105 audit_token_t client_audit_token;
106 mach_port_t client_bootstrap_port;
107 uid_t client_uid;
108 gid_t client_gid;
109 pid_t client_pid;
110 uuid_t client_uuid;
111 CFStringRef client_bundle_id;
112
113 /* ref to PPP controller for control messages */
114 mach_port_t session_port;
115
116 /* ref to PPP controller for notification messages */
117 CFMachPortRef notify_port;
118
119 /* keep track of whether we're acquired the initial status */
120 Boolean haveStatus;
121
122 /* run loop source, callout, context, rl scheduling info */
123 Boolean scheduled;
124 CFRunLoopSourceRef rls;
125 SCNetworkConnectionCallBack rlsFunction;
126 SCNetworkConnectionContext rlsContext;
127 CFMutableArrayRef rlList;
128
129 /* SCNetworkConnectionSetDispatchQueue */
130 dispatch_group_t dispatchGroup;
131 dispatch_queue_t dispatchQueue;
132 dispatch_source_t dispatchSource;
133
134 SCNetworkConnectionType type;
135 Boolean on_demand;
136 CFDictionaryRef on_demand_info;
137 CFDictionaryRef on_demand_user_options;
138 CFStringRef on_demand_required_probe;
139
140 /* Flow Divert support info */
141 CFDictionaryRef flow_divert_token_params;
142
143 #if !TARGET_IPHONE_SIMULATOR
144 /* NetworkExtension data structures */
145 ne_session_t ne_session;
146 #endif /* !TARGET_IPHONE_SIMULATOR */
147 } SCNetworkConnectionPrivate, *SCNetworkConnectionPrivateRef;
148
149
150 static __inline__ CFTypeRef
151 isA_SCNetworkConnection(CFTypeRef obj)
152 {
153 return (isA_CFType(obj, SCNetworkConnectionGetTypeID()));
154 }
155
156
157 #if !TARGET_IPHONE_SIMULATOR
158 Boolean
159 __SCNetworkConnectionUseNetworkExtension(SCNetworkConnectionPrivateRef connectionPrivate)
160 {
161 Boolean result = FALSE;
162
163 if (ne_session_use_as_system_vpn() && connectionPrivate->service != NULL) {
164 _SCErrorSet(kSCStatusOK);
165 result = _SCNetworkServiceIsVPN(connectionPrivate->service);
166 /*
167 * SCNetworkServiceGetInterface (called by _SCNetworkServiceIsVPN) will set the SC error to kSCStatusInvalidArgument if the service does not have an associated prefs object.
168 * In that case, we try to get the service type/subtype from the dynamic store.
169 */
170 if (!result && SCError() == kSCStatusInvalidArgument) {
171 CFStringRef interfaceKey = SCDynamicStoreKeyCreateNetworkServiceEntity(kCFAllocatorDefault,
172 kSCDynamicStoreDomainSetup,
173 SCNetworkServiceGetServiceID(connectionPrivate->service),
174 kSCEntNetInterface);
175 CFDictionaryRef interfaceDict = SCDynamicStoreCopyValue(NULL, interfaceKey);
176 if (isA_CFDictionary(interfaceDict)) {
177 CFStringRef interfaceType = CFDictionaryGetValue(interfaceDict, kSCPropNetInterfaceType);
178 if (isA_CFString(interfaceType)) {
179 if (CFEqual(interfaceType, kSCNetworkInterfaceTypePPP)) {
180 CFStringRef interfaceSubType = CFDictionaryGetValue(interfaceDict, kSCPropNetInterfaceSubType);
181 result = (isA_CFString(interfaceSubType) && (CFEqual(interfaceSubType, kSCValNetInterfaceSubTypePPTP) || CFEqual(interfaceSubType, kSCValNetInterfaceSubTypeL2TP)));
182 } else {
183 result = (CFEqual(interfaceType, kSCNetworkInterfaceTypeVPN) || CFEqual(interfaceType, kSCNetworkInterfaceTypeIPSec));
184 }
185 }
186 }
187 if (interfaceDict != NULL) {
188 CFRelease(interfaceDict);
189 }
190 CFRelease(interfaceKey);
191 }
192 }
193
194 return result;
195 }
196 #endif /* !TARGET_IPHONE_SIMULATOR */
197
198
199 Boolean
200 __SCNetworkConnectionUsingNetworkExtension(SCNetworkConnectionPrivateRef connectionPrivate)
201 {
202 #if !TARGET_IPHONE_SIMULATOR
203 return (connectionPrivate->ne_session != NULL);
204 #else
205 return FALSE;
206 #endif /* !TARGET_IPHONE_SIMULATOR */
207 }
208
209
210 static CFStringRef
211 __SCNetworkConnectionCopyDescription(CFTypeRef cf)
212 {
213 CFAllocatorRef allocator = CFGetAllocator(cf);
214 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)cf;
215 CFMutableStringRef result;
216
217 result = CFStringCreateMutable(allocator, 0);
218 CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkConnection, %p [%p]> {"), cf, allocator);
219 CFStringAppendFormat(result, NULL, CFSTR("service = %p"), connectionPrivate->service);
220 if (connectionPrivate->session_port != MACH_PORT_NULL) {
221 CFStringAppendFormat(result, NULL, CFSTR(", server port = 0x%x"), connectionPrivate->session_port);
222 }
223 CFStringAppendFormat(result, NULL, CFSTR("using NetworkExtension = %s"), (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate) ? "yes" : "no"));
224 CFStringAppendFormat(result, NULL, CFSTR("}"));
225
226 return result;
227 }
228
229
230 static void
231 __SCNetworkConnectionDeallocate(CFTypeRef cf)
232 {
233 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)cf;
234
235 /* release resources */
236 pthread_mutex_destroy(&connectionPrivate->lock);
237
238 if (connectionPrivate->client_audit_session != MACH_PORT_NULL) {
239 mach_port_mod_refs(mach_task_self(),
240 connectionPrivate->client_audit_session,
241 MACH_PORT_RIGHT_SEND,
242 -1);
243 }
244
245 if (connectionPrivate->client_bootstrap_port != MACH_PORT_NULL) {
246 mach_port_mod_refs(mach_task_self(),
247 connectionPrivate->client_bootstrap_port,
248 MACH_PORT_RIGHT_SEND,
249 -1);
250 }
251
252 if (connectionPrivate->client_bundle_id != NULL) {
253 CFRelease(connectionPrivate->client_bundle_id);
254 }
255
256 if (connectionPrivate->rls != NULL) {
257 CFRunLoopSourceInvalidate(connectionPrivate->rls);
258 CFRelease(connectionPrivate->rls);
259 }
260
261 if (connectionPrivate->rlList != NULL) {
262 CFRelease(connectionPrivate->rlList);
263 }
264
265 if (connectionPrivate->notify_port != NULL) {
266 mach_port_t mp = CFMachPortGetPort(connectionPrivate->notify_port);
267
268 __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionDeallocate notify_port", mp);
269 CFMachPortInvalidate(connectionPrivate->notify_port);
270 CFRelease(connectionPrivate->notify_port);
271 mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1);
272 }
273
274 if (connectionPrivate->session_port != MACH_PORT_NULL) {
275 __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionDeallocate session_port", connectionPrivate->session_port);
276 (void) mach_port_deallocate(mach_task_self(), connectionPrivate->session_port);
277 }
278
279 if (connectionPrivate->rlsContext.release != NULL)
280 (*connectionPrivate->rlsContext.release)(connectionPrivate->rlsContext.info);
281
282 if (connectionPrivate->service != NULL) {
283 CFRelease(connectionPrivate->service);
284 }
285
286 if (connectionPrivate->on_demand_info != NULL) {
287 CFRelease(connectionPrivate->on_demand_info);
288 }
289
290 if (connectionPrivate->on_demand_user_options != NULL) {
291 CFRelease(connectionPrivate->on_demand_user_options);
292 }
293
294 if (connectionPrivate->on_demand_required_probe != NULL) {
295 CFRelease(connectionPrivate->on_demand_required_probe);
296 }
297
298 if (connectionPrivate->flow_divert_token_params != NULL) {
299 CFRelease(connectionPrivate->flow_divert_token_params);
300 }
301
302 #if !TARGET_IPHONE_SIMULATOR
303 if (connectionPrivate->ne_session != NULL) {
304 ne_session_set_event_handler(connectionPrivate->ne_session, NULL, NULL);
305 ne_session_release(connectionPrivate->ne_session);
306 }
307 #endif /* !TARGET_IPHONE_SIMULATOR */
308
309 return;
310 }
311
312
313 static CFTypeID __kSCNetworkConnectionTypeID = _kCFRuntimeNotATypeID;
314
315 static const CFRuntimeClass __SCNetworkConnectionClass = {
316 0, // version
317 "SCNetworkConnection", // className
318 NULL, // init
319 NULL, // copy
320 __SCNetworkConnectionDeallocate, // dealloc
321 NULL, // equal
322 NULL, // hash
323 NULL, // copyFormattingDesc
324 __SCNetworkConnectionCopyDescription // copyDebugDesc
325 };
326
327
328 static void
329 childForkHandler()
330 {
331 /* the process has forked (and we are the child process) */
332
333 scnc_server = MACH_PORT_NULL;
334 scnc_server_name = NULL;
335 return;
336 }
337
338
339 static void
340 __SCNetworkConnectionInitialize(void)
341 {
342 char *env;
343
344 /* get the debug environment variable */
345 env = getenv("PPPDebug");
346 if (env != NULL) {
347 if (sscanf(env, "%d", &debug) != 1) {
348 /* PPPDebug value is not valid (or non-numeric), set debug to 1 */
349 debug = 1;
350 }
351 }
352
353 /* register with CoreFoundation */
354 __kSCNetworkConnectionTypeID = _CFRuntimeRegisterClass(&__SCNetworkConnectionClass);
355
356 /* add handler to cleanup after fork() */
357 (void) pthread_atfork(NULL, NULL, childForkHandler);
358
359 return;
360 }
361
362
363 static Boolean
364 __SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection);
365
366 #define SC_NETWORK_CONNECTION_QUEUE "SCNetworkConnectionQueue"
367
368 static dispatch_queue_t
369 __SCNetworkConnectionQueue()
370 {
371 static dispatch_once_t once;
372 static dispatch_queue_t q;
373
374 dispatch_once(&once, ^{
375 q = dispatch_queue_create(SC_NETWORK_CONNECTION_QUEUE, NULL);
376 });
377
378 return q;
379 }
380
381
382 static void
383 __SCNetworkConnectionNotify(SCNetworkConnectionRef connection,
384 SCNetworkConnectionCallBack rlsFunction,
385 SCNetworkConnectionStatus nc_status,
386 void (*context_release)(const void *),
387 void *context_info)
388 {
389 os_activity_t activity_id;
390
391 activity_id = os_activity_start("processing SCNetworkConnection notification",
392 OS_ACTIVITY_FLAG_DEFAULT);
393 (*rlsFunction)(connection, nc_status, context_info);
394 if ((context_release != NULL) && (context_info != NULL)) {
395 (*context_release)(context_info);
396 }
397 os_activity_end(activity_id);
398
399 return;
400 }
401
402
403 static void
404 __SCNetworkConnectionCallBackRunLoopPerform(SCNetworkConnectionRef connection,
405 CFRunLoopRef rl,
406 CFStringRef rl_mode,
407 SCNetworkConnectionCallBack rlsFunction,
408 void (*context_release)(const void *),
409 void *context_info)
410 {
411 SCNetworkConnectionStatus nc_status;
412
413 nc_status = SCNetworkConnectionGetStatus(connection);
414 CFRunLoopPerformBlock(rl, rl_mode, ^{
415 __SCNetworkConnectionNotify(connection, rlsFunction, nc_status, context_release, context_info);
416 CFRelease(connection);
417 });
418 CFRunLoopWakeUp(rl);
419 return;
420 }
421
422 static void
423 __SCNetworkConnectionCallBackDispatchPerform(SCNetworkConnectionRef connection,
424 dispatch_queue_t q,
425 SCNetworkConnectionCallBack rlsFunction,
426 void (*context_release)(const void *),
427 void *context_info)
428 {
429 SCNetworkConnectionStatus nc_status;
430
431 nc_status = SCNetworkConnectionGetStatus(connection);
432 dispatch_async(q, ^{
433 __SCNetworkConnectionNotify(connection, rlsFunction, nc_status, context_release, context_info);
434 CFRelease(connection);
435 });
436 return;
437 }
438
439
440 static void
441 __SCNetworkConnectionCallBack(void *connection)
442 {
443 boolean_t exec_async = FALSE;
444 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
445 void *context_info;
446 void (*context_release)(const void *);
447 CFRunLoopRef rl = NULL;
448 CFStringRef rl_mode;
449 SCNetworkConnectionCallBack rlsFunction = NULL;
450 dispatch_queue_t q = NULL;
451 SCNetworkConnectionStatus nc_status = kSCNetworkConnectionInvalid;
452
453 pthread_mutex_lock(&connectionPrivate->lock);
454
455 if (!connectionPrivate->scheduled) {
456 // if not currently scheduled
457 pthread_mutex_unlock(&connectionPrivate->lock);
458 return;
459 }
460
461 rlsFunction = connectionPrivate->rlsFunction;
462 if (rlsFunction == NULL) {
463 pthread_mutex_unlock(&connectionPrivate->lock);
464 return;
465 }
466
467 if ((connectionPrivate->rlsContext.retain != NULL) && (connectionPrivate->rlsContext.info != NULL)) {
468 context_info = (void *)(*connectionPrivate->rlsContext.retain)(connectionPrivate->rlsContext.info);
469 context_release = connectionPrivate->rlsContext.release;
470 } else {
471 context_info = connectionPrivate->rlsContext.info;
472 context_release = NULL;
473 }
474
475 #if !TARGET_IPHONE_SIMULATOR
476 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
477 pthread_mutex_unlock(&connectionPrivate->lock);
478
479 nc_status = SCNetworkConnectionGetStatus(connection);
480 __SCNetworkConnectionNotify(connection, rlsFunction, nc_status, context_release, context_info);
481 CFRelease(connection); /* This releases the reference that we took in the NESessionEventStatusChanged event handler */
482 return;
483 }
484 #endif /* !TARGET_IPHONE_SIMULATOR */
485
486 // Do we need to spin a new thread? (either we are running on the main
487 // dispatch queue or main runloop)
488 if (connectionPrivate->rlList == NULL) {
489 // if we are performing the callback on a dispatch queue
490 q = connectionPrivate->dispatchQueue;
491 if (q == dispatch_get_main_queue()) {
492 exec_async = TRUE;
493 }
494 } else {
495 rl = CFRunLoopGetCurrent();
496 if (rl == CFRunLoopGetMain()) {
497 exec_async = TRUE;
498 }
499 }
500
501 CFRetain(connection);
502 pthread_mutex_unlock(&connectionPrivate->lock);
503
504 if (!exec_async) {
505 nc_status = SCNetworkConnectionGetStatus(connection);
506 __SCNetworkConnectionNotify(connection, rlsFunction, nc_status, context_release, context_info);
507 CFRelease(connection);
508 return;
509 }
510
511 if (connectionPrivate->rlList == NULL) {
512 assert(q != NULL);
513 dispatch_retain(q);
514 dispatch_async(__SCNetworkConnectionQueue(), ^{
515 __SCNetworkConnectionCallBackDispatchPerform(connection,
516 q,
517 rlsFunction,
518 context_release,
519 context_info);
520 dispatch_release(q);
521 });
522 } else {
523 assert(rl != NULL);
524 CFRetain(rl);
525 rl_mode = CFRunLoopCopyCurrentMode(rl);
526 dispatch_async(__SCNetworkConnectionQueue(), ^{
527 __SCNetworkConnectionCallBackRunLoopPerform(connection,
528 rl,
529 rl_mode,
530 rlsFunction,
531 context_release,
532 context_info);
533 CFRelease(rl);
534 CFRelease(rl_mode);
535 });
536 }
537
538 return;
539 }
540
541
542 static void
543 __SCNetworkConnectionMachCallBack(CFMachPortRef port, void * msg, CFIndex size, void * info)
544 {
545 mach_no_senders_notification_t *buf = msg;
546 mach_msg_id_t msgid = buf->not_header.msgh_id;
547 SCNetworkConnectionRef connection = (SCNetworkConnectionRef)info;
548
549 if (msgid == MACH_NOTIFY_NO_SENDERS) {
550 // re-establish notification
551 SC_log(LOG_INFO, "PPPController server died");
552 (void)__SCNetworkConnectionReconnectNotifications(connection);
553 }
554
555 __SCNetworkConnectionCallBack(info);
556
557 return;
558 }
559
560
561 #pragma mark -
562 #pragma mark SCNetworkConnection APIs
563
564
565 static CFStringRef
566 pppMPCopyDescription(const void *info)
567 {
568 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)info;
569
570 return CFStringCreateWithFormat(NULL,
571 NULL,
572 CFSTR("<SCNetworkConnection MP %p> {service = %@, callout = %p}"),
573 connectionPrivate,
574 connectionPrivate->service,
575 connectionPrivate->rlsFunction);
576 }
577
578
579 static SCNetworkConnectionPrivateRef
580 __SCNetworkConnectionCreatePrivate(CFAllocatorRef allocator,
581 SCNetworkServiceRef service,
582 SCNetworkConnectionCallBack callout,
583 SCNetworkConnectionContext *context)
584 {
585 SCNetworkConnectionPrivateRef connectionPrivate = NULL;
586 uint32_t size;
587
588
589 /* initialize runtime */
590 pthread_once(&initialized, __SCNetworkConnectionInitialize);
591
592 /* allocate NetworkConnection */
593 size = sizeof(SCNetworkConnectionPrivate) - sizeof(CFRuntimeBase);
594 connectionPrivate = (SCNetworkConnectionPrivateRef)_CFRuntimeCreateInstance(allocator, __kSCNetworkConnectionTypeID, size, NULL);
595 if (connectionPrivate == NULL) {
596 goto fail;
597 }
598
599 /* zero the data structure */
600 bzero(((u_char*)connectionPrivate)+sizeof(CFRuntimeBase), size);
601
602 pthread_mutex_init(&connectionPrivate->lock, NULL);
603
604 /* save the service */
605 if (service != NULL) {
606 connectionPrivate->service = CFRetain(service);
607 }
608
609 connectionPrivate->client_audit_session = MACH_PORT_NULL;
610 connectionPrivate->client_bootstrap_port = MACH_PORT_NULL;
611 connectionPrivate->client_uid = geteuid();
612 connectionPrivate->client_gid = getegid();
613 connectionPrivate->client_pid = getpid();
614 connectionPrivate->client_bundle_id = NULL;
615 uuid_clear(connectionPrivate->client_uuid);
616
617 connectionPrivate->rlsFunction = callout;
618
619 if (context) {
620 bcopy(context, &connectionPrivate->rlsContext, sizeof(SCNetworkConnectionContext));
621 if (context->retain != NULL) {
622 connectionPrivate->rlsContext.info = (void *)(*context->retain)(context->info);
623 }
624 }
625
626 connectionPrivate->type = kSCNetworkConnectionTypeUnknown;
627
628 #if !TARGET_IPHONE_SIMULATOR
629 if (__SCNetworkConnectionUseNetworkExtension(connectionPrivate)) {
630 CFStringRef serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service);
631 if (serviceID != NULL) {
632 uuid_string_t service_uuid_str;
633 if (CFStringGetCString(serviceID, service_uuid_str, sizeof(service_uuid_str), kCFStringEncodingUTF8)) {
634 uuid_t config_id;
635 if (uuid_parse(service_uuid_str, config_id) == 0) {
636 connectionPrivate->ne_session = ne_session_create(config_id, NESessionTypeVPN);
637 }
638 }
639 }
640
641 if (connectionPrivate->ne_session == NULL) {
642 SC_log(LOG_NOTICE,
643 "SCNetworkConnection failed to create an ne_session: service ID %@ is not a valid UUID",
644 serviceID);
645 goto fail;
646 }
647 }
648 #endif /* !TARGET_IPHONE_SIMULATOR */
649
650 /* success, return the connection reference */
651 return connectionPrivate;
652
653 fail:
654
655 /* failure, clean up and leave */
656 if (connectionPrivate != NULL) {
657 CFRelease(connectionPrivate);
658 }
659
660 _SCErrorSet(kSCStatusFailed);
661 return NULL;
662 }
663
664
665 static mach_port_t
666 __SCNetworkConnectionServerPort(kern_return_t *status)
667 {
668 mach_port_t server = MACH_PORT_NULL;
669
670 #ifdef BOOTSTRAP_PRIVILEGED_SERVER
671 *status = bootstrap_look_up2(bootstrap_port,
672 __SCNetworkConnectionGetControllerPortName(),
673 &server,
674 0,
675 BOOTSTRAP_PRIVILEGED_SERVER);
676 #else // BOOTSTRAP_PRIVILEGED_SERVER
677 *status = bootstrap_look_up(bootstrap_port, __SCNetworkConnectionGetControllerPortName(), &server);
678 #endif // BOOTSTRAP_PRIVILEGED_SERVER
679
680 switch (*status) {
681 case BOOTSTRAP_SUCCESS :
682 // service currently registered, "a good thing" (tm)
683 return server;
684 case BOOTSTRAP_NOT_PRIVILEGED :
685 // the service is not privileged
686 break;
687 case BOOTSTRAP_UNKNOWN_SERVICE :
688 // service not currently registered, try again later
689 break;
690 default :
691 #ifdef DEBUG
692 SC_log(LOG_DEBUG, "bootstrap_look_up() failed: status=%s",
693 bootstrap_strerror(*status));
694 #endif // DEBUG
695 break;
696 }
697
698 scnc_server_name = NULL; /* reset pppcontroller server */
699 return MACH_PORT_NULL;
700 }
701
702 static mach_port_t
703 __SCNetworkConnectionGetCurrentServerPort(void)
704 {
705 return scnc_server;
706 }
707
708 static mach_port_t
709 __SCNetworkConnectionRefreshServerPort(mach_port_t current_server, int *mach_result)
710 {
711 mach_port_t new_server;
712
713 pthread_mutex_lock(&scnc_lock);
714 if (scnc_server != MACH_PORT_NULL) {
715 if (current_server == scnc_server) {
716 scnc_server_name = NULL;
717 // if the server we tried returned the error
718 (void)mach_port_deallocate(mach_task_self(), scnc_server);
719 scnc_server = __SCNetworkConnectionServerPort(mach_result);
720 } else {
721 // another thread has refreshed the server port
722 }
723 } else {
724 scnc_server = __SCNetworkConnectionServerPort(mach_result);
725 }
726 new_server = scnc_server;
727 pthread_mutex_unlock(&scnc_lock);
728
729 return new_server;
730 }
731
732 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && (!TARGET_IPHONE_SIMULATOR || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 70000))
733 #define HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
734 #endif
735
736
737 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && !TARGET_IPHONE_SIMULATOR
738 #define HAVE_PPPCONTROLLER_ATTACHWITHPROXY
739 #endif
740
741 static mach_port_t
742 __SCNetworkConnectionSessionPort(SCNetworkConnectionPrivateRef connectionPrivate)
743 {
744 void *data = NULL;
745 CFIndex dataLen = 0;
746 CFDataRef dataRef = NULL;
747 mach_port_t notify_port = MACH_PORT_NULL;
748 mach_port_t oldNotify = MACH_PORT_NULL;
749 int retry = 0;
750 int sc_status = kSCStatusFailed;
751 mach_port_t server = __SCNetworkConnectionGetCurrentServerPort();
752 kern_return_t status = KERN_SUCCESS;
753
754 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
755 mach_port_t au_session = MACH_PORT_NULL;
756 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
757
758 if (connectionPrivate->session_port != MACH_PORT_NULL) {
759 return connectionPrivate->session_port;
760 }
761
762 if (connectionPrivate->service == NULL) {
763 sc_status = kSCStatusConnectionNoService;
764 goto done;
765 }
766
767 if (!_SCSerializeString(SCNetworkServiceGetServiceID(connectionPrivate->service), &dataRef, &data, &dataLen)) {
768 goto done;
769 }
770
771 if (connectionPrivate->notify_port != NULL) {
772 mach_port_t mp = CFMachPortGetPort(connectionPrivate->notify_port);
773
774 __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionSessionPort mp", mp);
775 CFMachPortInvalidate(connectionPrivate->notify_port);
776 CFRelease(connectionPrivate->notify_port);
777 connectionPrivate->notify_port = NULL;
778 mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1);
779 }
780
781 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
782 au_session = audit_session_self();
783 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
784
785 // open a new session with the server
786 while (TRUE) {
787 if ((connectionPrivate->rlsFunction != NULL) && (notify_port == MACH_PORT_NULL)) {
788 // allocate port (for server response)
789 status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &notify_port);
790 if (status != KERN_SUCCESS) {
791 SC_log(LOG_ERR, "mach_port_allocate() failed: %s", mach_error_string(status));
792 sc_status = status;
793 goto done;
794 }
795
796 // add send right (passed to the server)
797 status = mach_port_insert_right(mach_task_self(),
798 notify_port,
799 notify_port,
800 MACH_MSG_TYPE_MAKE_SEND);
801 if (status != KERN_SUCCESS) {
802 SC_log(LOG_NOTICE, "mach_port_insert_right() failed: %s", mach_error_string(status));
803 mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
804 sc_status = status;
805 goto done;
806 }
807 }
808
809 if (server != MACH_PORT_NULL) {
810 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHPROXY
811 if ((connectionPrivate->client_audit_session == MACH_PORT_NULL) &&
812 (connectionPrivate->client_bootstrap_port == MACH_PORT_NULL) &&
813 (connectionPrivate->client_uid == geteuid()) &&
814 (connectionPrivate->client_gid == getegid()) &&
815 (connectionPrivate->client_pid == getpid())
816 ) {
817 #endif // HAVE_PPPCONTROLLER_ATTACHWITHPROXY
818 status = pppcontroller_attach(server,
819 data,
820 (mach_msg_type_number_t)dataLen,
821 bootstrap_port,
822 notify_port,
823 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
824 au_session,
825 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
826 &connectionPrivate->session_port,
827 &sc_status);
828 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHPROXY
829 } else {
830 mach_port_t client_au_session;
831 mach_port_t client_bootstrap_port;
832
833 if (connectionPrivate->client_audit_session == MACH_PORT_NULL) {
834 client_au_session = au_session;
835 } else {
836 client_au_session = connectionPrivate->client_audit_session;
837 }
838
839 if (connectionPrivate->client_bootstrap_port == MACH_PORT_NULL) {
840 client_bootstrap_port = bootstrap_port;
841 } else {
842 client_bootstrap_port = connectionPrivate->client_bootstrap_port;
843 }
844
845 status = pppcontroller_attach_proxy(server,
846 data,
847 (mach_msg_type_number_t)dataLen,
848 client_bootstrap_port,
849 notify_port,
850 client_au_session,
851 connectionPrivate->client_uid,
852 connectionPrivate->client_gid,
853 connectionPrivate->client_pid,
854 &connectionPrivate->session_port,
855 &sc_status);
856 }
857 #endif // HAVE_PPPCONTROLLER_ATTACHWITHPROXY
858 if (status == KERN_SUCCESS) {
859 if (sc_status != kSCStatusOK) {
860 SC_log(LOG_DEBUG, "attach w/error, sc_status=%s%s",
861 SCErrorString(sc_status),
862 (connectionPrivate->session_port != MACH_PORT_NULL) ? ", w/session_port!=MACH_PORT_NULL" : "");
863
864 if (connectionPrivate->session_port != MACH_PORT_NULL) {
865 __MACH_PORT_DEBUG(TRUE,
866 "*** __SCNetworkConnectionSessionPort session_port (attach w/error, cleanup)",
867 connectionPrivate->session_port);
868 mach_port_deallocate(mach_task_self(), connectionPrivate->session_port);
869 connectionPrivate->session_port = MACH_PORT_NULL;
870 }
871
872 if (notify_port != MACH_PORT_NULL) {
873 __MACH_PORT_DEBUG(TRUE,
874 "*** __SCNetworkConnectionSessionPort notify_port (attach w/error, cleanup)",
875 notify_port);
876 (void) mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
877 notify_port = MACH_PORT_NULL;
878 }
879 }
880 break;
881 }
882
883 // our [cached] server port is not valid
884 SC_log(LOG_INFO, "!attach: %s", SCErrorString(status));
885 if (status == MACH_SEND_INVALID_DEST) {
886 // the server is not yet available
887 __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionSessionPort notify_port (!dest)", notify_port);
888 } else if (status == MIG_SERVER_DIED) {
889 __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionSessionPort notify_port (!mig)", notify_port);
890 // the server we were using is gone and we've lost our send right
891 mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
892 notify_port = MACH_PORT_NULL;
893 } else {
894 // if we got an unexpected error, don't retry
895 sc_status = status;
896 break;
897 }
898 }
899
900 server = __SCNetworkConnectionRefreshServerPort(server, &sc_status);
901 if (server == MACH_PORT_NULL) {
902 // if server not available
903 if (sc_status == BOOTSTRAP_UNKNOWN_SERVICE) {
904 // if first retry attempt, wait for SCDynamicStore server
905 if (retry == 0) {
906 SCDynamicStoreRef store;
907
908 store = SCDynamicStoreCreate(NULL,
909 CFSTR("SCNetworkConnection connect"),
910 NULL,
911 NULL);
912 if (store != NULL) {
913 CFRelease(store);
914 }
915 }
916
917 // wait up to 2.5 seconds for the [SCNetworkConnection] server
918 // to startup
919 if ((retry += 50) < 2500) {
920 usleep(50 * 1000); // sleep 50ms between attempts
921 continue;
922 }
923 }
924 break;
925 }
926 }
927
928 if (notify_port != MACH_PORT_NULL) {
929 if (connectionPrivate->session_port != MACH_PORT_NULL) {
930 CFMachPortContext context = { 0
931 , (void *)connectionPrivate
932 , NULL
933 , NULL
934 , pppMPCopyDescription
935 };
936
937 // request a notification when/if the server dies
938 status = mach_port_request_notification(mach_task_self(),
939 notify_port,
940 MACH_NOTIFY_NO_SENDERS,
941 1,
942 notify_port,
943 MACH_MSG_TYPE_MAKE_SEND_ONCE,
944 &oldNotify);
945 if (status != KERN_SUCCESS) {
946 SC_log(LOG_NOTICE, "mach_port_request_notification() failed: %s", mach_error_string(status));
947 mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
948 sc_status = status;
949 goto done;
950 }
951
952 if (oldNotify != MACH_PORT_NULL) {
953 SC_log(LOG_NOTICE, "oldNotify != MACH_PORT_NULL");
954 }
955
956 // create CFMachPort for SCNetworkConnection notification callback
957 connectionPrivate->notify_port = _SC_CFMachPortCreateWithPort("SCNetworkConnection",
958 notify_port,
959 __SCNetworkConnectionMachCallBack,
960 &context);
961
962 // we need to try a bit harder to acquire the initial status
963 connectionPrivate->haveStatus = FALSE;
964 } else {
965 // with no server port, release the notification port we allocated
966 __MACH_PORT_DEBUG(TRUE,
967 "*** __SCNetworkConnectionSessionPort notify_port (!server)",
968 notify_port);
969 (void) mach_port_mod_refs (mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
970 (void) mach_port_deallocate(mach_task_self(), notify_port);
971 notify_port = MACH_PORT_NULL;
972 }
973 }
974
975 done :
976
977 // clean up
978
979 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
980 if (au_session != MACH_PORT_NULL) {
981 (void)mach_port_deallocate(mach_task_self(), au_session);
982 }
983 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
984
985 if (dataRef != NULL) CFRelease(dataRef);
986
987 switch (sc_status) {
988 case kSCStatusOK :
989 __MACH_PORT_DEBUG(connectionPrivate->session_port != MACH_PORT_NULL,
990 "*** __SCNetworkConnectionSessionPort session_port",
991 connectionPrivate->session_port);
992 __MACH_PORT_DEBUG(notify_port != MACH_PORT_NULL,
993 "*** __SCNetworkConnectionSessionPort notify_port",
994 notify_port);
995 break;
996 case BOOTSTRAP_UNKNOWN_SERVICE :
997 SC_log((status == KERN_SUCCESS) ? LOG_NOTICE : LOG_ERR, "PPPController not available");
998 break;
999 default :
1000 SC_log((status == KERN_SUCCESS) ? LOG_NOTICE : LOG_ERR, "pppcontroller_attach() failed: %s",
1001 SCErrorString(sc_status));
1002 break;
1003 }
1004
1005 if (sc_status != kSCStatusOK) {
1006 _SCErrorSet(sc_status);
1007 }
1008
1009 return connectionPrivate->session_port;
1010 }
1011
1012
1013 static Boolean
1014 __SCNetworkConnectionReconnect(SCNetworkConnectionRef connection)
1015 {
1016 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1017 mach_port_t port;
1018
1019 port = __SCNetworkConnectionSessionPort(connectionPrivate);
1020 return (port != MACH_PORT_NULL);
1021 }
1022
1023
1024 static Boolean
1025 __SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection)
1026 {
1027 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1028 dispatch_group_t dispatchGroup = NULL;
1029 dispatch_queue_t dispatchQueue = NULL;
1030 Boolean ok = TRUE;
1031 CFArrayRef rlList = NULL;
1032
1033 // Before we fully tearing down our [old] notifications, make sure
1034 // we have retained any information that is needed to re-register the
1035 // [new] notifications.
1036
1037 pthread_mutex_lock(&connectionPrivate->lock);
1038
1039 // save and cancel [old] notifications
1040 if (connectionPrivate->rlList != NULL) {
1041 rlList = connectionPrivate->rlList;
1042 connectionPrivate->rlList = NULL;
1043 }
1044 if (connectionPrivate->rls != NULL) {
1045 CFRunLoopSourceInvalidate(connectionPrivate->rls);
1046 CFRelease(connectionPrivate->rls);
1047 connectionPrivate->rls = NULL;
1048 }
1049 if (connectionPrivate->dispatchSource != NULL) {
1050 dispatch_source_cancel(connectionPrivate->dispatchSource);
1051 connectionPrivate->dispatchSource = NULL;
1052 }
1053
1054 // make sure dispatchSource is cancelled before removing group/queue
1055 if (connectionPrivate->dispatchQueue != NULL) {
1056 // save dispatchQueue, release reference when we've queue'd blocks
1057 // complete, allow re-scheduling
1058 dispatchGroup = connectionPrivate->dispatchGroup;
1059 connectionPrivate->dispatchGroup = NULL;
1060 dispatchQueue = connectionPrivate->dispatchQueue;
1061 connectionPrivate->dispatchQueue = NULL;
1062
1063 // and take an extra reference for rescheduling
1064 dispatch_retain(dispatchQueue);
1065 }
1066
1067 connectionPrivate->scheduled = FALSE;
1068
1069 pthread_mutex_unlock(&connectionPrivate->lock);
1070
1071 if (dispatchGroup != NULL) {
1072 dispatch_group_notify(dispatchGroup, dispatchQueue, ^{
1073 // release group/queue references
1074 dispatch_release(dispatchQueue);
1075 dispatch_release(dispatchGroup); // releases our connection reference
1076 });
1077 }
1078
1079 // re-schedule
1080 if (rlList != NULL) {
1081 CFIndex i;
1082 CFIndex n;
1083
1084 n = CFArrayGetCount(rlList);
1085 for (i = 0; i < n; i += 3) {
1086 CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(rlList, i+1);
1087 CFStringRef rlMode = (CFStringRef) CFArrayGetValueAtIndex(rlList, i+2);
1088
1089 ok = SCNetworkConnectionScheduleWithRunLoop(connection, rl, rlMode);
1090 if (!ok) {
1091 if (SCError() != BOOTSTRAP_UNKNOWN_SERVICE) {
1092 SC_log(LOG_NOTICE, "SCNetworkConnectionScheduleWithRunLoop() failed");
1093 }
1094 goto done;
1095 }
1096 }
1097 } else if (dispatchQueue != NULL) {
1098 ok = SCNetworkConnectionSetDispatchQueue(connection, dispatchQueue);
1099 if (!ok) {
1100 if (SCError() != BOOTSTRAP_UNKNOWN_SERVICE) {
1101 SC_log(LOG_NOTICE, "SCNetworkConnectionSetDispatchQueue() failed");
1102 }
1103 goto done;
1104 }
1105 } else {
1106 ok = FALSE;
1107 }
1108
1109 done :
1110
1111 // cleanup
1112 if (rlList != NULL) {
1113 CFRelease(rlList);
1114 }
1115 if (dispatchQueue != NULL) {
1116 dispatch_release(dispatchQueue);
1117 }
1118
1119 if (!ok) {
1120 SC_log(LOG_NOTICE, "SCNetworkConnection server %s, notification not restored",
1121 (SCError() == BOOTSTRAP_UNKNOWN_SERVICE) ? "shutdown" : "failed");
1122 }
1123
1124 return ok;
1125 }
1126
1127
1128 static Boolean
1129 __SCNetworkConnectionNeedsRetry(SCNetworkConnectionRef connection,
1130 const char *error_label,
1131 kern_return_t status,
1132 int *sc_status)
1133 {
1134 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1135
1136 if (status == KERN_SUCCESS) {
1137 return FALSE;
1138 }
1139
1140 if ((status == MACH_SEND_INVALID_DEST) || (status == MIG_SERVER_DIED)) {
1141 // the server's gone and our session port's dead, remove the dead name right
1142 (void) mach_port_deallocate(mach_task_self(), connectionPrivate->session_port);
1143 } else {
1144 // we got an unexpected error, leave the [session] port alone
1145 SC_log(LOG_NOTICE, "%s: %s", error_label, mach_error_string(status));
1146 }
1147 connectionPrivate->session_port = MACH_PORT_NULL;
1148 if ((status == MACH_SEND_INVALID_DEST) || (status == MIG_SERVER_DIED)) {
1149 if (__SCNetworkConnectionReconnect(connection)) {
1150 return TRUE;
1151 }
1152 }
1153 *sc_status = status;
1154
1155 return FALSE;
1156 }
1157
1158
1159 CFTypeID
1160 SCNetworkConnectionGetTypeID(void) {
1161 pthread_once(&initialized, __SCNetworkConnectionInitialize); /* initialize runtime */
1162 return __kSCNetworkConnectionTypeID;
1163 }
1164
1165
1166 CFArrayRef /* of SCNetworkServiceRef's */
1167 SCNetworkConnectionCopyAvailableServices(SCNetworkSetRef set)
1168 {
1169 CFMutableArrayRef available;
1170 Boolean tempSet = FALSE;
1171
1172 if (set == NULL) {
1173 SCPreferencesRef prefs;
1174
1175 prefs = SCPreferencesCreate(NULL, CFSTR("SCNetworkConnectionCopyAvailableServices"), NULL);
1176 if (prefs != NULL) {
1177 set = SCNetworkSetCopyCurrent(prefs);
1178 CFRelease(prefs);
1179 }
1180 tempSet = TRUE;
1181 }
1182
1183 available = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1184
1185 if (set != NULL) {
1186 CFArrayRef services;
1187
1188 services = SCNetworkSetCopyServices(set);
1189 if (services != NULL) {
1190 CFIndex i;
1191 CFIndex n;
1192
1193 n = CFArrayGetCount(services);
1194 for (i = 0; i < n; i++) {
1195 SCNetworkInterfaceRef interface;
1196 CFStringRef interfaceType;
1197 SCNetworkServiceRef service;
1198
1199 service = CFArrayGetValueAtIndex(services, i);
1200 interface = SCNetworkServiceGetInterface(service);
1201 if (interface == NULL) {
1202 continue;
1203 }
1204
1205 interfaceType = SCNetworkInterfaceGetInterfaceType(interface);
1206 if (CFEqual(interfaceType, kSCNetworkInterfaceTypePPP) ||
1207 CFEqual(interfaceType, kSCNetworkInterfaceTypeVPN) ||
1208 CFEqual(interfaceType, kSCNetworkInterfaceTypeIPSec)) {
1209 CFArrayAppendValue(available, service);
1210 }
1211 }
1212
1213 CFRelease(services);
1214 }
1215 }
1216
1217 if (tempSet && (set != NULL)) {
1218 CFRelease(set);
1219 }
1220 return available;
1221 }
1222
1223
1224 SCNetworkConnectionRef
1225 SCNetworkConnectionCreateWithService(CFAllocatorRef allocator,
1226 SCNetworkServiceRef service,
1227 SCNetworkConnectionCallBack callout,
1228 SCNetworkConnectionContext *context)
1229 {
1230 SCNetworkConnectionPrivateRef connectionPrivate;
1231
1232 if (!isA_SCNetworkService(service)) {
1233 _SCErrorSet(kSCStatusInvalidArgument);
1234 return FALSE;
1235 }
1236
1237 connectionPrivate = __SCNetworkConnectionCreatePrivate(allocator, service, callout, context);
1238 return (SCNetworkConnectionRef)connectionPrivate;
1239 }
1240
1241
1242 SCNetworkConnectionRef
1243 SCNetworkConnectionCreateWithServiceID(CFAllocatorRef allocator,
1244 CFStringRef serviceID,
1245 SCNetworkConnectionCallBack callout,
1246 SCNetworkConnectionContext *context)
1247 {
1248 SCNetworkConnectionRef connection;
1249 SCNetworkServiceRef service;
1250
1251 if (!isA_CFString(serviceID)) {
1252 _SCErrorSet(kSCStatusInvalidArgument);
1253 return NULL;
1254 }
1255
1256 service = _SCNetworkServiceCopyActive(NULL, serviceID);
1257 if (service == NULL) {
1258 return NULL;
1259 }
1260
1261 connection = SCNetworkConnectionCreateWithService(allocator, service, callout, context);
1262 CFRelease(service);
1263
1264 return connection;
1265 }
1266
1267
1268 SCNetworkConnectionRef
1269 SCNetworkConnectionCreate(CFAllocatorRef allocator,
1270 SCNetworkConnectionCallBack callout,
1271 SCNetworkConnectionContext *context)
1272 {
1273 SCNetworkConnectionPrivateRef connectionPrivate = __SCNetworkConnectionCreatePrivate(allocator, NULL, callout, context);
1274 return (SCNetworkConnectionRef)connectionPrivate;
1275 }
1276
1277
1278 CFStringRef
1279 SCNetworkConnectionCopyServiceID(SCNetworkConnectionRef connection)
1280 {
1281 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1282 CFStringRef serviceID;
1283
1284 if (!isA_SCNetworkConnection(connection)) {
1285 _SCErrorSet(kSCStatusInvalidArgument);
1286 return NULL;
1287 }
1288
1289 if (connectionPrivate->service == NULL) {
1290 _SCErrorSet(kSCStatusConnectionNoService);
1291 return NULL;
1292 }
1293
1294 serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service);
1295 return CFRetain(serviceID);
1296 }
1297
1298
1299 Boolean
1300 SCNetworkConnectionSetClientInfo(SCNetworkConnectionRef connection,
1301 mach_port_t client_audit_session,
1302 uid_t client_uid,
1303 gid_t client_gid,
1304 pid_t client_pid)
1305 {
1306 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1307
1308 if (!isA_SCNetworkConnection(connection)) {
1309 _SCErrorSet(kSCStatusInvalidArgument);
1310 return FALSE;
1311 }
1312
1313 // save client audit session port
1314 if (connectionPrivate->client_audit_session != MACH_PORT_NULL) {
1315 mach_port_mod_refs(mach_task_self(),
1316 connectionPrivate->client_audit_session,
1317 MACH_PORT_RIGHT_SEND,
1318 -1);
1319 connectionPrivate->client_audit_session = MACH_PORT_NULL;
1320 }
1321 connectionPrivate->client_audit_session = client_audit_session;
1322 if (connectionPrivate->client_audit_session != MACH_PORT_NULL) {
1323 mach_port_mod_refs(mach_task_self(),
1324 connectionPrivate->client_audit_session,
1325 MACH_PORT_RIGHT_SEND,
1326 1);
1327 }
1328
1329 // save client UID, GID, and PID
1330 connectionPrivate->client_uid = client_uid;
1331 connectionPrivate->client_gid = client_gid;
1332 connectionPrivate->client_pid = client_pid;
1333
1334 return TRUE;
1335 }
1336
1337
1338 Boolean
1339 SCNetworkConnectionSetClientAuditInfo(SCNetworkConnectionRef connection,
1340 audit_token_t client_audit_token,
1341 mach_port_t audit_session,
1342 mach_port_t bootstrap_port,
1343 pid_t client_pid,
1344 const uuid_t uuid,
1345 const char *bundle_id)
1346 {
1347 const audit_token_t null_audit = KERNEL_AUDIT_TOKEN_VALUE;
1348 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1349 gid_t gid = 0;
1350 pid_t pid = 0;
1351 uid_t uid = 0;
1352
1353 if (memcmp(&client_audit_token, &null_audit, sizeof(client_audit_token))) {
1354 #if TARGET_OS_IPHONE
1355 audit_token_to_au32(client_audit_token, NULL, &uid, &gid, NULL, NULL, &pid, NULL, NULL);
1356 #else // TARGET_OS_IPHONE
1357 uid = audit_token_to_euid(client_audit_token);
1358 gid = audit_token_to_egid(client_audit_token);
1359 pid = audit_token_to_pid(client_audit_token);
1360 #endif // TARGET_OS_IPHONE
1361 } else {
1362 pid = client_pid;
1363 }
1364
1365 if (!SCNetworkConnectionSetClientInfo(connection, audit_session, uid, gid, pid)) {
1366 return FALSE;
1367 }
1368
1369 if (connectionPrivate->client_bootstrap_port != MACH_PORT_NULL) {
1370 mach_port_mod_refs(mach_task_self(),
1371 connectionPrivate->client_bootstrap_port,
1372 MACH_PORT_RIGHT_SEND,
1373 -1);
1374 connectionPrivate->client_bootstrap_port = MACH_PORT_NULL;
1375 }
1376
1377 connectionPrivate->client_bootstrap_port = bootstrap_port;
1378 if (connectionPrivate->client_bootstrap_port != MACH_PORT_NULL) {
1379 mach_port_mod_refs(mach_task_self(),
1380 connectionPrivate->client_bootstrap_port,
1381 MACH_PORT_RIGHT_SEND,
1382 1);
1383 }
1384
1385 memcpy(&connectionPrivate->client_audit_token, &client_audit_token, sizeof(connectionPrivate->client_audit_token));
1386
1387 if (uuid != NULL && !uuid_is_null(uuid)) {
1388 uuid_copy(connectionPrivate->client_uuid, uuid);
1389 }
1390
1391 if (connectionPrivate->client_bundle_id != NULL) {
1392 CFRelease(connectionPrivate->client_bundle_id);
1393 connectionPrivate->client_bundle_id = NULL;
1394 }
1395
1396 if (bundle_id != NULL) {
1397 connectionPrivate->client_bundle_id = CFStringCreateWithCString(kCFAllocatorDefault, bundle_id, kCFStringEncodingUTF8);
1398 }
1399
1400 return TRUE;
1401 }
1402
1403
1404 CFDictionaryRef
1405 SCNetworkConnectionCopyStatistics(SCNetworkConnectionRef connection)
1406 {
1407 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1408 xmlDataOut_t data = NULL;
1409 mach_msg_type_number_t datalen = 0;
1410 int sc_status = kSCStatusFailed;
1411 mach_port_t session_port;
1412 CFPropertyListRef statistics = NULL;
1413 kern_return_t status;
1414
1415 if (!isA_SCNetworkConnection(connection)) {
1416 _SCErrorSet(kSCStatusInvalidArgument);
1417 return NULL;
1418 }
1419
1420 pthread_mutex_lock(&connectionPrivate->lock);
1421
1422 #if !TARGET_IPHONE_SIMULATOR
1423 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
1424 __block xpc_object_t xstats = NULL;
1425 ne_session_t ne_session = connectionPrivate->ne_session;
1426
1427 ne_session_retain(ne_session);
1428 pthread_mutex_unlock(&connectionPrivate->lock);
1429
1430 dispatch_semaphore_t ne_sema = dispatch_semaphore_create(0);
1431 ne_session_get_info(ne_session, NESessionInfoTypeStatistics, __SCNetworkConnectionQueue(), ^(xpc_object_t result) {
1432 if (result != NULL) {
1433 xstats = xpc_retain(result);
1434 }
1435 ne_session_release(ne_session);
1436 dispatch_semaphore_signal(ne_sema);
1437 });
1438 dispatch_semaphore_wait(ne_sema, DISPATCH_TIME_FOREVER);
1439 dispatch_release(ne_sema);
1440
1441 if (xstats != NULL) {
1442 statistics = _CFXPCCreateCFObjectFromXPCObject(xstats);
1443 xpc_release(xstats);
1444 } else {
1445 _SCErrorSet(kSCStatusFailed);
1446 }
1447
1448 return statistics;
1449 }
1450 #endif /* !TARGET_IPHONE_SIMULATOR */
1451
1452 retry :
1453
1454 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1455 if (session_port == MACH_PORT_NULL) {
1456 goto done;
1457 }
1458
1459 status = pppcontroller_copystatistics(session_port, &data, &datalen, &sc_status);
1460 if (__SCNetworkConnectionNeedsRetry(connection,
1461 "SCNetworkConnectionCopyStatistics()",
1462 status,
1463 &sc_status)) {
1464 goto retry;
1465 }
1466
1467 if (data != NULL) {
1468 if (!_SCUnserialize(&statistics, NULL, data, datalen)) {
1469 if (sc_status != kSCStatusOK) sc_status = SCError();
1470 }
1471 if ((sc_status == kSCStatusOK) && !isA_CFDictionary(statistics)) {
1472 sc_status = kSCStatusFailed;
1473 }
1474 }
1475
1476 if (sc_status != kSCStatusOK) {
1477 if (statistics != NULL) {
1478 CFRelease(statistics);
1479 statistics = NULL;
1480 }
1481 _SCErrorSet(sc_status);
1482 }
1483
1484 done :
1485
1486 pthread_mutex_unlock(&connectionPrivate->lock);
1487 return statistics;
1488 }
1489
1490
1491 SCNetworkServiceRef
1492 SCNetworkConnectionGetService(SCNetworkConnectionRef connection)
1493 {
1494 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1495
1496 if (!isA_SCNetworkConnection(connection)) {
1497 _SCErrorSet(kSCStatusInvalidArgument);
1498 return NULL;
1499 }
1500
1501 return connectionPrivate->service;
1502 }
1503
1504
1505 SCNetworkConnectionStatus
1506 SCNetworkConnectionGetStatus(SCNetworkConnectionRef connection)
1507 {
1508 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1509 SCNetworkConnectionStatus nc_status = kSCNetworkConnectionInvalid;
1510 int retry = 0;
1511 int sc_status = kSCStatusFailed;
1512 mach_port_t session_port;
1513 kern_return_t status;
1514 CFStringRef serviceID;
1515
1516 if (!isA_SCNetworkConnection(connection)) {
1517 _SCErrorSet(kSCStatusInvalidArgument);
1518 return kSCNetworkConnectionInvalid;
1519 }
1520
1521 if (connectionPrivate->service == NULL) {
1522 _SCErrorSet(kSCStatusConnectionNoService);
1523 return kSCNetworkConnectionInvalid;
1524 }
1525
1526 // skip retry and return immediately if we know no service is to be found.
1527 serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service);
1528 if (CFStringGetLength(serviceID) == 0) {
1529 _SCErrorSet(kSCStatusConnectionNoService);
1530 return kSCNetworkConnectionInvalid;
1531 }
1532
1533 pthread_mutex_lock(&connectionPrivate->lock);
1534
1535 #if !TARGET_IPHONE_SIMULATOR
1536 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
1537 __block ne_session_status_t ne_status;
1538 ne_session_t ne_session = connectionPrivate->ne_session;
1539
1540 ne_session_retain(ne_session);
1541 pthread_mutex_unlock(&connectionPrivate->lock);
1542
1543 dispatch_semaphore_t ne_sema = dispatch_semaphore_create(0);
1544 ne_session_get_status(ne_session, __SCNetworkConnectionQueue(), ^(ne_session_status_t status) {
1545 ne_status = status;
1546 ne_session_release(ne_session);
1547 dispatch_semaphore_signal(ne_sema);
1548 });
1549 dispatch_semaphore_wait(ne_sema, DISPATCH_TIME_FOREVER);
1550 dispatch_release(ne_sema);
1551
1552 return SCNetworkConnectionGetStatusFromNEStatus(ne_status);
1553 }
1554 #endif /* !TARGET_IPHONE_SIMULATOR */
1555
1556 retry :
1557
1558 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1559 if (session_port == MACH_PORT_NULL) {
1560 nc_status = kSCNetworkConnectionInvalid;
1561 goto done;
1562 }
1563
1564 status = pppcontroller_getstatus(session_port, &nc_status, &sc_status);
1565 if (__SCNetworkConnectionNeedsRetry(connection,
1566 "SCNetworkConnectionGetStatus()",
1567 status,
1568 &sc_status)) {
1569 goto retry;
1570 }
1571
1572 // wait up to 250 ms for the network service to become available
1573 if (!connectionPrivate->haveStatus &&
1574 (sc_status == kSCStatusConnectionNoService) &&
1575 ((retry += 10) < 250)) {
1576 usleep(10 * 1000); // sleep 10ms between attempts
1577 goto retry;
1578 }
1579
1580 if (sc_status == kSCStatusOK) {
1581 connectionPrivate->haveStatus = TRUE;
1582 } else {
1583 _SCErrorSet(sc_status);
1584 nc_status = kSCNetworkConnectionInvalid;
1585 }
1586
1587 done :
1588
1589 pthread_mutex_unlock(&connectionPrivate->lock);
1590 return nc_status;
1591 }
1592
1593
1594 CFDictionaryRef
1595 SCNetworkConnectionCopyExtendedStatus(SCNetworkConnectionRef connection)
1596 {
1597 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1598 xmlDataOut_t data = NULL;
1599 mach_msg_type_number_t datalen = 0;
1600 CFPropertyListRef extstatus = NULL;
1601 int retry = 0;
1602 int sc_status = kSCStatusFailed;
1603 mach_port_t session_port;
1604 kern_return_t status;
1605 CFStringRef serviceID;
1606
1607 if (!isA_SCNetworkConnection(connection)) {
1608 _SCErrorSet(kSCStatusInvalidArgument);
1609 return NULL;
1610 }
1611
1612 if (connectionPrivate->service == NULL) {
1613 _SCErrorSet(kSCStatusConnectionNoService);
1614 return NULL;
1615 }
1616
1617 // skip retry and return immediately if we know no service is to be found.
1618 serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service);
1619 if (CFStringGetLength(serviceID) == 0) {
1620 _SCErrorSet(kSCStatusConnectionNoService);
1621 return NULL;
1622 }
1623
1624 pthread_mutex_lock(&connectionPrivate->lock);
1625
1626 #if !TARGET_IPHONE_SIMULATOR
1627 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
1628 __block CFDictionaryRef statusDictionary = NULL;
1629 ne_session_t ne_session = connectionPrivate->ne_session;
1630
1631 ne_session_retain(ne_session);
1632 pthread_mutex_unlock(&connectionPrivate->lock);
1633
1634 dispatch_semaphore_t ne_sema = dispatch_semaphore_create(0);
1635 ne_session_get_info(ne_session, NESessionInfoTypeExtendedStatus, __SCNetworkConnectionQueue(), ^(xpc_object_t extended_status) {
1636 if (extended_status != NULL) {
1637 statusDictionary = _CFXPCCreateCFObjectFromXPCObject(extended_status);
1638 ne_session_release(ne_session);
1639 dispatch_semaphore_signal(ne_sema);
1640 } else {
1641 ne_session_get_status(ne_session, __SCNetworkConnectionQueue(), ^(ne_session_status_t ne_status) {
1642 SCNetworkConnectionStatus status = SCNetworkConnectionGetStatusFromNEStatus(ne_status);
1643 if (status != kSCNetworkConnectionInvalid) {
1644 CFStringRef keys[1] = { kSCNetworkConnectionStatus };
1645 CFNumberRef values[1] = { NULL };
1646 values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &status);
1647 statusDictionary = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void **)values, sizeof(values) / sizeof(values[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1648 CFRelease(values[0]);
1649 }
1650 ne_session_release(ne_session);
1651 dispatch_semaphore_signal(ne_sema);
1652 });
1653 }
1654 });
1655 dispatch_semaphore_wait(ne_sema, DISPATCH_TIME_FOREVER);
1656 dispatch_release(ne_sema);
1657
1658 if (statusDictionary != NULL) {
1659 extstatus = (CFPropertyListRef)statusDictionary;
1660 } else {
1661 _SCErrorSet(kSCStatusFailed);
1662 }
1663
1664 return extstatus;
1665 }
1666 #endif
1667
1668 retry :
1669
1670 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1671 if (session_port == MACH_PORT_NULL) {
1672 goto done;
1673 }
1674
1675 status = pppcontroller_copyextendedstatus(session_port, &data, &datalen, &sc_status);
1676 if (__SCNetworkConnectionNeedsRetry(connection,
1677 "SCNetworkConnectionCopyExtendedStatus()",
1678 status,
1679 &sc_status)) {
1680 goto retry;
1681 }
1682
1683 if (data != NULL) {
1684 if (!_SCUnserialize(&extstatus, NULL, data, datalen)) {
1685 if (sc_status != kSCStatusOK) sc_status = SCError();
1686 }
1687 if ((sc_status == kSCStatusOK) && !isA_CFDictionary(extstatus)) {
1688 sc_status = kSCStatusFailed;
1689 }
1690 }
1691
1692 // wait up to 250 ms for the network service to become available
1693 if (!connectionPrivate->haveStatus &&
1694 (sc_status == kSCStatusConnectionNoService) &&
1695 ((retry += 10) < 250)) {
1696 usleep(10 * 1000); // sleep 10ms between attempts
1697 goto retry;
1698 }
1699
1700 if (sc_status == kSCStatusOK) {
1701 connectionPrivate->haveStatus = TRUE;
1702 } else {
1703 if (extstatus != NULL) {
1704 CFRelease(extstatus);
1705 extstatus = NULL;
1706 }
1707 _SCErrorSet(sc_status);
1708 }
1709
1710 done :
1711
1712 pthread_mutex_unlock(&connectionPrivate->lock);
1713 return extstatus;
1714 }
1715
1716
1717 static void
1718 _SCNetworkConnectionMergeDictionaries (const void *key, const void *value, void *context)
1719 {
1720 /* Add value only if not present */
1721 CFDictionaryAddValue((CFMutableDictionaryRef)context, (CFStringRef)key, (CFTypeRef)value);
1722 }
1723
1724
1725 Boolean
1726 SCNetworkConnectionStart(SCNetworkConnectionRef connection,
1727 CFDictionaryRef userOptions,
1728 Boolean linger)
1729 {
1730 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1731 CFDataRef dataref = NULL;
1732 void *data = NULL;
1733 CFIndex datalen = 0;
1734 Boolean ok = FALSE;
1735 int sc_status = kSCStatusFailed;
1736 mach_port_t session_port;
1737 kern_return_t status;
1738
1739 if (!isA_SCNetworkConnection(connection)) {
1740 _SCErrorSet(kSCStatusInvalidArgument);
1741 return FALSE;
1742 }
1743
1744 if ((userOptions != NULL) && !isA_CFDictionary(userOptions)) {
1745 _SCErrorSet(kSCStatusInvalidArgument);
1746 return FALSE;
1747 }
1748
1749 if (userOptions == NULL) {
1750 userOptions = connectionPrivate->on_demand_user_options;
1751 } else if (connectionPrivate->on_demand_user_options != NULL) {
1752 CFDictionaryRef localUserOptions = NULL;
1753
1754 localUserOptions = CFDictionaryCreateMutableCopy(NULL, 0, userOptions);
1755 if (localUserOptions) {
1756 CFDictionaryApplyFunction(connectionPrivate->on_demand_user_options,
1757 _SCNetworkConnectionMergeDictionaries,
1758 (void *)localUserOptions);
1759 CFRelease(connectionPrivate->on_demand_user_options);
1760 userOptions = connectionPrivate->on_demand_user_options = localUserOptions;
1761 }
1762 }
1763
1764 if (debug > 0) {
1765 CFMutableDictionaryRef mdict = NULL;
1766
1767 SC_log(LOG_INFO, "SCNetworkConnectionStart (%p)", connectionPrivate);
1768
1769 if (userOptions != NULL) {
1770 CFDictionaryRef dict;
1771 CFStringRef encryption;
1772 CFMutableDictionaryRef new_dict;
1773
1774 /* special code to remove secret information */
1775 mdict = CFDictionaryCreateMutableCopy(NULL, 0, userOptions);
1776
1777 dict = CFDictionaryGetValue(mdict, kSCEntNetPPP);
1778 if (isA_CFDictionary(dict)) {
1779 encryption = CFDictionaryGetValue(dict, kSCPropNetPPPAuthPasswordEncryption);
1780 if (!isA_CFString(encryption) ||
1781 !CFEqual(encryption, kSCValNetPPPAuthPasswordEncryptionKeychain)) {
1782 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
1783 CFDictionaryReplaceValue(new_dict, kSCPropNetPPPAuthPassword, CFSTR("******"));
1784 CFDictionarySetValue(mdict, kSCEntNetPPP, new_dict);
1785 CFRelease(new_dict);
1786 }
1787 }
1788
1789 dict = CFDictionaryGetValue(mdict, kSCEntNetL2TP);
1790 if (isA_CFDictionary(dict)) {
1791 encryption = CFDictionaryGetValue(dict, kSCPropNetL2TPIPSecSharedSecretEncryption);
1792 if (!isA_CFString(encryption) ||
1793 !CFEqual(encryption, kSCValNetL2TPIPSecSharedSecretEncryptionKeychain)) {
1794 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
1795 CFDictionaryReplaceValue(new_dict, kSCPropNetL2TPIPSecSharedSecret, CFSTR("******"));
1796 CFDictionarySetValue(mdict, kSCEntNetL2TP, new_dict);
1797 CFRelease(new_dict);
1798 }
1799 }
1800
1801 dict = CFDictionaryGetValue(mdict, kSCEntNetIPSec);
1802 if (isA_CFDictionary(dict)) {
1803 encryption = CFDictionaryGetValue(dict, kSCPropNetIPSecSharedSecretEncryption);
1804 if (!isA_CFString(encryption) ||
1805 !CFEqual(encryption, kSCValNetIPSecSharedSecretEncryptionKeychain)) {
1806 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
1807 CFDictionaryReplaceValue(new_dict, kSCPropNetIPSecSharedSecret, CFSTR("******"));
1808 CFDictionarySetValue(mdict, kSCEntNetIPSec, new_dict);
1809 CFRelease(new_dict);
1810 }
1811 }
1812 }
1813
1814 SC_log(LOG_INFO, "User options: %@", mdict);
1815 if (mdict != NULL) CFRelease(mdict);
1816 }
1817
1818 pthread_mutex_lock(&connectionPrivate->lock);
1819
1820 /* Clear out any cached flow divert token parameters */
1821 if (connectionPrivate->flow_divert_token_params != NULL) {
1822 CFRelease(connectionPrivate->flow_divert_token_params);
1823 connectionPrivate->flow_divert_token_params = NULL;
1824 }
1825
1826 #if !TARGET_IPHONE_SIMULATOR
1827 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
1828 xpc_object_t xuser_options = NULL;
1829
1830 if (userOptions != NULL) {
1831 xuser_options = _CFXPCCreateXPCObjectFromCFObject(userOptions);
1832 }
1833
1834 if (connectionPrivate->client_bootstrap_port != MACH_PORT_NULL) {
1835 #if NE_SESSION_VERSION > 2
1836 ne_session_start_on_behalf_of(connectionPrivate->ne_session,
1837 xuser_options,
1838 connectionPrivate->client_bootstrap_port,
1839 connectionPrivate->client_audit_session,
1840 connectionPrivate->client_uid,
1841 connectionPrivate->client_gid,
1842 connectionPrivate->client_pid);
1843 #else
1844 ne_session_start_on_behalf_of(connectionPrivate->ne_session,
1845 xuser_options,
1846 connectionPrivate->client_bootstrap_port,
1847 connectionPrivate->client_audit_session,
1848 connectionPrivate->client_uid,
1849 connectionPrivate->client_gid);
1850 #endif
1851 } else {
1852 ne_session_start_with_options(connectionPrivate->ne_session, xuser_options);
1853 }
1854
1855 /* make sure the xpc_message goes through */
1856 ne_session_send_barrier(connectionPrivate->ne_session);
1857
1858 if (xuser_options != NULL) {
1859 xpc_release(xuser_options);
1860 }
1861
1862 ok = TRUE;
1863 goto done;
1864 }
1865 #endif /* !TARGET_IPHONE_SIMULATOR */
1866
1867 if (userOptions && !_SCSerialize(userOptions, &dataref, &data, &datalen)) {
1868 return FALSE;
1869 }
1870
1871 retry :
1872
1873 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1874 if (session_port == MACH_PORT_NULL) {
1875 if (dataref) CFRelease(dataref);
1876 goto done;
1877 }
1878
1879 status = pppcontroller_start(session_port,
1880 data,
1881 (mach_msg_type_number_t)datalen,
1882 linger,
1883 &sc_status);
1884 if (__SCNetworkConnectionNeedsRetry(connection,
1885 "SCNetworkConnectionStart()",
1886 status,
1887 &sc_status)) {
1888 goto retry;
1889 }
1890
1891 if (dataref) CFRelease(dataref);
1892
1893 if (debug > 0) {
1894 SC_log(LOG_INFO, "SCNetworkConnectionStart (%p), return: %d", connectionPrivate, sc_status);
1895 }
1896
1897 if (sc_status != kSCStatusOK) {
1898 _SCErrorSet(sc_status);
1899 goto done;
1900 }
1901
1902 /* connection is now started */
1903 ok = TRUE;
1904
1905 done:
1906 pthread_mutex_unlock(&connectionPrivate->lock);
1907 return ok;
1908 }
1909
1910
1911 Boolean
1912 SCNetworkConnectionStop(SCNetworkConnectionRef connection,
1913 Boolean forceDisconnect)
1914 {
1915 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1916 Boolean ok = FALSE;
1917 int sc_status = kSCStatusFailed;
1918 mach_port_t session_port;
1919 kern_return_t status;
1920
1921 if (!isA_SCNetworkConnection(connection)) {
1922 _SCErrorSet(kSCStatusInvalidArgument);
1923 return FALSE;
1924 }
1925
1926 if (debug > 0) {
1927 SC_log(LOG_INFO, "SCNetworkConnectionStop (%p)", connectionPrivate);
1928 }
1929
1930 pthread_mutex_lock(&connectionPrivate->lock);
1931
1932 #if !TARGET_IPHONE_SIMULATOR
1933 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
1934 ne_session_stop(connectionPrivate->ne_session);
1935 /* make sure the xpc_message goes through */
1936 ne_session_send_barrier(connectionPrivate->ne_session);
1937 ok = TRUE;
1938 goto done;
1939 }
1940 #endif /* !TARGET_IPHONE_SIMULATOR */
1941
1942 retry :
1943
1944 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1945 if (session_port == MACH_PORT_NULL) {
1946 goto done;
1947 }
1948
1949 status = pppcontroller_stop(session_port, forceDisconnect, &sc_status);
1950 if (__SCNetworkConnectionNeedsRetry(connection,
1951 "SCNetworkConnectionStop()",
1952 status,
1953 &sc_status)) {
1954 goto retry;
1955 }
1956
1957 if (debug > 0) {
1958 SC_log(LOG_INFO, "SCNetworkConnectionStop (%p), return: %d", connectionPrivate, sc_status);
1959 }
1960
1961 if (sc_status != kSCStatusOK) {
1962 _SCErrorSet(sc_status);
1963 goto done;
1964 }
1965
1966 /* connection is now disconnecting */
1967 ok = TRUE;
1968
1969 done :
1970
1971 pthread_mutex_unlock(&connectionPrivate->lock);
1972 return ok;
1973 }
1974
1975
1976 Boolean
1977 SCNetworkConnectionSuspend(SCNetworkConnectionRef connection)
1978 {
1979 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1980 Boolean ok = FALSE;
1981 int sc_status = kSCStatusFailed;
1982 mach_port_t session_port;
1983 kern_return_t status;
1984
1985 if (!isA_SCNetworkConnection(connection)) {
1986 _SCErrorSet(kSCStatusInvalidArgument);
1987 return FALSE;
1988 }
1989
1990 if (debug > 0) {
1991 SC_log(LOG_INFO, "SCNetworkConnectionSuspend (%p)", connectionPrivate);
1992 }
1993
1994 pthread_mutex_lock(&connectionPrivate->lock);
1995
1996 #if !!TARGET_IPHONE_SIMULATOR
1997 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
1998 /* Suspend only applies to PPPSerial and PPPoE */
1999 ok = TRUE;
2000 goto done;
2001 }
2002 #endif /* !TARGET_IPHONE_SIMULATOR */
2003
2004 retry :
2005
2006 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
2007 if (session_port == MACH_PORT_NULL) {
2008 goto done;
2009 }
2010
2011 status = pppcontroller_suspend(session_port, &sc_status);
2012 if (__SCNetworkConnectionNeedsRetry(connection,
2013 "SCNetworkConnectionSuspend()",
2014 status,
2015 &sc_status)) {
2016 goto retry;
2017 }
2018
2019 if (debug > 0) {
2020 SC_log(LOG_INFO, "SCNetworkConnectionSuspend (%p), return: %d", connectionPrivate, sc_status);
2021 }
2022
2023 if (sc_status != kSCStatusOK) {
2024 _SCErrorSet(sc_status);
2025 goto done;
2026 }
2027
2028 /* connection is now suspended */
2029 ok = TRUE;
2030
2031 done :
2032
2033 pthread_mutex_unlock(&connectionPrivate->lock);
2034 return ok;
2035 }
2036
2037
2038 Boolean
2039 SCNetworkConnectionResume(SCNetworkConnectionRef connection)
2040 {
2041 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
2042 Boolean ok = FALSE;
2043 int sc_status = kSCStatusFailed;
2044 mach_port_t session_port;
2045 kern_return_t status;
2046
2047 if (!isA_SCNetworkConnection(connection)) {
2048 _SCErrorSet(kSCStatusInvalidArgument);
2049 return FALSE;
2050 }
2051
2052 if (debug > 0) {
2053 SC_log(LOG_INFO, "SCNetworkConnectionResume (%p)", connectionPrivate);
2054 }
2055
2056 pthread_mutex_lock(&connectionPrivate->lock);
2057
2058 #if !TARGET_IPHONE_SIMULATOR
2059 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
2060 /* Resume only applies to PPPSerial and PPPoE */
2061 ok = TRUE;
2062 goto done;
2063 }
2064 #endif /* !TARGET_IPHONE_SIMULATOR */
2065
2066 retry :
2067
2068 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
2069 if (session_port == MACH_PORT_NULL) {
2070 goto done;
2071 }
2072
2073 status = pppcontroller_resume(session_port, &sc_status);
2074 if (__SCNetworkConnectionNeedsRetry(connection,
2075 "SCNetworkConnectionResume()",
2076 status,
2077 &sc_status)) {
2078 goto retry;
2079 }
2080
2081 if (debug > 0) {
2082 SC_log(LOG_INFO, "SCNetworkConnectionResume (%p), return: %d", connectionPrivate, sc_status);
2083 }
2084
2085 if (sc_status != kSCStatusOK) {
2086 _SCErrorSet(sc_status);
2087 goto done;
2088 }
2089
2090 /* connection is now resume */
2091 ok = TRUE;
2092
2093 done :
2094
2095 pthread_mutex_unlock(&connectionPrivate->lock);
2096 return ok;
2097 }
2098
2099
2100 #if !TARGET_IPHONE_SIMULATOR
2101 Boolean
2102 SCNetworkConnectionRefreshOnDemandState(SCNetworkConnectionRef connection)
2103 {
2104 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
2105 Boolean ok = FALSE;
2106 uint32_t retry = 0;
2107 int sc_status = kSCStatusFailed;
2108 mach_port_t server_port = __SCNetworkConnectionGetCurrentServerPort();
2109 kern_return_t status = KERN_SUCCESS;
2110
2111 if (!isA_SCNetworkConnection(connection)) {
2112 _SCErrorSet(kSCStatusInvalidArgument);
2113 return FALSE;
2114 }
2115
2116 if (debug > 0) {
2117 SC_log(LOG_INFO, "SCNetworkConnectionRefreshOnDemandState (%p)", connectionPrivate);
2118 }
2119
2120 pthread_mutex_lock(&connectionPrivate->lock);
2121
2122 while (TRUE) {
2123 if (server_port == MACH_PORT_NULL) {
2124 server_port = __SCNetworkConnectionRefreshServerPort(server_port, &sc_status);
2125 if (server_port == MACH_PORT_NULL) {
2126 // if server not available
2127 if (sc_status == BOOTSTRAP_UNKNOWN_SERVICE) {
2128 // wait up to 2.5 seconds for the [SCNetworkConnection] server
2129 // to startup
2130 if ((retry += 50) < 2500) {
2131 usleep(50 * 1000); // sleep 50ms between attempts
2132 continue;
2133 }
2134 }
2135 break;
2136 }
2137 }
2138
2139 status = pppcontroller_ondemand_refresh_state(server_port, &sc_status);
2140 if (status == KERN_SUCCESS)
2141 break;
2142
2143 if (status == MACH_SEND_INVALID_DEST) {
2144 // the server is not yet available
2145 SC_log(LOG_NOTICE, "SCNetworkConnectionRefreshOnDemandState (!dest) (%p)", connectionPrivate);
2146 } else if (status == MIG_SERVER_DIED) {
2147 // the server we were using is gone
2148 SC_log(LOG_NOTICE, "SCNetworkConnectionRefreshOnDemandState (!mig) (%p)", connectionPrivate);
2149 } else {
2150 // if we got an unexpected error, don't retry
2151 sc_status = status;
2152 break;
2153 }
2154 }
2155
2156 if (debug > 0) {
2157 SC_log(LOG_INFO, "SCNetworkConnectionRefreshOnDemandState (%p), return: %d/%d", connectionPrivate, status, sc_status);
2158 }
2159
2160 if (sc_status != kSCStatusOK) {
2161 _SCErrorSet(sc_status);
2162 goto done;
2163 }
2164
2165 ok = TRUE;
2166
2167 done :
2168
2169 pthread_mutex_unlock(&connectionPrivate->lock);
2170 return ok;
2171 }
2172 #endif /* !TARGET_IPHONE_SIMULATOR */
2173
2174
2175 CFDictionaryRef
2176 SCNetworkConnectionCopyUserOptions(SCNetworkConnectionRef connection)
2177 {
2178 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
2179 xmlDataOut_t data = NULL;
2180 mach_msg_type_number_t datalen = 0;
2181 int sc_status = kSCStatusFailed;
2182 mach_port_t session_port;
2183 kern_return_t status;
2184 CFPropertyListRef userOptions = NULL;
2185
2186 if (!isA_SCNetworkConnection(connection)) {
2187 _SCErrorSet(kSCStatusInvalidArgument);
2188 return NULL;
2189 }
2190
2191 pthread_mutex_lock(&connectionPrivate->lock);
2192
2193 #if !TARGET_IPHONE_SIMULATOR
2194 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
2195 __block xpc_object_t config = NULL;
2196 ne_session_t ne_session = connectionPrivate->ne_session;
2197
2198 ne_session_retain(ne_session);
2199 pthread_mutex_unlock(&connectionPrivate->lock);
2200
2201 dispatch_semaphore_t ne_sema = dispatch_semaphore_create(0);
2202 ne_session_get_info(ne_session, NESessionInfoTypeConfiguration, __SCNetworkConnectionQueue(), ^(xpc_object_t result) {
2203 if (result != NULL) {
2204 config = xpc_retain(result);
2205 }
2206 ne_session_release(ne_session);
2207 dispatch_semaphore_signal(ne_sema);
2208 });
2209 dispatch_semaphore_wait(ne_sema, DISPATCH_TIME_FOREVER);
2210 dispatch_release(ne_sema);
2211
2212 if (config != NULL) {
2213 xpc_object_t xoptions = xpc_dictionary_get_value(config, NESMSessionLegacyUserConfigurationKey);
2214 if (xoptions != NULL) {
2215 userOptions = _CFXPCCreateCFObjectFromXPCObject(xoptions);
2216 }
2217 xpc_release(config);
2218 }
2219 return userOptions;
2220 }
2221 #endif /* !TARGET_IPHONE_SIMULATOR */
2222
2223 retry :
2224
2225 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
2226 if (session_port == MACH_PORT_NULL) {
2227 goto done;
2228 }
2229
2230 status = pppcontroller_copyuseroptions(session_port, &data, &datalen, &sc_status);
2231 if (__SCNetworkConnectionNeedsRetry(connection,
2232 "SCNetworkConnectionCopyUserOptions()",
2233 status,
2234 &sc_status)) {
2235 goto retry;
2236 }
2237
2238 if (data != NULL) {
2239 if (!_SCUnserialize(&userOptions, NULL, data, datalen)) {
2240 if (sc_status != kSCStatusOK) sc_status = SCError();
2241 }
2242 if ((sc_status == kSCStatusOK) && (userOptions != NULL) && !isA_CFDictionary(userOptions)) {
2243 sc_status = kSCStatusFailed;
2244 }
2245 }
2246
2247 if (sc_status == kSCStatusOK) {
2248 if (userOptions == NULL) {
2249 // if no user options, return an empty dictionary
2250 userOptions = CFDictionaryCreate(NULL,
2251 NULL,
2252 NULL,
2253 0,
2254 &kCFTypeDictionaryKeyCallBacks,
2255 &kCFTypeDictionaryValueCallBacks);
2256 }
2257 } else {
2258 if (userOptions) {
2259 CFRelease(userOptions);
2260 userOptions = NULL;
2261 }
2262 _SCErrorSet(sc_status);
2263 }
2264
2265 done :
2266
2267 pthread_mutex_unlock(&connectionPrivate->lock);
2268 return userOptions;
2269 }
2270
2271
2272 static Boolean
2273 __SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection,
2274 CFRunLoopRef runLoop,
2275 CFStringRef runLoopMode,
2276 dispatch_queue_t queue)
2277 {
2278 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
2279 Boolean ok = FALSE;
2280 int sc_status = kSCStatusFailed;
2281 mach_port_t session_port;
2282 kern_return_t status;
2283
2284 pthread_mutex_lock(&connectionPrivate->lock);
2285
2286 if (connectionPrivate->rlsFunction == NULL) {
2287 _SCErrorSet(kSCStatusInvalidArgument);
2288 goto done;
2289 }
2290
2291 if ((connectionPrivate->dispatchQueue != NULL) || // if we are already scheduled on a dispatch queue
2292 ((queue != NULL) && connectionPrivate->scheduled)) { // if we are already scheduled on a CFRunLoop
2293 _SCErrorSet(kSCStatusInvalidArgument);
2294 goto done;
2295 }
2296
2297 if (!connectionPrivate->scheduled) {
2298
2299 retry :
2300
2301 if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
2302 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
2303 if (session_port == MACH_PORT_NULL) {
2304 goto done;
2305 }
2306
2307 status = pppcontroller_notification(session_port, 1, &sc_status);
2308 if (__SCNetworkConnectionNeedsRetry(connection,
2309 "__SCNetworkConnectionScheduleWithRunLoop()",
2310 status,
2311 &sc_status)) {
2312 goto retry;
2313 }
2314
2315 if (sc_status != kSCStatusOK) {
2316 _SCErrorSet(sc_status);
2317 goto done;
2318 }
2319
2320 if (runLoop != NULL) {
2321 connectionPrivate->rls = CFMachPortCreateRunLoopSource(NULL, connectionPrivate->notify_port, 0);
2322 connectionPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2323 }
2324 } else if (runLoop != NULL) {
2325 CFRunLoopSourceContext rlsContext = {
2326 0, // version
2327 (void *)connection, // info
2328 NULL, // retain
2329 NULL, // release
2330 NULL, // copy description
2331 NULL, // equal
2332 NULL, // hash
2333 NULL, // schedule
2334 NULL, // cancel
2335 __SCNetworkConnectionCallBack, // perform
2336 };
2337
2338 connectionPrivate->rls = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &rlsContext);
2339 connectionPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2340 }
2341
2342 connectionPrivate->scheduled = TRUE;
2343 }
2344
2345 if (queue != NULL) {
2346 // retain the dispatch queue
2347 connectionPrivate->dispatchQueue = queue;
2348 dispatch_retain(connectionPrivate->dispatchQueue);
2349
2350 if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
2351 dispatch_group_t group = NULL;
2352 mach_port_t mp;
2353 dispatch_source_t source;
2354
2355 //
2356 // We've taken a reference to the caller's dispatch_queue and we
2357 // want to hold on to that reference until we've processed any/all
2358 // notifications. To facilitate this we create a group, dispatch
2359 // any notification blocks via that group, and when the caller
2360 // has told us to stop the notifications (unschedule) we wait for
2361 // the group to empty and use the group's finalizer to release
2362 // our reference to the SCNetworkConnection.
2363 //
2364 group = dispatch_group_create();
2365 connectionPrivate->dispatchGroup = group;
2366 CFRetain(connection);
2367 dispatch_set_context(connectionPrivate->dispatchGroup, (void *)connection);
2368 dispatch_set_finalizer_f(connectionPrivate->dispatchGroup, (dispatch_function_t)CFRelease);
2369
2370 mp = CFMachPortGetPort(connectionPrivate->notify_port);
2371 source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, mp, 0, queue);
2372 if (source == NULL) {
2373 SC_log(LOG_NOTICE, "dispatch_source_create() failed");
2374 _SCErrorSet(kSCStatusFailed);
2375 goto done;
2376 }
2377
2378 // have our dispatch source hold a reference to the notification CFMachPort
2379 CFRetain(connectionPrivate->notify_port);
2380 dispatch_set_context(source, (void *)connectionPrivate->notify_port);
2381 dispatch_set_finalizer_f(source, (dispatch_function_t)CFRelease);
2382
2383 dispatch_source_set_event_handler(source, ^{
2384 kern_return_t kr;
2385 typedef union {
2386 u_int8_t buf[sizeof(mach_msg_empty_t) + MAX_TRAILER_SIZE];
2387 mach_msg_empty_rcv_t msg;
2388 mach_no_senders_notification_t no_senders;
2389 } *notify_message_t;
2390 CFMachPortRef notify_port;
2391 notify_message_t notify_msg;
2392
2393 notify_msg = (notify_message_t)malloc(sizeof(*notify_msg));
2394
2395 kr = mach_msg(&notify_msg->msg.header, // msg
2396 MACH_RCV_MSG, // options
2397 0, // send_size
2398 sizeof(*notify_msg), // rcv_size
2399 mp, // rcv_name
2400 MACH_MSG_TIMEOUT_NONE, // timeout
2401 MACH_PORT_NULL); // notify
2402 if (kr != KERN_SUCCESS) {
2403 SC_log(LOG_NOTICE, "SCDynamicStore notification handler, kr=0x%x", kr);
2404 return;
2405 }
2406
2407 CFRetain(connection);
2408 notify_port = dispatch_get_context(source);
2409
2410 dispatch_group_async(group, queue, ^{
2411 __SCNetworkConnectionMachCallBack(notify_port,
2412 (void *)notify_msg,
2413 sizeof(*notify_msg),
2414 (void *)connection);
2415 free(notify_msg);
2416 CFRelease(connection);
2417 });
2418 });
2419
2420 dispatch_source_set_cancel_handler(source, ^{
2421 dispatch_release(source);
2422 });
2423
2424 connectionPrivate->dispatchSource = source;
2425 dispatch_resume(source);
2426 }
2427 } else {
2428 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, connectionPrivate->rlList)) {
2429 /*
2430 * if we do not already have notifications scheduled with
2431 * this runLoop / runLoopMode
2432 */
2433 CFRunLoopAddSource(runLoop, connectionPrivate->rls, runLoopMode);
2434 }
2435
2436 _SC_schedule(connection, runLoop, runLoopMode, connectionPrivate->rlList);
2437 }
2438
2439 #if !TARGET_IPHONE_SIMULATOR
2440 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
2441 CFRetain(connection);
2442 ne_session_set_event_handler(connectionPrivate->ne_session, __SCNetworkConnectionQueue(), ^(ne_session_event_t event, void *event_data) {
2443 #pragma unused(event_data)
2444 if (event == NESessionEventStatusChanged) {
2445 CFRetain(connection); /* Released in __SCNetworkConnectionCallBack */
2446 pthread_mutex_lock(&connectionPrivate->lock);
2447 if (connectionPrivate->rls != NULL) {
2448 CFRunLoopSourceSignal(connectionPrivate->rls);
2449 _SC_signalRunLoop(connection, connectionPrivate->rls, connectionPrivate->rlList);
2450 } else if (connectionPrivate->dispatchQueue != NULL) {
2451 dispatch_async(connectionPrivate->dispatchQueue, ^{
2452 __SCNetworkConnectionCallBack((void *)connection);
2453 });
2454 }
2455 pthread_mutex_unlock(&connectionPrivate->lock);
2456 } else if (event == NESessionEventCanceled) {
2457 CFRelease(connection);
2458 }
2459 });
2460 }
2461 #endif /* !TARGET_IPHONE_SIMULATOR */
2462
2463 ok = TRUE;
2464
2465 done :
2466
2467 pthread_mutex_unlock(&connectionPrivate->lock);
2468 return ok;
2469 }
2470
2471
2472 static Boolean
2473 __SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection,
2474 CFRunLoopRef runLoop,
2475 CFStringRef runLoopMode,
2476 dispatch_queue_t queue)
2477 {
2478 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
2479 dispatch_group_t drainGroup = NULL;
2480 dispatch_queue_t drainQueue = NULL;
2481 int sc_status = kSCStatusFailed;
2482 CFIndex n = 0;
2483 Boolean ok = FALSE;
2484 kern_return_t status;
2485
2486 // hold a reference while we unschedule
2487 CFRetain(connection);
2488
2489 pthread_mutex_lock(&connectionPrivate->lock);
2490
2491 if ((runLoop != NULL) && !connectionPrivate->scheduled) { // if we should be scheduled (but are not)
2492 _SCErrorSet(kSCStatusInvalidArgument);
2493 goto done;
2494 }
2495
2496 if (((runLoop == NULL) && (connectionPrivate->dispatchQueue == NULL)) || // if we should be scheduled on a dispatch queue (but are not)
2497 ((runLoop != NULL) && (connectionPrivate->dispatchQueue != NULL))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
2498 _SCErrorSet(kSCStatusInvalidArgument);
2499 goto done;
2500 }
2501
2502 if (connectionPrivate->dispatchQueue != NULL) {
2503 if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
2504 // cancel dispatchSource
2505 if (connectionPrivate->dispatchSource != NULL) {
2506 dispatch_source_cancel(connectionPrivate->dispatchSource);
2507 connectionPrivate->dispatchSource = NULL;
2508 }
2509
2510 // save dispatchQueue/group, release reference when all queue'd blocks
2511 // have been processed, allow re-scheduling
2512 drainGroup = connectionPrivate->dispatchGroup;
2513 connectionPrivate->dispatchGroup = NULL;
2514 drainQueue = connectionPrivate->dispatchQueue;
2515 connectionPrivate->dispatchQueue = NULL;
2516 } else {
2517 dispatch_release(connectionPrivate->dispatchQueue);
2518 connectionPrivate->dispatchQueue = NULL;
2519 }
2520 } else {
2521 if (!_SC_unschedule(connection, runLoop, runLoopMode, connectionPrivate->rlList, FALSE)) {
2522 // if not currently scheduled on this runLoop / runLoopMode
2523 _SCErrorSet(kSCStatusFailed);
2524 goto done;
2525 }
2526
2527 n = CFArrayGetCount(connectionPrivate->rlList);
2528 if (n == 0 || !_SC_isScheduled(NULL, runLoop, runLoopMode, connectionPrivate->rlList)) {
2529 /*
2530 * if we are no longer scheduled to receive notifications for
2531 * this runLoop / runLoopMode
2532 */
2533 CFRunLoopRemoveSource(runLoop, connectionPrivate->rls, runLoopMode);
2534
2535 if (n == 0) {
2536 // if *all* notifications have been unscheduled
2537 CFRelease(connectionPrivate->rlList);
2538 connectionPrivate->rlList = NULL;
2539 CFRunLoopSourceInvalidate(connectionPrivate->rls);
2540 CFRelease(connectionPrivate->rls);
2541 connectionPrivate->rls = NULL;
2542 }
2543 }
2544 }
2545
2546 if (n == 0) {
2547 // if *all* notifications have been unscheduled
2548 connectionPrivate->scheduled = FALSE;
2549
2550 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
2551 #if !TARGET_IPHONE_SIMULATOR
2552 ne_session_cancel(connectionPrivate->ne_session);
2553 #endif /* !TARGET_IPHONE_SIMULATOR */
2554 } else {
2555 mach_port_t session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
2556 if (session_port == MACH_PORT_NULL) {
2557 goto done;
2558 }
2559
2560 status = pppcontroller_notification(session_port, 0, &sc_status);
2561 if (__SCNetworkConnectionNeedsRetry(connection,
2562 "__SCNetworkConnectionUnscheduleFromRunLoop pppcontroller_notification()",
2563 status,
2564 &sc_status)) {
2565 sc_status = kSCStatusOK;
2566 status = KERN_SUCCESS;
2567 }
2568
2569 if ((status != KERN_SUCCESS) || (sc_status != kSCStatusOK)) {
2570 _SCErrorSet(sc_status);
2571 goto done;
2572 }
2573 }
2574 }
2575
2576 ok = TRUE;
2577
2578 done :
2579
2580 pthread_mutex_unlock(&connectionPrivate->lock);
2581
2582 if (drainGroup != NULL) {
2583 dispatch_group_notify(drainGroup, drainQueue, ^{
2584 // release group/queue references
2585 dispatch_release(drainQueue);
2586 dispatch_release(drainGroup); // releases our connection reference
2587 });
2588 }
2589
2590 // release our reference
2591 CFRelease(connection);
2592
2593 return ok;
2594 }
2595
2596
2597 Boolean
2598 SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection,
2599 CFRunLoopRef runLoop,
2600 CFStringRef runLoopMode)
2601 {
2602 if (!isA_SCNetworkConnection(connection) || (runLoop == NULL) || (runLoopMode == NULL)) {
2603 _SCErrorSet(kSCStatusInvalidArgument);
2604 return FALSE;
2605 }
2606
2607 return __SCNetworkConnectionScheduleWithRunLoop(connection, runLoop, runLoopMode, NULL);
2608 }
2609
2610
2611 Boolean
2612 SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection,
2613 CFRunLoopRef runLoop,
2614 CFStringRef runLoopMode)
2615 {
2616 if (!isA_SCNetworkConnection(connection) || (runLoop == NULL) || (runLoopMode == NULL)) {
2617 _SCErrorSet(kSCStatusInvalidArgument);
2618 return FALSE;
2619 }
2620
2621 return __SCNetworkConnectionUnscheduleFromRunLoop(connection, runLoop, runLoopMode, NULL);
2622 }
2623
2624
2625 Boolean
2626 SCNetworkConnectionSetDispatchQueue(SCNetworkConnectionRef connection,
2627 dispatch_queue_t queue)
2628 {
2629 Boolean ok = FALSE;
2630
2631 if (!isA_SCNetworkConnection(connection)) {
2632 _SCErrorSet(kSCStatusInvalidArgument);
2633 return FALSE;
2634 }
2635
2636 if (queue != NULL) {
2637 ok = __SCNetworkConnectionScheduleWithRunLoop(connection, NULL, NULL, queue);
2638 } else {
2639 ok = __SCNetworkConnectionUnscheduleFromRunLoop(connection, NULL, NULL, NULL);
2640 }
2641
2642 return ok;
2643 }
2644
2645
2646 /* Requires having called SCNetworkConnectionSelectServiceWithOptions previously */
2647 Boolean
2648 SCNetworkConnectionIsOnDemandSuspended(SCNetworkConnectionRef connection)
2649 {
2650 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
2651
2652 if (!isA_SCNetworkConnection(connection)) {
2653 _SCErrorSet(kSCStatusInvalidArgument);
2654 return FALSE;
2655 }
2656
2657 if (connectionPrivate->on_demand_info != NULL) {
2658 uint32_t isSuspended = 0;
2659 CFNumberRef num = NULL;
2660
2661 num = CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCPropNetVPNOnDemandSuspended);
2662 if (isA_CFNumber(num) &&
2663 CFNumberGetValue(num, kCFNumberSInt32Type, &isSuspended) &&
2664 (isSuspended != 0)) {
2665 return TRUE;
2666 }
2667 }
2668
2669 _SCErrorSet(kSCStatusOK);
2670 return FALSE;
2671 }
2672
2673 Boolean
2674 SCNetworkConnectionTriggerOnDemandIfNeeded (CFStringRef hostName,
2675 Boolean afterDNSFail,
2676 int timeout,
2677 int trafficClass)
2678 {
2679 #if !TARGET_IPHONE_SIMULATOR
2680 __block Boolean triggeredOnDemand = FALSE;
2681 struct proc_uniqidentifierinfo procu;
2682 void *policy_match = NULL;
2683 char *hostname = NULL;
2684 CFIndex hostnameSize = 0;
2685 pid_t pid = getpid();
2686 uid_t uid = geteuid();
2687
2688 /* Require hostName, require non-root user */
2689 if (hostName == NULL || geteuid() == 0) {
2690 goto done;
2691 }
2692
2693 hostnameSize = CFStringGetLength(hostName);
2694 if (hostnameSize == 0) {
2695 goto done;
2696 }
2697
2698 hostname = malloc(hostnameSize + 1);
2699 CFStringGetCString(hostName, hostname, hostnameSize + 1, kCFStringEncodingUTF8);
2700
2701 if (proc_pidinfo(pid, PROC_PIDUNIQIDENTIFIERINFO, 1, &procu, sizeof(procu)) != sizeof(procu)) {
2702 goto done;
2703 }
2704
2705 policy_match = ne_session_copy_policy_match(hostname, NULL, NULL, procu.p_uuid, procu.p_uuid, pid, uid, 0, trafficClass);
2706
2707 NEPolicyServiceActionType action_type = ne_session_policy_match_get_service_action(policy_match);
2708 if (action_type == NESessionPolicyActionTrigger ||
2709 (afterDNSFail && action_type == NESessionPolicyActionTriggerIfNeeded)) {
2710 uuid_t config_id;
2711 if (ne_session_policy_match_get_service(policy_match, config_id)) {
2712 xpc_object_t start_options = xpc_dictionary_create(NULL, NULL, 0);
2713 if (start_options != NULL) {
2714 xpc_dictionary_set_bool(start_options, NESessionStartOptionIsOnDemandKey, true);
2715 xpc_dictionary_set_string(start_options, NESessionStartOptionMatchHostnameKey, hostname);
2716
2717 ne_session_t new_session = ne_session_create(config_id, ne_session_policy_match_get_service_type(policy_match));
2718 if (new_session != NULL) {
2719 dispatch_semaphore_t wait_for_session = dispatch_semaphore_create(0);
2720 dispatch_retain(wait_for_session);
2721 xpc_retain(start_options);
2722 ne_session_get_status(new_session, __SCNetworkConnectionQueue(),
2723 ^(ne_session_status_t status) {
2724 if (status == NESessionStatusDisconnected) {
2725 dispatch_retain(wait_for_session);
2726 ne_session_set_event_handler(new_session, __SCNetworkConnectionQueue(),
2727 ^(ne_session_event_t event, void *event_data) {
2728 os_activity_t activity_id;
2729
2730 activity_id = os_activity_start("processing ne_session notification",
2731 OS_ACTIVITY_FLAG_DEFAULT);
2732
2733 if (event == NESessionEventStatusChanged) {
2734 dispatch_retain(wait_for_session);
2735 ne_session_get_status(new_session, __SCNetworkConnectionQueue(),
2736 ^(ne_session_status_t new_status) {
2737 if (new_status != NESessionStatusConnecting) {
2738 if (status == NESessionStatusConnected) {
2739 triggeredOnDemand = TRUE;
2740 }
2741 ne_session_cancel(new_session);
2742 }
2743 dispatch_release(wait_for_session);
2744 });
2745 } else if (event == NESessionEventCanceled) {
2746 dispatch_semaphore_signal(wait_for_session);
2747 dispatch_release(wait_for_session);
2748 }
2749
2750 os_activity_end(activity_id);
2751 });
2752 ne_session_start_with_options(new_session, start_options);
2753 } else {
2754 dispatch_semaphore_signal(wait_for_session);
2755 }
2756 dispatch_release(wait_for_session);
2757 xpc_release(start_options);
2758 });
2759 dispatch_semaphore_wait(wait_for_session, timeout ? dispatch_time(DISPATCH_TIME_NOW, (int64_t)timeout * NSEC_PER_SEC) : DISPATCH_TIME_FOREVER);
2760 dispatch_release(wait_for_session);
2761 ne_session_release(new_session);
2762 }
2763
2764 xpc_release(start_options);
2765 }
2766 }
2767 }
2768 done:
2769 if (hostname) {
2770 free(hostname);
2771 }
2772
2773 if (policy_match) {
2774 free(policy_match);
2775 }
2776
2777 return triggeredOnDemand;
2778 #else
2779 #pragma unused(hostName, afterDNSFail, timeout, trafficClass)
2780 return FALSE;
2781 #endif
2782 }
2783
2784
2785 Boolean
2786 SCNetworkConnectionCopyOnDemandInfo(SCNetworkConnectionRef connection,
2787 CFStringRef *onDemandRemoteAddress,
2788 SCNetworkConnectionStatus *onDemandConnectionStatus)
2789 {
2790 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
2791
2792 if (!isA_SCNetworkConnection(connection)) {
2793 _SCErrorSet(kSCStatusInvalidArgument);
2794 return FALSE;
2795 }
2796
2797 if (connectionPrivate->service == NULL) {
2798 _SCErrorSet(kSCStatusConnectionNoService);
2799 return FALSE;
2800 }
2801
2802 if (onDemandRemoteAddress != NULL) {
2803 *onDemandRemoteAddress = NULL;
2804 }
2805
2806 if (onDemandConnectionStatus != NULL) {
2807 *onDemandConnectionStatus = kSCNetworkConnectionInvalid;
2808 }
2809
2810 if (connectionPrivate->on_demand_info != NULL) {
2811 if (onDemandRemoteAddress != NULL) {
2812 CFStringRef address =
2813 CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCNetworkConnectionOnDemandRemoteAddress);
2814 if (isA_CFString(address)) {
2815 *onDemandRemoteAddress = address;
2816 CFRetain(*onDemandRemoteAddress);
2817 }
2818 }
2819
2820 if (onDemandConnectionStatus != NULL) {
2821 int num;
2822 CFNumberRef status_num =
2823 CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCNetworkConnectionOnDemandStatus);
2824 if (isA_CFNumber(status_num) && CFNumberGetValue(status_num, kCFNumberIntType, &num)) {
2825 *onDemandConnectionStatus = num;
2826 }
2827 }
2828 }
2829
2830 return connectionPrivate->on_demand;
2831 }
2832
2833
2834 Boolean
2835 SCNetworkConnectionGetReachabilityInfo(SCNetworkConnectionRef connection,
2836 SCNetworkReachabilityFlags *reach_flags,
2837 unsigned int *reach_if_index)
2838 {
2839 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
2840
2841 if (!isA_SCNetworkConnection(connection)) {
2842 _SCErrorSet(kSCStatusInvalidArgument);
2843 return FALSE;
2844 }
2845
2846 if (connectionPrivate->service == NULL) {
2847 _SCErrorSet(kSCStatusConnectionNoService);
2848 return FALSE;
2849 }
2850
2851 if (reach_flags != NULL) {
2852 *reach_flags = 0;
2853 }
2854
2855 if (reach_if_index != NULL) {
2856 *reach_if_index = 0;
2857 }
2858
2859 if (connectionPrivate->on_demand_info != NULL) {
2860 if (reach_flags != NULL) {
2861 int num;
2862 CFNumberRef flags_num =
2863 CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCNetworkConnectionOnDemandReachFlags);
2864 if (isA_CFNumber(flags_num) && CFNumberGetValue(flags_num, kCFNumberIntType, &num)) {
2865 *reach_flags = num;
2866 }
2867 }
2868
2869 if (reach_if_index != NULL) {
2870 int num;
2871 CFNumberRef if_index_num =
2872 CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCNetworkConnectionOnDemandReachInterfaceIndex);
2873 if (isA_CFNumber(if_index_num) && CFNumberGetValue(if_index_num, kCFNumberIntType, &num)) {
2874 *reach_if_index = num;
2875 }
2876 }
2877 }
2878
2879 return TRUE;
2880 }
2881
2882
2883 SCNetworkConnectionType
2884 SCNetworkConnectionGetType(SCNetworkConnectionRef connection)
2885 {
2886 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
2887
2888 if (!isA_SCNetworkConnection(connection)) {
2889 _SCErrorSet(kSCStatusInvalidArgument);
2890 return kSCNetworkConnectionTypeUnknown;
2891 }
2892
2893 if (connectionPrivate->service == NULL) {
2894 _SCErrorSet(kSCStatusConnectionNoService);
2895 return kSCNetworkConnectionTypeUnknown;
2896 }
2897
2898 _SCErrorSet(kSCStatusOK);
2899
2900 return connectionPrivate->type;
2901 }
2902
2903
2904 CFDataRef
2905 SCNetworkConnectionCopyFlowDivertToken(SCNetworkConnectionRef connection,
2906 CFDictionaryRef flowProperties)
2907 {
2908 #pragma unused(connection, flowProperties)
2909 _SCErrorSet(kSCStatusFailed);
2910 return NULL;
2911 }
2912
2913
2914 int
2915 SCNetworkConnectionGetServiceIdentifier (SCNetworkConnectionRef connection)
2916 {
2917 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
2918 int service_identifier = -1;
2919
2920 if (connectionPrivate->service != NULL) {
2921 service_identifier = 0;
2922 if (connectionPrivate->on_demand_info != NULL) {
2923 CFNumberRef id_num = CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCPropNetDNSServiceIdentifier);
2924
2925 if (isA_CFNumber(id_num)) {
2926 CFNumberGetValue(id_num, kCFNumberIntType, &service_identifier);
2927 }
2928 }
2929 }
2930
2931 return service_identifier;
2932 }
2933
2934
2935 #if !TARGET_IPHONE_SIMULATOR
2936 SCNetworkConnectionStatus
2937 SCNetworkConnectionGetStatusFromNEStatus(ne_session_status_t status)
2938 {
2939 switch (status) {
2940 case NESessionStatusInvalid:
2941 return kSCNetworkConnectionInvalid;
2942 case NESessionStatusDisconnected:
2943 return kSCNetworkConnectionDisconnected;
2944 case NESessionStatusConnecting:
2945 case NESessionStatusReasserting:
2946 return kSCNetworkConnectionConnecting;
2947 case NESessionStatusConnected:
2948 return kSCNetworkConnectionConnected;
2949 case NESessionStatusDisconnecting:
2950 return kSCNetworkConnectionDisconnecting;
2951 }
2952
2953 return kSCNetworkConnectionInvalid;
2954 }
2955 #endif /* !TARGET_IPHONE_SIMULATOR */
2956
2957
2958 #pragma mark -
2959 #pragma mark User level "dial" API
2960
2961
2962 #define k_NetworkConnect_Notification "com.apple.networkConnect"
2963 #define k_NetworkConnect_Pref_File CFSTR("com.apple.networkConnect")
2964 #define k_InterentConnect_Pref_File CFSTR("com.apple.internetconnect")
2965
2966 #define k_Dial_Default_Key CFSTR("ConnectByDefault") // needs to go into SC
2967 #define k_Last_Service_Id_Key CFSTR("ServiceID")
2968 #define k_Unique_Id_Key CFSTR("UniqueIdentifier")
2969
2970
2971 /* Private Prototypes */
2972 static Boolean SCNetworkConnectionPrivateCopyDefaultServiceIDForDial (CFStringRef *serviceID);
2973 static Boolean SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore (CFStringRef *serviceID);
2974 static Boolean SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray, CFDictionaryRef *userOptions);
2975 static Boolean SCNetworkConnectionPrivateIsPPPService (CFStringRef serviceID, CFStringRef subType1, CFStringRef subType2);
2976 static void addPasswordFromKeychain (CFStringRef serviceID, CFDictionaryRef *userOptions);
2977 static CFStringRef copyPasswordFromKeychain (CFStringRef uniqueID);
2978
2979 static int notify_userprefs_token = -1;
2980
2981 static CFDictionaryRef onDemand_configuration = NULL;
2982 static Boolean onDemand_force_refresh = FALSE;
2983 static pthread_mutex_t onDemand_notify_lock = PTHREAD_MUTEX_INITIALIZER;
2984 static int onDemand_notify_token = -1;
2985
2986
2987 /*
2988 * return TRUE if domain1 ends with domain2, and will check for trailing "."
2989 */
2990 #define WILD_CARD_MATCH_STR CFSTR("*")
2991 Boolean
2992 _SC_domainEndsWithDomain(CFStringRef compare_domain, CFStringRef match_domain)
2993 {
2994 CFRange range;
2995 Boolean ret = FALSE;
2996 CFStringRef s1 = NULL;
2997 Boolean s1_created = FALSE;
2998 CFStringRef s2 = NULL;
2999 Boolean s2_created = FALSE;
3000 CFStringRef s3 = NULL;
3001
3002 if (CFEqual(match_domain, WILD_CARD_MATCH_STR)) {
3003 return TRUE;
3004 }
3005
3006 if (CFStringHasSuffix(compare_domain, CFSTR("."))) {
3007 range.location = 0;
3008 range.length = CFStringGetLength(compare_domain) - 1;
3009 s1 = CFStringCreateWithSubstring(NULL, compare_domain, range);
3010 if (s1 == NULL) {
3011 goto done;
3012 }
3013 s1_created = TRUE;
3014 } else {
3015 s1 = compare_domain;
3016 }
3017
3018 if (CFStringHasSuffix(match_domain, CFSTR("."))) {
3019 range.location = 0;
3020 range.length = CFStringGetLength(match_domain) - 1;
3021 s2 = CFStringCreateWithSubstring(NULL, match_domain, range);
3022 if (s2 == NULL) {
3023 goto done;
3024 }
3025 s2_created = TRUE;
3026 } else {
3027 s2 = match_domain;
3028 }
3029
3030 if (CFStringHasPrefix(s2, CFSTR("*."))) {
3031 range.location = 2;
3032 range.length = CFStringGetLength(s2)-2;
3033 s3 = CFStringCreateWithSubstring(NULL, s2, range);
3034 if (s3 == NULL) {
3035 goto done;
3036 }
3037 if (s2_created) {
3038 CFRelease(s2);
3039 }
3040 s2 = s3;
3041 s2_created = TRUE;
3042 }
3043
3044 ret = CFStringHasSuffix(s1, s2);
3045
3046 done :
3047
3048 if (s1_created) CFRelease(s1);
3049 if (s2_created) CFRelease(s2);
3050 return ret;
3051 }
3052
3053 static CFCharacterSetRef
3054 _SC_getNotDotOrStarCharacterSet (void)
3055 {
3056 static CFCharacterSetRef notDotOrStar = NULL;
3057 if (notDotOrStar == NULL) {
3058 CFCharacterSetRef dotOrStar = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, CFSTR(".*"));
3059 if (dotOrStar) {
3060 notDotOrStar = CFCharacterSetCreateInvertedSet(kCFAllocatorDefault, dotOrStar);
3061 CFRelease(dotOrStar);
3062 }
3063 }
3064 return notDotOrStar;
3065 }
3066
3067 static CFMutableStringRef
3068 _SC_createStringByTrimmingDotsAndStars (CFStringRef string)
3069 {
3070 CFCharacterSetRef notDotOrStar = _SC_getNotDotOrStarCharacterSet();
3071 CFRange entireString = CFRangeMake(0, CFStringGetLength(string));
3072 CFMutableStringRef result = CFStringCreateMutableCopy(kCFAllocatorDefault, entireString.length, string);
3073 CFRange start;
3074 CFRange end = CFRangeMake(entireString.length, 0);
3075
3076 if (CFStringFindCharacterFromSet(string, notDotOrStar, entireString, 0, &start) &&
3077 CFStringFindCharacterFromSet(string, notDotOrStar, entireString, kCFCompareBackwards, &end)) {
3078 if (start.location == kCFNotFound || end.location == kCFNotFound || start.location > end.location) {
3079 CFRelease(result);
3080 return NULL;
3081 }
3082 }
3083
3084 if ((end.location + 1) < entireString.length) {
3085 CFStringReplace(result, CFRangeMake(end.location + 1, entireString.length - (end.location + 1)), CFSTR(""));
3086 }
3087 if (start.location > 0) {
3088 CFStringReplace(result, CFRangeMake(0, start.location), CFSTR(""));
3089 }
3090
3091 return result;
3092 }
3093
3094 static CFIndex
3095 _SC_getCountOfStringInString (CFStringRef string, CFStringRef substring)
3096 {
3097 CFIndex count = 0;
3098 CFArrayRef ranges = CFStringCreateArrayWithFindResults(kCFAllocatorDefault, string, substring, CFRangeMake(0, CFStringGetLength(string)), 0);
3099 if (ranges != NULL) {
3100 count = CFArrayGetCount(ranges);
3101 CFRelease(ranges);
3102 }
3103 return count;
3104 }
3105
3106 Boolean
3107 _SC_hostMatchesDomain(CFStringRef hostname, CFStringRef domain)
3108 {
3109 Boolean result = FALSE;
3110 CFMutableStringRef trimmedHostname = NULL;
3111 CFMutableStringRef trimmedDomain = NULL;
3112
3113 if (!isA_CFString(hostname) || !isA_CFString(domain)) {
3114 goto done;
3115 }
3116
3117 trimmedHostname = _SC_createStringByTrimmingDotsAndStars(hostname);
3118 trimmedDomain = _SC_createStringByTrimmingDotsAndStars(domain);
3119
3120 if (!isA_CFString(trimmedHostname) || !isA_CFString(trimmedDomain)) {
3121 goto done;
3122 }
3123
3124 CFIndex numHostnameDots = _SC_getCountOfStringInString(trimmedHostname, CFSTR("."));
3125 CFIndex numDomainDots = _SC_getCountOfStringInString(trimmedDomain, CFSTR("."));
3126 if (numHostnameDots == numDomainDots) {
3127 result = CFEqual(trimmedHostname, trimmedDomain);
3128 } else if (numDomainDots > 0 && numDomainDots < numHostnameDots) {
3129 CFStringReplace(trimmedDomain, CFRangeMake(0, 0), CFSTR("."));
3130 result = CFStringHasSuffix(trimmedHostname, trimmedDomain);
3131 } else {
3132 result = FALSE;
3133 }
3134
3135 done:
3136 if (trimmedHostname) {
3137 CFRelease(trimmedHostname);
3138 }
3139 if (trimmedDomain) {
3140 CFRelease(trimmedDomain);
3141 }
3142 return result;
3143 }
3144
3145 /* VPN On Demand */
3146
3147 static CFDictionaryRef
3148 __SCNetworkConnectionCopyOnDemandConfiguration(void)
3149 {
3150 int changed = 1;
3151 int status;
3152 uint64_t triggersCount = 0;
3153 CFDictionaryRef configuration;
3154
3155 pthread_mutex_lock(&onDemand_notify_lock);
3156 if (onDemand_notify_token == -1) {
3157 status = notify_register_check(kSCNETWORKCONNECTION_ONDEMAND_NOTIFY_KEY, &onDemand_notify_token);
3158 if (status != NOTIFY_STATUS_OK) {
3159 SC_log(LOG_NOTICE, "notify_register_check() failed, status=%d", status);
3160 onDemand_notify_token = -1;
3161 }
3162 }
3163
3164 if (onDemand_notify_token != -1) {
3165 status = notify_check(onDemand_notify_token, &changed);
3166 if (status != NOTIFY_STATUS_OK) {
3167 SC_log(LOG_NOTICE, "notify_check() failed, status=%d", status);
3168 (void)notify_cancel(onDemand_notify_token);
3169 onDemand_notify_token = -1;
3170 }
3171 }
3172
3173 if (changed && (onDemand_notify_token != -1)) {
3174 status = notify_get_state(onDemand_notify_token, &triggersCount);
3175 if (status != NOTIFY_STATUS_OK) {
3176 SC_log(LOG_NOTICE, "notify_get_state() failed, status=%d", status);
3177 (void)notify_cancel(onDemand_notify_token);
3178 onDemand_notify_token = -1;
3179 }
3180 }
3181
3182 if (changed || onDemand_force_refresh) {
3183 CFStringRef key;
3184
3185 SC_log(LOG_INFO, "OnDemand information %s",
3186 (onDemand_configuration == NULL) ? "fetched" : "updated");
3187
3188 if (onDemand_configuration != NULL) {
3189 CFRelease(onDemand_configuration);
3190 onDemand_configuration = NULL;
3191 }
3192
3193 if ((triggersCount > 0) || onDemand_force_refresh) {
3194 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetOnDemand);
3195 onDemand_configuration = SCDynamicStoreCopyValue(NULL, key);
3196 CFRelease(key);
3197 if ((onDemand_configuration != NULL) && !isA_CFDictionary(onDemand_configuration)) {
3198 CFRelease(onDemand_configuration);
3199 onDemand_configuration = NULL;
3200 }
3201 }
3202
3203 onDemand_force_refresh = FALSE;
3204 }
3205
3206 configuration = (onDemand_configuration != NULL) ? CFRetain(onDemand_configuration) : NULL;
3207 pthread_mutex_unlock(&onDemand_notify_lock);
3208
3209 return configuration;
3210 }
3211
3212
3213 __private_extern__
3214 void
3215 __SCNetworkConnectionForceOnDemandConfigurationRefresh(void)
3216 {
3217 pthread_mutex_lock(&onDemand_notify_lock);
3218 onDemand_force_refresh = TRUE;
3219 pthread_mutex_unlock(&onDemand_notify_lock);
3220
3221 return;
3222 }
3223
3224
3225 static Boolean
3226 __SCNetworkConnectionShouldNeverMatch(CFDictionaryRef trigger, CFStringRef hostName, pid_t client_pid)
3227 {
3228 CFArrayRef exceptedProcesses;
3229 CFIndex exceptedProcessesCount;
3230 CFIndex exceptedProcessesIndex;
3231 CFArrayRef exceptions;
3232 CFIndex exceptionsCount;
3233 int exceptionsIndex;
3234
3235 // we have a matching domain, check against exception list
3236 exceptions = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandMatchDomainsNever);
3237 exceptionsCount = isA_CFArray(exceptions) ? CFArrayGetCount(exceptions) : 0;
3238 for (exceptionsIndex = 0; exceptionsIndex < exceptionsCount; exceptionsIndex++) {
3239 CFStringRef exception;
3240
3241 exception = CFArrayGetValueAtIndex(exceptions, exceptionsIndex);
3242 if (isA_CFString(exception) && _SC_domainEndsWithDomain(hostName, exception)) {
3243 // found matching exception
3244 SC_log(LOG_INFO, "OnDemand match exception");
3245 return TRUE;
3246 }
3247 }
3248
3249 if (client_pid != 0) {
3250 exceptedProcesses = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandPluginPIDs);
3251 exceptedProcessesCount = isA_CFArray(exceptedProcesses) ? CFArrayGetCount(exceptedProcesses) : 0;
3252 for (exceptedProcessesIndex = 0; exceptedProcessesIndex < exceptedProcessesCount; exceptedProcessesIndex++) {
3253 int pid;
3254 CFNumberRef pidRef;
3255
3256 pidRef = CFArrayGetValueAtIndex(exceptedProcesses, exceptedProcessesIndex);
3257 if (isA_CFNumber(pidRef) && CFNumberGetValue(pidRef, kCFNumberIntType, &pid)) {
3258 if (pid == client_pid) {
3259 return TRUE;
3260 }
3261 }
3262 }
3263 }
3264
3265 return FALSE;
3266 }
3267
3268 static CFStringRef
3269 __SCNetworkConnectionDomainGetMatchWithParameters(CFStringRef action, CFPropertyListRef actionParameters, CFStringRef hostName, CFStringRef *probeString)
3270 {
3271 CFArrayRef actionArray = NULL;
3272 CFIndex actionArraySize = 0;
3273 CFIndex i;
3274 CFStringRef matchDomain = NULL;
3275
3276 /* For now, only support EvaluateConnection, which takes a CFArray */
3277 if (!CFEqual(action, kSCValNetVPNOnDemandRuleActionEvaluateConnection) || !isA_CFArray(actionParameters)) {
3278 return NULL;
3279 }
3280
3281 actionArray = (CFArrayRef)actionParameters;
3282 actionArraySize = CFArrayGetCount(actionArray);
3283
3284 /* Process domain rules, with actions of ConnectIfNeeded and NeverConnect */
3285 for (i = 0; i < actionArraySize; i++) {
3286 CFStringRef domainAction = NULL;
3287 CFDictionaryRef domainRule = CFArrayGetValueAtIndex(actionArray, i);
3288 CFArrayRef domains = NULL;
3289 CFIndex domainsCount = 0;
3290 CFIndex domainsIndex;
3291
3292 if (!isA_CFDictionary(domainRule)) {
3293 continue;
3294 }
3295
3296 domains = CFDictionaryGetValue(domainRule, kSCPropNetVPNOnDemandRuleActionParametersDomains);
3297 if (!isA_CFArray(domains)) {
3298 continue;
3299 }
3300
3301 domainsCount = CFArrayGetCount(domains);
3302 for (domainsIndex = 0; domainsIndex < domainsCount; domainsIndex++) {
3303 CFStringRef domain;
3304 domain = CFArrayGetValueAtIndex(domains, domainsIndex);
3305 if (isA_CFString(domain) && _SC_domainEndsWithDomain(hostName, domain)) {
3306 matchDomain = domain;
3307 break;
3308 }
3309 }
3310
3311 if (matchDomain) {
3312 domainAction = CFDictionaryGetValue(domainRule, kSCPropNetVPNOnDemandRuleActionParametersDomainAction);
3313 if (isA_CFString(domainAction) && CFEqual(domainAction, kSCValNetVPNOnDemandRuleActionParametersDomainActionNeverConnect)) {
3314 return NULL;
3315 } else {
3316 /* If we found a match, save the optional probe string as well */
3317 if (probeString) {
3318 *probeString = CFDictionaryGetValue(domainRule, kSCPropNetVPNOnDemandRuleActionParametersRequiredURLStringProbe);
3319 }
3320 break;
3321 }
3322 }
3323 }
3324
3325 return matchDomain;
3326 }
3327
3328 static CFStringRef
3329 __SCNetworkConnectionDomainGetMatch(CFDictionaryRef trigger, CFStringRef hostName, Boolean onDemandRetry)
3330 {
3331 CFArrayRef domains;
3332 CFIndex domainsCount;
3333 int domainsIndex;
3334 CFStringRef key;
3335 CFStringRef match_domain = NULL;
3336
3337 /* Old configuration: always, never, on retry lists */
3338 key = onDemandRetry ? kSCNetworkConnectionOnDemandMatchDomainsOnRetry : kSCNetworkConnectionOnDemandMatchDomainsAlways;
3339
3340 domains = CFDictionaryGetValue(trigger, key);
3341 domainsCount = isA_CFArray(domains) ? CFArrayGetCount(domains) : 0;
3342 for (domainsIndex = 0; domainsIndex < domainsCount; domainsIndex++) {
3343 CFStringRef domain;
3344
3345 domain = CFArrayGetValueAtIndex(domains, domainsIndex);
3346 if (isA_CFString(domain) && _SC_domainEndsWithDomain(hostName, domain)) {
3347 match_domain = domain;
3348 break;
3349 }
3350 }
3351
3352 return match_domain;
3353 }
3354
3355
3356 static Boolean
3357 __SCNetworkConnectionShouldAlwaysConnect(CFDictionaryRef trigger)
3358 {
3359 CFStringRef action = CFDictionaryGetValue(trigger, kSCPropNetVPNOnDemandRuleAction);
3360 return (isA_CFString(action) && CFEqual(action, kSCValNetVPNOnDemandRuleActionConnect));
3361 }
3362
3363
3364 static Boolean
3365 __SCNetworkConnectionShouldIgnoreTrigger(CFDictionaryRef trigger)
3366 {
3367 CFStringRef action = CFDictionaryGetValue(trigger, kSCPropNetVPNOnDemandRuleAction);
3368
3369 if (isA_CFString(action) &&
3370 (CFEqual(action, kSCValNetVPNOnDemandRuleActionIgnore) ||
3371 CFEqual(action, kSCValNetVPNOnDemandRuleActionDisconnect))) {
3372 return TRUE;
3373 }
3374
3375 return FALSE;
3376 }
3377
3378
3379 static CFDictionaryRef
3380 __SCNetworkConnectionCopyMatchingTriggerWithName(CFDictionaryRef configuration,
3381 CFStringRef hostName,
3382 pid_t client_pid,
3383 Boolean onDemandRetry,
3384 CFDictionaryRef *match_info,
3385 Boolean *triggerNow,
3386 CFStringRef *probe_string)
3387 {
3388 CFDictionaryRef result = NULL;
3389 int sc_status = kSCStatusOK;
3390 CFArrayRef triggers;
3391 uint64_t triggersCount = 0;
3392 int triggersIndex;
3393 Boolean usedOnDemandRetry = FALSE;
3394
3395 if (triggerNow != NULL) {
3396 *triggerNow = FALSE;
3397 }
3398
3399 if (match_info != NULL) {
3400 *match_info = NULL;
3401 }
3402
3403 triggers = CFDictionaryGetValue(configuration, kSCNetworkConnectionOnDemandTriggers);
3404 triggersCount = isA_CFArray(triggers) ? CFArrayGetCount(triggers) : 0;
3405 for (triggersIndex = 0; triggersIndex < triggersCount; triggersIndex++) {
3406 CFStringRef matched_domain = NULL;
3407 CFStringRef matched_probe_string = NULL;
3408 CFDictionaryRef trigger;
3409 Boolean trigger_matched = FALSE;
3410
3411 usedOnDemandRetry = FALSE;
3412
3413 trigger = CFArrayGetValueAtIndex(triggers, triggersIndex);
3414 if (!isA_CFDictionary(trigger)) {
3415 // if not a valid "OnDemand" configuration
3416 continue;
3417 }
3418
3419 if (__SCNetworkConnectionShouldAlwaysConnect(trigger)) {
3420 /* If the trigger action is 'Connect', always match this trigger */
3421 /* First check the never match list */
3422 if (__SCNetworkConnectionShouldNeverMatch(trigger, hostName, client_pid)) {
3423 continue;
3424 }
3425 trigger_matched = TRUE;
3426 } else if (__SCNetworkConnectionShouldIgnoreTrigger(trigger)) {
3427 /* If the trigger action is 'Ignore' or 'Disconnect', skip this trigger */
3428 sc_status = kSCStatusConnectionIgnore;
3429 continue;
3430 } else {
3431 CFStringRef action = CFDictionaryGetValue(trigger, kSCPropNetVPNOnDemandRuleAction);
3432 CFArrayRef actionParameters = CFDictionaryGetValue(trigger, kSCPropNetVPNOnDemandRuleActionParameters);
3433 if (action && actionParameters) {
3434 matched_domain = __SCNetworkConnectionDomainGetMatchWithParameters(action, actionParameters, hostName, &matched_probe_string);
3435 usedOnDemandRetry = TRUE;
3436 } else {
3437 if (onDemandRetry) {
3438 matched_domain = __SCNetworkConnectionDomainGetMatch(trigger, hostName, TRUE);
3439 usedOnDemandRetry = TRUE;
3440 } else {
3441 matched_domain = __SCNetworkConnectionDomainGetMatch(trigger, hostName, FALSE);
3442 if (matched_domain == NULL && result == NULL) {
3443 /* Check the retry list if Always failed */
3444 matched_domain = __SCNetworkConnectionDomainGetMatch(trigger, hostName, TRUE);
3445 usedOnDemandRetry = TRUE;
3446 }
3447 }
3448 }
3449
3450 if (matched_domain) {
3451 if (__SCNetworkConnectionShouldNeverMatch(trigger, hostName, client_pid)) {
3452 matched_domain = NULL;
3453 continue;
3454 } else {
3455 trigger_matched = TRUE;
3456 }
3457 }
3458 }
3459
3460 if (trigger_matched) {
3461 // if we have a matching domain and there were no exceptions
3462 // then we pass back the OnDemand info
3463 if (match_info != NULL) {
3464 CFMutableDictionaryRef minfo;
3465 SCNetworkConnectionType type = kSCNetworkConnectionTypeIPLayerVPN;
3466 CFNumberRef type_num;
3467
3468 if (*match_info != NULL) {
3469 CFRelease(*match_info);
3470 *match_info = NULL;
3471 }
3472
3473 minfo = CFDictionaryCreateMutable(kCFAllocatorDefault,
3474 0,
3475 &kCFTypeDictionaryKeyCallBacks,
3476 &kCFTypeDictionaryValueCallBacks);
3477
3478 type_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &type);
3479 CFDictionarySetValue(minfo, kSCNetworkConnectionOnDemandMatchInfoVPNType, type_num);
3480 CFRelease(type_num);
3481 if (matched_domain) {
3482 CFDictionarySetValue(minfo, kSCNetworkConnectionOnDemandMatchInfoDomain, matched_domain);
3483 }
3484 CFDictionarySetValue(minfo,
3485 kSCNetworkConnectionOnDemandMatchInfoOnRetry,
3486 (usedOnDemandRetry ? kCFBooleanTrue : kCFBooleanFalse));
3487
3488 *match_info = minfo;
3489 }
3490
3491 if (probe_string != NULL) {
3492 if (*probe_string != NULL) {
3493 CFRelease(*probe_string);
3494 *probe_string = NULL;
3495 }
3496
3497 if (matched_probe_string) {
3498 *probe_string = CFRetain(matched_probe_string);
3499 }
3500 }
3501
3502 result = trigger;
3503
3504 /* If retry was requested, or we found Always match, trigger now */
3505 if (onDemandRetry || !usedOnDemandRetry) {
3506 if (triggerNow != NULL) {
3507 *triggerNow = TRUE;
3508 }
3509 break;
3510 }
3511
3512 /* If we matched the Retry list, but Always was requested,
3513 keep going through triggers in case one matches an Always */
3514 }
3515 }
3516
3517 if (result) {
3518 CFRetain(result);
3519 }
3520
3521 _SCErrorSet(sc_status);
3522 return result;
3523 }
3524
3525
3526 static CFDictionaryRef
3527 __SCNetworkConnectionCopyTriggerWithService(CFDictionaryRef configuration,
3528 CFStringRef service_id)
3529 {
3530 CFArrayRef triggers;
3531 uint64_t triggersCount = 0;
3532 int triggersIndex;
3533
3534 triggers = CFDictionaryGetValue(configuration, kSCNetworkConnectionOnDemandTriggers);
3535 triggersCount = isA_CFArray(triggers) ? CFArrayGetCount(triggers) : 0;
3536 for (triggersIndex = 0; triggersIndex < triggersCount; triggersIndex++) {
3537 CFDictionaryRef trigger;
3538 CFStringRef trigger_service_id;
3539
3540 trigger = CFArrayGetValueAtIndex(triggers, triggersIndex);
3541 if (!isA_CFDictionary(trigger)) {
3542 // if not a valid "OnDemand" configuration
3543 continue;
3544 }
3545
3546 trigger_service_id = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandServiceID);
3547 if (isA_CFString(trigger_service_id) && CFEqual(trigger_service_id, service_id)) {
3548 CFRetain(trigger);
3549 return trigger;
3550 }
3551 }
3552
3553 return NULL;
3554 }
3555
3556
3557 __private_extern__ CFDictionaryRef
3558 __SCNetworkConnectionCopyTokenParameters(SCNetworkConnectionRef connection)
3559 {
3560 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
3561 CFDictionaryRef parameters = NULL;
3562 uint8_t params_buffer[PPP_MACH_MAX_INLINE_DATA];
3563 uint32_t params_buffer_len = sizeof(params_buffer);
3564 int sc_status = kSCStatusOK;
3565 mach_port_t session_port;
3566 kern_return_t status;
3567
3568 pthread_mutex_lock(&connectionPrivate->lock);
3569
3570 parameters = connectionPrivate->flow_divert_token_params;
3571 if (parameters != NULL) {
3572 CFRetain(parameters);
3573 goto done;
3574 }
3575
3576 retry:
3577 if (parameters != NULL) {
3578 CFRelease(parameters);
3579 parameters = NULL;
3580 }
3581
3582 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
3583 if (session_port == MACH_PORT_NULL) {
3584 goto done;
3585 }
3586
3587 status = pppcontroller_flow_divert_copy_token_parameters(session_port, params_buffer, &params_buffer_len);
3588 if (status == KERN_SUCCESS) {
3589 if (params_buffer_len > 0) {
3590 CFDataRef params_data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
3591 params_buffer,
3592 params_buffer_len,
3593 kCFAllocatorNull);
3594 parameters = CFPropertyListCreateWithData(kCFAllocatorDefault,
3595 params_data,
3596 kCFPropertyListImmutable,
3597 NULL,
3598 NULL);
3599 CFRelease(params_data);
3600 }
3601 }
3602
3603 if (__SCNetworkConnectionNeedsRetry(connection, "__SCNetworkConnectionCopyTokenParameters()", status, &sc_status)) {
3604 goto retry;
3605 }
3606
3607 if (sc_status != kSCStatusOK) {
3608 _SCErrorSet(sc_status);
3609 }
3610
3611 done:
3612 if (parameters != NULL && connectionPrivate->flow_divert_token_params == NULL) {
3613 connectionPrivate->flow_divert_token_params = (CFDictionaryRef)CFRetain(parameters);
3614 }
3615
3616 pthread_mutex_unlock(&connectionPrivate->lock);
3617
3618 return parameters;
3619 }
3620
3621 Boolean
3622 __SCNetworkConnectionCopyOnDemandInfoWithName(SCDynamicStoreRef *storeP,
3623 CFStringRef hostName,
3624 Boolean onDemandRetry,
3625 CFStringRef *connectionServiceID,
3626 SCNetworkConnectionStatus *connectionStatus,
3627 CFStringRef *vpnRemoteAddress) /* CFDictionaryRef *info */
3628 {
3629 CFDictionaryRef configuration;
3630 Boolean ok = FALSE;
3631 int sc_status = kSCStatusOK;
3632 CFDictionaryRef trigger;
3633 Boolean trigger_now = FALSE;
3634
3635 configuration = __SCNetworkConnectionCopyOnDemandConfiguration();
3636 if (configuration == NULL) {
3637 _SCErrorSet(sc_status);
3638 return ok;
3639 }
3640
3641 trigger = __SCNetworkConnectionCopyMatchingTriggerWithName(configuration, hostName, 0, onDemandRetry, NULL, &trigger_now, NULL);
3642 if (trigger != NULL && trigger_now) {
3643 CFNumberRef num;
3644 SCNetworkConnectionStatus onDemandStatus = kSCNetworkConnectionDisconnected;
3645
3646 ok = TRUE;
3647
3648 if (!CFDictionaryGetValueIfPresent(trigger, kSCNetworkConnectionOnDemandStatus, (const void **)&num) ||
3649 !isA_CFNumber(num) ||
3650 !CFNumberGetValue(num, kCFNumberSInt32Type, &onDemandStatus)) {
3651 onDemandStatus = kSCNetworkConnectionDisconnected;
3652 }
3653 if (connectionStatus != NULL) {
3654 *connectionStatus = onDemandStatus;
3655 }
3656
3657 if (connectionServiceID != NULL) {
3658 *connectionServiceID = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandServiceID);
3659 *connectionServiceID = isA_CFString(*connectionServiceID);
3660 if ((*connectionServiceID != NULL) && (CFStringGetLength(*connectionServiceID) > 0)) {
3661 CFRetain(*connectionServiceID);
3662 } else {
3663 SC_log(LOG_INFO, "OnDemand%s configuration error, no serviceID",
3664 onDemandRetry ? " (on retry)" : "");
3665 *connectionServiceID = NULL;
3666 ok = FALSE;
3667 }
3668 }
3669
3670 if (vpnRemoteAddress != NULL) {
3671 *vpnRemoteAddress = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandRemoteAddress);
3672 *vpnRemoteAddress = isA_CFString(*vpnRemoteAddress);
3673 if ((*vpnRemoteAddress != NULL) && (CFStringGetLength(*vpnRemoteAddress) > 0)) {
3674 CFRetain(*vpnRemoteAddress);
3675 } else {
3676 SC_log(LOG_INFO, "OnDemand%s configuration error, no server address",
3677 onDemandRetry ? " (on retry)" : "");
3678 *vpnRemoteAddress = NULL;
3679 ok = FALSE;
3680 }
3681 }
3682
3683 if (!ok) {
3684 if ((connectionServiceID != NULL) && (*connectionServiceID != NULL)) {
3685 CFRelease(*connectionServiceID);
3686 *connectionServiceID = NULL;
3687 }
3688 if ((vpnRemoteAddress != NULL) && (*vpnRemoteAddress != NULL)) {
3689 CFRelease(*vpnRemoteAddress);
3690 *vpnRemoteAddress = NULL;
3691 }
3692 sc_status = kSCStatusFailed;
3693 } else {
3694 SC_log(LOG_INFO, "OnDemand%s match, connection status = %d",
3695 onDemandRetry ? " (on retry)" : "",
3696 onDemandStatus);
3697 }
3698 }
3699
3700 if (trigger) {
3701 CFRelease(trigger);
3702 }
3703
3704 // SC_log(LOG_INFO, "OnDemand domain name(s) not matched");
3705
3706 if (configuration != NULL) CFRelease(configuration);
3707 if (!ok) {
3708 _SCErrorSet(sc_status);
3709 }
3710 return ok;
3711 }
3712
3713 static Boolean
3714 __SCNetworkConnectionCopyUserPreferencesInternal(CFDictionaryRef selectionOptions,
3715 CFStringRef *serviceID,
3716 CFDictionaryRef *userOptions)
3717 {
3718 int prefsChanged = 1;
3719 int status;
3720 Boolean success = FALSE;
3721
3722 if (notify_userprefs_token == -1) {
3723 status = notify_register_check(k_NetworkConnect_Notification, &notify_userprefs_token);
3724 if (status != NOTIFY_STATUS_OK) {
3725 SC_log(LOG_NOTICE, "notify_register_check() failed, status=%d", status);
3726 (void)notify_cancel(notify_userprefs_token);
3727 notify_userprefs_token = -1;
3728 } else {
3729 // clear the "something has changed" state
3730 (void) notify_check(notify_userprefs_token, &prefsChanged);
3731 prefsChanged = 1;
3732 }
3733 }
3734 if (notify_userprefs_token != -1) {
3735 status = notify_check(notify_userprefs_token, &prefsChanged);
3736 if (status != NOTIFY_STATUS_OK) {
3737 SC_log(LOG_NOTICE, "notify_check() failed, status=%d", status);
3738 (void)notify_cancel(notify_userprefs_token);
3739 notify_userprefs_token = -1;
3740 }
3741 }
3742
3743
3744 *serviceID = NULL;
3745 *userOptions = NULL;
3746
3747 if (selectionOptions != NULL) {
3748 Boolean catchAllFound = FALSE;
3749 CFIndex catchAllService = 0;
3750 CFIndex catchAllConfig = 0;
3751 CFStringRef hostName = NULL;
3752 CFStringRef priority = NULL;
3753 CFArrayRef serviceNames = NULL;
3754 CFDictionaryRef services = NULL;
3755 CFIndex serviceIndex;
3756 CFIndex servicesCount;
3757
3758 hostName = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName);
3759 if (hostName == NULL) {
3760 hostName = CFDictionaryGetValue(selectionOptions, kSCPropNetPPPOnDemandHostName);
3761 }
3762 hostName = isA_CFString(hostName);
3763 if (hostName == NULL)
3764 goto done_selection; // if no hostname for matching
3765
3766 priority = CFDictionaryGetValue(selectionOptions, kSCPropNetPPPOnDemandPriority);
3767 if (!isA_CFString(priority))
3768 priority = kSCValNetPPPOnDemandPriorityDefault;
3769
3770
3771 if (!isA_CFArray(serviceNames))
3772 goto done_selection;
3773
3774
3775 if (!isA_CFDictionary(services)) {
3776 goto done_selection;
3777 }
3778
3779 servicesCount = CFArrayGetCount(serviceNames);
3780 for (serviceIndex = 0; serviceIndex < servicesCount; serviceIndex++) {
3781 CFIndex configIndex;
3782 CFIndex configsCount;
3783 CFArrayRef serviceConfigs;
3784 CFStringRef serviceName;
3785 int val;
3786
3787 serviceName = CFArrayGetValueAtIndex(serviceNames, serviceIndex);
3788 if (!isA_CFString(serviceName)) {
3789 continue;
3790 }
3791
3792 serviceConfigs = CFDictionaryGetValue(services, serviceName);
3793 if (!isA_CFArray(serviceConfigs)) {
3794 continue;
3795 }
3796
3797 configsCount = CFArrayGetCount(serviceConfigs);
3798 for (configIndex = 0; configIndex < configsCount; configIndex++) {
3799 CFNumberRef autodial;
3800 CFDictionaryRef config;
3801 CFDictionaryRef pppConfig;
3802
3803 config = CFArrayGetValueAtIndex(serviceConfigs, configIndex);
3804 if (!isA_CFDictionary(config)) {
3805 continue;
3806 }
3807
3808 pppConfig = CFDictionaryGetValue(config, kSCEntNetPPP);
3809 if (!isA_CFDictionary(pppConfig)) {
3810 continue;
3811 }
3812
3813 autodial = CFDictionaryGetValue(pppConfig, kSCPropNetPPPOnDemandEnabled);
3814 if (!isA_CFNumber(autodial)) {
3815 continue;
3816 }
3817
3818 CFNumberGetValue(autodial, kCFNumberIntType, &val);
3819 if (val) {
3820 CFArrayRef domains;
3821 CFIndex domainsCount;
3822 CFIndex domainsIndex;
3823
3824 /* we found an conditional connection enabled configuration */
3825
3826 /* check domain */
3827 domains = CFDictionaryGetValue(pppConfig, kSCPropNetPPPOnDemandDomains);
3828 if (!isA_CFArray(domains)) {
3829 continue;
3830 }
3831
3832 domainsCount = CFArrayGetCount(domains);
3833 for (domainsIndex = 0; domainsIndex < domainsCount; domainsIndex++) {
3834 CFStringRef domain;
3835
3836 domain = CFArrayGetValueAtIndex(domains, domainsIndex);
3837 if (!isA_CFString(domain)) {
3838 continue;
3839 }
3840
3841 if (!catchAllFound &&
3842 (CFStringCompare(domain, CFSTR(""), 0) == kCFCompareEqualTo
3843 || CFStringCompare(domain, CFSTR("."), 0) == kCFCompareEqualTo))
3844 {
3845 // found a catch all
3846 catchAllFound = TRUE;
3847 catchAllService = serviceIndex;
3848 catchAllConfig = configIndex;
3849 }
3850
3851 if (_SC_domainEndsWithDomain(hostName, domain)) {
3852 // found matching configuration
3853 *serviceID = serviceName;
3854 CFRetain(*serviceID);
3855 *userOptions = CFDictionaryCreateMutableCopy(NULL, 0, config);
3856 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, hostName);
3857 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCPropNetPPPOnDemandPriority, priority);
3858 addPasswordFromKeychain(*serviceID, userOptions);
3859 success = TRUE;
3860 goto done_selection;
3861 }
3862 }
3863 }
3864 }
3865 }
3866
3867 // config not found, do we have a catchall ?
3868 if (catchAllFound) {
3869 CFDictionaryRef config;
3870 CFArrayRef serviceConfigs;
3871 CFStringRef serviceName;
3872
3873 serviceName = CFArrayGetValueAtIndex(serviceNames, catchAllService);
3874 serviceConfigs = CFDictionaryGetValue(services, serviceName);
3875 config = CFArrayGetValueAtIndex(serviceConfigs, catchAllConfig);
3876
3877 *serviceID = serviceName;
3878 CFRetain(*serviceID);
3879 *userOptions = CFDictionaryCreateMutableCopy(NULL, 0, config);
3880 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, hostName);
3881 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCPropNetPPPOnDemandPriority, priority);
3882 addPasswordFromKeychain(*serviceID, userOptions);
3883 success = TRUE;
3884 goto done_selection;
3885 }
3886
3887 done_selection:
3888
3889 if (serviceNames) {
3890 CFRelease(serviceNames);
3891 }
3892 if (services) {
3893 CFRelease(services);
3894 }
3895
3896 if (debug > 1) {
3897 SC_log(LOG_INFO, "SCNetworkConnectionCopyUserPreferences %s", success ? "succeeded" : "failed");
3898 SC_log(LOG_INFO, "Selection options: %@", selectionOptions);
3899 }
3900
3901 return success;
3902 }
3903
3904 /* we don't have selection options */
3905
3906 // (1) Figure out which service ID we care about, allocate it into passed "serviceID"
3907 success = SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(serviceID);
3908
3909 if (success && (*serviceID != NULL)) {
3910 // (2) Get the list of user data for this service ID
3911 CFPropertyListRef userServices = NULL;
3912
3913
3914 // (3) We are expecting an array if the user has defined records for this service ID or NULL if the user hasn't
3915 if (userServices != NULL) {
3916 if (isA_CFArray(userServices)) {
3917 // (4) Get the default set of user options for this service
3918 success = SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray((CFArrayRef)userServices,
3919 userOptions);
3920 if(success && (userOptions != NULL)) {
3921 addPasswordFromKeychain(*serviceID, userOptions);
3922 }
3923 } else {
3924 SC_log(LOG_INFO, "Error, userServices are not of type CFArray!");
3925 }
3926
3927 CFRelease(userServices); // this is OK because SCNetworkConnectionPrivateISExpectedCFType() checks for NULL
3928 }
3929 }
3930
3931 if (debug > 1) {
3932 SC_log(LOG_INFO, "SCNetworkConnectionCopyUserPreferences %@, no selection options",
3933 success ? CFSTR("succeeded") : CFSTR("failed"));
3934 }
3935
3936 return success;
3937 }
3938
3939
3940 Boolean
3941 SCNetworkConnectionCopyUserPreferences(CFDictionaryRef selectionOptions,
3942 CFStringRef *serviceID,
3943 CFDictionaryRef *userOptions)
3944 {
3945 Boolean success = FALSE;
3946
3947
3948 /* initialize runtime */
3949 pthread_once(&initialized, __SCNetworkConnectionInitialize);
3950
3951 /* first check for new VPN OnDemand style */
3952 if (selectionOptions != NULL) {
3953 CFStringRef hostName;
3954
3955 hostName = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName);
3956 if (isA_CFString(hostName)) {
3957 CFStringRef connectionServiceID = NULL;
3958 SCNetworkConnectionStatus connectionStatus = kSCNetworkConnectionInvalid;
3959 Boolean onDemandRetry;
3960 CFTypeRef val;
3961
3962 val = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandRetry);
3963 onDemandRetry = isA_CFBoolean(val) ? CFBooleanGetValue(val) : TRUE;
3964
3965 success = __SCNetworkConnectionCopyOnDemandInfoWithName(NULL,
3966 hostName,
3967 onDemandRetry,
3968 &connectionServiceID,
3969 &connectionStatus,
3970 NULL);
3971 if (debug > 1) {
3972 SC_log(LOG_INFO, "__SCNetworkConnectionCopyOnDemandInfoWithName: return %d, status %d",
3973 success,
3974 connectionStatus);
3975 }
3976
3977 if (success) {
3978 // if the hostname matches an OnDemand domain
3979 if (connectionStatus == kSCNetworkConnectionConnected) {
3980 // if we are already connected
3981 if (connectionServiceID != NULL) {
3982 CFRelease(connectionServiceID);
3983 }
3984 return FALSE;
3985 }
3986
3987 *serviceID = connectionServiceID;
3988 *userOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
3989 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, hostName);
3990 return TRUE;
3991 } else if (!onDemandRetry) {
3992 // if the hostname does not match an OnDemand domain and we have
3993 // not yet issued an initial DNS query (i.e. it's not a query
3994 // being retried after the VPN has been established) then we're
3995 // done
3996 return FALSE;
3997 }
3998 }
3999 }
4000
4001 return __SCNetworkConnectionCopyUserPreferencesInternal(selectionOptions, serviceID, userOptions);
4002 }
4003
4004
4005 Boolean
4006 SCNetworkConnectionOnDemandShouldRetryOnFailure (SCNetworkConnectionRef connection)
4007 {
4008 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
4009 CFDictionaryRef match_info = NULL;
4010
4011 if (!isA_SCNetworkConnection(connection)) {
4012 _SCErrorSet(kSCStatusInvalidArgument);
4013 goto fail;
4014 }
4015
4016 if (connectionPrivate->service == NULL) {
4017 _SCErrorSet(kSCStatusConnectionNoService);
4018 goto fail;
4019 }
4020
4021 if (isA_CFDictionary(connectionPrivate->on_demand_user_options)) {
4022 match_info = CFDictionaryGetValue(connectionPrivate->on_demand_user_options, kSCNetworkConnectionSelectionOptionOnDemandMatchInfo);
4023 if (isA_CFDictionary(match_info)) {
4024 CFBooleanRef onRetry = CFDictionaryGetValue(match_info, kSCNetworkConnectionOnDemandMatchInfoOnRetry);
4025 if (isA_CFBoolean(onRetry)) {
4026 return CFBooleanGetValue(onRetry);
4027 }
4028 }
4029 }
4030
4031 fail:
4032 return FALSE;
4033 }
4034
4035
4036 // Mask is optional in routes dictionary; if not present, whole addresses are matched
4037 Boolean
4038 __SCNetworkConnectionIPv4AddressMatchesRoutes (struct sockaddr_in *addr_in, CFDictionaryRef routes)
4039 {
4040 CFIndex count;
4041 CFIndex i;
4042 CFDataRef maskData = NULL;
4043 struct in_addr *maskDataArray;
4044 CFDataRef routeaddrData = NULL;
4045 struct in_addr *routeaddrDataArray;
4046
4047 if (!isA_CFDictionary(routes)) {
4048 return FALSE;
4049 }
4050
4051 routeaddrData = CFDictionaryGetValue(routes, kSCNetworkConnectionNetworkInfoAddresses);
4052 maskData = CFDictionaryGetValue(routes, kSCNetworkConnectionNetworkInfoMasks);
4053
4054 /* routeaddrData and maskData are packed arrays of addresses; make sure they have the same length */
4055 if (!isA_CFData(routeaddrData) || (maskData && (!isA_CFData(maskData) || CFDataGetLength(routeaddrData) != CFDataGetLength(maskData)))) {
4056 return FALSE;
4057 }
4058
4059 routeaddrDataArray = (struct in_addr*)(void*)CFDataGetBytePtr(routeaddrData);
4060 if (maskData) {
4061 maskDataArray = (struct in_addr*)(void*)CFDataGetBytePtr(maskData);
4062 }
4063
4064 count = CFDataGetLength(routeaddrData) / sizeof(struct in_addr);
4065 for (i=0; i<count; i++) {
4066 struct in_addr routeAddr = *routeaddrDataArray;
4067
4068 if (maskData) {
4069 struct in_addr mask = *maskDataArray;
4070
4071 if ((addr_in->sin_addr.s_addr & mask.s_addr) == (routeAddr.s_addr & mask.s_addr)) {
4072 return TRUE;
4073 }
4074 maskDataArray++;
4075 } else {
4076 if (addr_in->sin_addr.s_addr == routeAddr.s_addr) {
4077 return TRUE;
4078 }
4079 }
4080 routeaddrDataArray++;
4081 }
4082 return FALSE;
4083 }
4084
4085
4086 void
4087 __SCNetworkConnectionMaskIPv6Address(struct in6_addr *addr, struct in6_addr *mask)
4088 {
4089 int i;
4090
4091 for (i = 0; i < sizeof(struct in6_addr); i++)
4092 addr->s6_addr[i] &= mask->s6_addr[i];
4093 }
4094
4095
4096 // Mask is optional in routes dictionary; if not present, whole addresses are matched
4097 Boolean
4098 __SCNetworkConnectionIPv6AddressMatchesRoutes (struct sockaddr_in6 *addr_in6, CFDictionaryRef routes)
4099 {
4100 CFIndex count;
4101 CFIndex i;
4102 CFDataRef maskData = NULL;
4103 struct in6_addr *maskDataArray;
4104 CFDataRef routeaddrData = NULL;
4105 struct in6_addr *routeaddrDataArray;
4106
4107 if (!isA_CFDictionary(routes)) {
4108 return FALSE;
4109 }
4110
4111 routeaddrData = CFDictionaryGetValue(routes, kSCNetworkConnectionNetworkInfoAddresses);
4112 maskData = CFDictionaryGetValue(routes, kSCNetworkConnectionNetworkInfoMasks);
4113
4114 /* routeaddrData and maskData are packed arrays of addresses; make sure they have the same length */
4115 if (!isA_CFData(routeaddrData) || (maskData && (!isA_CFData(maskData) || CFDataGetLength(routeaddrData) != CFDataGetLength(maskData)))) {
4116 return FALSE;
4117 }
4118
4119 routeaddrDataArray = (struct in6_addr*)(void*)CFDataGetBytePtr(routeaddrData);
4120 if (maskData) {
4121 maskDataArray = (struct in6_addr*)(void*)CFDataGetBytePtr(maskData);
4122 }
4123
4124 count = CFDataGetLength(routeaddrData) / sizeof(struct in6_addr);
4125 for (i=0; i<count; i++) {
4126 if (maskData) {
4127 struct in6_addr cmpAddr;
4128 struct in6_addr *mask = maskDataArray;
4129 struct in6_addr routeAddr;
4130
4131 memcpy(&routeAddr, routeaddrDataArray, sizeof(routeAddr));
4132 memcpy(&cmpAddr, &addr_in6->sin6_addr, sizeof(cmpAddr));
4133 __SCNetworkConnectionMaskIPv6Address(&routeAddr, mask);
4134 __SCNetworkConnectionMaskIPv6Address(&cmpAddr, mask);
4135 maskDataArray++;
4136 if (!memcmp(&routeAddr, &cmpAddr, sizeof(routeAddr))) {
4137 return TRUE;
4138 }
4139 } else {
4140 if (!memcmp(routeaddrDataArray, &addr_in6->sin6_addr, sizeof(struct in6_addr))) {
4141 return TRUE;
4142 }
4143 }
4144
4145 routeaddrDataArray++;
4146 }
4147 return FALSE;
4148 }
4149
4150
4151 static Boolean
4152 __SCNetworkConnectionAddressMatchesRedirectedDNS(CFDictionaryRef trigger, const struct sockaddr *input_addr)
4153 {
4154 CFBooleanRef redirectedRef = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandDNSRedirectDetected);
4155
4156 if (isA_CFBoolean(redirectedRef) && CFBooleanGetValue(redirectedRef)) {
4157 /* DNS is redirected. Look for address list. */
4158 CFDictionaryRef redirectedAddressesRef = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandDNSRedirectedAddresses);
4159
4160 if (isA_CFDictionary(redirectedAddressesRef)) {
4161 if (input_addr->sa_family == AF_INET) {
4162 return __SCNetworkConnectionIPv4AddressMatchesRoutes((struct sockaddr_in*)(void*)input_addr, CFDictionaryGetValue(redirectedAddressesRef, kSCNetworkConnectionNetworkInfoIPv4));
4163 } else if (input_addr->sa_family == AF_INET6) {
4164 return __SCNetworkConnectionIPv6AddressMatchesRoutes((struct sockaddr_in6*)(void*)input_addr, CFDictionaryGetValue(redirectedAddressesRef, kSCNetworkConnectionNetworkInfoIPv6));
4165 }
4166 }
4167 }
4168
4169 return FALSE;
4170 }
4171
4172 /* If the required probe has failed, we need to tunnel the address. Equivalent to redirected DNS. */
4173 static Boolean
4174 __SCNetworkConnectionRequiredProbeFailed (CFDictionaryRef trigger, CFStringRef probeString)
4175 {
4176 CFDictionaryRef probeResults = NULL;
4177
4178 if (!isA_CFString(probeString)) {
4179 return FALSE;
4180 }
4181
4182 probeResults = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandProbeResults);
4183 if (!isA_CFDictionary(probeResults)) {
4184 return TRUE;
4185 }
4186
4187 CFBooleanRef result = CFDictionaryGetValue(probeResults, probeString);
4188
4189 /* Only a value of kCFBooleanFalse marks the probe as failed */
4190 return (isA_CFBoolean(result) && !CFBooleanGetValue(result));
4191 }
4192
4193 Boolean
4194 SCNetworkConnectionCanTunnelAddress (SCNetworkConnectionRef connection, const struct sockaddr *address, Boolean *startImmediately)
4195 {
4196 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
4197 CFStringRef serviceID = NULL;
4198 CFDictionaryRef configuration = NULL;
4199 CFDictionaryRef trigger = NULL;
4200 CFDictionaryRef tunneledNetworks = NULL;
4201 sa_family_t address_family = AF_UNSPEC;
4202 Boolean success = FALSE;
4203
4204 if (startImmediately) {
4205 *startImmediately = FALSE;
4206 }
4207
4208 if (address == NULL) {
4209 goto done;
4210 }
4211
4212 address_family = address->sa_family;
4213 if (address_family != AF_INET && address_family != AF_INET6) {
4214 goto done;
4215 }
4216
4217 if (!isA_SCNetworkConnection(connection)) {
4218 _SCErrorSet(kSCStatusInvalidArgument);
4219 goto done;
4220 }
4221
4222 if (connectionPrivate->service == NULL) {
4223 _SCErrorSet(kSCStatusConnectionNoService);
4224 goto done;
4225 }
4226
4227 serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service);
4228 if (!isA_CFString(serviceID)) {
4229 goto done;
4230 }
4231
4232 configuration = __SCNetworkConnectionCopyOnDemandConfiguration();
4233 if (configuration == NULL) {
4234 goto done;
4235 }
4236
4237 trigger = __SCNetworkConnectionCopyTriggerWithService(configuration, serviceID);
4238 if (trigger == NULL) {
4239 goto done;
4240 }
4241
4242 if (__SCNetworkConnectionRequiredProbeFailed(trigger, connectionPrivate->on_demand_required_probe)) {
4243 /* If probe failed, we can't trust DNS - connect now */
4244 if (startImmediately) {
4245 *startImmediately = TRUE;
4246 }
4247 success = TRUE;
4248 goto done;
4249 }
4250
4251 if (__SCNetworkConnectionAddressMatchesRedirectedDNS(trigger, address)) {
4252 if (startImmediately) {
4253 *startImmediately = TRUE;
4254 }
4255 success = TRUE;
4256 goto done;
4257 }
4258
4259 tunneledNetworks = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandTunneledNetworks);
4260 if (!isA_CFDictionary(tunneledNetworks)) {
4261 goto done;
4262 }
4263
4264 if (address_family == AF_INET) {
4265 CFDictionaryRef ip_dict;
4266 Boolean matches = FALSE;
4267 struct sockaddr_in *addr_in = (struct sockaddr_in *)(void*)address;
4268
4269 ip_dict = CFDictionaryGetValue(tunneledNetworks, kSCNetworkConnectionNetworkInfoIPv4);
4270 if (!isA_CFDictionary(ip_dict)) {
4271 goto done;
4272 }
4273
4274 matches = __SCNetworkConnectionIPv4AddressMatchesRoutes(addr_in, CFDictionaryGetValue(ip_dict, kSCNetworkConnectionNetworkInfoIncludedRoutes));
4275
4276 if (matches) {
4277 if (!__SCNetworkConnectionIPv4AddressMatchesRoutes(addr_in, CFDictionaryGetValue(ip_dict, kSCNetworkConnectionNetworkInfoExcludedRoutes))) {
4278 success = TRUE;
4279 goto done;
4280 }
4281 }
4282 } else {
4283 CFDictionaryRef ip6_dict;
4284 Boolean matches = FALSE;
4285 struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)(void*)address;
4286
4287 ip6_dict = CFDictionaryGetValue(tunneledNetworks, kSCNetworkConnectionNetworkInfoIPv6);
4288 if (!isA_CFDictionary(ip6_dict)) {
4289 goto done;
4290 }
4291
4292 matches = __SCNetworkConnectionIPv6AddressMatchesRoutes(addr_in6, CFDictionaryGetValue(ip6_dict, kSCNetworkConnectionNetworkInfoIncludedRoutes));
4293
4294 if (matches) {
4295 if (!__SCNetworkConnectionIPv6AddressMatchesRoutes(addr_in6, CFDictionaryGetValue(ip6_dict, kSCNetworkConnectionNetworkInfoExcludedRoutes))) {
4296 success = TRUE;
4297 goto done;
4298 }
4299 }
4300 }
4301 done:
4302 if (configuration) {
4303 CFRelease(configuration);
4304 }
4305 if (trigger) {
4306 CFRelease(trigger);
4307 }
4308 return success;
4309 }
4310
4311 Boolean
4312 SCNetworkConnectionSelectServiceWithOptions(SCNetworkConnectionRef connection, CFDictionaryRef selectionOptions)
4313 {
4314 CFStringRef account_identifier = NULL;
4315 CFDictionaryRef configuration = NULL;
4316 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
4317 CFDictionaryRef found_trigger = NULL;
4318 CFStringRef host_name = NULL;
4319 Boolean is_retry = TRUE;
4320 CFDictionaryRef match_info = NULL;
4321 CFMutableDictionaryRef new_user_options = NULL;
4322 SCNetworkConnectionStatus on_demand_status = kSCNetworkConnectionInvalid;
4323 CFStringRef requiredProbe = NULL;
4324 CFStringRef service_id = NULL;
4325 Boolean skip_prefs = FALSE;
4326 Boolean success = TRUE;
4327 CFDictionaryRef user_options = NULL;
4328
4329 if (!isA_SCNetworkConnection(connection)) {
4330 _SCErrorSet(kSCStatusInvalidArgument);
4331 success = FALSE;
4332 goto done;
4333 }
4334
4335 /* Can't call this on a connection that is already associated with a service */
4336 if (connectionPrivate->service != NULL) {
4337 _SCErrorSet(kSCStatusInvalidArgument);
4338 success = FALSE;
4339 goto done;
4340 }
4341
4342 if (isA_CFDictionary(selectionOptions)) {
4343 CFBooleanRef no_user_prefs = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionNoUserPrefs);
4344 CFBooleanRef retry = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandRetry);
4345
4346 account_identifier = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandAccountIdentifier);
4347 host_name = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName);
4348 skip_prefs = (isA_CFBoolean(no_user_prefs) && CFBooleanGetValue(no_user_prefs));
4349
4350 if (isA_CFBoolean(retry)) {
4351 is_retry = CFBooleanGetValue(retry);
4352 }
4353 }
4354
4355 configuration = __SCNetworkConnectionCopyOnDemandConfiguration();
4356
4357 /* First, check for a match with the App Layer rules */
4358 service_id = VPNAppLayerCopyMatchingService(connectionPrivate->client_audit_token,
4359 connectionPrivate->client_pid,
4360 connectionPrivate->client_uuid,
4361 connectionPrivate->client_bundle_id,
4362 host_name,
4363 account_identifier,
4364 &match_info);
4365 if (service_id != NULL) {
4366 Boolean use_app_layer = TRUE;
4367
4368 if (isA_CFDictionary(configuration)) {
4369 found_trigger = __SCNetworkConnectionCopyTriggerWithService(configuration, service_id);
4370 if (found_trigger != NULL) {
4371 CFNumberRef status_num = CFDictionaryGetValue(found_trigger, kSCNetworkConnectionOnDemandStatus);
4372 if (isA_CFNumber(status_num)) {
4373 CFNumberGetValue(status_num, kCFNumberIntType, &on_demand_status);
4374 }
4375 /*
4376 * If the trigger should be ignored, still use App Layer VPN if it is already connected or
4377 * is in the process of connecting.
4378 */
4379 if (__SCNetworkConnectionShouldIgnoreTrigger(found_trigger) &&
4380 on_demand_status != kSCNetworkConnectionConnecting &&
4381 on_demand_status != kSCNetworkConnectionConnected)
4382 {
4383 use_app_layer = FALSE;
4384 }
4385 }
4386 }
4387
4388 if (use_app_layer) {
4389 /* If this is not the 'OnRetry' call, and the service has not yet started, the match may need to return false */
4390 if (!is_retry &&
4391 match_info != NULL &&
4392 on_demand_status != kSCNetworkConnectionConnecting &&
4393 on_demand_status != kSCNetworkConnectionConnected) {
4394 CFBooleanRef matchedOnRetry = CFDictionaryGetValue(match_info, kSCNetworkConnectionOnDemandMatchInfoOnRetry);
4395 if (matchedOnRetry && CFBooleanGetValue(matchedOnRetry)) {
4396 /* Don't return that we matched always; wait for SCNetworkConnectionOnDemandShouldRetryOnFailure */
4397 success = FALSE;
4398 }
4399 }
4400 connectionPrivate->type = kSCNetworkConnectionTypeAppLayerVPN;
4401 goto search_done;
4402 } else {
4403 CFRelease(service_id);
4404 service_id = NULL;
4405 if (match_info != NULL) {
4406 CFRelease(match_info);
4407 match_info = NULL;
4408 }
4409 if (found_trigger != NULL) {
4410 CFRelease(found_trigger);
4411 found_trigger = NULL;
4412 }
4413 }
4414 }
4415
4416 /* Next, check the IP layer rules */
4417 if (isA_CFDictionary(configuration) && host_name != NULL) {
4418 Boolean triggerNow = FALSE;
4419
4420 found_trigger = __SCNetworkConnectionCopyMatchingTriggerWithName(configuration, host_name, connectionPrivate->client_pid, is_retry, &match_info, &triggerNow, &requiredProbe);
4421 if (found_trigger != NULL) {
4422 service_id = CFDictionaryGetValue(found_trigger, kSCNetworkConnectionOnDemandServiceID);
4423 if (isA_CFString(service_id)) {
4424 CFRetain(service_id);
4425 connectionPrivate->type = kSCNetworkConnectionTypeIPLayerVPN;
4426 } else {
4427 service_id = NULL;
4428 }
4429 if (!triggerNow) {
4430 success = FALSE;
4431 }
4432 goto search_done;
4433 } else if (!is_retry) {
4434 goto search_done;
4435 }
4436
4437 if (match_info != NULL) {
4438 CFRelease(match_info);
4439 match_info = NULL;
4440 }
4441 }
4442
4443 /* Next, check the user preferences */
4444 if (!skip_prefs && __SCNetworkConnectionCopyUserPreferencesInternal(selectionOptions, &service_id, &user_options)) {
4445 CFMutableDictionaryRef minfo;
4446 CFNumberRef type_num;
4447
4448 if (isA_CFDictionary(configuration)) {
4449 found_trigger = __SCNetworkConnectionCopyTriggerWithService(configuration, service_id);
4450 }
4451 connectionPrivate->type = kSCNetworkConnectionTypePPP;
4452
4453 minfo = CFDictionaryCreateMutable(NULL,
4454 0,
4455 &kCFTypeDictionaryKeyCallBacks,
4456 &kCFTypeDictionaryValueCallBacks);
4457 type_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &connectionPrivate->type);
4458 CFDictionarySetValue(minfo, kSCNetworkConnectionOnDemandMatchInfoVPNType, type_num);
4459 CFRelease(type_num);
4460 match_info = minfo;
4461 goto search_done;
4462 }
4463
4464 search_done:
4465 if (service_id == NULL) {
4466 _SCErrorSet(kSCStatusOK);
4467 success = FALSE;
4468 goto done;
4469 }
4470
4471 connectionPrivate->service = _SCNetworkServiceCopyActive(NULL, service_id);
4472 if (connectionPrivate->service == NULL) {
4473 _SCErrorSet(kSCStatusOK);
4474 success = FALSE;
4475 goto done;
4476 }
4477
4478 if (found_trigger != NULL) {
4479 if (connectionPrivate->on_demand_info) {
4480 CFRelease(connectionPrivate->on_demand_info);
4481 }
4482 connectionPrivate->on_demand_info = found_trigger;
4483 CFRetain(connectionPrivate->on_demand_info);
4484
4485 if (on_demand_status == kSCNetworkConnectionInvalid) {
4486 CFNumberRef status_num = CFDictionaryGetValue(found_trigger, kSCNetworkConnectionOnDemandStatus);
4487 if (isA_CFNumber(status_num)) {
4488 CFNumberGetValue(status_num, kCFNumberIntType, &on_demand_status);
4489 }
4490 }
4491
4492 if (on_demand_status != kSCNetworkConnectionConnected) {
4493 if (connectionPrivate->type == kSCNetworkConnectionTypeAppLayerVPN) {
4494 /* Check App Layer OnDemand flag */
4495 CFBooleanRef app_on_demand_enabled =
4496 CFDictionaryGetValue(found_trigger, kSCNetworkConnectionOnDemandMatchAppEnabled);
4497 if (isA_CFBoolean(app_on_demand_enabled) && CFBooleanGetValue(app_on_demand_enabled)) {
4498 connectionPrivate->on_demand = TRUE;
4499 }
4500 } else {
4501 connectionPrivate->on_demand = TRUE;
4502 }
4503 }
4504 } else if (connectionPrivate->type == kSCNetworkConnectionTypePPP) {
4505 /* If we got the service from __SCNetworkConnectionCopyUserPreferencesInternal, then it's on demand */
4506 connectionPrivate->on_demand = TRUE;
4507 }
4508
4509 if (user_options == NULL) {
4510 new_user_options = CFDictionaryCreateMutable(kCFAllocatorDefault,
4511 0,
4512 &kCFTypeDictionaryKeyCallBacks,
4513 &kCFTypeDictionaryValueCallBacks);
4514 } else {
4515 new_user_options = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, user_options);
4516 }
4517
4518 if (host_name != NULL) {
4519 CFDictionarySetValue(new_user_options, kSCNetworkConnectionSelectionOptionOnDemandHostName, host_name);
4520 }
4521
4522 if (connectionPrivate->on_demand && match_info != NULL) {
4523 CFDictionarySetValue(new_user_options, kSCNetworkConnectionSelectionOptionOnDemandMatchInfo, match_info);
4524 }
4525
4526 connectionPrivate->on_demand_user_options = new_user_options;
4527 CFRetain(connectionPrivate->on_demand_user_options);
4528
4529 if (requiredProbe) {
4530 connectionPrivate->on_demand_required_probe = requiredProbe;
4531 CFRetain(connectionPrivate->on_demand_required_probe);
4532 }
4533
4534 done:
4535 if (service_id != NULL) {
4536 CFRelease(service_id);
4537 }
4538
4539 if (configuration != NULL) {
4540 CFRelease(configuration);
4541 }
4542
4543 if (found_trigger != NULL) {
4544 CFRelease(found_trigger);
4545 }
4546
4547 if (user_options != NULL) {
4548 CFRelease(user_options);
4549 }
4550
4551 if (new_user_options != NULL) {
4552 CFRelease(new_user_options);
4553 }
4554
4555 if (match_info != NULL) {
4556 CFRelease(match_info);
4557 }
4558
4559 if (requiredProbe != NULL) {
4560 CFRelease(requiredProbe);
4561 }
4562
4563 return success;
4564 }
4565
4566 //*******************************************************************************************
4567 // SCNetworkConnectionPrivateCopyDefaultServiceIDForDial
4568 // ----------------------------------------------------
4569 // Try to find the service id to connect
4570 // (1) Start by looking at the last service used in Network Pref / Network menu extra
4571 // (2) If Network Pref / Network menu extra has not been used, find the PPP service
4572 // with the highest ordering
4573 //********************************************************************************************
4574 static Boolean
4575 SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(CFStringRef *serviceID)
4576 {
4577 Boolean foundService = FALSE;
4578 CFPropertyListRef lastServiceSelectedInIC = NULL;
4579
4580
4581
4582 // we found the service the user last had open in IC
4583 if (lastServiceSelectedInIC != NULL) {
4584 // make sure its a PPP service
4585 if (SCNetworkConnectionPrivateIsPPPService(lastServiceSelectedInIC, kSCValNetInterfaceSubTypePPPSerial, kSCValNetInterfaceSubTypePPPoE)) {
4586 // make sure the service that we found is valid
4587 CFDictionaryRef dict;
4588 CFStringRef key;
4589
4590 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
4591 kSCDynamicStoreDomainSetup,
4592 lastServiceSelectedInIC,
4593 kSCEntNetInterface);
4594 dict = SCDynamicStoreCopyValue(NULL, key);
4595 CFRelease(key);
4596 if (dict != NULL) {
4597 CFRelease(dict);
4598 *serviceID = CFRetain(lastServiceSelectedInIC);
4599 foundService = TRUE;
4600 }
4601 }
4602 CFRelease(lastServiceSelectedInIC);
4603 }
4604
4605 if (!foundService) {
4606 foundService = SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(serviceID);
4607 }
4608
4609 return foundService;
4610 }
4611
4612 //********************************************************************************
4613 // SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore
4614 // -------------------------------------------------------
4615 // Find the highest ordered PPP service in the dynamic store
4616 //********************************************************************************
4617 static Boolean
4618 SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(CFStringRef *serviceID)
4619 {
4620 CFDictionaryRef dict = NULL;
4621 CFStringRef key = NULL;
4622 CFArrayRef serviceIDs = NULL;
4623 Boolean success = FALSE;
4624
4625 *serviceID = NULL;
4626
4627 do {
4628 CFIndex count;
4629 CFIndex i;
4630
4631 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainSetup, kSCEntNetIPv4);
4632 if (key == NULL) {
4633 fprintf(stderr, "Error, Setup Key == NULL!\n");
4634 break;
4635 }
4636
4637 dict = SCDynamicStoreCopyValue(NULL, key);
4638 if (!isA_CFDictionary(dict)) {
4639 fprintf(stderr, "no global IPv4 entity\n");
4640 break;
4641 }
4642
4643 serviceIDs = CFDictionaryGetValue(dict, kSCPropNetServiceOrder); // array of service id's
4644 if (!isA_CFArray(serviceIDs)) {
4645 fprintf(stderr, "service order not specified\n");
4646 break;
4647 }
4648
4649 count = CFArrayGetCount(serviceIDs);
4650 for (i = 0; i < count; i++) {
4651 CFStringRef service = CFArrayGetValueAtIndex(serviceIDs, i);
4652
4653 if (SCNetworkConnectionPrivateIsPPPService(service, kSCValNetInterfaceSubTypePPPSerial, kSCValNetInterfaceSubTypePPPoE)) {
4654 *serviceID = CFRetain(service);
4655 success = TRUE;
4656 break;
4657 }
4658 }
4659 } while (FALSE);
4660
4661 if (key != NULL) CFRelease(key);
4662 if (dict != NULL) CFRelease(dict);
4663
4664 return success;
4665 }
4666
4667 //********************************************************************************
4668 // SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray
4669 // ---------------------------------------------------------
4670 // Copy over user preferences for a particular service if they exist
4671 //********************************************************************************
4672 static Boolean
4673 SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray, CFDictionaryRef *userOptions)
4674 {
4675 CFIndex count = CFArrayGetCount(userOptionsArray);
4676 int i;
4677
4678 for (i = 0; i < count; i++) {
4679 // (1) Find the dictionary
4680 CFPropertyListRef propertyList = CFArrayGetValueAtIndex(userOptionsArray, i);
4681
4682 if (isA_CFDictionary(propertyList) != NULL) {
4683 // See if there's a value for dial on demand
4684 CFPropertyListRef value;
4685
4686 value = CFDictionaryGetValue((CFDictionaryRef)propertyList, k_Dial_Default_Key);
4687 if (isA_CFBoolean(value) != NULL) {
4688 if (CFBooleanGetValue(value)) {
4689 // we found the default user options
4690 *userOptions = CFDictionaryCreateCopy(NULL,
4691 (CFDictionaryRef)propertyList);
4692 break;
4693 }
4694 }
4695 }
4696 }
4697
4698 return TRUE;
4699 }
4700
4701 //********************************************************************************
4702 // SCNetworkConnectionPrivateIsServiceType
4703 // --------------------------------------
4704 // Check and see if the service is a PPP service of the given types
4705 //********************************************************************************
4706 static Boolean
4707 SCNetworkConnectionPrivateIsPPPService(CFStringRef serviceID, CFStringRef subType1, CFStringRef subType2)
4708 {
4709 CFStringRef entityKey;
4710 Boolean isPPPService = FALSE;
4711 Boolean isMatchingSubType = FALSE;
4712 CFDictionaryRef serviceDict;
4713
4714 entityKey = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
4715 kSCDynamicStoreDomainSetup,
4716 serviceID,
4717 kSCEntNetInterface);
4718 if (entityKey == NULL) {
4719 return FALSE;
4720 }
4721
4722 serviceDict = SCDynamicStoreCopyValue(NULL, entityKey);
4723 if (serviceDict != NULL) {
4724 if (isA_CFDictionary(serviceDict)) {
4725 CFStringRef type;
4726 CFStringRef subtype;
4727
4728 type = CFDictionaryGetValue(serviceDict, kSCPropNetInterfaceType);
4729 if (isA_CFString(type)) {
4730 isPPPService = CFEqual(type, kSCValNetInterfaceTypePPP);
4731 }
4732
4733 subtype = CFDictionaryGetValue(serviceDict, kSCPropNetInterfaceSubType);
4734 if (isA_CFString(subtype)) {
4735 isMatchingSubType = CFEqual(subtype, subType1);
4736 if (!isMatchingSubType && subType2)
4737 isMatchingSubType = CFEqual(subtype, subType2);
4738 }
4739 }
4740 CFRelease(serviceDict);
4741 }
4742 CFRelease(entityKey);
4743
4744 return (isPPPService && isMatchingSubType);
4745 }
4746
4747 //********************************************************************************
4748 // addPasswordFromKeychain
4749 // --------------------------------------
4750 // Get the password and shared secret out of the keychain and add
4751 // them to the PPP and IPSec dictionaries
4752 //********************************************************************************
4753 static void
4754 addPasswordFromKeychain(CFStringRef serviceID, CFDictionaryRef *userOptions)
4755 {
4756 CFPropertyListRef uniqueID;
4757 CFStringRef password;
4758 CFStringRef sharedsecret = NULL;
4759
4760 /* user options must exist */
4761 if (*userOptions == NULL)
4762 return;
4763
4764 /* first, get the unique identifier used to store passwords in the keychain */
4765 uniqueID = CFDictionaryGetValue(*userOptions, k_Unique_Id_Key);
4766 if (!isA_CFString(uniqueID))
4767 return;
4768
4769 /* first, get the PPP password */
4770 password = copyPasswordFromKeychain(uniqueID);
4771
4772 /* then, if necessary, get the IPSec Shared Secret */
4773 if (SCNetworkConnectionPrivateIsPPPService(serviceID, kSCValNetInterfaceSubTypeL2TP, 0)) {
4774 CFMutableStringRef uniqueIDSS;
4775
4776 uniqueIDSS = CFStringCreateMutableCopy(NULL, 0, uniqueID);
4777 CFStringAppend(uniqueIDSS, CFSTR(".SS"));
4778 sharedsecret = copyPasswordFromKeychain(uniqueIDSS);
4779 CFRelease(uniqueIDSS);
4780 }
4781
4782 /* did we find our information in the key chain ? */
4783 if ((password != NULL) || (sharedsecret != NULL)) {
4784 CFMutableDictionaryRef newOptions;
4785
4786 newOptions = CFDictionaryCreateMutableCopy(NULL, 0, *userOptions);
4787
4788 /* PPP password */
4789 if (password != NULL) {
4790 CFDictionaryRef entity;
4791 CFMutableDictionaryRef newEntity;
4792
4793 entity = CFDictionaryGetValue(*userOptions, kSCEntNetPPP);
4794 if (isA_CFDictionary(entity))
4795 newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity);
4796 else
4797 newEntity = CFDictionaryCreateMutable(NULL,
4798 0,
4799 &kCFTypeDictionaryKeyCallBacks,
4800 &kCFTypeDictionaryValueCallBacks);
4801
4802
4803 /* set the PPP password */
4804 CFDictionarySetValue(newEntity, kSCPropNetPPPAuthPassword, uniqueID);
4805 CFDictionarySetValue(newEntity, kSCPropNetPPPAuthPasswordEncryption, kSCValNetPPPAuthPasswordEncryptionKeychain);
4806 CFRelease(password);
4807
4808 /* update the PPP entity */
4809 CFDictionarySetValue(newOptions, kSCEntNetPPP, newEntity);
4810 CFRelease(newEntity);
4811 }
4812
4813 /* IPSec Shared Secret */
4814 if (sharedsecret != NULL) {
4815 CFDictionaryRef entity;
4816 CFMutableDictionaryRef newEntity;
4817
4818 entity = CFDictionaryGetValue(*userOptions, kSCEntNetIPSec);
4819 if (isA_CFDictionary(entity))
4820 newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity);
4821 else
4822 newEntity = CFDictionaryCreateMutable(NULL,
4823 0,
4824 &kCFTypeDictionaryKeyCallBacks,
4825 &kCFTypeDictionaryValueCallBacks);
4826
4827 /* set the IPSec Shared Secret */
4828 CFDictionarySetValue(newEntity, kSCPropNetIPSecSharedSecret, sharedsecret);
4829 CFRelease(sharedsecret);
4830
4831 /* update the IPSec entity */
4832 CFDictionarySetValue(newOptions, kSCEntNetIPSec, newEntity);
4833 CFRelease(newEntity);
4834 }
4835
4836 /* update the userOptions dictionary */
4837 CFRelease(*userOptions);
4838 *userOptions = CFDictionaryCreateCopy(NULL, newOptions);
4839 CFRelease(newOptions);
4840 }
4841
4842 }
4843
4844 #if !TARGET_OS_IPHONE
4845 //********************************************************************************
4846 // copyKeychainEnumerator
4847 // --------------------------------------
4848 // Gather Keychain Enumerator
4849 //********************************************************************************
4850 static CFArrayRef
4851 copyKeychainEnumerator(CFStringRef uniqueIdentifier)
4852 {
4853 CFArrayRef itemArray = NULL;
4854 CFMutableDictionaryRef query;
4855 OSStatus result;
4856
4857 query = CFDictionaryCreateMutable(NULL,
4858 0,
4859 &kCFTypeDictionaryKeyCallBacks,
4860 &kCFTypeDictionaryValueCallBacks);
4861 CFDictionarySetValue(query, kSecClass , kSecClassGenericPassword);
4862 CFDictionarySetValue(query, kSecAttrService, uniqueIdentifier);
4863 CFDictionarySetValue(query, kSecReturnRef , kCFBooleanTrue);
4864 CFDictionarySetValue(query, kSecMatchLimit , kSecMatchLimitAll);
4865 result = SecItemCopyMatching(query, (CFTypeRef *)&itemArray);
4866 CFRelease(query);
4867 if ((result != noErr) && (itemArray != NULL)) {
4868 CFRelease(itemArray);
4869 itemArray = NULL;
4870 }
4871
4872 return itemArray;
4873 }
4874 #endif // !TARGET_OS_IPHONE
4875
4876 //********************************************************************************
4877 // copyPasswordFromKeychain
4878 // --------------------------------------
4879 // Given a uniqueID, retrieve the password from the keychain
4880 //********************************************************************************
4881 static CFStringRef
4882 copyPasswordFromKeychain(CFStringRef uniqueID)
4883 {
4884 #if !TARGET_OS_IPHONE
4885 CFArrayRef enumerator;
4886 CFIndex n;
4887 CFStringRef password = NULL;
4888
4889 enumerator = copyKeychainEnumerator(uniqueID);
4890 if (enumerator == NULL) {
4891 return NULL; // if no keychain enumerator
4892 }
4893
4894 n = CFArrayGetCount(enumerator);
4895 if (n > 0) {
4896 void *data = NULL;
4897 UInt32 dataLen = 0;
4898 SecKeychainItemRef itemRef;
4899 OSStatus result;
4900
4901 itemRef = (SecKeychainItemRef)CFArrayGetValueAtIndex(enumerator, 0);
4902 result = SecKeychainItemCopyContent(itemRef, // itemRef
4903 NULL, // itemClass
4904 NULL, // attrList
4905 &dataLen, // length
4906 (void *)&data); // outData
4907 if ((result == noErr) && (data != NULL) && (dataLen > 0)) {
4908 password = CFStringCreateWithBytes(NULL, data, dataLen, kCFStringEncodingUTF8, TRUE);
4909 (void) SecKeychainItemFreeContent(NULL, data);
4910 }
4911
4912 }
4913
4914 CFRelease(enumerator);
4915
4916 return password;
4917 #else // !TARGET_OS_IPHONE
4918 return NULL;
4919 #endif // !TARGET_OS_IPHONE
4920 }
4921
4922
4923 __private_extern__
4924 char *
4925 __SCNetworkConnectionGetControllerPortName(void)
4926 {
4927 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 70000)) && !TARGET_IPHONE_SIMULATOR
4928 if (scnc_server_name == NULL){
4929 if (!(sandbox_check(getpid(), "mach-lookup", SANDBOX_FILTER_GLOBAL_NAME | SANDBOX_CHECK_NO_REPORT, PPPCONTROLLER_SERVER_PRIV))){
4930 scnc_server_name = PPPCONTROLLER_SERVER_PRIV;
4931 }
4932 else{
4933 scnc_server_name = PPPCONTROLLER_SERVER;
4934 }
4935 }
4936 #else
4937 scnc_server_name = PPPCONTROLLER_SERVER;
4938 #endif
4939 return scnc_server_name;
4940 }
4941