2 * Copyright (c) 2003-2013 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * April 14, 2004 Christophe Allie <callie@apple.com>
30 * December 20, 2002 Christophe Allie <callie@apple.com>
35 //#define DEBUG_MACH_PORT_ALLOCATIONS
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 <SystemConfiguration/SystemConfiguration.h>
45 #include <SystemConfiguration/SCPrivate.h>
46 #include <SystemConfiguration/SCValidation.h>
47 #include <SystemConfiguration/VPNAppLayerPrivate.h>
48 #include <SystemConfiguration/VPNTunnel.h>
51 #include <Security/Security.h>
52 #include "dy_framework.h"
53 #endif // !TARGET_OS_IPHONE
55 #include <servers/bootstrap.h>
56 #include <bootstrap_priv.h>
60 #include <netinet/in.h>
61 #include <arpa/inet.h>
64 #include <sys/ioctl.h>
65 #include <sys/socket.h>
67 #include <mach/mach.h>
68 #include <bsm/audit.h>
69 #include <bsm/libbsm.h>
72 #include <ppp/ppp_msg.h>
73 #include "pppcontroller.h"
74 #include <ppp/pppcontroller_types.h>
76 #ifndef PPPCONTROLLER_SERVER_PRIV
77 #define PPPCONTROLLER_SERVER_PRIV PPPCONTROLLER_SERVER
78 #endif // !PPPCONTROLLER_SERVER_PRIV
81 #include "SCNetworkConnectionInternal.h"
84 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
85 static pthread_mutex_t scnc_lock
= PTHREAD_MUTEX_INITIALIZER
;
86 static mach_port_t scnc_server
= MACH_PORT_NULL
;
87 static char *scnc_server_name
= NULL
;
92 /* base CFType information */
99 SCNetworkServiceRef service
;
101 /* client info (if we are proxying for another process */
102 mach_port_t client_audit_session
;
103 audit_token_t client_audit_token
;
104 mach_port_t client_bootstrap_port
;
109 CFStringRef client_bundle_id
;
111 /* ref to PPP controller for control messages */
112 mach_port_t session_port
;
114 /* ref to PPP controller for notification messages */
115 CFMachPortRef notify_port
;
117 /* keep track of whether we're acquired the initial status */
120 /* run loop source, callout, context, rl scheduling info */
122 CFRunLoopSourceRef rls
;
123 SCNetworkConnectionCallBack rlsFunction
;
124 SCNetworkConnectionContext rlsContext
;
125 CFMutableArrayRef rlList
;
127 /* SCNetworkConnectionSetDispatchQueue */
128 dispatch_group_t dispatchGroup
;
129 dispatch_queue_t dispatchQueue
;
130 dispatch_source_t dispatchSource
;
132 SCNetworkConnectionType type
;
134 CFDictionaryRef on_demand_info
;
135 CFDictionaryRef on_demand_user_options
;
136 CFStringRef on_demand_required_probe
;
138 /* Flow Divert support info */
139 CFDictionaryRef flow_divert_token_params
;
140 } SCNetworkConnectionPrivate
, *SCNetworkConnectionPrivateRef
;
143 static __inline__ CFTypeRef
144 isA_SCNetworkConnection(CFTypeRef obj
)
146 return (isA_CFType(obj
, SCNetworkConnectionGetTypeID()));
151 __SCNetworkConnectionCopyDescription(CFTypeRef cf
)
153 CFAllocatorRef allocator
= CFGetAllocator(cf
);
154 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)cf
;
155 CFMutableStringRef result
;
157 result
= CFStringCreateMutable(allocator
, 0);
158 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkConnection, %p [%p]> {"), cf
, allocator
);
159 CFStringAppendFormat(result
, NULL
, CFSTR("service = %p"), connectionPrivate
->service
);
160 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
161 CFStringAppendFormat(result
, NULL
, CFSTR(", server port = 0x%x"), connectionPrivate
->session_port
);
163 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
170 __SCNetworkConnectionDeallocate(CFTypeRef cf
)
172 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)cf
;
174 /* release resources */
175 pthread_mutex_destroy(&connectionPrivate
->lock
);
177 if (connectionPrivate
->client_audit_session
!= MACH_PORT_NULL
) {
178 mach_port_mod_refs(mach_task_self(),
179 connectionPrivate
->client_audit_session
,
180 MACH_PORT_RIGHT_SEND
,
184 if (connectionPrivate
->client_bootstrap_port
!= MACH_PORT_NULL
) {
185 mach_port_mod_refs(mach_task_self(),
186 connectionPrivate
->client_bootstrap_port
,
187 MACH_PORT_RIGHT_SEND
,
191 if (connectionPrivate
->client_bundle_id
!= NULL
) {
192 CFRelease(connectionPrivate
->client_bundle_id
);
195 if (connectionPrivate
->rls
!= NULL
) {
196 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
197 CFRelease(connectionPrivate
->rls
);
200 if (connectionPrivate
->rlList
!= NULL
) {
201 CFRelease(connectionPrivate
->rlList
);
204 if (connectionPrivate
->notify_port
!= NULL
) {
205 mach_port_t mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
207 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionDeallocate notify_port", mp
);
208 CFMachPortInvalidate(connectionPrivate
->notify_port
);
209 CFRelease(connectionPrivate
->notify_port
);
210 mach_port_mod_refs(mach_task_self(), mp
, MACH_PORT_RIGHT_RECEIVE
, -1);
213 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
214 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionDeallocate session_port", connectionPrivate
->session_port
);
215 (void) mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
218 if (connectionPrivate
->rlsContext
.release
!= NULL
)
219 (*connectionPrivate
->rlsContext
.release
)(connectionPrivate
->rlsContext
.info
);
221 if (connectionPrivate
->service
!= NULL
) {
222 CFRelease(connectionPrivate
->service
);
225 if (connectionPrivate
->on_demand_info
!= NULL
) {
226 CFRelease(connectionPrivate
->on_demand_info
);
229 if (connectionPrivate
->on_demand_user_options
!= NULL
) {
230 CFRelease(connectionPrivate
->on_demand_user_options
);
233 if (connectionPrivate
->on_demand_required_probe
!= NULL
) {
234 CFRelease(connectionPrivate
->on_demand_required_probe
);
237 if (connectionPrivate
->flow_divert_token_params
!= NULL
) {
238 CFRelease(connectionPrivate
->flow_divert_token_params
);
245 static CFTypeID __kSCNetworkConnectionTypeID
= _kCFRuntimeNotATypeID
;
247 static const CFRuntimeClass __SCNetworkConnectionClass
= {
249 "SCNetworkConnection", // className
252 __SCNetworkConnectionDeallocate
, // dealloc
255 NULL
, // copyFormattingDesc
256 __SCNetworkConnectionCopyDescription
// copyDebugDesc
263 /* the process has forked (and we are the child process) */
265 scnc_server
= MACH_PORT_NULL
;
266 scnc_server_name
= NULL
;
272 __SCNetworkConnectionInitialize(void)
276 /* get the debug environment variable */
277 env
= getenv("PPPDebug");
279 if (sscanf(env
, "%d", &debug
) != 1) {
280 /* PPPDebug value is not valid (or non-numeric), set debug to 1 */
285 /* register with CoreFoundation */
286 __kSCNetworkConnectionTypeID
= _CFRuntimeRegisterClass(&__SCNetworkConnectionClass
);
288 /* add handler to cleanup after fork() */
289 (void) pthread_atfork(NULL
, NULL
, childForkHandler
);
296 __SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection
);
298 #define SC_NETWORK_CONNECTION_QUEUE "SCNetworkConnectionQueue"
300 static dispatch_queue_t
301 __SCNetworkConnectionQueue()
303 static dispatch_once_t once
;
304 static dispatch_queue_t q
;
306 dispatch_once(&once
, ^{
307 q
= dispatch_queue_create(SC_NETWORK_CONNECTION_QUEUE
, NULL
);
315 __SCNetworkConnectionCallBackRunLoopPerform(SCNetworkConnectionRef connection
,
318 SCNetworkConnectionCallBack rlsFunction
,
319 void (*context_release
)(const void *),
322 SCNetworkConnectionStatus nc_status
= kSCNetworkConnectionInvalid
;
324 nc_status
= SCNetworkConnectionGetStatus(connection
);
325 CFRunLoopPerformBlock(rl
, rl_mode
,
327 (*rlsFunction
)(connection
, nc_status
, context_info
);
328 if ((context_release
!= NULL
) && (context_info
!= NULL
)) {
329 (*context_release
)(context_info
);
333 CFRelease(connection
);
340 __SCNetworkConnectionCallBackDispatchPerform(SCNetworkConnectionRef connection
,
342 SCNetworkConnectionCallBack rlsFunction
,
343 void (*context_release
)(const void *),
346 SCNetworkConnectionStatus nc_status
= kSCNetworkConnectionInvalid
;
348 nc_status
= SCNetworkConnectionGetStatus(connection
);
351 (*rlsFunction
)(connection
, nc_status
, context_info
);
352 if ((context_release
!= NULL
) && (context_info
!= NULL
)) {
353 (*context_release
)(context_info
);
356 CFRelease(connection
);
363 __SCNetworkConnectionCallBack(CFMachPortRef port
, void * msg
, CFIndex size
, void * info
)
365 mach_no_senders_notification_t
*buf
= msg
;
366 mach_msg_id_t msgid
= buf
->not_header
.msgh_id
;
367 boolean_t exec_async
= FALSE
;
368 SCNetworkConnectionRef connection
= (SCNetworkConnectionRef
)info
;
369 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
371 void (*context_release
)(const void *);
372 CFRunLoopRef rl
= NULL
;
374 SCNetworkConnectionCallBack rlsFunction
= NULL
;
375 dispatch_queue_t q
= NULL
;
376 SCNetworkConnectionStatus nc_status
= kSCNetworkConnectionInvalid
;
378 if (msgid
== MACH_NOTIFY_NO_SENDERS
) {
379 // re-establish notification
380 SCLog(_sc_verbose
, LOG_DEBUG
, CFSTR("__SCNetworkConnectionCallBack: PPPController server died"));
381 (void)__SCNetworkConnectionReconnectNotifications(connection
);
384 pthread_mutex_lock(&connectionPrivate
->lock
);
386 if (!connectionPrivate
->scheduled
) {
387 // if not currently scheduled
388 pthread_mutex_unlock(&connectionPrivate
->lock
);
392 rlsFunction
= connectionPrivate
->rlsFunction
;
393 if (rlsFunction
== NULL
) {
394 pthread_mutex_unlock(&connectionPrivate
->lock
);
398 if ((connectionPrivate
->rlsContext
.retain
!= NULL
) && (connectionPrivate
->rlsContext
.info
!= NULL
)) {
399 context_info
= (void *)(*connectionPrivate
->rlsContext
.retain
)(connectionPrivate
->rlsContext
.info
);
400 context_release
= connectionPrivate
->rlsContext
.release
;
402 context_info
= connectionPrivate
->rlsContext
.info
;
403 context_release
= NULL
;
406 // Do we need to spin a new thread? (either we are running on the main
407 // dispatch queue or main runloop)
408 if (connectionPrivate
->rlList
== NULL
) {
409 // this is the case if we are performing a callback on a dispatch queue
410 q
= connectionPrivate
->dispatchQueue
;
411 if (q
== dispatch_get_main_queue()) {
415 rl
= CFRunLoopGetCurrent();
416 if (rl
== CFRunLoopGetMain()) {
421 CFRetain(connection
);
422 pthread_mutex_unlock(&connectionPrivate
->lock
);
424 if (exec_async
== FALSE
) {
425 nc_status
= SCNetworkConnectionGetStatus(connection
);
426 (*rlsFunction
)(connection
, nc_status
, context_info
);
427 if ((context_release
!= NULL
) && (context_info
!= NULL
)) {
428 (*context_release
)(context_info
);
430 CFRelease(connection
);
434 if (connectionPrivate
->rlList
== NULL
) {
437 dispatch_async(__SCNetworkConnectionQueue(),
438 ^{__SCNetworkConnectionCallBackDispatchPerform(connection
,
446 rl_mode
= CFRunLoopCopyCurrentMode(rl
);
447 dispatch_async(__SCNetworkConnectionQueue(),
448 ^{__SCNetworkConnectionCallBackRunLoopPerform(connection
,
461 #pragma mark SCNetworkConnection APIs
465 pppMPCopyDescription(const void *info
)
467 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)info
;
469 return CFStringCreateWithFormat(NULL
,
471 CFSTR("<SCNetworkConnection MP %p> {service = %@, callout = %p}"),
473 connectionPrivate
->service
,
474 connectionPrivate
->rlsFunction
);
478 static SCNetworkConnectionPrivateRef
479 __SCNetworkConnectionCreatePrivate(CFAllocatorRef allocator
,
480 SCNetworkServiceRef service
,
481 SCNetworkConnectionCallBack callout
,
482 SCNetworkConnectionContext
*context
)
484 SCNetworkConnectionPrivateRef connectionPrivate
= NULL
;
488 /* initialize runtime */
489 pthread_once(&initialized
, __SCNetworkConnectionInitialize
);
491 /* allocate NetworkConnection */
492 size
= sizeof(SCNetworkConnectionPrivate
) - sizeof(CFRuntimeBase
);
493 connectionPrivate
= (SCNetworkConnectionPrivateRef
)_CFRuntimeCreateInstance(allocator
, __kSCNetworkConnectionTypeID
, size
, NULL
);
494 if (connectionPrivate
== NULL
) {
498 /* zero the data structure */
499 bzero(((u_char
*)connectionPrivate
)+sizeof(CFRuntimeBase
), size
);
501 pthread_mutex_init(&connectionPrivate
->lock
, NULL
);
503 /* save the service */
504 if (service
!= NULL
) {
505 connectionPrivate
->service
= CFRetain(service
);
508 connectionPrivate
->client_audit_session
= MACH_PORT_NULL
;
509 connectionPrivate
->client_bootstrap_port
= MACH_PORT_NULL
;
510 connectionPrivate
->client_uid
= geteuid();
511 connectionPrivate
->client_gid
= getegid();
512 connectionPrivate
->client_pid
= getpid();
513 connectionPrivate
->client_bundle_id
= NULL
;
514 uuid_clear(connectionPrivate
->client_uuid
);
516 connectionPrivate
->rlsFunction
= callout
;
519 bcopy(context
, &connectionPrivate
->rlsContext
, sizeof(SCNetworkConnectionContext
));
520 if (context
->retain
!= NULL
) {
521 connectionPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
525 connectionPrivate
->type
= kSCNetworkConnectionTypeUnknown
;
527 /* success, return the connection reference */
528 return connectionPrivate
;
532 /* failure, clean up and leave */
533 if (connectionPrivate
!= NULL
) {
534 CFRelease(connectionPrivate
);
537 _SCErrorSet(kSCStatusFailed
);
543 __SCNetworkConnectionServerPort(kern_return_t
*status
)
545 mach_port_t server
= MACH_PORT_NULL
;
547 #ifdef BOOTSTRAP_PRIVILEGED_SERVER
548 *status
= bootstrap_look_up2(bootstrap_port
,
549 __SCNetworkConnectionGetControllerPortName(),
552 BOOTSTRAP_PRIVILEGED_SERVER
);
553 #else // BOOTSTRAP_PRIVILEGED_SERVER
554 *status
= bootstrap_look_up(bootstrap_port
, __SCNetworkConnectionGetControllerPortName(), &server
);
555 #endif // BOOTSTRAP_PRIVILEGED_SERVER
558 case BOOTSTRAP_SUCCESS
:
559 // service currently registered, "a good thing" (tm)
561 case BOOTSTRAP_NOT_PRIVILEGED
:
562 // the service is not privileged
564 case BOOTSTRAP_UNKNOWN_SERVICE
:
565 // service not currently registered, try again later
569 SCLog(_sc_verbose
, LOG_DEBUG
,
570 CFSTR("SCNetworkConnection bootstrap_look_up() failed: status=%s"),
571 bootstrap_strerror(*status
));
576 scnc_server_name
= NULL
; /* reset pppcontroller server */
577 return MACH_PORT_NULL
;
581 __SCNetworkConnectionGetCurrentServerPort(void)
587 __SCNetworkConnectionRefreshServerPort(mach_port_t current_server
, int *mach_result
)
589 mach_port_t new_server
;
591 pthread_mutex_lock(&scnc_lock
);
592 if (scnc_server
!= MACH_PORT_NULL
) {
593 if (current_server
== scnc_server
) {
594 scnc_server_name
= NULL
;
595 // if the server we tried returned the error
596 (void)mach_port_deallocate(mach_task_self(), scnc_server
);
597 scnc_server
= __SCNetworkConnectionServerPort(mach_result
);
599 // another thread has refreshed the server port
602 scnc_server
= __SCNetworkConnectionServerPort(mach_result
);
604 new_server
= scnc_server
;
605 pthread_mutex_unlock(&scnc_lock
);
610 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && (!TARGET_IPHONE_SIMULATOR || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 70000))
611 #define HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
615 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && !TARGET_IPHONE_SIMULATOR
616 #define HAVE_PPPCONTROLLER_ATTACHWITHPROXY
620 __SCNetworkConnectionSessionPort(SCNetworkConnectionPrivateRef connectionPrivate
)
624 CFDataRef dataRef
= NULL
;
625 mach_port_t notify_port
= MACH_PORT_NULL
;
626 mach_port_t oldNotify
= MACH_PORT_NULL
;
628 int sc_status
= kSCStatusFailed
;
629 mach_port_t server
= __SCNetworkConnectionGetCurrentServerPort();
630 kern_return_t status
= KERN_SUCCESS
;
632 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
633 mach_port_t au_session
= MACH_PORT_NULL
;
634 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
636 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
637 return connectionPrivate
->session_port
;
640 if (connectionPrivate
->service
== NULL
) {
641 sc_status
= kSCStatusConnectionNoService
;
645 if (!_SCSerializeString(SCNetworkServiceGetServiceID(connectionPrivate
->service
), &dataRef
, &data
, &dataLen
)) {
649 if (connectionPrivate
->notify_port
!= NULL
) {
650 mach_port_t mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
652 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionReconnectNotifications mp", mp
);
653 CFMachPortInvalidate(connectionPrivate
->notify_port
);
654 CFRelease(connectionPrivate
->notify_port
);
655 connectionPrivate
->notify_port
= NULL
;
656 mach_port_mod_refs(mach_task_self(), mp
, MACH_PORT_RIGHT_RECEIVE
, -1);
659 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
660 au_session
= audit_session_self();
661 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
663 // open a new session with the server
665 if ((connectionPrivate
->rlsFunction
!= NULL
) && (notify_port
== MACH_PORT_NULL
)) {
666 // allocate port (for server response)
667 status
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE
, ¬ify_port
);
668 if (status
!= KERN_SUCCESS
) {
669 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkConnectionSessionPort mach_port_allocate(): %s"), mach_error_string(status
));
674 // add send right (passed to the server)
675 status
= mach_port_insert_right(mach_task_self(),
678 MACH_MSG_TYPE_MAKE_SEND
);
679 if (status
!= KERN_SUCCESS
) {
680 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkConnectionSessionPort mach_port_insert_right(): %s"), mach_error_string(status
));
681 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
687 if (server
!= MACH_PORT_NULL
) {
688 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHPROXY
689 if ((connectionPrivate
->client_audit_session
== MACH_PORT_NULL
) &&
690 (connectionPrivate
->client_bootstrap_port
== MACH_PORT_NULL
) &&
691 (connectionPrivate
->client_uid
== geteuid()) &&
692 (connectionPrivate
->client_gid
== getegid()) &&
693 (connectionPrivate
->client_pid
== getpid())
695 #endif // HAVE_PPPCONTROLLER_ATTACHWITHPROXY
696 status
= pppcontroller_attach(server
,
701 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
703 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
704 &connectionPrivate
->session_port
,
706 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHPROXY
708 mach_port_t client_au_session
;
709 mach_port_t client_bootstrap_port
;
711 if (connectionPrivate
->client_audit_session
== MACH_PORT_NULL
) {
712 client_au_session
= au_session
;
714 client_au_session
= connectionPrivate
->client_audit_session
;
717 if (connectionPrivate
->client_bootstrap_port
== MACH_PORT_NULL
) {
718 client_bootstrap_port
= bootstrap_port
;
720 client_bootstrap_port
= connectionPrivate
->client_bootstrap_port
;
723 status
= pppcontroller_attach_proxy(server
,
726 client_bootstrap_port
,
729 connectionPrivate
->client_uid
,
730 connectionPrivate
->client_gid
,
731 connectionPrivate
->client_pid
,
732 &connectionPrivate
->session_port
,
735 #endif // HAVE_PPPCONTROLLER_ATTACHWITHPROXY
736 if (status
== KERN_SUCCESS
) {
737 if (sc_status
!= kSCStatusOK
) {
738 SCLog(TRUE
, LOG_DEBUG
,
739 CFSTR("__SCNetworkConnectionSessionPort : attach w/error, sc_status=%s%s"),
740 SCErrorString(sc_status
),
741 (connectionPrivate
->session_port
!= MACH_PORT_NULL
) ? ", w/session_port!=MACH_PORT_NULL" : "");
743 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
744 __MACH_PORT_DEBUG(TRUE
,
745 "*** __SCNetworkConnectionSessionPort session_port (attach w/error, cleanup)",
746 connectionPrivate
->session_port
);
747 mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
748 connectionPrivate
->session_port
= MACH_PORT_NULL
;
751 if (notify_port
!= MACH_PORT_NULL
) {
752 __MACH_PORT_DEBUG(TRUE
,
753 "*** __SCNetworkConnectionSessionPort notify_port (attach w/error, cleanup)",
755 (void) mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
756 notify_port
= MACH_PORT_NULL
;
762 // our [cached] server port is not valid
763 SCLog(TRUE
, LOG_DEBUG
, CFSTR("__SCNetworkConnectionSessionPort : !attach: %s"), SCErrorString(status
));
764 if (status
== MACH_SEND_INVALID_DEST
) {
765 // the server is not yet available
766 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionSessionPort notify_port (!dest)", notify_port
);
767 } else if (status
== MIG_SERVER_DIED
) {
768 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionSessionPort notify_port (!mig)", notify_port
);
769 // the server we were using is gone and we've lost our send right
770 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
771 notify_port
= MACH_PORT_NULL
;
773 // if we got an unexpected error, don't retry
779 server
= __SCNetworkConnectionRefreshServerPort(server
, &sc_status
);
780 if (server
== MACH_PORT_NULL
) {
781 // if server not available
782 if (sc_status
== BOOTSTRAP_UNKNOWN_SERVICE
) {
783 // if first retry attempt, wait for SCDynamicStore server
785 SCDynamicStoreRef store
;
787 store
= SCDynamicStoreCreate(NULL
,
788 CFSTR("SCNetworkConnection connect"),
796 // wait up to 2.5 seconds for the [SCNetworkConnection] server
798 if ((retry
+= 50) < 2500) {
799 usleep(50 * 1000); // sleep 50ms between attempts
807 if (notify_port
!= MACH_PORT_NULL
) {
808 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
809 CFMachPortContext context
= { 0
810 , (void *)connectionPrivate
813 , pppMPCopyDescription
816 // request a notification when/if the server dies
817 status
= mach_port_request_notification(mach_task_self(),
819 MACH_NOTIFY_NO_SENDERS
,
822 MACH_MSG_TYPE_MAKE_SEND_ONCE
,
824 if (status
!= KERN_SUCCESS
) {
825 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkConnectionSessionPort mach_port_request_notification(): %s"), mach_error_string(status
));
826 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
831 if (oldNotify
!= MACH_PORT_NULL
) {
832 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkConnectionSessionPort(): oldNotify != MACH_PORT_NULL"));
835 // create CFMachPort for SCNetworkConnection notification callback
836 connectionPrivate
->notify_port
= _SC_CFMachPortCreateWithPort("SCNetworkConnection",
838 __SCNetworkConnectionCallBack
,
841 // we need to try a bit harder to acquire the initial status
842 connectionPrivate
->haveStatus
= FALSE
;
844 // with no server port, release the notification port we allocated
845 __MACH_PORT_DEBUG(TRUE
,
846 "*** __SCNetworkConnectionSessionPort notify_port (!server)",
848 (void) mach_port_mod_refs (mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
849 (void) mach_port_deallocate(mach_task_self(), notify_port
);
850 notify_port
= MACH_PORT_NULL
;
858 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
859 if (au_session
!= MACH_PORT_NULL
) {
860 (void)mach_port_deallocate(mach_task_self(), au_session
);
862 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
864 if (dataRef
!= NULL
) CFRelease(dataRef
);
868 __MACH_PORT_DEBUG(connectionPrivate
->session_port
!= MACH_PORT_NULL
,
869 "*** __SCNetworkConnectionSessionPort session_port",
870 connectionPrivate
->session_port
);
871 __MACH_PORT_DEBUG(notify_port
!= MACH_PORT_NULL
,
872 "*** __SCNetworkConnectionSessionPort notify_port",
875 case BOOTSTRAP_UNKNOWN_SERVICE
:
877 (status
== KERN_SUCCESS
) ? LOG_DEBUG
: LOG_ERR
,
878 CFSTR("PPPController not available"));
882 (status
== KERN_SUCCESS
) ? LOG_DEBUG
: LOG_ERR
,
883 CFSTR("__SCNetworkConnectionSessionPort pppcontroller_attach(): %s"),
884 SCErrorString(sc_status
));
888 if (sc_status
!= kSCStatusOK
) {
889 _SCErrorSet(sc_status
);
892 return connectionPrivate
->session_port
;
897 __SCNetworkConnectionReconnect(SCNetworkConnectionRef connection
)
899 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
902 port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
903 return (port
!= MACH_PORT_NULL
);
908 __SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection
)
910 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
911 dispatch_group_t dispatchGroup
= NULL
;
912 dispatch_queue_t dispatchQueue
= NULL
;
914 CFArrayRef rlList
= NULL
;
916 // Before we fully tearing down our [old] notifications, make sure
917 // we have retained any information that is needed to re-register the
918 // [new] notifications.
920 pthread_mutex_lock(&connectionPrivate
->lock
);
922 if (connectionPrivate
->rlList
!= NULL
) {
923 rlList
= CFArrayCreateCopy(NULL
, connectionPrivate
->rlList
);
926 // cancel [old] notifications
927 if (connectionPrivate
->rlList
!= NULL
) {
928 CFRelease(connectionPrivate
->rlList
);
929 connectionPrivate
->rlList
= NULL
;
931 if (connectionPrivate
->rls
!= NULL
) {
932 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
933 CFRelease(connectionPrivate
->rls
);
934 connectionPrivate
->rls
= NULL
;
936 if (connectionPrivate
->dispatchSource
!= NULL
) {
937 dispatch_source_cancel(connectionPrivate
->dispatchSource
);
938 connectionPrivate
->dispatchSource
= NULL
;
941 /* Make sure dispatchSource is cancelled before removing group/queue */
942 if (connectionPrivate
->dispatchQueue
!= NULL
) {
943 // save dispatchQueue, release reference when we've queue'd blocks
944 // complete, allow re-scheduling
945 dispatchGroup
= connectionPrivate
->dispatchGroup
;
946 connectionPrivate
->dispatchGroup
= NULL
;
947 dispatchQueue
= connectionPrivate
->dispatchQueue
;
948 connectionPrivate
->dispatchQueue
= NULL
;
950 // and take an extra reference for rescheduling
951 dispatch_retain(dispatchQueue
);
954 connectionPrivate
->scheduled
= FALSE
;
956 pthread_mutex_unlock(&connectionPrivate
->lock
);
958 if (dispatchGroup
!= NULL
) {
959 dispatch_group_notify(dispatchGroup
, dispatchQueue
, ^{
960 // release group/queue references
961 dispatch_release(dispatchQueue
);
962 dispatch_release(dispatchGroup
); // releases our connection reference
967 if (rlList
!= NULL
) {
971 n
= CFArrayGetCount(rlList
);
972 for (i
= 0; i
< n
; i
+= 3) {
973 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(rlList
, i
+1);
974 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(rlList
, i
+2);
976 ok
= SCNetworkConnectionScheduleWithRunLoop(connection
, rl
, rlMode
);
978 SCLog((SCError() != BOOTSTRAP_UNKNOWN_SERVICE
),
980 CFSTR("__SCNetworkConnectionReconnectNotifications: SCNetworkConnectionScheduleWithRunLoop() failed"));
984 } else if (dispatchQueue
!= NULL
) {
985 ok
= SCNetworkConnectionSetDispatchQueue(connection
, dispatchQueue
);
987 SCLog((SCError() != BOOTSTRAP_UNKNOWN_SERVICE
),
989 CFSTR("__SCNetworkConnectionReconnectNotifications: SCNetworkConnectionSetDispatchQueue() failed"));
999 if (rlList
!= NULL
) {
1002 if (dispatchQueue
!= NULL
) {
1003 dispatch_release(dispatchQueue
);
1007 SCLog(TRUE
, LOG_ERR
,
1008 CFSTR("SCNetworkConnection server %s, notification not restored"),
1009 (SCError() == BOOTSTRAP_UNKNOWN_SERVICE
) ? "shutdown" : "failed");
1017 __SCNetworkConnectionNeedsRetry(SCNetworkConnectionRef connection
,
1018 const char *error_label
,
1019 kern_return_t status
,
1022 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1024 if (status
== KERN_SUCCESS
) {
1028 if ((status
== MACH_SEND_INVALID_DEST
) || (status
== MIG_SERVER_DIED
)) {
1029 // the server's gone and our session port's dead, remove the dead name right
1030 (void) mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
1032 // we got an unexpected error, leave the [session] port alone
1033 SCLog(TRUE
, LOG_ERR
, CFSTR("%s: %s"), error_label
, mach_error_string(status
));
1035 connectionPrivate
->session_port
= MACH_PORT_NULL
;
1036 if ((status
== MACH_SEND_INVALID_DEST
) || (status
== MIG_SERVER_DIED
)) {
1037 if (__SCNetworkConnectionReconnect(connection
)) {
1041 *sc_status
= status
;
1048 SCNetworkConnectionGetTypeID(void) {
1049 pthread_once(&initialized
, __SCNetworkConnectionInitialize
); /* initialize runtime */
1050 return __kSCNetworkConnectionTypeID
;
1054 CFArrayRef
/* of SCNetworkServiceRef's */
1055 SCNetworkConnectionCopyAvailableServices(SCNetworkSetRef set
)
1057 CFMutableArrayRef available
;
1058 Boolean tempSet
= FALSE
;
1061 SCPreferencesRef prefs
;
1063 prefs
= SCPreferencesCreate(NULL
, CFSTR("SCNetworkConnectionCopyAvailableServices"), NULL
);
1064 if (prefs
!= NULL
) {
1065 set
= SCNetworkSetCopyCurrent(prefs
);
1071 available
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1074 CFArrayRef services
;
1076 services
= SCNetworkSetCopyServices(set
);
1077 if (services
!= NULL
) {
1081 n
= CFArrayGetCount(services
);
1082 for (i
= 0; i
< n
; i
++) {
1083 SCNetworkInterfaceRef interface
;
1084 CFStringRef interfaceType
;
1085 SCNetworkServiceRef service
;
1087 service
= CFArrayGetValueAtIndex(services
, i
);
1088 interface
= SCNetworkServiceGetInterface(service
);
1089 if (interface
== NULL
) {
1093 interfaceType
= SCNetworkInterfaceGetInterfaceType(interface
);
1094 if (CFEqual(interfaceType
, kSCNetworkInterfaceTypePPP
) ||
1095 CFEqual(interfaceType
, kSCNetworkInterfaceTypeVPN
) ||
1096 CFEqual(interfaceType
, kSCNetworkInterfaceTypeIPSec
)) {
1097 CFArrayAppendValue(available
, service
);
1101 CFRelease(services
);
1105 if (tempSet
&& (set
!= NULL
)) {
1112 SCNetworkConnectionRef
1113 SCNetworkConnectionCreateWithService(CFAllocatorRef allocator
,
1114 SCNetworkServiceRef service
,
1115 SCNetworkConnectionCallBack callout
,
1116 SCNetworkConnectionContext
*context
)
1118 SCNetworkConnectionPrivateRef connectionPrivate
;
1120 if (!isA_SCNetworkService(service
)) {
1121 _SCErrorSet(kSCStatusInvalidArgument
);
1125 connectionPrivate
= __SCNetworkConnectionCreatePrivate(allocator
, service
, callout
, context
);
1126 return (SCNetworkConnectionRef
)connectionPrivate
;
1130 SCNetworkConnectionRef
1131 SCNetworkConnectionCreateWithServiceID(CFAllocatorRef allocator
,
1132 CFStringRef serviceID
,
1133 SCNetworkConnectionCallBack callout
,
1134 SCNetworkConnectionContext
*context
)
1136 SCNetworkConnectionRef connection
;
1137 SCNetworkServiceRef service
;
1139 if (!isA_CFString(serviceID
)) {
1140 _SCErrorSet(kSCStatusInvalidArgument
);
1144 service
= _SCNetworkServiceCopyActive(NULL
, serviceID
);
1145 if (service
== NULL
) {
1149 connection
= SCNetworkConnectionCreateWithService(allocator
, service
, callout
, context
);
1156 SCNetworkConnectionRef
1157 SCNetworkConnectionCreate(CFAllocatorRef allocator
,
1158 SCNetworkConnectionCallBack callout
,
1159 SCNetworkConnectionContext
*context
)
1161 SCNetworkConnectionPrivateRef connectionPrivate
= __SCNetworkConnectionCreatePrivate(allocator
, NULL
, callout
, context
);
1162 return (SCNetworkConnectionRef
)connectionPrivate
;
1167 SCNetworkConnectionCopyServiceID(SCNetworkConnectionRef connection
)
1169 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1170 CFStringRef serviceID
;
1172 if (!isA_SCNetworkConnection(connection
)) {
1173 _SCErrorSet(kSCStatusInvalidArgument
);
1177 if (connectionPrivate
->service
== NULL
) {
1178 _SCErrorSet(kSCStatusConnectionNoService
);
1182 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
1183 return CFRetain(serviceID
);
1188 SCNetworkConnectionSetClientInfo(SCNetworkConnectionRef connection
,
1189 mach_port_t client_audit_session
,
1194 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1196 if (!isA_SCNetworkConnection(connection
)) {
1197 _SCErrorSet(kSCStatusInvalidArgument
);
1201 // save client audit session port
1202 if (connectionPrivate
->client_audit_session
!= MACH_PORT_NULL
) {
1203 mach_port_mod_refs(mach_task_self(),
1204 connectionPrivate
->client_audit_session
,
1205 MACH_PORT_RIGHT_SEND
,
1207 connectionPrivate
->client_audit_session
= MACH_PORT_NULL
;
1209 connectionPrivate
->client_audit_session
= client_audit_session
;
1210 if (connectionPrivate
->client_audit_session
!= MACH_PORT_NULL
) {
1211 mach_port_mod_refs(mach_task_self(),
1212 connectionPrivate
->client_audit_session
,
1213 MACH_PORT_RIGHT_SEND
,
1217 // save client UID, GID, and PID
1218 connectionPrivate
->client_uid
= client_uid
;
1219 connectionPrivate
->client_gid
= client_gid
;
1220 connectionPrivate
->client_pid
= client_pid
;
1227 SCNetworkConnectionSetClientAuditInfo(SCNetworkConnectionRef connection
,
1228 audit_token_t client_audit_token
,
1229 mach_port_t audit_session
,
1230 mach_port_t bootstrap_port
,
1233 const char *bundle_id
)
1235 const audit_token_t null_audit
= KERNEL_AUDIT_TOKEN_VALUE
;
1236 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1241 if (memcmp(&client_audit_token
, &null_audit
, sizeof(client_audit_token
))) {
1242 #if TARGET_OS_IPHONE
1243 audit_token_to_au32(client_audit_token
, NULL
, &uid
, &gid
, NULL
, NULL
, &pid
, NULL
, NULL
);
1244 #else // TARGET_OS_IPHONE
1245 uid
= audit_token_to_euid(client_audit_token
);
1246 gid
= audit_token_to_egid(client_audit_token
);
1247 pid
= audit_token_to_pid(client_audit_token
);
1248 #endif // TARGET_OS_IPHONE
1253 if (!SCNetworkConnectionSetClientInfo(connection
, audit_session
, uid
, gid
, pid
)) {
1257 if (connectionPrivate
->client_bootstrap_port
!= MACH_PORT_NULL
) {
1258 mach_port_mod_refs(mach_task_self(),
1259 connectionPrivate
->client_bootstrap_port
,
1260 MACH_PORT_RIGHT_SEND
,
1262 connectionPrivate
->client_bootstrap_port
= MACH_PORT_NULL
;
1265 connectionPrivate
->client_bootstrap_port
= bootstrap_port
;
1266 if (connectionPrivate
->client_bootstrap_port
!= MACH_PORT_NULL
) {
1267 mach_port_mod_refs(mach_task_self(),
1268 connectionPrivate
->client_bootstrap_port
,
1269 MACH_PORT_RIGHT_SEND
,
1273 memcpy(&connectionPrivate
->client_audit_token
, &client_audit_token
, sizeof(connectionPrivate
->client_audit_token
));
1275 if (uuid
!= NULL
&& !uuid_is_null(uuid
)) {
1276 uuid_copy(connectionPrivate
->client_uuid
, uuid
);
1279 if (connectionPrivate
->client_bundle_id
!= NULL
) {
1280 CFRelease(connectionPrivate
->client_bundle_id
);
1281 connectionPrivate
->client_bundle_id
= NULL
;
1284 if (bundle_id
!= NULL
) {
1285 connectionPrivate
->client_bundle_id
= CFStringCreateWithCString(kCFAllocatorDefault
, bundle_id
, kCFStringEncodingUTF8
);
1293 SCNetworkConnectionCopyStatistics(SCNetworkConnectionRef connection
)
1295 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1296 xmlDataOut_t data
= NULL
;
1297 mach_msg_type_number_t datalen
= 0;
1298 int sc_status
= kSCStatusFailed
;
1299 mach_port_t session_port
;
1300 CFPropertyListRef statistics
= NULL
;
1301 kern_return_t status
;
1303 if (!isA_SCNetworkConnection(connection
)) {
1304 _SCErrorSet(kSCStatusInvalidArgument
);
1308 pthread_mutex_lock(&connectionPrivate
->lock
);
1312 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1313 if (session_port
== MACH_PORT_NULL
) {
1317 status
= pppcontroller_copystatistics(session_port
, &data
, &datalen
, &sc_status
);
1318 if (__SCNetworkConnectionNeedsRetry(connection
,
1319 "SCNetworkConnectionCopyStatistics()",
1326 if (!_SCUnserialize(&statistics
, NULL
, data
, datalen
)) {
1327 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
1329 if ((sc_status
== kSCStatusOK
) && !isA_CFDictionary(statistics
)) {
1330 sc_status
= kSCStatusFailed
;
1334 if (sc_status
!= kSCStatusOK
) {
1335 if (statistics
!= NULL
) {
1336 CFRelease(statistics
);
1339 _SCErrorSet(sc_status
);
1344 pthread_mutex_unlock(&connectionPrivate
->lock
);
1350 SCNetworkConnectionGetService(SCNetworkConnectionRef connection
)
1352 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1354 if (!isA_SCNetworkConnection(connection
)) {
1355 _SCErrorSet(kSCStatusInvalidArgument
);
1359 return connectionPrivate
->service
;
1363 SCNetworkConnectionStatus
1364 SCNetworkConnectionGetStatus(SCNetworkConnectionRef connection
)
1366 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1367 SCNetworkConnectionStatus nc_status
= kSCNetworkConnectionInvalid
;
1369 int sc_status
= kSCStatusFailed
;
1370 mach_port_t session_port
;
1371 kern_return_t status
;
1372 CFStringRef serviceID
;
1374 if (!isA_SCNetworkConnection(connection
)) {
1375 _SCErrorSet(kSCStatusInvalidArgument
);
1376 return kSCNetworkConnectionInvalid
;
1379 if (connectionPrivate
->service
== NULL
) {
1380 _SCErrorSet(kSCStatusConnectionNoService
);
1381 return kSCNetworkConnectionInvalid
;
1384 // skip retry and return immediately if we know no service is to be found.
1385 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
1386 if (CFStringGetLength(serviceID
) == 0) {
1387 _SCErrorSet(kSCStatusConnectionNoService
);
1388 return kSCNetworkConnectionInvalid
;
1391 pthread_mutex_lock(&connectionPrivate
->lock
);
1395 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1396 if (session_port
== MACH_PORT_NULL
) {
1397 nc_status
= kSCNetworkConnectionInvalid
;
1401 status
= pppcontroller_getstatus(session_port
, &nc_status
, &sc_status
);
1402 if (__SCNetworkConnectionNeedsRetry(connection
,
1403 "SCNetworkConnectionGetStatus()",
1409 // wait up to 250 ms for the network service to become available
1410 if (!connectionPrivate
->haveStatus
&&
1411 (sc_status
== kSCStatusConnectionNoService
) &&
1412 ((retry
+= 10) < 250)) {
1413 usleep(10 * 1000); // sleep 10ms between attempts
1417 if (sc_status
== kSCStatusOK
) {
1418 connectionPrivate
->haveStatus
= TRUE
;
1420 _SCErrorSet(sc_status
);
1421 nc_status
= kSCNetworkConnectionInvalid
;
1426 pthread_mutex_unlock(&connectionPrivate
->lock
);
1432 SCNetworkConnectionCopyExtendedStatus(SCNetworkConnectionRef connection
)
1434 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1435 xmlDataOut_t data
= NULL
;
1436 mach_msg_type_number_t datalen
= 0;
1437 CFPropertyListRef extstatus
= NULL
;
1439 int sc_status
= kSCStatusFailed
;
1440 mach_port_t session_port
;
1441 kern_return_t status
;
1442 CFStringRef serviceID
;
1444 if (!isA_SCNetworkConnection(connection
)) {
1445 _SCErrorSet(kSCStatusInvalidArgument
);
1449 if (connectionPrivate
->service
== NULL
) {
1450 _SCErrorSet(kSCStatusConnectionNoService
);
1454 // skip retry and return immediately if we know no service is to be found.
1455 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
1456 if (CFStringGetLength(serviceID
) == 0) {
1457 _SCErrorSet(kSCStatusConnectionNoService
);
1461 pthread_mutex_lock(&connectionPrivate
->lock
);
1465 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1466 if (session_port
== MACH_PORT_NULL
) {
1470 status
= pppcontroller_copyextendedstatus(session_port
, &data
, &datalen
, &sc_status
);
1471 if (__SCNetworkConnectionNeedsRetry(connection
,
1472 "SCNetworkConnectionCopyExtendedStatus()",
1479 if (!_SCUnserialize(&extstatus
, NULL
, data
, datalen
)) {
1480 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
1482 if ((sc_status
== kSCStatusOK
) && !isA_CFDictionary(extstatus
)) {
1483 sc_status
= kSCStatusFailed
;
1487 // wait up to 250 ms for the network service to become available
1488 if (!connectionPrivate
->haveStatus
&&
1489 (sc_status
== kSCStatusConnectionNoService
) &&
1490 ((retry
+= 10) < 250)) {
1491 usleep(10 * 1000); // sleep 10ms between attempts
1495 if (sc_status
== kSCStatusOK
) {
1496 connectionPrivate
->haveStatus
= TRUE
;
1498 if (extstatus
!= NULL
) {
1499 CFRelease(extstatus
);
1502 _SCErrorSet(sc_status
);
1507 pthread_mutex_unlock(&connectionPrivate
->lock
);
1513 _SCNetworkConnectionMergeDictionaries (const void *key
, const void *value
, void *context
)
1515 /* Add value only if not present */
1516 CFDictionaryAddValue((CFMutableDictionaryRef
)context
, (CFStringRef
)key
, (CFTypeRef
)value
);
1521 SCNetworkConnectionStart(SCNetworkConnectionRef connection
,
1522 CFDictionaryRef userOptions
,
1525 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1526 CFDataRef dataref
= NULL
;
1528 CFIndex datalen
= 0;
1530 int sc_status
= kSCStatusFailed
;
1531 mach_port_t session_port
;
1532 kern_return_t status
;
1534 if (!isA_SCNetworkConnection(connection
)) {
1535 _SCErrorSet(kSCStatusInvalidArgument
);
1539 if ((userOptions
!= NULL
) && !isA_CFDictionary(userOptions
)) {
1540 _SCErrorSet(kSCStatusInvalidArgument
);
1544 if (userOptions
== NULL
) {
1545 userOptions
= connectionPrivate
->on_demand_user_options
;
1546 } else if (connectionPrivate
->on_demand_user_options
!= NULL
) {
1547 CFDictionaryRef localUserOptions
= NULL
;
1549 localUserOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, userOptions
);
1550 if (localUserOptions
) {
1551 CFDictionaryApplyFunction(connectionPrivate
->on_demand_user_options
,
1552 _SCNetworkConnectionMergeDictionaries
,
1553 (void *)localUserOptions
);
1554 CFRelease(connectionPrivate
->on_demand_user_options
);
1555 userOptions
= connectionPrivate
->on_demand_user_options
= localUserOptions
;
1560 CFMutableDictionaryRef mdict
= NULL
;
1562 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionStart (0x%x)"), connectionPrivate
);
1564 if (userOptions
!= NULL
) {
1565 CFDictionaryRef dict
;
1566 CFStringRef encryption
;
1567 CFMutableDictionaryRef new_dict
;
1569 /* special code to remove secret information */
1570 mdict
= CFDictionaryCreateMutableCopy(NULL
, 0, userOptions
);
1572 dict
= CFDictionaryGetValue(mdict
, kSCEntNetPPP
);
1573 if (isA_CFDictionary(dict
)) {
1574 encryption
= CFDictionaryGetValue(dict
, kSCPropNetPPPAuthPasswordEncryption
);
1575 if (!isA_CFString(encryption
) ||
1576 !CFEqual(encryption
, kSCValNetPPPAuthPasswordEncryptionKeychain
)) {
1577 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1578 CFDictionaryReplaceValue(new_dict
, kSCPropNetPPPAuthPassword
, CFSTR("******"));
1579 CFDictionarySetValue(mdict
, kSCEntNetPPP
, new_dict
);
1580 CFRelease(new_dict
);
1584 dict
= CFDictionaryGetValue(mdict
, kSCEntNetL2TP
);
1585 if (isA_CFDictionary(dict
)) {
1586 encryption
= CFDictionaryGetValue(dict
, kSCPropNetL2TPIPSecSharedSecretEncryption
);
1587 if (!isA_CFString(encryption
) ||
1588 !CFEqual(encryption
, kSCValNetL2TPIPSecSharedSecretEncryptionKeychain
)) {
1589 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1590 CFDictionaryReplaceValue(new_dict
, kSCPropNetL2TPIPSecSharedSecret
, CFSTR("******"));
1591 CFDictionarySetValue(mdict
, kSCEntNetL2TP
, new_dict
);
1592 CFRelease(new_dict
);
1596 dict
= CFDictionaryGetValue(mdict
, kSCEntNetIPSec
);
1597 if (isA_CFDictionary(dict
)) {
1598 encryption
= CFDictionaryGetValue(dict
, kSCPropNetIPSecSharedSecretEncryption
);
1599 if (!isA_CFString(encryption
) ||
1600 !CFEqual(encryption
, kSCValNetIPSecSharedSecretEncryptionKeychain
)) {
1601 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1602 CFDictionaryReplaceValue(new_dict
, kSCPropNetIPSecSharedSecret
, CFSTR("******"));
1603 CFDictionarySetValue(mdict
, kSCEntNetIPSec
, new_dict
);
1604 CFRelease(new_dict
);
1609 SCLog(TRUE
, LOG_DEBUG
, CFSTR("User options: %@"), mdict
);
1610 if (mdict
!= NULL
) CFRelease(mdict
);
1613 if (userOptions
&& !_SCSerialize(userOptions
, &dataref
, &data
, &datalen
)) {
1617 pthread_mutex_lock(&connectionPrivate
->lock
);
1619 /* Clear out any cached flow divert token parameters */
1620 if (connectionPrivate
->flow_divert_token_params
!= NULL
) {
1621 CFRelease(connectionPrivate
->flow_divert_token_params
);
1622 connectionPrivate
->flow_divert_token_params
= NULL
;
1627 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1628 if (session_port
== MACH_PORT_NULL
) {
1629 if (dataref
) CFRelease(dataref
);
1633 status
= pppcontroller_start(session_port
, data
, datalen
, linger
, &sc_status
);
1634 if (__SCNetworkConnectionNeedsRetry(connection
,
1635 "SCNetworkConnectionStart()",
1641 if (dataref
) CFRelease(dataref
);
1644 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionStart (0x%x), return: %d"), connectionPrivate
, sc_status
);
1647 if (sc_status
!= kSCStatusOK
) {
1648 _SCErrorSet(sc_status
);
1652 /* connection is now started */
1656 pthread_mutex_unlock(&connectionPrivate
->lock
);
1662 SCNetworkConnectionStop(SCNetworkConnectionRef connection
,
1663 Boolean forceDisconnect
)
1665 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1667 int sc_status
= kSCStatusFailed
;
1668 mach_port_t session_port
;
1669 kern_return_t status
;
1671 if (!isA_SCNetworkConnection(connection
)) {
1672 _SCErrorSet(kSCStatusInvalidArgument
);
1677 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionStop (0x%x)"), connectionPrivate
);
1680 pthread_mutex_lock(&connectionPrivate
->lock
);
1684 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1685 if (session_port
== MACH_PORT_NULL
) {
1689 status
= pppcontroller_stop(session_port
, forceDisconnect
, &sc_status
);
1690 if (__SCNetworkConnectionNeedsRetry(connection
,
1691 "SCNetworkConnectionStop()",
1698 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionStop (0x%x), return: %d"), connectionPrivate
, sc_status
);
1701 if (sc_status
!= kSCStatusOK
) {
1702 _SCErrorSet(sc_status
);
1706 /* connection is now disconnecting */
1711 pthread_mutex_unlock(&connectionPrivate
->lock
);
1717 SCNetworkConnectionSuspend(SCNetworkConnectionRef connection
)
1719 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1721 int sc_status
= kSCStatusFailed
;
1722 mach_port_t session_port
;
1723 kern_return_t status
;
1725 if (!isA_SCNetworkConnection(connection
)) {
1726 _SCErrorSet(kSCStatusInvalidArgument
);
1731 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionSuspend (0x%x)"), connectionPrivate
);
1734 pthread_mutex_lock(&connectionPrivate
->lock
);
1738 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1739 if (session_port
== MACH_PORT_NULL
) {
1743 status
= pppcontroller_suspend(session_port
, &sc_status
);
1744 if (__SCNetworkConnectionNeedsRetry(connection
,
1745 "SCNetworkConnectionSuspend()",
1752 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionSuspend (0x%x), return: %d"), connectionPrivate
, sc_status
);
1755 if (sc_status
!= kSCStatusOK
) {
1756 _SCErrorSet(sc_status
);
1760 /* connection is now suspended */
1765 pthread_mutex_unlock(&connectionPrivate
->lock
);
1771 SCNetworkConnectionResume(SCNetworkConnectionRef connection
)
1773 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1775 int sc_status
= kSCStatusFailed
;
1776 mach_port_t session_port
;
1777 kern_return_t status
;
1779 if (!isA_SCNetworkConnection(connection
)) {
1780 _SCErrorSet(kSCStatusInvalidArgument
);
1785 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionResume (0x%x)"), connectionPrivate
);
1788 pthread_mutex_lock(&connectionPrivate
->lock
);
1792 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1793 if (session_port
== MACH_PORT_NULL
) {
1797 status
= pppcontroller_resume(session_port
, &sc_status
);
1798 if (__SCNetworkConnectionNeedsRetry(connection
,
1799 "SCNetworkConnectionResume()",
1806 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionResume (0x%x), return: %d"), connectionPrivate
, sc_status
);
1809 if (sc_status
!= kSCStatusOK
) {
1810 _SCErrorSet(sc_status
);
1814 /* connection is now resume */
1819 pthread_mutex_unlock(&connectionPrivate
->lock
);
1824 #if !TARGET_IPHONE_SIMULATOR
1826 SCNetworkConnectionRefreshOnDemandState(SCNetworkConnectionRef connection
)
1828 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1831 int sc_status
= kSCStatusFailed
;
1832 mach_port_t server_port
= __SCNetworkConnectionGetCurrentServerPort();
1833 kern_return_t status
= KERN_SUCCESS
;
1835 if (!isA_SCNetworkConnection(connection
)) {
1836 _SCErrorSet(kSCStatusInvalidArgument
);
1841 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionRefreshOnDemandState (0x%x)"), connectionPrivate
);
1844 pthread_mutex_lock(&connectionPrivate
->lock
);
1847 if (server_port
== MACH_PORT_NULL
) {
1848 server_port
= __SCNetworkConnectionRefreshServerPort(server_port
, &sc_status
);
1849 if (server_port
== MACH_PORT_NULL
) {
1850 // if server not available
1851 if (sc_status
== BOOTSTRAP_UNKNOWN_SERVICE
) {
1852 // wait up to 2.5 seconds for the [SCNetworkConnection] server
1854 if ((retry
+= 50) < 2500) {
1855 usleep(50 * 1000); // sleep 50ms between attempts
1863 status
= pppcontroller_ondemand_refresh_state(server_port
, &sc_status
);
1864 if (status
== KERN_SUCCESS
)
1867 if (status
== MACH_SEND_INVALID_DEST
) {
1868 // the server is not yet available
1869 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkConnectionRefreshOnDemandState (!dest) (0x%x)"), connectionPrivate
);
1870 } else if (status
== MIG_SERVER_DIED
) {
1871 // the server we were using is gone
1872 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkConnectionRefreshOnDemandState (!mig) (0x%x)"), connectionPrivate
);
1874 // if we got an unexpected error, don't retry
1881 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionRefreshOnDemandState (0x%x), return: %d/%d"), connectionPrivate
, status
, sc_status
);
1884 if (sc_status
!= kSCStatusOK
) {
1885 _SCErrorSet(sc_status
);
1893 pthread_mutex_unlock(&connectionPrivate
->lock
);
1896 #endif /* !TARGET_IPHONE_SIMULATOR */
1900 SCNetworkConnectionCopyUserOptions(SCNetworkConnectionRef connection
)
1902 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1903 xmlDataOut_t data
= NULL
;
1904 mach_msg_type_number_t datalen
= 0;
1905 int sc_status
= kSCStatusFailed
;
1906 mach_port_t session_port
;
1907 kern_return_t status
;
1908 CFPropertyListRef userOptions
= NULL
;
1910 if (!isA_SCNetworkConnection(connection
)) {
1911 _SCErrorSet(kSCStatusInvalidArgument
);
1915 pthread_mutex_lock(&connectionPrivate
->lock
);
1919 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1920 if (session_port
== MACH_PORT_NULL
) {
1924 status
= pppcontroller_copyuseroptions(session_port
, &data
, &datalen
, &sc_status
);
1925 if (__SCNetworkConnectionNeedsRetry(connection
,
1926 "SCNetworkConnectionCopyUserOptions()",
1933 if (!_SCUnserialize(&userOptions
, NULL
, data
, datalen
)) {
1934 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
1936 if ((sc_status
== kSCStatusOK
) && (userOptions
!= NULL
) && !isA_CFDictionary(userOptions
)) {
1937 sc_status
= kSCStatusFailed
;
1941 if (sc_status
== kSCStatusOK
) {
1942 if (userOptions
== NULL
) {
1943 // if no user options, return an empty dictionary
1944 userOptions
= CFDictionaryCreate(NULL
,
1948 &kCFTypeDictionaryKeyCallBacks
,
1949 &kCFTypeDictionaryValueCallBacks
);
1953 CFRelease(userOptions
);
1956 _SCErrorSet(sc_status
);
1961 pthread_mutex_unlock(&connectionPrivate
->lock
);
1967 __SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection
,
1968 CFRunLoopRef runLoop
,
1969 CFStringRef runLoopMode
,
1970 dispatch_queue_t queue
)
1972 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1974 int sc_status
= kSCStatusFailed
;
1975 mach_port_t session_port
;
1976 kern_return_t status
;
1978 pthread_mutex_lock(&connectionPrivate
->lock
);
1980 if (connectionPrivate
->rlsFunction
== NULL
) {
1981 _SCErrorSet(kSCStatusInvalidArgument
);
1985 if ((connectionPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled on a dispatch queue
1986 ((queue
!= NULL
) && connectionPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
1987 _SCErrorSet(kSCStatusInvalidArgument
);
1991 if (!connectionPrivate
->scheduled
) {
1995 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1996 if (session_port
== MACH_PORT_NULL
) {
2000 status
= pppcontroller_notification(session_port
, 1, &sc_status
);
2001 if (__SCNetworkConnectionNeedsRetry(connection
,
2002 "__SCNetworkConnectionScheduleWithRunLoop()",
2008 if (sc_status
!= kSCStatusOK
) {
2009 _SCErrorSet(sc_status
);
2013 if (runLoop
!= NULL
) {
2014 connectionPrivate
->rls
= CFMachPortCreateRunLoopSource(NULL
, connectionPrivate
->notify_port
, 0);
2015 connectionPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2018 connectionPrivate
->scheduled
= TRUE
;
2021 if (queue
!= NULL
) {
2022 dispatch_group_t group
= NULL
;
2024 dispatch_source_t source
;
2026 mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
2027 source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
, mp
, 0, queue
);
2028 if (source
== NULL
) {
2029 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkConnection dispatch_source_create() failed"));
2030 _SCErrorSet(kSCStatusFailed
);
2034 // have our dispatch source hold a reference to the notification CFMachPort
2035 CFRetain(connectionPrivate
->notify_port
);
2036 dispatch_set_context(source
, (void *)connectionPrivate
->notify_port
);
2037 dispatch_set_finalizer_f(source
, (dispatch_function_t
)CFRelease
);
2039 // retain the dispatch queue
2040 connectionPrivate
->dispatchQueue
= queue
;
2041 dispatch_retain(connectionPrivate
->dispatchQueue
);
2044 // We've taken a reference to the callers dispatch_queue and we
2045 // want to hold on to that reference until we've processed any/all
2046 // notifications. To facilitate this we create a group, dispatch
2047 // any notification blocks to via that group, and when the caller
2048 // has told us to stop the notifications (unschedule) we wait for
2049 // the group to empty and use the group's finalizer to release
2050 // our reference to the SCNetworkConnection.
2052 group
= dispatch_group_create();
2053 connectionPrivate
->dispatchGroup
= group
;
2054 CFRetain(connection
);
2055 dispatch_set_context(connectionPrivate
->dispatchGroup
, (void *)connection
);
2056 dispatch_set_finalizer_f(connectionPrivate
->dispatchGroup
, (dispatch_function_t
)CFRelease
);
2058 dispatch_source_set_event_handler(source
, ^{
2061 u_int8_t buf
[sizeof(mach_msg_empty_t
) + MAX_TRAILER_SIZE
];
2062 mach_msg_empty_rcv_t msg
;
2063 mach_no_senders_notification_t no_senders
;
2065 CFMachPortRef notify_port
;
2067 kr
= mach_msg(¬ify_msg
.msg
.header
, // msg
2068 MACH_RCV_MSG
, // options
2070 sizeof(notify_msg
), // rcv_size
2072 MACH_MSG_TIMEOUT_NONE
, // timeout
2073 MACH_PORT_NULL
); // notify
2074 if (kr
!= KERN_SUCCESS
) {
2075 SCLog(TRUE
, LOG_ERR
,
2076 CFSTR("SCDynamicStore notification handler, kr=0x%x"),
2081 CFRetain(connection
);
2082 notify_port
= dispatch_get_context(source
);
2084 dispatch_group_async(group
, queue
, ^{
2085 __SCNetworkConnectionCallBack(notify_port
,
2086 (void *)¬ify_msg
.msg
,
2088 (void *)connection
);
2089 CFRelease(connection
);
2093 dispatch_source_set_cancel_handler(source
, ^{
2094 dispatch_release(source
);
2097 connectionPrivate
->dispatchSource
= source
;
2098 dispatch_resume(source
);
2100 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, connectionPrivate
->rlList
)) {
2102 * if we do not already have notifications scheduled with
2103 * this runLoop / runLoopMode
2105 CFRunLoopAddSource(runLoop
, connectionPrivate
->rls
, runLoopMode
);
2108 _SC_schedule(connection
, runLoop
, runLoopMode
, connectionPrivate
->rlList
);
2115 pthread_mutex_unlock(&connectionPrivate
->lock
);
2121 __SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection
,
2122 CFRunLoopRef runLoop
,
2123 CFStringRef runLoopMode
,
2124 dispatch_queue_t queue
)
2126 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2127 dispatch_group_t drainGroup
= NULL
;
2128 dispatch_queue_t drainQueue
= NULL
;
2129 int sc_status
= kSCStatusFailed
;
2132 mach_port_t session_port
;
2133 kern_return_t status
;
2135 // hold a reference while we unschedule
2136 CFRetain(connection
);
2138 pthread_mutex_lock(&connectionPrivate
->lock
);
2140 if ((runLoop
!= NULL
) && !connectionPrivate
->scheduled
) { // if we should be scheduled (but are not)
2141 _SCErrorSet(kSCStatusInvalidArgument
);
2145 if (((runLoop
== NULL
) && (connectionPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
2146 ((runLoop
!= NULL
) && (connectionPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
2147 _SCErrorSet(kSCStatusInvalidArgument
);
2151 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2152 if (session_port
== MACH_PORT_NULL
) {
2156 if (connectionPrivate
->dispatchQueue
!= NULL
) {
2157 // cancel dispatchSource
2158 if (connectionPrivate
->dispatchSource
!= NULL
) {
2159 dispatch_source_cancel(connectionPrivate
->dispatchSource
);
2160 connectionPrivate
->dispatchSource
= NULL
;
2163 // save dispatchQueue/group, release reference when all queue'd blocks
2164 // have been processed, allow re-scheduling
2165 drainGroup
= connectionPrivate
->dispatchGroup
;
2166 connectionPrivate
->dispatchGroup
= NULL
;
2167 drainQueue
= connectionPrivate
->dispatchQueue
;
2168 connectionPrivate
->dispatchQueue
= NULL
;
2170 if (!_SC_unschedule(connection
, runLoop
, runLoopMode
, connectionPrivate
->rlList
, FALSE
)) {
2171 // if not currently scheduled on this runLoop / runLoopMode
2172 _SCErrorSet(kSCStatusFailed
);
2176 n
= CFArrayGetCount(connectionPrivate
->rlList
);
2177 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, connectionPrivate
->rlList
)) {
2179 * if we are no longer scheduled to receive notifications for
2180 * this runLoop / runLoopMode
2182 CFRunLoopRemoveSource(runLoop
, connectionPrivate
->rls
, runLoopMode
);
2185 // if *all* notifications have been unscheduled
2186 CFRelease(connectionPrivate
->rlList
);
2187 connectionPrivate
->rlList
= NULL
;
2188 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
2189 CFRelease(connectionPrivate
->rls
);
2190 connectionPrivate
->rls
= NULL
;
2196 // if *all* notifications have been unscheduled
2197 connectionPrivate
->scheduled
= FALSE
;
2199 status
= pppcontroller_notification(session_port
, 0, &sc_status
);
2200 if (__SCNetworkConnectionNeedsRetry(connection
,
2201 "__SCNetworkConnectionUnscheduleFromRunLoop pppcontroller_notification()",
2204 sc_status
= kSCStatusOK
;
2205 status
= KERN_SUCCESS
;
2208 if ((status
!= KERN_SUCCESS
) || (sc_status
!= kSCStatusOK
)) {
2209 _SCErrorSet(sc_status
);
2218 pthread_mutex_unlock(&connectionPrivate
->lock
);
2220 if (drainGroup
!= NULL
) {
2221 dispatch_group_notify(drainGroup
, drainQueue
, ^{
2222 // release group/queue references
2223 dispatch_release(drainQueue
);
2224 dispatch_release(drainGroup
); // releases our connection reference
2228 // release our reference
2229 CFRelease(connection
);
2236 SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection
,
2237 CFRunLoopRef runLoop
,
2238 CFStringRef runLoopMode
)
2240 if (!isA_SCNetworkConnection(connection
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
2241 _SCErrorSet(kSCStatusInvalidArgument
);
2245 return __SCNetworkConnectionScheduleWithRunLoop(connection
, runLoop
, runLoopMode
, NULL
);
2250 SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection
,
2251 CFRunLoopRef runLoop
,
2252 CFStringRef runLoopMode
)
2254 if (!isA_SCNetworkConnection(connection
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
2255 _SCErrorSet(kSCStatusInvalidArgument
);
2259 return __SCNetworkConnectionUnscheduleFromRunLoop(connection
, runLoop
, runLoopMode
, NULL
);
2264 SCNetworkConnectionSetDispatchQueue(SCNetworkConnectionRef connection
,
2265 dispatch_queue_t queue
)
2269 if (!isA_SCNetworkConnection(connection
)) {
2270 _SCErrorSet(kSCStatusInvalidArgument
);
2274 if (queue
!= NULL
) {
2275 ok
= __SCNetworkConnectionScheduleWithRunLoop(connection
, NULL
, NULL
, queue
);
2277 ok
= __SCNetworkConnectionUnscheduleFromRunLoop(connection
, NULL
, NULL
, NULL
);
2284 /* Requires having called SCNetworkConnectionSelectServiceWithOptions previously */
2286 SCNetworkConnectionIsOnDemandSuspended(SCNetworkConnectionRef connection
)
2288 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2290 if (!isA_SCNetworkConnection(connection
)) {
2291 _SCErrorSet(kSCStatusInvalidArgument
);
2295 if (connectionPrivate
->on_demand_info
!= NULL
) {
2296 uint32_t isSuspended
= 0;
2297 CFNumberRef num
= NULL
;
2299 num
= CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCPropNetVPNOnDemandSuspended
);
2300 if (isA_CFNumber(num
) &&
2301 CFNumberGetValue(num
, kCFNumberSInt32Type
, &isSuspended
) &&
2302 (isSuspended
!= 0)) {
2307 _SCErrorSet(kSCStatusOK
);
2312 SCNetworkConnectionTriggerOnDemandCallback (SCNetworkConnectionRef connection
,
2313 SCNetworkConnectionStatus status
,
2316 dispatch_group_t group
= (dispatch_group_t
)info
;
2317 if (status
!= kSCNetworkConnectionConnecting
) {
2318 dispatch_group_leave(group
);
2323 SCNetworkConnectionTriggerOnDemandIfNeeded (CFStringRef hostName
,
2324 Boolean afterDNSFail
,
2328 SCNetworkConnectionRef connection
= NULL
;
2329 dispatch_group_t group
= NULL
;
2330 CFMutableDictionaryRef options_dict
= NULL
;
2331 Boolean connectedIfNeeded
= FALSE
;
2333 /* Require hostName, require non-root user */
2334 if (hostName
== NULL
|| geteuid() == 0) {
2338 group
= dispatch_group_create();
2339 if (group
== NULL
) {
2343 SCNetworkConnectionContext context
= { 0, (void*)group
, NULL
, NULL
, NULL
};
2344 connection
= SCNetworkConnectionCreate(kCFAllocatorDefault
, SCNetworkConnectionTriggerOnDemandCallback
, &context
);
2345 if (connection
== NULL
) {
2349 options_dict
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2350 if (options_dict
== NULL
) {
2354 CFDictionaryAddValue(options_dict
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
2355 CFDictionaryAddValue(options_dict
, kSCNetworkConnectionSelectionOptionOnDemandRetry
, (afterDNSFail
? kCFBooleanTrue
: kCFBooleanFalse
));
2358 CFNumberRef trafficClassRef
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &trafficClass
);
2359 if (trafficClassRef
) {
2360 CFDictionaryAddValue(options_dict
, kSCNetworkConnectionSelectionOptionOnDemandTrafficClass
, trafficClassRef
);
2361 CFRelease(trafficClassRef
);
2365 if (SCNetworkConnectionSelectServiceWithOptions(connection
, options_dict
)) {
2366 SCNetworkConnectionStatus status
= SCNetworkConnectionGetStatus(connection
);
2368 if (status
== kSCNetworkConnectionConnected
) {
2369 /* If already connected, done */
2370 connectedIfNeeded
= TRUE
;
2371 } else if (status
== kSCNetworkConnectionDisconnected
|| status
== kSCNetworkConnectionConnecting
) {
2372 dispatch_queue_t callback_queue
= dispatch_queue_create("SCNetworkConnectionTrigger queue", NULL
);
2373 if (callback_queue
) {
2374 SCNetworkConnectionSetDispatchQueue(connection
, callback_queue
);
2376 dispatch_group_enter(group
);
2378 /* If the connection is already connecting, we don't need to start.
2379 If disconnected, do call start. */
2380 if (status
== kSCNetworkConnectionDisconnected
) {
2381 SCNetworkConnectionStart(connection
, NULL
, TRUE
);
2384 dispatch_group_wait(group
, (timeout
== 0) ? DISPATCH_TIME_FOREVER
: dispatch_time(DISPATCH_TIME_NOW
, NSEC_PER_SEC
* timeout
));
2386 SCNetworkConnectionSetDispatchQueue(connection
, NULL
);
2388 status
= SCNetworkConnectionGetStatus(connection
);
2389 if (status
== kSCNetworkConnectionConnected
) {
2390 connectedIfNeeded
= TRUE
;
2393 dispatch_release(callback_queue
);
2397 /* Not needed, so we can return true */
2398 connectedIfNeeded
= TRUE
;
2403 CFRelease(options_dict
);
2407 CFRelease(connection
);
2411 dispatch_release(group
);
2414 return connectedIfNeeded
;
2419 SCNetworkConnectionCopyOnDemandInfo(SCNetworkConnectionRef connection
,
2420 CFStringRef
*onDemandRemoteAddress
,
2421 SCNetworkConnectionStatus
*onDemandConnectionStatus
)
2423 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2425 if (!isA_SCNetworkConnection(connection
)) {
2426 _SCErrorSet(kSCStatusInvalidArgument
);
2430 if (connectionPrivate
->service
== NULL
) {
2431 _SCErrorSet(kSCStatusConnectionNoService
);
2435 if (onDemandRemoteAddress
!= NULL
) {
2436 *onDemandRemoteAddress
= NULL
;
2439 if (onDemandConnectionStatus
!= NULL
) {
2440 *onDemandConnectionStatus
= kSCNetworkConnectionInvalid
;
2443 if (connectionPrivate
->on_demand_info
!= NULL
) {
2444 if (onDemandRemoteAddress
!= NULL
) {
2445 CFStringRef address
=
2446 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandRemoteAddress
);
2447 if (isA_CFString(address
)) {
2448 *onDemandRemoteAddress
= address
;
2449 CFRetain(*onDemandRemoteAddress
);
2453 if (onDemandConnectionStatus
!= NULL
) {
2455 CFNumberRef status_num
=
2456 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandStatus
);
2457 if (isA_CFNumber(status_num
) && CFNumberGetValue(status_num
, kCFNumberIntType
, &num
)) {
2458 *onDemandConnectionStatus
= num
;
2463 return connectionPrivate
->on_demand
;
2468 SCNetworkConnectionGetReachabilityInfo(SCNetworkConnectionRef connection
,
2469 SCNetworkReachabilityFlags
*reach_flags
,
2470 unsigned int *reach_if_index
)
2472 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2474 if (!isA_SCNetworkConnection(connection
)) {
2475 _SCErrorSet(kSCStatusInvalidArgument
);
2479 if (connectionPrivate
->service
== NULL
) {
2480 _SCErrorSet(kSCStatusConnectionNoService
);
2484 if (reach_flags
!= NULL
) {
2488 if (reach_if_index
!= NULL
) {
2489 *reach_if_index
= 0;
2492 if (connectionPrivate
->on_demand_info
!= NULL
) {
2493 if (reach_flags
!= NULL
) {
2495 CFNumberRef flags_num
=
2496 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandReachFlags
);
2497 if (isA_CFNumber(flags_num
) && CFNumberGetValue(flags_num
, kCFNumberIntType
, &num
)) {
2502 if (reach_if_index
!= NULL
) {
2504 CFNumberRef if_index_num
=
2505 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandReachInterfaceIndex
);
2506 if (isA_CFNumber(if_index_num
) && CFNumberGetValue(if_index_num
, kCFNumberIntType
, &num
)) {
2507 *reach_if_index
= num
;
2516 SCNetworkConnectionType
2517 SCNetworkConnectionGetType(SCNetworkConnectionRef connection
)
2519 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2521 if (!isA_SCNetworkConnection(connection
)) {
2522 _SCErrorSet(kSCStatusInvalidArgument
);
2523 return kSCNetworkConnectionTypeUnknown
;
2526 if (connectionPrivate
->service
== NULL
) {
2527 _SCErrorSet(kSCStatusConnectionNoService
);
2528 return kSCNetworkConnectionTypeUnknown
;
2531 _SCErrorSet(kSCStatusOK
);
2533 return connectionPrivate
->type
;
2538 validate_flow_properties(CFDictionaryRef flowProperties
)
2540 CFStringRef host_name_str
;
2541 CFDataRef host_address_data
;
2542 CFNumberRef host_port_num
;
2544 if (!isA_CFDictionary(flowProperties
)) {
2548 /* Validate the host name if one was given */
2549 host_name_str
= CFDictionaryGetValue(flowProperties
, kSCNetworkConnectionFlowPropertyHostName
);
2550 if (host_name_str
!= NULL
&& (!isA_CFString(host_name_str
) || CFStringGetLength(host_name_str
) == 0)) {
2554 /* Validate the address if one was given */
2555 host_address_data
= CFDictionaryGetValue(flowProperties
, kSCNetworkConnectionFlowPropertyHostAddress
);
2556 if (host_address_data
!= NULL
) {
2557 struct sockaddr
*sock_addr
;
2559 if (!isA_CFData(host_address_data
) || CFDataGetLength(host_address_data
) < sizeof(struct sockaddr
)) {
2563 sock_addr
= (struct sockaddr
*)CFDataGetBytePtr(host_address_data
);
2564 if (CFDataGetLength(host_address_data
) < sock_addr
->sa_len
) {
2568 if (sock_addr
->sa_family
== AF_INET
) {
2569 if (sock_addr
->sa_len
>= sizeof(struct sockaddr_in
)) {
2570 struct sockaddr_in
*sa_in
= (struct sockaddr_in
*)(void *)sock_addr
;
2571 in_addr_t any
= { INADDR_ANY
};
2572 if (memcmp(&sa_in
->sin_addr
, &any
, sizeof(any
)) == 0) {
2578 } else if (sock_addr
->sa_family
== AF_INET6
) {
2579 if (sock_addr
->sa_len
>= sizeof(struct sockaddr_in6
)) {
2580 struct sockaddr_in6
*sa_in6
= (struct sockaddr_in6
*)(void *)sock_addr
;
2581 struct in6_addr any
= IN6ADDR_ANY_INIT
;
2582 if (memcmp(&sa_in6
->sin6_addr
, &any
, sizeof(any
)) == 0) {
2589 /* We must have either a host name or an address */
2590 if (host_name_str
== NULL
&& host_address_data
== NULL
) {
2594 /* Validate the port */
2595 host_port_num
= CFDictionaryGetValue(flowProperties
, kSCNetworkConnectionFlowPropertyHostPort
);
2596 if (host_port_num
!= NULL
) {
2598 if (!isA_CFNumber(host_port_num
) || !CFNumberGetValue(host_port_num
, kCFNumberIntType
, &num
)) {
2614 SCNetworkConnectionCopyFlowDivertToken(SCNetworkConnectionRef connection
,
2615 CFDictionaryRef flowProperties
)
2617 CFDictionaryRef app_properties
= NULL
;
2618 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2619 CFDataRef token
= NULL
;
2621 if (!isA_SCNetworkConnection(connection
)) {
2622 _SCErrorSet(kSCStatusInvalidArgument
);
2626 if (connectionPrivate
->service
== NULL
) {
2627 _SCErrorSet(kSCStatusConnectionNoService
);
2631 if (connectionPrivate
->type
!= kSCNetworkConnectionTypeAppLayerVPN
) {
2632 _SCErrorSet(kSCStatusInvalidArgument
);
2636 if (!validate_flow_properties(flowProperties
)) {
2637 _SCErrorSet(kSCStatusInvalidArgument
);
2641 app_properties
= VPNAppLayerCopyCachedAppProperties(connectionPrivate
->client_audit_token
,
2642 connectionPrivate
->client_pid
,
2643 connectionPrivate
->client_uuid
,
2644 connectionPrivate
->client_bundle_id
);
2645 if (app_properties
== NULL
) {
2646 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkConnectionCopyFlowDivertToken: no cached app properties available"));
2647 _SCErrorSet(kSCStatusFailed
);
2651 token
= VPNAppLayerCreateFlowDivertToken(connection
, app_properties
, flowProperties
);
2654 if (app_properties
!= NULL
) {
2655 CFRelease(app_properties
);
2663 SCNetworkConnectionGetServiceIdentifier (SCNetworkConnectionRef connection
)
2665 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2666 int service_identifier
= -1;
2668 if (connectionPrivate
->service
!= NULL
) {
2669 service_identifier
= 0;
2670 if (connectionPrivate
->on_demand_info
!= NULL
) {
2671 CFNumberRef id_num
= CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCPropNetDNSServiceIdentifier
);
2673 if (isA_CFNumber(id_num
)) {
2674 CFNumberGetValue(id_num
, kCFNumberIntType
, &service_identifier
);
2679 return service_identifier
;
2684 #pragma mark User level "dial" API
2687 #define k_NetworkConnect_Notification "com.apple.networkConnect"
2688 #define k_NetworkConnect_Pref_File CFSTR("com.apple.networkConnect")
2689 #define k_InterentConnect_Pref_File CFSTR("com.apple.internetconnect")
2691 #define k_Dial_Default_Key CFSTR("ConnectByDefault") // needs to go into SC
2692 #define k_Last_Service_Id_Key CFSTR("ServiceID")
2693 #define k_Unique_Id_Key CFSTR("UniqueIdentifier")
2696 /* Private Prototypes */
2697 static Boolean
SCNetworkConnectionPrivateCopyDefaultServiceIDForDial (CFStringRef
*serviceID
);
2698 static Boolean
SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore (CFStringRef
*serviceID
);
2699 static Boolean
SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray
, CFDictionaryRef
*userOptions
);
2700 static Boolean
SCNetworkConnectionPrivateIsPPPService (CFStringRef serviceID
, CFStringRef subType1
, CFStringRef subType2
);
2701 static void addPasswordFromKeychain (CFStringRef serviceID
, CFDictionaryRef
*userOptions
);
2702 static CFStringRef
copyPasswordFromKeychain (CFStringRef uniqueID
);
2704 static int notify_userprefs_token
= -1;
2706 static CFDictionaryRef onDemand_configuration
= NULL
;
2707 static Boolean onDemand_force_refresh
= FALSE
;
2708 static pthread_mutex_t onDemand_notify_lock
= PTHREAD_MUTEX_INITIALIZER
;
2709 static int onDemand_notify_token
= -1;
2713 * return TRUE if domain1 ends with domain2, and will check for trailing "."
2715 #define WILD_CARD_MATCH_STR CFSTR("*")
2717 _SC_domainEndsWithDomain(CFStringRef compare_domain
, CFStringRef match_domain
)
2720 Boolean ret
= FALSE
;
2721 CFStringRef s1
= NULL
;
2722 Boolean s1_created
= FALSE
;
2723 CFStringRef s2
= NULL
;
2724 Boolean s2_created
= FALSE
;
2725 CFStringRef s3
= NULL
;
2727 if (CFEqual(match_domain
, WILD_CARD_MATCH_STR
)) {
2731 if (CFStringHasSuffix(compare_domain
, CFSTR("."))) {
2733 range
.length
= CFStringGetLength(compare_domain
) - 1;
2734 s1
= CFStringCreateWithSubstring(NULL
, compare_domain
, range
);
2740 s1
= compare_domain
;
2743 if (CFStringHasSuffix(match_domain
, CFSTR("."))) {
2745 range
.length
= CFStringGetLength(match_domain
) - 1;
2746 s2
= CFStringCreateWithSubstring(NULL
, match_domain
, range
);
2755 if (CFStringHasPrefix(s2
, CFSTR("*."))) {
2757 range
.length
= CFStringGetLength(s2
)-2;
2758 s3
= CFStringCreateWithSubstring(NULL
, s2
, range
);
2769 ret
= CFStringHasSuffix(s1
, s2
);
2773 if (s1_created
) CFRelease(s1
);
2774 if (s2_created
) CFRelease(s2
);
2780 static CFDictionaryRef
2781 __SCNetworkConnectionCopyOnDemandConfiguration(void)
2785 uint64_t triggersCount
= 0;
2786 CFDictionaryRef configuration
;
2788 pthread_mutex_lock(&onDemand_notify_lock
);
2789 if (onDemand_notify_token
== -1) {
2790 status
= notify_register_check(kSCNETWORKCONNECTION_ONDEMAND_NOTIFY_KEY
, &onDemand_notify_token
);
2791 if (status
!= NOTIFY_STATUS_OK
) {
2792 SCLog(TRUE
, LOG_ERR
, CFSTR("notify_register_check() failed, status=%lu"), status
);
2793 onDemand_notify_token
= -1;
2797 if (onDemand_notify_token
!= -1) {
2798 status
= notify_check(onDemand_notify_token
, &changed
);
2799 if (status
!= NOTIFY_STATUS_OK
) {
2800 SCLog(TRUE
, LOG_ERR
, CFSTR("notify_check() failed, status=%lu"), status
);
2801 (void)notify_cancel(onDemand_notify_token
);
2802 onDemand_notify_token
= -1;
2806 if (changed
&& (onDemand_notify_token
!= -1)) {
2807 status
= notify_get_state(onDemand_notify_token
, &triggersCount
);
2808 if (status
!= NOTIFY_STATUS_OK
) {
2809 SCLog(TRUE
, LOG_ERR
, CFSTR("notify_get_state() failed, status=%lu"), status
);
2810 (void)notify_cancel(onDemand_notify_token
);
2811 onDemand_notify_token
= -1;
2815 if (changed
|| onDemand_force_refresh
) {
2818 if (_sc_debug
|| (debug
> 0)) {
2819 SCLog(TRUE
, LOG_INFO
,
2820 CFSTR("OnDemand information %s"),
2821 (onDemand_configuration
== NULL
) ? "fetched" : "updated");
2824 if (onDemand_configuration
!= NULL
) {
2825 CFRelease(onDemand_configuration
);
2826 onDemand_configuration
= NULL
;
2829 if ((triggersCount
> 0) || onDemand_force_refresh
) {
2830 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, kSCDynamicStoreDomainState
, kSCEntNetOnDemand
);
2831 onDemand_configuration
= SCDynamicStoreCopyValue(NULL
, key
);
2833 if ((onDemand_configuration
!= NULL
) && !isA_CFDictionary(onDemand_configuration
)) {
2834 CFRelease(onDemand_configuration
);
2835 onDemand_configuration
= NULL
;
2839 onDemand_force_refresh
= FALSE
;
2842 configuration
= (onDemand_configuration
!= NULL
) ? CFRetain(onDemand_configuration
) : NULL
;
2843 pthread_mutex_unlock(&onDemand_notify_lock
);
2845 return configuration
;
2851 __SCNetworkConnectionForceOnDemandConfigurationRefresh(void)
2853 pthread_mutex_lock(&onDemand_notify_lock
);
2854 onDemand_force_refresh
= TRUE
;
2855 pthread_mutex_unlock(&onDemand_notify_lock
);
2862 __SCNetworkConnectionShouldNeverMatch(CFDictionaryRef trigger
, CFStringRef hostName
, pid_t client_pid
)
2864 CFArrayRef exceptedProcesses
;
2865 int exceptedProcessesCount
;
2866 int exceptedProcessesIndex
;
2867 CFArrayRef exceptions
;
2868 int exceptionsCount
;
2869 int exceptionsIndex
;
2871 // we have a matching domain, check against exception list
2872 exceptions
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandMatchDomainsNever
);
2873 exceptionsCount
= isA_CFArray(exceptions
) ? CFArrayGetCount(exceptions
) : 0;
2874 for (exceptionsIndex
= 0; exceptionsIndex
< exceptionsCount
; exceptionsIndex
++) {
2875 CFStringRef exception
;
2877 exception
= CFArrayGetValueAtIndex(exceptions
, exceptionsIndex
);
2878 if (isA_CFString(exception
) && _SC_domainEndsWithDomain(hostName
, exception
)) {
2879 // found matching exception
2880 if (_sc_debug
|| (debug
> 0)) {
2881 SCLog(TRUE
, LOG_INFO
, CFSTR("OnDemand match exception"));
2887 if (client_pid
!= 0) {
2888 exceptedProcesses
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandPluginPIDs
);
2889 exceptedProcessesCount
= isA_CFArray(exceptedProcesses
) ? CFArrayGetCount(exceptedProcesses
) : 0;
2890 for (exceptedProcessesIndex
= 0; exceptedProcessesIndex
< exceptedProcessesCount
; exceptedProcessesIndex
++) {
2894 pidRef
= CFArrayGetValueAtIndex(exceptedProcesses
, exceptedProcessesIndex
);
2895 if (isA_CFNumber(pidRef
) && CFNumberGetValue(pidRef
, kCFNumberIntType
, &pid
)) {
2896 if (pid
== client_pid
) {
2907 __SCNetworkConnectionDomainGetMatchWithParameters(CFStringRef action
, CFPropertyListRef actionParameters
, CFStringRef hostName
, CFStringRef
*probeString
)
2909 CFArrayRef actionArray
= NULL
;
2910 CFIndex actionArraySize
= 0;
2912 CFStringRef matchDomain
= NULL
;
2914 /* For now, only support EvaluateConnection, which takes a CFArray */
2915 if (!CFEqual(action
, kSCValNetVPNOnDemandRuleActionEvaluateConnection
) || !isA_CFArray(actionParameters
)) {
2919 actionArray
= (CFArrayRef
)actionParameters
;
2920 actionArraySize
= CFArrayGetCount(actionArray
);
2922 /* Process domain rules, with actions of ConnectIfNeeded and NeverConnect */
2923 for (i
= 0; i
< actionArraySize
; i
++) {
2924 CFStringRef domainAction
= NULL
;
2925 CFDictionaryRef domainRule
= CFArrayGetValueAtIndex(actionArray
, i
);
2926 CFArrayRef domains
= NULL
;
2927 CFIndex domainsCount
= 0;
2928 CFIndex domainsIndex
;
2930 if (!isA_CFDictionary(domainRule
)) {
2934 domains
= CFDictionaryGetValue(domainRule
, kSCPropNetVPNOnDemandRuleActionParametersDomains
);
2935 if (!isA_CFArray(domains
)) {
2939 domainsCount
= CFArrayGetCount(domains
);
2940 for (domainsIndex
= 0; domainsIndex
< domainsCount
; domainsIndex
++) {
2942 domain
= CFArrayGetValueAtIndex(domains
, domainsIndex
);
2943 if (isA_CFString(domain
) && _SC_domainEndsWithDomain(hostName
, domain
)) {
2944 matchDomain
= domain
;
2950 domainAction
= CFDictionaryGetValue(domainRule
, kSCPropNetVPNOnDemandRuleActionParametersDomainAction
);
2951 if (isA_CFString(domainAction
) && CFEqual(domainAction
, kSCValNetVPNOnDemandRuleActionParametersDomainActionNeverConnect
)) {
2954 /* If we found a match, save the optional probe string as well */
2956 *probeString
= CFDictionaryGetValue(domainRule
, kSCPropNetVPNOnDemandRuleActionParametersRequiredURLStringProbe
);
2967 __SCNetworkConnectionDomainGetMatch(CFDictionaryRef trigger
, CFStringRef hostName
, Boolean onDemandRetry
)
2973 CFStringRef match_domain
= NULL
;
2975 /* Old configuration: always, never, on retry lists */
2976 key
= onDemandRetry
? kSCNetworkConnectionOnDemandMatchDomainsOnRetry
: kSCNetworkConnectionOnDemandMatchDomainsAlways
;
2978 domains
= CFDictionaryGetValue(trigger
, key
);
2979 domainsCount
= isA_CFArray(domains
) ? CFArrayGetCount(domains
) : 0;
2980 for (domainsIndex
= 0; domainsIndex
< domainsCount
; domainsIndex
++) {
2983 domain
= CFArrayGetValueAtIndex(domains
, domainsIndex
);
2984 if (isA_CFString(domain
) && _SC_domainEndsWithDomain(hostName
, domain
)) {
2985 match_domain
= domain
;
2990 return match_domain
;
2995 __SCNetworkConnectionShouldAlwaysConnect(CFDictionaryRef trigger
)
2997 CFStringRef action
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleAction
);
2998 return (isA_CFString(action
) && CFEqual(action
, kSCValNetVPNOnDemandRuleActionConnect
));
3003 __SCNetworkConnectionShouldIgnoreTrigger(CFDictionaryRef trigger
)
3005 CFStringRef action
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleAction
);
3007 CFNumberRef reachFlags
;
3009 if (isA_CFString(action
) &&
3010 (CFEqual(action
, kSCValNetVPNOnDemandRuleActionIgnore
) ||
3011 CFEqual(action
, kSCValNetVPNOnDemandRuleActionDisconnect
))) {
3015 /* If the VPN server is not reachable immediately, ignore this trigger */
3016 reachFlags
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandReachFlags
);
3017 if (isA_CFNumber(reachFlags
) && CFNumberGetValue(reachFlags
, kCFNumberIntType
, &flags
)) {
3018 if (!(flags
& kSCNetworkReachabilityFlagsReachable
) || (flags
& kSCNetworkReachabilityFlagsConnectionRequired
)) {
3027 static CFDictionaryRef
3028 __SCNetworkConnectionCopyMatchingTriggerWithName(CFDictionaryRef configuration
,
3029 CFStringRef hostName
,
3031 Boolean onDemandRetry
,
3032 CFDictionaryRef
*match_info
,
3033 Boolean
*triggerNow
,
3034 CFStringRef
*probe_string
)
3036 CFDictionaryRef result
= NULL
;
3037 int sc_status
= kSCStatusOK
;
3038 CFArrayRef triggers
;
3039 uint64_t triggersCount
= 0;
3041 Boolean usedOnDemandRetry
= FALSE
;
3043 if (triggerNow
!= NULL
) {
3044 *triggerNow
= FALSE
;
3047 if (match_info
!= NULL
) {
3051 triggers
= CFDictionaryGetValue(configuration
, kSCNetworkConnectionOnDemandTriggers
);
3052 triggersCount
= isA_CFArray(triggers
) ? CFArrayGetCount(triggers
) : 0;
3053 for (triggersIndex
= 0; triggersIndex
< triggersCount
; triggersIndex
++) {
3054 CFStringRef matched_domain
= NULL
;
3055 CFStringRef matched_probe_string
= NULL
;
3056 CFDictionaryRef trigger
;
3057 Boolean trigger_matched
= FALSE
;
3059 usedOnDemandRetry
= FALSE
;
3061 trigger
= CFArrayGetValueAtIndex(triggers
, triggersIndex
);
3062 if (!isA_CFDictionary(trigger
)) {
3063 // if not a valid "OnDemand" configuration
3067 if (__SCNetworkConnectionShouldAlwaysConnect(trigger
)) {
3068 /* If the trigger action is 'Connect', always match this trigger */
3069 /* First check the never match list */
3070 if (__SCNetworkConnectionShouldNeverMatch(trigger
, hostName
, client_pid
)) {
3073 trigger_matched
= TRUE
;
3074 } else if (__SCNetworkConnectionShouldIgnoreTrigger(trigger
)) {
3075 /* If the trigger action is 'Ignore' or 'Disconnect', skip this trigger */
3076 sc_status
= kSCStatusConnectionIgnore
;
3079 CFStringRef action
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleAction
);
3080 CFArrayRef actionParameters
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleActionParameters
);
3081 if (action
&& actionParameters
) {
3082 matched_domain
= __SCNetworkConnectionDomainGetMatchWithParameters(action
, actionParameters
, hostName
, &matched_probe_string
);
3083 usedOnDemandRetry
= TRUE
;
3085 if (onDemandRetry
) {
3086 matched_domain
= __SCNetworkConnectionDomainGetMatch(trigger
, hostName
, TRUE
);
3087 usedOnDemandRetry
= TRUE
;
3089 matched_domain
= __SCNetworkConnectionDomainGetMatch(trigger
, hostName
, FALSE
);
3090 if (matched_domain
== NULL
&& result
== NULL
) {
3091 /* Check the retry list if Always failed */
3092 matched_domain
= __SCNetworkConnectionDomainGetMatch(trigger
, hostName
, TRUE
);
3093 usedOnDemandRetry
= TRUE
;
3098 if (matched_domain
) {
3099 if (__SCNetworkConnectionShouldNeverMatch(trigger
, hostName
, client_pid
)) {
3100 matched_domain
= NULL
;
3103 trigger_matched
= TRUE
;
3108 if (trigger_matched
) {
3109 // if we have a matching domain and there were no exceptions
3110 // then we pass back the OnDemand info
3111 if (match_info
!= NULL
) {
3112 CFMutableDictionaryRef minfo
;
3113 SCNetworkConnectionType type
= kSCNetworkConnectionTypeIPLayerVPN
;
3114 CFNumberRef type_num
;
3116 if (*match_info
!= NULL
) {
3117 CFRelease(*match_info
);
3121 minfo
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
3123 &kCFTypeDictionaryKeyCallBacks
,
3124 &kCFTypeDictionaryValueCallBacks
);
3126 type_num
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &type
);
3127 CFDictionarySetValue(minfo
, kSCNetworkConnectionOnDemandMatchInfoVPNType
, type_num
);
3128 CFRelease(type_num
);
3129 if (matched_domain
) {
3130 CFDictionarySetValue(minfo
, kSCNetworkConnectionOnDemandMatchInfoDomain
, matched_domain
);
3132 CFDictionarySetValue(minfo
,
3133 kSCNetworkConnectionOnDemandMatchInfoOnRetry
,
3134 (usedOnDemandRetry
? kCFBooleanTrue
: kCFBooleanFalse
));
3136 *match_info
= minfo
;
3139 if (probe_string
!= NULL
) {
3140 if (*probe_string
!= NULL
) {
3141 CFRelease(*probe_string
);
3142 *probe_string
= NULL
;
3145 if (matched_probe_string
) {
3146 *probe_string
= CFRetain(matched_probe_string
);
3152 /* If retry was requested, or we found Always match, trigger now */
3153 if (onDemandRetry
|| !usedOnDemandRetry
) {
3154 if (triggerNow
!= NULL
) {
3160 /* If we matched the Retry list, but Always was requested,
3161 keep going through triggers in case one matches an Always */
3169 _SCErrorSet(sc_status
);
3174 static CFDictionaryRef
3175 __SCNetworkConnectionCopyTriggerWithService(CFDictionaryRef configuration
,
3176 CFStringRef service_id
)
3178 CFArrayRef triggers
;
3179 uint64_t triggersCount
= 0;
3182 triggers
= CFDictionaryGetValue(configuration
, kSCNetworkConnectionOnDemandTriggers
);
3183 triggersCount
= isA_CFArray(triggers
) ? CFArrayGetCount(triggers
) : 0;
3184 for (triggersIndex
= 0; triggersIndex
< triggersCount
; triggersIndex
++) {
3185 CFDictionaryRef trigger
;
3186 CFStringRef trigger_service_id
;
3188 trigger
= CFArrayGetValueAtIndex(triggers
, triggersIndex
);
3189 if (!isA_CFDictionary(trigger
)) {
3190 // if not a valid "OnDemand" configuration
3194 trigger_service_id
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandServiceID
);
3195 if (isA_CFString(trigger_service_id
) && CFEqual(trigger_service_id
, service_id
)) {
3205 __private_extern__ CFDictionaryRef
3206 __SCNetworkConnectionCopyTokenParameters(SCNetworkConnectionRef connection
)
3208 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
3209 CFDictionaryRef parameters
= NULL
;
3210 uint8_t params_buffer
[PPP_MACH_MAX_INLINE_DATA
];
3211 uint32_t params_buffer_len
= sizeof(params_buffer
);
3212 int sc_status
= kSCStatusOK
;
3213 mach_port_t session_port
;
3214 kern_return_t status
;
3216 pthread_mutex_lock(&connectionPrivate
->lock
);
3218 parameters
= connectionPrivate
->flow_divert_token_params
;
3219 if (parameters
!= NULL
) {
3220 CFRetain(parameters
);
3225 if (parameters
!= NULL
) {
3226 CFRelease(parameters
);
3230 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
3231 if (session_port
== MACH_PORT_NULL
) {
3235 status
= pppcontroller_flow_divert_copy_token_parameters(session_port
, params_buffer
, ¶ms_buffer_len
);
3236 if (status
== KERN_SUCCESS
) {
3237 if (params_buffer_len
> 0) {
3238 CFDataRef params_data
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
,
3242 parameters
= CFPropertyListCreateWithData(kCFAllocatorDefault
,
3244 kCFPropertyListImmutable
,
3247 CFRelease(params_data
);
3251 if (__SCNetworkConnectionNeedsRetry(connection
, "__SCNetworkConnectionCopyTokenParameters()", status
, &sc_status
)) {
3255 if (sc_status
!= kSCStatusOK
) {
3256 _SCErrorSet(sc_status
);
3260 if (parameters
!= NULL
&& connectionPrivate
->flow_divert_token_params
== NULL
) {
3261 connectionPrivate
->flow_divert_token_params
= (CFDictionaryRef
)CFRetain(parameters
);
3264 pthread_mutex_unlock(&connectionPrivate
->lock
);
3270 __SCNetworkConnectionCopyOnDemandInfoWithName(SCDynamicStoreRef
*storeP
,
3271 CFStringRef hostName
,
3272 Boolean onDemandRetry
,
3273 CFStringRef
*connectionServiceID
,
3274 SCNetworkConnectionStatus
*connectionStatus
,
3275 CFStringRef
*vpnRemoteAddress
) /* CFDictionaryRef *info */
3277 CFDictionaryRef configuration
;
3279 int sc_status
= kSCStatusOK
;
3280 CFDictionaryRef trigger
;
3281 Boolean trigger_now
= FALSE
;
3283 configuration
= __SCNetworkConnectionCopyOnDemandConfiguration();
3284 if (configuration
== NULL
) {
3285 _SCErrorSet(sc_status
);
3289 trigger
= __SCNetworkConnectionCopyMatchingTriggerWithName(configuration
, hostName
, 0, onDemandRetry
, NULL
, &trigger_now
, NULL
);
3290 if (trigger
!= NULL
&& trigger_now
) {
3292 SCNetworkConnectionStatus onDemandStatus
= kSCNetworkConnectionDisconnected
;
3296 if (!CFDictionaryGetValueIfPresent(trigger
, kSCNetworkConnectionOnDemandStatus
, (const void **)&num
) ||
3297 !isA_CFNumber(num
) ||
3298 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &onDemandStatus
)) {
3299 onDemandStatus
= kSCNetworkConnectionDisconnected
;
3301 if (connectionStatus
!= NULL
) {
3302 *connectionStatus
= onDemandStatus
;
3305 if (connectionServiceID
!= NULL
) {
3306 *connectionServiceID
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandServiceID
);
3307 *connectionServiceID
= isA_CFString(*connectionServiceID
);
3308 if ((*connectionServiceID
!= NULL
) && (CFStringGetLength(*connectionServiceID
) > 0)) {
3309 CFRetain(*connectionServiceID
);
3311 SCLog(TRUE
, LOG_INFO
, CFSTR("OnDemand%s configuration error, no serviceID"),
3312 onDemandRetry
? " (on retry)" : "");
3313 *connectionServiceID
= NULL
;
3318 if (vpnRemoteAddress
!= NULL
) {
3319 *vpnRemoteAddress
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandRemoteAddress
);
3320 *vpnRemoteAddress
= isA_CFString(*vpnRemoteAddress
);
3321 if ((*vpnRemoteAddress
!= NULL
) && (CFStringGetLength(*vpnRemoteAddress
) > 0)) {
3322 CFRetain(*vpnRemoteAddress
);
3324 SCLog(TRUE
, LOG_INFO
, CFSTR("OnDemand%s configuration error, no server address"),
3325 onDemandRetry
? " (on retry)" : "");
3326 *vpnRemoteAddress
= NULL
;
3332 if ((connectionServiceID
!= NULL
) && (*connectionServiceID
!= NULL
)) {
3333 CFRelease(*connectionServiceID
);
3334 *connectionServiceID
= NULL
;
3336 if ((vpnRemoteAddress
!= NULL
) && (*vpnRemoteAddress
!= NULL
)) {
3337 CFRelease(*vpnRemoteAddress
);
3338 *vpnRemoteAddress
= NULL
;
3340 sc_status
= kSCStatusFailed
;
3342 if (_sc_debug
|| (debug
> 0)) {
3343 SCLog(TRUE
, LOG_INFO
, CFSTR("OnDemand%s match, connection status = %d"),
3344 onDemandRetry
? " (on retry)" : "",
3354 // if (_sc_debug || (debug > 0)) {
3355 // SCLog(TRUE, LOG_INFO, CFSTR("OnDemand domain name(s) not matched"));
3358 if (configuration
!= NULL
) CFRelease(configuration
);
3360 _SCErrorSet(sc_status
);
3366 __SCNetworkConnectionCopyUserPreferencesInternal(CFDictionaryRef selectionOptions
,
3367 CFStringRef
*serviceID
,
3368 CFDictionaryRef
*userOptions
)
3370 int prefsChanged
= 1;
3372 Boolean success
= FALSE
;
3374 if (notify_userprefs_token
== -1) {
3375 status
= notify_register_check(k_NetworkConnect_Notification
, ¬ify_userprefs_token
);
3376 if (status
!= NOTIFY_STATUS_OK
) {
3377 SCLog(TRUE
, LOG_ERR
, CFSTR("notify_register_check() failed, status=%lu"), status
);
3378 (void)notify_cancel(notify_userprefs_token
);
3379 notify_userprefs_token
= -1;
3381 // clear the "something has changed" state
3382 (void) notify_check(notify_userprefs_token
, &prefsChanged
);
3386 if (notify_userprefs_token
!= -1) {
3387 status
= notify_check(notify_userprefs_token
, &prefsChanged
);
3388 if (status
!= NOTIFY_STATUS_OK
) {
3389 SCLog(TRUE
, LOG_ERR
, CFSTR("notify_check() failed, status=%lu"), status
);
3390 (void)notify_cancel(notify_userprefs_token
);
3391 notify_userprefs_token
= -1;
3397 *userOptions
= NULL
;
3399 if (selectionOptions
!= NULL
) {
3400 Boolean catchAllFound
= FALSE
;
3401 CFIndex catchAllService
= 0;
3402 CFIndex catchAllConfig
= 0;
3403 CFStringRef hostName
= NULL
;
3404 CFStringRef priority
= NULL
;
3405 CFArrayRef serviceNames
= NULL
;
3406 CFDictionaryRef services
= NULL
;
3407 CFIndex serviceIndex
;
3408 CFIndex servicesCount
;
3410 hostName
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
);
3411 if (hostName
== NULL
) {
3412 hostName
= CFDictionaryGetValue(selectionOptions
, kSCPropNetPPPOnDemandHostName
);
3414 hostName
= isA_CFString(hostName
);
3415 if (hostName
== NULL
)
3416 goto done_selection
; // if no hostname for matching
3418 priority
= CFDictionaryGetValue(selectionOptions
, kSCPropNetPPPOnDemandPriority
);
3419 if (!isA_CFString(priority
))
3420 priority
= kSCValNetPPPOnDemandPriorityDefault
;
3423 if (!isA_CFArray(serviceNames
))
3424 goto done_selection
;
3427 if (!isA_CFDictionary(services
)) {
3428 goto done_selection
;
3431 servicesCount
= CFArrayGetCount(serviceNames
);
3432 for (serviceIndex
= 0; serviceIndex
< servicesCount
; serviceIndex
++) {
3433 CFIndex configIndex
;
3434 CFIndex configsCount
;
3435 CFArrayRef serviceConfigs
;
3436 CFStringRef serviceName
;
3439 serviceName
= CFArrayGetValueAtIndex(serviceNames
, serviceIndex
);
3440 if (!isA_CFString(serviceName
)) {
3444 serviceConfigs
= CFDictionaryGetValue(services
, serviceName
);
3445 if (!isA_CFArray(serviceConfigs
)) {
3449 configsCount
= CFArrayGetCount(serviceConfigs
);
3450 for (configIndex
= 0; configIndex
< configsCount
; configIndex
++) {
3451 CFNumberRef autodial
;
3452 CFDictionaryRef config
;
3453 CFDictionaryRef pppConfig
;
3455 config
= CFArrayGetValueAtIndex(serviceConfigs
, configIndex
);
3456 if (!isA_CFDictionary(config
)) {
3460 pppConfig
= CFDictionaryGetValue(config
, kSCEntNetPPP
);
3461 if (!isA_CFDictionary(pppConfig
)) {
3465 autodial
= CFDictionaryGetValue(pppConfig
, kSCPropNetPPPOnDemandEnabled
);
3466 if (!isA_CFNumber(autodial
)) {
3470 CFNumberGetValue(autodial
, kCFNumberIntType
, &val
);
3473 CFIndex domainsCount
;
3474 CFIndex domainsIndex
;
3476 /* we found an conditional connection enabled configuration */
3479 domains
= CFDictionaryGetValue(pppConfig
, kSCPropNetPPPOnDemandDomains
);
3480 if (!isA_CFArray(domains
)) {
3484 domainsCount
= CFArrayGetCount(domains
);
3485 for (domainsIndex
= 0; domainsIndex
< domainsCount
; domainsIndex
++) {
3488 domain
= CFArrayGetValueAtIndex(domains
, domainsIndex
);
3489 if (!isA_CFString(domain
)) {
3493 if (!catchAllFound
&&
3494 (CFStringCompare(domain
, CFSTR(""), 0) == kCFCompareEqualTo
3495 || CFStringCompare(domain
, CFSTR("."), 0) == kCFCompareEqualTo
))
3497 // found a catch all
3498 catchAllFound
= TRUE
;
3499 catchAllService
= serviceIndex
;
3500 catchAllConfig
= configIndex
;
3503 if (_SC_domainEndsWithDomain(hostName
, domain
)) {
3504 // found matching configuration
3505 *serviceID
= serviceName
;
3506 CFRetain(*serviceID
);
3507 *userOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, config
);
3508 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
3509 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCPropNetPPPOnDemandPriority
, priority
);
3510 addPasswordFromKeychain(*serviceID
, userOptions
);
3512 goto done_selection
;
3519 // config not found, do we have a catchall ?
3520 if (catchAllFound
) {
3521 CFDictionaryRef config
;
3522 CFArrayRef serviceConfigs
;
3523 CFStringRef serviceName
;
3525 serviceName
= CFArrayGetValueAtIndex(serviceNames
, catchAllService
);
3526 serviceConfigs
= CFDictionaryGetValue(services
, serviceName
);
3527 config
= CFArrayGetValueAtIndex(serviceConfigs
, catchAllConfig
);
3529 *serviceID
= serviceName
;
3530 CFRetain(*serviceID
);
3531 *userOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, config
);
3532 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
3533 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCPropNetPPPOnDemandPriority
, priority
);
3534 addPasswordFromKeychain(*serviceID
, userOptions
);
3536 goto done_selection
;
3542 CFRelease(serviceNames
);
3545 CFRelease(services
);
3549 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionCopyUserPreferences %@"), success
? CFSTR("succeeded") : CFSTR("failed"));
3550 SCLog(TRUE
, LOG_DEBUG
, CFSTR("Selection options: %@"), selectionOptions
);
3556 /* we don't have selection options */
3558 // (1) Figure out which service ID we care about, allocate it into passed "serviceID"
3559 success
= SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(serviceID
);
3561 if (success
&& (*serviceID
!= NULL
)) {
3562 // (2) Get the list of user data for this service ID
3563 CFPropertyListRef userServices
= NULL
;
3566 // (3) We are expecting an array if the user has defined records for this service ID or NULL if the user hasn't
3567 if (userServices
!= NULL
) {
3568 if (isA_CFArray(userServices
)) {
3569 // (4) Get the default set of user options for this service
3570 success
= SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray((CFArrayRef
)userServices
,
3572 if(success
&& (userOptions
!= NULL
)) {
3573 addPasswordFromKeychain(*serviceID
, userOptions
);
3576 SCLog(TRUE
, LOG_DEBUG
, CFSTR("Error, userServices are not of type CFArray!"));
3579 CFRelease(userServices
); // this is OK because SCNetworkConnectionPrivateISExpectedCFType() checks for NULL
3584 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionCopyUserPreferences %@, no selection options"), success
? CFSTR("succeeded") : CFSTR("failed"));
3592 SCNetworkConnectionCopyUserPreferences(CFDictionaryRef selectionOptions
,
3593 CFStringRef
*serviceID
,
3594 CFDictionaryRef
*userOptions
)
3596 Boolean success
= FALSE
;
3599 /* initialize runtime */
3600 pthread_once(&initialized
, __SCNetworkConnectionInitialize
);
3602 /* first check for new VPN OnDemand style */
3603 if (selectionOptions
!= NULL
) {
3604 CFStringRef hostName
;
3606 hostName
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
);
3607 if (isA_CFString(hostName
)) {
3608 CFStringRef connectionServiceID
= NULL
;
3609 SCNetworkConnectionStatus connectionStatus
= kSCNetworkConnectionInvalid
;
3610 Boolean onDemandRetry
;
3613 val
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandRetry
);
3614 onDemandRetry
= isA_CFBoolean(val
) ? CFBooleanGetValue(val
) : TRUE
;
3616 success
= __SCNetworkConnectionCopyOnDemandInfoWithName(NULL
,
3619 &connectionServiceID
,
3623 SCLog(TRUE
, LOG_DEBUG
,
3624 CFSTR("SCNetworkConnectionCopyUserPreferences __SCNetworkConnectionCopyOnDemandInfoWithName returns %d w/status %d"),
3630 // if the hostname matches an OnDemand domain
3631 if (connectionStatus
== kSCNetworkConnectionConnected
) {
3632 // if we are already connected
3633 if (connectionServiceID
!= NULL
) {
3634 CFRelease(connectionServiceID
);
3639 *serviceID
= connectionServiceID
;
3640 *userOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3641 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
3643 } else if (!onDemandRetry
) {
3644 // if the hostname does not match an OnDemand domain and we have
3645 // not yet issued an initial DNS query (i.e. it's not a query
3646 // being retried after the VPN has been established) then we're
3653 return __SCNetworkConnectionCopyUserPreferencesInternal(selectionOptions
, serviceID
, userOptions
);
3658 SCNetworkConnectionOnDemandShouldRetryOnFailure (SCNetworkConnectionRef connection
)
3660 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
3661 CFDictionaryRef match_info
= NULL
;
3663 if (!isA_SCNetworkConnection(connection
)) {
3664 _SCErrorSet(kSCStatusInvalidArgument
);
3668 if (connectionPrivate
->service
== NULL
) {
3669 _SCErrorSet(kSCStatusConnectionNoService
);
3673 if (isA_CFDictionary(connectionPrivate
->on_demand_user_options
)) {
3674 match_info
= CFDictionaryGetValue(connectionPrivate
->on_demand_user_options
, kSCNetworkConnectionSelectionOptionOnDemandMatchInfo
);
3675 if (isA_CFDictionary(match_info
)) {
3676 CFBooleanRef onRetry
= CFDictionaryGetValue(match_info
, kSCNetworkConnectionOnDemandMatchInfoOnRetry
);
3677 if (isA_CFBoolean(onRetry
)) {
3678 return CFBooleanGetValue(onRetry
);
3688 // Mask is optional in routes dictionary; if not present, whole addresses are matched
3690 __SCNetworkConnectionIPv4AddressMatchesRoutes (struct sockaddr_in
*addr_in
, CFDictionaryRef routes
)
3694 CFDataRef maskData
= NULL
;
3695 struct in_addr
*maskDataArray
;
3696 CFDataRef routeaddrData
= NULL
;
3697 struct in_addr
*routeaddrDataArray
;
3699 if (!isA_CFDictionary(routes
)) {
3703 routeaddrData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoAddresses
);
3704 maskData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoMasks
);
3706 /* routeaddrData and maskData are packed arrays of addresses; make sure they have the same length */
3707 if (!isA_CFData(routeaddrData
) || (maskData
&& (!isA_CFData(maskData
) || CFDataGetLength(routeaddrData
) != CFDataGetLength(maskData
)))) {
3711 routeaddrDataArray
= (struct in_addr
*)(void*)CFDataGetBytePtr(routeaddrData
);
3713 maskDataArray
= (struct in_addr
*)(void*)CFDataGetBytePtr(maskData
);
3716 count
= CFDataGetLength(routeaddrData
) / sizeof(struct in_addr
);
3718 for (i
=0; i
<count
; i
++) {
3719 struct in_addr routeAddr
= *routeaddrDataArray
;
3722 struct in_addr mask
= *maskDataArray
;
3724 if ((addr_in
->sin_addr
.s_addr
& mask
.s_addr
) == (routeAddr
.s_addr
& mask
.s_addr
)) {
3729 if (addr_in
->sin_addr
.s_addr
== routeAddr
.s_addr
) {
3733 routeaddrDataArray
++;
3740 __SCNetworkConnectionMaskIPv6Address(struct in6_addr
*addr
, struct in6_addr
*mask
)
3744 for (i
= 0; i
< sizeof(struct in6_addr
); i
++)
3745 addr
->s6_addr
[i
] &= mask
->s6_addr
[i
];
3749 // Mask is optional in routes dictionary; if not present, whole addresses are matched
3751 __SCNetworkConnectionIPv6AddressMatchesRoutes (struct sockaddr_in6
*addr_in6
, CFDictionaryRef routes
)
3755 CFDataRef maskData
= NULL
;
3756 struct in6_addr
*maskDataArray
;
3757 CFDataRef routeaddrData
= NULL
;
3758 struct in6_addr
*routeaddrDataArray
;
3760 if (!isA_CFDictionary(routes
)) {
3764 routeaddrData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoAddresses
);
3765 maskData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoMasks
);
3767 /* routeaddrData and maskData are packed arrays of addresses; make sure they have the same length */
3768 if (!isA_CFData(routeaddrData
) || (maskData
&& (!isA_CFData(maskData
) || CFDataGetLength(routeaddrData
) != CFDataGetLength(maskData
)))) {
3772 routeaddrDataArray
= (struct in6_addr
*)(void*)CFDataGetBytePtr(routeaddrData
);
3774 maskDataArray
= (struct in6_addr
*)(void*)CFDataGetBytePtr(maskData
);
3777 count
= CFDataGetLength(routeaddrData
) / sizeof(struct in6_addr
);
3779 for (i
=0; i
<count
; i
++) {
3781 struct in6_addr cmpAddr
;
3782 struct in6_addr
*mask
= maskDataArray
;
3783 struct in6_addr routeAddr
;
3785 memcpy(&routeAddr
, routeaddrDataArray
, sizeof(routeAddr
));
3786 memcpy(&cmpAddr
, &addr_in6
->sin6_addr
, sizeof(cmpAddr
));
3787 __SCNetworkConnectionMaskIPv6Address(&routeAddr
, mask
);
3788 __SCNetworkConnectionMaskIPv6Address(&cmpAddr
, mask
);
3790 if (!memcmp(&routeAddr
, &cmpAddr
, sizeof(routeAddr
))) {
3794 if (!memcmp(routeaddrDataArray
, &addr_in6
->sin6_addr
, sizeof(struct in6_addr
))) {
3799 routeaddrDataArray
++;
3806 __SCNetworkConnectionAddressMatchesRedirectedDNS(CFDictionaryRef trigger
, const struct sockaddr
*input_addr
)
3808 CFBooleanRef redirectedRef
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandDNSRedirectDetected
);
3810 if (isA_CFBoolean(redirectedRef
) && CFBooleanGetValue(redirectedRef
)) {
3811 /* DNS is redirected. Look for address list. */
3812 CFDictionaryRef redirectedAddressesRef
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandDNSRedirectedAddresses
);
3814 if (isA_CFDictionary(redirectedAddressesRef
)) {
3815 if (input_addr
->sa_family
== AF_INET
) {
3816 return __SCNetworkConnectionIPv4AddressMatchesRoutes((struct sockaddr_in
*)(void*)input_addr
, CFDictionaryGetValue(redirectedAddressesRef
, kSCNetworkConnectionNetworkInfoIPv4
));
3817 } else if (input_addr
->sa_family
== AF_INET6
) {
3818 return __SCNetworkConnectionIPv6AddressMatchesRoutes((struct sockaddr_in6
*)(void*)input_addr
, CFDictionaryGetValue(redirectedAddressesRef
, kSCNetworkConnectionNetworkInfoIPv6
));
3826 /* If the required probe has failed, we need to tunnel the address. Equivalent to redirected DNS. */
3828 __SCNetworkConnectionRequiredProbeFailed (CFDictionaryRef trigger
, CFStringRef probeString
)
3830 CFDictionaryRef probeResults
= NULL
;
3832 if (!isA_CFString(probeString
)) {
3836 probeResults
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandProbeResults
);
3837 if (!isA_CFDictionary(probeResults
)) {
3841 CFBooleanRef result
= CFDictionaryGetValue(probeResults
, probeString
);
3843 /* Only a value of kCFBooleanFalse marks the probe as failed */
3844 return (isA_CFBoolean(result
) && !CFBooleanGetValue(result
));
3848 SCNetworkConnectionCanTunnelAddress (SCNetworkConnectionRef connection
, const struct sockaddr
*address
, Boolean
*startImmediately
)
3850 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
3851 CFStringRef serviceID
= NULL
;
3852 CFDictionaryRef configuration
= NULL
;
3853 CFDictionaryRef trigger
= NULL
;
3854 CFDictionaryRef tunneledNetworks
= NULL
;
3855 sa_family_t address_family
= AF_UNSPEC
;
3856 Boolean success
= FALSE
;
3858 if (startImmediately
) {
3859 *startImmediately
= FALSE
;
3862 if (address
== NULL
) {
3866 address_family
= address
->sa_family
;
3867 if (address_family
!= AF_INET
&& address_family
!= AF_INET6
) {
3871 if (!isA_SCNetworkConnection(connection
)) {
3872 _SCErrorSet(kSCStatusInvalidArgument
);
3876 if (connectionPrivate
->service
== NULL
) {
3877 _SCErrorSet(kSCStatusConnectionNoService
);
3881 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
3882 if (!isA_CFString(serviceID
)) {
3886 configuration
= __SCNetworkConnectionCopyOnDemandConfiguration();
3887 if (configuration
== NULL
) {
3891 trigger
= __SCNetworkConnectionCopyTriggerWithService(configuration
, serviceID
);
3892 if (trigger
== NULL
) {
3896 if (__SCNetworkConnectionRequiredProbeFailed(trigger
, connectionPrivate
->on_demand_required_probe
)) {
3897 /* If probe failed, we can't trust DNS - connect now */
3898 if (startImmediately
) {
3899 *startImmediately
= TRUE
;
3905 if (__SCNetworkConnectionAddressMatchesRedirectedDNS(trigger
, address
)) {
3906 if (startImmediately
) {
3907 *startImmediately
= TRUE
;
3913 tunneledNetworks
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandTunneledNetworks
);
3914 if (!isA_CFDictionary(tunneledNetworks
)) {
3918 if (address_family
== AF_INET
) {
3919 CFDictionaryRef ip_dict
;
3920 Boolean matches
= FALSE
;
3921 struct sockaddr_in
*addr_in
= (struct sockaddr_in
*)(void*)address
;
3923 ip_dict
= CFDictionaryGetValue(tunneledNetworks
, kSCNetworkConnectionNetworkInfoIPv4
);
3924 if (!isA_CFDictionary(ip_dict
)) {
3928 matches
= __SCNetworkConnectionIPv4AddressMatchesRoutes(addr_in
, CFDictionaryGetValue(ip_dict
, kSCNetworkConnectionNetworkInfoIncludedRoutes
));
3931 if (!__SCNetworkConnectionIPv4AddressMatchesRoutes(addr_in
, CFDictionaryGetValue(ip_dict
, kSCNetworkConnectionNetworkInfoExcludedRoutes
))) {
3937 CFDictionaryRef ip6_dict
;
3938 Boolean matches
= FALSE
;
3939 struct sockaddr_in6
*addr_in6
= (struct sockaddr_in6
*)(void*)address
;
3941 ip6_dict
= CFDictionaryGetValue(tunneledNetworks
, kSCNetworkConnectionNetworkInfoIPv6
);
3942 if (!isA_CFDictionary(ip6_dict
)) {
3946 matches
= __SCNetworkConnectionIPv6AddressMatchesRoutes(addr_in6
, CFDictionaryGetValue(ip6_dict
, kSCNetworkConnectionNetworkInfoIncludedRoutes
));
3949 if (!__SCNetworkConnectionIPv6AddressMatchesRoutes(addr_in6
, CFDictionaryGetValue(ip6_dict
, kSCNetworkConnectionNetworkInfoExcludedRoutes
))) {
3956 if (configuration
) {
3957 CFRelease(configuration
);
3966 SCNetworkConnectionSelectServiceWithOptions(SCNetworkConnectionRef connection
, CFDictionaryRef selectionOptions
)
3968 CFStringRef account_identifier
= NULL
;
3969 CFDictionaryRef configuration
= NULL
;
3970 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
3971 CFDictionaryRef found_trigger
= NULL
;
3972 CFStringRef host_name
= NULL
;
3973 Boolean is_retry
= TRUE
;
3974 CFDictionaryRef match_info
= NULL
;
3975 CFMutableDictionaryRef new_user_options
= NULL
;
3976 SCNetworkConnectionStatus on_demand_status
= kSCNetworkConnectionInvalid
;
3977 CFStringRef requiredProbe
= NULL
;
3978 CFStringRef service_id
= NULL
;
3979 Boolean skip_prefs
= FALSE
;
3980 Boolean success
= TRUE
;
3981 CFDictionaryRef user_options
= NULL
;
3983 if (!isA_SCNetworkConnection(connection
)) {
3984 _SCErrorSet(kSCStatusInvalidArgument
);
3989 /* Can't call this on a connection that is already associated with a service */
3990 if (connectionPrivate
->service
!= NULL
) {
3991 _SCErrorSet(kSCStatusInvalidArgument
);
3996 if (isA_CFDictionary(selectionOptions
)) {
3997 CFBooleanRef no_user_prefs
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionNoUserPrefs
);
3998 CFBooleanRef retry
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandRetry
);
4000 account_identifier
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandAccountIdentifier
);
4001 host_name
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
);
4002 skip_prefs
= (isA_CFBoolean(no_user_prefs
) && CFBooleanGetValue(no_user_prefs
));
4004 if (isA_CFBoolean(retry
)) {
4005 is_retry
= CFBooleanGetValue(retry
);
4009 configuration
= __SCNetworkConnectionCopyOnDemandConfiguration();
4011 /* First, check for a match with the App Layer rules */
4012 service_id
= VPNAppLayerCopyMatchingService(connectionPrivate
->client_audit_token
,
4013 connectionPrivate
->client_pid
,
4014 connectionPrivate
->client_uuid
,
4015 connectionPrivate
->client_bundle_id
,
4019 if (service_id
!= NULL
) {
4020 Boolean use_app_layer
= TRUE
;
4022 if (isA_CFDictionary(configuration
)) {
4023 found_trigger
= __SCNetworkConnectionCopyTriggerWithService(configuration
, service_id
);
4024 if (found_trigger
!= NULL
) {
4025 CFNumberRef status_num
= CFDictionaryGetValue(found_trigger
, kSCNetworkConnectionOnDemandStatus
);
4026 if (isA_CFNumber(status_num
)) {
4027 CFNumberGetValue(status_num
, kCFNumberIntType
, &on_demand_status
);
4030 * If the trigger should be ignored, still use App Layer VPN if it is already connected or
4031 * is in the process of connecting.
4033 if (__SCNetworkConnectionShouldIgnoreTrigger(found_trigger
) &&
4034 on_demand_status
!= kSCNetworkConnectionConnecting
&&
4035 on_demand_status
!= kSCNetworkConnectionConnected
)
4037 use_app_layer
= FALSE
;
4042 if (use_app_layer
) {
4043 /* If this is not the 'OnRetry' call, and the service has not yet started, the match may need to return false */
4045 match_info
!= NULL
&&
4046 on_demand_status
!= kSCNetworkConnectionConnecting
&&
4047 on_demand_status
!= kSCNetworkConnectionConnected
) {
4048 CFBooleanRef matchedOnRetry
= CFDictionaryGetValue(match_info
, kSCNetworkConnectionOnDemandMatchInfoOnRetry
);
4049 if (matchedOnRetry
&& CFBooleanGetValue(matchedOnRetry
)) {
4050 /* Don't return that we matched always; wait for SCNetworkConnectionOnDemandShouldRetryOnFailure */
4054 connectionPrivate
->type
= kSCNetworkConnectionTypeAppLayerVPN
;
4057 CFRelease(service_id
);
4059 if (match_info
!= NULL
) {
4060 CFRelease(match_info
);
4063 if (found_trigger
!= NULL
) {
4064 CFRelease(found_trigger
);
4065 found_trigger
= NULL
;
4070 /* Next, check the IP layer rules */
4071 if (isA_CFDictionary(configuration
) && host_name
!= NULL
) {
4072 Boolean triggerNow
= FALSE
;
4074 found_trigger
= __SCNetworkConnectionCopyMatchingTriggerWithName(configuration
, host_name
, connectionPrivate
->client_pid
, is_retry
, &match_info
, &triggerNow
, &requiredProbe
);
4075 if (found_trigger
!= NULL
) {
4076 service_id
= CFDictionaryGetValue(found_trigger
, kSCNetworkConnectionOnDemandServiceID
);
4077 if (isA_CFString(service_id
)) {
4078 CFRetain(service_id
);
4079 connectionPrivate
->type
= kSCNetworkConnectionTypeIPLayerVPN
;
4087 } else if (!is_retry
) {
4091 if (match_info
!= NULL
) {
4092 CFRelease(match_info
);
4097 /* Next, check the user preferences */
4098 if (!skip_prefs
&& __SCNetworkConnectionCopyUserPreferencesInternal(selectionOptions
, &service_id
, &user_options
)) {
4099 CFMutableDictionaryRef minfo
;
4100 CFNumberRef type_num
;
4102 if (isA_CFDictionary(configuration
)) {
4103 found_trigger
= __SCNetworkConnectionCopyTriggerWithService(configuration
, service_id
);
4105 connectionPrivate
->type
= kSCNetworkConnectionTypePPP
;
4107 minfo
= CFDictionaryCreateMutable(NULL
,
4109 &kCFTypeDictionaryKeyCallBacks
,
4110 &kCFTypeDictionaryValueCallBacks
);
4111 type_num
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &connectionPrivate
->type
);
4112 CFDictionarySetValue(minfo
, kSCNetworkConnectionOnDemandMatchInfoVPNType
, type_num
);
4113 CFRelease(type_num
);
4119 if (service_id
== NULL
) {
4120 _SCErrorSet(kSCStatusOK
);
4125 connectionPrivate
->service
= _SCNetworkServiceCopyActive(NULL
, service_id
);
4126 if (connectionPrivate
->service
== NULL
) {
4127 _SCErrorSet(kSCStatusOK
);
4132 if (found_trigger
!= NULL
) {
4133 if (connectionPrivate
->on_demand_info
) {
4134 CFRelease(connectionPrivate
->on_demand_info
);
4136 connectionPrivate
->on_demand_info
= found_trigger
;
4137 CFRetain(connectionPrivate
->on_demand_info
);
4139 if (on_demand_status
== kSCNetworkConnectionInvalid
) {
4140 CFNumberRef status_num
= CFDictionaryGetValue(found_trigger
, kSCNetworkConnectionOnDemandStatus
);
4141 if (isA_CFNumber(status_num
)) {
4142 CFNumberGetValue(status_num
, kCFNumberIntType
, &on_demand_status
);
4146 if (on_demand_status
!= kSCNetworkConnectionConnected
) {
4147 if (connectionPrivate
->type
== kSCNetworkConnectionTypeAppLayerVPN
) {
4148 /* Check App Layer on demand flag */
4149 CFBooleanRef app_on_demand_enabled
=
4150 CFDictionaryGetValue(found_trigger
, kSCNetworkConnectionOnDemandMatchAppEnabled
);
4151 if (isA_CFBoolean(app_on_demand_enabled
) && CFBooleanGetValue(app_on_demand_enabled
)) {
4152 connectionPrivate
->on_demand
= TRUE
;
4155 connectionPrivate
->on_demand
= TRUE
;
4158 } else if (connectionPrivate
->type
== kSCNetworkConnectionTypePPP
) {
4159 /* If we got the service from __SCNetworkConnectionCopyUserPreferencesInternal, then it's on demand */
4160 connectionPrivate
->on_demand
= TRUE
;
4163 if (user_options
== NULL
) {
4164 new_user_options
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
4166 &kCFTypeDictionaryKeyCallBacks
,
4167 &kCFTypeDictionaryValueCallBacks
);
4169 new_user_options
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, user_options
);
4172 if (host_name
!= NULL
) {
4173 CFDictionarySetValue(new_user_options
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, host_name
);
4176 if (connectionPrivate
->on_demand
&& match_info
!= NULL
) {
4177 CFDictionarySetValue(new_user_options
, kSCNetworkConnectionSelectionOptionOnDemandMatchInfo
, match_info
);
4180 connectionPrivate
->on_demand_user_options
= new_user_options
;
4181 CFRetain(connectionPrivate
->on_demand_user_options
);
4183 if (requiredProbe
) {
4184 connectionPrivate
->on_demand_required_probe
= requiredProbe
;
4185 CFRetain(connectionPrivate
->on_demand_required_probe
);
4189 if (service_id
!= NULL
) {
4190 CFRelease(service_id
);
4193 if (configuration
!= NULL
) {
4194 CFRelease(configuration
);
4197 if (found_trigger
!= NULL
) {
4198 CFRelease(found_trigger
);
4201 if (user_options
!= NULL
) {
4202 CFRelease(user_options
);
4205 if (new_user_options
!= NULL
) {
4206 CFRelease(new_user_options
);
4209 if (match_info
!= NULL
) {
4210 CFRelease(match_info
);
4213 if (requiredProbe
!= NULL
) {
4214 CFRelease(requiredProbe
);
4220 //*******************************************************************************************
4221 // SCNetworkConnectionPrivateCopyDefaultServiceIDForDial
4222 // ----------------------------------------------------
4223 // Try to find the service id to connect
4224 // (1) Start by looking at the last service used in Network Pref / Network menu extra
4225 // (2) If Network Pref / Network menu extra has not been used, find the PPP service
4226 // with the highest ordering
4227 //********************************************************************************************
4229 SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(CFStringRef
*serviceID
)
4231 Boolean foundService
= FALSE
;
4232 CFPropertyListRef lastServiceSelectedInIC
= NULL
;
4236 // we found the service the user last had open in IC
4237 if (lastServiceSelectedInIC
!= NULL
) {
4238 // make sure its a PPP service
4239 if (SCNetworkConnectionPrivateIsPPPService(lastServiceSelectedInIC
, kSCValNetInterfaceSubTypePPPSerial
, kSCValNetInterfaceSubTypePPPoE
)) {
4240 // make sure the service that we found is valid
4241 CFDictionaryRef dict
;
4244 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4245 kSCDynamicStoreDomainSetup
,
4246 lastServiceSelectedInIC
,
4247 kSCEntNetInterface
);
4248 dict
= SCDynamicStoreCopyValue(NULL
, key
);
4252 *serviceID
= CFRetain(lastServiceSelectedInIC
);
4253 foundService
= TRUE
;
4256 CFRelease(lastServiceSelectedInIC
);
4259 if (!foundService
) {
4260 foundService
= SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(serviceID
);
4263 return foundService
;
4266 //********************************************************************************
4267 // SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore
4268 // -------------------------------------------------------
4269 // Find the highest ordered PPP service in the dynamic store
4270 //********************************************************************************
4272 SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(CFStringRef
*serviceID
)
4274 CFDictionaryRef dict
= NULL
;
4275 CFStringRef key
= NULL
;
4276 CFArrayRef serviceIDs
= NULL
;
4277 Boolean success
= FALSE
;
4285 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, kSCDynamicStoreDomainSetup
, kSCEntNetIPv4
);
4287 fprintf(stderr
, "Error, Setup Key == NULL!\n");
4291 dict
= SCDynamicStoreCopyValue(NULL
, key
);
4292 if (!isA_CFDictionary(dict
)) {
4293 fprintf(stderr
, "no global IPv4 entity\n");
4297 serviceIDs
= CFDictionaryGetValue(dict
, kSCPropNetServiceOrder
); // array of service id's
4298 if (!isA_CFArray(serviceIDs
)) {
4299 fprintf(stderr
, "service order not specified\n");
4303 count
= CFArrayGetCount(serviceIDs
);
4304 for (i
= 0; i
< count
; i
++) {
4305 CFStringRef service
= CFArrayGetValueAtIndex(serviceIDs
, i
);
4307 if (SCNetworkConnectionPrivateIsPPPService(service
, kSCValNetInterfaceSubTypePPPSerial
, kSCValNetInterfaceSubTypePPPoE
)) {
4308 *serviceID
= CFRetain(service
);
4315 if (key
!= NULL
) CFRelease(key
);
4316 if (dict
!= NULL
) CFRelease(dict
);
4321 //********************************************************************************
4322 // SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray
4323 // ---------------------------------------------------------
4324 // Copy over user preferences for a particular service if they exist
4325 //********************************************************************************
4327 SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray
, CFDictionaryRef
*userOptions
)
4329 CFIndex count
= CFArrayGetCount(userOptionsArray
);
4332 for (i
= 0; i
< count
; i
++) {
4333 // (1) Find the dictionary
4334 CFPropertyListRef propertyList
= CFArrayGetValueAtIndex(userOptionsArray
, i
);
4336 if (isA_CFDictionary(propertyList
) != NULL
) {
4337 // See if there's a value for dial on demand
4338 CFPropertyListRef value
;
4340 value
= CFDictionaryGetValue((CFDictionaryRef
)propertyList
, k_Dial_Default_Key
);
4341 if (isA_CFBoolean(value
) != NULL
) {
4342 if (CFBooleanGetValue(value
)) {
4343 // we found the default user options
4344 *userOptions
= CFDictionaryCreateCopy(NULL
,
4345 (CFDictionaryRef
)propertyList
);
4355 //********************************************************************************
4356 // SCNetworkConnectionPrivateIsServiceType
4357 // --------------------------------------
4358 // Check and see if the service is a PPP service of the given types
4359 //********************************************************************************
4361 SCNetworkConnectionPrivateIsPPPService(CFStringRef serviceID
, CFStringRef subType1
, CFStringRef subType2
)
4363 CFStringRef entityKey
;
4364 Boolean isPPPService
= FALSE
;
4365 Boolean isMatchingSubType
= FALSE
;
4366 CFDictionaryRef serviceDict
;
4368 entityKey
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4369 kSCDynamicStoreDomainSetup
,
4371 kSCEntNetInterface
);
4372 if (entityKey
== NULL
) {
4376 serviceDict
= SCDynamicStoreCopyValue(NULL
, entityKey
);
4377 if (serviceDict
!= NULL
) {
4378 if (isA_CFDictionary(serviceDict
)) {
4380 CFStringRef subtype
;
4382 type
= CFDictionaryGetValue(serviceDict
, kSCPropNetInterfaceType
);
4383 if (isA_CFString(type
)) {
4384 isPPPService
= CFEqual(type
, kSCValNetInterfaceTypePPP
);
4387 subtype
= CFDictionaryGetValue(serviceDict
, kSCPropNetInterfaceSubType
);
4388 if (isA_CFString(subtype
)) {
4389 isMatchingSubType
= CFEqual(subtype
, subType1
);
4390 if (!isMatchingSubType
&& subType2
)
4391 isMatchingSubType
= CFEqual(subtype
, subType2
);
4394 CFRelease(serviceDict
);
4396 CFRelease(entityKey
);
4398 return (isPPPService
&& isMatchingSubType
);
4401 //********************************************************************************
4402 // addPasswordFromKeychain
4403 // --------------------------------------
4404 // Get the password and shared secret out of the keychain and add
4405 // them to the PPP and IPSec dictionaries
4406 //********************************************************************************
4408 addPasswordFromKeychain(CFStringRef serviceID
, CFDictionaryRef
*userOptions
)
4410 CFPropertyListRef uniqueID
;
4411 CFStringRef password
;
4412 CFStringRef sharedsecret
= NULL
;
4414 /* user options must exist */
4415 if (*userOptions
== NULL
)
4418 /* first, get the unique identifier used to store passwords in the keychain */
4419 uniqueID
= CFDictionaryGetValue(*userOptions
, k_Unique_Id_Key
);
4420 if (!isA_CFString(uniqueID
))
4423 /* first, get the PPP password */
4424 password
= copyPasswordFromKeychain(uniqueID
);
4426 /* then, if necessary, get the IPSec Shared Secret */
4427 if (SCNetworkConnectionPrivateIsPPPService(serviceID
, kSCValNetInterfaceSubTypeL2TP
, 0)) {
4428 CFMutableStringRef uniqueIDSS
;
4430 uniqueIDSS
= CFStringCreateMutableCopy(NULL
, 0, uniqueID
);
4431 CFStringAppend(uniqueIDSS
, CFSTR(".SS"));
4432 sharedsecret
= copyPasswordFromKeychain(uniqueIDSS
);
4433 CFRelease(uniqueIDSS
);
4436 /* did we find our information in the key chain ? */
4437 if ((password
!= NULL
) || (sharedsecret
!= NULL
)) {
4438 CFMutableDictionaryRef newOptions
;
4440 newOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, *userOptions
);
4443 if (password
!= NULL
) {
4444 CFDictionaryRef entity
;
4445 CFMutableDictionaryRef newEntity
;
4447 entity
= CFDictionaryGetValue(*userOptions
, kSCEntNetPPP
);
4448 if (isA_CFDictionary(entity
))
4449 newEntity
= CFDictionaryCreateMutableCopy(NULL
, 0, entity
);
4451 newEntity
= CFDictionaryCreateMutable(NULL
,
4453 &kCFTypeDictionaryKeyCallBacks
,
4454 &kCFTypeDictionaryValueCallBacks
);
4457 /* set the PPP password */
4458 CFDictionarySetValue(newEntity
, kSCPropNetPPPAuthPassword
, uniqueID
);
4459 CFDictionarySetValue(newEntity
, kSCPropNetPPPAuthPasswordEncryption
, kSCValNetPPPAuthPasswordEncryptionKeychain
);
4460 CFRelease(password
);
4462 /* update the PPP entity */
4463 CFDictionarySetValue(newOptions
, kSCEntNetPPP
, newEntity
);
4464 CFRelease(newEntity
);
4467 /* IPSec Shared Secret */
4468 if (sharedsecret
!= NULL
) {
4469 CFDictionaryRef entity
;
4470 CFMutableDictionaryRef newEntity
;
4472 entity
= CFDictionaryGetValue(*userOptions
, kSCEntNetIPSec
);
4473 if (isA_CFDictionary(entity
))
4474 newEntity
= CFDictionaryCreateMutableCopy(NULL
, 0, entity
);
4476 newEntity
= CFDictionaryCreateMutable(NULL
,
4478 &kCFTypeDictionaryKeyCallBacks
,
4479 &kCFTypeDictionaryValueCallBacks
);
4481 /* set the IPSec Shared Secret */
4482 CFDictionarySetValue(newEntity
, kSCPropNetIPSecSharedSecret
, sharedsecret
);
4483 CFRelease(sharedsecret
);
4485 /* update the IPSec entity */
4486 CFDictionarySetValue(newOptions
, kSCEntNetIPSec
, newEntity
);
4487 CFRelease(newEntity
);
4490 /* update the userOptions dictionary */
4491 CFRelease(*userOptions
);
4492 *userOptions
= CFDictionaryCreateCopy(NULL
, newOptions
);
4493 CFRelease(newOptions
);
4498 #if !TARGET_OS_IPHONE
4499 //********************************************************************************
4500 // copyKeychainEnumerator
4501 // --------------------------------------
4502 // Gather Keychain Enumerator
4503 //********************************************************************************
4505 copyKeychainEnumerator(CFStringRef uniqueIdentifier
)
4507 CFArrayRef itemArray
= NULL
;
4508 CFMutableDictionaryRef query
;
4511 query
= CFDictionaryCreateMutable(NULL
,
4513 &kCFTypeDictionaryKeyCallBacks
,
4514 &kCFTypeDictionaryValueCallBacks
);
4515 CFDictionarySetValue(query
, kSecClass
, kSecClassGenericPassword
);
4516 CFDictionarySetValue(query
, kSecAttrService
, uniqueIdentifier
);
4517 CFDictionarySetValue(query
, kSecReturnRef
, kCFBooleanTrue
);
4518 CFDictionarySetValue(query
, kSecMatchLimit
, kSecMatchLimitAll
);
4519 result
= SecItemCopyMatching(query
, (CFTypeRef
*)&itemArray
);
4521 if ((result
!= noErr
) && (itemArray
!= NULL
)) {
4522 CFRelease(itemArray
);
4528 #endif // !TARGET_OS_IPHONE
4530 //********************************************************************************
4531 // copyPasswordFromKeychain
4532 // --------------------------------------
4533 // Given a uniqueID, retrieve the password from the keychain
4534 //********************************************************************************
4536 copyPasswordFromKeychain(CFStringRef uniqueID
)
4538 #if !TARGET_OS_IPHONE
4539 CFArrayRef enumerator
;
4541 CFStringRef password
= NULL
;
4543 enumerator
= copyKeychainEnumerator(uniqueID
);
4544 if (enumerator
== NULL
) {
4545 return NULL
; // if no keychain enumerator
4548 n
= CFArrayGetCount(enumerator
);
4552 SecKeychainItemRef itemRef
;
4555 itemRef
= (SecKeychainItemRef
)CFArrayGetValueAtIndex(enumerator
, 0);
4556 result
= SecKeychainItemCopyContent(itemRef
, // itemRef
4560 (void *)&data
); // outData
4561 if ((result
== noErr
) && (data
!= NULL
) && (dataLen
> 0)) {
4562 password
= CFStringCreateWithBytes(NULL
, data
, dataLen
, kCFStringEncodingUTF8
, TRUE
);
4563 (void) SecKeychainItemFreeContent(NULL
, data
);
4568 CFRelease(enumerator
);
4571 #else // !TARGET_OS_IPHONE
4573 #endif // !TARGET_OS_IPHONE
4579 __SCNetworkConnectionGetControllerPortName()
4581 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 70000)) && !TARGET_IPHONE_SIMULATOR
4582 if (scnc_server_name
== NULL
){
4583 if (!(sandbox_check(getpid(), "mach-lookup", SANDBOX_FILTER_GLOBAL_NAME
| SANDBOX_CHECK_NO_REPORT
, PPPCONTROLLER_SERVER_PRIV
))){
4584 scnc_server_name
= PPPCONTROLLER_SERVER_PRIV
;
4587 scnc_server_name
= PPPCONTROLLER_SERVER
;
4589 SCLog(TRUE
, LOG_DEBUG
, CFSTR("__SCNetworkConnectionGetControllerPortName() returns port: %s"), scnc_server_name
);
4592 scnc_server_name
= PPPCONTROLLER_SERVER
;
4594 return scnc_server_name
;