2 * Copyright (c) 2003-2018 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 <os/availability.h>
39 #include <TargetConditionals.h>
40 #include <sys/cdefs.h>
41 #include <dispatch/dispatch.h>
42 #include <CoreFoundation/CFXPCBridge.h>
44 #include "SCNetworkConnectionInternal.h"
45 #include "SCNetworkConfigurationInternal.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 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
84 static pthread_mutex_t scnc_lock
= PTHREAD_MUTEX_INITIALIZER
;
85 static mach_port_t scnc_server
= MACH_PORT_NULL
;
86 static char *scnc_server_name
= NULL
;
91 /* base CFType information */
98 SCNetworkServiceRef service
;
100 /* client info (if we are proxying for another process */
101 mach_port_t client_audit_session
;
102 audit_token_t client_audit_token
;
103 mach_port_t client_bootstrap_port
;
108 CFStringRef client_bundle_id
;
110 /* ref to PPP controller for control messages */
111 mach_port_t session_port
;
113 /* ref to PPP controller for notification messages */
114 CFMachPortRef notify_port
;
116 /* keep track of whether we're acquired the initial status */
119 /* run loop source, callout, context, rl scheduling info */
121 CFRunLoopSourceRef rls
;
122 SCNetworkConnectionCallBack rlsFunction
;
123 SCNetworkConnectionContext rlsContext
;
124 CFMutableArrayRef rlList
;
126 /* SCNetworkConnectionSetDispatchQueue */
127 dispatch_group_t dispatchGroup
;
128 dispatch_queue_t dispatchQueue
;
129 dispatch_source_t dispatchSource
;
131 SCNetworkConnectionType type
;
133 CFDictionaryRef on_demand_info
;
134 CFDictionaryRef on_demand_user_options
;
135 CFStringRef on_demand_required_probe
;
137 /* Flow Divert support info */
138 CFDictionaryRef flow_divert_token_params
;
140 #if !TARGET_OS_SIMULATOR
141 /* NetworkExtension data structures */
142 ne_session_t ne_session
;
143 #endif /* !TARGET_OS_SIMULATOR */
144 } SCNetworkConnectionPrivate
, *SCNetworkConnectionPrivateRef
;
147 __private_extern__ os_log_t
148 __log_SCNetworkConnection(void)
150 static os_log_t log
= NULL
;
153 log
= os_log_create("com.apple.SystemConfiguration", "SCNetworkConnection");
160 static __inline__ CFTypeRef
161 isA_SCNetworkConnection(CFTypeRef obj
)
163 return (isA_CFType(obj
, SCNetworkConnectionGetTypeID()));
167 #if !TARGET_OS_SIMULATOR
169 __SCNetworkConnectionUseNetworkExtension(SCNetworkConnectionPrivateRef connectionPrivate
)
171 Boolean result
= FALSE
;
173 if (ne_session_use_as_system_vpn() && connectionPrivate
->service
!= NULL
) {
174 _SCErrorSet(kSCStatusOK
);
175 result
= _SCNetworkServiceIsVPN(connectionPrivate
->service
);
177 * SCNetworkServiceGetInterface (called by _SCNetworkServiceIsVPN) will set the SC error to kSCStatusInvalidArgument if the service does not have an associated prefs object.
178 * In that case, we try to get the service type/subtype from the dynamic store.
180 if (!result
&& SCError() == kSCStatusInvalidArgument
) {
181 CFStringRef interfaceKey
;
182 CFDictionaryRef interfaceDict
;
183 CFStringRef serviceID
;
185 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
186 interfaceKey
= SCDynamicStoreKeyCreateNetworkServiceEntity(kCFAllocatorDefault
,
187 kSCDynamicStoreDomainSetup
,
190 interfaceDict
= SCDynamicStoreCopyValue(NULL
, interfaceKey
);
191 if (isA_CFDictionary(interfaceDict
)) {
192 CFStringRef interfaceType
= CFDictionaryGetValue(interfaceDict
, kSCPropNetInterfaceType
);
193 if (isA_CFString(interfaceType
)) {
194 if (CFEqual(interfaceType
, kSCNetworkInterfaceTypePPP
)) {
195 CFStringRef interfaceSubType
= CFDictionaryGetValue(interfaceDict
, kSCPropNetInterfaceSubType
);
196 #pragma GCC diagnostic push
197 #pragma GCC diagnostic ignored "-Wdeprecated"
198 result
= (isA_CFString(interfaceSubType
) &&
199 (CFEqual(interfaceSubType
, kSCValNetInterfaceSubTypePPTP
) ||
200 CFEqual(interfaceSubType
, kSCValNetInterfaceSubTypeL2TP
)));
201 #pragma GCC diagnostic pop
203 result
= (CFEqual(interfaceType
, kSCNetworkInterfaceTypeVPN
) || CFEqual(interfaceType
, kSCNetworkInterfaceTypeIPSec
));
207 if (interfaceDict
!= NULL
) {
208 CFRelease(interfaceDict
);
210 CFRelease(interfaceKey
);
216 #endif /* !TARGET_OS_SIMULATOR */
220 __SCNetworkConnectionUsingNetworkExtension(SCNetworkConnectionPrivateRef connectionPrivate
)
222 #if !TARGET_OS_SIMULATOR
223 return (connectionPrivate
->ne_session
!= NULL
);
225 #pragma unused(connectionPrivate)
227 #endif /* !TARGET_OS_SIMULATOR */
232 __SCNetworkConnectionCopyDescription(CFTypeRef cf
)
234 CFAllocatorRef allocator
= CFGetAllocator(cf
);
235 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)cf
;
236 CFMutableStringRef result
;
238 result
= CFStringCreateMutable(allocator
, 0);
239 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkConnection, %p [%p]> {"), cf
, allocator
);
240 CFStringAppendFormat(result
, NULL
, CFSTR("service = %p"), connectionPrivate
->service
);
241 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
242 CFStringAppendFormat(result
, NULL
, CFSTR(", server port = 0x%x"), connectionPrivate
->session_port
);
244 CFStringAppendFormat(result
, NULL
, CFSTR("using NetworkExtension = %s"), (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
) ? "yes" : "no"));
245 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
252 __SCNetworkConnectionDeallocate(CFTypeRef cf
)
254 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)cf
;
256 /* release resources */
257 pthread_mutex_destroy(&connectionPrivate
->lock
);
259 if (connectionPrivate
->client_audit_session
!= MACH_PORT_NULL
) {
260 mach_port_mod_refs(mach_task_self(),
261 connectionPrivate
->client_audit_session
,
262 MACH_PORT_RIGHT_SEND
,
266 if (connectionPrivate
->client_bootstrap_port
!= MACH_PORT_NULL
) {
267 mach_port_mod_refs(mach_task_self(),
268 connectionPrivate
->client_bootstrap_port
,
269 MACH_PORT_RIGHT_SEND
,
273 if (connectionPrivate
->client_bundle_id
!= NULL
) {
274 CFRelease(connectionPrivate
->client_bundle_id
);
277 if (connectionPrivate
->rls
!= NULL
) {
278 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
279 CFRelease(connectionPrivate
->rls
);
282 if (connectionPrivate
->rlList
!= NULL
) {
283 CFRelease(connectionPrivate
->rlList
);
286 if (connectionPrivate
->notify_port
!= NULL
) {
287 mach_port_t mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
289 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionDeallocate notify_port", mp
);
290 CFMachPortInvalidate(connectionPrivate
->notify_port
);
291 CFRelease(connectionPrivate
->notify_port
);
292 mach_port_mod_refs(mach_task_self(), mp
, MACH_PORT_RIGHT_RECEIVE
, -1);
295 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
296 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionDeallocate session_port", connectionPrivate
->session_port
);
297 (void) mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
300 if (connectionPrivate
->rlsContext
.release
!= NULL
)
301 (*connectionPrivate
->rlsContext
.release
)(connectionPrivate
->rlsContext
.info
);
303 if (connectionPrivate
->service
!= NULL
) {
304 CFRelease(connectionPrivate
->service
);
307 if (connectionPrivate
->on_demand_info
!= NULL
) {
308 CFRelease(connectionPrivate
->on_demand_info
);
311 if (connectionPrivate
->on_demand_user_options
!= NULL
) {
312 CFRelease(connectionPrivate
->on_demand_user_options
);
315 if (connectionPrivate
->on_demand_required_probe
!= NULL
) {
316 CFRelease(connectionPrivate
->on_demand_required_probe
);
319 if (connectionPrivate
->flow_divert_token_params
!= NULL
) {
320 CFRelease(connectionPrivate
->flow_divert_token_params
);
323 #if !TARGET_OS_SIMULATOR
324 if (connectionPrivate
->ne_session
!= NULL
) {
325 ne_session_set_event_handler(connectionPrivate
->ne_session
, NULL
, NULL
);
326 ne_session_release(connectionPrivate
->ne_session
);
328 #endif /* !TARGET_OS_SIMULATOR */
334 static CFTypeID __kSCNetworkConnectionTypeID
= _kCFRuntimeNotATypeID
;
336 static const CFRuntimeClass __SCNetworkConnectionClass
= {
338 "SCNetworkConnection", // className
341 __SCNetworkConnectionDeallocate
, // dealloc
344 NULL
, // copyFormattingDesc
345 __SCNetworkConnectionCopyDescription
// copyDebugDesc
352 /* the process has forked (and we are the child process) */
354 scnc_server
= MACH_PORT_NULL
;
355 scnc_server_name
= NULL
;
361 __SCNetworkConnectionInitialize(void)
365 /* get the debug environment variable */
366 env
= getenv("PPPDebug");
368 if (sscanf(env
, "%d", &debug
) != 1) {
369 /* PPPDebug value is not valid (or non-numeric), set debug to 1 */
374 /* register with CoreFoundation */
375 __kSCNetworkConnectionTypeID
= _CFRuntimeRegisterClass(&__SCNetworkConnectionClass
);
377 /* add handler to cleanup after fork() */
378 (void) pthread_atfork(NULL
, NULL
, childForkHandler
);
385 __SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection
);
387 #define SC_NETWORK_CONNECTION_QUEUE "SCNetworkConnectionQueue"
389 static dispatch_queue_t
390 __SCNetworkConnectionQueue()
392 static dispatch_once_t once
;
393 static dispatch_queue_t q
;
395 dispatch_once(&once
, ^{
396 q
= dispatch_queue_create(SC_NETWORK_CONNECTION_QUEUE
, NULL
);
404 __SCNetworkConnectionNotify(SCNetworkConnectionRef connection
,
405 SCNetworkConnectionCallBack rlsFunction
,
406 SCNetworkConnectionStatus nc_status
,
407 void (*context_release
)(const void *),
410 #ifdef VERBOSE_ACTIVITY_LOGGING
411 os_activity_t activity
;
413 activity
= os_activity_create("processing SCNetworkConnection notification",
415 OS_ACTIVITY_FLAG_DEFAULT
);
416 os_activity_scope(activity
);
417 #endif // VERBOSE_ACTIVITY_LOGGING
419 SC_log(LOG_DEBUG
, "exec SCNetworkConnection callout");
420 (*rlsFunction
)(connection
, nc_status
, context_info
);
421 if ((context_release
!= NULL
) && (context_info
!= NULL
)) {
422 (*context_release
)(context_info
);
425 #ifdef VERBOSE_ACTIVITY_LOGGING
426 os_release(activity
);
427 #endif // VERBOSE_ACTIVITY_LOGGING
434 __SCNetworkConnectionCallBackRunLoopPerform(SCNetworkConnectionRef connection
,
437 SCNetworkConnectionCallBack rlsFunction
,
438 void (*context_release
)(const void *),
441 SCNetworkConnectionStatus nc_status
;
443 nc_status
= SCNetworkConnectionGetStatus(connection
);
444 CFRunLoopPerformBlock(rl
, rl_mode
, ^{
445 __SCNetworkConnectionNotify(connection
, rlsFunction
, nc_status
, context_release
, context_info
);
446 CFRelease(connection
);
453 __SCNetworkConnectionCallBackDispatchPerform(SCNetworkConnectionRef connection
,
455 SCNetworkConnectionCallBack rlsFunction
,
456 void (*context_release
)(const void *),
459 SCNetworkConnectionStatus nc_status
;
461 nc_status
= SCNetworkConnectionGetStatus(connection
);
463 __SCNetworkConnectionNotify(connection
, rlsFunction
, nc_status
, context_release
, context_info
);
464 CFRelease(connection
);
471 __SCNetworkConnectionCallBack(void *connection
)
473 boolean_t exec_async
= FALSE
;
474 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
476 void (*context_release
)(const void *);
477 CFRunLoopRef rl
= NULL
;
479 SCNetworkConnectionCallBack rlsFunction
= NULL
;
480 dispatch_queue_t q
= NULL
;
481 SCNetworkConnectionStatus nc_status
= kSCNetworkConnectionInvalid
;
483 pthread_mutex_lock(&connectionPrivate
->lock
);
485 if (!connectionPrivate
->scheduled
) {
486 // if not currently scheduled
487 pthread_mutex_unlock(&connectionPrivate
->lock
);
491 rlsFunction
= connectionPrivate
->rlsFunction
;
492 if (rlsFunction
== NULL
) {
493 pthread_mutex_unlock(&connectionPrivate
->lock
);
497 if ((connectionPrivate
->rlsContext
.retain
!= NULL
) && (connectionPrivate
->rlsContext
.info
!= NULL
)) {
498 context_info
= (void *)(*connectionPrivate
->rlsContext
.retain
)(connectionPrivate
->rlsContext
.info
);
499 context_release
= connectionPrivate
->rlsContext
.release
;
501 context_info
= connectionPrivate
->rlsContext
.info
;
502 context_release
= NULL
;
505 #if !TARGET_OS_SIMULATOR
506 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
507 pthread_mutex_unlock(&connectionPrivate
->lock
);
509 nc_status
= SCNetworkConnectionGetStatus(connection
);
510 __SCNetworkConnectionNotify(connection
, rlsFunction
, nc_status
, context_release
, context_info
);
511 CFRelease(connection
); /* This releases the reference that we took in the NESessionEventStatusChanged event handler */
514 #endif /* !TARGET_OS_SIMULATOR */
516 // Do we need to spin a new thread? (either we are running on the main
517 // dispatch queue or main runloop)
518 if (connectionPrivate
->rlList
== NULL
) {
519 // if we are performing the callback on a dispatch queue
520 q
= connectionPrivate
->dispatchQueue
;
521 if (q
== dispatch_get_main_queue()) {
525 rl
= CFRunLoopGetCurrent();
526 if (rl
== CFRunLoopGetMain()) {
531 CFRetain(connection
);
532 pthread_mutex_unlock(&connectionPrivate
->lock
);
535 nc_status
= SCNetworkConnectionGetStatus(connection
);
536 __SCNetworkConnectionNotify(connection
, rlsFunction
, nc_status
, context_release
, context_info
);
537 CFRelease(connection
);
541 if (connectionPrivate
->rlList
== NULL
) {
544 dispatch_async(__SCNetworkConnectionQueue(), ^{
545 __SCNetworkConnectionCallBackDispatchPerform(connection
,
555 rl_mode
= CFRunLoopCopyCurrentMode(rl
);
556 dispatch_async(__SCNetworkConnectionQueue(), ^{
557 __SCNetworkConnectionCallBackRunLoopPerform(connection
,
573 __SCNetworkConnectionMachCallBack(CFMachPortRef port
, void * msg
, CFIndex size
, void * info
)
577 mach_no_senders_notification_t
*buf
= msg
;
578 mach_msg_id_t msgid
= buf
->not_header
.msgh_id
;
579 SCNetworkConnectionRef connection
= (SCNetworkConnectionRef
)info
;
581 if (msgid
== MACH_NOTIFY_NO_SENDERS
) {
582 // re-establish notification
583 SC_log(LOG_INFO
, "PPPController server died");
584 (void)__SCNetworkConnectionReconnectNotifications(connection
);
587 __SCNetworkConnectionCallBack(info
);
594 #pragma mark SCNetworkConnection APIs
598 pppMPCopyDescription(const void *info
)
600 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)info
;
602 return CFStringCreateWithFormat(NULL
,
604 CFSTR("<SCNetworkConnection MP %p> {service = %@, callout = %p}"),
606 connectionPrivate
->service
,
607 connectionPrivate
->rlsFunction
);
611 static SCNetworkConnectionPrivateRef
612 __SCNetworkConnectionCreatePrivate(CFAllocatorRef allocator
,
613 SCNetworkServiceRef service
,
614 SCNetworkConnectionCallBack callout
,
615 SCNetworkConnectionContext
*context
)
617 SCNetworkConnectionPrivateRef connectionPrivate
= NULL
;
620 /* initialize runtime */
621 pthread_once(&initialized
, __SCNetworkConnectionInitialize
);
623 /* allocate NetworkConnection */
624 size
= sizeof(SCNetworkConnectionPrivate
) - sizeof(CFRuntimeBase
);
625 connectionPrivate
= (SCNetworkConnectionPrivateRef
)_CFRuntimeCreateInstance(allocator
, __kSCNetworkConnectionTypeID
, size
, NULL
);
626 if (connectionPrivate
== NULL
) {
630 /* initialize non-zero/NULL members */
631 pthread_mutex_init(&connectionPrivate
->lock
, NULL
);
632 if (service
!= NULL
) {
633 connectionPrivate
->service
= CFRetain(service
);
635 connectionPrivate
->client_uid
= geteuid();
636 connectionPrivate
->client_gid
= getegid();
637 connectionPrivate
->client_pid
= getpid();
638 connectionPrivate
->rlsFunction
= callout
;
640 bcopy(context
, &connectionPrivate
->rlsContext
, sizeof(SCNetworkConnectionContext
));
641 if (context
->retain
!= NULL
) {
642 connectionPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
645 connectionPrivate
->type
= kSCNetworkConnectionTypeUnknown
;
647 #if !TARGET_OS_SIMULATOR
648 if (__SCNetworkConnectionUseNetworkExtension(connectionPrivate
)) {
649 CFStringRef serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
650 if (serviceID
!= NULL
) {
651 uuid_string_t service_uuid_str
;
652 if (CFStringGetCString(serviceID
, service_uuid_str
, sizeof(service_uuid_str
), kCFStringEncodingUTF8
)) {
654 if (uuid_parse(service_uuid_str
, config_id
) == 0) {
655 connectionPrivate
->ne_session
= ne_session_create(config_id
, NESessionTypeVPN
);
660 if (connectionPrivate
->ne_session
== NULL
) {
662 "SCNetworkConnection failed to create an ne_session: service ID %@ is not a valid UUID",
667 #endif /* !TARGET_OS_SIMULATOR */
669 /* success, return the connection reference */
670 return connectionPrivate
;
674 /* failure, clean up and leave */
675 if (connectionPrivate
!= NULL
) {
676 CFRelease(connectionPrivate
);
679 _SCErrorSet(kSCStatusFailed
);
685 __SCNetworkConnectionServerPort(kern_return_t
*status
)
687 mach_port_t server
= MACH_PORT_NULL
;
689 #ifdef BOOTSTRAP_PRIVILEGED_SERVER
690 *status
= bootstrap_look_up2(bootstrap_port
,
691 __SCNetworkConnectionGetControllerPortName(),
694 BOOTSTRAP_PRIVILEGED_SERVER
);
695 #else // BOOTSTRAP_PRIVILEGED_SERVER
696 *status
= bootstrap_look_up(bootstrap_port
, __SCNetworkConnectionGetControllerPortName(), &server
);
697 #endif // BOOTSTRAP_PRIVILEGED_SERVER
700 case BOOTSTRAP_SUCCESS
:
701 // service currently registered, "a good thing" (tm)
703 case BOOTSTRAP_NOT_PRIVILEGED
:
704 // the service is not privileged
706 case BOOTSTRAP_UNKNOWN_SERVICE
:
707 // service not currently registered, try again later
711 SC_log(LOG_DEBUG
, "bootstrap_look_up() failed: status=%s",
712 bootstrap_strerror(*status
));
717 scnc_server_name
= NULL
; /* reset pppcontroller server */
718 return MACH_PORT_NULL
;
722 __SCNetworkConnectionGetCurrentServerPort(void)
728 __SCNetworkConnectionRefreshServerPort(mach_port_t current_server
, int *mach_result
)
730 mach_port_t new_server
;
732 pthread_mutex_lock(&scnc_lock
);
733 if (scnc_server
!= MACH_PORT_NULL
) {
734 if (current_server
== scnc_server
) {
735 scnc_server_name
= NULL
;
736 // if the server we tried returned the error
737 (void)mach_port_deallocate(mach_task_self(), scnc_server
);
738 scnc_server
= __SCNetworkConnectionServerPort(mach_result
);
740 // another thread has refreshed the server port
743 scnc_server
= __SCNetworkConnectionServerPort(mach_result
);
745 new_server
= scnc_server
;
746 pthread_mutex_unlock(&scnc_lock
);
751 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && (!TARGET_OS_SIMULATOR || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 70000))
752 #define HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
756 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && !TARGET_OS_SIMULATOR
757 #define HAVE_PPPCONTROLLER_ATTACHWITHPROXY
761 __SCNetworkConnectionSessionPort(SCNetworkConnectionPrivateRef connectionPrivate
)
765 CFDataRef dataRef
= NULL
;
766 mach_port_t notify_port
= MACH_PORT_NULL
;
767 mach_port_t oldNotify
= MACH_PORT_NULL
;
769 int sc_status
= kSCStatusFailed
;
770 mach_port_t server
= __SCNetworkConnectionGetCurrentServerPort();
771 kern_return_t status
= KERN_SUCCESS
;
773 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
774 mach_port_t au_session
= MACH_PORT_NULL
;
775 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
777 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
778 return connectionPrivate
->session_port
;
781 if (connectionPrivate
->service
== NULL
) {
782 sc_status
= kSCStatusConnectionNoService
;
786 if (!_SCSerializeString(SCNetworkServiceGetServiceID(connectionPrivate
->service
), &dataRef
, &data
, &dataLen
)) {
790 if (connectionPrivate
->notify_port
!= NULL
) {
791 mach_port_t mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
793 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionSessionPort mp", mp
);
794 CFMachPortInvalidate(connectionPrivate
->notify_port
);
795 CFRelease(connectionPrivate
->notify_port
);
796 connectionPrivate
->notify_port
= NULL
;
797 mach_port_mod_refs(mach_task_self(), mp
, MACH_PORT_RIGHT_RECEIVE
, -1);
800 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
801 au_session
= audit_session_self();
802 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
804 // open a new session with the server
806 if ((connectionPrivate
->rlsFunction
!= NULL
) && (notify_port
== MACH_PORT_NULL
)) {
807 // allocate port (for server response)
808 status
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE
, ¬ify_port
);
809 if (status
!= KERN_SUCCESS
) {
810 SC_log(LOG_ERR
, "mach_port_allocate() failed: %s", mach_error_string(status
));
815 // add send right (passed to the server)
816 status
= mach_port_insert_right(mach_task_self(),
819 MACH_MSG_TYPE_MAKE_SEND
);
820 if (status
!= KERN_SUCCESS
) {
821 SC_log(LOG_NOTICE
, "mach_port_insert_right() failed: %s", mach_error_string(status
));
822 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
828 if (server
!= MACH_PORT_NULL
) {
829 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHPROXY
830 if ((connectionPrivate
->client_audit_session
== MACH_PORT_NULL
) &&
831 (connectionPrivate
->client_bootstrap_port
== MACH_PORT_NULL
) &&
832 (connectionPrivate
->client_uid
== geteuid()) &&
833 (connectionPrivate
->client_gid
== getegid()) &&
834 (connectionPrivate
->client_pid
== getpid())
836 #endif // HAVE_PPPCONTROLLER_ATTACHWITHPROXY
837 status
= pppcontroller_attach(server
,
839 (mach_msg_type_number_t
)dataLen
,
842 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
844 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
845 &connectionPrivate
->session_port
,
847 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHPROXY
849 mach_port_t client_au_session
;
850 mach_port_t client_bootstrap_port
;
852 if (connectionPrivate
->client_audit_session
== MACH_PORT_NULL
) {
853 client_au_session
= au_session
;
855 client_au_session
= connectionPrivate
->client_audit_session
;
858 if (connectionPrivate
->client_bootstrap_port
== MACH_PORT_NULL
) {
859 client_bootstrap_port
= bootstrap_port
;
861 client_bootstrap_port
= connectionPrivate
->client_bootstrap_port
;
864 status
= pppcontroller_attach_proxy(server
,
866 (mach_msg_type_number_t
)dataLen
,
867 client_bootstrap_port
,
870 connectionPrivate
->client_uid
,
871 connectionPrivate
->client_gid
,
872 connectionPrivate
->client_pid
,
873 &connectionPrivate
->session_port
,
876 #endif // HAVE_PPPCONTROLLER_ATTACHWITHPROXY
877 if (status
== KERN_SUCCESS
) {
878 if (sc_status
!= kSCStatusOK
) {
879 SC_log(LOG_DEBUG
, "attach w/error, sc_status=%s%s",
880 SCErrorString(sc_status
),
881 (connectionPrivate
->session_port
!= MACH_PORT_NULL
) ? ", w/session_port!=MACH_PORT_NULL" : "");
883 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
884 __MACH_PORT_DEBUG(TRUE
,
885 "*** __SCNetworkConnectionSessionPort session_port (attach w/error, cleanup)",
886 connectionPrivate
->session_port
);
887 mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
888 connectionPrivate
->session_port
= MACH_PORT_NULL
;
891 if (notify_port
!= MACH_PORT_NULL
) {
892 __MACH_PORT_DEBUG(TRUE
,
893 "*** __SCNetworkConnectionSessionPort notify_port (attach w/error, cleanup)",
895 (void) mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
896 notify_port
= MACH_PORT_NULL
;
902 // our [cached] server port is not valid
903 SC_log(LOG_INFO
, "!attach: %s", SCErrorString(status
));
904 if (status
== MACH_SEND_INVALID_DEST
) {
905 // the server is not yet available
906 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionSessionPort notify_port (!dest)", notify_port
);
907 } else if (status
== MIG_SERVER_DIED
) {
908 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionSessionPort notify_port (!mig)", notify_port
);
909 // the server we were using is gone and we've lost our send right
910 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
911 notify_port
= MACH_PORT_NULL
;
913 // if we got an unexpected error, don't retry
919 server
= __SCNetworkConnectionRefreshServerPort(server
, &sc_status
);
920 if (server
== MACH_PORT_NULL
) {
921 // if server not available
922 if (sc_status
== BOOTSTRAP_UNKNOWN_SERVICE
) {
923 // if first retry attempt, wait for SCDynamicStore server
925 SCDynamicStoreRef store
;
927 store
= SCDynamicStoreCreate(NULL
,
928 CFSTR("SCNetworkConnection connect"),
936 // wait up to 2.5 seconds for the [SCNetworkConnection] server
938 if ((retry
+= 50) < 2500) {
939 usleep(50 * 1000); // sleep 50ms between attempts
947 if (notify_port
!= MACH_PORT_NULL
) {
948 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
949 CFMachPortContext context
= { 0
950 , (void *)connectionPrivate
953 , pppMPCopyDescription
956 // request a notification when/if the server dies
957 status
= mach_port_request_notification(mach_task_self(),
959 MACH_NOTIFY_NO_SENDERS
,
962 MACH_MSG_TYPE_MAKE_SEND_ONCE
,
964 if (status
!= KERN_SUCCESS
) {
965 SC_log(LOG_NOTICE
, "mach_port_request_notification() failed: %s", mach_error_string(status
));
966 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
971 if (oldNotify
!= MACH_PORT_NULL
) {
972 SC_log(LOG_NOTICE
, "oldNotify != MACH_PORT_NULL");
975 // create CFMachPort for SCNetworkConnection notification callback
976 connectionPrivate
->notify_port
= _SC_CFMachPortCreateWithPort("SCNetworkConnection",
978 __SCNetworkConnectionMachCallBack
,
981 // we need to try a bit harder to acquire the initial status
982 connectionPrivate
->haveStatus
= FALSE
;
984 // with no server port, release the notification port we allocated
985 __MACH_PORT_DEBUG(TRUE
,
986 "*** __SCNetworkConnectionSessionPort notify_port (!server)",
988 (void) mach_port_mod_refs (mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
989 (void) mach_port_deallocate(mach_task_self(), notify_port
);
990 notify_port
= MACH_PORT_NULL
;
998 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
999 if (au_session
!= MACH_PORT_NULL
) {
1000 (void)mach_port_deallocate(mach_task_self(), au_session
);
1002 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
1004 if (dataRef
!= NULL
) CFRelease(dataRef
);
1006 switch (sc_status
) {
1008 __MACH_PORT_DEBUG(connectionPrivate
->session_port
!= MACH_PORT_NULL
,
1009 "*** __SCNetworkConnectionSessionPort session_port",
1010 connectionPrivate
->session_port
);
1011 __MACH_PORT_DEBUG(notify_port
!= MACH_PORT_NULL
,
1012 "*** __SCNetworkConnectionSessionPort notify_port",
1015 case BOOTSTRAP_UNKNOWN_SERVICE
:
1016 SC_log((status
== KERN_SUCCESS
) ? LOG_NOTICE
: LOG_ERR
, "PPPController not available");
1019 SC_log((status
== KERN_SUCCESS
) ? LOG_NOTICE
: LOG_ERR
, "pppcontroller_attach() failed: %s",
1020 SCErrorString(sc_status
));
1024 if (sc_status
!= kSCStatusOK
) {
1025 _SCErrorSet(sc_status
);
1028 return connectionPrivate
->session_port
;
1033 __SCNetworkConnectionReconnect(SCNetworkConnectionRef connection
)
1035 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1038 port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1039 return (port
!= MACH_PORT_NULL
);
1044 __SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection
)
1046 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1047 dispatch_group_t dispatchGroup
= NULL
;
1048 dispatch_queue_t dispatchQueue
= NULL
;
1050 CFArrayRef rlList
= NULL
;
1052 // Before we fully tearing down our [old] notifications, make sure
1053 // we have retained any information that is needed to re-register the
1054 // [new] notifications.
1056 pthread_mutex_lock(&connectionPrivate
->lock
);
1058 // save and cancel [old] notifications
1059 if (connectionPrivate
->rlList
!= NULL
) {
1060 rlList
= connectionPrivate
->rlList
;
1061 connectionPrivate
->rlList
= NULL
;
1063 if (connectionPrivate
->rls
!= NULL
) {
1064 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
1065 CFRelease(connectionPrivate
->rls
);
1066 connectionPrivate
->rls
= NULL
;
1068 if (connectionPrivate
->dispatchSource
!= NULL
) {
1069 dispatch_source_cancel(connectionPrivate
->dispatchSource
);
1070 connectionPrivate
->dispatchSource
= NULL
;
1073 // make sure dispatchSource is cancelled before removing group/queue
1074 if (connectionPrivate
->dispatchQueue
!= NULL
) {
1075 // save dispatchQueue, release reference when we've queue'd blocks
1076 // complete, allow re-scheduling
1077 dispatchGroup
= connectionPrivate
->dispatchGroup
;
1078 connectionPrivate
->dispatchGroup
= NULL
;
1079 dispatchQueue
= connectionPrivate
->dispatchQueue
;
1080 connectionPrivate
->dispatchQueue
= NULL
;
1082 // and take an extra reference for rescheduling
1083 dispatch_retain(dispatchQueue
);
1086 connectionPrivate
->scheduled
= FALSE
;
1088 pthread_mutex_unlock(&connectionPrivate
->lock
);
1090 if (dispatchGroup
!= NULL
) {
1091 dispatch_group_notify(dispatchGroup
, dispatchQueue
, ^{
1092 // release group/queue references
1093 dispatch_release(dispatchQueue
);
1094 dispatch_release(dispatchGroup
); // releases our connection reference
1099 if (rlList
!= NULL
) {
1103 n
= CFArrayGetCount(rlList
);
1104 for (i
= 0; i
< n
; i
+= 3) {
1105 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(rlList
, i
+1);
1106 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(rlList
, i
+2);
1108 ok
= SCNetworkConnectionScheduleWithRunLoop(connection
, rl
, rlMode
);
1110 if (SCError() != BOOTSTRAP_UNKNOWN_SERVICE
) {
1111 SC_log(LOG_NOTICE
, "SCNetworkConnectionScheduleWithRunLoop() failed");
1116 } else if (dispatchQueue
!= NULL
) {
1117 ok
= SCNetworkConnectionSetDispatchQueue(connection
, dispatchQueue
);
1119 if (SCError() != BOOTSTRAP_UNKNOWN_SERVICE
) {
1120 SC_log(LOG_NOTICE
, "SCNetworkConnectionSetDispatchQueue() failed");
1131 if (rlList
!= NULL
) {
1134 if (dispatchQueue
!= NULL
) {
1135 dispatch_release(dispatchQueue
);
1139 SC_log(LOG_NOTICE
, "SCNetworkConnection server %s, notification not restored",
1140 (SCError() == BOOTSTRAP_UNKNOWN_SERVICE
) ? "shutdown" : "failed");
1148 __SCNetworkConnectionNeedsRetry(SCNetworkConnectionRef connection
,
1149 const char *error_label
,
1150 kern_return_t status
,
1153 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1155 if (status
== KERN_SUCCESS
) {
1159 if ((status
== MACH_SEND_INVALID_DEST
) || (status
== MIG_SERVER_DIED
)) {
1160 // the server's gone and our session port's dead, remove the dead name right
1161 (void) mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
1163 // we got an unexpected error, leave the [session] port alone
1164 SC_log(LOG_NOTICE
, "%s: %s", error_label
, mach_error_string(status
));
1166 connectionPrivate
->session_port
= MACH_PORT_NULL
;
1167 if ((status
== MACH_SEND_INVALID_DEST
) || (status
== MIG_SERVER_DIED
)) {
1168 if (__SCNetworkConnectionReconnect(connection
)) {
1172 *sc_status
= status
;
1179 SCNetworkConnectionGetTypeID(void) {
1180 pthread_once(&initialized
, __SCNetworkConnectionInitialize
); /* initialize runtime */
1181 return __kSCNetworkConnectionTypeID
;
1185 CFArrayRef
/* of SCNetworkServiceRef's */
1186 SCNetworkConnectionCopyAvailableServices(SCNetworkSetRef set
)
1188 CFMutableArrayRef available
;
1189 Boolean tempSet
= FALSE
;
1192 SCPreferencesRef prefs
;
1194 prefs
= SCPreferencesCreate(NULL
, CFSTR("SCNetworkConnectionCopyAvailableServices"), NULL
);
1195 if (prefs
!= NULL
) {
1196 set
= SCNetworkSetCopyCurrent(prefs
);
1202 available
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1205 CFArrayRef services
;
1207 services
= SCNetworkSetCopyServices(set
);
1208 if (services
!= NULL
) {
1212 n
= CFArrayGetCount(services
);
1213 for (i
= 0; i
< n
; i
++) {
1214 SCNetworkInterfaceRef interface
;
1215 CFStringRef interfaceType
;
1216 SCNetworkServiceRef service
;
1218 service
= CFArrayGetValueAtIndex(services
, i
);
1219 interface
= SCNetworkServiceGetInterface(service
);
1220 if (interface
== NULL
) {
1224 interfaceType
= SCNetworkInterfaceGetInterfaceType(interface
);
1225 if (CFEqual(interfaceType
, kSCNetworkInterfaceTypePPP
) ||
1226 CFEqual(interfaceType
, kSCNetworkInterfaceTypeVPN
) ||
1227 CFEqual(interfaceType
, kSCNetworkInterfaceTypeIPSec
)) {
1228 CFArrayAppendValue(available
, service
);
1232 CFRelease(services
);
1236 if (tempSet
&& (set
!= NULL
)) {
1243 SCNetworkConnectionRef
1244 SCNetworkConnectionCreateWithService(CFAllocatorRef allocator
,
1245 SCNetworkServiceRef service
,
1246 SCNetworkConnectionCallBack callout
,
1247 SCNetworkConnectionContext
*context
)
1249 SCNetworkConnectionPrivateRef connectionPrivate
;
1251 if (!isA_SCNetworkService(service
)) {
1252 _SCErrorSet(kSCStatusInvalidArgument
);
1256 if (__SCNetworkServiceIsPPTP(service
)) {
1257 SC_log(LOG_INFO
, "PPTP VPNs are no longer supported");
1258 _SCErrorSet(kSCStatusConnectionIgnore
);
1262 connectionPrivate
= __SCNetworkConnectionCreatePrivate(allocator
, service
, callout
, context
);
1263 return (SCNetworkConnectionRef
)connectionPrivate
;
1267 SCNetworkConnectionRef
1268 SCNetworkConnectionCreateWithServiceID(CFAllocatorRef allocator
,
1269 CFStringRef serviceID
,
1270 SCNetworkConnectionCallBack callout
,
1271 SCNetworkConnectionContext
*context
)
1273 SCNetworkConnectionRef connection
;
1274 SCNetworkServiceRef service
;
1276 if (!isA_CFString(serviceID
)) {
1277 _SCErrorSet(kSCStatusInvalidArgument
);
1281 service
= _SCNetworkServiceCopyActive(NULL
, serviceID
);
1282 if (service
== NULL
) {
1286 connection
= SCNetworkConnectionCreateWithService(allocator
, service
, callout
, context
);
1293 SCNetworkConnectionRef
1294 SCNetworkConnectionCreate(CFAllocatorRef allocator
,
1295 SCNetworkConnectionCallBack callout
,
1296 SCNetworkConnectionContext
*context
)
1298 SCNetworkConnectionPrivateRef connectionPrivate
= __SCNetworkConnectionCreatePrivate(allocator
, NULL
, callout
, context
);
1299 return (SCNetworkConnectionRef
)connectionPrivate
;
1304 SCNetworkConnectionCopyServiceID(SCNetworkConnectionRef connection
)
1306 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1307 CFStringRef serviceID
;
1309 if (!isA_SCNetworkConnection(connection
)) {
1310 _SCErrorSet(kSCStatusInvalidArgument
);
1314 if (connectionPrivate
->service
== NULL
) {
1315 _SCErrorSet(kSCStatusConnectionNoService
);
1319 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
1320 return CFRetain(serviceID
);
1325 SCNetworkConnectionSetClientInfo(SCNetworkConnectionRef connection
,
1326 mach_port_t client_audit_session
,
1331 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1333 if (!isA_SCNetworkConnection(connection
)) {
1334 _SCErrorSet(kSCStatusInvalidArgument
);
1338 // save client audit session port
1339 if (connectionPrivate
->client_audit_session
!= MACH_PORT_NULL
) {
1340 mach_port_mod_refs(mach_task_self(),
1341 connectionPrivate
->client_audit_session
,
1342 MACH_PORT_RIGHT_SEND
,
1344 connectionPrivate
->client_audit_session
= MACH_PORT_NULL
;
1346 connectionPrivate
->client_audit_session
= client_audit_session
;
1347 if (connectionPrivate
->client_audit_session
!= MACH_PORT_NULL
) {
1348 mach_port_mod_refs(mach_task_self(),
1349 connectionPrivate
->client_audit_session
,
1350 MACH_PORT_RIGHT_SEND
,
1354 // save client UID, GID, and PID
1355 connectionPrivate
->client_uid
= client_uid
;
1356 connectionPrivate
->client_gid
= client_gid
;
1357 connectionPrivate
->client_pid
= client_pid
;
1364 SCNetworkConnectionSetClientAuditInfo(SCNetworkConnectionRef connection
,
1365 audit_token_t client_audit_token
,
1366 mach_port_t audit_session
,
1367 mach_port_t bootstrap_port
,
1370 const char *bundle_id
)
1372 const audit_token_t null_audit
= KERNEL_AUDIT_TOKEN_VALUE
;
1373 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1378 if (memcmp(&client_audit_token
, &null_audit
, sizeof(client_audit_token
))) {
1379 #if TARGET_OS_IPHONE
1380 audit_token_to_au32(client_audit_token
, NULL
, &uid
, &gid
, NULL
, NULL
, &pid
, NULL
, NULL
);
1381 #else // TARGET_OS_IPHONE
1382 uid
= audit_token_to_euid(client_audit_token
);
1383 gid
= audit_token_to_egid(client_audit_token
);
1384 pid
= audit_token_to_pid(client_audit_token
);
1385 #endif // TARGET_OS_IPHONE
1390 if (!SCNetworkConnectionSetClientInfo(connection
, audit_session
, uid
, gid
, pid
)) {
1394 if (connectionPrivate
->client_bootstrap_port
!= MACH_PORT_NULL
) {
1395 mach_port_mod_refs(mach_task_self(),
1396 connectionPrivate
->client_bootstrap_port
,
1397 MACH_PORT_RIGHT_SEND
,
1399 connectionPrivate
->client_bootstrap_port
= MACH_PORT_NULL
;
1402 connectionPrivate
->client_bootstrap_port
= bootstrap_port
;
1403 if (connectionPrivate
->client_bootstrap_port
!= MACH_PORT_NULL
) {
1404 mach_port_mod_refs(mach_task_self(),
1405 connectionPrivate
->client_bootstrap_port
,
1406 MACH_PORT_RIGHT_SEND
,
1410 memcpy(&connectionPrivate
->client_audit_token
, &client_audit_token
, sizeof(connectionPrivate
->client_audit_token
));
1412 if (uuid
!= NULL
&& !uuid_is_null(uuid
)) {
1413 uuid_copy(connectionPrivate
->client_uuid
, uuid
);
1416 if (connectionPrivate
->client_bundle_id
!= NULL
) {
1417 CFRelease(connectionPrivate
->client_bundle_id
);
1418 connectionPrivate
->client_bundle_id
= NULL
;
1421 if (bundle_id
!= NULL
) {
1422 connectionPrivate
->client_bundle_id
= CFStringCreateWithCString(kCFAllocatorDefault
, bundle_id
, kCFStringEncodingUTF8
);
1430 SCNetworkConnectionCopyStatistics(SCNetworkConnectionRef connection
)
1432 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1433 xmlDataOut_t data
= NULL
;
1434 mach_msg_type_number_t datalen
= 0;
1435 int sc_status
= kSCStatusFailed
;
1436 mach_port_t session_port
;
1437 CFPropertyListRef statistics
= NULL
;
1438 kern_return_t status
;
1440 if (!isA_SCNetworkConnection(connection
)) {
1441 _SCErrorSet(kSCStatusInvalidArgument
);
1445 pthread_mutex_lock(&connectionPrivate
->lock
);
1447 #if !TARGET_OS_SIMULATOR
1448 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1449 __block xpc_object_t xstats
= NULL
;
1450 ne_session_t ne_session
= connectionPrivate
->ne_session
;
1452 ne_session_retain(ne_session
);
1453 pthread_mutex_unlock(&connectionPrivate
->lock
);
1455 dispatch_semaphore_t ne_sema
= dispatch_semaphore_create(0);
1456 ne_session_get_info(ne_session
, NESessionInfoTypeStatistics
, __SCNetworkConnectionQueue(), ^(xpc_object_t result
) {
1457 if (result
!= NULL
) {
1458 xstats
= xpc_retain(result
);
1460 ne_session_release(ne_session
);
1461 dispatch_semaphore_signal(ne_sema
);
1463 dispatch_semaphore_wait(ne_sema
, DISPATCH_TIME_FOREVER
);
1464 dispatch_release(ne_sema
);
1466 if (xstats
!= NULL
) {
1467 statistics
= _CFXPCCreateCFObjectFromXPCObject(xstats
);
1468 xpc_release(xstats
);
1470 _SCErrorSet(kSCStatusFailed
);
1475 #endif /* !TARGET_OS_SIMULATOR */
1479 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1480 if (session_port
== MACH_PORT_NULL
) {
1484 status
= pppcontroller_copystatistics(session_port
, &data
, &datalen
, &sc_status
);
1485 if (__SCNetworkConnectionNeedsRetry(connection
,
1486 "SCNetworkConnectionCopyStatistics()",
1493 if (!_SCUnserialize(&statistics
, NULL
, data
, datalen
)) {
1494 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
1496 if ((sc_status
== kSCStatusOK
) && !isA_CFDictionary(statistics
)) {
1497 sc_status
= kSCStatusFailed
;
1501 if (sc_status
!= kSCStatusOK
) {
1502 if (statistics
!= NULL
) {
1503 CFRelease(statistics
);
1506 _SCErrorSet(sc_status
);
1511 pthread_mutex_unlock(&connectionPrivate
->lock
);
1517 SCNetworkConnectionGetService(SCNetworkConnectionRef connection
)
1519 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1521 if (!isA_SCNetworkConnection(connection
)) {
1522 _SCErrorSet(kSCStatusInvalidArgument
);
1526 return connectionPrivate
->service
;
1530 SCNetworkConnectionStatus
1531 SCNetworkConnectionGetStatus(SCNetworkConnectionRef connection
)
1533 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1534 SCNetworkConnectionStatus nc_status
= kSCNetworkConnectionInvalid
;
1536 int sc_status
= kSCStatusFailed
;
1537 mach_port_t session_port
;
1538 kern_return_t status
;
1539 CFStringRef serviceID
;
1541 if (!isA_SCNetworkConnection(connection
)) {
1542 _SCErrorSet(kSCStatusInvalidArgument
);
1543 return kSCNetworkConnectionInvalid
;
1546 if (connectionPrivate
->service
== NULL
) {
1547 _SCErrorSet(kSCStatusConnectionNoService
);
1548 return kSCNetworkConnectionInvalid
;
1551 // skip retry and return immediately if we know no service is to be found.
1552 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
1553 if (CFStringGetLength(serviceID
) == 0) {
1554 _SCErrorSet(kSCStatusConnectionNoService
);
1555 return kSCNetworkConnectionInvalid
;
1558 pthread_mutex_lock(&connectionPrivate
->lock
);
1560 #if !TARGET_OS_SIMULATOR
1561 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1562 __block ne_session_status_t ne_status
;
1563 ne_session_t ne_session
= connectionPrivate
->ne_session
;
1565 ne_session_retain(ne_session
);
1566 pthread_mutex_unlock(&connectionPrivate
->lock
);
1568 dispatch_semaphore_t ne_sema
= dispatch_semaphore_create(0);
1569 ne_session_get_status(ne_session
, __SCNetworkConnectionQueue(), ^(ne_session_status_t status
) {
1571 ne_session_release(ne_session
);
1572 dispatch_semaphore_signal(ne_sema
);
1574 dispatch_semaphore_wait(ne_sema
, DISPATCH_TIME_FOREVER
);
1575 dispatch_release(ne_sema
);
1577 return SCNetworkConnectionGetStatusFromNEStatus(ne_status
);
1579 #endif /* !TARGET_OS_SIMULATOR */
1583 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1584 if (session_port
== MACH_PORT_NULL
) {
1585 nc_status
= kSCNetworkConnectionInvalid
;
1589 status
= pppcontroller_getstatus(session_port
, &nc_status
, &sc_status
);
1590 if (__SCNetworkConnectionNeedsRetry(connection
,
1591 "SCNetworkConnectionGetStatus()",
1597 // wait up to 250 ms for the network service to become available
1598 if (!connectionPrivate
->haveStatus
&&
1599 (sc_status
== kSCStatusConnectionNoService
) &&
1600 ((retry
+= 10) < 250)) {
1601 usleep(10 * 1000); // sleep 10ms between attempts
1605 if (sc_status
== kSCStatusOK
) {
1606 connectionPrivate
->haveStatus
= TRUE
;
1608 _SCErrorSet(sc_status
);
1609 nc_status
= kSCNetworkConnectionInvalid
;
1614 pthread_mutex_unlock(&connectionPrivate
->lock
);
1620 SCNetworkConnectionCopyExtendedStatus(SCNetworkConnectionRef connection
)
1622 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1623 xmlDataOut_t data
= NULL
;
1624 mach_msg_type_number_t datalen
= 0;
1625 CFPropertyListRef extstatus
= NULL
;
1627 int sc_status
= kSCStatusFailed
;
1628 mach_port_t session_port
;
1629 kern_return_t status
;
1630 CFStringRef serviceID
;
1632 if (!isA_SCNetworkConnection(connection
)) {
1633 _SCErrorSet(kSCStatusInvalidArgument
);
1637 if (connectionPrivate
->service
== NULL
) {
1638 _SCErrorSet(kSCStatusConnectionNoService
);
1642 // skip retry and return immediately if we know no service is to be found.
1643 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
1644 if (CFStringGetLength(serviceID
) == 0) {
1645 _SCErrorSet(kSCStatusConnectionNoService
);
1649 pthread_mutex_lock(&connectionPrivate
->lock
);
1651 #if !TARGET_OS_SIMULATOR
1652 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1653 __block CFDictionaryRef statusDictionary
= NULL
;
1654 ne_session_t ne_session
= connectionPrivate
->ne_session
;
1656 ne_session_retain(ne_session
);
1657 pthread_mutex_unlock(&connectionPrivate
->lock
);
1659 dispatch_semaphore_t ne_sema
= dispatch_semaphore_create(0);
1660 ne_session_get_info(ne_session
, NESessionInfoTypeExtendedStatus
, __SCNetworkConnectionQueue(), ^(xpc_object_t extended_status
) {
1661 if (extended_status
!= NULL
) {
1662 statusDictionary
= _CFXPCCreateCFObjectFromXPCObject(extended_status
);
1663 ne_session_release(ne_session
);
1664 dispatch_semaphore_signal(ne_sema
);
1666 ne_session_get_status(ne_session
, __SCNetworkConnectionQueue(), ^(ne_session_status_t ne_status
) {
1667 SCNetworkConnectionStatus status
= SCNetworkConnectionGetStatusFromNEStatus(ne_status
);
1668 if (status
!= kSCNetworkConnectionInvalid
) {
1669 CFStringRef keys
[1] = { kSCNetworkConnectionStatus
};
1670 CFNumberRef values
[1] = { NULL
};
1671 values
[0] = CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &status
);
1672 statusDictionary
= CFDictionaryCreate(kCFAllocatorDefault
, (const void **)keys
, (const void **)values
, sizeof(values
) / sizeof(values
[0]), &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1673 CFRelease(values
[0]);
1675 ne_session_release(ne_session
);
1676 dispatch_semaphore_signal(ne_sema
);
1680 dispatch_semaphore_wait(ne_sema
, DISPATCH_TIME_FOREVER
);
1681 dispatch_release(ne_sema
);
1683 if (statusDictionary
!= NULL
) {
1684 extstatus
= (CFPropertyListRef
)statusDictionary
;
1686 _SCErrorSet(kSCStatusFailed
);
1695 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1696 if (session_port
== MACH_PORT_NULL
) {
1700 status
= pppcontroller_copyextendedstatus(session_port
, &data
, &datalen
, &sc_status
);
1701 if (__SCNetworkConnectionNeedsRetry(connection
,
1702 "SCNetworkConnectionCopyExtendedStatus()",
1709 if (!_SCUnserialize(&extstatus
, NULL
, data
, datalen
)) {
1710 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
1712 if ((sc_status
== kSCStatusOK
) && !isA_CFDictionary(extstatus
)) {
1713 sc_status
= kSCStatusFailed
;
1717 // wait up to 250 ms for the network service to become available
1718 if (!connectionPrivate
->haveStatus
&&
1719 (sc_status
== kSCStatusConnectionNoService
) &&
1720 ((retry
+= 10) < 250)) {
1721 usleep(10 * 1000); // sleep 10ms between attempts
1725 if (sc_status
== kSCStatusOK
) {
1726 connectionPrivate
->haveStatus
= TRUE
;
1728 if (extstatus
!= NULL
) {
1729 CFRelease(extstatus
);
1732 _SCErrorSet(sc_status
);
1737 pthread_mutex_unlock(&connectionPrivate
->lock
);
1743 _SCNetworkConnectionMergeDictionaries (const void *key
, const void *value
, void *context
)
1745 /* Add value only if not present */
1746 CFDictionaryAddValue((CFMutableDictionaryRef
)context
, (CFStringRef
)key
, (CFTypeRef
)value
);
1751 SCNetworkConnectionStart(SCNetworkConnectionRef connection
,
1752 CFDictionaryRef userOptions
,
1755 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1756 CFDataRef dataref
= NULL
;
1758 CFIndex datalen
= 0;
1760 int sc_status
= kSCStatusFailed
;
1761 mach_port_t session_port
;
1762 kern_return_t status
;
1764 if (!isA_SCNetworkConnection(connection
)) {
1765 _SCErrorSet(kSCStatusInvalidArgument
);
1769 if ((userOptions
!= NULL
) && !isA_CFDictionary(userOptions
)) {
1770 _SCErrorSet(kSCStatusInvalidArgument
);
1774 if (userOptions
== NULL
) {
1775 userOptions
= connectionPrivate
->on_demand_user_options
;
1776 } else if (connectionPrivate
->on_demand_user_options
!= NULL
) {
1777 CFDictionaryRef localUserOptions
= NULL
;
1779 localUserOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, userOptions
);
1780 if (localUserOptions
) {
1781 CFDictionaryApplyFunction(connectionPrivate
->on_demand_user_options
,
1782 _SCNetworkConnectionMergeDictionaries
,
1783 (void *)localUserOptions
);
1784 CFRelease(connectionPrivate
->on_demand_user_options
);
1785 userOptions
= connectionPrivate
->on_demand_user_options
= localUserOptions
;
1790 CFMutableDictionaryRef mdict
= NULL
;
1792 SC_log(LOG_INFO
, "SCNetworkConnectionStart (%p)", connectionPrivate
);
1794 if (userOptions
!= NULL
) {
1795 CFDictionaryRef dict
;
1796 CFStringRef encryption
;
1797 CFMutableDictionaryRef new_dict
;
1799 /* special code to remove secret information */
1800 mdict
= CFDictionaryCreateMutableCopy(NULL
, 0, userOptions
);
1802 dict
= CFDictionaryGetValue(mdict
, kSCEntNetPPP
);
1803 if (isA_CFDictionary(dict
)) {
1804 encryption
= CFDictionaryGetValue(dict
, kSCPropNetPPPAuthPasswordEncryption
);
1805 if (!isA_CFString(encryption
) ||
1806 !CFEqual(encryption
, kSCValNetPPPAuthPasswordEncryptionKeychain
)) {
1807 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1808 CFDictionaryReplaceValue(new_dict
, kSCPropNetPPPAuthPassword
, CFSTR("******"));
1809 CFDictionarySetValue(mdict
, kSCEntNetPPP
, new_dict
);
1810 CFRelease(new_dict
);
1814 dict
= CFDictionaryGetValue(mdict
, kSCEntNetL2TP
);
1815 if (isA_CFDictionary(dict
)) {
1816 encryption
= CFDictionaryGetValue(dict
, kSCPropNetL2TPIPSecSharedSecretEncryption
);
1817 if (!isA_CFString(encryption
) ||
1818 !CFEqual(encryption
, kSCValNetL2TPIPSecSharedSecretEncryptionKeychain
)) {
1819 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1820 CFDictionaryReplaceValue(new_dict
, kSCPropNetL2TPIPSecSharedSecret
, CFSTR("******"));
1821 CFDictionarySetValue(mdict
, kSCEntNetL2TP
, new_dict
);
1822 CFRelease(new_dict
);
1826 dict
= CFDictionaryGetValue(mdict
, kSCEntNetIPSec
);
1827 if (isA_CFDictionary(dict
)) {
1828 encryption
= CFDictionaryGetValue(dict
, kSCPropNetIPSecSharedSecretEncryption
);
1829 if (!isA_CFString(encryption
) ||
1830 !CFEqual(encryption
, kSCValNetIPSecSharedSecretEncryptionKeychain
)) {
1831 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1832 CFDictionaryReplaceValue(new_dict
, kSCPropNetIPSecSharedSecret
, CFSTR("******"));
1833 CFDictionarySetValue(mdict
, kSCEntNetIPSec
, new_dict
);
1834 CFRelease(new_dict
);
1839 SC_log(LOG_INFO
, "User options: %@", mdict
);
1840 if (mdict
!= NULL
) CFRelease(mdict
);
1843 pthread_mutex_lock(&connectionPrivate
->lock
);
1845 /* Clear out any cached flow divert token parameters */
1846 if (connectionPrivate
->flow_divert_token_params
!= NULL
) {
1847 CFRelease(connectionPrivate
->flow_divert_token_params
);
1848 connectionPrivate
->flow_divert_token_params
= NULL
;
1851 #if !TARGET_OS_SIMULATOR
1852 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1853 xpc_object_t xuser_options
= NULL
;
1855 if (userOptions
!= NULL
) {
1856 xuser_options
= _CFXPCCreateXPCObjectFromCFObject(userOptions
);
1859 if (connectionPrivate
->client_bootstrap_port
!= MACH_PORT_NULL
) {
1860 #if NE_SESSION_VERSION > 2
1861 ne_session_start_on_behalf_of(connectionPrivate
->ne_session
,
1863 connectionPrivate
->client_bootstrap_port
,
1864 connectionPrivate
->client_audit_session
,
1865 connectionPrivate
->client_uid
,
1866 connectionPrivate
->client_gid
,
1867 connectionPrivate
->client_pid
);
1869 ne_session_start_on_behalf_of(connectionPrivate
->ne_session
,
1871 connectionPrivate
->client_bootstrap_port
,
1872 connectionPrivate
->client_audit_session
,
1873 connectionPrivate
->client_uid
,
1874 connectionPrivate
->client_gid
);
1877 ne_session_start_with_options(connectionPrivate
->ne_session
, xuser_options
);
1880 /* make sure the xpc_message goes through */
1881 ne_session_send_barrier(connectionPrivate
->ne_session
);
1883 if (xuser_options
!= NULL
) {
1884 xpc_release(xuser_options
);
1890 #endif /* !TARGET_OS_SIMULATOR */
1892 if (userOptions
&& !_SCSerialize(userOptions
, &dataref
, &data
, &datalen
)) {
1898 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1899 if (session_port
== MACH_PORT_NULL
) {
1900 if (dataref
) CFRelease(dataref
);
1904 status
= pppcontroller_start(session_port
,
1906 (mach_msg_type_number_t
)datalen
,
1909 if (__SCNetworkConnectionNeedsRetry(connection
,
1910 "SCNetworkConnectionStart()",
1916 if (dataref
) CFRelease(dataref
);
1919 SC_log(LOG_INFO
, "SCNetworkConnectionStart (%p), return: %d", connectionPrivate
, sc_status
);
1922 if (sc_status
!= kSCStatusOK
) {
1923 _SCErrorSet(sc_status
);
1927 /* connection is now started */
1931 pthread_mutex_unlock(&connectionPrivate
->lock
);
1937 SCNetworkConnectionStop(SCNetworkConnectionRef connection
,
1938 Boolean forceDisconnect
)
1940 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1942 int sc_status
= kSCStatusFailed
;
1943 mach_port_t session_port
;
1944 kern_return_t status
;
1946 if (!isA_SCNetworkConnection(connection
)) {
1947 _SCErrorSet(kSCStatusInvalidArgument
);
1952 SC_log(LOG_INFO
, "SCNetworkConnectionStop (%p)", connectionPrivate
);
1955 pthread_mutex_lock(&connectionPrivate
->lock
);
1957 #if !TARGET_OS_SIMULATOR
1958 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1959 ne_session_stop(connectionPrivate
->ne_session
);
1960 /* make sure the xpc_message goes through */
1961 ne_session_send_barrier(connectionPrivate
->ne_session
);
1965 #endif /* !TARGET_OS_SIMULATOR */
1969 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1970 if (session_port
== MACH_PORT_NULL
) {
1974 status
= pppcontroller_stop(session_port
, forceDisconnect
, &sc_status
);
1975 if (__SCNetworkConnectionNeedsRetry(connection
,
1976 "SCNetworkConnectionStop()",
1983 SC_log(LOG_INFO
, "SCNetworkConnectionStop (%p), return: %d", connectionPrivate
, sc_status
);
1986 if (sc_status
!= kSCStatusOK
) {
1987 _SCErrorSet(sc_status
);
1991 /* connection is now disconnecting */
1996 pthread_mutex_unlock(&connectionPrivate
->lock
);
2002 SCNetworkConnectionSuspend(SCNetworkConnectionRef connection
)
2004 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2006 int sc_status
= kSCStatusFailed
;
2007 mach_port_t session_port
;
2008 kern_return_t status
;
2010 if (!isA_SCNetworkConnection(connection
)) {
2011 _SCErrorSet(kSCStatusInvalidArgument
);
2016 SC_log(LOG_INFO
, "SCNetworkConnectionSuspend (%p)", connectionPrivate
);
2019 pthread_mutex_lock(&connectionPrivate
->lock
);
2021 #if !!TARGET_OS_SIMULATOR
2022 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2023 /* Suspend only applies to PPPSerial and PPPoE */
2027 #endif /* !TARGET_OS_SIMULATOR */
2031 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2032 if (session_port
== MACH_PORT_NULL
) {
2036 status
= pppcontroller_suspend(session_port
, &sc_status
);
2037 if (__SCNetworkConnectionNeedsRetry(connection
,
2038 "SCNetworkConnectionSuspend()",
2045 SC_log(LOG_INFO
, "SCNetworkConnectionSuspend (%p), return: %d", connectionPrivate
, sc_status
);
2048 if (sc_status
!= kSCStatusOK
) {
2049 _SCErrorSet(sc_status
);
2053 /* connection is now suspended */
2058 pthread_mutex_unlock(&connectionPrivate
->lock
);
2064 SCNetworkConnectionResume(SCNetworkConnectionRef connection
)
2066 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2068 int sc_status
= kSCStatusFailed
;
2069 mach_port_t session_port
;
2070 kern_return_t status
;
2072 if (!isA_SCNetworkConnection(connection
)) {
2073 _SCErrorSet(kSCStatusInvalidArgument
);
2078 SC_log(LOG_INFO
, "SCNetworkConnectionResume (%p)", connectionPrivate
);
2081 pthread_mutex_lock(&connectionPrivate
->lock
);
2083 #if !TARGET_OS_SIMULATOR
2084 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2085 /* Resume only applies to PPPSerial and PPPoE */
2089 #endif /* !TARGET_OS_SIMULATOR */
2093 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2094 if (session_port
== MACH_PORT_NULL
) {
2098 status
= pppcontroller_resume(session_port
, &sc_status
);
2099 if (__SCNetworkConnectionNeedsRetry(connection
,
2100 "SCNetworkConnectionResume()",
2107 SC_log(LOG_INFO
, "SCNetworkConnectionResume (%p), return: %d", connectionPrivate
, sc_status
);
2110 if (sc_status
!= kSCStatusOK
) {
2111 _SCErrorSet(sc_status
);
2115 /* connection is now resume */
2120 pthread_mutex_unlock(&connectionPrivate
->lock
);
2125 #if !TARGET_OS_SIMULATOR
2127 SCNetworkConnectionRefreshOnDemandState(SCNetworkConnectionRef connection
)
2129 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2132 int sc_status
= kSCStatusFailed
;
2133 mach_port_t server_port
= __SCNetworkConnectionGetCurrentServerPort();
2134 kern_return_t status
= KERN_SUCCESS
;
2136 if (!isA_SCNetworkConnection(connection
)) {
2137 _SCErrorSet(kSCStatusInvalidArgument
);
2142 SC_log(LOG_INFO
, "SCNetworkConnectionRefreshOnDemandState (%p)", connectionPrivate
);
2145 pthread_mutex_lock(&connectionPrivate
->lock
);
2148 if (server_port
== MACH_PORT_NULL
) {
2149 server_port
= __SCNetworkConnectionRefreshServerPort(server_port
, &sc_status
);
2150 if (server_port
== MACH_PORT_NULL
) {
2151 // if server not available
2152 if (sc_status
== BOOTSTRAP_UNKNOWN_SERVICE
) {
2153 // wait up to 2.5 seconds for the [SCNetworkConnection] server
2155 if ((retry
+= 50) < 2500) {
2156 usleep(50 * 1000); // sleep 50ms between attempts
2164 status
= pppcontroller_ondemand_refresh_state(server_port
, &sc_status
);
2165 if (status
== KERN_SUCCESS
)
2168 if (status
== MACH_SEND_INVALID_DEST
) {
2169 // the server is not yet available
2170 SC_log(LOG_NOTICE
, "SCNetworkConnectionRefreshOnDemandState (!dest) (%p)", connectionPrivate
);
2171 } else if (status
== MIG_SERVER_DIED
) {
2172 // the server we were using is gone
2173 SC_log(LOG_NOTICE
, "SCNetworkConnectionRefreshOnDemandState (!mig) (%p)", connectionPrivate
);
2175 // if we got an unexpected error, don't retry
2182 SC_log(LOG_INFO
, "SCNetworkConnectionRefreshOnDemandState (%p), return: %d/%d", connectionPrivate
, status
, sc_status
);
2185 if (sc_status
!= kSCStatusOK
) {
2186 _SCErrorSet(sc_status
);
2194 pthread_mutex_unlock(&connectionPrivate
->lock
);
2197 #endif /* !TARGET_OS_SIMULATOR */
2201 SCNetworkConnectionCopyUserOptions(SCNetworkConnectionRef connection
)
2203 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2204 xmlDataOut_t data
= NULL
;
2205 mach_msg_type_number_t datalen
= 0;
2206 int sc_status
= kSCStatusFailed
;
2207 mach_port_t session_port
;
2208 kern_return_t status
;
2209 CFPropertyListRef userOptions
= NULL
;
2211 if (!isA_SCNetworkConnection(connection
)) {
2212 _SCErrorSet(kSCStatusInvalidArgument
);
2216 pthread_mutex_lock(&connectionPrivate
->lock
);
2218 #if !TARGET_OS_SIMULATOR
2219 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2220 __block xpc_object_t config
= NULL
;
2221 ne_session_t ne_session
= connectionPrivate
->ne_session
;
2223 ne_session_retain(ne_session
);
2224 pthread_mutex_unlock(&connectionPrivate
->lock
);
2226 dispatch_semaphore_t ne_sema
= dispatch_semaphore_create(0);
2227 ne_session_get_info(ne_session
, NESessionInfoTypeConfiguration
, __SCNetworkConnectionQueue(), ^(xpc_object_t result
) {
2228 if (result
!= NULL
) {
2229 config
= xpc_retain(result
);
2231 ne_session_release(ne_session
);
2232 dispatch_semaphore_signal(ne_sema
);
2234 dispatch_semaphore_wait(ne_sema
, DISPATCH_TIME_FOREVER
);
2235 dispatch_release(ne_sema
);
2237 if (config
!= NULL
) {
2238 xpc_object_t xoptions
= xpc_dictionary_get_value(config
, NESMSessionLegacyUserConfigurationKey
);
2239 if (xoptions
!= NULL
) {
2240 userOptions
= _CFXPCCreateCFObjectFromXPCObject(xoptions
);
2242 xpc_release(config
);
2246 #endif /* !TARGET_OS_SIMULATOR */
2250 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2251 if (session_port
== MACH_PORT_NULL
) {
2255 status
= pppcontroller_copyuseroptions(session_port
, &data
, &datalen
, &sc_status
);
2256 if (__SCNetworkConnectionNeedsRetry(connection
,
2257 "SCNetworkConnectionCopyUserOptions()",
2264 if (!_SCUnserialize(&userOptions
, NULL
, data
, datalen
)) {
2265 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
2267 if ((sc_status
== kSCStatusOK
) && (userOptions
!= NULL
) && !isA_CFDictionary(userOptions
)) {
2268 sc_status
= kSCStatusFailed
;
2272 if (sc_status
== kSCStatusOK
) {
2273 if (userOptions
== NULL
) {
2274 // if no user options, return an empty dictionary
2275 userOptions
= CFDictionaryCreate(NULL
,
2279 &kCFTypeDictionaryKeyCallBacks
,
2280 &kCFTypeDictionaryValueCallBacks
);
2284 CFRelease(userOptions
);
2287 _SCErrorSet(sc_status
);
2292 pthread_mutex_unlock(&connectionPrivate
->lock
);
2298 __SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection
,
2299 CFRunLoopRef runLoop
,
2300 CFStringRef runLoopMode
,
2301 dispatch_queue_t queue
)
2303 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2305 int sc_status
= kSCStatusFailed
;
2306 mach_port_t session_port
;
2307 kern_return_t status
;
2309 pthread_mutex_lock(&connectionPrivate
->lock
);
2311 if (connectionPrivate
->rlsFunction
== NULL
) {
2312 _SCErrorSet(kSCStatusInvalidArgument
);
2316 if ((connectionPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled on a dispatch queue
2317 ((queue
!= NULL
) && connectionPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
2318 _SCErrorSet(kSCStatusInvalidArgument
);
2322 if (!connectionPrivate
->scheduled
) {
2326 if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2327 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2328 if (session_port
== MACH_PORT_NULL
) {
2332 status
= pppcontroller_notification(session_port
, 1, &sc_status
);
2333 if (__SCNetworkConnectionNeedsRetry(connection
,
2334 "__SCNetworkConnectionScheduleWithRunLoop()",
2340 if (sc_status
!= kSCStatusOK
) {
2341 _SCErrorSet(sc_status
);
2345 if (runLoop
!= NULL
) {
2346 connectionPrivate
->rls
= CFMachPortCreateRunLoopSource(NULL
, connectionPrivate
->notify_port
, 0);
2347 connectionPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2349 } else if (runLoop
!= NULL
) {
2350 CFRunLoopSourceContext rlsContext
= {
2352 (void *)connection
, // info
2355 NULL
, // copy description
2360 __SCNetworkConnectionCallBack
, // perform
2363 connectionPrivate
->rls
= CFRunLoopSourceCreate(kCFAllocatorDefault
, 0, &rlsContext
);
2364 connectionPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2367 connectionPrivate
->scheduled
= TRUE
;
2370 if (queue
!= NULL
) {
2371 // retain the dispatch queue
2372 connectionPrivate
->dispatchQueue
= queue
;
2373 dispatch_retain(connectionPrivate
->dispatchQueue
);
2375 if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2376 dispatch_group_t group
= NULL
;
2378 dispatch_source_t source
;
2381 // We've taken a reference to the caller's dispatch_queue and we
2382 // want to hold on to that reference until we've processed any/all
2383 // notifications. To facilitate this we create a group, dispatch
2384 // any notification blocks via that group, and when the caller
2385 // has told us to stop the notifications (unschedule) we wait for
2386 // the group to empty and use the group's finalizer to release
2387 // our reference to the SCNetworkConnection.
2389 group
= dispatch_group_create();
2390 connectionPrivate
->dispatchGroup
= group
;
2391 CFRetain(connection
);
2392 dispatch_set_context(connectionPrivate
->dispatchGroup
, (void *)connection
);
2393 dispatch_set_finalizer_f(connectionPrivate
->dispatchGroup
, (dispatch_function_t
)CFRelease
);
2395 mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
2396 source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
, mp
, 0, queue
);
2397 if (source
== NULL
) {
2398 SC_log(LOG_NOTICE
, "dispatch_source_create() failed");
2399 _SCErrorSet(kSCStatusFailed
);
2403 // have our dispatch source hold a reference to the notification CFMachPort
2404 CFRetain(connectionPrivate
->notify_port
);
2405 dispatch_set_context(source
, (void *)connectionPrivate
->notify_port
);
2406 dispatch_set_finalizer_f(source
, (dispatch_function_t
)CFRelease
);
2408 dispatch_source_set_event_handler(source
, ^{
2411 u_int8_t buf
[sizeof(mach_msg_empty_t
) + MAX_TRAILER_SIZE
];
2412 mach_msg_empty_rcv_t msg
;
2413 mach_no_senders_notification_t no_senders
;
2414 } *notify_message_t
;
2415 CFMachPortRef notify_port
;
2416 notify_message_t notify_msg
;
2418 notify_msg
= (notify_message_t
)malloc(sizeof(*notify_msg
));
2420 kr
= mach_msg(¬ify_msg
->msg
.header
, // msg
2421 MACH_RCV_MSG
, // options
2423 sizeof(*notify_msg
), // rcv_size
2425 MACH_MSG_TIMEOUT_NONE
, // timeout
2426 MACH_PORT_NULL
); // notify
2427 if (kr
!= KERN_SUCCESS
) {
2428 SC_log(LOG_NOTICE
, "SCDynamicStore notification handler, kr=0x%x", kr
);
2433 CFRetain(connection
);
2434 notify_port
= dispatch_get_context(source
);
2436 dispatch_group_async(group
, queue
, ^{
2437 __SCNetworkConnectionMachCallBack(notify_port
,
2439 sizeof(*notify_msg
),
2440 (void *)connection
);
2442 CFRelease(connection
);
2446 dispatch_source_set_cancel_handler(source
, ^{
2447 dispatch_release(source
);
2450 connectionPrivate
->dispatchSource
= source
;
2451 dispatch_resume(source
);
2454 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, connectionPrivate
->rlList
)) {
2456 * if we do not already have notifications scheduled with
2457 * this runLoop / runLoopMode
2459 CFRunLoopAddSource(runLoop
, connectionPrivate
->rls
, runLoopMode
);
2462 _SC_schedule(connection
, runLoop
, runLoopMode
, connectionPrivate
->rlList
);
2465 #if !TARGET_OS_SIMULATOR
2466 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2467 CFRetain(connection
);
2468 ne_session_set_event_handler(connectionPrivate
->ne_session
, __SCNetworkConnectionQueue(), ^(ne_session_event_t event
, void *event_data
) {
2469 #pragma unused(event_data)
2470 if (event
== NESessionEventStatusChanged
) {
2471 CFRetain(connection
); /* Released in __SCNetworkConnectionCallBack */
2472 pthread_mutex_lock(&connectionPrivate
->lock
);
2473 if (connectionPrivate
->rls
!= NULL
) {
2474 CFRunLoopSourceSignal(connectionPrivate
->rls
);
2475 _SC_signalRunLoop(connection
, connectionPrivate
->rls
, connectionPrivate
->rlList
);
2476 } else if (connectionPrivate
->dispatchQueue
!= NULL
) {
2477 dispatch_async(connectionPrivate
->dispatchQueue
, ^{
2478 __SCNetworkConnectionCallBack((void *)connection
);
2481 pthread_mutex_unlock(&connectionPrivate
->lock
);
2482 } else if (event
== NESessionEventCanceled
) {
2483 CFRelease(connection
);
2487 #endif /* !TARGET_OS_SIMULATOR */
2493 pthread_mutex_unlock(&connectionPrivate
->lock
);
2499 __SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection
,
2500 CFRunLoopRef runLoop
,
2501 CFStringRef runLoopMode
,
2502 dispatch_queue_t queue
)
2504 #pragma unused(queue)
2505 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2506 dispatch_group_t drainGroup
= NULL
;
2507 dispatch_queue_t drainQueue
= NULL
;
2508 int sc_status
= kSCStatusFailed
;
2511 kern_return_t status
;
2513 // hold a reference while we unschedule
2514 CFRetain(connection
);
2516 pthread_mutex_lock(&connectionPrivate
->lock
);
2518 if ((runLoop
!= NULL
) && !connectionPrivate
->scheduled
) { // if we should be scheduled (but are not)
2519 _SCErrorSet(kSCStatusInvalidArgument
);
2523 if (((runLoop
== NULL
) && (connectionPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
2524 ((runLoop
!= NULL
) && (connectionPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
2525 _SCErrorSet(kSCStatusInvalidArgument
);
2529 if (connectionPrivate
->dispatchQueue
!= NULL
) {
2530 if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2531 // cancel dispatchSource
2532 if (connectionPrivate
->dispatchSource
!= NULL
) {
2533 dispatch_source_cancel(connectionPrivate
->dispatchSource
);
2534 connectionPrivate
->dispatchSource
= NULL
;
2537 // save dispatchQueue/group, release reference when all queue'd blocks
2538 // have been processed, allow re-scheduling
2539 drainGroup
= connectionPrivate
->dispatchGroup
;
2540 connectionPrivate
->dispatchGroup
= NULL
;
2541 drainQueue
= connectionPrivate
->dispatchQueue
;
2542 connectionPrivate
->dispatchQueue
= NULL
;
2544 dispatch_release(connectionPrivate
->dispatchQueue
);
2545 connectionPrivate
->dispatchQueue
= NULL
;
2548 if (!_SC_unschedule(connection
, runLoop
, runLoopMode
, connectionPrivate
->rlList
, FALSE
)) {
2549 // if not currently scheduled on this runLoop / runLoopMode
2550 _SCErrorSet(kSCStatusFailed
);
2554 n
= CFArrayGetCount(connectionPrivate
->rlList
);
2555 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, connectionPrivate
->rlList
)) {
2557 * if we are no longer scheduled to receive notifications for
2558 * this runLoop / runLoopMode
2560 CFRunLoopRemoveSource(runLoop
, connectionPrivate
->rls
, runLoopMode
);
2563 // if *all* notifications have been unscheduled
2564 CFRelease(connectionPrivate
->rlList
);
2565 connectionPrivate
->rlList
= NULL
;
2566 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
2567 CFRelease(connectionPrivate
->rls
);
2568 connectionPrivate
->rls
= NULL
;
2574 // if *all* notifications have been unscheduled
2575 connectionPrivate
->scheduled
= FALSE
;
2577 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2578 #if !TARGET_OS_SIMULATOR
2579 ne_session_cancel(connectionPrivate
->ne_session
);
2580 #endif /* !TARGET_OS_SIMULATOR */
2582 mach_port_t session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2583 if (session_port
== MACH_PORT_NULL
) {
2587 status
= pppcontroller_notification(session_port
, 0, &sc_status
);
2588 if (__SCNetworkConnectionNeedsRetry(connection
,
2589 "__SCNetworkConnectionUnscheduleFromRunLoop pppcontroller_notification()",
2592 sc_status
= kSCStatusOK
;
2593 status
= KERN_SUCCESS
;
2596 if ((status
!= KERN_SUCCESS
) || (sc_status
!= kSCStatusOK
)) {
2597 _SCErrorSet(sc_status
);
2607 pthread_mutex_unlock(&connectionPrivate
->lock
);
2609 if (drainGroup
!= NULL
) {
2610 dispatch_group_notify(drainGroup
, drainQueue
, ^{
2611 // release group/queue references
2612 dispatch_release(drainQueue
);
2613 dispatch_release(drainGroup
); // releases our connection reference
2617 // release our reference
2618 CFRelease(connection
);
2625 SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection
,
2626 CFRunLoopRef runLoop
,
2627 CFStringRef runLoopMode
)
2629 if (!isA_SCNetworkConnection(connection
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
2630 _SCErrorSet(kSCStatusInvalidArgument
);
2634 return __SCNetworkConnectionScheduleWithRunLoop(connection
, runLoop
, runLoopMode
, NULL
);
2639 SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection
,
2640 CFRunLoopRef runLoop
,
2641 CFStringRef runLoopMode
)
2643 if (!isA_SCNetworkConnection(connection
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
2644 _SCErrorSet(kSCStatusInvalidArgument
);
2648 return __SCNetworkConnectionUnscheduleFromRunLoop(connection
, runLoop
, runLoopMode
, NULL
);
2653 SCNetworkConnectionSetDispatchQueue(SCNetworkConnectionRef connection
,
2654 dispatch_queue_t queue
)
2658 if (!isA_SCNetworkConnection(connection
)) {
2659 _SCErrorSet(kSCStatusInvalidArgument
);
2663 if (queue
!= NULL
) {
2664 ok
= __SCNetworkConnectionScheduleWithRunLoop(connection
, NULL
, NULL
, queue
);
2666 ok
= __SCNetworkConnectionUnscheduleFromRunLoop(connection
, NULL
, NULL
, NULL
);
2673 /* Requires having called SCNetworkConnectionSelectServiceWithOptions previously */
2675 SCNetworkConnectionIsOnDemandSuspended(SCNetworkConnectionRef connection
)
2677 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2679 if (!isA_SCNetworkConnection(connection
)) {
2680 _SCErrorSet(kSCStatusInvalidArgument
);
2684 if (connectionPrivate
->on_demand_info
!= NULL
) {
2685 uint32_t isSuspended
= 0;
2686 CFNumberRef num
= NULL
;
2688 num
= CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCPropNetVPNOnDemandSuspended
);
2689 if (isA_CFNumber(num
) &&
2690 CFNumberGetValue(num
, kCFNumberSInt32Type
, &isSuspended
) &&
2691 (isSuspended
!= 0)) {
2696 _SCErrorSet(kSCStatusOK
);
2701 SCNetworkConnectionTriggerOnDemandIfNeeded (CFStringRef hostName
,
2702 Boolean afterDNSFail
,
2706 #if !TARGET_OS_SIMULATOR
2707 __block Boolean triggeredOnDemand
= FALSE
;
2708 struct proc_uniqidentifierinfo procu
;
2709 void *policy_match
= NULL
;
2710 char *hostname
= NULL
;
2711 pid_t pid
= getpid();
2712 uid_t uid
= geteuid();
2714 /* Require hostName, require non-root user */
2715 if (hostName
== NULL
|| geteuid() == 0) {
2719 hostname
= _SC_cfstring_to_cstring(hostName
, NULL
, 0, kCFStringEncodingUTF8
);
2721 if (proc_pidinfo(pid
, PROC_PIDUNIQIDENTIFIERINFO
, 1, &procu
, sizeof(procu
)) != sizeof(procu
)) {
2725 policy_match
= ne_session_copy_policy_match(hostname
, NULL
, NULL
, procu
.p_uuid
, procu
.p_uuid
, pid
, uid
, 0, trafficClass
);
2727 NEPolicyServiceActionType action_type
= ne_session_policy_match_get_service_action(policy_match
);
2728 if (action_type
== NESessionPolicyActionTrigger
||
2729 (afterDNSFail
&& action_type
== NESessionPolicyActionTriggerIfNeeded
)) {
2731 if (ne_session_policy_match_get_service(policy_match
, config_id
)) {
2732 xpc_object_t start_options
= xpc_dictionary_create(NULL
, NULL
, 0);
2733 if (start_options
!= NULL
) {
2734 xpc_dictionary_set_bool(start_options
, NESessionStartOptionIsOnDemandKey
, true);
2735 xpc_dictionary_set_string(start_options
, NESessionStartOptionMatchHostnameKey
, hostname
);
2737 ne_session_t new_session
= ne_session_create(config_id
, ne_session_policy_match_get_service_type(policy_match
));
2738 if (new_session
!= NULL
) {
2739 dispatch_semaphore_t wait_for_session
= dispatch_semaphore_create(0);
2740 dispatch_retain(wait_for_session
);
2741 xpc_retain(start_options
);
2742 ne_session_get_status(new_session
, __SCNetworkConnectionQueue(),
2743 ^(ne_session_status_t status
) {
2744 if (status
== NESessionStatusDisconnected
) {
2745 dispatch_retain(wait_for_session
);
2746 ne_session_set_event_handler(new_session
, __SCNetworkConnectionQueue(),
2747 ^(ne_session_event_t event
, void *event_data
) {
2748 #pragma unused(event_data)
2749 os_activity_t activity
;
2751 activity
= os_activity_create("processing ne_session notification",
2752 OS_ACTIVITY_CURRENT
,
2753 OS_ACTIVITY_FLAG_DEFAULT
);
2754 os_activity_scope(activity
);
2756 if (event
== NESessionEventStatusChanged
) {
2757 dispatch_retain(wait_for_session
);
2758 ne_session_get_status(new_session
, __SCNetworkConnectionQueue(),
2759 ^(ne_session_status_t new_status
) {
2760 if (new_status
!= NESessionStatusConnecting
) {
2761 if (status
== NESessionStatusConnected
) {
2762 triggeredOnDemand
= TRUE
;
2764 ne_session_cancel(new_session
);
2766 dispatch_release(wait_for_session
);
2768 } else if (event
== NESessionEventCanceled
) {
2769 dispatch_semaphore_signal(wait_for_session
);
2770 dispatch_release(wait_for_session
);
2773 os_release(activity
);
2775 ne_session_start_with_options(new_session
, start_options
);
2777 dispatch_semaphore_signal(wait_for_session
);
2779 dispatch_release(wait_for_session
);
2780 xpc_release(start_options
);
2782 dispatch_semaphore_wait(wait_for_session
, timeout
? dispatch_time(DISPATCH_TIME_NOW
, (int64_t)timeout
* NSEC_PER_SEC
) : DISPATCH_TIME_FOREVER
);
2783 dispatch_release(wait_for_session
);
2784 ne_session_release(new_session
);
2787 xpc_release(start_options
);
2793 CFAllocatorDeallocate(NULL
, hostname
);
2800 return triggeredOnDemand
;
2802 #pragma unused(hostName, afterDNSFail, timeout, trafficClass)
2809 SCNetworkConnectionCopyOnDemandInfo(SCNetworkConnectionRef connection
,
2810 CFStringRef
*onDemandRemoteAddress
,
2811 SCNetworkConnectionStatus
*onDemandConnectionStatus
)
2813 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2815 if (!isA_SCNetworkConnection(connection
)) {
2816 _SCErrorSet(kSCStatusInvalidArgument
);
2820 if (connectionPrivate
->service
== NULL
) {
2821 _SCErrorSet(kSCStatusConnectionNoService
);
2825 if (onDemandRemoteAddress
!= NULL
) {
2826 *onDemandRemoteAddress
= NULL
;
2829 if (onDemandConnectionStatus
!= NULL
) {
2830 *onDemandConnectionStatus
= kSCNetworkConnectionInvalid
;
2833 if (connectionPrivate
->on_demand_info
!= NULL
) {
2834 if (onDemandRemoteAddress
!= NULL
) {
2835 CFStringRef address
=
2836 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandRemoteAddress
);
2837 if (isA_CFString(address
)) {
2838 *onDemandRemoteAddress
= address
;
2839 CFRetain(*onDemandRemoteAddress
);
2843 if (onDemandConnectionStatus
!= NULL
) {
2845 CFNumberRef status_num
=
2846 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandStatus
);
2847 if (isA_CFNumber(status_num
) && CFNumberGetValue(status_num
, kCFNumberIntType
, &num
)) {
2848 *onDemandConnectionStatus
= num
;
2853 return connectionPrivate
->on_demand
;
2858 SCNetworkConnectionGetReachabilityInfo(SCNetworkConnectionRef connection
,
2859 SCNetworkReachabilityFlags
*reach_flags
,
2860 unsigned int *reach_if_index
)
2862 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2864 if (!isA_SCNetworkConnection(connection
)) {
2865 _SCErrorSet(kSCStatusInvalidArgument
);
2869 if (connectionPrivate
->service
== NULL
) {
2870 _SCErrorSet(kSCStatusConnectionNoService
);
2874 if (reach_flags
!= NULL
) {
2878 if (reach_if_index
!= NULL
) {
2879 *reach_if_index
= 0;
2882 if (connectionPrivate
->on_demand_info
!= NULL
) {
2883 if (reach_flags
!= NULL
) {
2885 CFNumberRef flags_num
=
2886 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandReachFlags
);
2887 if (isA_CFNumber(flags_num
) && CFNumberGetValue(flags_num
, kCFNumberIntType
, &num
)) {
2892 if (reach_if_index
!= NULL
) {
2894 CFNumberRef if_index_num
=
2895 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandReachInterfaceIndex
);
2896 if (isA_CFNumber(if_index_num
) && CFNumberGetValue(if_index_num
, kCFNumberIntType
, &num
)) {
2897 *reach_if_index
= num
;
2906 SCNetworkConnectionType
2907 SCNetworkConnectionGetType(SCNetworkConnectionRef connection
)
2909 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2911 if (!isA_SCNetworkConnection(connection
)) {
2912 _SCErrorSet(kSCStatusInvalidArgument
);
2913 return kSCNetworkConnectionTypeUnknown
;
2916 if (connectionPrivate
->service
== NULL
) {
2917 _SCErrorSet(kSCStatusConnectionNoService
);
2918 return kSCNetworkConnectionTypeUnknown
;
2921 _SCErrorSet(kSCStatusOK
);
2923 return connectionPrivate
->type
;
2928 SCNetworkConnectionCopyFlowDivertToken(SCNetworkConnectionRef connection
,
2929 CFDictionaryRef flowProperties
)
2931 #pragma unused(connection, flowProperties)
2932 _SCErrorSet(kSCStatusFailed
);
2938 SCNetworkConnectionGetServiceIdentifier (SCNetworkConnectionRef connection
)
2940 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2941 int service_identifier
= -1;
2943 if (connectionPrivate
->service
!= NULL
) {
2944 service_identifier
= 0;
2945 if (connectionPrivate
->on_demand_info
!= NULL
) {
2946 CFNumberRef id_num
= CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCPropNetDNSServiceIdentifier
);
2948 if (isA_CFNumber(id_num
)) {
2949 CFNumberGetValue(id_num
, kCFNumberIntType
, &service_identifier
);
2954 return service_identifier
;
2958 #if !TARGET_OS_SIMULATOR
2959 SCNetworkConnectionStatus
2960 SCNetworkConnectionGetStatusFromNEStatus(ne_session_status_t status
)
2963 case NESessionStatusInvalid
:
2964 return kSCNetworkConnectionInvalid
;
2965 case NESessionStatusDisconnected
:
2966 return kSCNetworkConnectionDisconnected
;
2967 case NESessionStatusConnecting
:
2968 case NESessionStatusReasserting
:
2969 return kSCNetworkConnectionConnecting
;
2970 case NESessionStatusConnected
:
2971 return kSCNetworkConnectionConnected
;
2972 case NESessionStatusDisconnecting
:
2973 return kSCNetworkConnectionDisconnecting
;
2976 return kSCNetworkConnectionInvalid
;
2978 #endif /* !TARGET_OS_SIMULATOR */
2982 #pragma mark User level "dial" API
2985 #define k_NetworkConnect_Notification "com.apple.networkConnect"
2986 #define k_NetworkConnect_Pref_File CFSTR("com.apple.networkConnect")
2987 #define k_InterentConnect_Pref_File CFSTR("com.apple.internetconnect")
2989 #define k_Dial_Default_Key CFSTR("ConnectByDefault") // needs to go into SC
2990 #define k_Last_Service_Id_Key CFSTR("ServiceID")
2991 #define k_Unique_Id_Key CFSTR("UniqueIdentifier")
2994 /* Private Prototypes */
2995 static Boolean
SCNetworkConnectionPrivateCopyDefaultServiceIDForDial (CFStringRef
*serviceID
);
2996 static Boolean
SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore (CFStringRef
*serviceID
);
2997 static Boolean
SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray
, CFDictionaryRef
*userOptions
);
2998 static Boolean
SCNetworkConnectionPrivateIsPPPService (CFStringRef serviceID
, CFStringRef subType1
, CFStringRef subType2
);
2999 static void addPasswordFromKeychain (CFStringRef serviceID
, CFDictionaryRef
*userOptions
);
3000 static CFStringRef
copyPasswordFromKeychain (CFStringRef uniqueID
);
3002 static int notify_userprefs_token
= -1;
3004 static CFDictionaryRef onDemand_configuration
= NULL
;
3005 static Boolean onDemand_force_refresh
= FALSE
;
3006 static pthread_mutex_t onDemand_notify_lock
= PTHREAD_MUTEX_INITIALIZER
;
3007 static int onDemand_notify_token
= -1;
3011 * return TRUE if domain1 ends with domain2, and will check for trailing "."
3013 #define WILD_CARD_MATCH_STR CFSTR("*")
3015 _SC_domainEndsWithDomain(CFStringRef compare_domain
, CFStringRef match_domain
)
3018 Boolean ret
= FALSE
;
3019 CFStringRef s1
= NULL
;
3020 Boolean s1_created
= FALSE
;
3021 CFStringRef s2
= NULL
;
3022 Boolean s2_created
= FALSE
;
3023 CFStringRef s3
= NULL
;
3025 if (CFEqual(match_domain
, WILD_CARD_MATCH_STR
)) {
3029 if (CFStringHasSuffix(compare_domain
, CFSTR("."))) {
3031 range
.length
= CFStringGetLength(compare_domain
) - 1;
3032 s1
= CFStringCreateWithSubstring(NULL
, compare_domain
, range
);
3038 s1
= compare_domain
;
3041 if (CFStringHasSuffix(match_domain
, CFSTR("."))) {
3043 range
.length
= CFStringGetLength(match_domain
) - 1;
3044 s2
= CFStringCreateWithSubstring(NULL
, match_domain
, range
);
3053 if (CFStringHasPrefix(s2
, CFSTR("*."))) {
3055 range
.length
= CFStringGetLength(s2
)-2;
3056 s3
= CFStringCreateWithSubstring(NULL
, s2
, range
);
3067 ret
= CFStringHasSuffix(s1
, s2
);
3071 if (s1_created
) CFRelease(s1
);
3072 if (s2_created
) CFRelease(s2
);
3076 static CFCharacterSetRef
3077 _SC_getNotDotOrStarCharacterSet (void)
3079 static CFCharacterSetRef notDotOrStar
= NULL
;
3080 if (notDotOrStar
== NULL
) {
3081 CFCharacterSetRef dotOrStar
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, CFSTR(".*"));
3083 notDotOrStar
= CFCharacterSetCreateInvertedSet(kCFAllocatorDefault
, dotOrStar
);
3084 CFRelease(dotOrStar
);
3087 return notDotOrStar
;
3090 static CFMutableStringRef
3091 _SC_createStringByTrimmingDotsAndStars (CFStringRef string
)
3093 CFCharacterSetRef notDotOrStar
= _SC_getNotDotOrStarCharacterSet();
3094 CFRange entireString
= CFRangeMake(0, CFStringGetLength(string
));
3095 CFMutableStringRef result
= CFStringCreateMutableCopy(kCFAllocatorDefault
, entireString
.length
, string
);
3097 CFRange end
= CFRangeMake(entireString
.length
, 0);
3099 if (CFStringFindCharacterFromSet(string
, notDotOrStar
, entireString
, 0, &start
) &&
3100 CFStringFindCharacterFromSet(string
, notDotOrStar
, entireString
, kCFCompareBackwards
, &end
)) {
3101 if (start
.location
== kCFNotFound
|| end
.location
== kCFNotFound
|| start
.location
> end
.location
) {
3107 if ((end
.location
+ 1) < entireString
.length
) {
3108 CFStringReplace(result
, CFRangeMake(end
.location
+ 1, entireString
.length
- (end
.location
+ 1)), CFSTR(""));
3110 if (start
.location
> 0) {
3111 CFStringReplace(result
, CFRangeMake(0, start
.location
), CFSTR(""));
3118 _SC_getCountOfStringInString (CFStringRef string
, CFStringRef substring
)
3121 CFArrayRef ranges
= CFStringCreateArrayWithFindResults(kCFAllocatorDefault
, string
, substring
, CFRangeMake(0, CFStringGetLength(string
)), 0);
3122 if (ranges
!= NULL
) {
3123 count
= CFArrayGetCount(ranges
);
3130 _SC_hostMatchesDomain(CFStringRef hostname
, CFStringRef domain
)
3132 Boolean result
= FALSE
;
3133 CFMutableStringRef trimmedHostname
= NULL
;
3134 CFMutableStringRef trimmedDomain
= NULL
;
3136 if (!isA_CFString(hostname
) || !isA_CFString(domain
)) {
3140 trimmedHostname
= _SC_createStringByTrimmingDotsAndStars(hostname
);
3141 trimmedDomain
= _SC_createStringByTrimmingDotsAndStars(domain
);
3143 if (!isA_CFString(trimmedHostname
) || !isA_CFString(trimmedDomain
)) {
3147 CFIndex numHostnameDots
= _SC_getCountOfStringInString(trimmedHostname
, CFSTR("."));
3148 CFIndex numDomainDots
= _SC_getCountOfStringInString(trimmedDomain
, CFSTR("."));
3149 if (numHostnameDots
== numDomainDots
) {
3150 result
= CFEqual(trimmedHostname
, trimmedDomain
);
3151 } else if (numDomainDots
> 0 && numDomainDots
< numHostnameDots
) {
3152 CFStringReplace(trimmedDomain
, CFRangeMake(0, 0), CFSTR("."));
3153 result
= CFStringHasSuffix(trimmedHostname
, trimmedDomain
);
3159 if (trimmedHostname
) {
3160 CFRelease(trimmedHostname
);
3162 if (trimmedDomain
) {
3163 CFRelease(trimmedDomain
);
3170 static CFDictionaryRef
3171 __SCNetworkConnectionCopyOnDemandConfiguration(void)
3175 uint64_t triggersCount
= 0;
3176 CFDictionaryRef configuration
;
3178 pthread_mutex_lock(&onDemand_notify_lock
);
3179 if (onDemand_notify_token
== -1) {
3180 status
= notify_register_check(kSCNETWORKCONNECTION_ONDEMAND_NOTIFY_KEY
, &onDemand_notify_token
);
3181 if (status
!= NOTIFY_STATUS_OK
) {
3182 SC_log(LOG_NOTICE
, "notify_register_check() failed, status=%d", status
);
3183 onDemand_notify_token
= -1;
3187 if (onDemand_notify_token
!= -1) {
3188 status
= notify_check(onDemand_notify_token
, &changed
);
3189 if (status
!= NOTIFY_STATUS_OK
) {
3190 SC_log(LOG_NOTICE
, "notify_check() failed, status=%d", status
);
3191 (void)notify_cancel(onDemand_notify_token
);
3192 onDemand_notify_token
= -1;
3196 if (changed
&& (onDemand_notify_token
!= -1)) {
3197 status
= notify_get_state(onDemand_notify_token
, &triggersCount
);
3198 if (status
!= NOTIFY_STATUS_OK
) {
3199 SC_log(LOG_NOTICE
, "notify_get_state() failed, status=%d", status
);
3200 (void)notify_cancel(onDemand_notify_token
);
3201 onDemand_notify_token
= -1;
3205 if (changed
|| onDemand_force_refresh
) {
3208 SC_log(LOG_INFO
, "OnDemand information %s",
3209 (onDemand_configuration
== NULL
) ? "fetched" : "updated");
3211 if (onDemand_configuration
!= NULL
) {
3212 CFRelease(onDemand_configuration
);
3213 onDemand_configuration
= NULL
;
3216 if ((triggersCount
> 0) || onDemand_force_refresh
) {
3217 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, kSCDynamicStoreDomainState
, kSCEntNetOnDemand
);
3218 onDemand_configuration
= SCDynamicStoreCopyValue(NULL
, key
);
3220 if ((onDemand_configuration
!= NULL
) && !isA_CFDictionary(onDemand_configuration
)) {
3221 CFRelease(onDemand_configuration
);
3222 onDemand_configuration
= NULL
;
3226 onDemand_force_refresh
= FALSE
;
3229 configuration
= (onDemand_configuration
!= NULL
) ? CFRetain(onDemand_configuration
) : NULL
;
3230 pthread_mutex_unlock(&onDemand_notify_lock
);
3232 return configuration
;
3238 __SCNetworkConnectionForceOnDemandConfigurationRefresh(void)
3240 pthread_mutex_lock(&onDemand_notify_lock
);
3241 onDemand_force_refresh
= TRUE
;
3242 pthread_mutex_unlock(&onDemand_notify_lock
);
3249 __SCNetworkConnectionShouldNeverMatch(CFDictionaryRef trigger
, CFStringRef hostName
, pid_t client_pid
)
3251 CFArrayRef exceptedProcesses
;
3252 CFIndex exceptedProcessesCount
;
3253 CFIndex exceptedProcessesIndex
;
3254 CFArrayRef exceptions
;
3255 CFIndex exceptionsCount
;
3256 int exceptionsIndex
;
3258 // we have a matching domain, check against exception list
3259 exceptions
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandMatchDomainsNever
);
3260 exceptionsCount
= isA_CFArray(exceptions
) ? CFArrayGetCount(exceptions
) : 0;
3261 for (exceptionsIndex
= 0; exceptionsIndex
< exceptionsCount
; exceptionsIndex
++) {
3262 CFStringRef exception
;
3264 exception
= CFArrayGetValueAtIndex(exceptions
, exceptionsIndex
);
3265 if (isA_CFString(exception
) && _SC_domainEndsWithDomain(hostName
, exception
)) {
3266 // found matching exception
3267 SC_log(LOG_INFO
, "OnDemand match exception");
3272 if (client_pid
!= 0) {
3273 exceptedProcesses
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandPluginPIDs
);
3274 exceptedProcessesCount
= isA_CFArray(exceptedProcesses
) ? CFArrayGetCount(exceptedProcesses
) : 0;
3275 for (exceptedProcessesIndex
= 0; exceptedProcessesIndex
< exceptedProcessesCount
; exceptedProcessesIndex
++) {
3279 pidRef
= CFArrayGetValueAtIndex(exceptedProcesses
, exceptedProcessesIndex
);
3280 if (isA_CFNumber(pidRef
) && CFNumberGetValue(pidRef
, kCFNumberIntType
, &pid
)) {
3281 if (pid
== client_pid
) {
3292 __SCNetworkConnectionDomainGetMatchWithParameters(CFStringRef action
, CFPropertyListRef actionParameters
, CFStringRef hostName
, CFStringRef
*probeString
)
3294 CFArrayRef actionArray
= NULL
;
3295 CFIndex actionArraySize
= 0;
3297 CFStringRef matchDomain
= NULL
;
3299 /* For now, only support EvaluateConnection, which takes a CFArray */
3300 if (!CFEqual(action
, kSCValNetVPNOnDemandRuleActionEvaluateConnection
) || !isA_CFArray(actionParameters
)) {
3304 actionArray
= (CFArrayRef
)actionParameters
;
3305 actionArraySize
= CFArrayGetCount(actionArray
);
3307 /* Process domain rules, with actions of ConnectIfNeeded and NeverConnect */
3308 for (i
= 0; i
< actionArraySize
; i
++) {
3309 CFStringRef domainAction
= NULL
;
3310 CFDictionaryRef domainRule
= CFArrayGetValueAtIndex(actionArray
, i
);
3311 CFArrayRef domains
= NULL
;
3312 CFIndex domainsCount
= 0;
3313 CFIndex domainsIndex
;
3315 if (!isA_CFDictionary(domainRule
)) {
3319 domains
= CFDictionaryGetValue(domainRule
, kSCPropNetVPNOnDemandRuleActionParametersDomains
);
3320 if (!isA_CFArray(domains
)) {
3324 domainsCount
= CFArrayGetCount(domains
);
3325 for (domainsIndex
= 0; domainsIndex
< domainsCount
; domainsIndex
++) {
3327 domain
= CFArrayGetValueAtIndex(domains
, domainsIndex
);
3328 if (isA_CFString(domain
) && _SC_domainEndsWithDomain(hostName
, domain
)) {
3329 matchDomain
= domain
;
3335 domainAction
= CFDictionaryGetValue(domainRule
, kSCPropNetVPNOnDemandRuleActionParametersDomainAction
);
3336 if (isA_CFString(domainAction
) && CFEqual(domainAction
, kSCValNetVPNOnDemandRuleActionParametersDomainActionNeverConnect
)) {
3339 /* If we found a match, save the optional probe string as well */
3341 *probeString
= CFDictionaryGetValue(domainRule
, kSCPropNetVPNOnDemandRuleActionParametersRequiredURLStringProbe
);
3352 __SCNetworkConnectionDomainGetMatch(CFDictionaryRef trigger
, CFStringRef hostName
, Boolean onDemandRetry
)
3355 CFIndex domainsCount
;
3358 CFStringRef match_domain
= NULL
;
3360 /* Old configuration: always, never, on retry lists */
3361 key
= onDemandRetry
? kSCNetworkConnectionOnDemandMatchDomainsOnRetry
: kSCNetworkConnectionOnDemandMatchDomainsAlways
;
3363 domains
= CFDictionaryGetValue(trigger
, key
);
3364 domainsCount
= isA_CFArray(domains
) ? CFArrayGetCount(domains
) : 0;
3365 for (domainsIndex
= 0; domainsIndex
< domainsCount
; domainsIndex
++) {
3368 domain
= CFArrayGetValueAtIndex(domains
, domainsIndex
);
3369 if (isA_CFString(domain
) && _SC_domainEndsWithDomain(hostName
, domain
)) {
3370 match_domain
= domain
;
3375 return match_domain
;
3380 __SCNetworkConnectionShouldAlwaysConnect(CFDictionaryRef trigger
)
3382 CFStringRef action
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleAction
);
3383 return (isA_CFString(action
) && CFEqual(action
, kSCValNetVPNOnDemandRuleActionConnect
));
3388 __SCNetworkConnectionShouldIgnoreTrigger(CFDictionaryRef trigger
)
3390 CFStringRef action
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleAction
);
3392 if (isA_CFString(action
) &&
3393 (CFEqual(action
, kSCValNetVPNOnDemandRuleActionIgnore
) ||
3394 CFEqual(action
, kSCValNetVPNOnDemandRuleActionDisconnect
))) {
3402 static CFDictionaryRef
3403 __SCNetworkConnectionCopyMatchingTriggerWithName(CFDictionaryRef configuration
,
3404 CFStringRef hostName
,
3406 Boolean onDemandRetry
,
3407 CFDictionaryRef
*match_info
,
3408 Boolean
*triggerNow
,
3409 CFStringRef
*probe_string
)
3411 CFDictionaryRef result
= NULL
;
3412 int sc_status
= kSCStatusOK
;
3413 CFArrayRef triggers
;
3414 CFIndex triggersCount
= 0;
3415 Boolean usedOnDemandRetry
= FALSE
;
3417 if (triggerNow
!= NULL
) {
3418 *triggerNow
= FALSE
;
3421 if (match_info
!= NULL
) {
3425 triggers
= CFDictionaryGetValue(configuration
, kSCNetworkConnectionOnDemandTriggers
);
3426 triggersCount
= isA_CFArray(triggers
) ? CFArrayGetCount(triggers
) : 0;
3427 for (CFIndex triggersIndex
= 0; triggersIndex
< triggersCount
; triggersIndex
++) {
3428 CFStringRef matched_domain
= NULL
;
3429 CFStringRef matched_probe_string
= NULL
;
3430 CFDictionaryRef trigger
;
3431 Boolean trigger_matched
= FALSE
;
3433 usedOnDemandRetry
= FALSE
;
3435 trigger
= CFArrayGetValueAtIndex(triggers
, triggersIndex
);
3436 if (!isA_CFDictionary(trigger
)) {
3437 // if not a valid "OnDemand" configuration
3441 if (__SCNetworkConnectionShouldAlwaysConnect(trigger
)) {
3442 /* If the trigger action is 'Connect', always match this trigger */
3443 /* First check the never match list */
3444 if (__SCNetworkConnectionShouldNeverMatch(trigger
, hostName
, client_pid
)) {
3447 trigger_matched
= TRUE
;
3448 } else if (__SCNetworkConnectionShouldIgnoreTrigger(trigger
)) {
3449 /* If the trigger action is 'Ignore' or 'Disconnect', skip this trigger */
3450 sc_status
= kSCStatusConnectionIgnore
;
3453 CFStringRef action
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleAction
);
3454 CFArrayRef actionParameters
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleActionParameters
);
3455 if (action
&& actionParameters
) {
3456 matched_domain
= __SCNetworkConnectionDomainGetMatchWithParameters(action
, actionParameters
, hostName
, &matched_probe_string
);
3457 usedOnDemandRetry
= TRUE
;
3459 if (onDemandRetry
) {
3460 matched_domain
= __SCNetworkConnectionDomainGetMatch(trigger
, hostName
, TRUE
);
3461 usedOnDemandRetry
= TRUE
;
3463 matched_domain
= __SCNetworkConnectionDomainGetMatch(trigger
, hostName
, FALSE
);
3464 if (matched_domain
== NULL
&& result
== NULL
) {
3465 /* Check the retry list if Always failed */
3466 matched_domain
= __SCNetworkConnectionDomainGetMatch(trigger
, hostName
, TRUE
);
3467 usedOnDemandRetry
= TRUE
;
3472 if (matched_domain
) {
3473 if (__SCNetworkConnectionShouldNeverMatch(trigger
, hostName
, client_pid
)) {
3474 matched_domain
= NULL
;
3477 trigger_matched
= TRUE
;
3482 if (trigger_matched
) {
3483 // if we have a matching domain and there were no exceptions
3484 // then we pass back the OnDemand info
3485 if (match_info
!= NULL
) {
3486 CFMutableDictionaryRef minfo
;
3487 SCNetworkConnectionType type
= kSCNetworkConnectionTypeIPLayerVPN
;
3488 CFNumberRef type_num
;
3490 if (*match_info
!= NULL
) {
3491 CFRelease(*match_info
);
3495 minfo
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
3497 &kCFTypeDictionaryKeyCallBacks
,
3498 &kCFTypeDictionaryValueCallBacks
);
3500 type_num
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &type
);
3501 CFDictionarySetValue(minfo
, kSCNetworkConnectionOnDemandMatchInfoVPNType
, type_num
);
3502 CFRelease(type_num
);
3503 if (matched_domain
) {
3504 CFDictionarySetValue(minfo
, kSCNetworkConnectionOnDemandMatchInfoDomain
, matched_domain
);
3506 CFDictionarySetValue(minfo
,
3507 kSCNetworkConnectionOnDemandMatchInfoOnRetry
,
3508 (usedOnDemandRetry
? kCFBooleanTrue
: kCFBooleanFalse
));
3510 *match_info
= minfo
;
3513 if (probe_string
!= NULL
) {
3514 if (*probe_string
!= NULL
) {
3515 CFRelease(*probe_string
);
3516 *probe_string
= NULL
;
3519 if (matched_probe_string
) {
3520 *probe_string
= CFRetain(matched_probe_string
);
3526 /* If retry was requested, or we found Always match, trigger now */
3527 if (onDemandRetry
|| !usedOnDemandRetry
) {
3528 if (triggerNow
!= NULL
) {
3534 /* If we matched the Retry list, but Always was requested,
3535 keep going through triggers in case one matches an Always */
3543 _SCErrorSet(sc_status
);
3548 static CFDictionaryRef
3549 __SCNetworkConnectionCopyTriggerWithService(CFDictionaryRef configuration
,
3550 CFStringRef service_id
)
3552 CFArrayRef triggers
;
3553 CFIndex triggersCount
;
3555 triggers
= CFDictionaryGetValue(configuration
, kSCNetworkConnectionOnDemandTriggers
);
3556 triggersCount
= isA_CFArray(triggers
) ? CFArrayGetCount(triggers
) : 0;
3557 for (CFIndex triggersIndex
= 0; triggersIndex
< triggersCount
; triggersIndex
++) {
3558 CFDictionaryRef trigger
;
3559 CFStringRef trigger_service_id
;
3561 trigger
= CFArrayGetValueAtIndex(triggers
, triggersIndex
);
3562 if (!isA_CFDictionary(trigger
)) {
3563 // if not a valid "OnDemand" configuration
3567 trigger_service_id
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandServiceID
);
3568 if (isA_CFString(trigger_service_id
) && CFEqual(trigger_service_id
, service_id
)) {
3578 __private_extern__ CFDictionaryRef
3579 __SCNetworkConnectionCopyTokenParameters(SCNetworkConnectionRef connection
)
3581 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
3582 CFDictionaryRef parameters
= NULL
;
3583 uint8_t params_buffer
[PPP_MACH_MAX_INLINE_DATA
];
3584 uint32_t params_buffer_len
= sizeof(params_buffer
);
3585 int sc_status
= kSCStatusOK
;
3586 mach_port_t session_port
;
3587 kern_return_t status
;
3589 pthread_mutex_lock(&connectionPrivate
->lock
);
3591 parameters
= connectionPrivate
->flow_divert_token_params
;
3592 if (parameters
!= NULL
) {
3593 CFRetain(parameters
);
3598 if (parameters
!= NULL
) {
3599 CFRelease(parameters
);
3603 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
3604 if (session_port
== MACH_PORT_NULL
) {
3608 status
= pppcontroller_flow_divert_copy_token_parameters(session_port
, params_buffer
, ¶ms_buffer_len
);
3609 if (status
== KERN_SUCCESS
) {
3610 if (params_buffer_len
> 0) {
3611 CFDataRef params_data
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
,
3615 parameters
= CFPropertyListCreateWithData(kCFAllocatorDefault
,
3617 kCFPropertyListImmutable
,
3620 CFRelease(params_data
);
3624 if (__SCNetworkConnectionNeedsRetry(connection
, "__SCNetworkConnectionCopyTokenParameters()", status
, &sc_status
)) {
3628 if (sc_status
!= kSCStatusOK
) {
3629 _SCErrorSet(sc_status
);
3633 if (parameters
!= NULL
&& connectionPrivate
->flow_divert_token_params
== NULL
) {
3634 connectionPrivate
->flow_divert_token_params
= (CFDictionaryRef
)CFRetain(parameters
);
3637 pthread_mutex_unlock(&connectionPrivate
->lock
);
3643 __SCNetworkConnectionCopyOnDemandInfoWithName(SCDynamicStoreRef
*storeP
,
3644 CFStringRef hostName
,
3645 Boolean onDemandRetry
,
3646 CFStringRef
*connectionServiceID
,
3647 SCNetworkConnectionStatus
*connectionStatus
,
3648 CFStringRef
*vpnRemoteAddress
) /* CFDictionaryRef *info */
3650 #pragma unused(storeP)
3651 CFDictionaryRef configuration
;
3653 int sc_status
= kSCStatusOK
;
3654 CFDictionaryRef trigger
;
3655 Boolean trigger_now
= FALSE
;
3657 configuration
= __SCNetworkConnectionCopyOnDemandConfiguration();
3658 if (configuration
== NULL
) {
3659 _SCErrorSet(sc_status
);
3663 trigger
= __SCNetworkConnectionCopyMatchingTriggerWithName(configuration
, hostName
, 0, onDemandRetry
, NULL
, &trigger_now
, NULL
);
3664 if (trigger
!= NULL
&& trigger_now
) {
3666 SCNetworkConnectionStatus onDemandStatus
= kSCNetworkConnectionDisconnected
;
3670 if (!CFDictionaryGetValueIfPresent(trigger
, kSCNetworkConnectionOnDemandStatus
, (const void **)&num
) ||
3671 !isA_CFNumber(num
) ||
3672 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &onDemandStatus
)) {
3673 onDemandStatus
= kSCNetworkConnectionDisconnected
;
3675 if (connectionStatus
!= NULL
) {
3676 *connectionStatus
= onDemandStatus
;
3679 if (connectionServiceID
!= NULL
) {
3680 *connectionServiceID
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandServiceID
);
3681 *connectionServiceID
= isA_CFString(*connectionServiceID
);
3682 if ((*connectionServiceID
!= NULL
) && (CFStringGetLength(*connectionServiceID
) > 0)) {
3683 CFRetain(*connectionServiceID
);
3685 SC_log(LOG_INFO
, "OnDemand%s configuration error, no serviceID",
3686 onDemandRetry
? " (on retry)" : "");
3687 *connectionServiceID
= NULL
;
3692 if (vpnRemoteAddress
!= NULL
) {
3693 *vpnRemoteAddress
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandRemoteAddress
);
3694 *vpnRemoteAddress
= isA_CFString(*vpnRemoteAddress
);
3695 if ((*vpnRemoteAddress
!= NULL
) && (CFStringGetLength(*vpnRemoteAddress
) > 0)) {
3696 CFRetain(*vpnRemoteAddress
);
3698 SC_log(LOG_INFO
, "OnDemand%s configuration error, no server address",
3699 onDemandRetry
? " (on retry)" : "");
3700 *vpnRemoteAddress
= NULL
;
3706 if ((connectionServiceID
!= NULL
) && (*connectionServiceID
!= NULL
)) {
3707 CFRelease(*connectionServiceID
);
3708 *connectionServiceID
= NULL
;
3710 if ((vpnRemoteAddress
!= NULL
) && (*vpnRemoteAddress
!= NULL
)) {
3711 CFRelease(*vpnRemoteAddress
);
3712 *vpnRemoteAddress
= NULL
;
3714 sc_status
= kSCStatusFailed
;
3716 SC_log(LOG_INFO
, "OnDemand%s match, connection status = %d",
3717 onDemandRetry
? " (on retry)" : "",
3726 // SC_log(LOG_INFO, "OnDemand domain name(s) not matched");
3728 if (configuration
!= NULL
) CFRelease(configuration
);
3730 _SCErrorSet(sc_status
);
3736 __SCNetworkConnectionCopyUserPreferencesInternal(CFDictionaryRef selectionOptions
,
3737 CFStringRef
*serviceID
,
3738 CFDictionaryRef
*userOptions
)
3740 int prefsChanged
= 1;
3742 Boolean success
= FALSE
;
3744 if (notify_userprefs_token
== -1) {
3745 status
= notify_register_check(k_NetworkConnect_Notification
, ¬ify_userprefs_token
);
3746 if (status
!= NOTIFY_STATUS_OK
) {
3747 SC_log(LOG_NOTICE
, "notify_register_check() failed, status=%d", status
);
3748 (void)notify_cancel(notify_userprefs_token
);
3749 notify_userprefs_token
= -1;
3751 // clear the "something has changed" state
3752 (void) notify_check(notify_userprefs_token
, &prefsChanged
);
3756 if (notify_userprefs_token
!= -1) {
3757 status
= notify_check(notify_userprefs_token
, &prefsChanged
);
3758 if (status
!= NOTIFY_STATUS_OK
) {
3759 SC_log(LOG_NOTICE
, "notify_check() failed, status=%d", status
);
3760 (void)notify_cancel(notify_userprefs_token
);
3761 notify_userprefs_token
= -1;
3767 *userOptions
= NULL
;
3769 if (selectionOptions
!= NULL
) {
3770 Boolean catchAllFound
= FALSE
;
3771 CFIndex catchAllService
= 0;
3772 CFIndex catchAllConfig
= 0;
3773 CFStringRef hostName
= NULL
;
3774 CFStringRef priority
= NULL
;
3775 CFArrayRef serviceNames
= NULL
;
3776 CFDictionaryRef services
= NULL
;
3777 CFIndex serviceIndex
;
3778 CFIndex servicesCount
;
3780 hostName
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
);
3781 if (hostName
== NULL
) {
3782 hostName
= CFDictionaryGetValue(selectionOptions
, kSCPropNetPPPOnDemandHostName
);
3784 hostName
= isA_CFString(hostName
);
3785 if (hostName
== NULL
)
3786 goto done_selection
; // if no hostname for matching
3788 priority
= CFDictionaryGetValue(selectionOptions
, kSCPropNetPPPOnDemandPriority
);
3789 if (!isA_CFString(priority
))
3790 priority
= kSCValNetPPPOnDemandPriorityDefault
;
3793 if (!isA_CFArray(serviceNames
))
3794 goto done_selection
;
3797 if (!isA_CFDictionary(services
)) {
3798 goto done_selection
;
3801 servicesCount
= CFArrayGetCount(serviceNames
);
3802 for (serviceIndex
= 0; serviceIndex
< servicesCount
; serviceIndex
++) {
3803 CFIndex configIndex
;
3804 CFIndex configsCount
;
3805 CFArrayRef serviceConfigs
;
3806 CFStringRef serviceName
;
3809 serviceName
= CFArrayGetValueAtIndex(serviceNames
, serviceIndex
);
3810 if (!isA_CFString(serviceName
)) {
3814 serviceConfigs
= CFDictionaryGetValue(services
, serviceName
);
3815 if (!isA_CFArray(serviceConfigs
)) {
3819 configsCount
= CFArrayGetCount(serviceConfigs
);
3820 for (configIndex
= 0; configIndex
< configsCount
; configIndex
++) {
3821 CFNumberRef autodial
;
3822 CFDictionaryRef config
;
3823 CFDictionaryRef pppConfig
;
3825 config
= CFArrayGetValueAtIndex(serviceConfigs
, configIndex
);
3826 if (!isA_CFDictionary(config
)) {
3830 pppConfig
= CFDictionaryGetValue(config
, kSCEntNetPPP
);
3831 if (!isA_CFDictionary(pppConfig
)) {
3835 autodial
= CFDictionaryGetValue(pppConfig
, kSCPropNetPPPOnDemandEnabled
);
3836 if (!isA_CFNumber(autodial
)) {
3840 CFNumberGetValue(autodial
, kCFNumberIntType
, &val
);
3843 CFIndex domainsCount
;
3844 CFIndex domainsIndex
;
3846 /* we found an conditional connection enabled configuration */
3849 domains
= CFDictionaryGetValue(pppConfig
, kSCPropNetPPPOnDemandDomains
);
3850 if (!isA_CFArray(domains
)) {
3854 domainsCount
= CFArrayGetCount(domains
);
3855 for (domainsIndex
= 0; domainsIndex
< domainsCount
; domainsIndex
++) {
3858 domain
= CFArrayGetValueAtIndex(domains
, domainsIndex
);
3859 if (!isA_CFString(domain
)) {
3863 if (!catchAllFound
&&
3864 (CFStringCompare(domain
, CFSTR(""), 0) == kCFCompareEqualTo
3865 || CFStringCompare(domain
, CFSTR("."), 0) == kCFCompareEqualTo
))
3867 // found a catch all
3868 catchAllFound
= TRUE
;
3869 catchAllService
= serviceIndex
;
3870 catchAllConfig
= configIndex
;
3873 if (_SC_domainEndsWithDomain(hostName
, domain
)) {
3874 // found matching configuration
3875 *serviceID
= serviceName
;
3876 CFRetain(*serviceID
);
3877 *userOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, config
);
3878 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
3879 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCPropNetPPPOnDemandPriority
, priority
);
3880 addPasswordFromKeychain(*serviceID
, userOptions
);
3882 goto done_selection
;
3889 // config not found, do we have a catchall ?
3890 if (catchAllFound
) {
3891 CFDictionaryRef config
;
3892 CFArrayRef serviceConfigs
;
3893 CFStringRef serviceName
;
3895 serviceName
= CFArrayGetValueAtIndex(serviceNames
, catchAllService
);
3896 serviceConfigs
= CFDictionaryGetValue(services
, serviceName
);
3897 config
= CFArrayGetValueAtIndex(serviceConfigs
, catchAllConfig
);
3899 *serviceID
= serviceName
;
3900 CFRetain(*serviceID
);
3901 *userOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, config
);
3902 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
3903 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCPropNetPPPOnDemandPriority
, priority
);
3904 addPasswordFromKeychain(*serviceID
, userOptions
);
3906 goto done_selection
;
3912 CFRelease(serviceNames
);
3915 CFRelease(services
);
3919 SC_log(LOG_INFO
, "SCNetworkConnectionCopyUserPreferences %s", success
? "succeeded" : "failed");
3920 SC_log(LOG_INFO
, "Selection options: %@", selectionOptions
);
3926 /* we don't have selection options */
3928 // (1) Figure out which service ID we care about, allocate it into passed "serviceID"
3929 success
= SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(serviceID
);
3931 if (success
&& (*serviceID
!= NULL
)) {
3932 // (2) Get the list of user data for this service ID
3933 CFPropertyListRef userServices
= NULL
;
3936 // (3) We are expecting an array if the user has defined records for this service ID or NULL if the user hasn't
3937 if (userServices
!= NULL
) {
3938 if (isA_CFArray(userServices
)) {
3939 // (4) Get the default set of user options for this service
3940 success
= SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray((CFArrayRef
)userServices
,
3943 addPasswordFromKeychain(*serviceID
, userOptions
);
3946 SC_log(LOG_INFO
, "Error, userServices are not of type CFArray!");
3949 CFRelease(userServices
); // this is OK because SCNetworkConnectionPrivateISExpectedCFType() checks for NULL
3954 SC_log(LOG_INFO
, "SCNetworkConnectionCopyUserPreferences %@, no selection options",
3955 success
? CFSTR("succeeded") : CFSTR("failed"));
3963 SCNetworkConnectionCopyUserPreferences(CFDictionaryRef selectionOptions
,
3964 CFStringRef
*serviceID
,
3965 CFDictionaryRef
*userOptions
)
3967 Boolean success
= FALSE
;
3969 /* initialize runtime */
3970 pthread_once(&initialized
, __SCNetworkConnectionInitialize
);
3972 /* first check for new VPN OnDemand style */
3973 if (selectionOptions
!= NULL
) {
3974 CFStringRef hostName
;
3976 hostName
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
);
3977 if (isA_CFString(hostName
)) {
3978 CFStringRef connectionServiceID
= NULL
;
3979 SCNetworkConnectionStatus connectionStatus
= kSCNetworkConnectionInvalid
;
3980 Boolean onDemandRetry
;
3983 val
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandRetry
);
3984 onDemandRetry
= isA_CFBoolean(val
) ? CFBooleanGetValue(val
) : TRUE
;
3986 success
= __SCNetworkConnectionCopyOnDemandInfoWithName(NULL
,
3989 &connectionServiceID
,
3993 SC_log(LOG_INFO
, "__SCNetworkConnectionCopyOnDemandInfoWithName: return %d, status %d",
3999 // if the hostname matches an OnDemand domain
4000 if (connectionStatus
== kSCNetworkConnectionConnected
) {
4001 // if we are already connected
4002 if (connectionServiceID
!= NULL
) {
4003 CFRelease(connectionServiceID
);
4008 *serviceID
= connectionServiceID
;
4009 *userOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
4010 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
4012 } else if (!onDemandRetry
) {
4013 // if the hostname does not match an OnDemand domain and we have
4014 // not yet issued an initial DNS query (i.e. it's not a query
4015 // being retried after the VPN has been established) then we're
4022 return __SCNetworkConnectionCopyUserPreferencesInternal(selectionOptions
, serviceID
, userOptions
);
4027 SCNetworkConnectionOnDemandShouldRetryOnFailure (SCNetworkConnectionRef connection
)
4029 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
4030 CFDictionaryRef match_info
= NULL
;
4032 if (!isA_SCNetworkConnection(connection
)) {
4033 _SCErrorSet(kSCStatusInvalidArgument
);
4037 if (connectionPrivate
->service
== NULL
) {
4038 _SCErrorSet(kSCStatusConnectionNoService
);
4042 if (isA_CFDictionary(connectionPrivate
->on_demand_user_options
)) {
4043 match_info
= CFDictionaryGetValue(connectionPrivate
->on_demand_user_options
, kSCNetworkConnectionSelectionOptionOnDemandMatchInfo
);
4044 if (isA_CFDictionary(match_info
)) {
4045 CFBooleanRef onRetry
= CFDictionaryGetValue(match_info
, kSCNetworkConnectionOnDemandMatchInfoOnRetry
);
4046 if (isA_CFBoolean(onRetry
)) {
4047 return CFBooleanGetValue(onRetry
);
4057 // Mask is optional in routes dictionary; if not present, whole addresses are matched
4059 __SCNetworkConnectionIPv4AddressMatchesRoutes (struct sockaddr_in
*addr_in
, CFDictionaryRef routes
)
4063 CFDataRef maskData
= NULL
;
4064 struct in_addr
*maskDataArray
;
4065 CFDataRef routeaddrData
= NULL
;
4066 struct in_addr
*routeaddrDataArray
;
4068 if (!isA_CFDictionary(routes
)) {
4072 routeaddrData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoAddresses
);
4073 maskData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoMasks
);
4075 /* routeaddrData and maskData are packed arrays of addresses; make sure they have the same length */
4076 if (!isA_CFData(routeaddrData
) || (maskData
&& (!isA_CFData(maskData
) || CFDataGetLength(routeaddrData
) != CFDataGetLength(maskData
)))) {
4080 routeaddrDataArray
= (struct in_addr
*)(void*)CFDataGetBytePtr(routeaddrData
);
4082 maskDataArray
= (struct in_addr
*)(void*)CFDataGetBytePtr(maskData
);
4085 count
= CFDataGetLength(routeaddrData
) / sizeof(struct in_addr
);
4086 for (i
=0; i
<count
; i
++) {
4087 struct in_addr routeAddr
= *routeaddrDataArray
;
4090 struct in_addr mask
= *maskDataArray
;
4092 if ((addr_in
->sin_addr
.s_addr
& mask
.s_addr
) == (routeAddr
.s_addr
& mask
.s_addr
)) {
4097 if (addr_in
->sin_addr
.s_addr
== routeAddr
.s_addr
) {
4101 routeaddrDataArray
++;
4108 __SCNetworkConnectionMaskIPv6Address(struct in6_addr
*addr
, struct in6_addr
*mask
)
4110 for (size_t i
= 0; i
< sizeof(struct in6_addr
); i
++)
4111 addr
->s6_addr
[i
] &= mask
->s6_addr
[i
];
4115 // Mask is optional in routes dictionary; if not present, whole addresses are matched
4117 __SCNetworkConnectionIPv6AddressMatchesRoutes (struct sockaddr_in6
*addr_in6
, CFDictionaryRef routes
)
4121 CFDataRef maskData
= NULL
;
4122 struct in6_addr
*maskDataArray
;
4123 CFDataRef routeaddrData
= NULL
;
4124 struct in6_addr
*routeaddrDataArray
;
4126 if (!isA_CFDictionary(routes
)) {
4130 routeaddrData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoAddresses
);
4131 maskData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoMasks
);
4133 /* routeaddrData and maskData are packed arrays of addresses; make sure they have the same length */
4134 if (!isA_CFData(routeaddrData
) || (maskData
&& (!isA_CFData(maskData
) || CFDataGetLength(routeaddrData
) != CFDataGetLength(maskData
)))) {
4138 routeaddrDataArray
= (struct in6_addr
*)(void*)CFDataGetBytePtr(routeaddrData
);
4140 maskDataArray
= (struct in6_addr
*)(void*)CFDataGetBytePtr(maskData
);
4143 count
= CFDataGetLength(routeaddrData
) / sizeof(struct in6_addr
);
4144 for (i
=0; i
<count
; i
++) {
4146 struct in6_addr cmpAddr
;
4147 struct in6_addr
*mask
= maskDataArray
;
4148 struct in6_addr routeAddr
;
4150 memcpy(&routeAddr
, routeaddrDataArray
, sizeof(routeAddr
));
4151 memcpy(&cmpAddr
, &addr_in6
->sin6_addr
, sizeof(cmpAddr
));
4152 __SCNetworkConnectionMaskIPv6Address(&routeAddr
, mask
);
4153 __SCNetworkConnectionMaskIPv6Address(&cmpAddr
, mask
);
4155 if (!memcmp(&routeAddr
, &cmpAddr
, sizeof(routeAddr
))) {
4159 if (!memcmp(routeaddrDataArray
, &addr_in6
->sin6_addr
, sizeof(struct in6_addr
))) {
4164 routeaddrDataArray
++;
4171 __SCNetworkConnectionAddressMatchesRedirectedDNS(CFDictionaryRef trigger
, const struct sockaddr
*input_addr
)
4173 CFBooleanRef redirectedRef
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandDNSRedirectDetected
);
4175 if (isA_CFBoolean(redirectedRef
) && CFBooleanGetValue(redirectedRef
)) {
4176 /* DNS is redirected. Look for address list. */
4177 CFDictionaryRef redirectedAddressesRef
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandDNSRedirectedAddresses
);
4179 if (isA_CFDictionary(redirectedAddressesRef
)) {
4180 if (input_addr
->sa_family
== AF_INET
) {
4181 return __SCNetworkConnectionIPv4AddressMatchesRoutes((struct sockaddr_in
*)(void*)input_addr
, CFDictionaryGetValue(redirectedAddressesRef
, kSCNetworkConnectionNetworkInfoIPv4
));
4182 } else if (input_addr
->sa_family
== AF_INET6
) {
4183 return __SCNetworkConnectionIPv6AddressMatchesRoutes((struct sockaddr_in6
*)(void*)input_addr
, CFDictionaryGetValue(redirectedAddressesRef
, kSCNetworkConnectionNetworkInfoIPv6
));
4191 /* If the required probe has failed, we need to tunnel the address. Equivalent to redirected DNS. */
4193 __SCNetworkConnectionRequiredProbeFailed (CFDictionaryRef trigger
, CFStringRef probeString
)
4195 CFDictionaryRef probeResults
= NULL
;
4197 if (!isA_CFString(probeString
)) {
4201 probeResults
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandProbeResults
);
4202 if (!isA_CFDictionary(probeResults
)) {
4206 CFBooleanRef result
= CFDictionaryGetValue(probeResults
, probeString
);
4208 /* Only a value of kCFBooleanFalse marks the probe as failed */
4209 return (isA_CFBoolean(result
) && !CFBooleanGetValue(result
));
4213 SCNetworkConnectionCanTunnelAddress (SCNetworkConnectionRef connection
, const struct sockaddr
*address
, Boolean
*startImmediately
)
4215 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
4216 CFStringRef serviceID
= NULL
;
4217 CFDictionaryRef configuration
= NULL
;
4218 CFDictionaryRef trigger
= NULL
;
4219 CFDictionaryRef tunneledNetworks
= NULL
;
4220 sa_family_t address_family
= AF_UNSPEC
;
4221 Boolean success
= FALSE
;
4223 if (startImmediately
) {
4224 *startImmediately
= FALSE
;
4227 if (address
== NULL
) {
4231 address_family
= address
->sa_family
;
4232 if (address_family
!= AF_INET
&& address_family
!= AF_INET6
) {
4236 if (!isA_SCNetworkConnection(connection
)) {
4237 _SCErrorSet(kSCStatusInvalidArgument
);
4241 if (connectionPrivate
->service
== NULL
) {
4242 _SCErrorSet(kSCStatusConnectionNoService
);
4246 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
4247 if (!isA_CFString(serviceID
)) {
4251 configuration
= __SCNetworkConnectionCopyOnDemandConfiguration();
4252 if (configuration
== NULL
) {
4256 trigger
= __SCNetworkConnectionCopyTriggerWithService(configuration
, serviceID
);
4257 if (trigger
== NULL
) {
4261 if (__SCNetworkConnectionRequiredProbeFailed(trigger
, connectionPrivate
->on_demand_required_probe
)) {
4262 /* If probe failed, we can't trust DNS - connect now */
4263 if (startImmediately
) {
4264 *startImmediately
= TRUE
;
4270 if (__SCNetworkConnectionAddressMatchesRedirectedDNS(trigger
, address
)) {
4271 if (startImmediately
) {
4272 *startImmediately
= TRUE
;
4278 tunneledNetworks
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandTunneledNetworks
);
4279 if (!isA_CFDictionary(tunneledNetworks
)) {
4283 if (address_family
== AF_INET
) {
4284 CFDictionaryRef ip_dict
;
4285 Boolean matches
= FALSE
;
4286 struct sockaddr_in
*addr_in
= (struct sockaddr_in
*)(void*)address
;
4288 ip_dict
= CFDictionaryGetValue(tunneledNetworks
, kSCNetworkConnectionNetworkInfoIPv4
);
4289 if (!isA_CFDictionary(ip_dict
)) {
4293 matches
= __SCNetworkConnectionIPv4AddressMatchesRoutes(addr_in
, CFDictionaryGetValue(ip_dict
, kSCNetworkConnectionNetworkInfoIncludedRoutes
));
4296 if (!__SCNetworkConnectionIPv4AddressMatchesRoutes(addr_in
, CFDictionaryGetValue(ip_dict
, kSCNetworkConnectionNetworkInfoExcludedRoutes
))) {
4302 CFDictionaryRef ip6_dict
;
4303 Boolean matches
= FALSE
;
4304 struct sockaddr_in6
*addr_in6
= (struct sockaddr_in6
*)(void*)address
;
4306 ip6_dict
= CFDictionaryGetValue(tunneledNetworks
, kSCNetworkConnectionNetworkInfoIPv6
);
4307 if (!isA_CFDictionary(ip6_dict
)) {
4311 matches
= __SCNetworkConnectionIPv6AddressMatchesRoutes(addr_in6
, CFDictionaryGetValue(ip6_dict
, kSCNetworkConnectionNetworkInfoIncludedRoutes
));
4314 if (!__SCNetworkConnectionIPv6AddressMatchesRoutes(addr_in6
, CFDictionaryGetValue(ip6_dict
, kSCNetworkConnectionNetworkInfoExcludedRoutes
))) {
4321 if (configuration
) {
4322 CFRelease(configuration
);
4331 SCNetworkConnectionSelectServiceWithOptions(SCNetworkConnectionRef connection
, CFDictionaryRef selectionOptions
)
4333 CFStringRef account_identifier
= NULL
;
4334 CFDictionaryRef configuration
= NULL
;
4335 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
4336 CFDictionaryRef found_trigger
= NULL
;
4337 CFStringRef host_name
= NULL
;
4338 Boolean is_retry
= TRUE
;
4339 CFDictionaryRef match_info
= NULL
;
4340 CFMutableDictionaryRef new_user_options
= NULL
;
4341 SCNetworkConnectionStatus on_demand_status
= kSCNetworkConnectionInvalid
;
4342 CFStringRef requiredProbe
= NULL
;
4343 CFStringRef service_id
= NULL
;
4344 Boolean skip_prefs
= FALSE
;
4345 Boolean success
= TRUE
;
4346 CFDictionaryRef user_options
= NULL
;
4348 if (!isA_SCNetworkConnection(connection
)) {
4349 _SCErrorSet(kSCStatusInvalidArgument
);
4354 /* Can't call this on a connection that is already associated with a service */
4355 if (connectionPrivate
->service
!= NULL
) {
4356 _SCErrorSet(kSCStatusInvalidArgument
);
4361 if (isA_CFDictionary(selectionOptions
)) {
4362 CFBooleanRef no_user_prefs
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionNoUserPrefs
);
4363 CFBooleanRef retry
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandRetry
);
4365 account_identifier
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandAccountIdentifier
);
4366 host_name
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
);
4367 skip_prefs
= (isA_CFBoolean(no_user_prefs
) && CFBooleanGetValue(no_user_prefs
));
4369 if (isA_CFBoolean(retry
)) {
4370 is_retry
= CFBooleanGetValue(retry
);
4374 configuration
= __SCNetworkConnectionCopyOnDemandConfiguration();
4376 /* First, check for a match with the App Layer rules */
4377 service_id
= VPNAppLayerCopyMatchingService(connectionPrivate
->client_audit_token
,
4378 connectionPrivate
->client_pid
,
4379 connectionPrivate
->client_uuid
,
4380 connectionPrivate
->client_bundle_id
,
4384 if (service_id
!= NULL
) {
4385 Boolean use_app_layer
= TRUE
;
4387 if (isA_CFDictionary(configuration
)) {
4388 found_trigger
= __SCNetworkConnectionCopyTriggerWithService(configuration
, service_id
);
4389 if (found_trigger
!= NULL
) {
4390 CFNumberRef status_num
;
4392 if (!CFDictionaryGetValueIfPresent(found_trigger
,
4393 kSCNetworkConnectionOnDemandStatus
,
4394 (const void **) &status_num
) ||
4395 !isA_CFNumber(status_num
) ||
4396 !CFNumberGetValue(status_num
, kCFNumberSInt32Type
, &on_demand_status
)) {
4397 on_demand_status
= kSCNetworkConnectionInvalid
;
4401 * If the trigger should be ignored, still use App Layer VPN if it is already connected or
4402 * is in the process of connecting.
4404 if (__SCNetworkConnectionShouldIgnoreTrigger(found_trigger
) &&
4405 (on_demand_status
!= kSCNetworkConnectionConnecting
) &&
4406 (on_demand_status
!= kSCNetworkConnectionConnected
)) {
4407 use_app_layer
= FALSE
;
4412 if (use_app_layer
) {
4413 /* If this is not the 'OnRetry' call, and the service has not yet started, the match may need to return false */
4415 match_info
!= NULL
&&
4416 on_demand_status
!= kSCNetworkConnectionConnecting
&&
4417 on_demand_status
!= kSCNetworkConnectionConnected
) {
4418 CFBooleanRef matchedOnRetry
= CFDictionaryGetValue(match_info
, kSCNetworkConnectionOnDemandMatchInfoOnRetry
);
4419 if (matchedOnRetry
&& CFBooleanGetValue(matchedOnRetry
)) {
4420 /* Don't return that we matched always; wait for SCNetworkConnectionOnDemandShouldRetryOnFailure */
4424 connectionPrivate
->type
= kSCNetworkConnectionTypeAppLayerVPN
;
4427 CFRelease(service_id
);
4429 if (match_info
!= NULL
) {
4430 CFRelease(match_info
);
4433 if (found_trigger
!= NULL
) {
4434 CFRelease(found_trigger
);
4435 found_trigger
= NULL
;
4440 /* Next, check the IP layer rules */
4441 if (isA_CFDictionary(configuration
) && host_name
!= NULL
) {
4442 Boolean triggerNow
= FALSE
;
4444 found_trigger
= __SCNetworkConnectionCopyMatchingTriggerWithName(configuration
, host_name
, connectionPrivate
->client_pid
, is_retry
, &match_info
, &triggerNow
, &requiredProbe
);
4445 if (found_trigger
!= NULL
) {
4446 service_id
= CFDictionaryGetValue(found_trigger
, kSCNetworkConnectionOnDemandServiceID
);
4447 if (isA_CFString(service_id
)) {
4448 CFRetain(service_id
);
4449 connectionPrivate
->type
= kSCNetworkConnectionTypeIPLayerVPN
;
4457 } else if (!is_retry
) {
4461 if (match_info
!= NULL
) {
4462 CFRelease(match_info
);
4467 /* Next, check the user preferences */
4468 if (!skip_prefs
&& __SCNetworkConnectionCopyUserPreferencesInternal(selectionOptions
, &service_id
, &user_options
)) {
4469 CFMutableDictionaryRef minfo
;
4470 CFNumberRef type_num
;
4472 if (isA_CFDictionary(configuration
)) {
4473 found_trigger
= __SCNetworkConnectionCopyTriggerWithService(configuration
, service_id
);
4475 connectionPrivate
->type
= kSCNetworkConnectionTypePPP
;
4477 minfo
= CFDictionaryCreateMutable(NULL
,
4479 &kCFTypeDictionaryKeyCallBacks
,
4480 &kCFTypeDictionaryValueCallBacks
);
4481 type_num
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &connectionPrivate
->type
);
4482 CFDictionarySetValue(minfo
, kSCNetworkConnectionOnDemandMatchInfoVPNType
, type_num
);
4483 CFRelease(type_num
);
4489 if (service_id
== NULL
) {
4490 _SCErrorSet(kSCStatusOK
);
4495 connectionPrivate
->service
= _SCNetworkServiceCopyActive(NULL
, service_id
);
4496 if (connectionPrivate
->service
== NULL
) {
4497 _SCErrorSet(kSCStatusOK
);
4502 if (found_trigger
!= NULL
) {
4503 if (connectionPrivate
->on_demand_info
) {
4504 CFRelease(connectionPrivate
->on_demand_info
);
4506 connectionPrivate
->on_demand_info
= found_trigger
;
4507 CFRetain(connectionPrivate
->on_demand_info
);
4509 if (on_demand_status
== kSCNetworkConnectionInvalid
) {
4510 CFNumberRef status_num
;
4512 if (!CFDictionaryGetValueIfPresent(found_trigger
,
4513 kSCNetworkConnectionOnDemandStatus
,
4514 (const void **) &status_num
) ||
4515 !isA_CFNumber(status_num
) ||
4516 !CFNumberGetValue(status_num
, kCFNumberSInt32Type
, &on_demand_status
)) {
4517 on_demand_status
= kSCNetworkConnectionInvalid
;
4521 if (on_demand_status
!= kSCNetworkConnectionConnected
) {
4522 if (connectionPrivate
->type
== kSCNetworkConnectionTypeAppLayerVPN
) {
4523 /* Check App Layer OnDemand flag */
4524 CFBooleanRef app_on_demand_enabled
=
4525 CFDictionaryGetValue(found_trigger
, kSCNetworkConnectionOnDemandMatchAppEnabled
);
4526 if (isA_CFBoolean(app_on_demand_enabled
) && CFBooleanGetValue(app_on_demand_enabled
)) {
4527 connectionPrivate
->on_demand
= TRUE
;
4530 connectionPrivate
->on_demand
= TRUE
;
4533 } else if (connectionPrivate
->type
== kSCNetworkConnectionTypePPP
) {
4534 /* If we got the service from __SCNetworkConnectionCopyUserPreferencesInternal, then it's on demand */
4535 connectionPrivate
->on_demand
= TRUE
;
4538 if (user_options
== NULL
) {
4539 new_user_options
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
4541 &kCFTypeDictionaryKeyCallBacks
,
4542 &kCFTypeDictionaryValueCallBacks
);
4544 new_user_options
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, user_options
);
4547 if (host_name
!= NULL
) {
4548 CFDictionarySetValue(new_user_options
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, host_name
);
4551 if (connectionPrivate
->on_demand
&& match_info
!= NULL
) {
4552 CFDictionarySetValue(new_user_options
, kSCNetworkConnectionSelectionOptionOnDemandMatchInfo
, match_info
);
4555 connectionPrivate
->on_demand_user_options
= new_user_options
;
4556 CFRetain(connectionPrivate
->on_demand_user_options
);
4558 if (requiredProbe
) {
4559 connectionPrivate
->on_demand_required_probe
= requiredProbe
;
4560 CFRetain(connectionPrivate
->on_demand_required_probe
);
4564 if (service_id
!= NULL
) {
4565 CFRelease(service_id
);
4568 if (configuration
!= NULL
) {
4569 CFRelease(configuration
);
4572 if (found_trigger
!= NULL
) {
4573 CFRelease(found_trigger
);
4576 if (user_options
!= NULL
) {
4577 CFRelease(user_options
);
4580 if (new_user_options
!= NULL
) {
4581 CFRelease(new_user_options
);
4584 if (match_info
!= NULL
) {
4585 CFRelease(match_info
);
4588 if (requiredProbe
!= NULL
) {
4589 CFRelease(requiredProbe
);
4595 //*******************************************************************************************
4596 // SCNetworkConnectionPrivateCopyDefaultServiceIDForDial
4597 // ----------------------------------------------------
4598 // Try to find the service id to connect
4599 // (1) Start by looking at the last service used in Network Pref / Network menu extra
4600 // (2) If Network Pref / Network menu extra has not been used, find the PPP service
4601 // with the highest ordering
4602 //********************************************************************************************
4604 SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(CFStringRef
*serviceID
)
4606 Boolean foundService
= FALSE
;
4607 CFPropertyListRef lastServiceSelectedInIC
= NULL
;
4610 // we found the service the user last had open in IC
4611 if (lastServiceSelectedInIC
!= NULL
) {
4612 // make sure its a PPP service
4613 if (SCNetworkConnectionPrivateIsPPPService(lastServiceSelectedInIC
, kSCValNetInterfaceSubTypePPPSerial
, kSCValNetInterfaceSubTypePPPoE
)) {
4614 // make sure the service that we found is valid
4615 CFDictionaryRef dict
;
4618 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4619 kSCDynamicStoreDomainSetup
,
4620 lastServiceSelectedInIC
,
4621 kSCEntNetInterface
);
4622 dict
= SCDynamicStoreCopyValue(NULL
, key
);
4626 *serviceID
= CFRetain(lastServiceSelectedInIC
);
4627 foundService
= TRUE
;
4630 CFRelease(lastServiceSelectedInIC
);
4633 if (!foundService
) {
4634 foundService
= SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(serviceID
);
4637 return foundService
;
4640 //********************************************************************************
4641 // SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore
4642 // -------------------------------------------------------
4643 // Find the highest ordered PPP service in the dynamic store
4644 //********************************************************************************
4646 SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(CFStringRef
*serviceID
)
4648 CFDictionaryRef dict
= NULL
;
4649 CFStringRef key
= NULL
;
4650 CFArrayRef serviceIDs
= NULL
;
4651 Boolean success
= FALSE
;
4659 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, kSCDynamicStoreDomainSetup
, kSCEntNetIPv4
);
4661 fprintf(stderr
, "Error, Setup Key == NULL!\n");
4665 dict
= SCDynamicStoreCopyValue(NULL
, key
);
4666 if (!isA_CFDictionary(dict
)) {
4667 fprintf(stderr
, "no global IPv4 entity\n");
4671 serviceIDs
= CFDictionaryGetValue(dict
, kSCPropNetServiceOrder
); // array of service id's
4672 if (!isA_CFArray(serviceIDs
)) {
4673 fprintf(stderr
, "service order not specified\n");
4677 count
= CFArrayGetCount(serviceIDs
);
4678 for (i
= 0; i
< count
; i
++) {
4679 CFStringRef service
= CFArrayGetValueAtIndex(serviceIDs
, i
);
4681 if (SCNetworkConnectionPrivateIsPPPService(service
, kSCValNetInterfaceSubTypePPPSerial
, kSCValNetInterfaceSubTypePPPoE
)) {
4682 *serviceID
= CFRetain(service
);
4689 if (key
!= NULL
) CFRelease(key
);
4690 if (dict
!= NULL
) CFRelease(dict
);
4695 //********************************************************************************
4696 // SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray
4697 // ---------------------------------------------------------
4698 // Copy over user preferences for a particular service if they exist
4699 //********************************************************************************
4701 SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray
, CFDictionaryRef
*userOptions
)
4703 CFIndex count
= CFArrayGetCount(userOptionsArray
);
4706 for (i
= 0; i
< count
; i
++) {
4707 // (1) Find the dictionary
4708 CFPropertyListRef propertyList
= CFArrayGetValueAtIndex(userOptionsArray
, i
);
4710 if (isA_CFDictionary(propertyList
) != NULL
) {
4711 // See if there's a value for dial on demand
4712 CFPropertyListRef value
;
4714 value
= CFDictionaryGetValue((CFDictionaryRef
)propertyList
, k_Dial_Default_Key
);
4715 if (isA_CFBoolean(value
) != NULL
) {
4716 if (CFBooleanGetValue(value
)) {
4717 // we found the default user options
4718 *userOptions
= CFDictionaryCreateCopy(NULL
,
4719 (CFDictionaryRef
)propertyList
);
4729 //********************************************************************************
4730 // SCNetworkConnectionPrivateIsServiceType
4731 // --------------------------------------
4732 // Check and see if the service is a PPP service of the given types
4733 //********************************************************************************
4735 SCNetworkConnectionPrivateIsPPPService(CFStringRef serviceID
, CFStringRef subType1
, CFStringRef subType2
)
4737 CFStringRef entityKey
;
4738 Boolean isPPPService
= FALSE
;
4739 Boolean isMatchingSubType
= FALSE
;
4740 CFDictionaryRef serviceDict
;
4742 entityKey
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4743 kSCDynamicStoreDomainSetup
,
4745 kSCEntNetInterface
);
4746 if (entityKey
== NULL
) {
4750 serviceDict
= SCDynamicStoreCopyValue(NULL
, entityKey
);
4751 if (serviceDict
!= NULL
) {
4752 if (isA_CFDictionary(serviceDict
)) {
4754 CFStringRef subtype
;
4756 type
= CFDictionaryGetValue(serviceDict
, kSCPropNetInterfaceType
);
4757 if (isA_CFString(type
)) {
4758 isPPPService
= CFEqual(type
, kSCValNetInterfaceTypePPP
);
4761 subtype
= CFDictionaryGetValue(serviceDict
, kSCPropNetInterfaceSubType
);
4762 if (isA_CFString(subtype
)) {
4763 isMatchingSubType
= CFEqual(subtype
, subType1
);
4764 if (!isMatchingSubType
&& subType2
)
4765 isMatchingSubType
= CFEqual(subtype
, subType2
);
4768 CFRelease(serviceDict
);
4770 CFRelease(entityKey
);
4772 return (isPPPService
&& isMatchingSubType
);
4775 //********************************************************************************
4776 // addPasswordFromKeychain
4777 // --------------------------------------
4778 // Get the password and shared secret out of the keychain and add
4779 // them to the PPP and IPSec dictionaries
4780 //********************************************************************************
4782 addPasswordFromKeychain(CFStringRef serviceID
, CFDictionaryRef
*userOptions
)
4784 CFPropertyListRef uniqueID
;
4785 CFStringRef password
;
4786 CFStringRef sharedsecret
= NULL
;
4788 /* user options must exist */
4789 if (*userOptions
== NULL
)
4792 /* first, get the unique identifier used to store passwords in the keychain */
4793 uniqueID
= CFDictionaryGetValue(*userOptions
, k_Unique_Id_Key
);
4794 if (!isA_CFString(uniqueID
))
4797 /* first, get the PPP password */
4798 password
= copyPasswordFromKeychain(uniqueID
);
4800 /* then, if necessary, get the IPSec Shared Secret */
4801 if (SCNetworkConnectionPrivateIsPPPService(serviceID
, kSCValNetInterfaceSubTypeL2TP
, 0)) {
4802 CFMutableStringRef uniqueIDSS
;
4804 uniqueIDSS
= CFStringCreateMutableCopy(NULL
, 0, uniqueID
);
4805 CFStringAppend(uniqueIDSS
, CFSTR(".SS"));
4806 sharedsecret
= copyPasswordFromKeychain(uniqueIDSS
);
4807 CFRelease(uniqueIDSS
);
4810 /* did we find our information in the key chain ? */
4811 if ((password
!= NULL
) || (sharedsecret
!= NULL
)) {
4812 CFMutableDictionaryRef newOptions
;
4814 newOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, *userOptions
);
4817 if (password
!= NULL
) {
4818 CFDictionaryRef entity
;
4819 CFMutableDictionaryRef newEntity
;
4821 entity
= CFDictionaryGetValue(*userOptions
, kSCEntNetPPP
);
4822 if (isA_CFDictionary(entity
))
4823 newEntity
= CFDictionaryCreateMutableCopy(NULL
, 0, entity
);
4825 newEntity
= CFDictionaryCreateMutable(NULL
,
4827 &kCFTypeDictionaryKeyCallBacks
,
4828 &kCFTypeDictionaryValueCallBacks
);
4831 /* set the PPP password */
4832 CFDictionarySetValue(newEntity
, kSCPropNetPPPAuthPassword
, uniqueID
);
4833 CFDictionarySetValue(newEntity
, kSCPropNetPPPAuthPasswordEncryption
, kSCValNetPPPAuthPasswordEncryptionKeychain
);
4834 CFRelease(password
);
4836 /* update the PPP entity */
4837 CFDictionarySetValue(newOptions
, kSCEntNetPPP
, newEntity
);
4838 CFRelease(newEntity
);
4841 /* IPSec Shared Secret */
4842 if (sharedsecret
!= NULL
) {
4843 CFDictionaryRef entity
;
4844 CFMutableDictionaryRef newEntity
;
4846 entity
= CFDictionaryGetValue(*userOptions
, kSCEntNetIPSec
);
4847 if (isA_CFDictionary(entity
))
4848 newEntity
= CFDictionaryCreateMutableCopy(NULL
, 0, entity
);
4850 newEntity
= CFDictionaryCreateMutable(NULL
,
4852 &kCFTypeDictionaryKeyCallBacks
,
4853 &kCFTypeDictionaryValueCallBacks
);
4855 /* set the IPSec Shared Secret */
4856 CFDictionarySetValue(newEntity
, kSCPropNetIPSecSharedSecret
, sharedsecret
);
4857 CFRelease(sharedsecret
);
4859 /* update the IPSec entity */
4860 CFDictionarySetValue(newOptions
, kSCEntNetIPSec
, newEntity
);
4861 CFRelease(newEntity
);
4864 /* update the userOptions dictionary */
4865 CFRelease(*userOptions
);
4866 *userOptions
= CFDictionaryCreateCopy(NULL
, newOptions
);
4867 CFRelease(newOptions
);
4872 #if !TARGET_OS_IPHONE
4873 //********************************************************************************
4874 // copyKeychainEnumerator
4875 // --------------------------------------
4876 // Gather Keychain Enumerator
4877 //********************************************************************************
4879 copyKeychainEnumerator(CFStringRef uniqueIdentifier
)
4881 CFArrayRef itemArray
= NULL
;
4882 CFMutableDictionaryRef query
;
4885 query
= CFDictionaryCreateMutable(NULL
,
4887 &kCFTypeDictionaryKeyCallBacks
,
4888 &kCFTypeDictionaryValueCallBacks
);
4889 CFDictionarySetValue(query
, kSecClass
, kSecClassGenericPassword
);
4890 CFDictionarySetValue(query
, kSecAttrService
, uniqueIdentifier
);
4891 CFDictionarySetValue(query
, kSecReturnRef
, kCFBooleanTrue
);
4892 CFDictionarySetValue(query
, kSecMatchLimit
, kSecMatchLimitAll
);
4893 result
= SecItemCopyMatching(query
, (CFTypeRef
*)&itemArray
);
4895 if ((result
!= noErr
) && (itemArray
!= NULL
)) {
4896 CFRelease(itemArray
);
4902 #endif // !TARGET_OS_IPHONE
4904 //********************************************************************************
4905 // copyPasswordFromKeychain
4906 // --------------------------------------
4907 // Given a uniqueID, retrieve the password from the keychain
4908 //********************************************************************************
4910 copyPasswordFromKeychain(CFStringRef uniqueID
)
4912 #if !TARGET_OS_IPHONE
4913 CFArrayRef enumerator
;
4915 CFStringRef password
= NULL
;
4917 enumerator
= copyKeychainEnumerator(uniqueID
);
4918 if (enumerator
== NULL
) {
4919 return NULL
; // if no keychain enumerator
4922 n
= CFArrayGetCount(enumerator
);
4926 SecKeychainItemRef itemRef
;
4929 itemRef
= (SecKeychainItemRef
)CFArrayGetValueAtIndex(enumerator
, 0);
4930 result
= SecKeychainItemCopyContent(itemRef
, // itemRef
4934 (void *)&data
); // outData
4935 if ((result
== noErr
) && (data
!= NULL
) && (dataLen
> 0)) {
4936 password
= CFStringCreateWithBytes(NULL
, data
, dataLen
, kCFStringEncodingUTF8
, TRUE
);
4937 (void) SecKeychainItemFreeContent(NULL
, data
);
4942 CFRelease(enumerator
);
4945 #else // !TARGET_OS_IPHONE
4946 #pragma unused(uniqueID)
4948 #endif // !TARGET_OS_IPHONE
4954 __SCNetworkConnectionGetControllerPortName(void)
4956 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 70000)) && !TARGET_OS_SIMULATOR
4957 if (scnc_server_name
== NULL
){
4958 if (!(sandbox_check(getpid(), "mach-lookup", SANDBOX_FILTER_GLOBAL_NAME
| SANDBOX_CHECK_NO_REPORT
, PPPCONTROLLER_SERVER_PRIV
))){
4959 scnc_server_name
= PPPCONTROLLER_SERVER_PRIV
;
4961 scnc_server_name
= PPPCONTROLLER_SERVER
;
4965 scnc_server_name
= PPPCONTROLLER_SERVER
;
4967 return scnc_server_name
;