2 * Copyright (c) 2003-2014 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 <CoreFoundation/CFXPCBridge.h>
45 #include <SystemConfiguration/SystemConfiguration.h>
46 #include <SystemConfiguration/SCPrivate.h>
47 #include <SystemConfiguration/SCValidation.h>
48 #include <SystemConfiguration/VPNAppLayerPrivate.h>
49 #include <SystemConfiguration/VPNTunnel.h>
52 #include <Security/Security.h>
53 #include "dy_framework.h"
54 #endif // !TARGET_OS_IPHONE
56 #include <bootstrap.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>
71 #include <sys/proc_info.h>
74 #include <ppp/ppp_msg.h>
75 #include "pppcontroller.h"
76 #include <ppp/pppcontroller_types.h>
78 #ifndef PPPCONTROLLER_SERVER_PRIV
79 #define PPPCONTROLLER_SERVER_PRIV PPPCONTROLLER_SERVER
80 #endif // !PPPCONTROLLER_SERVER_PRIV
83 #include "SCNetworkConnectionInternal.h"
86 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
87 static pthread_mutex_t scnc_lock
= PTHREAD_MUTEX_INITIALIZER
;
88 static mach_port_t scnc_server
= MACH_PORT_NULL
;
89 static char *scnc_server_name
= NULL
;
94 /* base CFType information */
101 SCNetworkServiceRef service
;
103 /* client info (if we are proxying for another process */
104 mach_port_t client_audit_session
;
105 audit_token_t client_audit_token
;
106 mach_port_t client_bootstrap_port
;
111 CFStringRef client_bundle_id
;
113 /* ref to PPP controller for control messages */
114 mach_port_t session_port
;
116 /* ref to PPP controller for notification messages */
117 CFMachPortRef notify_port
;
119 /* keep track of whether we're acquired the initial status */
122 /* run loop source, callout, context, rl scheduling info */
124 CFRunLoopSourceRef rls
;
125 SCNetworkConnectionCallBack rlsFunction
;
126 SCNetworkConnectionContext rlsContext
;
127 CFMutableArrayRef rlList
;
129 /* SCNetworkConnectionSetDispatchQueue */
130 dispatch_group_t dispatchGroup
;
131 dispatch_queue_t dispatchQueue
;
132 dispatch_source_t dispatchSource
;
134 SCNetworkConnectionType type
;
136 CFDictionaryRef on_demand_info
;
137 CFDictionaryRef on_demand_user_options
;
138 CFStringRef on_demand_required_probe
;
140 /* Flow Divert support info */
141 CFDictionaryRef flow_divert_token_params
;
143 #if !TARGET_IPHONE_SIMULATOR
144 /* NetworkExtension data structures */
145 ne_session_t ne_session
;
146 #endif /* !TARGET_IPHONE_SIMULATOR */
147 } SCNetworkConnectionPrivate
, *SCNetworkConnectionPrivateRef
;
150 static __inline__ CFTypeRef
151 isA_SCNetworkConnection(CFTypeRef obj
)
153 return (isA_CFType(obj
, SCNetworkConnectionGetTypeID()));
157 #if !TARGET_IPHONE_SIMULATOR
159 __SCNetworkConnectionUseNetworkExtension(SCNetworkConnectionPrivateRef connectionPrivate
)
161 Boolean result
= FALSE
;
163 if (ne_session_use_as_system_vpn() && connectionPrivate
->service
!= NULL
) {
164 _SCErrorSet(kSCStatusOK
);
165 result
= _SCNetworkServiceIsVPN(connectionPrivate
->service
);
167 * SCNetworkServiceGetInterface (called by _SCNetworkServiceIsVPN) will set the SC error to kSCStatusInvalidArgument if the service does not have an associated prefs object.
168 * In that case, we try to get the service type/subtype from the dynamic store.
170 if (!result
&& SCError() == kSCStatusInvalidArgument
) {
171 CFStringRef interfaceKey
= SCDynamicStoreKeyCreateNetworkServiceEntity(kCFAllocatorDefault
,
172 kSCDynamicStoreDomainSetup
,
173 SCNetworkServiceGetServiceID(connectionPrivate
->service
),
175 CFDictionaryRef interfaceDict
= SCDynamicStoreCopyValue(NULL
, interfaceKey
);
176 if (isA_CFDictionary(interfaceDict
)) {
177 CFStringRef interfaceType
= CFDictionaryGetValue(interfaceDict
, kSCPropNetInterfaceType
);
178 if (isA_CFString(interfaceType
)) {
179 if (CFEqual(interfaceType
, kSCNetworkInterfaceTypePPP
)) {
180 CFStringRef interfaceSubType
= CFDictionaryGetValue(interfaceDict
, kSCPropNetInterfaceSubType
);
181 result
= (isA_CFString(interfaceSubType
) && (CFEqual(interfaceSubType
, kSCValNetInterfaceSubTypePPTP
) || CFEqual(interfaceSubType
, kSCValNetInterfaceSubTypeL2TP
)));
183 result
= (CFEqual(interfaceType
, kSCNetworkInterfaceTypeVPN
) || CFEqual(interfaceType
, kSCNetworkInterfaceTypeIPSec
));
187 if (interfaceDict
!= NULL
) {
188 CFRelease(interfaceDict
);
190 CFRelease(interfaceKey
);
196 #endif /* !TARGET_IPHONE_SIMULATOR */
200 __SCNetworkConnectionUsingNetworkExtension(SCNetworkConnectionPrivateRef connectionPrivate
)
202 #if !TARGET_IPHONE_SIMULATOR
203 return (connectionPrivate
->ne_session
!= NULL
);
206 #endif /* !TARGET_IPHONE_SIMULATOR */
211 __SCNetworkConnectionCopyDescription(CFTypeRef cf
)
213 CFAllocatorRef allocator
= CFGetAllocator(cf
);
214 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)cf
;
215 CFMutableStringRef result
;
217 result
= CFStringCreateMutable(allocator
, 0);
218 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkConnection, %p [%p]> {"), cf
, allocator
);
219 CFStringAppendFormat(result
, NULL
, CFSTR("service = %p"), connectionPrivate
->service
);
220 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
221 CFStringAppendFormat(result
, NULL
, CFSTR(", server port = 0x%x"), connectionPrivate
->session_port
);
223 CFStringAppendFormat(result
, NULL
, CFSTR("using NetworkExtension = %s"), (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
) ? "yes" : "no"));
224 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
231 __SCNetworkConnectionDeallocate(CFTypeRef cf
)
233 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)cf
;
235 /* release resources */
236 pthread_mutex_destroy(&connectionPrivate
->lock
);
238 if (connectionPrivate
->client_audit_session
!= MACH_PORT_NULL
) {
239 mach_port_mod_refs(mach_task_self(),
240 connectionPrivate
->client_audit_session
,
241 MACH_PORT_RIGHT_SEND
,
245 if (connectionPrivate
->client_bootstrap_port
!= MACH_PORT_NULL
) {
246 mach_port_mod_refs(mach_task_self(),
247 connectionPrivate
->client_bootstrap_port
,
248 MACH_PORT_RIGHT_SEND
,
252 if (connectionPrivate
->client_bundle_id
!= NULL
) {
253 CFRelease(connectionPrivate
->client_bundle_id
);
256 if (connectionPrivate
->rls
!= NULL
) {
257 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
258 CFRelease(connectionPrivate
->rls
);
261 if (connectionPrivate
->rlList
!= NULL
) {
262 CFRelease(connectionPrivate
->rlList
);
265 if (connectionPrivate
->notify_port
!= NULL
) {
266 mach_port_t mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
268 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionDeallocate notify_port", mp
);
269 CFMachPortInvalidate(connectionPrivate
->notify_port
);
270 CFRelease(connectionPrivate
->notify_port
);
271 mach_port_mod_refs(mach_task_self(), mp
, MACH_PORT_RIGHT_RECEIVE
, -1);
274 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
275 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionDeallocate session_port", connectionPrivate
->session_port
);
276 (void) mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
279 if (connectionPrivate
->rlsContext
.release
!= NULL
)
280 (*connectionPrivate
->rlsContext
.release
)(connectionPrivate
->rlsContext
.info
);
282 if (connectionPrivate
->service
!= NULL
) {
283 CFRelease(connectionPrivate
->service
);
286 if (connectionPrivate
->on_demand_info
!= NULL
) {
287 CFRelease(connectionPrivate
->on_demand_info
);
290 if (connectionPrivate
->on_demand_user_options
!= NULL
) {
291 CFRelease(connectionPrivate
->on_demand_user_options
);
294 if (connectionPrivate
->on_demand_required_probe
!= NULL
) {
295 CFRelease(connectionPrivate
->on_demand_required_probe
);
298 if (connectionPrivate
->flow_divert_token_params
!= NULL
) {
299 CFRelease(connectionPrivate
->flow_divert_token_params
);
302 #if !TARGET_IPHONE_SIMULATOR
303 if (connectionPrivate
->ne_session
!= NULL
) {
304 ne_session_set_event_handler(connectionPrivate
->ne_session
, NULL
, NULL
);
305 ne_session_release(connectionPrivate
->ne_session
);
307 #endif /* !TARGET_IPHONE_SIMULATOR */
313 static CFTypeID __kSCNetworkConnectionTypeID
= _kCFRuntimeNotATypeID
;
315 static const CFRuntimeClass __SCNetworkConnectionClass
= {
317 "SCNetworkConnection", // className
320 __SCNetworkConnectionDeallocate
, // dealloc
323 NULL
, // copyFormattingDesc
324 __SCNetworkConnectionCopyDescription
// copyDebugDesc
331 /* the process has forked (and we are the child process) */
333 scnc_server
= MACH_PORT_NULL
;
334 scnc_server_name
= NULL
;
340 __SCNetworkConnectionInitialize(void)
344 /* get the debug environment variable */
345 env
= getenv("PPPDebug");
347 if (sscanf(env
, "%d", &debug
) != 1) {
348 /* PPPDebug value is not valid (or non-numeric), set debug to 1 */
353 /* register with CoreFoundation */
354 __kSCNetworkConnectionTypeID
= _CFRuntimeRegisterClass(&__SCNetworkConnectionClass
);
356 /* add handler to cleanup after fork() */
357 (void) pthread_atfork(NULL
, NULL
, childForkHandler
);
364 __SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection
);
366 #define SC_NETWORK_CONNECTION_QUEUE "SCNetworkConnectionQueue"
368 static dispatch_queue_t
369 __SCNetworkConnectionQueue()
371 static dispatch_once_t once
;
372 static dispatch_queue_t q
;
374 dispatch_once(&once
, ^{
375 q
= dispatch_queue_create(SC_NETWORK_CONNECTION_QUEUE
, NULL
);
383 __SCNetworkConnectionCallBackRunLoopPerform(SCNetworkConnectionRef connection
,
386 SCNetworkConnectionCallBack rlsFunction
,
387 void (*context_release
)(const void *),
390 SCNetworkConnectionStatus nc_status
= kSCNetworkConnectionInvalid
;
392 nc_status
= SCNetworkConnectionGetStatus(connection
);
393 CFRunLoopPerformBlock(rl
, rl_mode
,
395 (*rlsFunction
)(connection
, nc_status
, context_info
);
396 if ((context_release
!= NULL
) && (context_info
!= NULL
)) {
397 (*context_release
)(context_info
);
401 CFRelease(connection
);
408 __SCNetworkConnectionCallBackDispatchPerform(SCNetworkConnectionRef connection
,
410 SCNetworkConnectionCallBack rlsFunction
,
411 void (*context_release
)(const void *),
414 SCNetworkConnectionStatus nc_status
= kSCNetworkConnectionInvalid
;
416 nc_status
= SCNetworkConnectionGetStatus(connection
);
419 (*rlsFunction
)(connection
, nc_status
, context_info
);
420 if ((context_release
!= NULL
) && (context_info
!= NULL
)) {
421 (*context_release
)(context_info
);
424 CFRelease(connection
);
431 __SCNetworkConnectionCallBack(void *connection
)
433 boolean_t exec_async
= FALSE
;
434 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
436 void (*context_release
)(const void *);
437 CFRunLoopRef rl
= NULL
;
439 SCNetworkConnectionCallBack rlsFunction
= NULL
;
440 dispatch_queue_t q
= NULL
;
441 SCNetworkConnectionStatus nc_status
= kSCNetworkConnectionInvalid
;
443 pthread_mutex_lock(&connectionPrivate
->lock
);
445 if (!connectionPrivate
->scheduled
) {
446 // if not currently scheduled
447 pthread_mutex_unlock(&connectionPrivate
->lock
);
451 rlsFunction
= connectionPrivate
->rlsFunction
;
452 if (rlsFunction
== NULL
) {
453 pthread_mutex_unlock(&connectionPrivate
->lock
);
457 if ((connectionPrivate
->rlsContext
.retain
!= NULL
) && (connectionPrivate
->rlsContext
.info
!= NULL
)) {
458 context_info
= (void *)(*connectionPrivate
->rlsContext
.retain
)(connectionPrivate
->rlsContext
.info
);
459 context_release
= connectionPrivate
->rlsContext
.release
;
461 context_info
= connectionPrivate
->rlsContext
.info
;
462 context_release
= NULL
;
465 #if !TARGET_IPHONE_SIMULATOR
466 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
467 pthread_mutex_unlock(&connectionPrivate
->lock
);
469 nc_status
= SCNetworkConnectionGetStatus(connection
);
470 (*rlsFunction
)(connection
, nc_status
, context_info
);
471 if ((context_release
!= NULL
) && (context_info
!= NULL
)) {
472 (*context_release
)(context_info
);
474 CFRelease(connection
); /* This releases the reference that we took in the NESessionEventStatusChanged event handler */
477 #endif /* !TARGET_IPHONE_SIMULATOR */
479 // Do we need to spin a new thread? (either we are running on the main
480 // dispatch queue or main runloop)
481 if (connectionPrivate
->rlList
== NULL
) {
482 // if we are performing the callback on a dispatch queue
483 q
= connectionPrivate
->dispatchQueue
;
484 if (q
== dispatch_get_main_queue()) {
488 rl
= CFRunLoopGetCurrent();
489 if (rl
== CFRunLoopGetMain()) {
494 CFRetain(connection
);
495 pthread_mutex_unlock(&connectionPrivate
->lock
);
498 nc_status
= SCNetworkConnectionGetStatus(connection
);
499 (*rlsFunction
)(connection
, nc_status
, context_info
);
500 if ((context_release
!= NULL
) && (context_info
!= NULL
)) {
501 (*context_release
)(context_info
);
503 CFRelease(connection
);
507 if (connectionPrivate
->rlList
== NULL
) {
510 dispatch_async(__SCNetworkConnectionQueue(), ^{
511 __SCNetworkConnectionCallBackDispatchPerform(connection
,
520 rl_mode
= CFRunLoopCopyCurrentMode(rl
);
521 dispatch_async(__SCNetworkConnectionQueue(), ^{
522 __SCNetworkConnectionCallBackRunLoopPerform(connection
,
536 __SCNetworkConnectionMachCallBack(CFMachPortRef port
, void * msg
, CFIndex size
, void * info
)
538 mach_no_senders_notification_t
*buf
= msg
;
539 mach_msg_id_t msgid
= buf
->not_header
.msgh_id
;
540 SCNetworkConnectionRef connection
= (SCNetworkConnectionRef
)info
;
542 if (msgid
== MACH_NOTIFY_NO_SENDERS
) {
543 // re-establish notification
544 SCLog(_sc_verbose
, LOG_DEBUG
, CFSTR("__SCNetworkConnectionMachCallBack: PPPController server died"));
545 (void)__SCNetworkConnectionReconnectNotifications(connection
);
548 __SCNetworkConnectionCallBack(info
);
553 #pragma mark SCNetworkConnection APIs
557 pppMPCopyDescription(const void *info
)
559 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)info
;
561 return CFStringCreateWithFormat(NULL
,
563 CFSTR("<SCNetworkConnection MP %p> {service = %@, callout = %p}"),
565 connectionPrivate
->service
,
566 connectionPrivate
->rlsFunction
);
570 static SCNetworkConnectionPrivateRef
571 __SCNetworkConnectionCreatePrivate(CFAllocatorRef allocator
,
572 SCNetworkServiceRef service
,
573 SCNetworkConnectionCallBack callout
,
574 SCNetworkConnectionContext
*context
)
576 SCNetworkConnectionPrivateRef connectionPrivate
= NULL
;
580 /* initialize runtime */
581 pthread_once(&initialized
, __SCNetworkConnectionInitialize
);
583 /* allocate NetworkConnection */
584 size
= sizeof(SCNetworkConnectionPrivate
) - sizeof(CFRuntimeBase
);
585 connectionPrivate
= (SCNetworkConnectionPrivateRef
)_CFRuntimeCreateInstance(allocator
, __kSCNetworkConnectionTypeID
, size
, NULL
);
586 if (connectionPrivate
== NULL
) {
590 /* zero the data structure */
591 bzero(((u_char
*)connectionPrivate
)+sizeof(CFRuntimeBase
), size
);
593 pthread_mutex_init(&connectionPrivate
->lock
, NULL
);
595 /* save the service */
596 if (service
!= NULL
) {
597 connectionPrivate
->service
= CFRetain(service
);
600 connectionPrivate
->client_audit_session
= MACH_PORT_NULL
;
601 connectionPrivate
->client_bootstrap_port
= MACH_PORT_NULL
;
602 connectionPrivate
->client_uid
= geteuid();
603 connectionPrivate
->client_gid
= getegid();
604 connectionPrivate
->client_pid
= getpid();
605 connectionPrivate
->client_bundle_id
= NULL
;
606 uuid_clear(connectionPrivate
->client_uuid
);
608 connectionPrivate
->rlsFunction
= callout
;
611 bcopy(context
, &connectionPrivate
->rlsContext
, sizeof(SCNetworkConnectionContext
));
612 if (context
->retain
!= NULL
) {
613 connectionPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
617 connectionPrivate
->type
= kSCNetworkConnectionTypeUnknown
;
619 #if !TARGET_IPHONE_SIMULATOR
620 if (__SCNetworkConnectionUseNetworkExtension(connectionPrivate
)) {
621 CFStringRef serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
622 if (serviceID
!= NULL
) {
623 uuid_string_t service_uuid_str
;
624 if (CFStringGetCString(serviceID
, service_uuid_str
, sizeof(service_uuid_str
), kCFStringEncodingUTF8
)) {
626 if (uuid_parse(service_uuid_str
, config_id
) == 0) {
627 connectionPrivate
->ne_session
= ne_session_create(config_id
, NESessionTypeVPN
);
632 if (connectionPrivate
->ne_session
== NULL
) {
633 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkConnection failed to create an ne_session: service ID %@ is not a valid UUID"), serviceID
);
637 #endif /* !TARGET_IPHONE_SIMULATOR */
639 /* success, return the connection reference */
640 return connectionPrivate
;
644 /* failure, clean up and leave */
645 if (connectionPrivate
!= NULL
) {
646 CFRelease(connectionPrivate
);
649 _SCErrorSet(kSCStatusFailed
);
655 __SCNetworkConnectionServerPort(kern_return_t
*status
)
657 mach_port_t server
= MACH_PORT_NULL
;
659 #ifdef BOOTSTRAP_PRIVILEGED_SERVER
660 *status
= bootstrap_look_up2(bootstrap_port
,
661 __SCNetworkConnectionGetControllerPortName(),
664 BOOTSTRAP_PRIVILEGED_SERVER
);
665 #else // BOOTSTRAP_PRIVILEGED_SERVER
666 *status
= bootstrap_look_up(bootstrap_port
, __SCNetworkConnectionGetControllerPortName(), &server
);
667 #endif // BOOTSTRAP_PRIVILEGED_SERVER
670 case BOOTSTRAP_SUCCESS
:
671 // service currently registered, "a good thing" (tm)
673 case BOOTSTRAP_NOT_PRIVILEGED
:
674 // the service is not privileged
676 case BOOTSTRAP_UNKNOWN_SERVICE
:
677 // service not currently registered, try again later
681 SCLog(_sc_verbose
, LOG_DEBUG
,
682 CFSTR("SCNetworkConnection bootstrap_look_up() failed: status=%s"),
683 bootstrap_strerror(*status
));
688 scnc_server_name
= NULL
; /* reset pppcontroller server */
689 return MACH_PORT_NULL
;
693 __SCNetworkConnectionGetCurrentServerPort(void)
699 __SCNetworkConnectionRefreshServerPort(mach_port_t current_server
, int *mach_result
)
701 mach_port_t new_server
;
703 pthread_mutex_lock(&scnc_lock
);
704 if (scnc_server
!= MACH_PORT_NULL
) {
705 if (current_server
== scnc_server
) {
706 scnc_server_name
= NULL
;
707 // if the server we tried returned the error
708 (void)mach_port_deallocate(mach_task_self(), scnc_server
);
709 scnc_server
= __SCNetworkConnectionServerPort(mach_result
);
711 // another thread has refreshed the server port
714 scnc_server
= __SCNetworkConnectionServerPort(mach_result
);
716 new_server
= scnc_server
;
717 pthread_mutex_unlock(&scnc_lock
);
722 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && (!TARGET_IPHONE_SIMULATOR || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 70000))
723 #define HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
727 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && !TARGET_IPHONE_SIMULATOR
728 #define HAVE_PPPCONTROLLER_ATTACHWITHPROXY
732 __SCNetworkConnectionSessionPort(SCNetworkConnectionPrivateRef connectionPrivate
)
736 CFDataRef dataRef
= NULL
;
737 mach_port_t notify_port
= MACH_PORT_NULL
;
738 mach_port_t oldNotify
= MACH_PORT_NULL
;
740 int sc_status
= kSCStatusFailed
;
741 mach_port_t server
= __SCNetworkConnectionGetCurrentServerPort();
742 kern_return_t status
= KERN_SUCCESS
;
744 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
745 mach_port_t au_session
= MACH_PORT_NULL
;
746 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
748 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
749 return connectionPrivate
->session_port
;
752 if (connectionPrivate
->service
== NULL
) {
753 sc_status
= kSCStatusConnectionNoService
;
757 if (!_SCSerializeString(SCNetworkServiceGetServiceID(connectionPrivate
->service
), &dataRef
, &data
, &dataLen
)) {
761 if (connectionPrivate
->notify_port
!= NULL
) {
762 mach_port_t mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
764 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionSessionPort mp", mp
);
765 CFMachPortInvalidate(connectionPrivate
->notify_port
);
766 CFRelease(connectionPrivate
->notify_port
);
767 connectionPrivate
->notify_port
= NULL
;
768 mach_port_mod_refs(mach_task_self(), mp
, MACH_PORT_RIGHT_RECEIVE
, -1);
771 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
772 au_session
= audit_session_self();
773 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
775 // open a new session with the server
777 if ((connectionPrivate
->rlsFunction
!= NULL
) && (notify_port
== MACH_PORT_NULL
)) {
778 // allocate port (for server response)
779 status
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE
, ¬ify_port
);
780 if (status
!= KERN_SUCCESS
) {
781 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkConnectionSessionPort mach_port_allocate(): %s"), mach_error_string(status
));
786 // add send right (passed to the server)
787 status
= mach_port_insert_right(mach_task_self(),
790 MACH_MSG_TYPE_MAKE_SEND
);
791 if (status
!= KERN_SUCCESS
) {
792 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkConnectionSessionPort mach_port_insert_right(): %s"), mach_error_string(status
));
793 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
799 if (server
!= MACH_PORT_NULL
) {
800 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHPROXY
801 if ((connectionPrivate
->client_audit_session
== MACH_PORT_NULL
) &&
802 (connectionPrivate
->client_bootstrap_port
== MACH_PORT_NULL
) &&
803 (connectionPrivate
->client_uid
== geteuid()) &&
804 (connectionPrivate
->client_gid
== getegid()) &&
805 (connectionPrivate
->client_pid
== getpid())
807 #endif // HAVE_PPPCONTROLLER_ATTACHWITHPROXY
808 status
= pppcontroller_attach(server
,
810 (mach_msg_type_number_t
)dataLen
,
813 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
815 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
816 &connectionPrivate
->session_port
,
818 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHPROXY
820 mach_port_t client_au_session
;
821 mach_port_t client_bootstrap_port
;
823 if (connectionPrivate
->client_audit_session
== MACH_PORT_NULL
) {
824 client_au_session
= au_session
;
826 client_au_session
= connectionPrivate
->client_audit_session
;
829 if (connectionPrivate
->client_bootstrap_port
== MACH_PORT_NULL
) {
830 client_bootstrap_port
= bootstrap_port
;
832 client_bootstrap_port
= connectionPrivate
->client_bootstrap_port
;
835 status
= pppcontroller_attach_proxy(server
,
837 (mach_msg_type_number_t
)dataLen
,
838 client_bootstrap_port
,
841 connectionPrivate
->client_uid
,
842 connectionPrivate
->client_gid
,
843 connectionPrivate
->client_pid
,
844 &connectionPrivate
->session_port
,
847 #endif // HAVE_PPPCONTROLLER_ATTACHWITHPROXY
848 if (status
== KERN_SUCCESS
) {
849 if (sc_status
!= kSCStatusOK
) {
850 SCLog(TRUE
, LOG_DEBUG
,
851 CFSTR("__SCNetworkConnectionSessionPort : attach w/error, sc_status=%s%s"),
852 SCErrorString(sc_status
),
853 (connectionPrivate
->session_port
!= MACH_PORT_NULL
) ? ", w/session_port!=MACH_PORT_NULL" : "");
855 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
856 __MACH_PORT_DEBUG(TRUE
,
857 "*** __SCNetworkConnectionSessionPort session_port (attach w/error, cleanup)",
858 connectionPrivate
->session_port
);
859 mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
860 connectionPrivate
->session_port
= MACH_PORT_NULL
;
863 if (notify_port
!= MACH_PORT_NULL
) {
864 __MACH_PORT_DEBUG(TRUE
,
865 "*** __SCNetworkConnectionSessionPort notify_port (attach w/error, cleanup)",
867 (void) mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
868 notify_port
= MACH_PORT_NULL
;
874 // our [cached] server port is not valid
875 SCLog(TRUE
, LOG_DEBUG
, CFSTR("__SCNetworkConnectionSessionPort : !attach: %s"), SCErrorString(status
));
876 if (status
== MACH_SEND_INVALID_DEST
) {
877 // the server is not yet available
878 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionSessionPort notify_port (!dest)", notify_port
);
879 } else if (status
== MIG_SERVER_DIED
) {
880 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionSessionPort notify_port (!mig)", notify_port
);
881 // the server we were using is gone and we've lost our send right
882 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
883 notify_port
= MACH_PORT_NULL
;
885 // if we got an unexpected error, don't retry
891 server
= __SCNetworkConnectionRefreshServerPort(server
, &sc_status
);
892 if (server
== MACH_PORT_NULL
) {
893 // if server not available
894 if (sc_status
== BOOTSTRAP_UNKNOWN_SERVICE
) {
895 // if first retry attempt, wait for SCDynamicStore server
897 SCDynamicStoreRef store
;
899 store
= SCDynamicStoreCreate(NULL
,
900 CFSTR("SCNetworkConnection connect"),
908 // wait up to 2.5 seconds for the [SCNetworkConnection] server
910 if ((retry
+= 50) < 2500) {
911 usleep(50 * 1000); // sleep 50ms between attempts
919 if (notify_port
!= MACH_PORT_NULL
) {
920 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
921 CFMachPortContext context
= { 0
922 , (void *)connectionPrivate
925 , pppMPCopyDescription
928 // request a notification when/if the server dies
929 status
= mach_port_request_notification(mach_task_self(),
931 MACH_NOTIFY_NO_SENDERS
,
934 MACH_MSG_TYPE_MAKE_SEND_ONCE
,
936 if (status
!= KERN_SUCCESS
) {
937 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkConnectionSessionPort mach_port_request_notification(): %s"), mach_error_string(status
));
938 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
943 if (oldNotify
!= MACH_PORT_NULL
) {
944 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkConnectionSessionPort(): oldNotify != MACH_PORT_NULL"));
947 // create CFMachPort for SCNetworkConnection notification callback
948 connectionPrivate
->notify_port
= _SC_CFMachPortCreateWithPort("SCNetworkConnection",
950 __SCNetworkConnectionMachCallBack
,
953 // we need to try a bit harder to acquire the initial status
954 connectionPrivate
->haveStatus
= FALSE
;
956 // with no server port, release the notification port we allocated
957 __MACH_PORT_DEBUG(TRUE
,
958 "*** __SCNetworkConnectionSessionPort notify_port (!server)",
960 (void) mach_port_mod_refs (mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
961 (void) mach_port_deallocate(mach_task_self(), notify_port
);
962 notify_port
= MACH_PORT_NULL
;
970 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
971 if (au_session
!= MACH_PORT_NULL
) {
972 (void)mach_port_deallocate(mach_task_self(), au_session
);
974 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
976 if (dataRef
!= NULL
) CFRelease(dataRef
);
980 __MACH_PORT_DEBUG(connectionPrivate
->session_port
!= MACH_PORT_NULL
,
981 "*** __SCNetworkConnectionSessionPort session_port",
982 connectionPrivate
->session_port
);
983 __MACH_PORT_DEBUG(notify_port
!= MACH_PORT_NULL
,
984 "*** __SCNetworkConnectionSessionPort notify_port",
987 case BOOTSTRAP_UNKNOWN_SERVICE
:
989 (status
== KERN_SUCCESS
) ? LOG_DEBUG
: LOG_ERR
,
990 CFSTR("PPPController not available"));
994 (status
== KERN_SUCCESS
) ? LOG_DEBUG
: LOG_ERR
,
995 CFSTR("__SCNetworkConnectionSessionPort pppcontroller_attach(): %s"),
996 SCErrorString(sc_status
));
1000 if (sc_status
!= kSCStatusOK
) {
1001 _SCErrorSet(sc_status
);
1004 return connectionPrivate
->session_port
;
1009 __SCNetworkConnectionReconnect(SCNetworkConnectionRef connection
)
1011 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1014 port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1015 return (port
!= MACH_PORT_NULL
);
1020 __SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection
)
1022 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1023 dispatch_group_t dispatchGroup
= NULL
;
1024 dispatch_queue_t dispatchQueue
= NULL
;
1026 CFArrayRef rlList
= NULL
;
1028 // Before we fully tearing down our [old] notifications, make sure
1029 // we have retained any information that is needed to re-register the
1030 // [new] notifications.
1032 pthread_mutex_lock(&connectionPrivate
->lock
);
1034 // save and cancel [old] notifications
1035 if (connectionPrivate
->rlList
!= NULL
) {
1036 rlList
= connectionPrivate
->rlList
;
1037 connectionPrivate
->rlList
= NULL
;
1039 if (connectionPrivate
->rls
!= NULL
) {
1040 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
1041 CFRelease(connectionPrivate
->rls
);
1042 connectionPrivate
->rls
= NULL
;
1044 if (connectionPrivate
->dispatchSource
!= NULL
) {
1045 dispatch_source_cancel(connectionPrivate
->dispatchSource
);
1046 connectionPrivate
->dispatchSource
= NULL
;
1049 // make sure dispatchSource is cancelled before removing group/queue
1050 if (connectionPrivate
->dispatchQueue
!= NULL
) {
1051 // save dispatchQueue, release reference when we've queue'd blocks
1052 // complete, allow re-scheduling
1053 dispatchGroup
= connectionPrivate
->dispatchGroup
;
1054 connectionPrivate
->dispatchGroup
= NULL
;
1055 dispatchQueue
= connectionPrivate
->dispatchQueue
;
1056 connectionPrivate
->dispatchQueue
= NULL
;
1058 // and take an extra reference for rescheduling
1059 dispatch_retain(dispatchQueue
);
1062 connectionPrivate
->scheduled
= FALSE
;
1064 pthread_mutex_unlock(&connectionPrivate
->lock
);
1066 if (dispatchGroup
!= NULL
) {
1067 dispatch_group_notify(dispatchGroup
, dispatchQueue
, ^{
1068 // release group/queue references
1069 dispatch_release(dispatchQueue
);
1070 dispatch_release(dispatchGroup
); // releases our connection reference
1075 if (rlList
!= NULL
) {
1079 n
= CFArrayGetCount(rlList
);
1080 for (i
= 0; i
< n
; i
+= 3) {
1081 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(rlList
, i
+1);
1082 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(rlList
, i
+2);
1084 ok
= SCNetworkConnectionScheduleWithRunLoop(connection
, rl
, rlMode
);
1086 SCLog((SCError() != BOOTSTRAP_UNKNOWN_SERVICE
),
1088 CFSTR("__SCNetworkConnectionReconnectNotifications: SCNetworkConnectionScheduleWithRunLoop() failed"));
1092 } else if (dispatchQueue
!= NULL
) {
1093 ok
= SCNetworkConnectionSetDispatchQueue(connection
, dispatchQueue
);
1095 SCLog((SCError() != BOOTSTRAP_UNKNOWN_SERVICE
),
1097 CFSTR("__SCNetworkConnectionReconnectNotifications: SCNetworkConnectionSetDispatchQueue() failed"));
1107 if (rlList
!= NULL
) {
1110 if (dispatchQueue
!= NULL
) {
1111 dispatch_release(dispatchQueue
);
1115 SCLog(TRUE
, LOG_ERR
,
1116 CFSTR("SCNetworkConnection server %s, notification not restored"),
1117 (SCError() == BOOTSTRAP_UNKNOWN_SERVICE
) ? "shutdown" : "failed");
1125 __SCNetworkConnectionNeedsRetry(SCNetworkConnectionRef connection
,
1126 const char *error_label
,
1127 kern_return_t status
,
1130 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1132 if (status
== KERN_SUCCESS
) {
1136 if ((status
== MACH_SEND_INVALID_DEST
) || (status
== MIG_SERVER_DIED
)) {
1137 // the server's gone and our session port's dead, remove the dead name right
1138 (void) mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
1140 // we got an unexpected error, leave the [session] port alone
1141 SCLog(TRUE
, LOG_ERR
, CFSTR("%s: %s"), error_label
, mach_error_string(status
));
1143 connectionPrivate
->session_port
= MACH_PORT_NULL
;
1144 if ((status
== MACH_SEND_INVALID_DEST
) || (status
== MIG_SERVER_DIED
)) {
1145 if (__SCNetworkConnectionReconnect(connection
)) {
1149 *sc_status
= status
;
1156 SCNetworkConnectionGetTypeID(void) {
1157 pthread_once(&initialized
, __SCNetworkConnectionInitialize
); /* initialize runtime */
1158 return __kSCNetworkConnectionTypeID
;
1162 CFArrayRef
/* of SCNetworkServiceRef's */
1163 SCNetworkConnectionCopyAvailableServices(SCNetworkSetRef set
)
1165 CFMutableArrayRef available
;
1166 Boolean tempSet
= FALSE
;
1169 SCPreferencesRef prefs
;
1171 prefs
= SCPreferencesCreate(NULL
, CFSTR("SCNetworkConnectionCopyAvailableServices"), NULL
);
1172 if (prefs
!= NULL
) {
1173 set
= SCNetworkSetCopyCurrent(prefs
);
1179 available
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1182 CFArrayRef services
;
1184 services
= SCNetworkSetCopyServices(set
);
1185 if (services
!= NULL
) {
1189 n
= CFArrayGetCount(services
);
1190 for (i
= 0; i
< n
; i
++) {
1191 SCNetworkInterfaceRef interface
;
1192 CFStringRef interfaceType
;
1193 SCNetworkServiceRef service
;
1195 service
= CFArrayGetValueAtIndex(services
, i
);
1196 interface
= SCNetworkServiceGetInterface(service
);
1197 if (interface
== NULL
) {
1201 interfaceType
= SCNetworkInterfaceGetInterfaceType(interface
);
1202 if (CFEqual(interfaceType
, kSCNetworkInterfaceTypePPP
) ||
1203 CFEqual(interfaceType
, kSCNetworkInterfaceTypeVPN
) ||
1204 CFEqual(interfaceType
, kSCNetworkInterfaceTypeIPSec
)) {
1205 CFArrayAppendValue(available
, service
);
1209 CFRelease(services
);
1213 if (tempSet
&& (set
!= NULL
)) {
1220 SCNetworkConnectionRef
1221 SCNetworkConnectionCreateWithService(CFAllocatorRef allocator
,
1222 SCNetworkServiceRef service
,
1223 SCNetworkConnectionCallBack callout
,
1224 SCNetworkConnectionContext
*context
)
1226 SCNetworkConnectionPrivateRef connectionPrivate
;
1228 if (!isA_SCNetworkService(service
)) {
1229 _SCErrorSet(kSCStatusInvalidArgument
);
1233 connectionPrivate
= __SCNetworkConnectionCreatePrivate(allocator
, service
, callout
, context
);
1234 return (SCNetworkConnectionRef
)connectionPrivate
;
1238 SCNetworkConnectionRef
1239 SCNetworkConnectionCreateWithServiceID(CFAllocatorRef allocator
,
1240 CFStringRef serviceID
,
1241 SCNetworkConnectionCallBack callout
,
1242 SCNetworkConnectionContext
*context
)
1244 SCNetworkConnectionRef connection
;
1245 SCNetworkServiceRef service
;
1247 if (!isA_CFString(serviceID
)) {
1248 _SCErrorSet(kSCStatusInvalidArgument
);
1252 service
= _SCNetworkServiceCopyActive(NULL
, serviceID
);
1253 if (service
== NULL
) {
1257 connection
= SCNetworkConnectionCreateWithService(allocator
, service
, callout
, context
);
1264 SCNetworkConnectionRef
1265 SCNetworkConnectionCreate(CFAllocatorRef allocator
,
1266 SCNetworkConnectionCallBack callout
,
1267 SCNetworkConnectionContext
*context
)
1269 SCNetworkConnectionPrivateRef connectionPrivate
= __SCNetworkConnectionCreatePrivate(allocator
, NULL
, callout
, context
);
1270 return (SCNetworkConnectionRef
)connectionPrivate
;
1275 SCNetworkConnectionCopyServiceID(SCNetworkConnectionRef connection
)
1277 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1278 CFStringRef serviceID
;
1280 if (!isA_SCNetworkConnection(connection
)) {
1281 _SCErrorSet(kSCStatusInvalidArgument
);
1285 if (connectionPrivate
->service
== NULL
) {
1286 _SCErrorSet(kSCStatusConnectionNoService
);
1290 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
1291 return CFRetain(serviceID
);
1296 SCNetworkConnectionSetClientInfo(SCNetworkConnectionRef connection
,
1297 mach_port_t client_audit_session
,
1302 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1304 if (!isA_SCNetworkConnection(connection
)) {
1305 _SCErrorSet(kSCStatusInvalidArgument
);
1309 // save client audit session port
1310 if (connectionPrivate
->client_audit_session
!= MACH_PORT_NULL
) {
1311 mach_port_mod_refs(mach_task_self(),
1312 connectionPrivate
->client_audit_session
,
1313 MACH_PORT_RIGHT_SEND
,
1315 connectionPrivate
->client_audit_session
= MACH_PORT_NULL
;
1317 connectionPrivate
->client_audit_session
= client_audit_session
;
1318 if (connectionPrivate
->client_audit_session
!= MACH_PORT_NULL
) {
1319 mach_port_mod_refs(mach_task_self(),
1320 connectionPrivate
->client_audit_session
,
1321 MACH_PORT_RIGHT_SEND
,
1325 // save client UID, GID, and PID
1326 connectionPrivate
->client_uid
= client_uid
;
1327 connectionPrivate
->client_gid
= client_gid
;
1328 connectionPrivate
->client_pid
= client_pid
;
1335 SCNetworkConnectionSetClientAuditInfo(SCNetworkConnectionRef connection
,
1336 audit_token_t client_audit_token
,
1337 mach_port_t audit_session
,
1338 mach_port_t bootstrap_port
,
1341 const char *bundle_id
)
1343 const audit_token_t null_audit
= KERNEL_AUDIT_TOKEN_VALUE
;
1344 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1349 if (memcmp(&client_audit_token
, &null_audit
, sizeof(client_audit_token
))) {
1350 #if TARGET_OS_IPHONE
1351 audit_token_to_au32(client_audit_token
, NULL
, &uid
, &gid
, NULL
, NULL
, &pid
, NULL
, NULL
);
1352 #else // TARGET_OS_IPHONE
1353 uid
= audit_token_to_euid(client_audit_token
);
1354 gid
= audit_token_to_egid(client_audit_token
);
1355 pid
= audit_token_to_pid(client_audit_token
);
1356 #endif // TARGET_OS_IPHONE
1361 if (!SCNetworkConnectionSetClientInfo(connection
, audit_session
, uid
, gid
, pid
)) {
1365 if (connectionPrivate
->client_bootstrap_port
!= MACH_PORT_NULL
) {
1366 mach_port_mod_refs(mach_task_self(),
1367 connectionPrivate
->client_bootstrap_port
,
1368 MACH_PORT_RIGHT_SEND
,
1370 connectionPrivate
->client_bootstrap_port
= MACH_PORT_NULL
;
1373 connectionPrivate
->client_bootstrap_port
= bootstrap_port
;
1374 if (connectionPrivate
->client_bootstrap_port
!= MACH_PORT_NULL
) {
1375 mach_port_mod_refs(mach_task_self(),
1376 connectionPrivate
->client_bootstrap_port
,
1377 MACH_PORT_RIGHT_SEND
,
1381 memcpy(&connectionPrivate
->client_audit_token
, &client_audit_token
, sizeof(connectionPrivate
->client_audit_token
));
1383 if (uuid
!= NULL
&& !uuid_is_null(uuid
)) {
1384 uuid_copy(connectionPrivate
->client_uuid
, uuid
);
1387 if (connectionPrivate
->client_bundle_id
!= NULL
) {
1388 CFRelease(connectionPrivate
->client_bundle_id
);
1389 connectionPrivate
->client_bundle_id
= NULL
;
1392 if (bundle_id
!= NULL
) {
1393 connectionPrivate
->client_bundle_id
= CFStringCreateWithCString(kCFAllocatorDefault
, bundle_id
, kCFStringEncodingUTF8
);
1401 SCNetworkConnectionCopyStatistics(SCNetworkConnectionRef connection
)
1403 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1404 xmlDataOut_t data
= NULL
;
1405 mach_msg_type_number_t datalen
= 0;
1406 int sc_status
= kSCStatusFailed
;
1407 mach_port_t session_port
;
1408 CFPropertyListRef statistics
= NULL
;
1409 kern_return_t status
;
1411 if (!isA_SCNetworkConnection(connection
)) {
1412 _SCErrorSet(kSCStatusInvalidArgument
);
1416 pthread_mutex_lock(&connectionPrivate
->lock
);
1418 #if !TARGET_IPHONE_SIMULATOR
1419 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1420 __block xpc_object_t xstats
= NULL
;
1421 ne_session_t ne_session
= connectionPrivate
->ne_session
;
1423 ne_session_retain(ne_session
);
1424 pthread_mutex_unlock(&connectionPrivate
->lock
);
1426 dispatch_semaphore_t ne_sema
= dispatch_semaphore_create(0);
1427 ne_session_get_info(ne_session
, NESessionInfoTypeStatistics
, __SCNetworkConnectionQueue(), ^(xpc_object_t result
) {
1428 if (result
!= NULL
) {
1429 xstats
= xpc_retain(result
);
1431 ne_session_release(ne_session
);
1432 dispatch_semaphore_signal(ne_sema
);
1434 dispatch_semaphore_wait(ne_sema
, DISPATCH_TIME_FOREVER
);
1435 dispatch_release(ne_sema
);
1437 if (xstats
!= NULL
) {
1438 statistics
= _CFXPCCreateCFObjectFromXPCObject(xstats
);
1439 xpc_release(xstats
);
1441 _SCErrorSet(kSCStatusFailed
);
1446 #endif /* !TARGET_IPHONE_SIMULATOR */
1450 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1451 if (session_port
== MACH_PORT_NULL
) {
1455 status
= pppcontroller_copystatistics(session_port
, &data
, &datalen
, &sc_status
);
1456 if (__SCNetworkConnectionNeedsRetry(connection
,
1457 "SCNetworkConnectionCopyStatistics()",
1464 if (!_SCUnserialize(&statistics
, NULL
, data
, datalen
)) {
1465 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
1467 if ((sc_status
== kSCStatusOK
) && !isA_CFDictionary(statistics
)) {
1468 sc_status
= kSCStatusFailed
;
1472 if (sc_status
!= kSCStatusOK
) {
1473 if (statistics
!= NULL
) {
1474 CFRelease(statistics
);
1477 _SCErrorSet(sc_status
);
1482 pthread_mutex_unlock(&connectionPrivate
->lock
);
1488 SCNetworkConnectionGetService(SCNetworkConnectionRef connection
)
1490 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1492 if (!isA_SCNetworkConnection(connection
)) {
1493 _SCErrorSet(kSCStatusInvalidArgument
);
1497 return connectionPrivate
->service
;
1501 SCNetworkConnectionStatus
1502 SCNetworkConnectionGetStatus(SCNetworkConnectionRef connection
)
1504 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1505 SCNetworkConnectionStatus nc_status
= kSCNetworkConnectionInvalid
;
1507 int sc_status
= kSCStatusFailed
;
1508 mach_port_t session_port
;
1509 kern_return_t status
;
1510 CFStringRef serviceID
;
1512 if (!isA_SCNetworkConnection(connection
)) {
1513 _SCErrorSet(kSCStatusInvalidArgument
);
1514 return kSCNetworkConnectionInvalid
;
1517 if (connectionPrivate
->service
== NULL
) {
1518 _SCErrorSet(kSCStatusConnectionNoService
);
1519 return kSCNetworkConnectionInvalid
;
1522 // skip retry and return immediately if we know no service is to be found.
1523 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
1524 if (CFStringGetLength(serviceID
) == 0) {
1525 _SCErrorSet(kSCStatusConnectionNoService
);
1526 return kSCNetworkConnectionInvalid
;
1529 pthread_mutex_lock(&connectionPrivate
->lock
);
1531 #if !TARGET_IPHONE_SIMULATOR
1532 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1533 __block ne_session_status_t ne_status
;
1534 ne_session_t ne_session
= connectionPrivate
->ne_session
;
1536 ne_session_retain(ne_session
);
1537 pthread_mutex_unlock(&connectionPrivate
->lock
);
1539 dispatch_semaphore_t ne_sema
= dispatch_semaphore_create(0);
1540 ne_session_get_status(ne_session
, __SCNetworkConnectionQueue(), ^(ne_session_status_t status
) {
1542 ne_session_release(ne_session
);
1543 dispatch_semaphore_signal(ne_sema
);
1545 dispatch_semaphore_wait(ne_sema
, DISPATCH_TIME_FOREVER
);
1546 dispatch_release(ne_sema
);
1548 return SCNetworkConnectionGetStatusFromNEStatus(ne_status
);
1550 #endif /* !TARGET_IPHONE_SIMULATOR */
1554 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1555 if (session_port
== MACH_PORT_NULL
) {
1556 nc_status
= kSCNetworkConnectionInvalid
;
1560 status
= pppcontroller_getstatus(session_port
, &nc_status
, &sc_status
);
1561 if (__SCNetworkConnectionNeedsRetry(connection
,
1562 "SCNetworkConnectionGetStatus()",
1568 // wait up to 250 ms for the network service to become available
1569 if (!connectionPrivate
->haveStatus
&&
1570 (sc_status
== kSCStatusConnectionNoService
) &&
1571 ((retry
+= 10) < 250)) {
1572 usleep(10 * 1000); // sleep 10ms between attempts
1576 if (sc_status
== kSCStatusOK
) {
1577 connectionPrivate
->haveStatus
= TRUE
;
1579 _SCErrorSet(sc_status
);
1580 nc_status
= kSCNetworkConnectionInvalid
;
1585 pthread_mutex_unlock(&connectionPrivate
->lock
);
1591 SCNetworkConnectionCopyExtendedStatus(SCNetworkConnectionRef connection
)
1593 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1594 xmlDataOut_t data
= NULL
;
1595 mach_msg_type_number_t datalen
= 0;
1596 CFPropertyListRef extstatus
= NULL
;
1598 int sc_status
= kSCStatusFailed
;
1599 mach_port_t session_port
;
1600 kern_return_t status
;
1601 CFStringRef serviceID
;
1603 if (!isA_SCNetworkConnection(connection
)) {
1604 _SCErrorSet(kSCStatusInvalidArgument
);
1608 if (connectionPrivate
->service
== NULL
) {
1609 _SCErrorSet(kSCStatusConnectionNoService
);
1613 // skip retry and return immediately if we know no service is to be found.
1614 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
1615 if (CFStringGetLength(serviceID
) == 0) {
1616 _SCErrorSet(kSCStatusConnectionNoService
);
1620 pthread_mutex_lock(&connectionPrivate
->lock
);
1622 #if !TARGET_IPHONE_SIMULATOR
1623 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1624 __block CFDictionaryRef statusDictionary
= NULL
;
1625 ne_session_t ne_session
= connectionPrivate
->ne_session
;
1627 ne_session_retain(ne_session
);
1628 pthread_mutex_unlock(&connectionPrivate
->lock
);
1630 dispatch_semaphore_t ne_sema
= dispatch_semaphore_create(0);
1631 ne_session_get_info(ne_session
, NESessionInfoTypeExtendedStatus
, __SCNetworkConnectionQueue(), ^(xpc_object_t extended_status
) {
1632 if (extended_status
!= NULL
) {
1633 statusDictionary
= _CFXPCCreateCFObjectFromXPCObject(extended_status
);
1634 ne_session_release(ne_session
);
1635 dispatch_semaphore_signal(ne_sema
);
1637 ne_session_get_status(ne_session
, __SCNetworkConnectionQueue(), ^(ne_session_status_t ne_status
) {
1638 SCNetworkConnectionStatus status
= SCNetworkConnectionGetStatusFromNEStatus(ne_status
);
1639 if (status
!= kSCNetworkConnectionInvalid
) {
1640 CFStringRef keys
[1] = { kSCNetworkConnectionStatus
};
1641 CFNumberRef values
[1] = { NULL
};
1642 values
[0] = CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &status
);
1643 statusDictionary
= CFDictionaryCreate(kCFAllocatorDefault
, (const void **)keys
, (const void **)values
, sizeof(values
) / sizeof(values
[0]), &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1644 CFRelease(values
[0]);
1646 ne_session_release(ne_session
);
1647 dispatch_semaphore_signal(ne_sema
);
1651 dispatch_semaphore_wait(ne_sema
, DISPATCH_TIME_FOREVER
);
1652 dispatch_release(ne_sema
);
1654 if (statusDictionary
!= NULL
) {
1655 extstatus
= (CFPropertyListRef
)statusDictionary
;
1657 _SCErrorSet(kSCStatusFailed
);
1666 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1667 if (session_port
== MACH_PORT_NULL
) {
1671 status
= pppcontroller_copyextendedstatus(session_port
, &data
, &datalen
, &sc_status
);
1672 if (__SCNetworkConnectionNeedsRetry(connection
,
1673 "SCNetworkConnectionCopyExtendedStatus()",
1680 if (!_SCUnserialize(&extstatus
, NULL
, data
, datalen
)) {
1681 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
1683 if ((sc_status
== kSCStatusOK
) && !isA_CFDictionary(extstatus
)) {
1684 sc_status
= kSCStatusFailed
;
1688 // wait up to 250 ms for the network service to become available
1689 if (!connectionPrivate
->haveStatus
&&
1690 (sc_status
== kSCStatusConnectionNoService
) &&
1691 ((retry
+= 10) < 250)) {
1692 usleep(10 * 1000); // sleep 10ms between attempts
1696 if (sc_status
== kSCStatusOK
) {
1697 connectionPrivate
->haveStatus
= TRUE
;
1699 if (extstatus
!= NULL
) {
1700 CFRelease(extstatus
);
1703 _SCErrorSet(sc_status
);
1708 pthread_mutex_unlock(&connectionPrivate
->lock
);
1714 _SCNetworkConnectionMergeDictionaries (const void *key
, const void *value
, void *context
)
1716 /* Add value only if not present */
1717 CFDictionaryAddValue((CFMutableDictionaryRef
)context
, (CFStringRef
)key
, (CFTypeRef
)value
);
1722 SCNetworkConnectionStart(SCNetworkConnectionRef connection
,
1723 CFDictionaryRef userOptions
,
1726 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1727 CFDataRef dataref
= NULL
;
1729 CFIndex datalen
= 0;
1731 int sc_status
= kSCStatusFailed
;
1732 mach_port_t session_port
;
1733 kern_return_t status
;
1735 if (!isA_SCNetworkConnection(connection
)) {
1736 _SCErrorSet(kSCStatusInvalidArgument
);
1740 if ((userOptions
!= NULL
) && !isA_CFDictionary(userOptions
)) {
1741 _SCErrorSet(kSCStatusInvalidArgument
);
1745 if (userOptions
== NULL
) {
1746 userOptions
= connectionPrivate
->on_demand_user_options
;
1747 } else if (connectionPrivate
->on_demand_user_options
!= NULL
) {
1748 CFDictionaryRef localUserOptions
= NULL
;
1750 localUserOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, userOptions
);
1751 if (localUserOptions
) {
1752 CFDictionaryApplyFunction(connectionPrivate
->on_demand_user_options
,
1753 _SCNetworkConnectionMergeDictionaries
,
1754 (void *)localUserOptions
);
1755 CFRelease(connectionPrivate
->on_demand_user_options
);
1756 userOptions
= connectionPrivate
->on_demand_user_options
= localUserOptions
;
1761 CFMutableDictionaryRef mdict
= NULL
;
1763 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionStart (%p)"), connectionPrivate
);
1765 if (userOptions
!= NULL
) {
1766 CFDictionaryRef dict
;
1767 CFStringRef encryption
;
1768 CFMutableDictionaryRef new_dict
;
1770 /* special code to remove secret information */
1771 mdict
= CFDictionaryCreateMutableCopy(NULL
, 0, userOptions
);
1773 dict
= CFDictionaryGetValue(mdict
, kSCEntNetPPP
);
1774 if (isA_CFDictionary(dict
)) {
1775 encryption
= CFDictionaryGetValue(dict
, kSCPropNetPPPAuthPasswordEncryption
);
1776 if (!isA_CFString(encryption
) ||
1777 !CFEqual(encryption
, kSCValNetPPPAuthPasswordEncryptionKeychain
)) {
1778 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1779 CFDictionaryReplaceValue(new_dict
, kSCPropNetPPPAuthPassword
, CFSTR("******"));
1780 CFDictionarySetValue(mdict
, kSCEntNetPPP
, new_dict
);
1781 CFRelease(new_dict
);
1785 dict
= CFDictionaryGetValue(mdict
, kSCEntNetL2TP
);
1786 if (isA_CFDictionary(dict
)) {
1787 encryption
= CFDictionaryGetValue(dict
, kSCPropNetL2TPIPSecSharedSecretEncryption
);
1788 if (!isA_CFString(encryption
) ||
1789 !CFEqual(encryption
, kSCValNetL2TPIPSecSharedSecretEncryptionKeychain
)) {
1790 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1791 CFDictionaryReplaceValue(new_dict
, kSCPropNetL2TPIPSecSharedSecret
, CFSTR("******"));
1792 CFDictionarySetValue(mdict
, kSCEntNetL2TP
, new_dict
);
1793 CFRelease(new_dict
);
1797 dict
= CFDictionaryGetValue(mdict
, kSCEntNetIPSec
);
1798 if (isA_CFDictionary(dict
)) {
1799 encryption
= CFDictionaryGetValue(dict
, kSCPropNetIPSecSharedSecretEncryption
);
1800 if (!isA_CFString(encryption
) ||
1801 !CFEqual(encryption
, kSCValNetIPSecSharedSecretEncryptionKeychain
)) {
1802 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1803 CFDictionaryReplaceValue(new_dict
, kSCPropNetIPSecSharedSecret
, CFSTR("******"));
1804 CFDictionarySetValue(mdict
, kSCEntNetIPSec
, new_dict
);
1805 CFRelease(new_dict
);
1810 SCLog(TRUE
, LOG_DEBUG
, CFSTR("User options: %@"), mdict
);
1811 if (mdict
!= NULL
) CFRelease(mdict
);
1814 pthread_mutex_lock(&connectionPrivate
->lock
);
1816 /* Clear out any cached flow divert token parameters */
1817 if (connectionPrivate
->flow_divert_token_params
!= NULL
) {
1818 CFRelease(connectionPrivate
->flow_divert_token_params
);
1819 connectionPrivate
->flow_divert_token_params
= NULL
;
1822 #if !TARGET_IPHONE_SIMULATOR
1823 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1824 xpc_object_t xuser_options
= NULL
;
1826 if (userOptions
!= NULL
) {
1827 xuser_options
= _CFXPCCreateXPCObjectFromCFObject(userOptions
);
1830 if (connectionPrivate
->client_bootstrap_port
!= MACH_PORT_NULL
) {
1831 #if NE_SESSION_VERSION > 2
1832 ne_session_start_on_behalf_of(connectionPrivate
->ne_session
,
1834 connectionPrivate
->client_bootstrap_port
,
1835 connectionPrivate
->client_audit_session
,
1836 connectionPrivate
->client_uid
,
1837 connectionPrivate
->client_gid
,
1838 connectionPrivate
->client_pid
);
1840 ne_session_start_on_behalf_of(connectionPrivate
->ne_session
,
1842 connectionPrivate
->client_bootstrap_port
,
1843 connectionPrivate
->client_audit_session
,
1844 connectionPrivate
->client_uid
,
1845 connectionPrivate
->client_gid
);
1848 ne_session_start_with_options(connectionPrivate
->ne_session
, xuser_options
);
1851 if (xuser_options
!= NULL
) {
1852 xpc_release(xuser_options
);
1858 #endif /* !TARGET_IPHONE_SIMULATOR */
1860 if (userOptions
&& !_SCSerialize(userOptions
, &dataref
, &data
, &datalen
)) {
1866 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1867 if (session_port
== MACH_PORT_NULL
) {
1868 if (dataref
) CFRelease(dataref
);
1872 status
= pppcontroller_start(session_port
,
1874 (mach_msg_type_number_t
)datalen
,
1877 if (__SCNetworkConnectionNeedsRetry(connection
,
1878 "SCNetworkConnectionStart()",
1884 if (dataref
) CFRelease(dataref
);
1887 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionStart (%p), return: %d"), connectionPrivate
, sc_status
);
1890 if (sc_status
!= kSCStatusOK
) {
1891 _SCErrorSet(sc_status
);
1895 /* connection is now started */
1899 pthread_mutex_unlock(&connectionPrivate
->lock
);
1905 SCNetworkConnectionStop(SCNetworkConnectionRef connection
,
1906 Boolean forceDisconnect
)
1908 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1910 int sc_status
= kSCStatusFailed
;
1911 mach_port_t session_port
;
1912 kern_return_t status
;
1914 if (!isA_SCNetworkConnection(connection
)) {
1915 _SCErrorSet(kSCStatusInvalidArgument
);
1920 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionStop (%p)"), connectionPrivate
);
1923 pthread_mutex_lock(&connectionPrivate
->lock
);
1925 #if !TARGET_IPHONE_SIMULATOR
1926 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1927 ne_session_stop(connectionPrivate
->ne_session
);
1931 #endif /* !TARGET_IPHONE_SIMULATOR */
1935 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1936 if (session_port
== MACH_PORT_NULL
) {
1940 status
= pppcontroller_stop(session_port
, forceDisconnect
, &sc_status
);
1941 if (__SCNetworkConnectionNeedsRetry(connection
,
1942 "SCNetworkConnectionStop()",
1949 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionStop (%p), return: %d"), connectionPrivate
, sc_status
);
1952 if (sc_status
!= kSCStatusOK
) {
1953 _SCErrorSet(sc_status
);
1957 /* connection is now disconnecting */
1962 pthread_mutex_unlock(&connectionPrivate
->lock
);
1968 SCNetworkConnectionSuspend(SCNetworkConnectionRef connection
)
1970 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1972 int sc_status
= kSCStatusFailed
;
1973 mach_port_t session_port
;
1974 kern_return_t status
;
1976 if (!isA_SCNetworkConnection(connection
)) {
1977 _SCErrorSet(kSCStatusInvalidArgument
);
1982 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionSuspend (%p)"), connectionPrivate
);
1985 pthread_mutex_lock(&connectionPrivate
->lock
);
1987 #if !!TARGET_IPHONE_SIMULATOR
1988 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1989 /* Suspend only applies to PPPSerial and PPPoE */
1993 #endif /* !TARGET_IPHONE_SIMULATOR */
1997 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1998 if (session_port
== MACH_PORT_NULL
) {
2002 status
= pppcontroller_suspend(session_port
, &sc_status
);
2003 if (__SCNetworkConnectionNeedsRetry(connection
,
2004 "SCNetworkConnectionSuspend()",
2011 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionSuspend (%p), return: %d"), connectionPrivate
, sc_status
);
2014 if (sc_status
!= kSCStatusOK
) {
2015 _SCErrorSet(sc_status
);
2019 /* connection is now suspended */
2024 pthread_mutex_unlock(&connectionPrivate
->lock
);
2030 SCNetworkConnectionResume(SCNetworkConnectionRef connection
)
2032 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2034 int sc_status
= kSCStatusFailed
;
2035 mach_port_t session_port
;
2036 kern_return_t status
;
2038 if (!isA_SCNetworkConnection(connection
)) {
2039 _SCErrorSet(kSCStatusInvalidArgument
);
2044 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionResume (%p)"), connectionPrivate
);
2047 pthread_mutex_lock(&connectionPrivate
->lock
);
2049 #if !TARGET_IPHONE_SIMULATOR
2050 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2051 /* Resume only applies to PPPSerial and PPPoE */
2055 #endif /* !TARGET_IPHONE_SIMULATOR */
2059 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2060 if (session_port
== MACH_PORT_NULL
) {
2064 status
= pppcontroller_resume(session_port
, &sc_status
);
2065 if (__SCNetworkConnectionNeedsRetry(connection
,
2066 "SCNetworkConnectionResume()",
2073 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionResume (%p), return: %d"), connectionPrivate
, sc_status
);
2076 if (sc_status
!= kSCStatusOK
) {
2077 _SCErrorSet(sc_status
);
2081 /* connection is now resume */
2086 pthread_mutex_unlock(&connectionPrivate
->lock
);
2091 #if !TARGET_IPHONE_SIMULATOR
2093 SCNetworkConnectionRefreshOnDemandState(SCNetworkConnectionRef connection
)
2095 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2098 int sc_status
= kSCStatusFailed
;
2099 mach_port_t server_port
= __SCNetworkConnectionGetCurrentServerPort();
2100 kern_return_t status
= KERN_SUCCESS
;
2102 if (!isA_SCNetworkConnection(connection
)) {
2103 _SCErrorSet(kSCStatusInvalidArgument
);
2108 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionRefreshOnDemandState (%p)"), connectionPrivate
);
2111 pthread_mutex_lock(&connectionPrivate
->lock
);
2114 if (server_port
== MACH_PORT_NULL
) {
2115 server_port
= __SCNetworkConnectionRefreshServerPort(server_port
, &sc_status
);
2116 if (server_port
== MACH_PORT_NULL
) {
2117 // if server not available
2118 if (sc_status
== BOOTSTRAP_UNKNOWN_SERVICE
) {
2119 // wait up to 2.5 seconds for the [SCNetworkConnection] server
2121 if ((retry
+= 50) < 2500) {
2122 usleep(50 * 1000); // sleep 50ms between attempts
2130 status
= pppcontroller_ondemand_refresh_state(server_port
, &sc_status
);
2131 if (status
== KERN_SUCCESS
)
2134 if (status
== MACH_SEND_INVALID_DEST
) {
2135 // the server is not yet available
2136 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkConnectionRefreshOnDemandState (!dest) (%p)"), connectionPrivate
);
2137 } else if (status
== MIG_SERVER_DIED
) {
2138 // the server we were using is gone
2139 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkConnectionRefreshOnDemandState (!mig) (%p)"), connectionPrivate
);
2141 // if we got an unexpected error, don't retry
2148 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionRefreshOnDemandState (%p), return: %d/%d"), connectionPrivate
, status
, sc_status
);
2151 if (sc_status
!= kSCStatusOK
) {
2152 _SCErrorSet(sc_status
);
2160 pthread_mutex_unlock(&connectionPrivate
->lock
);
2163 #endif /* !TARGET_IPHONE_SIMULATOR */
2167 SCNetworkConnectionCopyUserOptions(SCNetworkConnectionRef connection
)
2169 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2170 xmlDataOut_t data
= NULL
;
2171 mach_msg_type_number_t datalen
= 0;
2172 int sc_status
= kSCStatusFailed
;
2173 mach_port_t session_port
;
2174 kern_return_t status
;
2175 CFPropertyListRef userOptions
= NULL
;
2177 if (!isA_SCNetworkConnection(connection
)) {
2178 _SCErrorSet(kSCStatusInvalidArgument
);
2182 pthread_mutex_lock(&connectionPrivate
->lock
);
2184 #if !TARGET_IPHONE_SIMULATOR
2185 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2186 __block xpc_object_t config
= NULL
;
2187 ne_session_t ne_session
= connectionPrivate
->ne_session
;
2189 ne_session_retain(ne_session
);
2190 pthread_mutex_unlock(&connectionPrivate
->lock
);
2192 dispatch_semaphore_t ne_sema
= dispatch_semaphore_create(0);
2193 ne_session_get_info(ne_session
, NESessionInfoTypeConfiguration
, __SCNetworkConnectionQueue(), ^(xpc_object_t result
) {
2194 if (result
!= NULL
) {
2195 config
= xpc_retain(result
);
2197 ne_session_release(ne_session
);
2198 dispatch_semaphore_signal(ne_sema
);
2200 dispatch_semaphore_wait(ne_sema
, DISPATCH_TIME_FOREVER
);
2201 dispatch_release(ne_sema
);
2203 if (config
!= NULL
) {
2204 xpc_object_t xoptions
= xpc_dictionary_get_value(config
, NESMSessionLegacyUserConfigurationKey
);
2205 if (xoptions
!= NULL
) {
2206 userOptions
= _CFXPCCreateCFObjectFromXPCObject(xoptions
);
2208 xpc_release(config
);
2212 #endif /* !TARGET_IPHONE_SIMULATOR */
2216 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2217 if (session_port
== MACH_PORT_NULL
) {
2221 status
= pppcontroller_copyuseroptions(session_port
, &data
, &datalen
, &sc_status
);
2222 if (__SCNetworkConnectionNeedsRetry(connection
,
2223 "SCNetworkConnectionCopyUserOptions()",
2230 if (!_SCUnserialize(&userOptions
, NULL
, data
, datalen
)) {
2231 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
2233 if ((sc_status
== kSCStatusOK
) && (userOptions
!= NULL
) && !isA_CFDictionary(userOptions
)) {
2234 sc_status
= kSCStatusFailed
;
2238 if (sc_status
== kSCStatusOK
) {
2239 if (userOptions
== NULL
) {
2240 // if no user options, return an empty dictionary
2241 userOptions
= CFDictionaryCreate(NULL
,
2245 &kCFTypeDictionaryKeyCallBacks
,
2246 &kCFTypeDictionaryValueCallBacks
);
2250 CFRelease(userOptions
);
2253 _SCErrorSet(sc_status
);
2258 pthread_mutex_unlock(&connectionPrivate
->lock
);
2264 __SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection
,
2265 CFRunLoopRef runLoop
,
2266 CFStringRef runLoopMode
,
2267 dispatch_queue_t queue
)
2269 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2271 int sc_status
= kSCStatusFailed
;
2272 mach_port_t session_port
;
2273 kern_return_t status
;
2275 pthread_mutex_lock(&connectionPrivate
->lock
);
2277 if (connectionPrivate
->rlsFunction
== NULL
) {
2278 _SCErrorSet(kSCStatusInvalidArgument
);
2282 if ((connectionPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled on a dispatch queue
2283 ((queue
!= NULL
) && connectionPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
2284 _SCErrorSet(kSCStatusInvalidArgument
);
2288 if (!connectionPrivate
->scheduled
) {
2292 if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2293 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2294 if (session_port
== MACH_PORT_NULL
) {
2298 status
= pppcontroller_notification(session_port
, 1, &sc_status
);
2299 if (__SCNetworkConnectionNeedsRetry(connection
,
2300 "__SCNetworkConnectionScheduleWithRunLoop()",
2306 if (sc_status
!= kSCStatusOK
) {
2307 _SCErrorSet(sc_status
);
2311 if (runLoop
!= NULL
) {
2312 connectionPrivate
->rls
= CFMachPortCreateRunLoopSource(NULL
, connectionPrivate
->notify_port
, 0);
2313 connectionPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2315 } else if (runLoop
!= NULL
) {
2316 CFRunLoopSourceContext rlsContext
= {
2318 (void *)connection
, // info
2321 NULL
, // copy description
2326 __SCNetworkConnectionCallBack
, // perform
2329 connectionPrivate
->rls
= CFRunLoopSourceCreate(kCFAllocatorDefault
, 0, &rlsContext
);
2330 connectionPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2333 connectionPrivate
->scheduled
= TRUE
;
2336 if (queue
!= NULL
) {
2337 // retain the dispatch queue
2338 connectionPrivate
->dispatchQueue
= queue
;
2339 dispatch_retain(connectionPrivate
->dispatchQueue
);
2341 if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2342 dispatch_group_t group
= NULL
;
2344 dispatch_source_t source
;
2347 // We've taken a reference to the caller's dispatch_queue and we
2348 // want to hold on to that reference until we've processed any/all
2349 // notifications. To facilitate this we create a group, dispatch
2350 // any notification blocks via that group, and when the caller
2351 // has told us to stop the notifications (unschedule) we wait for
2352 // the group to empty and use the group's finalizer to release
2353 // our reference to the SCNetworkConnection.
2355 group
= dispatch_group_create();
2356 connectionPrivate
->dispatchGroup
= group
;
2357 CFRetain(connection
);
2358 dispatch_set_context(connectionPrivate
->dispatchGroup
, (void *)connection
);
2359 dispatch_set_finalizer_f(connectionPrivate
->dispatchGroup
, (dispatch_function_t
)CFRelease
);
2361 mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
2362 source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
, mp
, 0, queue
);
2363 if (source
== NULL
) {
2364 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkConnection dispatch_source_create() failed"));
2365 _SCErrorSet(kSCStatusFailed
);
2369 // have our dispatch source hold a reference to the notification CFMachPort
2370 CFRetain(connectionPrivate
->notify_port
);
2371 dispatch_set_context(source
, (void *)connectionPrivate
->notify_port
);
2372 dispatch_set_finalizer_f(source
, (dispatch_function_t
)CFRelease
);
2374 dispatch_source_set_event_handler(source
, ^{
2377 u_int8_t buf
[sizeof(mach_msg_empty_t
) + MAX_TRAILER_SIZE
];
2378 mach_msg_empty_rcv_t msg
;
2379 mach_no_senders_notification_t no_senders
;
2380 } *notify_message_t
;
2381 CFMachPortRef notify_port
;
2382 notify_message_t notify_msg
;
2384 notify_msg
= (notify_message_t
)malloc(sizeof(*notify_msg
));
2386 kr
= mach_msg(¬ify_msg
->msg
.header
, // msg
2387 MACH_RCV_MSG
, // options
2389 sizeof(*notify_msg
), // rcv_size
2391 MACH_MSG_TIMEOUT_NONE
, // timeout
2392 MACH_PORT_NULL
); // notify
2393 if (kr
!= KERN_SUCCESS
) {
2394 SCLog(TRUE
, LOG_ERR
,
2395 CFSTR("SCDynamicStore notification handler, kr=0x%x"),
2400 CFRetain(connection
);
2401 notify_port
= dispatch_get_context(source
);
2403 dispatch_group_async(group
, queue
, ^{
2404 __SCNetworkConnectionMachCallBack(notify_port
,
2406 sizeof(*notify_msg
),
2407 (void *)connection
);
2409 CFRelease(connection
);
2413 dispatch_source_set_cancel_handler(source
, ^{
2414 dispatch_release(source
);
2417 connectionPrivate
->dispatchSource
= source
;
2418 dispatch_resume(source
);
2421 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, connectionPrivate
->rlList
)) {
2423 * if we do not already have notifications scheduled with
2424 * this runLoop / runLoopMode
2426 CFRunLoopAddSource(runLoop
, connectionPrivate
->rls
, runLoopMode
);
2429 _SC_schedule(connection
, runLoop
, runLoopMode
, connectionPrivate
->rlList
);
2432 #if !TARGET_IPHONE_SIMULATOR
2433 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2434 CFRetain(connection
);
2435 ne_session_set_event_handler(connectionPrivate
->ne_session
, __SCNetworkConnectionQueue(), ^(ne_session_event_t event
, void *event_data
) {
2436 #pragma unused(event_data)
2437 if (event
== NESessionEventStatusChanged
) {
2438 CFRetain(connection
); /* Released in __SCNetworkConnectionCallBack */
2439 pthread_mutex_lock(&connectionPrivate
->lock
);
2440 if (connectionPrivate
->rls
!= NULL
) {
2441 CFRunLoopSourceSignal(connectionPrivate
->rls
);
2442 _SC_signalRunLoop(connection
, connectionPrivate
->rls
, connectionPrivate
->rlList
);
2443 } else if (connectionPrivate
->dispatchQueue
!= NULL
) {
2444 dispatch_async(connectionPrivate
->dispatchQueue
, ^{ __SCNetworkConnectionCallBack((void *)connection
); });
2446 pthread_mutex_unlock(&connectionPrivate
->lock
);
2447 } else if (event
== NESessionEventCanceled
) {
2448 CFRelease(connection
);
2452 #endif /* !TARGET_IPHONE_SIMULATOR */
2458 pthread_mutex_unlock(&connectionPrivate
->lock
);
2464 __SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection
,
2465 CFRunLoopRef runLoop
,
2466 CFStringRef runLoopMode
,
2467 dispatch_queue_t queue
)
2469 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2470 dispatch_group_t drainGroup
= NULL
;
2471 dispatch_queue_t drainQueue
= NULL
;
2472 int sc_status
= kSCStatusFailed
;
2475 kern_return_t status
;
2477 // hold a reference while we unschedule
2478 CFRetain(connection
);
2480 pthread_mutex_lock(&connectionPrivate
->lock
);
2482 if ((runLoop
!= NULL
) && !connectionPrivate
->scheduled
) { // if we should be scheduled (but are not)
2483 _SCErrorSet(kSCStatusInvalidArgument
);
2487 if (((runLoop
== NULL
) && (connectionPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
2488 ((runLoop
!= NULL
) && (connectionPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
2489 _SCErrorSet(kSCStatusInvalidArgument
);
2493 if (connectionPrivate
->dispatchQueue
!= NULL
) {
2494 if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2495 // cancel dispatchSource
2496 if (connectionPrivate
->dispatchSource
!= NULL
) {
2497 dispatch_source_cancel(connectionPrivate
->dispatchSource
);
2498 connectionPrivate
->dispatchSource
= NULL
;
2501 // save dispatchQueue/group, release reference when all queue'd blocks
2502 // have been processed, allow re-scheduling
2503 drainGroup
= connectionPrivate
->dispatchGroup
;
2504 connectionPrivate
->dispatchGroup
= NULL
;
2505 drainQueue
= connectionPrivate
->dispatchQueue
;
2506 connectionPrivate
->dispatchQueue
= NULL
;
2508 dispatch_release(connectionPrivate
->dispatchQueue
);
2509 connectionPrivate
->dispatchQueue
= NULL
;
2512 if (!_SC_unschedule(connection
, runLoop
, runLoopMode
, connectionPrivate
->rlList
, FALSE
)) {
2513 // if not currently scheduled on this runLoop / runLoopMode
2514 _SCErrorSet(kSCStatusFailed
);
2518 n
= CFArrayGetCount(connectionPrivate
->rlList
);
2519 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, connectionPrivate
->rlList
)) {
2521 * if we are no longer scheduled to receive notifications for
2522 * this runLoop / runLoopMode
2524 CFRunLoopRemoveSource(runLoop
, connectionPrivate
->rls
, runLoopMode
);
2527 // if *all* notifications have been unscheduled
2528 CFRelease(connectionPrivate
->rlList
);
2529 connectionPrivate
->rlList
= NULL
;
2530 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
2531 CFRelease(connectionPrivate
->rls
);
2532 connectionPrivate
->rls
= NULL
;
2538 // if *all* notifications have been unscheduled
2539 connectionPrivate
->scheduled
= FALSE
;
2541 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2542 #if !TARGET_IPHONE_SIMULATOR
2543 ne_session_cancel(connectionPrivate
->ne_session
);
2544 #endif /* !TARGET_IPHONE_SIMULATOR */
2546 mach_port_t session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2547 if (session_port
== MACH_PORT_NULL
) {
2551 status
= pppcontroller_notification(session_port
, 0, &sc_status
);
2552 if (__SCNetworkConnectionNeedsRetry(connection
,
2553 "__SCNetworkConnectionUnscheduleFromRunLoop pppcontroller_notification()",
2556 sc_status
= kSCStatusOK
;
2557 status
= KERN_SUCCESS
;
2560 if ((status
!= KERN_SUCCESS
) || (sc_status
!= kSCStatusOK
)) {
2561 _SCErrorSet(sc_status
);
2571 pthread_mutex_unlock(&connectionPrivate
->lock
);
2573 if (drainGroup
!= NULL
) {
2574 dispatch_group_notify(drainGroup
, drainQueue
, ^{
2575 // release group/queue references
2576 dispatch_release(drainQueue
);
2577 dispatch_release(drainGroup
); // releases our connection reference
2581 // release our reference
2582 CFRelease(connection
);
2589 SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection
,
2590 CFRunLoopRef runLoop
,
2591 CFStringRef runLoopMode
)
2593 if (!isA_SCNetworkConnection(connection
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
2594 _SCErrorSet(kSCStatusInvalidArgument
);
2598 return __SCNetworkConnectionScheduleWithRunLoop(connection
, runLoop
, runLoopMode
, NULL
);
2603 SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection
,
2604 CFRunLoopRef runLoop
,
2605 CFStringRef runLoopMode
)
2607 if (!isA_SCNetworkConnection(connection
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
2608 _SCErrorSet(kSCStatusInvalidArgument
);
2612 return __SCNetworkConnectionUnscheduleFromRunLoop(connection
, runLoop
, runLoopMode
, NULL
);
2617 SCNetworkConnectionSetDispatchQueue(SCNetworkConnectionRef connection
,
2618 dispatch_queue_t queue
)
2622 if (!isA_SCNetworkConnection(connection
)) {
2623 _SCErrorSet(kSCStatusInvalidArgument
);
2627 if (queue
!= NULL
) {
2628 ok
= __SCNetworkConnectionScheduleWithRunLoop(connection
, NULL
, NULL
, queue
);
2630 ok
= __SCNetworkConnectionUnscheduleFromRunLoop(connection
, NULL
, NULL
, NULL
);
2637 /* Requires having called SCNetworkConnectionSelectServiceWithOptions previously */
2639 SCNetworkConnectionIsOnDemandSuspended(SCNetworkConnectionRef connection
)
2641 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2643 if (!isA_SCNetworkConnection(connection
)) {
2644 _SCErrorSet(kSCStatusInvalidArgument
);
2648 if (connectionPrivate
->on_demand_info
!= NULL
) {
2649 uint32_t isSuspended
= 0;
2650 CFNumberRef num
= NULL
;
2652 num
= CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCPropNetVPNOnDemandSuspended
);
2653 if (isA_CFNumber(num
) &&
2654 CFNumberGetValue(num
, kCFNumberSInt32Type
, &isSuspended
) &&
2655 (isSuspended
!= 0)) {
2660 _SCErrorSet(kSCStatusOK
);
2665 SCNetworkConnectionTriggerOnDemandIfNeeded (CFStringRef hostName
,
2666 Boolean afterDNSFail
,
2670 #if !TARGET_IPHONE_SIMULATOR
2671 __block Boolean triggeredOnDemand
= FALSE
;
2672 struct proc_uniqidentifierinfo procu
;
2673 void *policy_match
= NULL
;
2674 char *hostname
= NULL
;
2675 CFIndex hostnameSize
= 0;
2676 pid_t pid
= getpid();
2677 uid_t uid
= geteuid();
2679 /* Require hostName, require non-root user */
2680 if (hostName
== NULL
|| geteuid() == 0) {
2684 hostnameSize
= CFStringGetLength(hostName
);
2685 if (hostnameSize
== 0) {
2689 hostname
= malloc(hostnameSize
+ 1);
2690 CFStringGetCString(hostName
, hostname
, hostnameSize
+ 1, kCFStringEncodingUTF8
);
2692 if (proc_pidinfo(pid
, PROC_PIDUNIQIDENTIFIERINFO
, 1, &procu
, sizeof(procu
)) != sizeof(procu
)) {
2696 policy_match
= ne_session_copy_policy_match(hostname
, NULL
, NULL
, procu
.p_uuid
, procu
.p_uuid
, pid
, uid
, 0, trafficClass
);
2698 NEPolicyServiceActionType action_type
= ne_session_policy_match_get_service_action(policy_match
);
2699 if (action_type
== NESessionPolicyActionTrigger
||
2700 (afterDNSFail
&& action_type
== NESessionPolicyActionTriggerIfNeeded
)) {
2702 if (ne_session_policy_match_get_service(policy_match
, config_id
)) {
2703 xpc_object_t start_options
= xpc_dictionary_create(NULL
, NULL
, 0);
2704 if (start_options
!= NULL
) {
2705 xpc_dictionary_set_bool(start_options
, NESessionStartOptionIsOnDemandKey
, true);
2706 xpc_dictionary_set_string(start_options
, NESessionStartOptionMatchHostnameKey
, hostname
);
2708 ne_session_t new_session
= ne_session_create(config_id
, ne_session_policy_match_get_service_type(policy_match
));
2709 if (new_session
!= NULL
) {
2710 dispatch_semaphore_t wait_for_session
= dispatch_semaphore_create(0);
2711 dispatch_retain(wait_for_session
);
2712 xpc_retain(start_options
);
2713 ne_session_get_status(new_session
, __SCNetworkConnectionQueue(),
2714 ^(ne_session_status_t status
) {
2715 if (status
== NESessionStatusDisconnected
) {
2716 dispatch_retain(wait_for_session
);
2717 ne_session_set_event_handler(new_session
, __SCNetworkConnectionQueue(),
2718 ^(ne_session_event_t event
, void *event_data
) {
2719 if (event
== NESessionEventStatusChanged
) {
2720 dispatch_retain(wait_for_session
);
2721 ne_session_get_status(new_session
, __SCNetworkConnectionQueue(),
2722 ^(ne_session_status_t new_status
) {
2723 if (new_status
!= NESessionStatusConnecting
) {
2724 if (status
== NESessionStatusConnected
) {
2725 triggeredOnDemand
= TRUE
;
2727 ne_session_cancel(new_session
);
2729 dispatch_release(wait_for_session
);
2731 } else if (event
== NESessionEventCanceled
) {
2732 dispatch_semaphore_signal(wait_for_session
);
2733 dispatch_release(wait_for_session
);
2736 ne_session_start_with_options(new_session
, start_options
);
2738 dispatch_semaphore_signal(wait_for_session
);
2740 dispatch_release(wait_for_session
);
2741 xpc_release(start_options
);
2743 dispatch_semaphore_wait(wait_for_session
, timeout
? dispatch_time(DISPATCH_TIME_NOW
, (int64_t)timeout
* NSEC_PER_SEC
) : DISPATCH_TIME_FOREVER
);
2744 dispatch_release(wait_for_session
);
2745 ne_session_release(new_session
);
2748 xpc_release(start_options
);
2761 return triggeredOnDemand
;
2763 #pragma unused(hostName, afterDNSFail, timeout, trafficClass)
2770 SCNetworkConnectionCopyOnDemandInfo(SCNetworkConnectionRef connection
,
2771 CFStringRef
*onDemandRemoteAddress
,
2772 SCNetworkConnectionStatus
*onDemandConnectionStatus
)
2774 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2776 if (!isA_SCNetworkConnection(connection
)) {
2777 _SCErrorSet(kSCStatusInvalidArgument
);
2781 if (connectionPrivate
->service
== NULL
) {
2782 _SCErrorSet(kSCStatusConnectionNoService
);
2786 if (onDemandRemoteAddress
!= NULL
) {
2787 *onDemandRemoteAddress
= NULL
;
2790 if (onDemandConnectionStatus
!= NULL
) {
2791 *onDemandConnectionStatus
= kSCNetworkConnectionInvalid
;
2794 if (connectionPrivate
->on_demand_info
!= NULL
) {
2795 if (onDemandRemoteAddress
!= NULL
) {
2796 CFStringRef address
=
2797 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandRemoteAddress
);
2798 if (isA_CFString(address
)) {
2799 *onDemandRemoteAddress
= address
;
2800 CFRetain(*onDemandRemoteAddress
);
2804 if (onDemandConnectionStatus
!= NULL
) {
2806 CFNumberRef status_num
=
2807 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandStatus
);
2808 if (isA_CFNumber(status_num
) && CFNumberGetValue(status_num
, kCFNumberIntType
, &num
)) {
2809 *onDemandConnectionStatus
= num
;
2814 return connectionPrivate
->on_demand
;
2819 SCNetworkConnectionGetReachabilityInfo(SCNetworkConnectionRef connection
,
2820 SCNetworkReachabilityFlags
*reach_flags
,
2821 unsigned int *reach_if_index
)
2823 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2825 if (!isA_SCNetworkConnection(connection
)) {
2826 _SCErrorSet(kSCStatusInvalidArgument
);
2830 if (connectionPrivate
->service
== NULL
) {
2831 _SCErrorSet(kSCStatusConnectionNoService
);
2835 if (reach_flags
!= NULL
) {
2839 if (reach_if_index
!= NULL
) {
2840 *reach_if_index
= 0;
2843 if (connectionPrivate
->on_demand_info
!= NULL
) {
2844 if (reach_flags
!= NULL
) {
2846 CFNumberRef flags_num
=
2847 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandReachFlags
);
2848 if (isA_CFNumber(flags_num
) && CFNumberGetValue(flags_num
, kCFNumberIntType
, &num
)) {
2853 if (reach_if_index
!= NULL
) {
2855 CFNumberRef if_index_num
=
2856 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandReachInterfaceIndex
);
2857 if (isA_CFNumber(if_index_num
) && CFNumberGetValue(if_index_num
, kCFNumberIntType
, &num
)) {
2858 *reach_if_index
= num
;
2867 SCNetworkConnectionType
2868 SCNetworkConnectionGetType(SCNetworkConnectionRef connection
)
2870 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2872 if (!isA_SCNetworkConnection(connection
)) {
2873 _SCErrorSet(kSCStatusInvalidArgument
);
2874 return kSCNetworkConnectionTypeUnknown
;
2877 if (connectionPrivate
->service
== NULL
) {
2878 _SCErrorSet(kSCStatusConnectionNoService
);
2879 return kSCNetworkConnectionTypeUnknown
;
2882 _SCErrorSet(kSCStatusOK
);
2884 return connectionPrivate
->type
;
2889 validate_flow_properties(CFDictionaryRef flowProperties
)
2891 CFStringRef host_name_str
;
2892 CFDataRef host_address_data
;
2893 CFNumberRef host_port_num
;
2895 if (!isA_CFDictionary(flowProperties
)) {
2899 /* Validate the host name if one was given */
2900 host_name_str
= CFDictionaryGetValue(flowProperties
, kSCNetworkConnectionFlowPropertyHostName
);
2901 if (host_name_str
!= NULL
&& (!isA_CFString(host_name_str
) || CFStringGetLength(host_name_str
) == 0)) {
2905 /* Validate the address if one was given */
2906 host_address_data
= CFDictionaryGetValue(flowProperties
, kSCNetworkConnectionFlowPropertyHostAddress
);
2907 if (host_address_data
!= NULL
) {
2908 struct sockaddr
*sock_addr
;
2910 if (!isA_CFData(host_address_data
) || CFDataGetLength(host_address_data
) < sizeof(struct sockaddr
)) {
2914 sock_addr
= (struct sockaddr
*)CFDataGetBytePtr(host_address_data
);
2915 if (CFDataGetLength(host_address_data
) < sock_addr
->sa_len
) {
2919 if (sock_addr
->sa_family
== AF_INET
) {
2920 if (sock_addr
->sa_len
>= sizeof(struct sockaddr_in
)) {
2921 struct sockaddr_in
*sa_in
= (struct sockaddr_in
*)(void *)sock_addr
;
2922 in_addr_t any
= { INADDR_ANY
};
2923 if (memcmp(&sa_in
->sin_addr
, &any
, sizeof(any
)) == 0) {
2929 } else if (sock_addr
->sa_family
== AF_INET6
) {
2930 if (sock_addr
->sa_len
>= sizeof(struct sockaddr_in6
)) {
2931 struct sockaddr_in6
*sa_in6
= (struct sockaddr_in6
*)(void *)sock_addr
;
2932 struct in6_addr any
= IN6ADDR_ANY_INIT
;
2933 if (memcmp(&sa_in6
->sin6_addr
, &any
, sizeof(any
)) == 0) {
2940 /* We must have either a host name or an address */
2941 if (host_name_str
== NULL
&& host_address_data
== NULL
) {
2945 /* Validate the port */
2946 host_port_num
= CFDictionaryGetValue(flowProperties
, kSCNetworkConnectionFlowPropertyHostPort
);
2947 if (host_port_num
!= NULL
) {
2949 if (!isA_CFNumber(host_port_num
) || !CFNumberGetValue(host_port_num
, kCFNumberIntType
, &num
)) {
2965 SCNetworkConnectionCopyFlowDivertToken(SCNetworkConnectionRef connection
,
2966 CFDictionaryRef flowProperties
)
2968 CFDictionaryRef app_properties
= NULL
;
2969 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2970 CFDataRef token
= NULL
;
2972 if (!isA_SCNetworkConnection(connection
)) {
2973 _SCErrorSet(kSCStatusInvalidArgument
);
2977 if (connectionPrivate
->service
== NULL
) {
2978 _SCErrorSet(kSCStatusConnectionNoService
);
2982 if (connectionPrivate
->type
!= kSCNetworkConnectionTypeAppLayerVPN
) {
2983 _SCErrorSet(kSCStatusInvalidArgument
);
2987 if (!validate_flow_properties(flowProperties
)) {
2988 _SCErrorSet(kSCStatusInvalidArgument
);
2992 app_properties
= VPNAppLayerCopyCachedAppProperties(connectionPrivate
->client_audit_token
,
2993 connectionPrivate
->client_pid
,
2994 connectionPrivate
->client_uuid
,
2995 connectionPrivate
->client_bundle_id
);
2996 if (app_properties
== NULL
) {
2997 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkConnectionCopyFlowDivertToken: no cached app properties available"));
2998 _SCErrorSet(kSCStatusFailed
);
3002 token
= VPNAppLayerCreateFlowDivertToken(connection
, app_properties
, flowProperties
);
3005 if (app_properties
!= NULL
) {
3006 CFRelease(app_properties
);
3014 SCNetworkConnectionGetServiceIdentifier (SCNetworkConnectionRef connection
)
3016 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
3017 int service_identifier
= -1;
3019 if (connectionPrivate
->service
!= NULL
) {
3020 service_identifier
= 0;
3021 if (connectionPrivate
->on_demand_info
!= NULL
) {
3022 CFNumberRef id_num
= CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCPropNetDNSServiceIdentifier
);
3024 if (isA_CFNumber(id_num
)) {
3025 CFNumberGetValue(id_num
, kCFNumberIntType
, &service_identifier
);
3030 return service_identifier
;
3034 #if !TARGET_IPHONE_SIMULATOR
3035 SCNetworkConnectionStatus
3036 SCNetworkConnectionGetStatusFromNEStatus(ne_session_status_t status
)
3039 case NESessionStatusInvalid
:
3040 return kSCNetworkConnectionInvalid
;
3041 case NESessionStatusDisconnected
:
3042 return kSCNetworkConnectionDisconnected
;
3043 case NESessionStatusConnecting
:
3044 case NESessionStatusReasserting
:
3045 return kSCNetworkConnectionConnecting
;
3046 case NESessionStatusConnected
:
3047 return kSCNetworkConnectionConnected
;
3048 case NESessionStatusDisconnecting
:
3049 return kSCNetworkConnectionDisconnecting
;
3052 return kSCNetworkConnectionInvalid
;
3054 #endif /* !TARGET_IPHONE_SIMULATOR */
3058 #pragma mark User level "dial" API
3061 #define k_NetworkConnect_Notification "com.apple.networkConnect"
3062 #define k_NetworkConnect_Pref_File CFSTR("com.apple.networkConnect")
3063 #define k_InterentConnect_Pref_File CFSTR("com.apple.internetconnect")
3065 #define k_Dial_Default_Key CFSTR("ConnectByDefault") // needs to go into SC
3066 #define k_Last_Service_Id_Key CFSTR("ServiceID")
3067 #define k_Unique_Id_Key CFSTR("UniqueIdentifier")
3070 /* Private Prototypes */
3071 static Boolean
SCNetworkConnectionPrivateCopyDefaultServiceIDForDial (CFStringRef
*serviceID
);
3072 static Boolean
SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore (CFStringRef
*serviceID
);
3073 static Boolean
SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray
, CFDictionaryRef
*userOptions
);
3074 static Boolean
SCNetworkConnectionPrivateIsPPPService (CFStringRef serviceID
, CFStringRef subType1
, CFStringRef subType2
);
3075 static void addPasswordFromKeychain (CFStringRef serviceID
, CFDictionaryRef
*userOptions
);
3076 static CFStringRef
copyPasswordFromKeychain (CFStringRef uniqueID
);
3078 static int notify_userprefs_token
= -1;
3080 static CFDictionaryRef onDemand_configuration
= NULL
;
3081 static Boolean onDemand_force_refresh
= FALSE
;
3082 static pthread_mutex_t onDemand_notify_lock
= PTHREAD_MUTEX_INITIALIZER
;
3083 static int onDemand_notify_token
= -1;
3087 * return TRUE if domain1 ends with domain2, and will check for trailing "."
3089 #define WILD_CARD_MATCH_STR CFSTR("*")
3091 _SC_domainEndsWithDomain(CFStringRef compare_domain
, CFStringRef match_domain
)
3094 Boolean ret
= FALSE
;
3095 CFStringRef s1
= NULL
;
3096 Boolean s1_created
= FALSE
;
3097 CFStringRef s2
= NULL
;
3098 Boolean s2_created
= FALSE
;
3099 CFStringRef s3
= NULL
;
3101 if (CFEqual(match_domain
, WILD_CARD_MATCH_STR
)) {
3105 if (CFStringHasSuffix(compare_domain
, CFSTR("."))) {
3107 range
.length
= CFStringGetLength(compare_domain
) - 1;
3108 s1
= CFStringCreateWithSubstring(NULL
, compare_domain
, range
);
3114 s1
= compare_domain
;
3117 if (CFStringHasSuffix(match_domain
, CFSTR("."))) {
3119 range
.length
= CFStringGetLength(match_domain
) - 1;
3120 s2
= CFStringCreateWithSubstring(NULL
, match_domain
, range
);
3129 if (CFStringHasPrefix(s2
, CFSTR("*."))) {
3131 range
.length
= CFStringGetLength(s2
)-2;
3132 s3
= CFStringCreateWithSubstring(NULL
, s2
, range
);
3143 ret
= CFStringHasSuffix(s1
, s2
);
3147 if (s1_created
) CFRelease(s1
);
3148 if (s2_created
) CFRelease(s2
);
3152 static CFCharacterSetRef
3153 _SC_getNotDotOrStarCharacterSet (void)
3155 static CFCharacterSetRef notDotOrStar
= NULL
;
3156 if (notDotOrStar
== NULL
) {
3157 CFCharacterSetRef dotOrStar
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, CFSTR(".*"));
3159 notDotOrStar
= CFCharacterSetCreateInvertedSet(kCFAllocatorDefault
, dotOrStar
);
3160 CFRelease(dotOrStar
);
3163 return notDotOrStar
;
3166 static CFMutableStringRef
3167 _SC_createStringByTrimmingDotsAndStars (CFStringRef string
)
3169 CFCharacterSetRef notDotOrStar
= _SC_getNotDotOrStarCharacterSet();
3170 CFRange entireString
= CFRangeMake(0, CFStringGetLength(string
));
3171 CFMutableStringRef result
= CFStringCreateMutableCopy(kCFAllocatorDefault
, entireString
.length
, string
);
3173 CFRange end
= CFRangeMake(entireString
.length
, 0);
3175 if (CFStringFindCharacterFromSet(string
, notDotOrStar
, entireString
, 0, &start
) &&
3176 CFStringFindCharacterFromSet(string
, notDotOrStar
, entireString
, kCFCompareBackwards
, &end
)) {
3177 if (start
.location
== kCFNotFound
|| end
.location
== kCFNotFound
|| start
.location
> end
.location
) {
3183 if ((end
.location
+ 1) < entireString
.length
) {
3184 CFStringReplace(result
, CFRangeMake(end
.location
+ 1, entireString
.length
- (end
.location
+ 1)), CFSTR(""));
3186 if (start
.location
> 0) {
3187 CFStringReplace(result
, CFRangeMake(0, start
.location
), CFSTR(""));
3194 _SC_getCountOfStringInString (CFStringRef string
, CFStringRef substring
)
3197 CFArrayRef ranges
= CFStringCreateArrayWithFindResults(kCFAllocatorDefault
, string
, substring
, CFRangeMake(0, CFStringGetLength(string
)), 0);
3198 if (ranges
!= NULL
) {
3199 count
= CFArrayGetCount(ranges
);
3206 _SC_hostMatchesDomain(CFStringRef hostname
, CFStringRef domain
)
3208 Boolean result
= FALSE
;
3209 CFMutableStringRef trimmedHostname
= NULL
;
3210 CFMutableStringRef trimmedDomain
= NULL
;
3212 if (!isA_CFString(hostname
) || !isA_CFString(domain
)) {
3216 trimmedHostname
= _SC_createStringByTrimmingDotsAndStars(hostname
);
3217 trimmedDomain
= _SC_createStringByTrimmingDotsAndStars(domain
);
3219 if (!isA_CFString(trimmedHostname
) || !isA_CFString(trimmedDomain
)) {
3223 CFIndex numHostnameDots
= _SC_getCountOfStringInString(trimmedHostname
, CFSTR("."));
3224 CFIndex numDomainDots
= _SC_getCountOfStringInString(trimmedDomain
, CFSTR("."));
3225 if (numHostnameDots
== numDomainDots
) {
3226 result
= CFEqual(trimmedHostname
, trimmedDomain
);
3227 } else if (numDomainDots
> 0 && numDomainDots
< numHostnameDots
) {
3228 CFStringReplace(trimmedDomain
, CFRangeMake(0, 0), CFSTR("."));
3229 result
= CFStringHasSuffix(trimmedHostname
, trimmedDomain
);
3235 if (trimmedHostname
) {
3236 CFRelease(trimmedHostname
);
3238 if (trimmedDomain
) {
3239 CFRelease(trimmedDomain
);
3246 static CFDictionaryRef
3247 __SCNetworkConnectionCopyOnDemandConfiguration(void)
3251 uint64_t triggersCount
= 0;
3252 CFDictionaryRef configuration
;
3254 pthread_mutex_lock(&onDemand_notify_lock
);
3255 if (onDemand_notify_token
== -1) {
3256 status
= notify_register_check(kSCNETWORKCONNECTION_ONDEMAND_NOTIFY_KEY
, &onDemand_notify_token
);
3257 if (status
!= NOTIFY_STATUS_OK
) {
3258 SCLog(TRUE
, LOG_ERR
, CFSTR("notify_register_check() failed, status=%d"), status
);
3259 onDemand_notify_token
= -1;
3263 if (onDemand_notify_token
!= -1) {
3264 status
= notify_check(onDemand_notify_token
, &changed
);
3265 if (status
!= NOTIFY_STATUS_OK
) {
3266 SCLog(TRUE
, LOG_ERR
, CFSTR("notify_check() failed, status=%d"), status
);
3267 (void)notify_cancel(onDemand_notify_token
);
3268 onDemand_notify_token
= -1;
3272 if (changed
&& (onDemand_notify_token
!= -1)) {
3273 status
= notify_get_state(onDemand_notify_token
, &triggersCount
);
3274 if (status
!= NOTIFY_STATUS_OK
) {
3275 SCLog(TRUE
, LOG_ERR
, CFSTR("notify_get_state() failed, status=%d"), status
);
3276 (void)notify_cancel(onDemand_notify_token
);
3277 onDemand_notify_token
= -1;
3281 if (changed
|| onDemand_force_refresh
) {
3284 if (_sc_debug
|| (debug
> 0)) {
3285 SCLog(TRUE
, LOG_INFO
,
3286 CFSTR("OnDemand information %s"),
3287 (onDemand_configuration
== NULL
) ? "fetched" : "updated");
3290 if (onDemand_configuration
!= NULL
) {
3291 CFRelease(onDemand_configuration
);
3292 onDemand_configuration
= NULL
;
3295 if ((triggersCount
> 0) || onDemand_force_refresh
) {
3296 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, kSCDynamicStoreDomainState
, kSCEntNetOnDemand
);
3297 onDemand_configuration
= SCDynamicStoreCopyValue(NULL
, key
);
3299 if ((onDemand_configuration
!= NULL
) && !isA_CFDictionary(onDemand_configuration
)) {
3300 CFRelease(onDemand_configuration
);
3301 onDemand_configuration
= NULL
;
3305 onDemand_force_refresh
= FALSE
;
3308 configuration
= (onDemand_configuration
!= NULL
) ? CFRetain(onDemand_configuration
) : NULL
;
3309 pthread_mutex_unlock(&onDemand_notify_lock
);
3311 return configuration
;
3317 __SCNetworkConnectionForceOnDemandConfigurationRefresh(void)
3319 pthread_mutex_lock(&onDemand_notify_lock
);
3320 onDemand_force_refresh
= TRUE
;
3321 pthread_mutex_unlock(&onDemand_notify_lock
);
3328 __SCNetworkConnectionShouldNeverMatch(CFDictionaryRef trigger
, CFStringRef hostName
, pid_t client_pid
)
3330 CFArrayRef exceptedProcesses
;
3331 CFIndex exceptedProcessesCount
;
3332 CFIndex exceptedProcessesIndex
;
3333 CFArrayRef exceptions
;
3334 CFIndex exceptionsCount
;
3335 int exceptionsIndex
;
3337 // we have a matching domain, check against exception list
3338 exceptions
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandMatchDomainsNever
);
3339 exceptionsCount
= isA_CFArray(exceptions
) ? CFArrayGetCount(exceptions
) : 0;
3340 for (exceptionsIndex
= 0; exceptionsIndex
< exceptionsCount
; exceptionsIndex
++) {
3341 CFStringRef exception
;
3343 exception
= CFArrayGetValueAtIndex(exceptions
, exceptionsIndex
);
3344 if (isA_CFString(exception
) && _SC_domainEndsWithDomain(hostName
, exception
)) {
3345 // found matching exception
3346 if (_sc_debug
|| (debug
> 0)) {
3347 SCLog(TRUE
, LOG_INFO
, CFSTR("OnDemand match exception"));
3353 if (client_pid
!= 0) {
3354 exceptedProcesses
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandPluginPIDs
);
3355 exceptedProcessesCount
= isA_CFArray(exceptedProcesses
) ? CFArrayGetCount(exceptedProcesses
) : 0;
3356 for (exceptedProcessesIndex
= 0; exceptedProcessesIndex
< exceptedProcessesCount
; exceptedProcessesIndex
++) {
3360 pidRef
= CFArrayGetValueAtIndex(exceptedProcesses
, exceptedProcessesIndex
);
3361 if (isA_CFNumber(pidRef
) && CFNumberGetValue(pidRef
, kCFNumberIntType
, &pid
)) {
3362 if (pid
== client_pid
) {
3373 __SCNetworkConnectionDomainGetMatchWithParameters(CFStringRef action
, CFPropertyListRef actionParameters
, CFStringRef hostName
, CFStringRef
*probeString
)
3375 CFArrayRef actionArray
= NULL
;
3376 CFIndex actionArraySize
= 0;
3378 CFStringRef matchDomain
= NULL
;
3380 /* For now, only support EvaluateConnection, which takes a CFArray */
3381 if (!CFEqual(action
, kSCValNetVPNOnDemandRuleActionEvaluateConnection
) || !isA_CFArray(actionParameters
)) {
3385 actionArray
= (CFArrayRef
)actionParameters
;
3386 actionArraySize
= CFArrayGetCount(actionArray
);
3388 /* Process domain rules, with actions of ConnectIfNeeded and NeverConnect */
3389 for (i
= 0; i
< actionArraySize
; i
++) {
3390 CFStringRef domainAction
= NULL
;
3391 CFDictionaryRef domainRule
= CFArrayGetValueAtIndex(actionArray
, i
);
3392 CFArrayRef domains
= NULL
;
3393 CFIndex domainsCount
= 0;
3394 CFIndex domainsIndex
;
3396 if (!isA_CFDictionary(domainRule
)) {
3400 domains
= CFDictionaryGetValue(domainRule
, kSCPropNetVPNOnDemandRuleActionParametersDomains
);
3401 if (!isA_CFArray(domains
)) {
3405 domainsCount
= CFArrayGetCount(domains
);
3406 for (domainsIndex
= 0; domainsIndex
< domainsCount
; domainsIndex
++) {
3408 domain
= CFArrayGetValueAtIndex(domains
, domainsIndex
);
3409 if (isA_CFString(domain
) && _SC_domainEndsWithDomain(hostName
, domain
)) {
3410 matchDomain
= domain
;
3416 domainAction
= CFDictionaryGetValue(domainRule
, kSCPropNetVPNOnDemandRuleActionParametersDomainAction
);
3417 if (isA_CFString(domainAction
) && CFEqual(domainAction
, kSCValNetVPNOnDemandRuleActionParametersDomainActionNeverConnect
)) {
3420 /* If we found a match, save the optional probe string as well */
3422 *probeString
= CFDictionaryGetValue(domainRule
, kSCPropNetVPNOnDemandRuleActionParametersRequiredURLStringProbe
);
3433 __SCNetworkConnectionDomainGetMatch(CFDictionaryRef trigger
, CFStringRef hostName
, Boolean onDemandRetry
)
3436 CFIndex domainsCount
;
3439 CFStringRef match_domain
= NULL
;
3441 /* Old configuration: always, never, on retry lists */
3442 key
= onDemandRetry
? kSCNetworkConnectionOnDemandMatchDomainsOnRetry
: kSCNetworkConnectionOnDemandMatchDomainsAlways
;
3444 domains
= CFDictionaryGetValue(trigger
, key
);
3445 domainsCount
= isA_CFArray(domains
) ? CFArrayGetCount(domains
) : 0;
3446 for (domainsIndex
= 0; domainsIndex
< domainsCount
; domainsIndex
++) {
3449 domain
= CFArrayGetValueAtIndex(domains
, domainsIndex
);
3450 if (isA_CFString(domain
) && _SC_domainEndsWithDomain(hostName
, domain
)) {
3451 match_domain
= domain
;
3456 return match_domain
;
3461 __SCNetworkConnectionShouldAlwaysConnect(CFDictionaryRef trigger
)
3463 CFStringRef action
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleAction
);
3464 return (isA_CFString(action
) && CFEqual(action
, kSCValNetVPNOnDemandRuleActionConnect
));
3469 __SCNetworkConnectionShouldIgnoreTrigger(CFDictionaryRef trigger
)
3471 CFStringRef action
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleAction
);
3473 if (isA_CFString(action
) &&
3474 (CFEqual(action
, kSCValNetVPNOnDemandRuleActionIgnore
) ||
3475 CFEqual(action
, kSCValNetVPNOnDemandRuleActionDisconnect
))) {
3483 static CFDictionaryRef
3484 __SCNetworkConnectionCopyMatchingTriggerWithName(CFDictionaryRef configuration
,
3485 CFStringRef hostName
,
3487 Boolean onDemandRetry
,
3488 CFDictionaryRef
*match_info
,
3489 Boolean
*triggerNow
,
3490 CFStringRef
*probe_string
)
3492 CFDictionaryRef result
= NULL
;
3493 int sc_status
= kSCStatusOK
;
3494 CFArrayRef triggers
;
3495 uint64_t triggersCount
= 0;
3497 Boolean usedOnDemandRetry
= FALSE
;
3499 if (triggerNow
!= NULL
) {
3500 *triggerNow
= FALSE
;
3503 if (match_info
!= NULL
) {
3507 triggers
= CFDictionaryGetValue(configuration
, kSCNetworkConnectionOnDemandTriggers
);
3508 triggersCount
= isA_CFArray(triggers
) ? CFArrayGetCount(triggers
) : 0;
3509 for (triggersIndex
= 0; triggersIndex
< triggersCount
; triggersIndex
++) {
3510 CFStringRef matched_domain
= NULL
;
3511 CFStringRef matched_probe_string
= NULL
;
3512 CFDictionaryRef trigger
;
3513 Boolean trigger_matched
= FALSE
;
3515 usedOnDemandRetry
= FALSE
;
3517 trigger
= CFArrayGetValueAtIndex(triggers
, triggersIndex
);
3518 if (!isA_CFDictionary(trigger
)) {
3519 // if not a valid "OnDemand" configuration
3523 if (__SCNetworkConnectionShouldAlwaysConnect(trigger
)) {
3524 /* If the trigger action is 'Connect', always match this trigger */
3525 /* First check the never match list */
3526 if (__SCNetworkConnectionShouldNeverMatch(trigger
, hostName
, client_pid
)) {
3529 trigger_matched
= TRUE
;
3530 } else if (__SCNetworkConnectionShouldIgnoreTrigger(trigger
)) {
3531 /* If the trigger action is 'Ignore' or 'Disconnect', skip this trigger */
3532 sc_status
= kSCStatusConnectionIgnore
;
3535 CFStringRef action
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleAction
);
3536 CFArrayRef actionParameters
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleActionParameters
);
3537 if (action
&& actionParameters
) {
3538 matched_domain
= __SCNetworkConnectionDomainGetMatchWithParameters(action
, actionParameters
, hostName
, &matched_probe_string
);
3539 usedOnDemandRetry
= TRUE
;
3541 if (onDemandRetry
) {
3542 matched_domain
= __SCNetworkConnectionDomainGetMatch(trigger
, hostName
, TRUE
);
3543 usedOnDemandRetry
= TRUE
;
3545 matched_domain
= __SCNetworkConnectionDomainGetMatch(trigger
, hostName
, FALSE
);
3546 if (matched_domain
== NULL
&& result
== NULL
) {
3547 /* Check the retry list if Always failed */
3548 matched_domain
= __SCNetworkConnectionDomainGetMatch(trigger
, hostName
, TRUE
);
3549 usedOnDemandRetry
= TRUE
;
3554 if (matched_domain
) {
3555 if (__SCNetworkConnectionShouldNeverMatch(trigger
, hostName
, client_pid
)) {
3556 matched_domain
= NULL
;
3559 trigger_matched
= TRUE
;
3564 if (trigger_matched
) {
3565 // if we have a matching domain and there were no exceptions
3566 // then we pass back the OnDemand info
3567 if (match_info
!= NULL
) {
3568 CFMutableDictionaryRef minfo
;
3569 SCNetworkConnectionType type
= kSCNetworkConnectionTypeIPLayerVPN
;
3570 CFNumberRef type_num
;
3572 if (*match_info
!= NULL
) {
3573 CFRelease(*match_info
);
3577 minfo
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
3579 &kCFTypeDictionaryKeyCallBacks
,
3580 &kCFTypeDictionaryValueCallBacks
);
3582 type_num
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &type
);
3583 CFDictionarySetValue(minfo
, kSCNetworkConnectionOnDemandMatchInfoVPNType
, type_num
);
3584 CFRelease(type_num
);
3585 if (matched_domain
) {
3586 CFDictionarySetValue(minfo
, kSCNetworkConnectionOnDemandMatchInfoDomain
, matched_domain
);
3588 CFDictionarySetValue(minfo
,
3589 kSCNetworkConnectionOnDemandMatchInfoOnRetry
,
3590 (usedOnDemandRetry
? kCFBooleanTrue
: kCFBooleanFalse
));
3592 *match_info
= minfo
;
3595 if (probe_string
!= NULL
) {
3596 if (*probe_string
!= NULL
) {
3597 CFRelease(*probe_string
);
3598 *probe_string
= NULL
;
3601 if (matched_probe_string
) {
3602 *probe_string
= CFRetain(matched_probe_string
);
3608 /* If retry was requested, or we found Always match, trigger now */
3609 if (onDemandRetry
|| !usedOnDemandRetry
) {
3610 if (triggerNow
!= NULL
) {
3616 /* If we matched the Retry list, but Always was requested,
3617 keep going through triggers in case one matches an Always */
3625 _SCErrorSet(sc_status
);
3630 static CFDictionaryRef
3631 __SCNetworkConnectionCopyTriggerWithService(CFDictionaryRef configuration
,
3632 CFStringRef service_id
)
3634 CFArrayRef triggers
;
3635 uint64_t triggersCount
= 0;
3638 triggers
= CFDictionaryGetValue(configuration
, kSCNetworkConnectionOnDemandTriggers
);
3639 triggersCount
= isA_CFArray(triggers
) ? CFArrayGetCount(triggers
) : 0;
3640 for (triggersIndex
= 0; triggersIndex
< triggersCount
; triggersIndex
++) {
3641 CFDictionaryRef trigger
;
3642 CFStringRef trigger_service_id
;
3644 trigger
= CFArrayGetValueAtIndex(triggers
, triggersIndex
);
3645 if (!isA_CFDictionary(trigger
)) {
3646 // if not a valid "OnDemand" configuration
3650 trigger_service_id
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandServiceID
);
3651 if (isA_CFString(trigger_service_id
) && CFEqual(trigger_service_id
, service_id
)) {
3661 __private_extern__ CFDictionaryRef
3662 __SCNetworkConnectionCopyTokenParameters(SCNetworkConnectionRef connection
)
3664 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
3665 CFDictionaryRef parameters
= NULL
;
3666 uint8_t params_buffer
[PPP_MACH_MAX_INLINE_DATA
];
3667 uint32_t params_buffer_len
= sizeof(params_buffer
);
3668 int sc_status
= kSCStatusOK
;
3669 mach_port_t session_port
;
3670 kern_return_t status
;
3672 pthread_mutex_lock(&connectionPrivate
->lock
);
3674 parameters
= connectionPrivate
->flow_divert_token_params
;
3675 if (parameters
!= NULL
) {
3676 CFRetain(parameters
);
3681 if (parameters
!= NULL
) {
3682 CFRelease(parameters
);
3686 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
3687 if (session_port
== MACH_PORT_NULL
) {
3691 status
= pppcontroller_flow_divert_copy_token_parameters(session_port
, params_buffer
, ¶ms_buffer_len
);
3692 if (status
== KERN_SUCCESS
) {
3693 if (params_buffer_len
> 0) {
3694 CFDataRef params_data
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
,
3698 parameters
= CFPropertyListCreateWithData(kCFAllocatorDefault
,
3700 kCFPropertyListImmutable
,
3703 CFRelease(params_data
);
3707 if (__SCNetworkConnectionNeedsRetry(connection
, "__SCNetworkConnectionCopyTokenParameters()", status
, &sc_status
)) {
3711 if (sc_status
!= kSCStatusOK
) {
3712 _SCErrorSet(sc_status
);
3716 if (parameters
!= NULL
&& connectionPrivate
->flow_divert_token_params
== NULL
) {
3717 connectionPrivate
->flow_divert_token_params
= (CFDictionaryRef
)CFRetain(parameters
);
3720 pthread_mutex_unlock(&connectionPrivate
->lock
);
3726 __SCNetworkConnectionCopyOnDemandInfoWithName(SCDynamicStoreRef
*storeP
,
3727 CFStringRef hostName
,
3728 Boolean onDemandRetry
,
3729 CFStringRef
*connectionServiceID
,
3730 SCNetworkConnectionStatus
*connectionStatus
,
3731 CFStringRef
*vpnRemoteAddress
) /* CFDictionaryRef *info */
3733 CFDictionaryRef configuration
;
3735 int sc_status
= kSCStatusOK
;
3736 CFDictionaryRef trigger
;
3737 Boolean trigger_now
= FALSE
;
3739 configuration
= __SCNetworkConnectionCopyOnDemandConfiguration();
3740 if (configuration
== NULL
) {
3741 _SCErrorSet(sc_status
);
3745 trigger
= __SCNetworkConnectionCopyMatchingTriggerWithName(configuration
, hostName
, 0, onDemandRetry
, NULL
, &trigger_now
, NULL
);
3746 if (trigger
!= NULL
&& trigger_now
) {
3748 SCNetworkConnectionStatus onDemandStatus
= kSCNetworkConnectionDisconnected
;
3752 if (!CFDictionaryGetValueIfPresent(trigger
, kSCNetworkConnectionOnDemandStatus
, (const void **)&num
) ||
3753 !isA_CFNumber(num
) ||
3754 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &onDemandStatus
)) {
3755 onDemandStatus
= kSCNetworkConnectionDisconnected
;
3757 if (connectionStatus
!= NULL
) {
3758 *connectionStatus
= onDemandStatus
;
3761 if (connectionServiceID
!= NULL
) {
3762 *connectionServiceID
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandServiceID
);
3763 *connectionServiceID
= isA_CFString(*connectionServiceID
);
3764 if ((*connectionServiceID
!= NULL
) && (CFStringGetLength(*connectionServiceID
) > 0)) {
3765 CFRetain(*connectionServiceID
);
3767 SCLog(TRUE
, LOG_INFO
, CFSTR("OnDemand%s configuration error, no serviceID"),
3768 onDemandRetry
? " (on retry)" : "");
3769 *connectionServiceID
= NULL
;
3774 if (vpnRemoteAddress
!= NULL
) {
3775 *vpnRemoteAddress
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandRemoteAddress
);
3776 *vpnRemoteAddress
= isA_CFString(*vpnRemoteAddress
);
3777 if ((*vpnRemoteAddress
!= NULL
) && (CFStringGetLength(*vpnRemoteAddress
) > 0)) {
3778 CFRetain(*vpnRemoteAddress
);
3780 SCLog(TRUE
, LOG_INFO
, CFSTR("OnDemand%s configuration error, no server address"),
3781 onDemandRetry
? " (on retry)" : "");
3782 *vpnRemoteAddress
= NULL
;
3788 if ((connectionServiceID
!= NULL
) && (*connectionServiceID
!= NULL
)) {
3789 CFRelease(*connectionServiceID
);
3790 *connectionServiceID
= NULL
;
3792 if ((vpnRemoteAddress
!= NULL
) && (*vpnRemoteAddress
!= NULL
)) {
3793 CFRelease(*vpnRemoteAddress
);
3794 *vpnRemoteAddress
= NULL
;
3796 sc_status
= kSCStatusFailed
;
3798 if (_sc_debug
|| (debug
> 0)) {
3799 SCLog(TRUE
, LOG_INFO
, CFSTR("OnDemand%s match, connection status = %d"),
3800 onDemandRetry
? " (on retry)" : "",
3810 // if (_sc_debug || (debug > 0)) {
3811 // SCLog(TRUE, LOG_INFO, CFSTR("OnDemand domain name(s) not matched"));
3814 if (configuration
!= NULL
) CFRelease(configuration
);
3816 _SCErrorSet(sc_status
);
3822 __SCNetworkConnectionCopyUserPreferencesInternal(CFDictionaryRef selectionOptions
,
3823 CFStringRef
*serviceID
,
3824 CFDictionaryRef
*userOptions
)
3826 int prefsChanged
= 1;
3828 Boolean success
= FALSE
;
3830 if (notify_userprefs_token
== -1) {
3831 status
= notify_register_check(k_NetworkConnect_Notification
, ¬ify_userprefs_token
);
3832 if (status
!= NOTIFY_STATUS_OK
) {
3833 SCLog(TRUE
, LOG_ERR
, CFSTR("notify_register_check() failed, status=%d"), status
);
3834 (void)notify_cancel(notify_userprefs_token
);
3835 notify_userprefs_token
= -1;
3837 // clear the "something has changed" state
3838 (void) notify_check(notify_userprefs_token
, &prefsChanged
);
3842 if (notify_userprefs_token
!= -1) {
3843 status
= notify_check(notify_userprefs_token
, &prefsChanged
);
3844 if (status
!= NOTIFY_STATUS_OK
) {
3845 SCLog(TRUE
, LOG_ERR
, CFSTR("notify_check() failed, status=%d"), status
);
3846 (void)notify_cancel(notify_userprefs_token
);
3847 notify_userprefs_token
= -1;
3853 *userOptions
= NULL
;
3855 if (selectionOptions
!= NULL
) {
3856 Boolean catchAllFound
= FALSE
;
3857 CFIndex catchAllService
= 0;
3858 CFIndex catchAllConfig
= 0;
3859 CFStringRef hostName
= NULL
;
3860 CFStringRef priority
= NULL
;
3861 CFArrayRef serviceNames
= NULL
;
3862 CFDictionaryRef services
= NULL
;
3863 CFIndex serviceIndex
;
3864 CFIndex servicesCount
;
3866 hostName
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
);
3867 if (hostName
== NULL
) {
3868 hostName
= CFDictionaryGetValue(selectionOptions
, kSCPropNetPPPOnDemandHostName
);
3870 hostName
= isA_CFString(hostName
);
3871 if (hostName
== NULL
)
3872 goto done_selection
; // if no hostname for matching
3874 priority
= CFDictionaryGetValue(selectionOptions
, kSCPropNetPPPOnDemandPriority
);
3875 if (!isA_CFString(priority
))
3876 priority
= kSCValNetPPPOnDemandPriorityDefault
;
3879 if (!isA_CFArray(serviceNames
))
3880 goto done_selection
;
3883 if (!isA_CFDictionary(services
)) {
3884 goto done_selection
;
3887 servicesCount
= CFArrayGetCount(serviceNames
);
3888 for (serviceIndex
= 0; serviceIndex
< servicesCount
; serviceIndex
++) {
3889 CFIndex configIndex
;
3890 CFIndex configsCount
;
3891 CFArrayRef serviceConfigs
;
3892 CFStringRef serviceName
;
3895 serviceName
= CFArrayGetValueAtIndex(serviceNames
, serviceIndex
);
3896 if (!isA_CFString(serviceName
)) {
3900 serviceConfigs
= CFDictionaryGetValue(services
, serviceName
);
3901 if (!isA_CFArray(serviceConfigs
)) {
3905 configsCount
= CFArrayGetCount(serviceConfigs
);
3906 for (configIndex
= 0; configIndex
< configsCount
; configIndex
++) {
3907 CFNumberRef autodial
;
3908 CFDictionaryRef config
;
3909 CFDictionaryRef pppConfig
;
3911 config
= CFArrayGetValueAtIndex(serviceConfigs
, configIndex
);
3912 if (!isA_CFDictionary(config
)) {
3916 pppConfig
= CFDictionaryGetValue(config
, kSCEntNetPPP
);
3917 if (!isA_CFDictionary(pppConfig
)) {
3921 autodial
= CFDictionaryGetValue(pppConfig
, kSCPropNetPPPOnDemandEnabled
);
3922 if (!isA_CFNumber(autodial
)) {
3926 CFNumberGetValue(autodial
, kCFNumberIntType
, &val
);
3929 CFIndex domainsCount
;
3930 CFIndex domainsIndex
;
3932 /* we found an conditional connection enabled configuration */
3935 domains
= CFDictionaryGetValue(pppConfig
, kSCPropNetPPPOnDemandDomains
);
3936 if (!isA_CFArray(domains
)) {
3940 domainsCount
= CFArrayGetCount(domains
);
3941 for (domainsIndex
= 0; domainsIndex
< domainsCount
; domainsIndex
++) {
3944 domain
= CFArrayGetValueAtIndex(domains
, domainsIndex
);
3945 if (!isA_CFString(domain
)) {
3949 if (!catchAllFound
&&
3950 (CFStringCompare(domain
, CFSTR(""), 0) == kCFCompareEqualTo
3951 || CFStringCompare(domain
, CFSTR("."), 0) == kCFCompareEqualTo
))
3953 // found a catch all
3954 catchAllFound
= TRUE
;
3955 catchAllService
= serviceIndex
;
3956 catchAllConfig
= configIndex
;
3959 if (_SC_domainEndsWithDomain(hostName
, domain
)) {
3960 // found matching configuration
3961 *serviceID
= serviceName
;
3962 CFRetain(*serviceID
);
3963 *userOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, config
);
3964 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
3965 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCPropNetPPPOnDemandPriority
, priority
);
3966 addPasswordFromKeychain(*serviceID
, userOptions
);
3968 goto done_selection
;
3975 // config not found, do we have a catchall ?
3976 if (catchAllFound
) {
3977 CFDictionaryRef config
;
3978 CFArrayRef serviceConfigs
;
3979 CFStringRef serviceName
;
3981 serviceName
= CFArrayGetValueAtIndex(serviceNames
, catchAllService
);
3982 serviceConfigs
= CFDictionaryGetValue(services
, serviceName
);
3983 config
= CFArrayGetValueAtIndex(serviceConfigs
, catchAllConfig
);
3985 *serviceID
= serviceName
;
3986 CFRetain(*serviceID
);
3987 *userOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, config
);
3988 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
3989 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCPropNetPPPOnDemandPriority
, priority
);
3990 addPasswordFromKeychain(*serviceID
, userOptions
);
3992 goto done_selection
;
3998 CFRelease(serviceNames
);
4001 CFRelease(services
);
4005 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionCopyUserPreferences %@"), success
? CFSTR("succeeded") : CFSTR("failed"));
4006 SCLog(TRUE
, LOG_DEBUG
, CFSTR("Selection options: %@"), selectionOptions
);
4012 /* we don't have selection options */
4014 // (1) Figure out which service ID we care about, allocate it into passed "serviceID"
4015 success
= SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(serviceID
);
4017 if (success
&& (*serviceID
!= NULL
)) {
4018 // (2) Get the list of user data for this service ID
4019 CFPropertyListRef userServices
= NULL
;
4022 // (3) We are expecting an array if the user has defined records for this service ID or NULL if the user hasn't
4023 if (userServices
!= NULL
) {
4024 if (isA_CFArray(userServices
)) {
4025 // (4) Get the default set of user options for this service
4026 success
= SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray((CFArrayRef
)userServices
,
4028 if(success
&& (userOptions
!= NULL
)) {
4029 addPasswordFromKeychain(*serviceID
, userOptions
);
4032 SCLog(TRUE
, LOG_DEBUG
, CFSTR("Error, userServices are not of type CFArray!"));
4035 CFRelease(userServices
); // this is OK because SCNetworkConnectionPrivateISExpectedCFType() checks for NULL
4040 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionCopyUserPreferences %@, no selection options"), success
? CFSTR("succeeded") : CFSTR("failed"));
4048 SCNetworkConnectionCopyUserPreferences(CFDictionaryRef selectionOptions
,
4049 CFStringRef
*serviceID
,
4050 CFDictionaryRef
*userOptions
)
4052 Boolean success
= FALSE
;
4055 /* initialize runtime */
4056 pthread_once(&initialized
, __SCNetworkConnectionInitialize
);
4058 /* first check for new VPN OnDemand style */
4059 if (selectionOptions
!= NULL
) {
4060 CFStringRef hostName
;
4062 hostName
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
);
4063 if (isA_CFString(hostName
)) {
4064 CFStringRef connectionServiceID
= NULL
;
4065 SCNetworkConnectionStatus connectionStatus
= kSCNetworkConnectionInvalid
;
4066 Boolean onDemandRetry
;
4069 val
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandRetry
);
4070 onDemandRetry
= isA_CFBoolean(val
) ? CFBooleanGetValue(val
) : TRUE
;
4072 success
= __SCNetworkConnectionCopyOnDemandInfoWithName(NULL
,
4075 &connectionServiceID
,
4079 SCLog(TRUE
, LOG_DEBUG
,
4080 CFSTR("SCNetworkConnectionCopyUserPreferences __SCNetworkConnectionCopyOnDemandInfoWithName returns %d w/status %d"),
4086 // if the hostname matches an OnDemand domain
4087 if (connectionStatus
== kSCNetworkConnectionConnected
) {
4088 // if we are already connected
4089 if (connectionServiceID
!= NULL
) {
4090 CFRelease(connectionServiceID
);
4095 *serviceID
= connectionServiceID
;
4096 *userOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
4097 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
4099 } else if (!onDemandRetry
) {
4100 // if the hostname does not match an OnDemand domain and we have
4101 // not yet issued an initial DNS query (i.e. it's not a query
4102 // being retried after the VPN has been established) then we're
4109 return __SCNetworkConnectionCopyUserPreferencesInternal(selectionOptions
, serviceID
, userOptions
);
4114 SCNetworkConnectionOnDemandShouldRetryOnFailure (SCNetworkConnectionRef connection
)
4116 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
4117 CFDictionaryRef match_info
= NULL
;
4119 if (!isA_SCNetworkConnection(connection
)) {
4120 _SCErrorSet(kSCStatusInvalidArgument
);
4124 if (connectionPrivate
->service
== NULL
) {
4125 _SCErrorSet(kSCStatusConnectionNoService
);
4129 if (isA_CFDictionary(connectionPrivate
->on_demand_user_options
)) {
4130 match_info
= CFDictionaryGetValue(connectionPrivate
->on_demand_user_options
, kSCNetworkConnectionSelectionOptionOnDemandMatchInfo
);
4131 if (isA_CFDictionary(match_info
)) {
4132 CFBooleanRef onRetry
= CFDictionaryGetValue(match_info
, kSCNetworkConnectionOnDemandMatchInfoOnRetry
);
4133 if (isA_CFBoolean(onRetry
)) {
4134 return CFBooleanGetValue(onRetry
);
4144 // Mask is optional in routes dictionary; if not present, whole addresses are matched
4146 __SCNetworkConnectionIPv4AddressMatchesRoutes (struct sockaddr_in
*addr_in
, CFDictionaryRef routes
)
4150 CFDataRef maskData
= NULL
;
4151 struct in_addr
*maskDataArray
;
4152 CFDataRef routeaddrData
= NULL
;
4153 struct in_addr
*routeaddrDataArray
;
4155 if (!isA_CFDictionary(routes
)) {
4159 routeaddrData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoAddresses
);
4160 maskData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoMasks
);
4162 /* routeaddrData and maskData are packed arrays of addresses; make sure they have the same length */
4163 if (!isA_CFData(routeaddrData
) || (maskData
&& (!isA_CFData(maskData
) || CFDataGetLength(routeaddrData
) != CFDataGetLength(maskData
)))) {
4167 routeaddrDataArray
= (struct in_addr
*)(void*)CFDataGetBytePtr(routeaddrData
);
4169 maskDataArray
= (struct in_addr
*)(void*)CFDataGetBytePtr(maskData
);
4172 count
= CFDataGetLength(routeaddrData
) / sizeof(struct in_addr
);
4173 for (i
=0; i
<count
; i
++) {
4174 struct in_addr routeAddr
= *routeaddrDataArray
;
4177 struct in_addr mask
= *maskDataArray
;
4179 if ((addr_in
->sin_addr
.s_addr
& mask
.s_addr
) == (routeAddr
.s_addr
& mask
.s_addr
)) {
4184 if (addr_in
->sin_addr
.s_addr
== routeAddr
.s_addr
) {
4188 routeaddrDataArray
++;
4195 __SCNetworkConnectionMaskIPv6Address(struct in6_addr
*addr
, struct in6_addr
*mask
)
4199 for (i
= 0; i
< sizeof(struct in6_addr
); i
++)
4200 addr
->s6_addr
[i
] &= mask
->s6_addr
[i
];
4204 // Mask is optional in routes dictionary; if not present, whole addresses are matched
4206 __SCNetworkConnectionIPv6AddressMatchesRoutes (struct sockaddr_in6
*addr_in6
, CFDictionaryRef routes
)
4210 CFDataRef maskData
= NULL
;
4211 struct in6_addr
*maskDataArray
;
4212 CFDataRef routeaddrData
= NULL
;
4213 struct in6_addr
*routeaddrDataArray
;
4215 if (!isA_CFDictionary(routes
)) {
4219 routeaddrData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoAddresses
);
4220 maskData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoMasks
);
4222 /* routeaddrData and maskData are packed arrays of addresses; make sure they have the same length */
4223 if (!isA_CFData(routeaddrData
) || (maskData
&& (!isA_CFData(maskData
) || CFDataGetLength(routeaddrData
) != CFDataGetLength(maskData
)))) {
4227 routeaddrDataArray
= (struct in6_addr
*)(void*)CFDataGetBytePtr(routeaddrData
);
4229 maskDataArray
= (struct in6_addr
*)(void*)CFDataGetBytePtr(maskData
);
4232 count
= CFDataGetLength(routeaddrData
) / sizeof(struct in6_addr
);
4233 for (i
=0; i
<count
; i
++) {
4235 struct in6_addr cmpAddr
;
4236 struct in6_addr
*mask
= maskDataArray
;
4237 struct in6_addr routeAddr
;
4239 memcpy(&routeAddr
, routeaddrDataArray
, sizeof(routeAddr
));
4240 memcpy(&cmpAddr
, &addr_in6
->sin6_addr
, sizeof(cmpAddr
));
4241 __SCNetworkConnectionMaskIPv6Address(&routeAddr
, mask
);
4242 __SCNetworkConnectionMaskIPv6Address(&cmpAddr
, mask
);
4244 if (!memcmp(&routeAddr
, &cmpAddr
, sizeof(routeAddr
))) {
4248 if (!memcmp(routeaddrDataArray
, &addr_in6
->sin6_addr
, sizeof(struct in6_addr
))) {
4253 routeaddrDataArray
++;
4260 __SCNetworkConnectionAddressMatchesRedirectedDNS(CFDictionaryRef trigger
, const struct sockaddr
*input_addr
)
4262 CFBooleanRef redirectedRef
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandDNSRedirectDetected
);
4264 if (isA_CFBoolean(redirectedRef
) && CFBooleanGetValue(redirectedRef
)) {
4265 /* DNS is redirected. Look for address list. */
4266 CFDictionaryRef redirectedAddressesRef
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandDNSRedirectedAddresses
);
4268 if (isA_CFDictionary(redirectedAddressesRef
)) {
4269 if (input_addr
->sa_family
== AF_INET
) {
4270 return __SCNetworkConnectionIPv4AddressMatchesRoutes((struct sockaddr_in
*)(void*)input_addr
, CFDictionaryGetValue(redirectedAddressesRef
, kSCNetworkConnectionNetworkInfoIPv4
));
4271 } else if (input_addr
->sa_family
== AF_INET6
) {
4272 return __SCNetworkConnectionIPv6AddressMatchesRoutes((struct sockaddr_in6
*)(void*)input_addr
, CFDictionaryGetValue(redirectedAddressesRef
, kSCNetworkConnectionNetworkInfoIPv6
));
4280 /* If the required probe has failed, we need to tunnel the address. Equivalent to redirected DNS. */
4282 __SCNetworkConnectionRequiredProbeFailed (CFDictionaryRef trigger
, CFStringRef probeString
)
4284 CFDictionaryRef probeResults
= NULL
;
4286 if (!isA_CFString(probeString
)) {
4290 probeResults
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandProbeResults
);
4291 if (!isA_CFDictionary(probeResults
)) {
4295 CFBooleanRef result
= CFDictionaryGetValue(probeResults
, probeString
);
4297 /* Only a value of kCFBooleanFalse marks the probe as failed */
4298 return (isA_CFBoolean(result
) && !CFBooleanGetValue(result
));
4302 SCNetworkConnectionCanTunnelAddress (SCNetworkConnectionRef connection
, const struct sockaddr
*address
, Boolean
*startImmediately
)
4304 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
4305 CFStringRef serviceID
= NULL
;
4306 CFDictionaryRef configuration
= NULL
;
4307 CFDictionaryRef trigger
= NULL
;
4308 CFDictionaryRef tunneledNetworks
= NULL
;
4309 sa_family_t address_family
= AF_UNSPEC
;
4310 Boolean success
= FALSE
;
4312 if (startImmediately
) {
4313 *startImmediately
= FALSE
;
4316 if (address
== NULL
) {
4320 address_family
= address
->sa_family
;
4321 if (address_family
!= AF_INET
&& address_family
!= AF_INET6
) {
4325 if (!isA_SCNetworkConnection(connection
)) {
4326 _SCErrorSet(kSCStatusInvalidArgument
);
4330 if (connectionPrivate
->service
== NULL
) {
4331 _SCErrorSet(kSCStatusConnectionNoService
);
4335 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
4336 if (!isA_CFString(serviceID
)) {
4340 configuration
= __SCNetworkConnectionCopyOnDemandConfiguration();
4341 if (configuration
== NULL
) {
4345 trigger
= __SCNetworkConnectionCopyTriggerWithService(configuration
, serviceID
);
4346 if (trigger
== NULL
) {
4350 if (__SCNetworkConnectionRequiredProbeFailed(trigger
, connectionPrivate
->on_demand_required_probe
)) {
4351 /* If probe failed, we can't trust DNS - connect now */
4352 if (startImmediately
) {
4353 *startImmediately
= TRUE
;
4359 if (__SCNetworkConnectionAddressMatchesRedirectedDNS(trigger
, address
)) {
4360 if (startImmediately
) {
4361 *startImmediately
= TRUE
;
4367 tunneledNetworks
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandTunneledNetworks
);
4368 if (!isA_CFDictionary(tunneledNetworks
)) {
4372 if (address_family
== AF_INET
) {
4373 CFDictionaryRef ip_dict
;
4374 Boolean matches
= FALSE
;
4375 struct sockaddr_in
*addr_in
= (struct sockaddr_in
*)(void*)address
;
4377 ip_dict
= CFDictionaryGetValue(tunneledNetworks
, kSCNetworkConnectionNetworkInfoIPv4
);
4378 if (!isA_CFDictionary(ip_dict
)) {
4382 matches
= __SCNetworkConnectionIPv4AddressMatchesRoutes(addr_in
, CFDictionaryGetValue(ip_dict
, kSCNetworkConnectionNetworkInfoIncludedRoutes
));
4385 if (!__SCNetworkConnectionIPv4AddressMatchesRoutes(addr_in
, CFDictionaryGetValue(ip_dict
, kSCNetworkConnectionNetworkInfoExcludedRoutes
))) {
4391 CFDictionaryRef ip6_dict
;
4392 Boolean matches
= FALSE
;
4393 struct sockaddr_in6
*addr_in6
= (struct sockaddr_in6
*)(void*)address
;
4395 ip6_dict
= CFDictionaryGetValue(tunneledNetworks
, kSCNetworkConnectionNetworkInfoIPv6
);
4396 if (!isA_CFDictionary(ip6_dict
)) {
4400 matches
= __SCNetworkConnectionIPv6AddressMatchesRoutes(addr_in6
, CFDictionaryGetValue(ip6_dict
, kSCNetworkConnectionNetworkInfoIncludedRoutes
));
4403 if (!__SCNetworkConnectionIPv6AddressMatchesRoutes(addr_in6
, CFDictionaryGetValue(ip6_dict
, kSCNetworkConnectionNetworkInfoExcludedRoutes
))) {
4410 if (configuration
) {
4411 CFRelease(configuration
);
4420 SCNetworkConnectionSelectServiceWithOptions(SCNetworkConnectionRef connection
, CFDictionaryRef selectionOptions
)
4422 CFStringRef account_identifier
= NULL
;
4423 CFDictionaryRef configuration
= NULL
;
4424 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
4425 CFDictionaryRef found_trigger
= NULL
;
4426 CFStringRef host_name
= NULL
;
4427 Boolean is_retry
= TRUE
;
4428 CFDictionaryRef match_info
= NULL
;
4429 CFMutableDictionaryRef new_user_options
= NULL
;
4430 SCNetworkConnectionStatus on_demand_status
= kSCNetworkConnectionInvalid
;
4431 CFStringRef requiredProbe
= NULL
;
4432 CFStringRef service_id
= NULL
;
4433 Boolean skip_prefs
= FALSE
;
4434 Boolean success
= TRUE
;
4435 CFDictionaryRef user_options
= NULL
;
4437 if (!isA_SCNetworkConnection(connection
)) {
4438 _SCErrorSet(kSCStatusInvalidArgument
);
4443 /* Can't call this on a connection that is already associated with a service */
4444 if (connectionPrivate
->service
!= NULL
) {
4445 _SCErrorSet(kSCStatusInvalidArgument
);
4450 if (isA_CFDictionary(selectionOptions
)) {
4451 CFBooleanRef no_user_prefs
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionNoUserPrefs
);
4452 CFBooleanRef retry
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandRetry
);
4454 account_identifier
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandAccountIdentifier
);
4455 host_name
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
);
4456 skip_prefs
= (isA_CFBoolean(no_user_prefs
) && CFBooleanGetValue(no_user_prefs
));
4458 if (isA_CFBoolean(retry
)) {
4459 is_retry
= CFBooleanGetValue(retry
);
4463 configuration
= __SCNetworkConnectionCopyOnDemandConfiguration();
4465 /* First, check for a match with the App Layer rules */
4466 service_id
= VPNAppLayerCopyMatchingService(connectionPrivate
->client_audit_token
,
4467 connectionPrivate
->client_pid
,
4468 connectionPrivate
->client_uuid
,
4469 connectionPrivate
->client_bundle_id
,
4473 if (service_id
!= NULL
) {
4474 Boolean use_app_layer
= TRUE
;
4476 if (isA_CFDictionary(configuration
)) {
4477 found_trigger
= __SCNetworkConnectionCopyTriggerWithService(configuration
, service_id
);
4478 if (found_trigger
!= NULL
) {
4479 CFNumberRef status_num
= CFDictionaryGetValue(found_trigger
, kSCNetworkConnectionOnDemandStatus
);
4480 if (isA_CFNumber(status_num
)) {
4481 CFNumberGetValue(status_num
, kCFNumberIntType
, &on_demand_status
);
4484 * If the trigger should be ignored, still use App Layer VPN if it is already connected or
4485 * is in the process of connecting.
4487 if (__SCNetworkConnectionShouldIgnoreTrigger(found_trigger
) &&
4488 on_demand_status
!= kSCNetworkConnectionConnecting
&&
4489 on_demand_status
!= kSCNetworkConnectionConnected
)
4491 use_app_layer
= FALSE
;
4496 if (use_app_layer
) {
4497 /* If this is not the 'OnRetry' call, and the service has not yet started, the match may need to return false */
4499 match_info
!= NULL
&&
4500 on_demand_status
!= kSCNetworkConnectionConnecting
&&
4501 on_demand_status
!= kSCNetworkConnectionConnected
) {
4502 CFBooleanRef matchedOnRetry
= CFDictionaryGetValue(match_info
, kSCNetworkConnectionOnDemandMatchInfoOnRetry
);
4503 if (matchedOnRetry
&& CFBooleanGetValue(matchedOnRetry
)) {
4504 /* Don't return that we matched always; wait for SCNetworkConnectionOnDemandShouldRetryOnFailure */
4508 connectionPrivate
->type
= kSCNetworkConnectionTypeAppLayerVPN
;
4511 CFRelease(service_id
);
4513 if (match_info
!= NULL
) {
4514 CFRelease(match_info
);
4517 if (found_trigger
!= NULL
) {
4518 CFRelease(found_trigger
);
4519 found_trigger
= NULL
;
4524 /* Next, check the IP layer rules */
4525 if (isA_CFDictionary(configuration
) && host_name
!= NULL
) {
4526 Boolean triggerNow
= FALSE
;
4528 found_trigger
= __SCNetworkConnectionCopyMatchingTriggerWithName(configuration
, host_name
, connectionPrivate
->client_pid
, is_retry
, &match_info
, &triggerNow
, &requiredProbe
);
4529 if (found_trigger
!= NULL
) {
4530 service_id
= CFDictionaryGetValue(found_trigger
, kSCNetworkConnectionOnDemandServiceID
);
4531 if (isA_CFString(service_id
)) {
4532 CFRetain(service_id
);
4533 connectionPrivate
->type
= kSCNetworkConnectionTypeIPLayerVPN
;
4541 } else if (!is_retry
) {
4545 if (match_info
!= NULL
) {
4546 CFRelease(match_info
);
4551 /* Next, check the user preferences */
4552 if (!skip_prefs
&& __SCNetworkConnectionCopyUserPreferencesInternal(selectionOptions
, &service_id
, &user_options
)) {
4553 CFMutableDictionaryRef minfo
;
4554 CFNumberRef type_num
;
4556 if (isA_CFDictionary(configuration
)) {
4557 found_trigger
= __SCNetworkConnectionCopyTriggerWithService(configuration
, service_id
);
4559 connectionPrivate
->type
= kSCNetworkConnectionTypePPP
;
4561 minfo
= CFDictionaryCreateMutable(NULL
,
4563 &kCFTypeDictionaryKeyCallBacks
,
4564 &kCFTypeDictionaryValueCallBacks
);
4565 type_num
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &connectionPrivate
->type
);
4566 CFDictionarySetValue(minfo
, kSCNetworkConnectionOnDemandMatchInfoVPNType
, type_num
);
4567 CFRelease(type_num
);
4573 if (service_id
== NULL
) {
4574 _SCErrorSet(kSCStatusOK
);
4579 connectionPrivate
->service
= _SCNetworkServiceCopyActive(NULL
, service_id
);
4580 if (connectionPrivate
->service
== NULL
) {
4581 _SCErrorSet(kSCStatusOK
);
4586 if (found_trigger
!= NULL
) {
4587 if (connectionPrivate
->on_demand_info
) {
4588 CFRelease(connectionPrivate
->on_demand_info
);
4590 connectionPrivate
->on_demand_info
= found_trigger
;
4591 CFRetain(connectionPrivate
->on_demand_info
);
4593 if (on_demand_status
== kSCNetworkConnectionInvalid
) {
4594 CFNumberRef status_num
= CFDictionaryGetValue(found_trigger
, kSCNetworkConnectionOnDemandStatus
);
4595 if (isA_CFNumber(status_num
)) {
4596 CFNumberGetValue(status_num
, kCFNumberIntType
, &on_demand_status
);
4600 if (on_demand_status
!= kSCNetworkConnectionConnected
) {
4601 if (connectionPrivate
->type
== kSCNetworkConnectionTypeAppLayerVPN
) {
4602 /* Check App Layer OnDemand flag */
4603 CFBooleanRef app_on_demand_enabled
=
4604 CFDictionaryGetValue(found_trigger
, kSCNetworkConnectionOnDemandMatchAppEnabled
);
4605 if (isA_CFBoolean(app_on_demand_enabled
) && CFBooleanGetValue(app_on_demand_enabled
)) {
4606 connectionPrivate
->on_demand
= TRUE
;
4609 connectionPrivate
->on_demand
= TRUE
;
4612 } else if (connectionPrivate
->type
== kSCNetworkConnectionTypePPP
) {
4613 /* If we got the service from __SCNetworkConnectionCopyUserPreferencesInternal, then it's on demand */
4614 connectionPrivate
->on_demand
= TRUE
;
4617 if (user_options
== NULL
) {
4618 new_user_options
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
4620 &kCFTypeDictionaryKeyCallBacks
,
4621 &kCFTypeDictionaryValueCallBacks
);
4623 new_user_options
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, user_options
);
4626 if (host_name
!= NULL
) {
4627 CFDictionarySetValue(new_user_options
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, host_name
);
4630 if (connectionPrivate
->on_demand
&& match_info
!= NULL
) {
4631 CFDictionarySetValue(new_user_options
, kSCNetworkConnectionSelectionOptionOnDemandMatchInfo
, match_info
);
4634 connectionPrivate
->on_demand_user_options
= new_user_options
;
4635 CFRetain(connectionPrivate
->on_demand_user_options
);
4637 if (requiredProbe
) {
4638 connectionPrivate
->on_demand_required_probe
= requiredProbe
;
4639 CFRetain(connectionPrivate
->on_demand_required_probe
);
4643 if (service_id
!= NULL
) {
4644 CFRelease(service_id
);
4647 if (configuration
!= NULL
) {
4648 CFRelease(configuration
);
4651 if (found_trigger
!= NULL
) {
4652 CFRelease(found_trigger
);
4655 if (user_options
!= NULL
) {
4656 CFRelease(user_options
);
4659 if (new_user_options
!= NULL
) {
4660 CFRelease(new_user_options
);
4663 if (match_info
!= NULL
) {
4664 CFRelease(match_info
);
4667 if (requiredProbe
!= NULL
) {
4668 CFRelease(requiredProbe
);
4674 //*******************************************************************************************
4675 // SCNetworkConnectionPrivateCopyDefaultServiceIDForDial
4676 // ----------------------------------------------------
4677 // Try to find the service id to connect
4678 // (1) Start by looking at the last service used in Network Pref / Network menu extra
4679 // (2) If Network Pref / Network menu extra has not been used, find the PPP service
4680 // with the highest ordering
4681 //********************************************************************************************
4683 SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(CFStringRef
*serviceID
)
4685 Boolean foundService
= FALSE
;
4686 CFPropertyListRef lastServiceSelectedInIC
= NULL
;
4690 // we found the service the user last had open in IC
4691 if (lastServiceSelectedInIC
!= NULL
) {
4692 // make sure its a PPP service
4693 if (SCNetworkConnectionPrivateIsPPPService(lastServiceSelectedInIC
, kSCValNetInterfaceSubTypePPPSerial
, kSCValNetInterfaceSubTypePPPoE
)) {
4694 // make sure the service that we found is valid
4695 CFDictionaryRef dict
;
4698 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4699 kSCDynamicStoreDomainSetup
,
4700 lastServiceSelectedInIC
,
4701 kSCEntNetInterface
);
4702 dict
= SCDynamicStoreCopyValue(NULL
, key
);
4706 *serviceID
= CFRetain(lastServiceSelectedInIC
);
4707 foundService
= TRUE
;
4710 CFRelease(lastServiceSelectedInIC
);
4713 if (!foundService
) {
4714 foundService
= SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(serviceID
);
4717 return foundService
;
4720 //********************************************************************************
4721 // SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore
4722 // -------------------------------------------------------
4723 // Find the highest ordered PPP service in the dynamic store
4724 //********************************************************************************
4726 SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(CFStringRef
*serviceID
)
4728 CFDictionaryRef dict
= NULL
;
4729 CFStringRef key
= NULL
;
4730 CFArrayRef serviceIDs
= NULL
;
4731 Boolean success
= FALSE
;
4739 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, kSCDynamicStoreDomainSetup
, kSCEntNetIPv4
);
4741 fprintf(stderr
, "Error, Setup Key == NULL!\n");
4745 dict
= SCDynamicStoreCopyValue(NULL
, key
);
4746 if (!isA_CFDictionary(dict
)) {
4747 fprintf(stderr
, "no global IPv4 entity\n");
4751 serviceIDs
= CFDictionaryGetValue(dict
, kSCPropNetServiceOrder
); // array of service id's
4752 if (!isA_CFArray(serviceIDs
)) {
4753 fprintf(stderr
, "service order not specified\n");
4757 count
= CFArrayGetCount(serviceIDs
);
4758 for (i
= 0; i
< count
; i
++) {
4759 CFStringRef service
= CFArrayGetValueAtIndex(serviceIDs
, i
);
4761 if (SCNetworkConnectionPrivateIsPPPService(service
, kSCValNetInterfaceSubTypePPPSerial
, kSCValNetInterfaceSubTypePPPoE
)) {
4762 *serviceID
= CFRetain(service
);
4769 if (key
!= NULL
) CFRelease(key
);
4770 if (dict
!= NULL
) CFRelease(dict
);
4775 //********************************************************************************
4776 // SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray
4777 // ---------------------------------------------------------
4778 // Copy over user preferences for a particular service if they exist
4779 //********************************************************************************
4781 SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray
, CFDictionaryRef
*userOptions
)
4783 CFIndex count
= CFArrayGetCount(userOptionsArray
);
4786 for (i
= 0; i
< count
; i
++) {
4787 // (1) Find the dictionary
4788 CFPropertyListRef propertyList
= CFArrayGetValueAtIndex(userOptionsArray
, i
);
4790 if (isA_CFDictionary(propertyList
) != NULL
) {
4791 // See if there's a value for dial on demand
4792 CFPropertyListRef value
;
4794 value
= CFDictionaryGetValue((CFDictionaryRef
)propertyList
, k_Dial_Default_Key
);
4795 if (isA_CFBoolean(value
) != NULL
) {
4796 if (CFBooleanGetValue(value
)) {
4797 // we found the default user options
4798 *userOptions
= CFDictionaryCreateCopy(NULL
,
4799 (CFDictionaryRef
)propertyList
);
4809 //********************************************************************************
4810 // SCNetworkConnectionPrivateIsServiceType
4811 // --------------------------------------
4812 // Check and see if the service is a PPP service of the given types
4813 //********************************************************************************
4815 SCNetworkConnectionPrivateIsPPPService(CFStringRef serviceID
, CFStringRef subType1
, CFStringRef subType2
)
4817 CFStringRef entityKey
;
4818 Boolean isPPPService
= FALSE
;
4819 Boolean isMatchingSubType
= FALSE
;
4820 CFDictionaryRef serviceDict
;
4822 entityKey
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4823 kSCDynamicStoreDomainSetup
,
4825 kSCEntNetInterface
);
4826 if (entityKey
== NULL
) {
4830 serviceDict
= SCDynamicStoreCopyValue(NULL
, entityKey
);
4831 if (serviceDict
!= NULL
) {
4832 if (isA_CFDictionary(serviceDict
)) {
4834 CFStringRef subtype
;
4836 type
= CFDictionaryGetValue(serviceDict
, kSCPropNetInterfaceType
);
4837 if (isA_CFString(type
)) {
4838 isPPPService
= CFEqual(type
, kSCValNetInterfaceTypePPP
);
4841 subtype
= CFDictionaryGetValue(serviceDict
, kSCPropNetInterfaceSubType
);
4842 if (isA_CFString(subtype
)) {
4843 isMatchingSubType
= CFEqual(subtype
, subType1
);
4844 if (!isMatchingSubType
&& subType2
)
4845 isMatchingSubType
= CFEqual(subtype
, subType2
);
4848 CFRelease(serviceDict
);
4850 CFRelease(entityKey
);
4852 return (isPPPService
&& isMatchingSubType
);
4855 //********************************************************************************
4856 // addPasswordFromKeychain
4857 // --------------------------------------
4858 // Get the password and shared secret out of the keychain and add
4859 // them to the PPP and IPSec dictionaries
4860 //********************************************************************************
4862 addPasswordFromKeychain(CFStringRef serviceID
, CFDictionaryRef
*userOptions
)
4864 CFPropertyListRef uniqueID
;
4865 CFStringRef password
;
4866 CFStringRef sharedsecret
= NULL
;
4868 /* user options must exist */
4869 if (*userOptions
== NULL
)
4872 /* first, get the unique identifier used to store passwords in the keychain */
4873 uniqueID
= CFDictionaryGetValue(*userOptions
, k_Unique_Id_Key
);
4874 if (!isA_CFString(uniqueID
))
4877 /* first, get the PPP password */
4878 password
= copyPasswordFromKeychain(uniqueID
);
4880 /* then, if necessary, get the IPSec Shared Secret */
4881 if (SCNetworkConnectionPrivateIsPPPService(serviceID
, kSCValNetInterfaceSubTypeL2TP
, 0)) {
4882 CFMutableStringRef uniqueIDSS
;
4884 uniqueIDSS
= CFStringCreateMutableCopy(NULL
, 0, uniqueID
);
4885 CFStringAppend(uniqueIDSS
, CFSTR(".SS"));
4886 sharedsecret
= copyPasswordFromKeychain(uniqueIDSS
);
4887 CFRelease(uniqueIDSS
);
4890 /* did we find our information in the key chain ? */
4891 if ((password
!= NULL
) || (sharedsecret
!= NULL
)) {
4892 CFMutableDictionaryRef newOptions
;
4894 newOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, *userOptions
);
4897 if (password
!= NULL
) {
4898 CFDictionaryRef entity
;
4899 CFMutableDictionaryRef newEntity
;
4901 entity
= CFDictionaryGetValue(*userOptions
, kSCEntNetPPP
);
4902 if (isA_CFDictionary(entity
))
4903 newEntity
= CFDictionaryCreateMutableCopy(NULL
, 0, entity
);
4905 newEntity
= CFDictionaryCreateMutable(NULL
,
4907 &kCFTypeDictionaryKeyCallBacks
,
4908 &kCFTypeDictionaryValueCallBacks
);
4911 /* set the PPP password */
4912 CFDictionarySetValue(newEntity
, kSCPropNetPPPAuthPassword
, uniqueID
);
4913 CFDictionarySetValue(newEntity
, kSCPropNetPPPAuthPasswordEncryption
, kSCValNetPPPAuthPasswordEncryptionKeychain
);
4914 CFRelease(password
);
4916 /* update the PPP entity */
4917 CFDictionarySetValue(newOptions
, kSCEntNetPPP
, newEntity
);
4918 CFRelease(newEntity
);
4921 /* IPSec Shared Secret */
4922 if (sharedsecret
!= NULL
) {
4923 CFDictionaryRef entity
;
4924 CFMutableDictionaryRef newEntity
;
4926 entity
= CFDictionaryGetValue(*userOptions
, kSCEntNetIPSec
);
4927 if (isA_CFDictionary(entity
))
4928 newEntity
= CFDictionaryCreateMutableCopy(NULL
, 0, entity
);
4930 newEntity
= CFDictionaryCreateMutable(NULL
,
4932 &kCFTypeDictionaryKeyCallBacks
,
4933 &kCFTypeDictionaryValueCallBacks
);
4935 /* set the IPSec Shared Secret */
4936 CFDictionarySetValue(newEntity
, kSCPropNetIPSecSharedSecret
, sharedsecret
);
4937 CFRelease(sharedsecret
);
4939 /* update the IPSec entity */
4940 CFDictionarySetValue(newOptions
, kSCEntNetIPSec
, newEntity
);
4941 CFRelease(newEntity
);
4944 /* update the userOptions dictionary */
4945 CFRelease(*userOptions
);
4946 *userOptions
= CFDictionaryCreateCopy(NULL
, newOptions
);
4947 CFRelease(newOptions
);
4952 #if !TARGET_OS_IPHONE
4953 //********************************************************************************
4954 // copyKeychainEnumerator
4955 // --------------------------------------
4956 // Gather Keychain Enumerator
4957 //********************************************************************************
4959 copyKeychainEnumerator(CFStringRef uniqueIdentifier
)
4961 CFArrayRef itemArray
= NULL
;
4962 CFMutableDictionaryRef query
;
4965 query
= CFDictionaryCreateMutable(NULL
,
4967 &kCFTypeDictionaryKeyCallBacks
,
4968 &kCFTypeDictionaryValueCallBacks
);
4969 CFDictionarySetValue(query
, kSecClass
, kSecClassGenericPassword
);
4970 CFDictionarySetValue(query
, kSecAttrService
, uniqueIdentifier
);
4971 CFDictionarySetValue(query
, kSecReturnRef
, kCFBooleanTrue
);
4972 CFDictionarySetValue(query
, kSecMatchLimit
, kSecMatchLimitAll
);
4973 result
= SecItemCopyMatching(query
, (CFTypeRef
*)&itemArray
);
4975 if ((result
!= noErr
) && (itemArray
!= NULL
)) {
4976 CFRelease(itemArray
);
4982 #endif // !TARGET_OS_IPHONE
4984 //********************************************************************************
4985 // copyPasswordFromKeychain
4986 // --------------------------------------
4987 // Given a uniqueID, retrieve the password from the keychain
4988 //********************************************************************************
4990 copyPasswordFromKeychain(CFStringRef uniqueID
)
4992 #if !TARGET_OS_IPHONE
4993 CFArrayRef enumerator
;
4995 CFStringRef password
= NULL
;
4997 enumerator
= copyKeychainEnumerator(uniqueID
);
4998 if (enumerator
== NULL
) {
4999 return NULL
; // if no keychain enumerator
5002 n
= CFArrayGetCount(enumerator
);
5006 SecKeychainItemRef itemRef
;
5009 itemRef
= (SecKeychainItemRef
)CFArrayGetValueAtIndex(enumerator
, 0);
5010 result
= SecKeychainItemCopyContent(itemRef
, // itemRef
5014 (void *)&data
); // outData
5015 if ((result
== noErr
) && (data
!= NULL
) && (dataLen
> 0)) {
5016 password
= CFStringCreateWithBytes(NULL
, data
, dataLen
, kCFStringEncodingUTF8
, TRUE
);
5017 (void) SecKeychainItemFreeContent(NULL
, data
);
5022 CFRelease(enumerator
);
5025 #else // !TARGET_OS_IPHONE
5027 #endif // !TARGET_OS_IPHONE
5033 __SCNetworkConnectionGetControllerPortName(void)
5035 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 70000)) && !TARGET_IPHONE_SIMULATOR
5036 if (scnc_server_name
== NULL
){
5037 if (!(sandbox_check(getpid(), "mach-lookup", SANDBOX_FILTER_GLOBAL_NAME
| SANDBOX_CHECK_NO_REPORT
, PPPCONTROLLER_SERVER_PRIV
))){
5038 scnc_server_name
= PPPCONTROLLER_SERVER_PRIV
;
5041 scnc_server_name
= PPPCONTROLLER_SERVER
;
5043 SCLog(TRUE
, LOG_DEBUG
, CFSTR("__SCNetworkConnectionGetControllerPortName() returns port: %s"), scnc_server_name
);
5046 scnc_server_name
= PPPCONTROLLER_SERVER
;
5048 return scnc_server_name
;