2 * Copyright (c) 2003-2015 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 __SCNetworkConnectionNotify(SCNetworkConnectionRef connection
,
384 SCNetworkConnectionCallBack rlsFunction
,
385 SCNetworkConnectionStatus nc_status
,
386 void (*context_release
)(const void *),
389 os_activity_t activity_id
;
391 activity_id
= os_activity_start("processing SCNetworkConnection notification",
392 OS_ACTIVITY_FLAG_DEFAULT
);
393 (*rlsFunction
)(connection
, nc_status
, context_info
);
394 if ((context_release
!= NULL
) && (context_info
!= NULL
)) {
395 (*context_release
)(context_info
);
397 os_activity_end(activity_id
);
404 __SCNetworkConnectionCallBackRunLoopPerform(SCNetworkConnectionRef connection
,
407 SCNetworkConnectionCallBack rlsFunction
,
408 void (*context_release
)(const void *),
411 SCNetworkConnectionStatus nc_status
;
413 nc_status
= SCNetworkConnectionGetStatus(connection
);
414 CFRunLoopPerformBlock(rl
, rl_mode
, ^{
415 __SCNetworkConnectionNotify(connection
, rlsFunction
, nc_status
, context_release
, context_info
);
416 CFRelease(connection
);
423 __SCNetworkConnectionCallBackDispatchPerform(SCNetworkConnectionRef connection
,
425 SCNetworkConnectionCallBack rlsFunction
,
426 void (*context_release
)(const void *),
429 SCNetworkConnectionStatus nc_status
;
431 nc_status
= SCNetworkConnectionGetStatus(connection
);
433 __SCNetworkConnectionNotify(connection
, rlsFunction
, nc_status
, context_release
, context_info
);
434 CFRelease(connection
);
441 __SCNetworkConnectionCallBack(void *connection
)
443 boolean_t exec_async
= FALSE
;
444 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
446 void (*context_release
)(const void *);
447 CFRunLoopRef rl
= NULL
;
449 SCNetworkConnectionCallBack rlsFunction
= NULL
;
450 dispatch_queue_t q
= NULL
;
451 SCNetworkConnectionStatus nc_status
= kSCNetworkConnectionInvalid
;
453 pthread_mutex_lock(&connectionPrivate
->lock
);
455 if (!connectionPrivate
->scheduled
) {
456 // if not currently scheduled
457 pthread_mutex_unlock(&connectionPrivate
->lock
);
461 rlsFunction
= connectionPrivate
->rlsFunction
;
462 if (rlsFunction
== NULL
) {
463 pthread_mutex_unlock(&connectionPrivate
->lock
);
467 if ((connectionPrivate
->rlsContext
.retain
!= NULL
) && (connectionPrivate
->rlsContext
.info
!= NULL
)) {
468 context_info
= (void *)(*connectionPrivate
->rlsContext
.retain
)(connectionPrivate
->rlsContext
.info
);
469 context_release
= connectionPrivate
->rlsContext
.release
;
471 context_info
= connectionPrivate
->rlsContext
.info
;
472 context_release
= NULL
;
475 #if !TARGET_IPHONE_SIMULATOR
476 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
477 pthread_mutex_unlock(&connectionPrivate
->lock
);
479 nc_status
= SCNetworkConnectionGetStatus(connection
);
480 __SCNetworkConnectionNotify(connection
, rlsFunction
, nc_status
, context_release
, context_info
);
481 CFRelease(connection
); /* This releases the reference that we took in the NESessionEventStatusChanged event handler */
484 #endif /* !TARGET_IPHONE_SIMULATOR */
486 // Do we need to spin a new thread? (either we are running on the main
487 // dispatch queue or main runloop)
488 if (connectionPrivate
->rlList
== NULL
) {
489 // if we are performing the callback on a dispatch queue
490 q
= connectionPrivate
->dispatchQueue
;
491 if (q
== dispatch_get_main_queue()) {
495 rl
= CFRunLoopGetCurrent();
496 if (rl
== CFRunLoopGetMain()) {
501 CFRetain(connection
);
502 pthread_mutex_unlock(&connectionPrivate
->lock
);
505 nc_status
= SCNetworkConnectionGetStatus(connection
);
506 __SCNetworkConnectionNotify(connection
, rlsFunction
, nc_status
, context_release
, context_info
);
507 CFRelease(connection
);
511 if (connectionPrivate
->rlList
== NULL
) {
514 dispatch_async(__SCNetworkConnectionQueue(), ^{
515 __SCNetworkConnectionCallBackDispatchPerform(connection
,
525 rl_mode
= CFRunLoopCopyCurrentMode(rl
);
526 dispatch_async(__SCNetworkConnectionQueue(), ^{
527 __SCNetworkConnectionCallBackRunLoopPerform(connection
,
543 __SCNetworkConnectionMachCallBack(CFMachPortRef port
, void * msg
, CFIndex size
, void * info
)
545 mach_no_senders_notification_t
*buf
= msg
;
546 mach_msg_id_t msgid
= buf
->not_header
.msgh_id
;
547 SCNetworkConnectionRef connection
= (SCNetworkConnectionRef
)info
;
549 if (msgid
== MACH_NOTIFY_NO_SENDERS
) {
550 // re-establish notification
551 SC_log(LOG_INFO
, "PPPController server died");
552 (void)__SCNetworkConnectionReconnectNotifications(connection
);
555 __SCNetworkConnectionCallBack(info
);
562 #pragma mark SCNetworkConnection APIs
566 pppMPCopyDescription(const void *info
)
568 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)info
;
570 return CFStringCreateWithFormat(NULL
,
572 CFSTR("<SCNetworkConnection MP %p> {service = %@, callout = %p}"),
574 connectionPrivate
->service
,
575 connectionPrivate
->rlsFunction
);
579 static SCNetworkConnectionPrivateRef
580 __SCNetworkConnectionCreatePrivate(CFAllocatorRef allocator
,
581 SCNetworkServiceRef service
,
582 SCNetworkConnectionCallBack callout
,
583 SCNetworkConnectionContext
*context
)
585 SCNetworkConnectionPrivateRef connectionPrivate
= NULL
;
589 /* initialize runtime */
590 pthread_once(&initialized
, __SCNetworkConnectionInitialize
);
592 /* allocate NetworkConnection */
593 size
= sizeof(SCNetworkConnectionPrivate
) - sizeof(CFRuntimeBase
);
594 connectionPrivate
= (SCNetworkConnectionPrivateRef
)_CFRuntimeCreateInstance(allocator
, __kSCNetworkConnectionTypeID
, size
, NULL
);
595 if (connectionPrivate
== NULL
) {
599 /* zero the data structure */
600 bzero(((u_char
*)connectionPrivate
)+sizeof(CFRuntimeBase
), size
);
602 pthread_mutex_init(&connectionPrivate
->lock
, NULL
);
604 /* save the service */
605 if (service
!= NULL
) {
606 connectionPrivate
->service
= CFRetain(service
);
609 connectionPrivate
->client_audit_session
= MACH_PORT_NULL
;
610 connectionPrivate
->client_bootstrap_port
= MACH_PORT_NULL
;
611 connectionPrivate
->client_uid
= geteuid();
612 connectionPrivate
->client_gid
= getegid();
613 connectionPrivate
->client_pid
= getpid();
614 connectionPrivate
->client_bundle_id
= NULL
;
615 uuid_clear(connectionPrivate
->client_uuid
);
617 connectionPrivate
->rlsFunction
= callout
;
620 bcopy(context
, &connectionPrivate
->rlsContext
, sizeof(SCNetworkConnectionContext
));
621 if (context
->retain
!= NULL
) {
622 connectionPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
626 connectionPrivate
->type
= kSCNetworkConnectionTypeUnknown
;
628 #if !TARGET_IPHONE_SIMULATOR
629 if (__SCNetworkConnectionUseNetworkExtension(connectionPrivate
)) {
630 CFStringRef serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
631 if (serviceID
!= NULL
) {
632 uuid_string_t service_uuid_str
;
633 if (CFStringGetCString(serviceID
, service_uuid_str
, sizeof(service_uuid_str
), kCFStringEncodingUTF8
)) {
635 if (uuid_parse(service_uuid_str
, config_id
) == 0) {
636 connectionPrivate
->ne_session
= ne_session_create(config_id
, NESessionTypeVPN
);
641 if (connectionPrivate
->ne_session
== NULL
) {
643 "SCNetworkConnection failed to create an ne_session: service ID %@ is not a valid UUID",
648 #endif /* !TARGET_IPHONE_SIMULATOR */
650 /* success, return the connection reference */
651 return connectionPrivate
;
655 /* failure, clean up and leave */
656 if (connectionPrivate
!= NULL
) {
657 CFRelease(connectionPrivate
);
660 _SCErrorSet(kSCStatusFailed
);
666 __SCNetworkConnectionServerPort(kern_return_t
*status
)
668 mach_port_t server
= MACH_PORT_NULL
;
670 #ifdef BOOTSTRAP_PRIVILEGED_SERVER
671 *status
= bootstrap_look_up2(bootstrap_port
,
672 __SCNetworkConnectionGetControllerPortName(),
675 BOOTSTRAP_PRIVILEGED_SERVER
);
676 #else // BOOTSTRAP_PRIVILEGED_SERVER
677 *status
= bootstrap_look_up(bootstrap_port
, __SCNetworkConnectionGetControllerPortName(), &server
);
678 #endif // BOOTSTRAP_PRIVILEGED_SERVER
681 case BOOTSTRAP_SUCCESS
:
682 // service currently registered, "a good thing" (tm)
684 case BOOTSTRAP_NOT_PRIVILEGED
:
685 // the service is not privileged
687 case BOOTSTRAP_UNKNOWN_SERVICE
:
688 // service not currently registered, try again later
692 SC_log(LOG_DEBUG
, "bootstrap_look_up() failed: status=%s",
693 bootstrap_strerror(*status
));
698 scnc_server_name
= NULL
; /* reset pppcontroller server */
699 return MACH_PORT_NULL
;
703 __SCNetworkConnectionGetCurrentServerPort(void)
709 __SCNetworkConnectionRefreshServerPort(mach_port_t current_server
, int *mach_result
)
711 mach_port_t new_server
;
713 pthread_mutex_lock(&scnc_lock
);
714 if (scnc_server
!= MACH_PORT_NULL
) {
715 if (current_server
== scnc_server
) {
716 scnc_server_name
= NULL
;
717 // if the server we tried returned the error
718 (void)mach_port_deallocate(mach_task_self(), scnc_server
);
719 scnc_server
= __SCNetworkConnectionServerPort(mach_result
);
721 // another thread has refreshed the server port
724 scnc_server
= __SCNetworkConnectionServerPort(mach_result
);
726 new_server
= scnc_server
;
727 pthread_mutex_unlock(&scnc_lock
);
732 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && (!TARGET_IPHONE_SIMULATOR || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 70000))
733 #define HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
737 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && !TARGET_IPHONE_SIMULATOR
738 #define HAVE_PPPCONTROLLER_ATTACHWITHPROXY
742 __SCNetworkConnectionSessionPort(SCNetworkConnectionPrivateRef connectionPrivate
)
746 CFDataRef dataRef
= NULL
;
747 mach_port_t notify_port
= MACH_PORT_NULL
;
748 mach_port_t oldNotify
= MACH_PORT_NULL
;
750 int sc_status
= kSCStatusFailed
;
751 mach_port_t server
= __SCNetworkConnectionGetCurrentServerPort();
752 kern_return_t status
= KERN_SUCCESS
;
754 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
755 mach_port_t au_session
= MACH_PORT_NULL
;
756 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
758 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
759 return connectionPrivate
->session_port
;
762 if (connectionPrivate
->service
== NULL
) {
763 sc_status
= kSCStatusConnectionNoService
;
767 if (!_SCSerializeString(SCNetworkServiceGetServiceID(connectionPrivate
->service
), &dataRef
, &data
, &dataLen
)) {
771 if (connectionPrivate
->notify_port
!= NULL
) {
772 mach_port_t mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
774 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionSessionPort mp", mp
);
775 CFMachPortInvalidate(connectionPrivate
->notify_port
);
776 CFRelease(connectionPrivate
->notify_port
);
777 connectionPrivate
->notify_port
= NULL
;
778 mach_port_mod_refs(mach_task_self(), mp
, MACH_PORT_RIGHT_RECEIVE
, -1);
781 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
782 au_session
= audit_session_self();
783 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
785 // open a new session with the server
787 if ((connectionPrivate
->rlsFunction
!= NULL
) && (notify_port
== MACH_PORT_NULL
)) {
788 // allocate port (for server response)
789 status
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE
, ¬ify_port
);
790 if (status
!= KERN_SUCCESS
) {
791 SC_log(LOG_ERR
, "mach_port_allocate() failed: %s", mach_error_string(status
));
796 // add send right (passed to the server)
797 status
= mach_port_insert_right(mach_task_self(),
800 MACH_MSG_TYPE_MAKE_SEND
);
801 if (status
!= KERN_SUCCESS
) {
802 SC_log(LOG_NOTICE
, "mach_port_insert_right() failed: %s", mach_error_string(status
));
803 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
809 if (server
!= MACH_PORT_NULL
) {
810 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHPROXY
811 if ((connectionPrivate
->client_audit_session
== MACH_PORT_NULL
) &&
812 (connectionPrivate
->client_bootstrap_port
== MACH_PORT_NULL
) &&
813 (connectionPrivate
->client_uid
== geteuid()) &&
814 (connectionPrivate
->client_gid
== getegid()) &&
815 (connectionPrivate
->client_pid
== getpid())
817 #endif // HAVE_PPPCONTROLLER_ATTACHWITHPROXY
818 status
= pppcontroller_attach(server
,
820 (mach_msg_type_number_t
)dataLen
,
823 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
825 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
826 &connectionPrivate
->session_port
,
828 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHPROXY
830 mach_port_t client_au_session
;
831 mach_port_t client_bootstrap_port
;
833 if (connectionPrivate
->client_audit_session
== MACH_PORT_NULL
) {
834 client_au_session
= au_session
;
836 client_au_session
= connectionPrivate
->client_audit_session
;
839 if (connectionPrivate
->client_bootstrap_port
== MACH_PORT_NULL
) {
840 client_bootstrap_port
= bootstrap_port
;
842 client_bootstrap_port
= connectionPrivate
->client_bootstrap_port
;
845 status
= pppcontroller_attach_proxy(server
,
847 (mach_msg_type_number_t
)dataLen
,
848 client_bootstrap_port
,
851 connectionPrivate
->client_uid
,
852 connectionPrivate
->client_gid
,
853 connectionPrivate
->client_pid
,
854 &connectionPrivate
->session_port
,
857 #endif // HAVE_PPPCONTROLLER_ATTACHWITHPROXY
858 if (status
== KERN_SUCCESS
) {
859 if (sc_status
!= kSCStatusOK
) {
860 SC_log(LOG_DEBUG
, "attach w/error, sc_status=%s%s",
861 SCErrorString(sc_status
),
862 (connectionPrivate
->session_port
!= MACH_PORT_NULL
) ? ", w/session_port!=MACH_PORT_NULL" : "");
864 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
865 __MACH_PORT_DEBUG(TRUE
,
866 "*** __SCNetworkConnectionSessionPort session_port (attach w/error, cleanup)",
867 connectionPrivate
->session_port
);
868 mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
869 connectionPrivate
->session_port
= MACH_PORT_NULL
;
872 if (notify_port
!= MACH_PORT_NULL
) {
873 __MACH_PORT_DEBUG(TRUE
,
874 "*** __SCNetworkConnectionSessionPort notify_port (attach w/error, cleanup)",
876 (void) mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
877 notify_port
= MACH_PORT_NULL
;
883 // our [cached] server port is not valid
884 SC_log(LOG_INFO
, "!attach: %s", SCErrorString(status
));
885 if (status
== MACH_SEND_INVALID_DEST
) {
886 // the server is not yet available
887 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionSessionPort notify_port (!dest)", notify_port
);
888 } else if (status
== MIG_SERVER_DIED
) {
889 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionSessionPort notify_port (!mig)", notify_port
);
890 // the server we were using is gone and we've lost our send right
891 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
892 notify_port
= MACH_PORT_NULL
;
894 // if we got an unexpected error, don't retry
900 server
= __SCNetworkConnectionRefreshServerPort(server
, &sc_status
);
901 if (server
== MACH_PORT_NULL
) {
902 // if server not available
903 if (sc_status
== BOOTSTRAP_UNKNOWN_SERVICE
) {
904 // if first retry attempt, wait for SCDynamicStore server
906 SCDynamicStoreRef store
;
908 store
= SCDynamicStoreCreate(NULL
,
909 CFSTR("SCNetworkConnection connect"),
917 // wait up to 2.5 seconds for the [SCNetworkConnection] server
919 if ((retry
+= 50) < 2500) {
920 usleep(50 * 1000); // sleep 50ms between attempts
928 if (notify_port
!= MACH_PORT_NULL
) {
929 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
930 CFMachPortContext context
= { 0
931 , (void *)connectionPrivate
934 , pppMPCopyDescription
937 // request a notification when/if the server dies
938 status
= mach_port_request_notification(mach_task_self(),
940 MACH_NOTIFY_NO_SENDERS
,
943 MACH_MSG_TYPE_MAKE_SEND_ONCE
,
945 if (status
!= KERN_SUCCESS
) {
946 SC_log(LOG_NOTICE
, "mach_port_request_notification() failed: %s", mach_error_string(status
));
947 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
952 if (oldNotify
!= MACH_PORT_NULL
) {
953 SC_log(LOG_NOTICE
, "oldNotify != MACH_PORT_NULL");
956 // create CFMachPort for SCNetworkConnection notification callback
957 connectionPrivate
->notify_port
= _SC_CFMachPortCreateWithPort("SCNetworkConnection",
959 __SCNetworkConnectionMachCallBack
,
962 // we need to try a bit harder to acquire the initial status
963 connectionPrivate
->haveStatus
= FALSE
;
965 // with no server port, release the notification port we allocated
966 __MACH_PORT_DEBUG(TRUE
,
967 "*** __SCNetworkConnectionSessionPort notify_port (!server)",
969 (void) mach_port_mod_refs (mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
970 (void) mach_port_deallocate(mach_task_self(), notify_port
);
971 notify_port
= MACH_PORT_NULL
;
979 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
980 if (au_session
!= MACH_PORT_NULL
) {
981 (void)mach_port_deallocate(mach_task_self(), au_session
);
983 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
985 if (dataRef
!= NULL
) CFRelease(dataRef
);
989 __MACH_PORT_DEBUG(connectionPrivate
->session_port
!= MACH_PORT_NULL
,
990 "*** __SCNetworkConnectionSessionPort session_port",
991 connectionPrivate
->session_port
);
992 __MACH_PORT_DEBUG(notify_port
!= MACH_PORT_NULL
,
993 "*** __SCNetworkConnectionSessionPort notify_port",
996 case BOOTSTRAP_UNKNOWN_SERVICE
:
997 SC_log((status
== KERN_SUCCESS
) ? LOG_NOTICE
: LOG_ERR
, "PPPController not available");
1000 SC_log((status
== KERN_SUCCESS
) ? LOG_NOTICE
: LOG_ERR
, "pppcontroller_attach() failed: %s",
1001 SCErrorString(sc_status
));
1005 if (sc_status
!= kSCStatusOK
) {
1006 _SCErrorSet(sc_status
);
1009 return connectionPrivate
->session_port
;
1014 __SCNetworkConnectionReconnect(SCNetworkConnectionRef connection
)
1016 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1019 port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1020 return (port
!= MACH_PORT_NULL
);
1025 __SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection
)
1027 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1028 dispatch_group_t dispatchGroup
= NULL
;
1029 dispatch_queue_t dispatchQueue
= NULL
;
1031 CFArrayRef rlList
= NULL
;
1033 // Before we fully tearing down our [old] notifications, make sure
1034 // we have retained any information that is needed to re-register the
1035 // [new] notifications.
1037 pthread_mutex_lock(&connectionPrivate
->lock
);
1039 // save and cancel [old] notifications
1040 if (connectionPrivate
->rlList
!= NULL
) {
1041 rlList
= connectionPrivate
->rlList
;
1042 connectionPrivate
->rlList
= NULL
;
1044 if (connectionPrivate
->rls
!= NULL
) {
1045 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
1046 CFRelease(connectionPrivate
->rls
);
1047 connectionPrivate
->rls
= NULL
;
1049 if (connectionPrivate
->dispatchSource
!= NULL
) {
1050 dispatch_source_cancel(connectionPrivate
->dispatchSource
);
1051 connectionPrivate
->dispatchSource
= NULL
;
1054 // make sure dispatchSource is cancelled before removing group/queue
1055 if (connectionPrivate
->dispatchQueue
!= NULL
) {
1056 // save dispatchQueue, release reference when we've queue'd blocks
1057 // complete, allow re-scheduling
1058 dispatchGroup
= connectionPrivate
->dispatchGroup
;
1059 connectionPrivate
->dispatchGroup
= NULL
;
1060 dispatchQueue
= connectionPrivate
->dispatchQueue
;
1061 connectionPrivate
->dispatchQueue
= NULL
;
1063 // and take an extra reference for rescheduling
1064 dispatch_retain(dispatchQueue
);
1067 connectionPrivate
->scheduled
= FALSE
;
1069 pthread_mutex_unlock(&connectionPrivate
->lock
);
1071 if (dispatchGroup
!= NULL
) {
1072 dispatch_group_notify(dispatchGroup
, dispatchQueue
, ^{
1073 // release group/queue references
1074 dispatch_release(dispatchQueue
);
1075 dispatch_release(dispatchGroup
); // releases our connection reference
1080 if (rlList
!= NULL
) {
1084 n
= CFArrayGetCount(rlList
);
1085 for (i
= 0; i
< n
; i
+= 3) {
1086 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(rlList
, i
+1);
1087 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(rlList
, i
+2);
1089 ok
= SCNetworkConnectionScheduleWithRunLoop(connection
, rl
, rlMode
);
1091 if (SCError() != BOOTSTRAP_UNKNOWN_SERVICE
) {
1092 SC_log(LOG_NOTICE
, "SCNetworkConnectionScheduleWithRunLoop() failed");
1097 } else if (dispatchQueue
!= NULL
) {
1098 ok
= SCNetworkConnectionSetDispatchQueue(connection
, dispatchQueue
);
1100 if (SCError() != BOOTSTRAP_UNKNOWN_SERVICE
) {
1101 SC_log(LOG_NOTICE
, "SCNetworkConnectionSetDispatchQueue() failed");
1112 if (rlList
!= NULL
) {
1115 if (dispatchQueue
!= NULL
) {
1116 dispatch_release(dispatchQueue
);
1120 SC_log(LOG_NOTICE
, "SCNetworkConnection server %s, notification not restored",
1121 (SCError() == BOOTSTRAP_UNKNOWN_SERVICE
) ? "shutdown" : "failed");
1129 __SCNetworkConnectionNeedsRetry(SCNetworkConnectionRef connection
,
1130 const char *error_label
,
1131 kern_return_t status
,
1134 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1136 if (status
== KERN_SUCCESS
) {
1140 if ((status
== MACH_SEND_INVALID_DEST
) || (status
== MIG_SERVER_DIED
)) {
1141 // the server's gone and our session port's dead, remove the dead name right
1142 (void) mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
1144 // we got an unexpected error, leave the [session] port alone
1145 SC_log(LOG_NOTICE
, "%s: %s", error_label
, mach_error_string(status
));
1147 connectionPrivate
->session_port
= MACH_PORT_NULL
;
1148 if ((status
== MACH_SEND_INVALID_DEST
) || (status
== MIG_SERVER_DIED
)) {
1149 if (__SCNetworkConnectionReconnect(connection
)) {
1153 *sc_status
= status
;
1160 SCNetworkConnectionGetTypeID(void) {
1161 pthread_once(&initialized
, __SCNetworkConnectionInitialize
); /* initialize runtime */
1162 return __kSCNetworkConnectionTypeID
;
1166 CFArrayRef
/* of SCNetworkServiceRef's */
1167 SCNetworkConnectionCopyAvailableServices(SCNetworkSetRef set
)
1169 CFMutableArrayRef available
;
1170 Boolean tempSet
= FALSE
;
1173 SCPreferencesRef prefs
;
1175 prefs
= SCPreferencesCreate(NULL
, CFSTR("SCNetworkConnectionCopyAvailableServices"), NULL
);
1176 if (prefs
!= NULL
) {
1177 set
= SCNetworkSetCopyCurrent(prefs
);
1183 available
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1186 CFArrayRef services
;
1188 services
= SCNetworkSetCopyServices(set
);
1189 if (services
!= NULL
) {
1193 n
= CFArrayGetCount(services
);
1194 for (i
= 0; i
< n
; i
++) {
1195 SCNetworkInterfaceRef interface
;
1196 CFStringRef interfaceType
;
1197 SCNetworkServiceRef service
;
1199 service
= CFArrayGetValueAtIndex(services
, i
);
1200 interface
= SCNetworkServiceGetInterface(service
);
1201 if (interface
== NULL
) {
1205 interfaceType
= SCNetworkInterfaceGetInterfaceType(interface
);
1206 if (CFEqual(interfaceType
, kSCNetworkInterfaceTypePPP
) ||
1207 CFEqual(interfaceType
, kSCNetworkInterfaceTypeVPN
) ||
1208 CFEqual(interfaceType
, kSCNetworkInterfaceTypeIPSec
)) {
1209 CFArrayAppendValue(available
, service
);
1213 CFRelease(services
);
1217 if (tempSet
&& (set
!= NULL
)) {
1224 SCNetworkConnectionRef
1225 SCNetworkConnectionCreateWithService(CFAllocatorRef allocator
,
1226 SCNetworkServiceRef service
,
1227 SCNetworkConnectionCallBack callout
,
1228 SCNetworkConnectionContext
*context
)
1230 SCNetworkConnectionPrivateRef connectionPrivate
;
1232 if (!isA_SCNetworkService(service
)) {
1233 _SCErrorSet(kSCStatusInvalidArgument
);
1237 connectionPrivate
= __SCNetworkConnectionCreatePrivate(allocator
, service
, callout
, context
);
1238 return (SCNetworkConnectionRef
)connectionPrivate
;
1242 SCNetworkConnectionRef
1243 SCNetworkConnectionCreateWithServiceID(CFAllocatorRef allocator
,
1244 CFStringRef serviceID
,
1245 SCNetworkConnectionCallBack callout
,
1246 SCNetworkConnectionContext
*context
)
1248 SCNetworkConnectionRef connection
;
1249 SCNetworkServiceRef service
;
1251 if (!isA_CFString(serviceID
)) {
1252 _SCErrorSet(kSCStatusInvalidArgument
);
1256 service
= _SCNetworkServiceCopyActive(NULL
, serviceID
);
1257 if (service
== NULL
) {
1261 connection
= SCNetworkConnectionCreateWithService(allocator
, service
, callout
, context
);
1268 SCNetworkConnectionRef
1269 SCNetworkConnectionCreate(CFAllocatorRef allocator
,
1270 SCNetworkConnectionCallBack callout
,
1271 SCNetworkConnectionContext
*context
)
1273 SCNetworkConnectionPrivateRef connectionPrivate
= __SCNetworkConnectionCreatePrivate(allocator
, NULL
, callout
, context
);
1274 return (SCNetworkConnectionRef
)connectionPrivate
;
1279 SCNetworkConnectionCopyServiceID(SCNetworkConnectionRef connection
)
1281 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1282 CFStringRef serviceID
;
1284 if (!isA_SCNetworkConnection(connection
)) {
1285 _SCErrorSet(kSCStatusInvalidArgument
);
1289 if (connectionPrivate
->service
== NULL
) {
1290 _SCErrorSet(kSCStatusConnectionNoService
);
1294 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
1295 return CFRetain(serviceID
);
1300 SCNetworkConnectionSetClientInfo(SCNetworkConnectionRef connection
,
1301 mach_port_t client_audit_session
,
1306 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1308 if (!isA_SCNetworkConnection(connection
)) {
1309 _SCErrorSet(kSCStatusInvalidArgument
);
1313 // save client audit session port
1314 if (connectionPrivate
->client_audit_session
!= MACH_PORT_NULL
) {
1315 mach_port_mod_refs(mach_task_self(),
1316 connectionPrivate
->client_audit_session
,
1317 MACH_PORT_RIGHT_SEND
,
1319 connectionPrivate
->client_audit_session
= MACH_PORT_NULL
;
1321 connectionPrivate
->client_audit_session
= client_audit_session
;
1322 if (connectionPrivate
->client_audit_session
!= MACH_PORT_NULL
) {
1323 mach_port_mod_refs(mach_task_self(),
1324 connectionPrivate
->client_audit_session
,
1325 MACH_PORT_RIGHT_SEND
,
1329 // save client UID, GID, and PID
1330 connectionPrivate
->client_uid
= client_uid
;
1331 connectionPrivate
->client_gid
= client_gid
;
1332 connectionPrivate
->client_pid
= client_pid
;
1339 SCNetworkConnectionSetClientAuditInfo(SCNetworkConnectionRef connection
,
1340 audit_token_t client_audit_token
,
1341 mach_port_t audit_session
,
1342 mach_port_t bootstrap_port
,
1345 const char *bundle_id
)
1347 const audit_token_t null_audit
= KERNEL_AUDIT_TOKEN_VALUE
;
1348 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1353 if (memcmp(&client_audit_token
, &null_audit
, sizeof(client_audit_token
))) {
1354 #if TARGET_OS_IPHONE
1355 audit_token_to_au32(client_audit_token
, NULL
, &uid
, &gid
, NULL
, NULL
, &pid
, NULL
, NULL
);
1356 #else // TARGET_OS_IPHONE
1357 uid
= audit_token_to_euid(client_audit_token
);
1358 gid
= audit_token_to_egid(client_audit_token
);
1359 pid
= audit_token_to_pid(client_audit_token
);
1360 #endif // TARGET_OS_IPHONE
1365 if (!SCNetworkConnectionSetClientInfo(connection
, audit_session
, uid
, gid
, pid
)) {
1369 if (connectionPrivate
->client_bootstrap_port
!= MACH_PORT_NULL
) {
1370 mach_port_mod_refs(mach_task_self(),
1371 connectionPrivate
->client_bootstrap_port
,
1372 MACH_PORT_RIGHT_SEND
,
1374 connectionPrivate
->client_bootstrap_port
= MACH_PORT_NULL
;
1377 connectionPrivate
->client_bootstrap_port
= bootstrap_port
;
1378 if (connectionPrivate
->client_bootstrap_port
!= MACH_PORT_NULL
) {
1379 mach_port_mod_refs(mach_task_self(),
1380 connectionPrivate
->client_bootstrap_port
,
1381 MACH_PORT_RIGHT_SEND
,
1385 memcpy(&connectionPrivate
->client_audit_token
, &client_audit_token
, sizeof(connectionPrivate
->client_audit_token
));
1387 if (uuid
!= NULL
&& !uuid_is_null(uuid
)) {
1388 uuid_copy(connectionPrivate
->client_uuid
, uuid
);
1391 if (connectionPrivate
->client_bundle_id
!= NULL
) {
1392 CFRelease(connectionPrivate
->client_bundle_id
);
1393 connectionPrivate
->client_bundle_id
= NULL
;
1396 if (bundle_id
!= NULL
) {
1397 connectionPrivate
->client_bundle_id
= CFStringCreateWithCString(kCFAllocatorDefault
, bundle_id
, kCFStringEncodingUTF8
);
1405 SCNetworkConnectionCopyStatistics(SCNetworkConnectionRef connection
)
1407 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1408 xmlDataOut_t data
= NULL
;
1409 mach_msg_type_number_t datalen
= 0;
1410 int sc_status
= kSCStatusFailed
;
1411 mach_port_t session_port
;
1412 CFPropertyListRef statistics
= NULL
;
1413 kern_return_t status
;
1415 if (!isA_SCNetworkConnection(connection
)) {
1416 _SCErrorSet(kSCStatusInvalidArgument
);
1420 pthread_mutex_lock(&connectionPrivate
->lock
);
1422 #if !TARGET_IPHONE_SIMULATOR
1423 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1424 __block xpc_object_t xstats
= NULL
;
1425 ne_session_t ne_session
= connectionPrivate
->ne_session
;
1427 ne_session_retain(ne_session
);
1428 pthread_mutex_unlock(&connectionPrivate
->lock
);
1430 dispatch_semaphore_t ne_sema
= dispatch_semaphore_create(0);
1431 ne_session_get_info(ne_session
, NESessionInfoTypeStatistics
, __SCNetworkConnectionQueue(), ^(xpc_object_t result
) {
1432 if (result
!= NULL
) {
1433 xstats
= xpc_retain(result
);
1435 ne_session_release(ne_session
);
1436 dispatch_semaphore_signal(ne_sema
);
1438 dispatch_semaphore_wait(ne_sema
, DISPATCH_TIME_FOREVER
);
1439 dispatch_release(ne_sema
);
1441 if (xstats
!= NULL
) {
1442 statistics
= _CFXPCCreateCFObjectFromXPCObject(xstats
);
1443 xpc_release(xstats
);
1445 _SCErrorSet(kSCStatusFailed
);
1450 #endif /* !TARGET_IPHONE_SIMULATOR */
1454 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1455 if (session_port
== MACH_PORT_NULL
) {
1459 status
= pppcontroller_copystatistics(session_port
, &data
, &datalen
, &sc_status
);
1460 if (__SCNetworkConnectionNeedsRetry(connection
,
1461 "SCNetworkConnectionCopyStatistics()",
1468 if (!_SCUnserialize(&statistics
, NULL
, data
, datalen
)) {
1469 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
1471 if ((sc_status
== kSCStatusOK
) && !isA_CFDictionary(statistics
)) {
1472 sc_status
= kSCStatusFailed
;
1476 if (sc_status
!= kSCStatusOK
) {
1477 if (statistics
!= NULL
) {
1478 CFRelease(statistics
);
1481 _SCErrorSet(sc_status
);
1486 pthread_mutex_unlock(&connectionPrivate
->lock
);
1492 SCNetworkConnectionGetService(SCNetworkConnectionRef connection
)
1494 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1496 if (!isA_SCNetworkConnection(connection
)) {
1497 _SCErrorSet(kSCStatusInvalidArgument
);
1501 return connectionPrivate
->service
;
1505 SCNetworkConnectionStatus
1506 SCNetworkConnectionGetStatus(SCNetworkConnectionRef connection
)
1508 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1509 SCNetworkConnectionStatus nc_status
= kSCNetworkConnectionInvalid
;
1511 int sc_status
= kSCStatusFailed
;
1512 mach_port_t session_port
;
1513 kern_return_t status
;
1514 CFStringRef serviceID
;
1516 if (!isA_SCNetworkConnection(connection
)) {
1517 _SCErrorSet(kSCStatusInvalidArgument
);
1518 return kSCNetworkConnectionInvalid
;
1521 if (connectionPrivate
->service
== NULL
) {
1522 _SCErrorSet(kSCStatusConnectionNoService
);
1523 return kSCNetworkConnectionInvalid
;
1526 // skip retry and return immediately if we know no service is to be found.
1527 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
1528 if (CFStringGetLength(serviceID
) == 0) {
1529 _SCErrorSet(kSCStatusConnectionNoService
);
1530 return kSCNetworkConnectionInvalid
;
1533 pthread_mutex_lock(&connectionPrivate
->lock
);
1535 #if !TARGET_IPHONE_SIMULATOR
1536 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1537 __block ne_session_status_t ne_status
;
1538 ne_session_t ne_session
= connectionPrivate
->ne_session
;
1540 ne_session_retain(ne_session
);
1541 pthread_mutex_unlock(&connectionPrivate
->lock
);
1543 dispatch_semaphore_t ne_sema
= dispatch_semaphore_create(0);
1544 ne_session_get_status(ne_session
, __SCNetworkConnectionQueue(), ^(ne_session_status_t status
) {
1546 ne_session_release(ne_session
);
1547 dispatch_semaphore_signal(ne_sema
);
1549 dispatch_semaphore_wait(ne_sema
, DISPATCH_TIME_FOREVER
);
1550 dispatch_release(ne_sema
);
1552 return SCNetworkConnectionGetStatusFromNEStatus(ne_status
);
1554 #endif /* !TARGET_IPHONE_SIMULATOR */
1558 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1559 if (session_port
== MACH_PORT_NULL
) {
1560 nc_status
= kSCNetworkConnectionInvalid
;
1564 status
= pppcontroller_getstatus(session_port
, &nc_status
, &sc_status
);
1565 if (__SCNetworkConnectionNeedsRetry(connection
,
1566 "SCNetworkConnectionGetStatus()",
1572 // wait up to 250 ms for the network service to become available
1573 if (!connectionPrivate
->haveStatus
&&
1574 (sc_status
== kSCStatusConnectionNoService
) &&
1575 ((retry
+= 10) < 250)) {
1576 usleep(10 * 1000); // sleep 10ms between attempts
1580 if (sc_status
== kSCStatusOK
) {
1581 connectionPrivate
->haveStatus
= TRUE
;
1583 _SCErrorSet(sc_status
);
1584 nc_status
= kSCNetworkConnectionInvalid
;
1589 pthread_mutex_unlock(&connectionPrivate
->lock
);
1595 SCNetworkConnectionCopyExtendedStatus(SCNetworkConnectionRef connection
)
1597 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1598 xmlDataOut_t data
= NULL
;
1599 mach_msg_type_number_t datalen
= 0;
1600 CFPropertyListRef extstatus
= NULL
;
1602 int sc_status
= kSCStatusFailed
;
1603 mach_port_t session_port
;
1604 kern_return_t status
;
1605 CFStringRef serviceID
;
1607 if (!isA_SCNetworkConnection(connection
)) {
1608 _SCErrorSet(kSCStatusInvalidArgument
);
1612 if (connectionPrivate
->service
== NULL
) {
1613 _SCErrorSet(kSCStatusConnectionNoService
);
1617 // skip retry and return immediately if we know no service is to be found.
1618 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
1619 if (CFStringGetLength(serviceID
) == 0) {
1620 _SCErrorSet(kSCStatusConnectionNoService
);
1624 pthread_mutex_lock(&connectionPrivate
->lock
);
1626 #if !TARGET_IPHONE_SIMULATOR
1627 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1628 __block CFDictionaryRef statusDictionary
= NULL
;
1629 ne_session_t ne_session
= connectionPrivate
->ne_session
;
1631 ne_session_retain(ne_session
);
1632 pthread_mutex_unlock(&connectionPrivate
->lock
);
1634 dispatch_semaphore_t ne_sema
= dispatch_semaphore_create(0);
1635 ne_session_get_info(ne_session
, NESessionInfoTypeExtendedStatus
, __SCNetworkConnectionQueue(), ^(xpc_object_t extended_status
) {
1636 if (extended_status
!= NULL
) {
1637 statusDictionary
= _CFXPCCreateCFObjectFromXPCObject(extended_status
);
1638 ne_session_release(ne_session
);
1639 dispatch_semaphore_signal(ne_sema
);
1641 ne_session_get_status(ne_session
, __SCNetworkConnectionQueue(), ^(ne_session_status_t ne_status
) {
1642 SCNetworkConnectionStatus status
= SCNetworkConnectionGetStatusFromNEStatus(ne_status
);
1643 if (status
!= kSCNetworkConnectionInvalid
) {
1644 CFStringRef keys
[1] = { kSCNetworkConnectionStatus
};
1645 CFNumberRef values
[1] = { NULL
};
1646 values
[0] = CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &status
);
1647 statusDictionary
= CFDictionaryCreate(kCFAllocatorDefault
, (const void **)keys
, (const void **)values
, sizeof(values
) / sizeof(values
[0]), &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1648 CFRelease(values
[0]);
1650 ne_session_release(ne_session
);
1651 dispatch_semaphore_signal(ne_sema
);
1655 dispatch_semaphore_wait(ne_sema
, DISPATCH_TIME_FOREVER
);
1656 dispatch_release(ne_sema
);
1658 if (statusDictionary
!= NULL
) {
1659 extstatus
= (CFPropertyListRef
)statusDictionary
;
1661 _SCErrorSet(kSCStatusFailed
);
1670 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1671 if (session_port
== MACH_PORT_NULL
) {
1675 status
= pppcontroller_copyextendedstatus(session_port
, &data
, &datalen
, &sc_status
);
1676 if (__SCNetworkConnectionNeedsRetry(connection
,
1677 "SCNetworkConnectionCopyExtendedStatus()",
1684 if (!_SCUnserialize(&extstatus
, NULL
, data
, datalen
)) {
1685 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
1687 if ((sc_status
== kSCStatusOK
) && !isA_CFDictionary(extstatus
)) {
1688 sc_status
= kSCStatusFailed
;
1692 // wait up to 250 ms for the network service to become available
1693 if (!connectionPrivate
->haveStatus
&&
1694 (sc_status
== kSCStatusConnectionNoService
) &&
1695 ((retry
+= 10) < 250)) {
1696 usleep(10 * 1000); // sleep 10ms between attempts
1700 if (sc_status
== kSCStatusOK
) {
1701 connectionPrivate
->haveStatus
= TRUE
;
1703 if (extstatus
!= NULL
) {
1704 CFRelease(extstatus
);
1707 _SCErrorSet(sc_status
);
1712 pthread_mutex_unlock(&connectionPrivate
->lock
);
1718 _SCNetworkConnectionMergeDictionaries (const void *key
, const void *value
, void *context
)
1720 /* Add value only if not present */
1721 CFDictionaryAddValue((CFMutableDictionaryRef
)context
, (CFStringRef
)key
, (CFTypeRef
)value
);
1726 SCNetworkConnectionStart(SCNetworkConnectionRef connection
,
1727 CFDictionaryRef userOptions
,
1730 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1731 CFDataRef dataref
= NULL
;
1733 CFIndex datalen
= 0;
1735 int sc_status
= kSCStatusFailed
;
1736 mach_port_t session_port
;
1737 kern_return_t status
;
1739 if (!isA_SCNetworkConnection(connection
)) {
1740 _SCErrorSet(kSCStatusInvalidArgument
);
1744 if ((userOptions
!= NULL
) && !isA_CFDictionary(userOptions
)) {
1745 _SCErrorSet(kSCStatusInvalidArgument
);
1749 if (userOptions
== NULL
) {
1750 userOptions
= connectionPrivate
->on_demand_user_options
;
1751 } else if (connectionPrivate
->on_demand_user_options
!= NULL
) {
1752 CFDictionaryRef localUserOptions
= NULL
;
1754 localUserOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, userOptions
);
1755 if (localUserOptions
) {
1756 CFDictionaryApplyFunction(connectionPrivate
->on_demand_user_options
,
1757 _SCNetworkConnectionMergeDictionaries
,
1758 (void *)localUserOptions
);
1759 CFRelease(connectionPrivate
->on_demand_user_options
);
1760 userOptions
= connectionPrivate
->on_demand_user_options
= localUserOptions
;
1765 CFMutableDictionaryRef mdict
= NULL
;
1767 SC_log(LOG_INFO
, "SCNetworkConnectionStart (%p)", connectionPrivate
);
1769 if (userOptions
!= NULL
) {
1770 CFDictionaryRef dict
;
1771 CFStringRef encryption
;
1772 CFMutableDictionaryRef new_dict
;
1774 /* special code to remove secret information */
1775 mdict
= CFDictionaryCreateMutableCopy(NULL
, 0, userOptions
);
1777 dict
= CFDictionaryGetValue(mdict
, kSCEntNetPPP
);
1778 if (isA_CFDictionary(dict
)) {
1779 encryption
= CFDictionaryGetValue(dict
, kSCPropNetPPPAuthPasswordEncryption
);
1780 if (!isA_CFString(encryption
) ||
1781 !CFEqual(encryption
, kSCValNetPPPAuthPasswordEncryptionKeychain
)) {
1782 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1783 CFDictionaryReplaceValue(new_dict
, kSCPropNetPPPAuthPassword
, CFSTR("******"));
1784 CFDictionarySetValue(mdict
, kSCEntNetPPP
, new_dict
);
1785 CFRelease(new_dict
);
1789 dict
= CFDictionaryGetValue(mdict
, kSCEntNetL2TP
);
1790 if (isA_CFDictionary(dict
)) {
1791 encryption
= CFDictionaryGetValue(dict
, kSCPropNetL2TPIPSecSharedSecretEncryption
);
1792 if (!isA_CFString(encryption
) ||
1793 !CFEqual(encryption
, kSCValNetL2TPIPSecSharedSecretEncryptionKeychain
)) {
1794 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1795 CFDictionaryReplaceValue(new_dict
, kSCPropNetL2TPIPSecSharedSecret
, CFSTR("******"));
1796 CFDictionarySetValue(mdict
, kSCEntNetL2TP
, new_dict
);
1797 CFRelease(new_dict
);
1801 dict
= CFDictionaryGetValue(mdict
, kSCEntNetIPSec
);
1802 if (isA_CFDictionary(dict
)) {
1803 encryption
= CFDictionaryGetValue(dict
, kSCPropNetIPSecSharedSecretEncryption
);
1804 if (!isA_CFString(encryption
) ||
1805 !CFEqual(encryption
, kSCValNetIPSecSharedSecretEncryptionKeychain
)) {
1806 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1807 CFDictionaryReplaceValue(new_dict
, kSCPropNetIPSecSharedSecret
, CFSTR("******"));
1808 CFDictionarySetValue(mdict
, kSCEntNetIPSec
, new_dict
);
1809 CFRelease(new_dict
);
1814 SC_log(LOG_INFO
, "User options: %@", mdict
);
1815 if (mdict
!= NULL
) CFRelease(mdict
);
1818 pthread_mutex_lock(&connectionPrivate
->lock
);
1820 /* Clear out any cached flow divert token parameters */
1821 if (connectionPrivate
->flow_divert_token_params
!= NULL
) {
1822 CFRelease(connectionPrivate
->flow_divert_token_params
);
1823 connectionPrivate
->flow_divert_token_params
= NULL
;
1826 #if !TARGET_IPHONE_SIMULATOR
1827 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1828 xpc_object_t xuser_options
= NULL
;
1830 if (userOptions
!= NULL
) {
1831 xuser_options
= _CFXPCCreateXPCObjectFromCFObject(userOptions
);
1834 if (connectionPrivate
->client_bootstrap_port
!= MACH_PORT_NULL
) {
1835 #if NE_SESSION_VERSION > 2
1836 ne_session_start_on_behalf_of(connectionPrivate
->ne_session
,
1838 connectionPrivate
->client_bootstrap_port
,
1839 connectionPrivate
->client_audit_session
,
1840 connectionPrivate
->client_uid
,
1841 connectionPrivate
->client_gid
,
1842 connectionPrivate
->client_pid
);
1844 ne_session_start_on_behalf_of(connectionPrivate
->ne_session
,
1846 connectionPrivate
->client_bootstrap_port
,
1847 connectionPrivate
->client_audit_session
,
1848 connectionPrivate
->client_uid
,
1849 connectionPrivate
->client_gid
);
1852 ne_session_start_with_options(connectionPrivate
->ne_session
, xuser_options
);
1855 /* make sure the xpc_message goes through */
1856 ne_session_send_barrier(connectionPrivate
->ne_session
);
1858 if (xuser_options
!= NULL
) {
1859 xpc_release(xuser_options
);
1865 #endif /* !TARGET_IPHONE_SIMULATOR */
1867 if (userOptions
&& !_SCSerialize(userOptions
, &dataref
, &data
, &datalen
)) {
1873 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1874 if (session_port
== MACH_PORT_NULL
) {
1875 if (dataref
) CFRelease(dataref
);
1879 status
= pppcontroller_start(session_port
,
1881 (mach_msg_type_number_t
)datalen
,
1884 if (__SCNetworkConnectionNeedsRetry(connection
,
1885 "SCNetworkConnectionStart()",
1891 if (dataref
) CFRelease(dataref
);
1894 SC_log(LOG_INFO
, "SCNetworkConnectionStart (%p), return: %d", connectionPrivate
, sc_status
);
1897 if (sc_status
!= kSCStatusOK
) {
1898 _SCErrorSet(sc_status
);
1902 /* connection is now started */
1906 pthread_mutex_unlock(&connectionPrivate
->lock
);
1912 SCNetworkConnectionStop(SCNetworkConnectionRef connection
,
1913 Boolean forceDisconnect
)
1915 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1917 int sc_status
= kSCStatusFailed
;
1918 mach_port_t session_port
;
1919 kern_return_t status
;
1921 if (!isA_SCNetworkConnection(connection
)) {
1922 _SCErrorSet(kSCStatusInvalidArgument
);
1927 SC_log(LOG_INFO
, "SCNetworkConnectionStop (%p)", connectionPrivate
);
1930 pthread_mutex_lock(&connectionPrivate
->lock
);
1932 #if !TARGET_IPHONE_SIMULATOR
1933 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1934 ne_session_stop(connectionPrivate
->ne_session
);
1935 /* make sure the xpc_message goes through */
1936 ne_session_send_barrier(connectionPrivate
->ne_session
);
1940 #endif /* !TARGET_IPHONE_SIMULATOR */
1944 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1945 if (session_port
== MACH_PORT_NULL
) {
1949 status
= pppcontroller_stop(session_port
, forceDisconnect
, &sc_status
);
1950 if (__SCNetworkConnectionNeedsRetry(connection
,
1951 "SCNetworkConnectionStop()",
1958 SC_log(LOG_INFO
, "SCNetworkConnectionStop (%p), return: %d", connectionPrivate
, sc_status
);
1961 if (sc_status
!= kSCStatusOK
) {
1962 _SCErrorSet(sc_status
);
1966 /* connection is now disconnecting */
1971 pthread_mutex_unlock(&connectionPrivate
->lock
);
1977 SCNetworkConnectionSuspend(SCNetworkConnectionRef connection
)
1979 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1981 int sc_status
= kSCStatusFailed
;
1982 mach_port_t session_port
;
1983 kern_return_t status
;
1985 if (!isA_SCNetworkConnection(connection
)) {
1986 _SCErrorSet(kSCStatusInvalidArgument
);
1991 SC_log(LOG_INFO
, "SCNetworkConnectionSuspend (%p)", connectionPrivate
);
1994 pthread_mutex_lock(&connectionPrivate
->lock
);
1996 #if !!TARGET_IPHONE_SIMULATOR
1997 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1998 /* Suspend only applies to PPPSerial and PPPoE */
2002 #endif /* !TARGET_IPHONE_SIMULATOR */
2006 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2007 if (session_port
== MACH_PORT_NULL
) {
2011 status
= pppcontroller_suspend(session_port
, &sc_status
);
2012 if (__SCNetworkConnectionNeedsRetry(connection
,
2013 "SCNetworkConnectionSuspend()",
2020 SC_log(LOG_INFO
, "SCNetworkConnectionSuspend (%p), return: %d", connectionPrivate
, sc_status
);
2023 if (sc_status
!= kSCStatusOK
) {
2024 _SCErrorSet(sc_status
);
2028 /* connection is now suspended */
2033 pthread_mutex_unlock(&connectionPrivate
->lock
);
2039 SCNetworkConnectionResume(SCNetworkConnectionRef connection
)
2041 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2043 int sc_status
= kSCStatusFailed
;
2044 mach_port_t session_port
;
2045 kern_return_t status
;
2047 if (!isA_SCNetworkConnection(connection
)) {
2048 _SCErrorSet(kSCStatusInvalidArgument
);
2053 SC_log(LOG_INFO
, "SCNetworkConnectionResume (%p)", connectionPrivate
);
2056 pthread_mutex_lock(&connectionPrivate
->lock
);
2058 #if !TARGET_IPHONE_SIMULATOR
2059 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2060 /* Resume only applies to PPPSerial and PPPoE */
2064 #endif /* !TARGET_IPHONE_SIMULATOR */
2068 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2069 if (session_port
== MACH_PORT_NULL
) {
2073 status
= pppcontroller_resume(session_port
, &sc_status
);
2074 if (__SCNetworkConnectionNeedsRetry(connection
,
2075 "SCNetworkConnectionResume()",
2082 SC_log(LOG_INFO
, "SCNetworkConnectionResume (%p), return: %d", connectionPrivate
, sc_status
);
2085 if (sc_status
!= kSCStatusOK
) {
2086 _SCErrorSet(sc_status
);
2090 /* connection is now resume */
2095 pthread_mutex_unlock(&connectionPrivate
->lock
);
2100 #if !TARGET_IPHONE_SIMULATOR
2102 SCNetworkConnectionRefreshOnDemandState(SCNetworkConnectionRef connection
)
2104 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2107 int sc_status
= kSCStatusFailed
;
2108 mach_port_t server_port
= __SCNetworkConnectionGetCurrentServerPort();
2109 kern_return_t status
= KERN_SUCCESS
;
2111 if (!isA_SCNetworkConnection(connection
)) {
2112 _SCErrorSet(kSCStatusInvalidArgument
);
2117 SC_log(LOG_INFO
, "SCNetworkConnectionRefreshOnDemandState (%p)", connectionPrivate
);
2120 pthread_mutex_lock(&connectionPrivate
->lock
);
2123 if (server_port
== MACH_PORT_NULL
) {
2124 server_port
= __SCNetworkConnectionRefreshServerPort(server_port
, &sc_status
);
2125 if (server_port
== MACH_PORT_NULL
) {
2126 // if server not available
2127 if (sc_status
== BOOTSTRAP_UNKNOWN_SERVICE
) {
2128 // wait up to 2.5 seconds for the [SCNetworkConnection] server
2130 if ((retry
+= 50) < 2500) {
2131 usleep(50 * 1000); // sleep 50ms between attempts
2139 status
= pppcontroller_ondemand_refresh_state(server_port
, &sc_status
);
2140 if (status
== KERN_SUCCESS
)
2143 if (status
== MACH_SEND_INVALID_DEST
) {
2144 // the server is not yet available
2145 SC_log(LOG_NOTICE
, "SCNetworkConnectionRefreshOnDemandState (!dest) (%p)", connectionPrivate
);
2146 } else if (status
== MIG_SERVER_DIED
) {
2147 // the server we were using is gone
2148 SC_log(LOG_NOTICE
, "SCNetworkConnectionRefreshOnDemandState (!mig) (%p)", connectionPrivate
);
2150 // if we got an unexpected error, don't retry
2157 SC_log(LOG_INFO
, "SCNetworkConnectionRefreshOnDemandState (%p), return: %d/%d", connectionPrivate
, status
, sc_status
);
2160 if (sc_status
!= kSCStatusOK
) {
2161 _SCErrorSet(sc_status
);
2169 pthread_mutex_unlock(&connectionPrivate
->lock
);
2172 #endif /* !TARGET_IPHONE_SIMULATOR */
2176 SCNetworkConnectionCopyUserOptions(SCNetworkConnectionRef connection
)
2178 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2179 xmlDataOut_t data
= NULL
;
2180 mach_msg_type_number_t datalen
= 0;
2181 int sc_status
= kSCStatusFailed
;
2182 mach_port_t session_port
;
2183 kern_return_t status
;
2184 CFPropertyListRef userOptions
= NULL
;
2186 if (!isA_SCNetworkConnection(connection
)) {
2187 _SCErrorSet(kSCStatusInvalidArgument
);
2191 pthread_mutex_lock(&connectionPrivate
->lock
);
2193 #if !TARGET_IPHONE_SIMULATOR
2194 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2195 __block xpc_object_t config
= NULL
;
2196 ne_session_t ne_session
= connectionPrivate
->ne_session
;
2198 ne_session_retain(ne_session
);
2199 pthread_mutex_unlock(&connectionPrivate
->lock
);
2201 dispatch_semaphore_t ne_sema
= dispatch_semaphore_create(0);
2202 ne_session_get_info(ne_session
, NESessionInfoTypeConfiguration
, __SCNetworkConnectionQueue(), ^(xpc_object_t result
) {
2203 if (result
!= NULL
) {
2204 config
= xpc_retain(result
);
2206 ne_session_release(ne_session
);
2207 dispatch_semaphore_signal(ne_sema
);
2209 dispatch_semaphore_wait(ne_sema
, DISPATCH_TIME_FOREVER
);
2210 dispatch_release(ne_sema
);
2212 if (config
!= NULL
) {
2213 xpc_object_t xoptions
= xpc_dictionary_get_value(config
, NESMSessionLegacyUserConfigurationKey
);
2214 if (xoptions
!= NULL
) {
2215 userOptions
= _CFXPCCreateCFObjectFromXPCObject(xoptions
);
2217 xpc_release(config
);
2221 #endif /* !TARGET_IPHONE_SIMULATOR */
2225 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2226 if (session_port
== MACH_PORT_NULL
) {
2230 status
= pppcontroller_copyuseroptions(session_port
, &data
, &datalen
, &sc_status
);
2231 if (__SCNetworkConnectionNeedsRetry(connection
,
2232 "SCNetworkConnectionCopyUserOptions()",
2239 if (!_SCUnserialize(&userOptions
, NULL
, data
, datalen
)) {
2240 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
2242 if ((sc_status
== kSCStatusOK
) && (userOptions
!= NULL
) && !isA_CFDictionary(userOptions
)) {
2243 sc_status
= kSCStatusFailed
;
2247 if (sc_status
== kSCStatusOK
) {
2248 if (userOptions
== NULL
) {
2249 // if no user options, return an empty dictionary
2250 userOptions
= CFDictionaryCreate(NULL
,
2254 &kCFTypeDictionaryKeyCallBacks
,
2255 &kCFTypeDictionaryValueCallBacks
);
2259 CFRelease(userOptions
);
2262 _SCErrorSet(sc_status
);
2267 pthread_mutex_unlock(&connectionPrivate
->lock
);
2273 __SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection
,
2274 CFRunLoopRef runLoop
,
2275 CFStringRef runLoopMode
,
2276 dispatch_queue_t queue
)
2278 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2280 int sc_status
= kSCStatusFailed
;
2281 mach_port_t session_port
;
2282 kern_return_t status
;
2284 pthread_mutex_lock(&connectionPrivate
->lock
);
2286 if (connectionPrivate
->rlsFunction
== NULL
) {
2287 _SCErrorSet(kSCStatusInvalidArgument
);
2291 if ((connectionPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled on a dispatch queue
2292 ((queue
!= NULL
) && connectionPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
2293 _SCErrorSet(kSCStatusInvalidArgument
);
2297 if (!connectionPrivate
->scheduled
) {
2301 if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2302 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2303 if (session_port
== MACH_PORT_NULL
) {
2307 status
= pppcontroller_notification(session_port
, 1, &sc_status
);
2308 if (__SCNetworkConnectionNeedsRetry(connection
,
2309 "__SCNetworkConnectionScheduleWithRunLoop()",
2315 if (sc_status
!= kSCStatusOK
) {
2316 _SCErrorSet(sc_status
);
2320 if (runLoop
!= NULL
) {
2321 connectionPrivate
->rls
= CFMachPortCreateRunLoopSource(NULL
, connectionPrivate
->notify_port
, 0);
2322 connectionPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2324 } else if (runLoop
!= NULL
) {
2325 CFRunLoopSourceContext rlsContext
= {
2327 (void *)connection
, // info
2330 NULL
, // copy description
2335 __SCNetworkConnectionCallBack
, // perform
2338 connectionPrivate
->rls
= CFRunLoopSourceCreate(kCFAllocatorDefault
, 0, &rlsContext
);
2339 connectionPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2342 connectionPrivate
->scheduled
= TRUE
;
2345 if (queue
!= NULL
) {
2346 // retain the dispatch queue
2347 connectionPrivate
->dispatchQueue
= queue
;
2348 dispatch_retain(connectionPrivate
->dispatchQueue
);
2350 if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2351 dispatch_group_t group
= NULL
;
2353 dispatch_source_t source
;
2356 // We've taken a reference to the caller's dispatch_queue and we
2357 // want to hold on to that reference until we've processed any/all
2358 // notifications. To facilitate this we create a group, dispatch
2359 // any notification blocks via that group, and when the caller
2360 // has told us to stop the notifications (unschedule) we wait for
2361 // the group to empty and use the group's finalizer to release
2362 // our reference to the SCNetworkConnection.
2364 group
= dispatch_group_create();
2365 connectionPrivate
->dispatchGroup
= group
;
2366 CFRetain(connection
);
2367 dispatch_set_context(connectionPrivate
->dispatchGroup
, (void *)connection
);
2368 dispatch_set_finalizer_f(connectionPrivate
->dispatchGroup
, (dispatch_function_t
)CFRelease
);
2370 mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
2371 source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
, mp
, 0, queue
);
2372 if (source
== NULL
) {
2373 SC_log(LOG_NOTICE
, "dispatch_source_create() failed");
2374 _SCErrorSet(kSCStatusFailed
);
2378 // have our dispatch source hold a reference to the notification CFMachPort
2379 CFRetain(connectionPrivate
->notify_port
);
2380 dispatch_set_context(source
, (void *)connectionPrivate
->notify_port
);
2381 dispatch_set_finalizer_f(source
, (dispatch_function_t
)CFRelease
);
2383 dispatch_source_set_event_handler(source
, ^{
2386 u_int8_t buf
[sizeof(mach_msg_empty_t
) + MAX_TRAILER_SIZE
];
2387 mach_msg_empty_rcv_t msg
;
2388 mach_no_senders_notification_t no_senders
;
2389 } *notify_message_t
;
2390 CFMachPortRef notify_port
;
2391 notify_message_t notify_msg
;
2393 notify_msg
= (notify_message_t
)malloc(sizeof(*notify_msg
));
2395 kr
= mach_msg(¬ify_msg
->msg
.header
, // msg
2396 MACH_RCV_MSG
, // options
2398 sizeof(*notify_msg
), // rcv_size
2400 MACH_MSG_TIMEOUT_NONE
, // timeout
2401 MACH_PORT_NULL
); // notify
2402 if (kr
!= KERN_SUCCESS
) {
2403 SC_log(LOG_NOTICE
, "SCDynamicStore notification handler, kr=0x%x", kr
);
2407 CFRetain(connection
);
2408 notify_port
= dispatch_get_context(source
);
2410 dispatch_group_async(group
, queue
, ^{
2411 __SCNetworkConnectionMachCallBack(notify_port
,
2413 sizeof(*notify_msg
),
2414 (void *)connection
);
2416 CFRelease(connection
);
2420 dispatch_source_set_cancel_handler(source
, ^{
2421 dispatch_release(source
);
2424 connectionPrivate
->dispatchSource
= source
;
2425 dispatch_resume(source
);
2428 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, connectionPrivate
->rlList
)) {
2430 * if we do not already have notifications scheduled with
2431 * this runLoop / runLoopMode
2433 CFRunLoopAddSource(runLoop
, connectionPrivate
->rls
, runLoopMode
);
2436 _SC_schedule(connection
, runLoop
, runLoopMode
, connectionPrivate
->rlList
);
2439 #if !TARGET_IPHONE_SIMULATOR
2440 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2441 CFRetain(connection
);
2442 ne_session_set_event_handler(connectionPrivate
->ne_session
, __SCNetworkConnectionQueue(), ^(ne_session_event_t event
, void *event_data
) {
2443 #pragma unused(event_data)
2444 if (event
== NESessionEventStatusChanged
) {
2445 CFRetain(connection
); /* Released in __SCNetworkConnectionCallBack */
2446 pthread_mutex_lock(&connectionPrivate
->lock
);
2447 if (connectionPrivate
->rls
!= NULL
) {
2448 CFRunLoopSourceSignal(connectionPrivate
->rls
);
2449 _SC_signalRunLoop(connection
, connectionPrivate
->rls
, connectionPrivate
->rlList
);
2450 } else if (connectionPrivate
->dispatchQueue
!= NULL
) {
2451 dispatch_async(connectionPrivate
->dispatchQueue
, ^{
2452 __SCNetworkConnectionCallBack((void *)connection
);
2455 pthread_mutex_unlock(&connectionPrivate
->lock
);
2456 } else if (event
== NESessionEventCanceled
) {
2457 CFRelease(connection
);
2461 #endif /* !TARGET_IPHONE_SIMULATOR */
2467 pthread_mutex_unlock(&connectionPrivate
->lock
);
2473 __SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection
,
2474 CFRunLoopRef runLoop
,
2475 CFStringRef runLoopMode
,
2476 dispatch_queue_t queue
)
2478 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2479 dispatch_group_t drainGroup
= NULL
;
2480 dispatch_queue_t drainQueue
= NULL
;
2481 int sc_status
= kSCStatusFailed
;
2484 kern_return_t status
;
2486 // hold a reference while we unschedule
2487 CFRetain(connection
);
2489 pthread_mutex_lock(&connectionPrivate
->lock
);
2491 if ((runLoop
!= NULL
) && !connectionPrivate
->scheduled
) { // if we should be scheduled (but are not)
2492 _SCErrorSet(kSCStatusInvalidArgument
);
2496 if (((runLoop
== NULL
) && (connectionPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
2497 ((runLoop
!= NULL
) && (connectionPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
2498 _SCErrorSet(kSCStatusInvalidArgument
);
2502 if (connectionPrivate
->dispatchQueue
!= NULL
) {
2503 if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2504 // cancel dispatchSource
2505 if (connectionPrivate
->dispatchSource
!= NULL
) {
2506 dispatch_source_cancel(connectionPrivate
->dispatchSource
);
2507 connectionPrivate
->dispatchSource
= NULL
;
2510 // save dispatchQueue/group, release reference when all queue'd blocks
2511 // have been processed, allow re-scheduling
2512 drainGroup
= connectionPrivate
->dispatchGroup
;
2513 connectionPrivate
->dispatchGroup
= NULL
;
2514 drainQueue
= connectionPrivate
->dispatchQueue
;
2515 connectionPrivate
->dispatchQueue
= NULL
;
2517 dispatch_release(connectionPrivate
->dispatchQueue
);
2518 connectionPrivate
->dispatchQueue
= NULL
;
2521 if (!_SC_unschedule(connection
, runLoop
, runLoopMode
, connectionPrivate
->rlList
, FALSE
)) {
2522 // if not currently scheduled on this runLoop / runLoopMode
2523 _SCErrorSet(kSCStatusFailed
);
2527 n
= CFArrayGetCount(connectionPrivate
->rlList
);
2528 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, connectionPrivate
->rlList
)) {
2530 * if we are no longer scheduled to receive notifications for
2531 * this runLoop / runLoopMode
2533 CFRunLoopRemoveSource(runLoop
, connectionPrivate
->rls
, runLoopMode
);
2536 // if *all* notifications have been unscheduled
2537 CFRelease(connectionPrivate
->rlList
);
2538 connectionPrivate
->rlList
= NULL
;
2539 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
2540 CFRelease(connectionPrivate
->rls
);
2541 connectionPrivate
->rls
= NULL
;
2547 // if *all* notifications have been unscheduled
2548 connectionPrivate
->scheduled
= FALSE
;
2550 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2551 #if !TARGET_IPHONE_SIMULATOR
2552 ne_session_cancel(connectionPrivate
->ne_session
);
2553 #endif /* !TARGET_IPHONE_SIMULATOR */
2555 mach_port_t session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2556 if (session_port
== MACH_PORT_NULL
) {
2560 status
= pppcontroller_notification(session_port
, 0, &sc_status
);
2561 if (__SCNetworkConnectionNeedsRetry(connection
,
2562 "__SCNetworkConnectionUnscheduleFromRunLoop pppcontroller_notification()",
2565 sc_status
= kSCStatusOK
;
2566 status
= KERN_SUCCESS
;
2569 if ((status
!= KERN_SUCCESS
) || (sc_status
!= kSCStatusOK
)) {
2570 _SCErrorSet(sc_status
);
2580 pthread_mutex_unlock(&connectionPrivate
->lock
);
2582 if (drainGroup
!= NULL
) {
2583 dispatch_group_notify(drainGroup
, drainQueue
, ^{
2584 // release group/queue references
2585 dispatch_release(drainQueue
);
2586 dispatch_release(drainGroup
); // releases our connection reference
2590 // release our reference
2591 CFRelease(connection
);
2598 SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection
,
2599 CFRunLoopRef runLoop
,
2600 CFStringRef runLoopMode
)
2602 if (!isA_SCNetworkConnection(connection
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
2603 _SCErrorSet(kSCStatusInvalidArgument
);
2607 return __SCNetworkConnectionScheduleWithRunLoop(connection
, runLoop
, runLoopMode
, NULL
);
2612 SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection
,
2613 CFRunLoopRef runLoop
,
2614 CFStringRef runLoopMode
)
2616 if (!isA_SCNetworkConnection(connection
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
2617 _SCErrorSet(kSCStatusInvalidArgument
);
2621 return __SCNetworkConnectionUnscheduleFromRunLoop(connection
, runLoop
, runLoopMode
, NULL
);
2626 SCNetworkConnectionSetDispatchQueue(SCNetworkConnectionRef connection
,
2627 dispatch_queue_t queue
)
2631 if (!isA_SCNetworkConnection(connection
)) {
2632 _SCErrorSet(kSCStatusInvalidArgument
);
2636 if (queue
!= NULL
) {
2637 ok
= __SCNetworkConnectionScheduleWithRunLoop(connection
, NULL
, NULL
, queue
);
2639 ok
= __SCNetworkConnectionUnscheduleFromRunLoop(connection
, NULL
, NULL
, NULL
);
2646 /* Requires having called SCNetworkConnectionSelectServiceWithOptions previously */
2648 SCNetworkConnectionIsOnDemandSuspended(SCNetworkConnectionRef connection
)
2650 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2652 if (!isA_SCNetworkConnection(connection
)) {
2653 _SCErrorSet(kSCStatusInvalidArgument
);
2657 if (connectionPrivate
->on_demand_info
!= NULL
) {
2658 uint32_t isSuspended
= 0;
2659 CFNumberRef num
= NULL
;
2661 num
= CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCPropNetVPNOnDemandSuspended
);
2662 if (isA_CFNumber(num
) &&
2663 CFNumberGetValue(num
, kCFNumberSInt32Type
, &isSuspended
) &&
2664 (isSuspended
!= 0)) {
2669 _SCErrorSet(kSCStatusOK
);
2674 SCNetworkConnectionTriggerOnDemandIfNeeded (CFStringRef hostName
,
2675 Boolean afterDNSFail
,
2679 #if !TARGET_IPHONE_SIMULATOR
2680 __block Boolean triggeredOnDemand
= FALSE
;
2681 struct proc_uniqidentifierinfo procu
;
2682 void *policy_match
= NULL
;
2683 char *hostname
= NULL
;
2684 CFIndex hostnameSize
= 0;
2685 pid_t pid
= getpid();
2686 uid_t uid
= geteuid();
2688 /* Require hostName, require non-root user */
2689 if (hostName
== NULL
|| geteuid() == 0) {
2693 hostnameSize
= CFStringGetLength(hostName
);
2694 if (hostnameSize
== 0) {
2698 hostname
= malloc(hostnameSize
+ 1);
2699 CFStringGetCString(hostName
, hostname
, hostnameSize
+ 1, kCFStringEncodingUTF8
);
2701 if (proc_pidinfo(pid
, PROC_PIDUNIQIDENTIFIERINFO
, 1, &procu
, sizeof(procu
)) != sizeof(procu
)) {
2705 policy_match
= ne_session_copy_policy_match(hostname
, NULL
, NULL
, procu
.p_uuid
, procu
.p_uuid
, pid
, uid
, 0, trafficClass
);
2707 NEPolicyServiceActionType action_type
= ne_session_policy_match_get_service_action(policy_match
);
2708 if (action_type
== NESessionPolicyActionTrigger
||
2709 (afterDNSFail
&& action_type
== NESessionPolicyActionTriggerIfNeeded
)) {
2711 if (ne_session_policy_match_get_service(policy_match
, config_id
)) {
2712 xpc_object_t start_options
= xpc_dictionary_create(NULL
, NULL
, 0);
2713 if (start_options
!= NULL
) {
2714 xpc_dictionary_set_bool(start_options
, NESessionStartOptionIsOnDemandKey
, true);
2715 xpc_dictionary_set_string(start_options
, NESessionStartOptionMatchHostnameKey
, hostname
);
2717 ne_session_t new_session
= ne_session_create(config_id
, ne_session_policy_match_get_service_type(policy_match
));
2718 if (new_session
!= NULL
) {
2719 dispatch_semaphore_t wait_for_session
= dispatch_semaphore_create(0);
2720 dispatch_retain(wait_for_session
);
2721 xpc_retain(start_options
);
2722 ne_session_get_status(new_session
, __SCNetworkConnectionQueue(),
2723 ^(ne_session_status_t status
) {
2724 if (status
== NESessionStatusDisconnected
) {
2725 dispatch_retain(wait_for_session
);
2726 ne_session_set_event_handler(new_session
, __SCNetworkConnectionQueue(),
2727 ^(ne_session_event_t event
, void *event_data
) {
2728 os_activity_t activity_id
;
2730 activity_id
= os_activity_start("processing ne_session notification",
2731 OS_ACTIVITY_FLAG_DEFAULT
);
2733 if (event
== NESessionEventStatusChanged
) {
2734 dispatch_retain(wait_for_session
);
2735 ne_session_get_status(new_session
, __SCNetworkConnectionQueue(),
2736 ^(ne_session_status_t new_status
) {
2737 if (new_status
!= NESessionStatusConnecting
) {
2738 if (status
== NESessionStatusConnected
) {
2739 triggeredOnDemand
= TRUE
;
2741 ne_session_cancel(new_session
);
2743 dispatch_release(wait_for_session
);
2745 } else if (event
== NESessionEventCanceled
) {
2746 dispatch_semaphore_signal(wait_for_session
);
2747 dispatch_release(wait_for_session
);
2750 os_activity_end(activity_id
);
2752 ne_session_start_with_options(new_session
, start_options
);
2754 dispatch_semaphore_signal(wait_for_session
);
2756 dispatch_release(wait_for_session
);
2757 xpc_release(start_options
);
2759 dispatch_semaphore_wait(wait_for_session
, timeout
? dispatch_time(DISPATCH_TIME_NOW
, (int64_t)timeout
* NSEC_PER_SEC
) : DISPATCH_TIME_FOREVER
);
2760 dispatch_release(wait_for_session
);
2761 ne_session_release(new_session
);
2764 xpc_release(start_options
);
2777 return triggeredOnDemand
;
2779 #pragma unused(hostName, afterDNSFail, timeout, trafficClass)
2786 SCNetworkConnectionCopyOnDemandInfo(SCNetworkConnectionRef connection
,
2787 CFStringRef
*onDemandRemoteAddress
,
2788 SCNetworkConnectionStatus
*onDemandConnectionStatus
)
2790 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2792 if (!isA_SCNetworkConnection(connection
)) {
2793 _SCErrorSet(kSCStatusInvalidArgument
);
2797 if (connectionPrivate
->service
== NULL
) {
2798 _SCErrorSet(kSCStatusConnectionNoService
);
2802 if (onDemandRemoteAddress
!= NULL
) {
2803 *onDemandRemoteAddress
= NULL
;
2806 if (onDemandConnectionStatus
!= NULL
) {
2807 *onDemandConnectionStatus
= kSCNetworkConnectionInvalid
;
2810 if (connectionPrivate
->on_demand_info
!= NULL
) {
2811 if (onDemandRemoteAddress
!= NULL
) {
2812 CFStringRef address
=
2813 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandRemoteAddress
);
2814 if (isA_CFString(address
)) {
2815 *onDemandRemoteAddress
= address
;
2816 CFRetain(*onDemandRemoteAddress
);
2820 if (onDemandConnectionStatus
!= NULL
) {
2822 CFNumberRef status_num
=
2823 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandStatus
);
2824 if (isA_CFNumber(status_num
) && CFNumberGetValue(status_num
, kCFNumberIntType
, &num
)) {
2825 *onDemandConnectionStatus
= num
;
2830 return connectionPrivate
->on_demand
;
2835 SCNetworkConnectionGetReachabilityInfo(SCNetworkConnectionRef connection
,
2836 SCNetworkReachabilityFlags
*reach_flags
,
2837 unsigned int *reach_if_index
)
2839 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2841 if (!isA_SCNetworkConnection(connection
)) {
2842 _SCErrorSet(kSCStatusInvalidArgument
);
2846 if (connectionPrivate
->service
== NULL
) {
2847 _SCErrorSet(kSCStatusConnectionNoService
);
2851 if (reach_flags
!= NULL
) {
2855 if (reach_if_index
!= NULL
) {
2856 *reach_if_index
= 0;
2859 if (connectionPrivate
->on_demand_info
!= NULL
) {
2860 if (reach_flags
!= NULL
) {
2862 CFNumberRef flags_num
=
2863 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandReachFlags
);
2864 if (isA_CFNumber(flags_num
) && CFNumberGetValue(flags_num
, kCFNumberIntType
, &num
)) {
2869 if (reach_if_index
!= NULL
) {
2871 CFNumberRef if_index_num
=
2872 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandReachInterfaceIndex
);
2873 if (isA_CFNumber(if_index_num
) && CFNumberGetValue(if_index_num
, kCFNumberIntType
, &num
)) {
2874 *reach_if_index
= num
;
2883 SCNetworkConnectionType
2884 SCNetworkConnectionGetType(SCNetworkConnectionRef connection
)
2886 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2888 if (!isA_SCNetworkConnection(connection
)) {
2889 _SCErrorSet(kSCStatusInvalidArgument
);
2890 return kSCNetworkConnectionTypeUnknown
;
2893 if (connectionPrivate
->service
== NULL
) {
2894 _SCErrorSet(kSCStatusConnectionNoService
);
2895 return kSCNetworkConnectionTypeUnknown
;
2898 _SCErrorSet(kSCStatusOK
);
2900 return connectionPrivate
->type
;
2905 SCNetworkConnectionCopyFlowDivertToken(SCNetworkConnectionRef connection
,
2906 CFDictionaryRef flowProperties
)
2908 #pragma unused(connection, flowProperties)
2909 _SCErrorSet(kSCStatusFailed
);
2915 SCNetworkConnectionGetServiceIdentifier (SCNetworkConnectionRef connection
)
2917 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2918 int service_identifier
= -1;
2920 if (connectionPrivate
->service
!= NULL
) {
2921 service_identifier
= 0;
2922 if (connectionPrivate
->on_demand_info
!= NULL
) {
2923 CFNumberRef id_num
= CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCPropNetDNSServiceIdentifier
);
2925 if (isA_CFNumber(id_num
)) {
2926 CFNumberGetValue(id_num
, kCFNumberIntType
, &service_identifier
);
2931 return service_identifier
;
2935 #if !TARGET_IPHONE_SIMULATOR
2936 SCNetworkConnectionStatus
2937 SCNetworkConnectionGetStatusFromNEStatus(ne_session_status_t status
)
2940 case NESessionStatusInvalid
:
2941 return kSCNetworkConnectionInvalid
;
2942 case NESessionStatusDisconnected
:
2943 return kSCNetworkConnectionDisconnected
;
2944 case NESessionStatusConnecting
:
2945 case NESessionStatusReasserting
:
2946 return kSCNetworkConnectionConnecting
;
2947 case NESessionStatusConnected
:
2948 return kSCNetworkConnectionConnected
;
2949 case NESessionStatusDisconnecting
:
2950 return kSCNetworkConnectionDisconnecting
;
2953 return kSCNetworkConnectionInvalid
;
2955 #endif /* !TARGET_IPHONE_SIMULATOR */
2959 #pragma mark User level "dial" API
2962 #define k_NetworkConnect_Notification "com.apple.networkConnect"
2963 #define k_NetworkConnect_Pref_File CFSTR("com.apple.networkConnect")
2964 #define k_InterentConnect_Pref_File CFSTR("com.apple.internetconnect")
2966 #define k_Dial_Default_Key CFSTR("ConnectByDefault") // needs to go into SC
2967 #define k_Last_Service_Id_Key CFSTR("ServiceID")
2968 #define k_Unique_Id_Key CFSTR("UniqueIdentifier")
2971 /* Private Prototypes */
2972 static Boolean
SCNetworkConnectionPrivateCopyDefaultServiceIDForDial (CFStringRef
*serviceID
);
2973 static Boolean
SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore (CFStringRef
*serviceID
);
2974 static Boolean
SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray
, CFDictionaryRef
*userOptions
);
2975 static Boolean
SCNetworkConnectionPrivateIsPPPService (CFStringRef serviceID
, CFStringRef subType1
, CFStringRef subType2
);
2976 static void addPasswordFromKeychain (CFStringRef serviceID
, CFDictionaryRef
*userOptions
);
2977 static CFStringRef
copyPasswordFromKeychain (CFStringRef uniqueID
);
2979 static int notify_userprefs_token
= -1;
2981 static CFDictionaryRef onDemand_configuration
= NULL
;
2982 static Boolean onDemand_force_refresh
= FALSE
;
2983 static pthread_mutex_t onDemand_notify_lock
= PTHREAD_MUTEX_INITIALIZER
;
2984 static int onDemand_notify_token
= -1;
2988 * return TRUE if domain1 ends with domain2, and will check for trailing "."
2990 #define WILD_CARD_MATCH_STR CFSTR("*")
2992 _SC_domainEndsWithDomain(CFStringRef compare_domain
, CFStringRef match_domain
)
2995 Boolean ret
= FALSE
;
2996 CFStringRef s1
= NULL
;
2997 Boolean s1_created
= FALSE
;
2998 CFStringRef s2
= NULL
;
2999 Boolean s2_created
= FALSE
;
3000 CFStringRef s3
= NULL
;
3002 if (CFEqual(match_domain
, WILD_CARD_MATCH_STR
)) {
3006 if (CFStringHasSuffix(compare_domain
, CFSTR("."))) {
3008 range
.length
= CFStringGetLength(compare_domain
) - 1;
3009 s1
= CFStringCreateWithSubstring(NULL
, compare_domain
, range
);
3015 s1
= compare_domain
;
3018 if (CFStringHasSuffix(match_domain
, CFSTR("."))) {
3020 range
.length
= CFStringGetLength(match_domain
) - 1;
3021 s2
= CFStringCreateWithSubstring(NULL
, match_domain
, range
);
3030 if (CFStringHasPrefix(s2
, CFSTR("*."))) {
3032 range
.length
= CFStringGetLength(s2
)-2;
3033 s3
= CFStringCreateWithSubstring(NULL
, s2
, range
);
3044 ret
= CFStringHasSuffix(s1
, s2
);
3048 if (s1_created
) CFRelease(s1
);
3049 if (s2_created
) CFRelease(s2
);
3053 static CFCharacterSetRef
3054 _SC_getNotDotOrStarCharacterSet (void)
3056 static CFCharacterSetRef notDotOrStar
= NULL
;
3057 if (notDotOrStar
== NULL
) {
3058 CFCharacterSetRef dotOrStar
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, CFSTR(".*"));
3060 notDotOrStar
= CFCharacterSetCreateInvertedSet(kCFAllocatorDefault
, dotOrStar
);
3061 CFRelease(dotOrStar
);
3064 return notDotOrStar
;
3067 static CFMutableStringRef
3068 _SC_createStringByTrimmingDotsAndStars (CFStringRef string
)
3070 CFCharacterSetRef notDotOrStar
= _SC_getNotDotOrStarCharacterSet();
3071 CFRange entireString
= CFRangeMake(0, CFStringGetLength(string
));
3072 CFMutableStringRef result
= CFStringCreateMutableCopy(kCFAllocatorDefault
, entireString
.length
, string
);
3074 CFRange end
= CFRangeMake(entireString
.length
, 0);
3076 if (CFStringFindCharacterFromSet(string
, notDotOrStar
, entireString
, 0, &start
) &&
3077 CFStringFindCharacterFromSet(string
, notDotOrStar
, entireString
, kCFCompareBackwards
, &end
)) {
3078 if (start
.location
== kCFNotFound
|| end
.location
== kCFNotFound
|| start
.location
> end
.location
) {
3084 if ((end
.location
+ 1) < entireString
.length
) {
3085 CFStringReplace(result
, CFRangeMake(end
.location
+ 1, entireString
.length
- (end
.location
+ 1)), CFSTR(""));
3087 if (start
.location
> 0) {
3088 CFStringReplace(result
, CFRangeMake(0, start
.location
), CFSTR(""));
3095 _SC_getCountOfStringInString (CFStringRef string
, CFStringRef substring
)
3098 CFArrayRef ranges
= CFStringCreateArrayWithFindResults(kCFAllocatorDefault
, string
, substring
, CFRangeMake(0, CFStringGetLength(string
)), 0);
3099 if (ranges
!= NULL
) {
3100 count
= CFArrayGetCount(ranges
);
3107 _SC_hostMatchesDomain(CFStringRef hostname
, CFStringRef domain
)
3109 Boolean result
= FALSE
;
3110 CFMutableStringRef trimmedHostname
= NULL
;
3111 CFMutableStringRef trimmedDomain
= NULL
;
3113 if (!isA_CFString(hostname
) || !isA_CFString(domain
)) {
3117 trimmedHostname
= _SC_createStringByTrimmingDotsAndStars(hostname
);
3118 trimmedDomain
= _SC_createStringByTrimmingDotsAndStars(domain
);
3120 if (!isA_CFString(trimmedHostname
) || !isA_CFString(trimmedDomain
)) {
3124 CFIndex numHostnameDots
= _SC_getCountOfStringInString(trimmedHostname
, CFSTR("."));
3125 CFIndex numDomainDots
= _SC_getCountOfStringInString(trimmedDomain
, CFSTR("."));
3126 if (numHostnameDots
== numDomainDots
) {
3127 result
= CFEqual(trimmedHostname
, trimmedDomain
);
3128 } else if (numDomainDots
> 0 && numDomainDots
< numHostnameDots
) {
3129 CFStringReplace(trimmedDomain
, CFRangeMake(0, 0), CFSTR("."));
3130 result
= CFStringHasSuffix(trimmedHostname
, trimmedDomain
);
3136 if (trimmedHostname
) {
3137 CFRelease(trimmedHostname
);
3139 if (trimmedDomain
) {
3140 CFRelease(trimmedDomain
);
3147 static CFDictionaryRef
3148 __SCNetworkConnectionCopyOnDemandConfiguration(void)
3152 uint64_t triggersCount
= 0;
3153 CFDictionaryRef configuration
;
3155 pthread_mutex_lock(&onDemand_notify_lock
);
3156 if (onDemand_notify_token
== -1) {
3157 status
= notify_register_check(kSCNETWORKCONNECTION_ONDEMAND_NOTIFY_KEY
, &onDemand_notify_token
);
3158 if (status
!= NOTIFY_STATUS_OK
) {
3159 SC_log(LOG_NOTICE
, "notify_register_check() failed, status=%d", status
);
3160 onDemand_notify_token
= -1;
3164 if (onDemand_notify_token
!= -1) {
3165 status
= notify_check(onDemand_notify_token
, &changed
);
3166 if (status
!= NOTIFY_STATUS_OK
) {
3167 SC_log(LOG_NOTICE
, "notify_check() failed, status=%d", status
);
3168 (void)notify_cancel(onDemand_notify_token
);
3169 onDemand_notify_token
= -1;
3173 if (changed
&& (onDemand_notify_token
!= -1)) {
3174 status
= notify_get_state(onDemand_notify_token
, &triggersCount
);
3175 if (status
!= NOTIFY_STATUS_OK
) {
3176 SC_log(LOG_NOTICE
, "notify_get_state() failed, status=%d", status
);
3177 (void)notify_cancel(onDemand_notify_token
);
3178 onDemand_notify_token
= -1;
3182 if (changed
|| onDemand_force_refresh
) {
3185 SC_log(LOG_INFO
, "OnDemand information %s",
3186 (onDemand_configuration
== NULL
) ? "fetched" : "updated");
3188 if (onDemand_configuration
!= NULL
) {
3189 CFRelease(onDemand_configuration
);
3190 onDemand_configuration
= NULL
;
3193 if ((triggersCount
> 0) || onDemand_force_refresh
) {
3194 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, kSCDynamicStoreDomainState
, kSCEntNetOnDemand
);
3195 onDemand_configuration
= SCDynamicStoreCopyValue(NULL
, key
);
3197 if ((onDemand_configuration
!= NULL
) && !isA_CFDictionary(onDemand_configuration
)) {
3198 CFRelease(onDemand_configuration
);
3199 onDemand_configuration
= NULL
;
3203 onDemand_force_refresh
= FALSE
;
3206 configuration
= (onDemand_configuration
!= NULL
) ? CFRetain(onDemand_configuration
) : NULL
;
3207 pthread_mutex_unlock(&onDemand_notify_lock
);
3209 return configuration
;
3215 __SCNetworkConnectionForceOnDemandConfigurationRefresh(void)
3217 pthread_mutex_lock(&onDemand_notify_lock
);
3218 onDemand_force_refresh
= TRUE
;
3219 pthread_mutex_unlock(&onDemand_notify_lock
);
3226 __SCNetworkConnectionShouldNeverMatch(CFDictionaryRef trigger
, CFStringRef hostName
, pid_t client_pid
)
3228 CFArrayRef exceptedProcesses
;
3229 CFIndex exceptedProcessesCount
;
3230 CFIndex exceptedProcessesIndex
;
3231 CFArrayRef exceptions
;
3232 CFIndex exceptionsCount
;
3233 int exceptionsIndex
;
3235 // we have a matching domain, check against exception list
3236 exceptions
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandMatchDomainsNever
);
3237 exceptionsCount
= isA_CFArray(exceptions
) ? CFArrayGetCount(exceptions
) : 0;
3238 for (exceptionsIndex
= 0; exceptionsIndex
< exceptionsCount
; exceptionsIndex
++) {
3239 CFStringRef exception
;
3241 exception
= CFArrayGetValueAtIndex(exceptions
, exceptionsIndex
);
3242 if (isA_CFString(exception
) && _SC_domainEndsWithDomain(hostName
, exception
)) {
3243 // found matching exception
3244 SC_log(LOG_INFO
, "OnDemand match exception");
3249 if (client_pid
!= 0) {
3250 exceptedProcesses
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandPluginPIDs
);
3251 exceptedProcessesCount
= isA_CFArray(exceptedProcesses
) ? CFArrayGetCount(exceptedProcesses
) : 0;
3252 for (exceptedProcessesIndex
= 0; exceptedProcessesIndex
< exceptedProcessesCount
; exceptedProcessesIndex
++) {
3256 pidRef
= CFArrayGetValueAtIndex(exceptedProcesses
, exceptedProcessesIndex
);
3257 if (isA_CFNumber(pidRef
) && CFNumberGetValue(pidRef
, kCFNumberIntType
, &pid
)) {
3258 if (pid
== client_pid
) {
3269 __SCNetworkConnectionDomainGetMatchWithParameters(CFStringRef action
, CFPropertyListRef actionParameters
, CFStringRef hostName
, CFStringRef
*probeString
)
3271 CFArrayRef actionArray
= NULL
;
3272 CFIndex actionArraySize
= 0;
3274 CFStringRef matchDomain
= NULL
;
3276 /* For now, only support EvaluateConnection, which takes a CFArray */
3277 if (!CFEqual(action
, kSCValNetVPNOnDemandRuleActionEvaluateConnection
) || !isA_CFArray(actionParameters
)) {
3281 actionArray
= (CFArrayRef
)actionParameters
;
3282 actionArraySize
= CFArrayGetCount(actionArray
);
3284 /* Process domain rules, with actions of ConnectIfNeeded and NeverConnect */
3285 for (i
= 0; i
< actionArraySize
; i
++) {
3286 CFStringRef domainAction
= NULL
;
3287 CFDictionaryRef domainRule
= CFArrayGetValueAtIndex(actionArray
, i
);
3288 CFArrayRef domains
= NULL
;
3289 CFIndex domainsCount
= 0;
3290 CFIndex domainsIndex
;
3292 if (!isA_CFDictionary(domainRule
)) {
3296 domains
= CFDictionaryGetValue(domainRule
, kSCPropNetVPNOnDemandRuleActionParametersDomains
);
3297 if (!isA_CFArray(domains
)) {
3301 domainsCount
= CFArrayGetCount(domains
);
3302 for (domainsIndex
= 0; domainsIndex
< domainsCount
; domainsIndex
++) {
3304 domain
= CFArrayGetValueAtIndex(domains
, domainsIndex
);
3305 if (isA_CFString(domain
) && _SC_domainEndsWithDomain(hostName
, domain
)) {
3306 matchDomain
= domain
;
3312 domainAction
= CFDictionaryGetValue(domainRule
, kSCPropNetVPNOnDemandRuleActionParametersDomainAction
);
3313 if (isA_CFString(domainAction
) && CFEqual(domainAction
, kSCValNetVPNOnDemandRuleActionParametersDomainActionNeverConnect
)) {
3316 /* If we found a match, save the optional probe string as well */
3318 *probeString
= CFDictionaryGetValue(domainRule
, kSCPropNetVPNOnDemandRuleActionParametersRequiredURLStringProbe
);
3329 __SCNetworkConnectionDomainGetMatch(CFDictionaryRef trigger
, CFStringRef hostName
, Boolean onDemandRetry
)
3332 CFIndex domainsCount
;
3335 CFStringRef match_domain
= NULL
;
3337 /* Old configuration: always, never, on retry lists */
3338 key
= onDemandRetry
? kSCNetworkConnectionOnDemandMatchDomainsOnRetry
: kSCNetworkConnectionOnDemandMatchDomainsAlways
;
3340 domains
= CFDictionaryGetValue(trigger
, key
);
3341 domainsCount
= isA_CFArray(domains
) ? CFArrayGetCount(domains
) : 0;
3342 for (domainsIndex
= 0; domainsIndex
< domainsCount
; domainsIndex
++) {
3345 domain
= CFArrayGetValueAtIndex(domains
, domainsIndex
);
3346 if (isA_CFString(domain
) && _SC_domainEndsWithDomain(hostName
, domain
)) {
3347 match_domain
= domain
;
3352 return match_domain
;
3357 __SCNetworkConnectionShouldAlwaysConnect(CFDictionaryRef trigger
)
3359 CFStringRef action
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleAction
);
3360 return (isA_CFString(action
) && CFEqual(action
, kSCValNetVPNOnDemandRuleActionConnect
));
3365 __SCNetworkConnectionShouldIgnoreTrigger(CFDictionaryRef trigger
)
3367 CFStringRef action
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleAction
);
3369 if (isA_CFString(action
) &&
3370 (CFEqual(action
, kSCValNetVPNOnDemandRuleActionIgnore
) ||
3371 CFEqual(action
, kSCValNetVPNOnDemandRuleActionDisconnect
))) {
3379 static CFDictionaryRef
3380 __SCNetworkConnectionCopyMatchingTriggerWithName(CFDictionaryRef configuration
,
3381 CFStringRef hostName
,
3383 Boolean onDemandRetry
,
3384 CFDictionaryRef
*match_info
,
3385 Boolean
*triggerNow
,
3386 CFStringRef
*probe_string
)
3388 CFDictionaryRef result
= NULL
;
3389 int sc_status
= kSCStatusOK
;
3390 CFArrayRef triggers
;
3391 uint64_t triggersCount
= 0;
3393 Boolean usedOnDemandRetry
= FALSE
;
3395 if (triggerNow
!= NULL
) {
3396 *triggerNow
= FALSE
;
3399 if (match_info
!= NULL
) {
3403 triggers
= CFDictionaryGetValue(configuration
, kSCNetworkConnectionOnDemandTriggers
);
3404 triggersCount
= isA_CFArray(triggers
) ? CFArrayGetCount(triggers
) : 0;
3405 for (triggersIndex
= 0; triggersIndex
< triggersCount
; triggersIndex
++) {
3406 CFStringRef matched_domain
= NULL
;
3407 CFStringRef matched_probe_string
= NULL
;
3408 CFDictionaryRef trigger
;
3409 Boolean trigger_matched
= FALSE
;
3411 usedOnDemandRetry
= FALSE
;
3413 trigger
= CFArrayGetValueAtIndex(triggers
, triggersIndex
);
3414 if (!isA_CFDictionary(trigger
)) {
3415 // if not a valid "OnDemand" configuration
3419 if (__SCNetworkConnectionShouldAlwaysConnect(trigger
)) {
3420 /* If the trigger action is 'Connect', always match this trigger */
3421 /* First check the never match list */
3422 if (__SCNetworkConnectionShouldNeverMatch(trigger
, hostName
, client_pid
)) {
3425 trigger_matched
= TRUE
;
3426 } else if (__SCNetworkConnectionShouldIgnoreTrigger(trigger
)) {
3427 /* If the trigger action is 'Ignore' or 'Disconnect', skip this trigger */
3428 sc_status
= kSCStatusConnectionIgnore
;
3431 CFStringRef action
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleAction
);
3432 CFArrayRef actionParameters
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleActionParameters
);
3433 if (action
&& actionParameters
) {
3434 matched_domain
= __SCNetworkConnectionDomainGetMatchWithParameters(action
, actionParameters
, hostName
, &matched_probe_string
);
3435 usedOnDemandRetry
= TRUE
;
3437 if (onDemandRetry
) {
3438 matched_domain
= __SCNetworkConnectionDomainGetMatch(trigger
, hostName
, TRUE
);
3439 usedOnDemandRetry
= TRUE
;
3441 matched_domain
= __SCNetworkConnectionDomainGetMatch(trigger
, hostName
, FALSE
);
3442 if (matched_domain
== NULL
&& result
== NULL
) {
3443 /* Check the retry list if Always failed */
3444 matched_domain
= __SCNetworkConnectionDomainGetMatch(trigger
, hostName
, TRUE
);
3445 usedOnDemandRetry
= TRUE
;
3450 if (matched_domain
) {
3451 if (__SCNetworkConnectionShouldNeverMatch(trigger
, hostName
, client_pid
)) {
3452 matched_domain
= NULL
;
3455 trigger_matched
= TRUE
;
3460 if (trigger_matched
) {
3461 // if we have a matching domain and there were no exceptions
3462 // then we pass back the OnDemand info
3463 if (match_info
!= NULL
) {
3464 CFMutableDictionaryRef minfo
;
3465 SCNetworkConnectionType type
= kSCNetworkConnectionTypeIPLayerVPN
;
3466 CFNumberRef type_num
;
3468 if (*match_info
!= NULL
) {
3469 CFRelease(*match_info
);
3473 minfo
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
3475 &kCFTypeDictionaryKeyCallBacks
,
3476 &kCFTypeDictionaryValueCallBacks
);
3478 type_num
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &type
);
3479 CFDictionarySetValue(minfo
, kSCNetworkConnectionOnDemandMatchInfoVPNType
, type_num
);
3480 CFRelease(type_num
);
3481 if (matched_domain
) {
3482 CFDictionarySetValue(minfo
, kSCNetworkConnectionOnDemandMatchInfoDomain
, matched_domain
);
3484 CFDictionarySetValue(minfo
,
3485 kSCNetworkConnectionOnDemandMatchInfoOnRetry
,
3486 (usedOnDemandRetry
? kCFBooleanTrue
: kCFBooleanFalse
));
3488 *match_info
= minfo
;
3491 if (probe_string
!= NULL
) {
3492 if (*probe_string
!= NULL
) {
3493 CFRelease(*probe_string
);
3494 *probe_string
= NULL
;
3497 if (matched_probe_string
) {
3498 *probe_string
= CFRetain(matched_probe_string
);
3504 /* If retry was requested, or we found Always match, trigger now */
3505 if (onDemandRetry
|| !usedOnDemandRetry
) {
3506 if (triggerNow
!= NULL
) {
3512 /* If we matched the Retry list, but Always was requested,
3513 keep going through triggers in case one matches an Always */
3521 _SCErrorSet(sc_status
);
3526 static CFDictionaryRef
3527 __SCNetworkConnectionCopyTriggerWithService(CFDictionaryRef configuration
,
3528 CFStringRef service_id
)
3530 CFArrayRef triggers
;
3531 uint64_t triggersCount
= 0;
3534 triggers
= CFDictionaryGetValue(configuration
, kSCNetworkConnectionOnDemandTriggers
);
3535 triggersCount
= isA_CFArray(triggers
) ? CFArrayGetCount(triggers
) : 0;
3536 for (triggersIndex
= 0; triggersIndex
< triggersCount
; triggersIndex
++) {
3537 CFDictionaryRef trigger
;
3538 CFStringRef trigger_service_id
;
3540 trigger
= CFArrayGetValueAtIndex(triggers
, triggersIndex
);
3541 if (!isA_CFDictionary(trigger
)) {
3542 // if not a valid "OnDemand" configuration
3546 trigger_service_id
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandServiceID
);
3547 if (isA_CFString(trigger_service_id
) && CFEqual(trigger_service_id
, service_id
)) {
3557 __private_extern__ CFDictionaryRef
3558 __SCNetworkConnectionCopyTokenParameters(SCNetworkConnectionRef connection
)
3560 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
3561 CFDictionaryRef parameters
= NULL
;
3562 uint8_t params_buffer
[PPP_MACH_MAX_INLINE_DATA
];
3563 uint32_t params_buffer_len
= sizeof(params_buffer
);
3564 int sc_status
= kSCStatusOK
;
3565 mach_port_t session_port
;
3566 kern_return_t status
;
3568 pthread_mutex_lock(&connectionPrivate
->lock
);
3570 parameters
= connectionPrivate
->flow_divert_token_params
;
3571 if (parameters
!= NULL
) {
3572 CFRetain(parameters
);
3577 if (parameters
!= NULL
) {
3578 CFRelease(parameters
);
3582 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
3583 if (session_port
== MACH_PORT_NULL
) {
3587 status
= pppcontroller_flow_divert_copy_token_parameters(session_port
, params_buffer
, ¶ms_buffer_len
);
3588 if (status
== KERN_SUCCESS
) {
3589 if (params_buffer_len
> 0) {
3590 CFDataRef params_data
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
,
3594 parameters
= CFPropertyListCreateWithData(kCFAllocatorDefault
,
3596 kCFPropertyListImmutable
,
3599 CFRelease(params_data
);
3603 if (__SCNetworkConnectionNeedsRetry(connection
, "__SCNetworkConnectionCopyTokenParameters()", status
, &sc_status
)) {
3607 if (sc_status
!= kSCStatusOK
) {
3608 _SCErrorSet(sc_status
);
3612 if (parameters
!= NULL
&& connectionPrivate
->flow_divert_token_params
== NULL
) {
3613 connectionPrivate
->flow_divert_token_params
= (CFDictionaryRef
)CFRetain(parameters
);
3616 pthread_mutex_unlock(&connectionPrivate
->lock
);
3622 __SCNetworkConnectionCopyOnDemandInfoWithName(SCDynamicStoreRef
*storeP
,
3623 CFStringRef hostName
,
3624 Boolean onDemandRetry
,
3625 CFStringRef
*connectionServiceID
,
3626 SCNetworkConnectionStatus
*connectionStatus
,
3627 CFStringRef
*vpnRemoteAddress
) /* CFDictionaryRef *info */
3629 CFDictionaryRef configuration
;
3631 int sc_status
= kSCStatusOK
;
3632 CFDictionaryRef trigger
;
3633 Boolean trigger_now
= FALSE
;
3635 configuration
= __SCNetworkConnectionCopyOnDemandConfiguration();
3636 if (configuration
== NULL
) {
3637 _SCErrorSet(sc_status
);
3641 trigger
= __SCNetworkConnectionCopyMatchingTriggerWithName(configuration
, hostName
, 0, onDemandRetry
, NULL
, &trigger_now
, NULL
);
3642 if (trigger
!= NULL
&& trigger_now
) {
3644 SCNetworkConnectionStatus onDemandStatus
= kSCNetworkConnectionDisconnected
;
3648 if (!CFDictionaryGetValueIfPresent(trigger
, kSCNetworkConnectionOnDemandStatus
, (const void **)&num
) ||
3649 !isA_CFNumber(num
) ||
3650 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &onDemandStatus
)) {
3651 onDemandStatus
= kSCNetworkConnectionDisconnected
;
3653 if (connectionStatus
!= NULL
) {
3654 *connectionStatus
= onDemandStatus
;
3657 if (connectionServiceID
!= NULL
) {
3658 *connectionServiceID
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandServiceID
);
3659 *connectionServiceID
= isA_CFString(*connectionServiceID
);
3660 if ((*connectionServiceID
!= NULL
) && (CFStringGetLength(*connectionServiceID
) > 0)) {
3661 CFRetain(*connectionServiceID
);
3663 SC_log(LOG_INFO
, "OnDemand%s configuration error, no serviceID",
3664 onDemandRetry
? " (on retry)" : "");
3665 *connectionServiceID
= NULL
;
3670 if (vpnRemoteAddress
!= NULL
) {
3671 *vpnRemoteAddress
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandRemoteAddress
);
3672 *vpnRemoteAddress
= isA_CFString(*vpnRemoteAddress
);
3673 if ((*vpnRemoteAddress
!= NULL
) && (CFStringGetLength(*vpnRemoteAddress
) > 0)) {
3674 CFRetain(*vpnRemoteAddress
);
3676 SC_log(LOG_INFO
, "OnDemand%s configuration error, no server address",
3677 onDemandRetry
? " (on retry)" : "");
3678 *vpnRemoteAddress
= NULL
;
3684 if ((connectionServiceID
!= NULL
) && (*connectionServiceID
!= NULL
)) {
3685 CFRelease(*connectionServiceID
);
3686 *connectionServiceID
= NULL
;
3688 if ((vpnRemoteAddress
!= NULL
) && (*vpnRemoteAddress
!= NULL
)) {
3689 CFRelease(*vpnRemoteAddress
);
3690 *vpnRemoteAddress
= NULL
;
3692 sc_status
= kSCStatusFailed
;
3694 SC_log(LOG_INFO
, "OnDemand%s match, connection status = %d",
3695 onDemandRetry
? " (on retry)" : "",
3704 // SC_log(LOG_INFO, "OnDemand domain name(s) not matched");
3706 if (configuration
!= NULL
) CFRelease(configuration
);
3708 _SCErrorSet(sc_status
);
3714 __SCNetworkConnectionCopyUserPreferencesInternal(CFDictionaryRef selectionOptions
,
3715 CFStringRef
*serviceID
,
3716 CFDictionaryRef
*userOptions
)
3718 int prefsChanged
= 1;
3720 Boolean success
= FALSE
;
3722 if (notify_userprefs_token
== -1) {
3723 status
= notify_register_check(k_NetworkConnect_Notification
, ¬ify_userprefs_token
);
3724 if (status
!= NOTIFY_STATUS_OK
) {
3725 SC_log(LOG_NOTICE
, "notify_register_check() failed, status=%d", status
);
3726 (void)notify_cancel(notify_userprefs_token
);
3727 notify_userprefs_token
= -1;
3729 // clear the "something has changed" state
3730 (void) notify_check(notify_userprefs_token
, &prefsChanged
);
3734 if (notify_userprefs_token
!= -1) {
3735 status
= notify_check(notify_userprefs_token
, &prefsChanged
);
3736 if (status
!= NOTIFY_STATUS_OK
) {
3737 SC_log(LOG_NOTICE
, "notify_check() failed, status=%d", status
);
3738 (void)notify_cancel(notify_userprefs_token
);
3739 notify_userprefs_token
= -1;
3745 *userOptions
= NULL
;
3747 if (selectionOptions
!= NULL
) {
3748 Boolean catchAllFound
= FALSE
;
3749 CFIndex catchAllService
= 0;
3750 CFIndex catchAllConfig
= 0;
3751 CFStringRef hostName
= NULL
;
3752 CFStringRef priority
= NULL
;
3753 CFArrayRef serviceNames
= NULL
;
3754 CFDictionaryRef services
= NULL
;
3755 CFIndex serviceIndex
;
3756 CFIndex servicesCount
;
3758 hostName
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
);
3759 if (hostName
== NULL
) {
3760 hostName
= CFDictionaryGetValue(selectionOptions
, kSCPropNetPPPOnDemandHostName
);
3762 hostName
= isA_CFString(hostName
);
3763 if (hostName
== NULL
)
3764 goto done_selection
; // if no hostname for matching
3766 priority
= CFDictionaryGetValue(selectionOptions
, kSCPropNetPPPOnDemandPriority
);
3767 if (!isA_CFString(priority
))
3768 priority
= kSCValNetPPPOnDemandPriorityDefault
;
3771 if (!isA_CFArray(serviceNames
))
3772 goto done_selection
;
3775 if (!isA_CFDictionary(services
)) {
3776 goto done_selection
;
3779 servicesCount
= CFArrayGetCount(serviceNames
);
3780 for (serviceIndex
= 0; serviceIndex
< servicesCount
; serviceIndex
++) {
3781 CFIndex configIndex
;
3782 CFIndex configsCount
;
3783 CFArrayRef serviceConfigs
;
3784 CFStringRef serviceName
;
3787 serviceName
= CFArrayGetValueAtIndex(serviceNames
, serviceIndex
);
3788 if (!isA_CFString(serviceName
)) {
3792 serviceConfigs
= CFDictionaryGetValue(services
, serviceName
);
3793 if (!isA_CFArray(serviceConfigs
)) {
3797 configsCount
= CFArrayGetCount(serviceConfigs
);
3798 for (configIndex
= 0; configIndex
< configsCount
; configIndex
++) {
3799 CFNumberRef autodial
;
3800 CFDictionaryRef config
;
3801 CFDictionaryRef pppConfig
;
3803 config
= CFArrayGetValueAtIndex(serviceConfigs
, configIndex
);
3804 if (!isA_CFDictionary(config
)) {
3808 pppConfig
= CFDictionaryGetValue(config
, kSCEntNetPPP
);
3809 if (!isA_CFDictionary(pppConfig
)) {
3813 autodial
= CFDictionaryGetValue(pppConfig
, kSCPropNetPPPOnDemandEnabled
);
3814 if (!isA_CFNumber(autodial
)) {
3818 CFNumberGetValue(autodial
, kCFNumberIntType
, &val
);
3821 CFIndex domainsCount
;
3822 CFIndex domainsIndex
;
3824 /* we found an conditional connection enabled configuration */
3827 domains
= CFDictionaryGetValue(pppConfig
, kSCPropNetPPPOnDemandDomains
);
3828 if (!isA_CFArray(domains
)) {
3832 domainsCount
= CFArrayGetCount(domains
);
3833 for (domainsIndex
= 0; domainsIndex
< domainsCount
; domainsIndex
++) {
3836 domain
= CFArrayGetValueAtIndex(domains
, domainsIndex
);
3837 if (!isA_CFString(domain
)) {
3841 if (!catchAllFound
&&
3842 (CFStringCompare(domain
, CFSTR(""), 0) == kCFCompareEqualTo
3843 || CFStringCompare(domain
, CFSTR("."), 0) == kCFCompareEqualTo
))
3845 // found a catch all
3846 catchAllFound
= TRUE
;
3847 catchAllService
= serviceIndex
;
3848 catchAllConfig
= configIndex
;
3851 if (_SC_domainEndsWithDomain(hostName
, domain
)) {
3852 // found matching configuration
3853 *serviceID
= serviceName
;
3854 CFRetain(*serviceID
);
3855 *userOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, config
);
3856 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
3857 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCPropNetPPPOnDemandPriority
, priority
);
3858 addPasswordFromKeychain(*serviceID
, userOptions
);
3860 goto done_selection
;
3867 // config not found, do we have a catchall ?
3868 if (catchAllFound
) {
3869 CFDictionaryRef config
;
3870 CFArrayRef serviceConfigs
;
3871 CFStringRef serviceName
;
3873 serviceName
= CFArrayGetValueAtIndex(serviceNames
, catchAllService
);
3874 serviceConfigs
= CFDictionaryGetValue(services
, serviceName
);
3875 config
= CFArrayGetValueAtIndex(serviceConfigs
, catchAllConfig
);
3877 *serviceID
= serviceName
;
3878 CFRetain(*serviceID
);
3879 *userOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, config
);
3880 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
3881 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCPropNetPPPOnDemandPriority
, priority
);
3882 addPasswordFromKeychain(*serviceID
, userOptions
);
3884 goto done_selection
;
3890 CFRelease(serviceNames
);
3893 CFRelease(services
);
3897 SC_log(LOG_INFO
, "SCNetworkConnectionCopyUserPreferences %s", success
? "succeeded" : "failed");
3898 SC_log(LOG_INFO
, "Selection options: %@", selectionOptions
);
3904 /* we don't have selection options */
3906 // (1) Figure out which service ID we care about, allocate it into passed "serviceID"
3907 success
= SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(serviceID
);
3909 if (success
&& (*serviceID
!= NULL
)) {
3910 // (2) Get the list of user data for this service ID
3911 CFPropertyListRef userServices
= NULL
;
3914 // (3) We are expecting an array if the user has defined records for this service ID or NULL if the user hasn't
3915 if (userServices
!= NULL
) {
3916 if (isA_CFArray(userServices
)) {
3917 // (4) Get the default set of user options for this service
3918 success
= SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray((CFArrayRef
)userServices
,
3920 if(success
&& (userOptions
!= NULL
)) {
3921 addPasswordFromKeychain(*serviceID
, userOptions
);
3924 SC_log(LOG_INFO
, "Error, userServices are not of type CFArray!");
3927 CFRelease(userServices
); // this is OK because SCNetworkConnectionPrivateISExpectedCFType() checks for NULL
3932 SC_log(LOG_INFO
, "SCNetworkConnectionCopyUserPreferences %@, no selection options",
3933 success
? CFSTR("succeeded") : CFSTR("failed"));
3941 SCNetworkConnectionCopyUserPreferences(CFDictionaryRef selectionOptions
,
3942 CFStringRef
*serviceID
,
3943 CFDictionaryRef
*userOptions
)
3945 Boolean success
= FALSE
;
3948 /* initialize runtime */
3949 pthread_once(&initialized
, __SCNetworkConnectionInitialize
);
3951 /* first check for new VPN OnDemand style */
3952 if (selectionOptions
!= NULL
) {
3953 CFStringRef hostName
;
3955 hostName
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
);
3956 if (isA_CFString(hostName
)) {
3957 CFStringRef connectionServiceID
= NULL
;
3958 SCNetworkConnectionStatus connectionStatus
= kSCNetworkConnectionInvalid
;
3959 Boolean onDemandRetry
;
3962 val
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandRetry
);
3963 onDemandRetry
= isA_CFBoolean(val
) ? CFBooleanGetValue(val
) : TRUE
;
3965 success
= __SCNetworkConnectionCopyOnDemandInfoWithName(NULL
,
3968 &connectionServiceID
,
3972 SC_log(LOG_INFO
, "__SCNetworkConnectionCopyOnDemandInfoWithName: return %d, status %d",
3978 // if the hostname matches an OnDemand domain
3979 if (connectionStatus
== kSCNetworkConnectionConnected
) {
3980 // if we are already connected
3981 if (connectionServiceID
!= NULL
) {
3982 CFRelease(connectionServiceID
);
3987 *serviceID
= connectionServiceID
;
3988 *userOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3989 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
3991 } else if (!onDemandRetry
) {
3992 // if the hostname does not match an OnDemand domain and we have
3993 // not yet issued an initial DNS query (i.e. it's not a query
3994 // being retried after the VPN has been established) then we're
4001 return __SCNetworkConnectionCopyUserPreferencesInternal(selectionOptions
, serviceID
, userOptions
);
4006 SCNetworkConnectionOnDemandShouldRetryOnFailure (SCNetworkConnectionRef connection
)
4008 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
4009 CFDictionaryRef match_info
= NULL
;
4011 if (!isA_SCNetworkConnection(connection
)) {
4012 _SCErrorSet(kSCStatusInvalidArgument
);
4016 if (connectionPrivate
->service
== NULL
) {
4017 _SCErrorSet(kSCStatusConnectionNoService
);
4021 if (isA_CFDictionary(connectionPrivate
->on_demand_user_options
)) {
4022 match_info
= CFDictionaryGetValue(connectionPrivate
->on_demand_user_options
, kSCNetworkConnectionSelectionOptionOnDemandMatchInfo
);
4023 if (isA_CFDictionary(match_info
)) {
4024 CFBooleanRef onRetry
= CFDictionaryGetValue(match_info
, kSCNetworkConnectionOnDemandMatchInfoOnRetry
);
4025 if (isA_CFBoolean(onRetry
)) {
4026 return CFBooleanGetValue(onRetry
);
4036 // Mask is optional in routes dictionary; if not present, whole addresses are matched
4038 __SCNetworkConnectionIPv4AddressMatchesRoutes (struct sockaddr_in
*addr_in
, CFDictionaryRef routes
)
4042 CFDataRef maskData
= NULL
;
4043 struct in_addr
*maskDataArray
;
4044 CFDataRef routeaddrData
= NULL
;
4045 struct in_addr
*routeaddrDataArray
;
4047 if (!isA_CFDictionary(routes
)) {
4051 routeaddrData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoAddresses
);
4052 maskData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoMasks
);
4054 /* routeaddrData and maskData are packed arrays of addresses; make sure they have the same length */
4055 if (!isA_CFData(routeaddrData
) || (maskData
&& (!isA_CFData(maskData
) || CFDataGetLength(routeaddrData
) != CFDataGetLength(maskData
)))) {
4059 routeaddrDataArray
= (struct in_addr
*)(void*)CFDataGetBytePtr(routeaddrData
);
4061 maskDataArray
= (struct in_addr
*)(void*)CFDataGetBytePtr(maskData
);
4064 count
= CFDataGetLength(routeaddrData
) / sizeof(struct in_addr
);
4065 for (i
=0; i
<count
; i
++) {
4066 struct in_addr routeAddr
= *routeaddrDataArray
;
4069 struct in_addr mask
= *maskDataArray
;
4071 if ((addr_in
->sin_addr
.s_addr
& mask
.s_addr
) == (routeAddr
.s_addr
& mask
.s_addr
)) {
4076 if (addr_in
->sin_addr
.s_addr
== routeAddr
.s_addr
) {
4080 routeaddrDataArray
++;
4087 __SCNetworkConnectionMaskIPv6Address(struct in6_addr
*addr
, struct in6_addr
*mask
)
4091 for (i
= 0; i
< sizeof(struct in6_addr
); i
++)
4092 addr
->s6_addr
[i
] &= mask
->s6_addr
[i
];
4096 // Mask is optional in routes dictionary; if not present, whole addresses are matched
4098 __SCNetworkConnectionIPv6AddressMatchesRoutes (struct sockaddr_in6
*addr_in6
, CFDictionaryRef routes
)
4102 CFDataRef maskData
= NULL
;
4103 struct in6_addr
*maskDataArray
;
4104 CFDataRef routeaddrData
= NULL
;
4105 struct in6_addr
*routeaddrDataArray
;
4107 if (!isA_CFDictionary(routes
)) {
4111 routeaddrData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoAddresses
);
4112 maskData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoMasks
);
4114 /* routeaddrData and maskData are packed arrays of addresses; make sure they have the same length */
4115 if (!isA_CFData(routeaddrData
) || (maskData
&& (!isA_CFData(maskData
) || CFDataGetLength(routeaddrData
) != CFDataGetLength(maskData
)))) {
4119 routeaddrDataArray
= (struct in6_addr
*)(void*)CFDataGetBytePtr(routeaddrData
);
4121 maskDataArray
= (struct in6_addr
*)(void*)CFDataGetBytePtr(maskData
);
4124 count
= CFDataGetLength(routeaddrData
) / sizeof(struct in6_addr
);
4125 for (i
=0; i
<count
; i
++) {
4127 struct in6_addr cmpAddr
;
4128 struct in6_addr
*mask
= maskDataArray
;
4129 struct in6_addr routeAddr
;
4131 memcpy(&routeAddr
, routeaddrDataArray
, sizeof(routeAddr
));
4132 memcpy(&cmpAddr
, &addr_in6
->sin6_addr
, sizeof(cmpAddr
));
4133 __SCNetworkConnectionMaskIPv6Address(&routeAddr
, mask
);
4134 __SCNetworkConnectionMaskIPv6Address(&cmpAddr
, mask
);
4136 if (!memcmp(&routeAddr
, &cmpAddr
, sizeof(routeAddr
))) {
4140 if (!memcmp(routeaddrDataArray
, &addr_in6
->sin6_addr
, sizeof(struct in6_addr
))) {
4145 routeaddrDataArray
++;
4152 __SCNetworkConnectionAddressMatchesRedirectedDNS(CFDictionaryRef trigger
, const struct sockaddr
*input_addr
)
4154 CFBooleanRef redirectedRef
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandDNSRedirectDetected
);
4156 if (isA_CFBoolean(redirectedRef
) && CFBooleanGetValue(redirectedRef
)) {
4157 /* DNS is redirected. Look for address list. */
4158 CFDictionaryRef redirectedAddressesRef
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandDNSRedirectedAddresses
);
4160 if (isA_CFDictionary(redirectedAddressesRef
)) {
4161 if (input_addr
->sa_family
== AF_INET
) {
4162 return __SCNetworkConnectionIPv4AddressMatchesRoutes((struct sockaddr_in
*)(void*)input_addr
, CFDictionaryGetValue(redirectedAddressesRef
, kSCNetworkConnectionNetworkInfoIPv4
));
4163 } else if (input_addr
->sa_family
== AF_INET6
) {
4164 return __SCNetworkConnectionIPv6AddressMatchesRoutes((struct sockaddr_in6
*)(void*)input_addr
, CFDictionaryGetValue(redirectedAddressesRef
, kSCNetworkConnectionNetworkInfoIPv6
));
4172 /* If the required probe has failed, we need to tunnel the address. Equivalent to redirected DNS. */
4174 __SCNetworkConnectionRequiredProbeFailed (CFDictionaryRef trigger
, CFStringRef probeString
)
4176 CFDictionaryRef probeResults
= NULL
;
4178 if (!isA_CFString(probeString
)) {
4182 probeResults
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandProbeResults
);
4183 if (!isA_CFDictionary(probeResults
)) {
4187 CFBooleanRef result
= CFDictionaryGetValue(probeResults
, probeString
);
4189 /* Only a value of kCFBooleanFalse marks the probe as failed */
4190 return (isA_CFBoolean(result
) && !CFBooleanGetValue(result
));
4194 SCNetworkConnectionCanTunnelAddress (SCNetworkConnectionRef connection
, const struct sockaddr
*address
, Boolean
*startImmediately
)
4196 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
4197 CFStringRef serviceID
= NULL
;
4198 CFDictionaryRef configuration
= NULL
;
4199 CFDictionaryRef trigger
= NULL
;
4200 CFDictionaryRef tunneledNetworks
= NULL
;
4201 sa_family_t address_family
= AF_UNSPEC
;
4202 Boolean success
= FALSE
;
4204 if (startImmediately
) {
4205 *startImmediately
= FALSE
;
4208 if (address
== NULL
) {
4212 address_family
= address
->sa_family
;
4213 if (address_family
!= AF_INET
&& address_family
!= AF_INET6
) {
4217 if (!isA_SCNetworkConnection(connection
)) {
4218 _SCErrorSet(kSCStatusInvalidArgument
);
4222 if (connectionPrivate
->service
== NULL
) {
4223 _SCErrorSet(kSCStatusConnectionNoService
);
4227 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
4228 if (!isA_CFString(serviceID
)) {
4232 configuration
= __SCNetworkConnectionCopyOnDemandConfiguration();
4233 if (configuration
== NULL
) {
4237 trigger
= __SCNetworkConnectionCopyTriggerWithService(configuration
, serviceID
);
4238 if (trigger
== NULL
) {
4242 if (__SCNetworkConnectionRequiredProbeFailed(trigger
, connectionPrivate
->on_demand_required_probe
)) {
4243 /* If probe failed, we can't trust DNS - connect now */
4244 if (startImmediately
) {
4245 *startImmediately
= TRUE
;
4251 if (__SCNetworkConnectionAddressMatchesRedirectedDNS(trigger
, address
)) {
4252 if (startImmediately
) {
4253 *startImmediately
= TRUE
;
4259 tunneledNetworks
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandTunneledNetworks
);
4260 if (!isA_CFDictionary(tunneledNetworks
)) {
4264 if (address_family
== AF_INET
) {
4265 CFDictionaryRef ip_dict
;
4266 Boolean matches
= FALSE
;
4267 struct sockaddr_in
*addr_in
= (struct sockaddr_in
*)(void*)address
;
4269 ip_dict
= CFDictionaryGetValue(tunneledNetworks
, kSCNetworkConnectionNetworkInfoIPv4
);
4270 if (!isA_CFDictionary(ip_dict
)) {
4274 matches
= __SCNetworkConnectionIPv4AddressMatchesRoutes(addr_in
, CFDictionaryGetValue(ip_dict
, kSCNetworkConnectionNetworkInfoIncludedRoutes
));
4277 if (!__SCNetworkConnectionIPv4AddressMatchesRoutes(addr_in
, CFDictionaryGetValue(ip_dict
, kSCNetworkConnectionNetworkInfoExcludedRoutes
))) {
4283 CFDictionaryRef ip6_dict
;
4284 Boolean matches
= FALSE
;
4285 struct sockaddr_in6
*addr_in6
= (struct sockaddr_in6
*)(void*)address
;
4287 ip6_dict
= CFDictionaryGetValue(tunneledNetworks
, kSCNetworkConnectionNetworkInfoIPv6
);
4288 if (!isA_CFDictionary(ip6_dict
)) {
4292 matches
= __SCNetworkConnectionIPv6AddressMatchesRoutes(addr_in6
, CFDictionaryGetValue(ip6_dict
, kSCNetworkConnectionNetworkInfoIncludedRoutes
));
4295 if (!__SCNetworkConnectionIPv6AddressMatchesRoutes(addr_in6
, CFDictionaryGetValue(ip6_dict
, kSCNetworkConnectionNetworkInfoExcludedRoutes
))) {
4302 if (configuration
) {
4303 CFRelease(configuration
);
4312 SCNetworkConnectionSelectServiceWithOptions(SCNetworkConnectionRef connection
, CFDictionaryRef selectionOptions
)
4314 CFStringRef account_identifier
= NULL
;
4315 CFDictionaryRef configuration
= NULL
;
4316 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
4317 CFDictionaryRef found_trigger
= NULL
;
4318 CFStringRef host_name
= NULL
;
4319 Boolean is_retry
= TRUE
;
4320 CFDictionaryRef match_info
= NULL
;
4321 CFMutableDictionaryRef new_user_options
= NULL
;
4322 SCNetworkConnectionStatus on_demand_status
= kSCNetworkConnectionInvalid
;
4323 CFStringRef requiredProbe
= NULL
;
4324 CFStringRef service_id
= NULL
;
4325 Boolean skip_prefs
= FALSE
;
4326 Boolean success
= TRUE
;
4327 CFDictionaryRef user_options
= NULL
;
4329 if (!isA_SCNetworkConnection(connection
)) {
4330 _SCErrorSet(kSCStatusInvalidArgument
);
4335 /* Can't call this on a connection that is already associated with a service */
4336 if (connectionPrivate
->service
!= NULL
) {
4337 _SCErrorSet(kSCStatusInvalidArgument
);
4342 if (isA_CFDictionary(selectionOptions
)) {
4343 CFBooleanRef no_user_prefs
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionNoUserPrefs
);
4344 CFBooleanRef retry
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandRetry
);
4346 account_identifier
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandAccountIdentifier
);
4347 host_name
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
);
4348 skip_prefs
= (isA_CFBoolean(no_user_prefs
) && CFBooleanGetValue(no_user_prefs
));
4350 if (isA_CFBoolean(retry
)) {
4351 is_retry
= CFBooleanGetValue(retry
);
4355 configuration
= __SCNetworkConnectionCopyOnDemandConfiguration();
4357 /* First, check for a match with the App Layer rules */
4358 service_id
= VPNAppLayerCopyMatchingService(connectionPrivate
->client_audit_token
,
4359 connectionPrivate
->client_pid
,
4360 connectionPrivate
->client_uuid
,
4361 connectionPrivate
->client_bundle_id
,
4365 if (service_id
!= NULL
) {
4366 Boolean use_app_layer
= TRUE
;
4368 if (isA_CFDictionary(configuration
)) {
4369 found_trigger
= __SCNetworkConnectionCopyTriggerWithService(configuration
, service_id
);
4370 if (found_trigger
!= NULL
) {
4371 CFNumberRef status_num
= CFDictionaryGetValue(found_trigger
, kSCNetworkConnectionOnDemandStatus
);
4372 if (isA_CFNumber(status_num
)) {
4373 CFNumberGetValue(status_num
, kCFNumberIntType
, &on_demand_status
);
4376 * If the trigger should be ignored, still use App Layer VPN if it is already connected or
4377 * is in the process of connecting.
4379 if (__SCNetworkConnectionShouldIgnoreTrigger(found_trigger
) &&
4380 on_demand_status
!= kSCNetworkConnectionConnecting
&&
4381 on_demand_status
!= kSCNetworkConnectionConnected
)
4383 use_app_layer
= FALSE
;
4388 if (use_app_layer
) {
4389 /* If this is not the 'OnRetry' call, and the service has not yet started, the match may need to return false */
4391 match_info
!= NULL
&&
4392 on_demand_status
!= kSCNetworkConnectionConnecting
&&
4393 on_demand_status
!= kSCNetworkConnectionConnected
) {
4394 CFBooleanRef matchedOnRetry
= CFDictionaryGetValue(match_info
, kSCNetworkConnectionOnDemandMatchInfoOnRetry
);
4395 if (matchedOnRetry
&& CFBooleanGetValue(matchedOnRetry
)) {
4396 /* Don't return that we matched always; wait for SCNetworkConnectionOnDemandShouldRetryOnFailure */
4400 connectionPrivate
->type
= kSCNetworkConnectionTypeAppLayerVPN
;
4403 CFRelease(service_id
);
4405 if (match_info
!= NULL
) {
4406 CFRelease(match_info
);
4409 if (found_trigger
!= NULL
) {
4410 CFRelease(found_trigger
);
4411 found_trigger
= NULL
;
4416 /* Next, check the IP layer rules */
4417 if (isA_CFDictionary(configuration
) && host_name
!= NULL
) {
4418 Boolean triggerNow
= FALSE
;
4420 found_trigger
= __SCNetworkConnectionCopyMatchingTriggerWithName(configuration
, host_name
, connectionPrivate
->client_pid
, is_retry
, &match_info
, &triggerNow
, &requiredProbe
);
4421 if (found_trigger
!= NULL
) {
4422 service_id
= CFDictionaryGetValue(found_trigger
, kSCNetworkConnectionOnDemandServiceID
);
4423 if (isA_CFString(service_id
)) {
4424 CFRetain(service_id
);
4425 connectionPrivate
->type
= kSCNetworkConnectionTypeIPLayerVPN
;
4433 } else if (!is_retry
) {
4437 if (match_info
!= NULL
) {
4438 CFRelease(match_info
);
4443 /* Next, check the user preferences */
4444 if (!skip_prefs
&& __SCNetworkConnectionCopyUserPreferencesInternal(selectionOptions
, &service_id
, &user_options
)) {
4445 CFMutableDictionaryRef minfo
;
4446 CFNumberRef type_num
;
4448 if (isA_CFDictionary(configuration
)) {
4449 found_trigger
= __SCNetworkConnectionCopyTriggerWithService(configuration
, service_id
);
4451 connectionPrivate
->type
= kSCNetworkConnectionTypePPP
;
4453 minfo
= CFDictionaryCreateMutable(NULL
,
4455 &kCFTypeDictionaryKeyCallBacks
,
4456 &kCFTypeDictionaryValueCallBacks
);
4457 type_num
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &connectionPrivate
->type
);
4458 CFDictionarySetValue(minfo
, kSCNetworkConnectionOnDemandMatchInfoVPNType
, type_num
);
4459 CFRelease(type_num
);
4465 if (service_id
== NULL
) {
4466 _SCErrorSet(kSCStatusOK
);
4471 connectionPrivate
->service
= _SCNetworkServiceCopyActive(NULL
, service_id
);
4472 if (connectionPrivate
->service
== NULL
) {
4473 _SCErrorSet(kSCStatusOK
);
4478 if (found_trigger
!= NULL
) {
4479 if (connectionPrivate
->on_demand_info
) {
4480 CFRelease(connectionPrivate
->on_demand_info
);
4482 connectionPrivate
->on_demand_info
= found_trigger
;
4483 CFRetain(connectionPrivate
->on_demand_info
);
4485 if (on_demand_status
== kSCNetworkConnectionInvalid
) {
4486 CFNumberRef status_num
= CFDictionaryGetValue(found_trigger
, kSCNetworkConnectionOnDemandStatus
);
4487 if (isA_CFNumber(status_num
)) {
4488 CFNumberGetValue(status_num
, kCFNumberIntType
, &on_demand_status
);
4492 if (on_demand_status
!= kSCNetworkConnectionConnected
) {
4493 if (connectionPrivate
->type
== kSCNetworkConnectionTypeAppLayerVPN
) {
4494 /* Check App Layer OnDemand flag */
4495 CFBooleanRef app_on_demand_enabled
=
4496 CFDictionaryGetValue(found_trigger
, kSCNetworkConnectionOnDemandMatchAppEnabled
);
4497 if (isA_CFBoolean(app_on_demand_enabled
) && CFBooleanGetValue(app_on_demand_enabled
)) {
4498 connectionPrivate
->on_demand
= TRUE
;
4501 connectionPrivate
->on_demand
= TRUE
;
4504 } else if (connectionPrivate
->type
== kSCNetworkConnectionTypePPP
) {
4505 /* If we got the service from __SCNetworkConnectionCopyUserPreferencesInternal, then it's on demand */
4506 connectionPrivate
->on_demand
= TRUE
;
4509 if (user_options
== NULL
) {
4510 new_user_options
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
4512 &kCFTypeDictionaryKeyCallBacks
,
4513 &kCFTypeDictionaryValueCallBacks
);
4515 new_user_options
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, user_options
);
4518 if (host_name
!= NULL
) {
4519 CFDictionarySetValue(new_user_options
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, host_name
);
4522 if (connectionPrivate
->on_demand
&& match_info
!= NULL
) {
4523 CFDictionarySetValue(new_user_options
, kSCNetworkConnectionSelectionOptionOnDemandMatchInfo
, match_info
);
4526 connectionPrivate
->on_demand_user_options
= new_user_options
;
4527 CFRetain(connectionPrivate
->on_demand_user_options
);
4529 if (requiredProbe
) {
4530 connectionPrivate
->on_demand_required_probe
= requiredProbe
;
4531 CFRetain(connectionPrivate
->on_demand_required_probe
);
4535 if (service_id
!= NULL
) {
4536 CFRelease(service_id
);
4539 if (configuration
!= NULL
) {
4540 CFRelease(configuration
);
4543 if (found_trigger
!= NULL
) {
4544 CFRelease(found_trigger
);
4547 if (user_options
!= NULL
) {
4548 CFRelease(user_options
);
4551 if (new_user_options
!= NULL
) {
4552 CFRelease(new_user_options
);
4555 if (match_info
!= NULL
) {
4556 CFRelease(match_info
);
4559 if (requiredProbe
!= NULL
) {
4560 CFRelease(requiredProbe
);
4566 //*******************************************************************************************
4567 // SCNetworkConnectionPrivateCopyDefaultServiceIDForDial
4568 // ----------------------------------------------------
4569 // Try to find the service id to connect
4570 // (1) Start by looking at the last service used in Network Pref / Network menu extra
4571 // (2) If Network Pref / Network menu extra has not been used, find the PPP service
4572 // with the highest ordering
4573 //********************************************************************************************
4575 SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(CFStringRef
*serviceID
)
4577 Boolean foundService
= FALSE
;
4578 CFPropertyListRef lastServiceSelectedInIC
= NULL
;
4582 // we found the service the user last had open in IC
4583 if (lastServiceSelectedInIC
!= NULL
) {
4584 // make sure its a PPP service
4585 if (SCNetworkConnectionPrivateIsPPPService(lastServiceSelectedInIC
, kSCValNetInterfaceSubTypePPPSerial
, kSCValNetInterfaceSubTypePPPoE
)) {
4586 // make sure the service that we found is valid
4587 CFDictionaryRef dict
;
4590 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4591 kSCDynamicStoreDomainSetup
,
4592 lastServiceSelectedInIC
,
4593 kSCEntNetInterface
);
4594 dict
= SCDynamicStoreCopyValue(NULL
, key
);
4598 *serviceID
= CFRetain(lastServiceSelectedInIC
);
4599 foundService
= TRUE
;
4602 CFRelease(lastServiceSelectedInIC
);
4605 if (!foundService
) {
4606 foundService
= SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(serviceID
);
4609 return foundService
;
4612 //********************************************************************************
4613 // SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore
4614 // -------------------------------------------------------
4615 // Find the highest ordered PPP service in the dynamic store
4616 //********************************************************************************
4618 SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(CFStringRef
*serviceID
)
4620 CFDictionaryRef dict
= NULL
;
4621 CFStringRef key
= NULL
;
4622 CFArrayRef serviceIDs
= NULL
;
4623 Boolean success
= FALSE
;
4631 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, kSCDynamicStoreDomainSetup
, kSCEntNetIPv4
);
4633 fprintf(stderr
, "Error, Setup Key == NULL!\n");
4637 dict
= SCDynamicStoreCopyValue(NULL
, key
);
4638 if (!isA_CFDictionary(dict
)) {
4639 fprintf(stderr
, "no global IPv4 entity\n");
4643 serviceIDs
= CFDictionaryGetValue(dict
, kSCPropNetServiceOrder
); // array of service id's
4644 if (!isA_CFArray(serviceIDs
)) {
4645 fprintf(stderr
, "service order not specified\n");
4649 count
= CFArrayGetCount(serviceIDs
);
4650 for (i
= 0; i
< count
; i
++) {
4651 CFStringRef service
= CFArrayGetValueAtIndex(serviceIDs
, i
);
4653 if (SCNetworkConnectionPrivateIsPPPService(service
, kSCValNetInterfaceSubTypePPPSerial
, kSCValNetInterfaceSubTypePPPoE
)) {
4654 *serviceID
= CFRetain(service
);
4661 if (key
!= NULL
) CFRelease(key
);
4662 if (dict
!= NULL
) CFRelease(dict
);
4667 //********************************************************************************
4668 // SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray
4669 // ---------------------------------------------------------
4670 // Copy over user preferences for a particular service if they exist
4671 //********************************************************************************
4673 SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray
, CFDictionaryRef
*userOptions
)
4675 CFIndex count
= CFArrayGetCount(userOptionsArray
);
4678 for (i
= 0; i
< count
; i
++) {
4679 // (1) Find the dictionary
4680 CFPropertyListRef propertyList
= CFArrayGetValueAtIndex(userOptionsArray
, i
);
4682 if (isA_CFDictionary(propertyList
) != NULL
) {
4683 // See if there's a value for dial on demand
4684 CFPropertyListRef value
;
4686 value
= CFDictionaryGetValue((CFDictionaryRef
)propertyList
, k_Dial_Default_Key
);
4687 if (isA_CFBoolean(value
) != NULL
) {
4688 if (CFBooleanGetValue(value
)) {
4689 // we found the default user options
4690 *userOptions
= CFDictionaryCreateCopy(NULL
,
4691 (CFDictionaryRef
)propertyList
);
4701 //********************************************************************************
4702 // SCNetworkConnectionPrivateIsServiceType
4703 // --------------------------------------
4704 // Check and see if the service is a PPP service of the given types
4705 //********************************************************************************
4707 SCNetworkConnectionPrivateIsPPPService(CFStringRef serviceID
, CFStringRef subType1
, CFStringRef subType2
)
4709 CFStringRef entityKey
;
4710 Boolean isPPPService
= FALSE
;
4711 Boolean isMatchingSubType
= FALSE
;
4712 CFDictionaryRef serviceDict
;
4714 entityKey
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4715 kSCDynamicStoreDomainSetup
,
4717 kSCEntNetInterface
);
4718 if (entityKey
== NULL
) {
4722 serviceDict
= SCDynamicStoreCopyValue(NULL
, entityKey
);
4723 if (serviceDict
!= NULL
) {
4724 if (isA_CFDictionary(serviceDict
)) {
4726 CFStringRef subtype
;
4728 type
= CFDictionaryGetValue(serviceDict
, kSCPropNetInterfaceType
);
4729 if (isA_CFString(type
)) {
4730 isPPPService
= CFEqual(type
, kSCValNetInterfaceTypePPP
);
4733 subtype
= CFDictionaryGetValue(serviceDict
, kSCPropNetInterfaceSubType
);
4734 if (isA_CFString(subtype
)) {
4735 isMatchingSubType
= CFEqual(subtype
, subType1
);
4736 if (!isMatchingSubType
&& subType2
)
4737 isMatchingSubType
= CFEqual(subtype
, subType2
);
4740 CFRelease(serviceDict
);
4742 CFRelease(entityKey
);
4744 return (isPPPService
&& isMatchingSubType
);
4747 //********************************************************************************
4748 // addPasswordFromKeychain
4749 // --------------------------------------
4750 // Get the password and shared secret out of the keychain and add
4751 // them to the PPP and IPSec dictionaries
4752 //********************************************************************************
4754 addPasswordFromKeychain(CFStringRef serviceID
, CFDictionaryRef
*userOptions
)
4756 CFPropertyListRef uniqueID
;
4757 CFStringRef password
;
4758 CFStringRef sharedsecret
= NULL
;
4760 /* user options must exist */
4761 if (*userOptions
== NULL
)
4764 /* first, get the unique identifier used to store passwords in the keychain */
4765 uniqueID
= CFDictionaryGetValue(*userOptions
, k_Unique_Id_Key
);
4766 if (!isA_CFString(uniqueID
))
4769 /* first, get the PPP password */
4770 password
= copyPasswordFromKeychain(uniqueID
);
4772 /* then, if necessary, get the IPSec Shared Secret */
4773 if (SCNetworkConnectionPrivateIsPPPService(serviceID
, kSCValNetInterfaceSubTypeL2TP
, 0)) {
4774 CFMutableStringRef uniqueIDSS
;
4776 uniqueIDSS
= CFStringCreateMutableCopy(NULL
, 0, uniqueID
);
4777 CFStringAppend(uniqueIDSS
, CFSTR(".SS"));
4778 sharedsecret
= copyPasswordFromKeychain(uniqueIDSS
);
4779 CFRelease(uniqueIDSS
);
4782 /* did we find our information in the key chain ? */
4783 if ((password
!= NULL
) || (sharedsecret
!= NULL
)) {
4784 CFMutableDictionaryRef newOptions
;
4786 newOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, *userOptions
);
4789 if (password
!= NULL
) {
4790 CFDictionaryRef entity
;
4791 CFMutableDictionaryRef newEntity
;
4793 entity
= CFDictionaryGetValue(*userOptions
, kSCEntNetPPP
);
4794 if (isA_CFDictionary(entity
))
4795 newEntity
= CFDictionaryCreateMutableCopy(NULL
, 0, entity
);
4797 newEntity
= CFDictionaryCreateMutable(NULL
,
4799 &kCFTypeDictionaryKeyCallBacks
,
4800 &kCFTypeDictionaryValueCallBacks
);
4803 /* set the PPP password */
4804 CFDictionarySetValue(newEntity
, kSCPropNetPPPAuthPassword
, uniqueID
);
4805 CFDictionarySetValue(newEntity
, kSCPropNetPPPAuthPasswordEncryption
, kSCValNetPPPAuthPasswordEncryptionKeychain
);
4806 CFRelease(password
);
4808 /* update the PPP entity */
4809 CFDictionarySetValue(newOptions
, kSCEntNetPPP
, newEntity
);
4810 CFRelease(newEntity
);
4813 /* IPSec Shared Secret */
4814 if (sharedsecret
!= NULL
) {
4815 CFDictionaryRef entity
;
4816 CFMutableDictionaryRef newEntity
;
4818 entity
= CFDictionaryGetValue(*userOptions
, kSCEntNetIPSec
);
4819 if (isA_CFDictionary(entity
))
4820 newEntity
= CFDictionaryCreateMutableCopy(NULL
, 0, entity
);
4822 newEntity
= CFDictionaryCreateMutable(NULL
,
4824 &kCFTypeDictionaryKeyCallBacks
,
4825 &kCFTypeDictionaryValueCallBacks
);
4827 /* set the IPSec Shared Secret */
4828 CFDictionarySetValue(newEntity
, kSCPropNetIPSecSharedSecret
, sharedsecret
);
4829 CFRelease(sharedsecret
);
4831 /* update the IPSec entity */
4832 CFDictionarySetValue(newOptions
, kSCEntNetIPSec
, newEntity
);
4833 CFRelease(newEntity
);
4836 /* update the userOptions dictionary */
4837 CFRelease(*userOptions
);
4838 *userOptions
= CFDictionaryCreateCopy(NULL
, newOptions
);
4839 CFRelease(newOptions
);
4844 #if !TARGET_OS_IPHONE
4845 //********************************************************************************
4846 // copyKeychainEnumerator
4847 // --------------------------------------
4848 // Gather Keychain Enumerator
4849 //********************************************************************************
4851 copyKeychainEnumerator(CFStringRef uniqueIdentifier
)
4853 CFArrayRef itemArray
= NULL
;
4854 CFMutableDictionaryRef query
;
4857 query
= CFDictionaryCreateMutable(NULL
,
4859 &kCFTypeDictionaryKeyCallBacks
,
4860 &kCFTypeDictionaryValueCallBacks
);
4861 CFDictionarySetValue(query
, kSecClass
, kSecClassGenericPassword
);
4862 CFDictionarySetValue(query
, kSecAttrService
, uniqueIdentifier
);
4863 CFDictionarySetValue(query
, kSecReturnRef
, kCFBooleanTrue
);
4864 CFDictionarySetValue(query
, kSecMatchLimit
, kSecMatchLimitAll
);
4865 result
= SecItemCopyMatching(query
, (CFTypeRef
*)&itemArray
);
4867 if ((result
!= noErr
) && (itemArray
!= NULL
)) {
4868 CFRelease(itemArray
);
4874 #endif // !TARGET_OS_IPHONE
4876 //********************************************************************************
4877 // copyPasswordFromKeychain
4878 // --------------------------------------
4879 // Given a uniqueID, retrieve the password from the keychain
4880 //********************************************************************************
4882 copyPasswordFromKeychain(CFStringRef uniqueID
)
4884 #if !TARGET_OS_IPHONE
4885 CFArrayRef enumerator
;
4887 CFStringRef password
= NULL
;
4889 enumerator
= copyKeychainEnumerator(uniqueID
);
4890 if (enumerator
== NULL
) {
4891 return NULL
; // if no keychain enumerator
4894 n
= CFArrayGetCount(enumerator
);
4898 SecKeychainItemRef itemRef
;
4901 itemRef
= (SecKeychainItemRef
)CFArrayGetValueAtIndex(enumerator
, 0);
4902 result
= SecKeychainItemCopyContent(itemRef
, // itemRef
4906 (void *)&data
); // outData
4907 if ((result
== noErr
) && (data
!= NULL
) && (dataLen
> 0)) {
4908 password
= CFStringCreateWithBytes(NULL
, data
, dataLen
, kCFStringEncodingUTF8
, TRUE
);
4909 (void) SecKeychainItemFreeContent(NULL
, data
);
4914 CFRelease(enumerator
);
4917 #else // !TARGET_OS_IPHONE
4919 #endif // !TARGET_OS_IPHONE
4925 __SCNetworkConnectionGetControllerPortName(void)
4927 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 70000)) && !TARGET_IPHONE_SIMULATOR
4928 if (scnc_server_name
== NULL
){
4929 if (!(sandbox_check(getpid(), "mach-lookup", SANDBOX_FILTER_GLOBAL_NAME
| SANDBOX_CHECK_NO_REPORT
, PPPCONTROLLER_SERVER_PRIV
))){
4930 scnc_server_name
= PPPCONTROLLER_SERVER_PRIV
;
4933 scnc_server_name
= PPPCONTROLLER_SERVER
;
4937 scnc_server_name
= PPPCONTROLLER_SERVER
;
4939 return scnc_server_name
;