2 * Copyright (c) 2003-2016 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * April 14, 2004 Christophe Allie <callie@apple.com>
30 * December 20, 2002 Christophe Allie <callie@apple.com>
35 //#define DEBUG_MACH_PORT_ALLOCATIONS
38 #include <Availability.h>
39 #include <TargetConditionals.h>
40 #include <sys/cdefs.h>
41 #include <dispatch/dispatch.h>
42 #include <CoreFoundation/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()
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
= SCDynamicStoreKeyCreateNetworkServiceEntity(kCFAllocatorDefault
,
182 kSCDynamicStoreDomainSetup
,
183 SCNetworkServiceGetServiceID(connectionPrivate
->service
),
185 CFDictionaryRef interfaceDict
= SCDynamicStoreCopyValue(NULL
, interfaceKey
);
186 if (isA_CFDictionary(interfaceDict
)) {
187 CFStringRef interfaceType
= CFDictionaryGetValue(interfaceDict
, kSCPropNetInterfaceType
);
188 if (isA_CFString(interfaceType
)) {
189 if (CFEqual(interfaceType
, kSCNetworkInterfaceTypePPP
)) {
190 CFStringRef interfaceSubType
= CFDictionaryGetValue(interfaceDict
, kSCPropNetInterfaceSubType
);
191 #pragma GCC diagnostic push
192 #pragma GCC diagnostic ignored "-Wdeprecated"
193 result
= (isA_CFString(interfaceSubType
) &&
194 (CFEqual(interfaceSubType
, kSCValNetInterfaceSubTypePPTP
) ||
195 CFEqual(interfaceSubType
, kSCValNetInterfaceSubTypeL2TP
)));
196 #pragma GCC diagnostic pop
198 result
= (CFEqual(interfaceType
, kSCNetworkInterfaceTypeVPN
) || CFEqual(interfaceType
, kSCNetworkInterfaceTypeIPSec
));
202 if (interfaceDict
!= NULL
) {
203 CFRelease(interfaceDict
);
205 CFRelease(interfaceKey
);
211 #endif /* !TARGET_OS_SIMULATOR */
215 __SCNetworkConnectionUsingNetworkExtension(SCNetworkConnectionPrivateRef connectionPrivate
)
217 #if !TARGET_OS_SIMULATOR
218 return (connectionPrivate
->ne_session
!= NULL
);
221 #endif /* !TARGET_OS_SIMULATOR */
226 __SCNetworkConnectionCopyDescription(CFTypeRef cf
)
228 CFAllocatorRef allocator
= CFGetAllocator(cf
);
229 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)cf
;
230 CFMutableStringRef result
;
232 result
= CFStringCreateMutable(allocator
, 0);
233 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkConnection, %p [%p]> {"), cf
, allocator
);
234 CFStringAppendFormat(result
, NULL
, CFSTR("service = %p"), connectionPrivate
->service
);
235 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
236 CFStringAppendFormat(result
, NULL
, CFSTR(", server port = 0x%x"), connectionPrivate
->session_port
);
238 CFStringAppendFormat(result
, NULL
, CFSTR("using NetworkExtension = %s"), (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
) ? "yes" : "no"));
239 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
246 __SCNetworkConnectionDeallocate(CFTypeRef cf
)
248 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)cf
;
250 /* release resources */
251 pthread_mutex_destroy(&connectionPrivate
->lock
);
253 if (connectionPrivate
->client_audit_session
!= MACH_PORT_NULL
) {
254 mach_port_mod_refs(mach_task_self(),
255 connectionPrivate
->client_audit_session
,
256 MACH_PORT_RIGHT_SEND
,
260 if (connectionPrivate
->client_bootstrap_port
!= MACH_PORT_NULL
) {
261 mach_port_mod_refs(mach_task_self(),
262 connectionPrivate
->client_bootstrap_port
,
263 MACH_PORT_RIGHT_SEND
,
267 if (connectionPrivate
->client_bundle_id
!= NULL
) {
268 CFRelease(connectionPrivate
->client_bundle_id
);
271 if (connectionPrivate
->rls
!= NULL
) {
272 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
273 CFRelease(connectionPrivate
->rls
);
276 if (connectionPrivate
->rlList
!= NULL
) {
277 CFRelease(connectionPrivate
->rlList
);
280 if (connectionPrivate
->notify_port
!= NULL
) {
281 mach_port_t mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
283 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionDeallocate notify_port", mp
);
284 CFMachPortInvalidate(connectionPrivate
->notify_port
);
285 CFRelease(connectionPrivate
->notify_port
);
286 mach_port_mod_refs(mach_task_self(), mp
, MACH_PORT_RIGHT_RECEIVE
, -1);
289 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
290 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionDeallocate session_port", connectionPrivate
->session_port
);
291 (void) mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
294 if (connectionPrivate
->rlsContext
.release
!= NULL
)
295 (*connectionPrivate
->rlsContext
.release
)(connectionPrivate
->rlsContext
.info
);
297 if (connectionPrivate
->service
!= NULL
) {
298 CFRelease(connectionPrivate
->service
);
301 if (connectionPrivate
->on_demand_info
!= NULL
) {
302 CFRelease(connectionPrivate
->on_demand_info
);
305 if (connectionPrivate
->on_demand_user_options
!= NULL
) {
306 CFRelease(connectionPrivate
->on_demand_user_options
);
309 if (connectionPrivate
->on_demand_required_probe
!= NULL
) {
310 CFRelease(connectionPrivate
->on_demand_required_probe
);
313 if (connectionPrivate
->flow_divert_token_params
!= NULL
) {
314 CFRelease(connectionPrivate
->flow_divert_token_params
);
317 #if !TARGET_OS_SIMULATOR
318 if (connectionPrivate
->ne_session
!= NULL
) {
319 ne_session_set_event_handler(connectionPrivate
->ne_session
, NULL
, NULL
);
320 ne_session_release(connectionPrivate
->ne_session
);
322 #endif /* !TARGET_OS_SIMULATOR */
328 static CFTypeID __kSCNetworkConnectionTypeID
= _kCFRuntimeNotATypeID
;
330 static const CFRuntimeClass __SCNetworkConnectionClass
= {
332 "SCNetworkConnection", // className
335 __SCNetworkConnectionDeallocate
, // dealloc
338 NULL
, // copyFormattingDesc
339 __SCNetworkConnectionCopyDescription
// copyDebugDesc
346 /* the process has forked (and we are the child process) */
348 scnc_server
= MACH_PORT_NULL
;
349 scnc_server_name
= NULL
;
355 __SCNetworkConnectionInitialize(void)
359 /* get the debug environment variable */
360 env
= getenv("PPPDebug");
362 if (sscanf(env
, "%d", &debug
) != 1) {
363 /* PPPDebug value is not valid (or non-numeric), set debug to 1 */
368 /* register with CoreFoundation */
369 __kSCNetworkConnectionTypeID
= _CFRuntimeRegisterClass(&__SCNetworkConnectionClass
);
371 /* add handler to cleanup after fork() */
372 (void) pthread_atfork(NULL
, NULL
, childForkHandler
);
379 __SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection
);
381 #define SC_NETWORK_CONNECTION_QUEUE "SCNetworkConnectionQueue"
383 static dispatch_queue_t
384 __SCNetworkConnectionQueue()
386 static dispatch_once_t once
;
387 static dispatch_queue_t q
;
389 dispatch_once(&once
, ^{
390 q
= dispatch_queue_create(SC_NETWORK_CONNECTION_QUEUE
, NULL
);
398 __SCNetworkConnectionNotify(SCNetworkConnectionRef connection
,
399 SCNetworkConnectionCallBack rlsFunction
,
400 SCNetworkConnectionStatus nc_status
,
401 void (*context_release
)(const void *),
404 os_activity_t activity
;
406 activity
= os_activity_create("processing SCHelper request",
408 OS_ACTIVITY_FLAG_DEFAULT
);
409 os_activity_scope(activity
);
411 SC_log(LOG_DEBUG
, "exec SCNetworkConnection callout");
412 (*rlsFunction
)(connection
, nc_status
, context_info
);
413 if ((context_release
!= NULL
) && (context_info
!= NULL
)) {
414 (*context_release
)(context_info
);
416 os_release(activity
);
423 __SCNetworkConnectionCallBackRunLoopPerform(SCNetworkConnectionRef connection
,
426 SCNetworkConnectionCallBack rlsFunction
,
427 void (*context_release
)(const void *),
430 SCNetworkConnectionStatus nc_status
;
432 nc_status
= SCNetworkConnectionGetStatus(connection
);
433 CFRunLoopPerformBlock(rl
, rl_mode
, ^{
434 __SCNetworkConnectionNotify(connection
, rlsFunction
, nc_status
, context_release
, context_info
);
435 CFRelease(connection
);
442 __SCNetworkConnectionCallBackDispatchPerform(SCNetworkConnectionRef connection
,
444 SCNetworkConnectionCallBack rlsFunction
,
445 void (*context_release
)(const void *),
448 SCNetworkConnectionStatus nc_status
;
450 nc_status
= SCNetworkConnectionGetStatus(connection
);
452 __SCNetworkConnectionNotify(connection
, rlsFunction
, nc_status
, context_release
, context_info
);
453 CFRelease(connection
);
460 __SCNetworkConnectionCallBack(void *connection
)
462 boolean_t exec_async
= FALSE
;
463 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
465 void (*context_release
)(const void *);
466 CFRunLoopRef rl
= NULL
;
468 SCNetworkConnectionCallBack rlsFunction
= NULL
;
469 dispatch_queue_t q
= NULL
;
470 SCNetworkConnectionStatus nc_status
= kSCNetworkConnectionInvalid
;
472 pthread_mutex_lock(&connectionPrivate
->lock
);
474 if (!connectionPrivate
->scheduled
) {
475 // if not currently scheduled
476 pthread_mutex_unlock(&connectionPrivate
->lock
);
480 rlsFunction
= connectionPrivate
->rlsFunction
;
481 if (rlsFunction
== NULL
) {
482 pthread_mutex_unlock(&connectionPrivate
->lock
);
486 if ((connectionPrivate
->rlsContext
.retain
!= NULL
) && (connectionPrivate
->rlsContext
.info
!= NULL
)) {
487 context_info
= (void *)(*connectionPrivate
->rlsContext
.retain
)(connectionPrivate
->rlsContext
.info
);
488 context_release
= connectionPrivate
->rlsContext
.release
;
490 context_info
= connectionPrivate
->rlsContext
.info
;
491 context_release
= NULL
;
494 #if !TARGET_OS_SIMULATOR
495 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
496 pthread_mutex_unlock(&connectionPrivate
->lock
);
498 nc_status
= SCNetworkConnectionGetStatus(connection
);
499 __SCNetworkConnectionNotify(connection
, rlsFunction
, nc_status
, context_release
, context_info
);
500 CFRelease(connection
); /* This releases the reference that we took in the NESessionEventStatusChanged event handler */
503 #endif /* !TARGET_OS_SIMULATOR */
505 // Do we need to spin a new thread? (either we are running on the main
506 // dispatch queue or main runloop)
507 if (connectionPrivate
->rlList
== NULL
) {
508 // if we are performing the callback on a dispatch queue
509 q
= connectionPrivate
->dispatchQueue
;
510 if (q
== dispatch_get_main_queue()) {
514 rl
= CFRunLoopGetCurrent();
515 if (rl
== CFRunLoopGetMain()) {
520 CFRetain(connection
);
521 pthread_mutex_unlock(&connectionPrivate
->lock
);
524 nc_status
= SCNetworkConnectionGetStatus(connection
);
525 __SCNetworkConnectionNotify(connection
, rlsFunction
, nc_status
, context_release
, context_info
);
526 CFRelease(connection
);
530 if (connectionPrivate
->rlList
== NULL
) {
533 dispatch_async(__SCNetworkConnectionQueue(), ^{
534 __SCNetworkConnectionCallBackDispatchPerform(connection
,
544 rl_mode
= CFRunLoopCopyCurrentMode(rl
);
545 dispatch_async(__SCNetworkConnectionQueue(), ^{
546 __SCNetworkConnectionCallBackRunLoopPerform(connection
,
562 __SCNetworkConnectionMachCallBack(CFMachPortRef port
, void * msg
, CFIndex size
, void * info
)
564 mach_no_senders_notification_t
*buf
= msg
;
565 mach_msg_id_t msgid
= buf
->not_header
.msgh_id
;
566 SCNetworkConnectionRef connection
= (SCNetworkConnectionRef
)info
;
568 if (msgid
== MACH_NOTIFY_NO_SENDERS
) {
569 // re-establish notification
570 SC_log(LOG_INFO
, "PPPController server died");
571 (void)__SCNetworkConnectionReconnectNotifications(connection
);
574 __SCNetworkConnectionCallBack(info
);
581 #pragma mark SCNetworkConnection APIs
585 pppMPCopyDescription(const void *info
)
587 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)info
;
589 return CFStringCreateWithFormat(NULL
,
591 CFSTR("<SCNetworkConnection MP %p> {service = %@, callout = %p}"),
593 connectionPrivate
->service
,
594 connectionPrivate
->rlsFunction
);
598 static SCNetworkConnectionPrivateRef
599 __SCNetworkConnectionCreatePrivate(CFAllocatorRef allocator
,
600 SCNetworkServiceRef service
,
601 SCNetworkConnectionCallBack callout
,
602 SCNetworkConnectionContext
*context
)
604 SCNetworkConnectionPrivateRef connectionPrivate
= NULL
;
607 /* initialize runtime */
608 pthread_once(&initialized
, __SCNetworkConnectionInitialize
);
610 /* allocate NetworkConnection */
611 size
= sizeof(SCNetworkConnectionPrivate
) - sizeof(CFRuntimeBase
);
612 connectionPrivate
= (SCNetworkConnectionPrivateRef
)_CFRuntimeCreateInstance(allocator
, __kSCNetworkConnectionTypeID
, size
, NULL
);
613 if (connectionPrivate
== NULL
) {
617 /* initialize non-zero/NULL members */
618 pthread_mutex_init(&connectionPrivate
->lock
, NULL
);
619 if (service
!= NULL
) {
620 connectionPrivate
->service
= CFRetain(service
);
622 connectionPrivate
->client_uid
= geteuid();
623 connectionPrivate
->client_gid
= getegid();
624 connectionPrivate
->client_pid
= getpid();
625 connectionPrivate
->rlsFunction
= callout
;
627 bcopy(context
, &connectionPrivate
->rlsContext
, sizeof(SCNetworkConnectionContext
));
628 if (context
->retain
!= NULL
) {
629 connectionPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
632 connectionPrivate
->type
= kSCNetworkConnectionTypeUnknown
;
634 #if !TARGET_OS_SIMULATOR
635 if (__SCNetworkConnectionUseNetworkExtension(connectionPrivate
)) {
636 CFStringRef serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
637 if (serviceID
!= NULL
) {
638 uuid_string_t service_uuid_str
;
639 if (CFStringGetCString(serviceID
, service_uuid_str
, sizeof(service_uuid_str
), kCFStringEncodingUTF8
)) {
641 if (uuid_parse(service_uuid_str
, config_id
) == 0) {
642 connectionPrivate
->ne_session
= ne_session_create(config_id
, NESessionTypeVPN
);
647 if (connectionPrivate
->ne_session
== NULL
) {
649 "SCNetworkConnection failed to create an ne_session: service ID %@ is not a valid UUID",
654 #endif /* !TARGET_OS_SIMULATOR */
656 /* success, return the connection reference */
657 return connectionPrivate
;
661 /* failure, clean up and leave */
662 if (connectionPrivate
!= NULL
) {
663 CFRelease(connectionPrivate
);
666 _SCErrorSet(kSCStatusFailed
);
672 __SCNetworkConnectionServerPort(kern_return_t
*status
)
674 mach_port_t server
= MACH_PORT_NULL
;
676 #ifdef BOOTSTRAP_PRIVILEGED_SERVER
677 *status
= bootstrap_look_up2(bootstrap_port
,
678 __SCNetworkConnectionGetControllerPortName(),
681 BOOTSTRAP_PRIVILEGED_SERVER
);
682 #else // BOOTSTRAP_PRIVILEGED_SERVER
683 *status
= bootstrap_look_up(bootstrap_port
, __SCNetworkConnectionGetControllerPortName(), &server
);
684 #endif // BOOTSTRAP_PRIVILEGED_SERVER
687 case BOOTSTRAP_SUCCESS
:
688 // service currently registered, "a good thing" (tm)
690 case BOOTSTRAP_NOT_PRIVILEGED
:
691 // the service is not privileged
693 case BOOTSTRAP_UNKNOWN_SERVICE
:
694 // service not currently registered, try again later
698 SC_log(LOG_DEBUG
, "bootstrap_look_up() failed: status=%s",
699 bootstrap_strerror(*status
));
704 scnc_server_name
= NULL
; /* reset pppcontroller server */
705 return MACH_PORT_NULL
;
709 __SCNetworkConnectionGetCurrentServerPort(void)
715 __SCNetworkConnectionRefreshServerPort(mach_port_t current_server
, int *mach_result
)
717 mach_port_t new_server
;
719 pthread_mutex_lock(&scnc_lock
);
720 if (scnc_server
!= MACH_PORT_NULL
) {
721 if (current_server
== scnc_server
) {
722 scnc_server_name
= NULL
;
723 // if the server we tried returned the error
724 (void)mach_port_deallocate(mach_task_self(), scnc_server
);
725 scnc_server
= __SCNetworkConnectionServerPort(mach_result
);
727 // another thread has refreshed the server port
730 scnc_server
= __SCNetworkConnectionServerPort(mach_result
);
732 new_server
= scnc_server
;
733 pthread_mutex_unlock(&scnc_lock
);
738 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && (!TARGET_OS_SIMULATOR || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 70000))
739 #define HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
743 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && !TARGET_OS_SIMULATOR
744 #define HAVE_PPPCONTROLLER_ATTACHWITHPROXY
748 __SCNetworkConnectionSessionPort(SCNetworkConnectionPrivateRef connectionPrivate
)
752 CFDataRef dataRef
= NULL
;
753 mach_port_t notify_port
= MACH_PORT_NULL
;
754 mach_port_t oldNotify
= MACH_PORT_NULL
;
756 int sc_status
= kSCStatusFailed
;
757 mach_port_t server
= __SCNetworkConnectionGetCurrentServerPort();
758 kern_return_t status
= KERN_SUCCESS
;
760 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
761 mach_port_t au_session
= MACH_PORT_NULL
;
762 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
764 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
765 return connectionPrivate
->session_port
;
768 if (connectionPrivate
->service
== NULL
) {
769 sc_status
= kSCStatusConnectionNoService
;
773 if (!_SCSerializeString(SCNetworkServiceGetServiceID(connectionPrivate
->service
), &dataRef
, &data
, &dataLen
)) {
777 if (connectionPrivate
->notify_port
!= NULL
) {
778 mach_port_t mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
780 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionSessionPort mp", mp
);
781 CFMachPortInvalidate(connectionPrivate
->notify_port
);
782 CFRelease(connectionPrivate
->notify_port
);
783 connectionPrivate
->notify_port
= NULL
;
784 mach_port_mod_refs(mach_task_self(), mp
, MACH_PORT_RIGHT_RECEIVE
, -1);
787 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
788 au_session
= audit_session_self();
789 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
791 // open a new session with the server
793 if ((connectionPrivate
->rlsFunction
!= NULL
) && (notify_port
== MACH_PORT_NULL
)) {
794 // allocate port (for server response)
795 status
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE
, ¬ify_port
);
796 if (status
!= KERN_SUCCESS
) {
797 SC_log(LOG_ERR
, "mach_port_allocate() failed: %s", mach_error_string(status
));
802 // add send right (passed to the server)
803 status
= mach_port_insert_right(mach_task_self(),
806 MACH_MSG_TYPE_MAKE_SEND
);
807 if (status
!= KERN_SUCCESS
) {
808 SC_log(LOG_NOTICE
, "mach_port_insert_right() failed: %s", mach_error_string(status
));
809 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
815 if (server
!= MACH_PORT_NULL
) {
816 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHPROXY
817 if ((connectionPrivate
->client_audit_session
== MACH_PORT_NULL
) &&
818 (connectionPrivate
->client_bootstrap_port
== MACH_PORT_NULL
) &&
819 (connectionPrivate
->client_uid
== geteuid()) &&
820 (connectionPrivate
->client_gid
== getegid()) &&
821 (connectionPrivate
->client_pid
== getpid())
823 #endif // HAVE_PPPCONTROLLER_ATTACHWITHPROXY
824 status
= pppcontroller_attach(server
,
826 (mach_msg_type_number_t
)dataLen
,
829 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
831 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
832 &connectionPrivate
->session_port
,
834 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHPROXY
836 mach_port_t client_au_session
;
837 mach_port_t client_bootstrap_port
;
839 if (connectionPrivate
->client_audit_session
== MACH_PORT_NULL
) {
840 client_au_session
= au_session
;
842 client_au_session
= connectionPrivate
->client_audit_session
;
845 if (connectionPrivate
->client_bootstrap_port
== MACH_PORT_NULL
) {
846 client_bootstrap_port
= bootstrap_port
;
848 client_bootstrap_port
= connectionPrivate
->client_bootstrap_port
;
851 status
= pppcontroller_attach_proxy(server
,
853 (mach_msg_type_number_t
)dataLen
,
854 client_bootstrap_port
,
857 connectionPrivate
->client_uid
,
858 connectionPrivate
->client_gid
,
859 connectionPrivate
->client_pid
,
860 &connectionPrivate
->session_port
,
863 #endif // HAVE_PPPCONTROLLER_ATTACHWITHPROXY
864 if (status
== KERN_SUCCESS
) {
865 if (sc_status
!= kSCStatusOK
) {
866 SC_log(LOG_DEBUG
, "attach w/error, sc_status=%s%s",
867 SCErrorString(sc_status
),
868 (connectionPrivate
->session_port
!= MACH_PORT_NULL
) ? ", w/session_port!=MACH_PORT_NULL" : "");
870 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
871 __MACH_PORT_DEBUG(TRUE
,
872 "*** __SCNetworkConnectionSessionPort session_port (attach w/error, cleanup)",
873 connectionPrivate
->session_port
);
874 mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
875 connectionPrivate
->session_port
= MACH_PORT_NULL
;
878 if (notify_port
!= MACH_PORT_NULL
) {
879 __MACH_PORT_DEBUG(TRUE
,
880 "*** __SCNetworkConnectionSessionPort notify_port (attach w/error, cleanup)",
882 (void) mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
883 notify_port
= MACH_PORT_NULL
;
889 // our [cached] server port is not valid
890 SC_log(LOG_INFO
, "!attach: %s", SCErrorString(status
));
891 if (status
== MACH_SEND_INVALID_DEST
) {
892 // the server is not yet available
893 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionSessionPort notify_port (!dest)", notify_port
);
894 } else if (status
== MIG_SERVER_DIED
) {
895 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionSessionPort notify_port (!mig)", notify_port
);
896 // the server we were using is gone and we've lost our send right
897 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
898 notify_port
= MACH_PORT_NULL
;
900 // if we got an unexpected error, don't retry
906 server
= __SCNetworkConnectionRefreshServerPort(server
, &sc_status
);
907 if (server
== MACH_PORT_NULL
) {
908 // if server not available
909 if (sc_status
== BOOTSTRAP_UNKNOWN_SERVICE
) {
910 // if first retry attempt, wait for SCDynamicStore server
912 SCDynamicStoreRef store
;
914 store
= SCDynamicStoreCreate(NULL
,
915 CFSTR("SCNetworkConnection connect"),
923 // wait up to 2.5 seconds for the [SCNetworkConnection] server
925 if ((retry
+= 50) < 2500) {
926 usleep(50 * 1000); // sleep 50ms between attempts
934 if (notify_port
!= MACH_PORT_NULL
) {
935 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
936 CFMachPortContext context
= { 0
937 , (void *)connectionPrivate
940 , pppMPCopyDescription
943 // request a notification when/if the server dies
944 status
= mach_port_request_notification(mach_task_self(),
946 MACH_NOTIFY_NO_SENDERS
,
949 MACH_MSG_TYPE_MAKE_SEND_ONCE
,
951 if (status
!= KERN_SUCCESS
) {
952 SC_log(LOG_NOTICE
, "mach_port_request_notification() failed: %s", mach_error_string(status
));
953 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
958 if (oldNotify
!= MACH_PORT_NULL
) {
959 SC_log(LOG_NOTICE
, "oldNotify != MACH_PORT_NULL");
962 // create CFMachPort for SCNetworkConnection notification callback
963 connectionPrivate
->notify_port
= _SC_CFMachPortCreateWithPort("SCNetworkConnection",
965 __SCNetworkConnectionMachCallBack
,
968 // we need to try a bit harder to acquire the initial status
969 connectionPrivate
->haveStatus
= FALSE
;
971 // with no server port, release the notification port we allocated
972 __MACH_PORT_DEBUG(TRUE
,
973 "*** __SCNetworkConnectionSessionPort notify_port (!server)",
975 (void) mach_port_mod_refs (mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
976 (void) mach_port_deallocate(mach_task_self(), notify_port
);
977 notify_port
= MACH_PORT_NULL
;
985 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
986 if (au_session
!= MACH_PORT_NULL
) {
987 (void)mach_port_deallocate(mach_task_self(), au_session
);
989 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
991 if (dataRef
!= NULL
) CFRelease(dataRef
);
995 __MACH_PORT_DEBUG(connectionPrivate
->session_port
!= MACH_PORT_NULL
,
996 "*** __SCNetworkConnectionSessionPort session_port",
997 connectionPrivate
->session_port
);
998 __MACH_PORT_DEBUG(notify_port
!= MACH_PORT_NULL
,
999 "*** __SCNetworkConnectionSessionPort notify_port",
1002 case BOOTSTRAP_UNKNOWN_SERVICE
:
1003 SC_log((status
== KERN_SUCCESS
) ? LOG_NOTICE
: LOG_ERR
, "PPPController not available");
1006 SC_log((status
== KERN_SUCCESS
) ? LOG_NOTICE
: LOG_ERR
, "pppcontroller_attach() failed: %s",
1007 SCErrorString(sc_status
));
1011 if (sc_status
!= kSCStatusOK
) {
1012 _SCErrorSet(sc_status
);
1015 return connectionPrivate
->session_port
;
1020 __SCNetworkConnectionReconnect(SCNetworkConnectionRef connection
)
1022 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1025 port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1026 return (port
!= MACH_PORT_NULL
);
1031 __SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection
)
1033 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1034 dispatch_group_t dispatchGroup
= NULL
;
1035 dispatch_queue_t dispatchQueue
= NULL
;
1037 CFArrayRef rlList
= NULL
;
1039 // Before we fully tearing down our [old] notifications, make sure
1040 // we have retained any information that is needed to re-register the
1041 // [new] notifications.
1043 pthread_mutex_lock(&connectionPrivate
->lock
);
1045 // save and cancel [old] notifications
1046 if (connectionPrivate
->rlList
!= NULL
) {
1047 rlList
= connectionPrivate
->rlList
;
1048 connectionPrivate
->rlList
= NULL
;
1050 if (connectionPrivate
->rls
!= NULL
) {
1051 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
1052 CFRelease(connectionPrivate
->rls
);
1053 connectionPrivate
->rls
= NULL
;
1055 if (connectionPrivate
->dispatchSource
!= NULL
) {
1056 dispatch_source_cancel(connectionPrivate
->dispatchSource
);
1057 connectionPrivate
->dispatchSource
= NULL
;
1060 // make sure dispatchSource is cancelled before removing group/queue
1061 if (connectionPrivate
->dispatchQueue
!= NULL
) {
1062 // save dispatchQueue, release reference when we've queue'd blocks
1063 // complete, allow re-scheduling
1064 dispatchGroup
= connectionPrivate
->dispatchGroup
;
1065 connectionPrivate
->dispatchGroup
= NULL
;
1066 dispatchQueue
= connectionPrivate
->dispatchQueue
;
1067 connectionPrivate
->dispatchQueue
= NULL
;
1069 // and take an extra reference for rescheduling
1070 dispatch_retain(dispatchQueue
);
1073 connectionPrivate
->scheduled
= FALSE
;
1075 pthread_mutex_unlock(&connectionPrivate
->lock
);
1077 if (dispatchGroup
!= NULL
) {
1078 dispatch_group_notify(dispatchGroup
, dispatchQueue
, ^{
1079 // release group/queue references
1080 dispatch_release(dispatchQueue
);
1081 dispatch_release(dispatchGroup
); // releases our connection reference
1086 if (rlList
!= NULL
) {
1090 n
= CFArrayGetCount(rlList
);
1091 for (i
= 0; i
< n
; i
+= 3) {
1092 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(rlList
, i
+1);
1093 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(rlList
, i
+2);
1095 ok
= SCNetworkConnectionScheduleWithRunLoop(connection
, rl
, rlMode
);
1097 if (SCError() != BOOTSTRAP_UNKNOWN_SERVICE
) {
1098 SC_log(LOG_NOTICE
, "SCNetworkConnectionScheduleWithRunLoop() failed");
1103 } else if (dispatchQueue
!= NULL
) {
1104 ok
= SCNetworkConnectionSetDispatchQueue(connection
, dispatchQueue
);
1106 if (SCError() != BOOTSTRAP_UNKNOWN_SERVICE
) {
1107 SC_log(LOG_NOTICE
, "SCNetworkConnectionSetDispatchQueue() failed");
1118 if (rlList
!= NULL
) {
1121 if (dispatchQueue
!= NULL
) {
1122 dispatch_release(dispatchQueue
);
1126 SC_log(LOG_NOTICE
, "SCNetworkConnection server %s, notification not restored",
1127 (SCError() == BOOTSTRAP_UNKNOWN_SERVICE
) ? "shutdown" : "failed");
1135 __SCNetworkConnectionNeedsRetry(SCNetworkConnectionRef connection
,
1136 const char *error_label
,
1137 kern_return_t status
,
1140 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1142 if (status
== KERN_SUCCESS
) {
1146 if ((status
== MACH_SEND_INVALID_DEST
) || (status
== MIG_SERVER_DIED
)) {
1147 // the server's gone and our session port's dead, remove the dead name right
1148 (void) mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
1150 // we got an unexpected error, leave the [session] port alone
1151 SC_log(LOG_NOTICE
, "%s: %s", error_label
, mach_error_string(status
));
1153 connectionPrivate
->session_port
= MACH_PORT_NULL
;
1154 if ((status
== MACH_SEND_INVALID_DEST
) || (status
== MIG_SERVER_DIED
)) {
1155 if (__SCNetworkConnectionReconnect(connection
)) {
1159 *sc_status
= status
;
1166 SCNetworkConnectionGetTypeID(void) {
1167 pthread_once(&initialized
, __SCNetworkConnectionInitialize
); /* initialize runtime */
1168 return __kSCNetworkConnectionTypeID
;
1172 CFArrayRef
/* of SCNetworkServiceRef's */
1173 SCNetworkConnectionCopyAvailableServices(SCNetworkSetRef set
)
1175 CFMutableArrayRef available
;
1176 Boolean tempSet
= FALSE
;
1179 SCPreferencesRef prefs
;
1181 prefs
= SCPreferencesCreate(NULL
, CFSTR("SCNetworkConnectionCopyAvailableServices"), NULL
);
1182 if (prefs
!= NULL
) {
1183 set
= SCNetworkSetCopyCurrent(prefs
);
1189 available
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1192 CFArrayRef services
;
1194 services
= SCNetworkSetCopyServices(set
);
1195 if (services
!= NULL
) {
1199 n
= CFArrayGetCount(services
);
1200 for (i
= 0; i
< n
; i
++) {
1201 SCNetworkInterfaceRef interface
;
1202 CFStringRef interfaceType
;
1203 SCNetworkServiceRef service
;
1205 service
= CFArrayGetValueAtIndex(services
, i
);
1206 interface
= SCNetworkServiceGetInterface(service
);
1207 if (interface
== NULL
) {
1211 interfaceType
= SCNetworkInterfaceGetInterfaceType(interface
);
1212 if (CFEqual(interfaceType
, kSCNetworkInterfaceTypePPP
) ||
1213 CFEqual(interfaceType
, kSCNetworkInterfaceTypeVPN
) ||
1214 CFEqual(interfaceType
, kSCNetworkInterfaceTypeIPSec
)) {
1215 CFArrayAppendValue(available
, service
);
1219 CFRelease(services
);
1223 if (tempSet
&& (set
!= NULL
)) {
1230 SCNetworkConnectionRef
1231 SCNetworkConnectionCreateWithService(CFAllocatorRef allocator
,
1232 SCNetworkServiceRef service
,
1233 SCNetworkConnectionCallBack callout
,
1234 SCNetworkConnectionContext
*context
)
1236 SCNetworkConnectionPrivateRef connectionPrivate
;
1238 if (!isA_SCNetworkService(service
)) {
1239 _SCErrorSet(kSCStatusInvalidArgument
);
1243 if (__SCNetworkServiceIsPPTP(service
)) {
1244 SC_log(LOG_INFO
, "PPTP VPNs are no longer supported");
1245 _SCErrorSet(kSCStatusConnectionIgnore
);
1249 connectionPrivate
= __SCNetworkConnectionCreatePrivate(allocator
, service
, callout
, context
);
1250 return (SCNetworkConnectionRef
)connectionPrivate
;
1254 SCNetworkConnectionRef
1255 SCNetworkConnectionCreateWithServiceID(CFAllocatorRef allocator
,
1256 CFStringRef serviceID
,
1257 SCNetworkConnectionCallBack callout
,
1258 SCNetworkConnectionContext
*context
)
1260 SCNetworkConnectionRef connection
;
1261 SCNetworkServiceRef service
;
1263 if (!isA_CFString(serviceID
)) {
1264 _SCErrorSet(kSCStatusInvalidArgument
);
1268 service
= _SCNetworkServiceCopyActive(NULL
, serviceID
);
1269 if (service
== NULL
) {
1273 connection
= SCNetworkConnectionCreateWithService(allocator
, service
, callout
, context
);
1280 SCNetworkConnectionRef
1281 SCNetworkConnectionCreate(CFAllocatorRef allocator
,
1282 SCNetworkConnectionCallBack callout
,
1283 SCNetworkConnectionContext
*context
)
1285 SCNetworkConnectionPrivateRef connectionPrivate
= __SCNetworkConnectionCreatePrivate(allocator
, NULL
, callout
, context
);
1286 return (SCNetworkConnectionRef
)connectionPrivate
;
1291 SCNetworkConnectionCopyServiceID(SCNetworkConnectionRef connection
)
1293 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1294 CFStringRef serviceID
;
1296 if (!isA_SCNetworkConnection(connection
)) {
1297 _SCErrorSet(kSCStatusInvalidArgument
);
1301 if (connectionPrivate
->service
== NULL
) {
1302 _SCErrorSet(kSCStatusConnectionNoService
);
1306 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
1307 return CFRetain(serviceID
);
1312 SCNetworkConnectionSetClientInfo(SCNetworkConnectionRef connection
,
1313 mach_port_t client_audit_session
,
1318 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1320 if (!isA_SCNetworkConnection(connection
)) {
1321 _SCErrorSet(kSCStatusInvalidArgument
);
1325 // save client audit session port
1326 if (connectionPrivate
->client_audit_session
!= MACH_PORT_NULL
) {
1327 mach_port_mod_refs(mach_task_self(),
1328 connectionPrivate
->client_audit_session
,
1329 MACH_PORT_RIGHT_SEND
,
1331 connectionPrivate
->client_audit_session
= MACH_PORT_NULL
;
1333 connectionPrivate
->client_audit_session
= client_audit_session
;
1334 if (connectionPrivate
->client_audit_session
!= MACH_PORT_NULL
) {
1335 mach_port_mod_refs(mach_task_self(),
1336 connectionPrivate
->client_audit_session
,
1337 MACH_PORT_RIGHT_SEND
,
1341 // save client UID, GID, and PID
1342 connectionPrivate
->client_uid
= client_uid
;
1343 connectionPrivate
->client_gid
= client_gid
;
1344 connectionPrivate
->client_pid
= client_pid
;
1351 SCNetworkConnectionSetClientAuditInfo(SCNetworkConnectionRef connection
,
1352 audit_token_t client_audit_token
,
1353 mach_port_t audit_session
,
1354 mach_port_t bootstrap_port
,
1357 const char *bundle_id
)
1359 const audit_token_t null_audit
= KERNEL_AUDIT_TOKEN_VALUE
;
1360 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1365 if (memcmp(&client_audit_token
, &null_audit
, sizeof(client_audit_token
))) {
1366 #if TARGET_OS_IPHONE
1367 audit_token_to_au32(client_audit_token
, NULL
, &uid
, &gid
, NULL
, NULL
, &pid
, NULL
, NULL
);
1368 #else // TARGET_OS_IPHONE
1369 uid
= audit_token_to_euid(client_audit_token
);
1370 gid
= audit_token_to_egid(client_audit_token
);
1371 pid
= audit_token_to_pid(client_audit_token
);
1372 #endif // TARGET_OS_IPHONE
1377 if (!SCNetworkConnectionSetClientInfo(connection
, audit_session
, uid
, gid
, pid
)) {
1381 if (connectionPrivate
->client_bootstrap_port
!= MACH_PORT_NULL
) {
1382 mach_port_mod_refs(mach_task_self(),
1383 connectionPrivate
->client_bootstrap_port
,
1384 MACH_PORT_RIGHT_SEND
,
1386 connectionPrivate
->client_bootstrap_port
= MACH_PORT_NULL
;
1389 connectionPrivate
->client_bootstrap_port
= bootstrap_port
;
1390 if (connectionPrivate
->client_bootstrap_port
!= MACH_PORT_NULL
) {
1391 mach_port_mod_refs(mach_task_self(),
1392 connectionPrivate
->client_bootstrap_port
,
1393 MACH_PORT_RIGHT_SEND
,
1397 memcpy(&connectionPrivate
->client_audit_token
, &client_audit_token
, sizeof(connectionPrivate
->client_audit_token
));
1399 if (uuid
!= NULL
&& !uuid_is_null(uuid
)) {
1400 uuid_copy(connectionPrivate
->client_uuid
, uuid
);
1403 if (connectionPrivate
->client_bundle_id
!= NULL
) {
1404 CFRelease(connectionPrivate
->client_bundle_id
);
1405 connectionPrivate
->client_bundle_id
= NULL
;
1408 if (bundle_id
!= NULL
) {
1409 connectionPrivate
->client_bundle_id
= CFStringCreateWithCString(kCFAllocatorDefault
, bundle_id
, kCFStringEncodingUTF8
);
1417 SCNetworkConnectionCopyStatistics(SCNetworkConnectionRef connection
)
1419 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1420 xmlDataOut_t data
= NULL
;
1421 mach_msg_type_number_t datalen
= 0;
1422 int sc_status
= kSCStatusFailed
;
1423 mach_port_t session_port
;
1424 CFPropertyListRef statistics
= NULL
;
1425 kern_return_t status
;
1427 if (!isA_SCNetworkConnection(connection
)) {
1428 _SCErrorSet(kSCStatusInvalidArgument
);
1432 pthread_mutex_lock(&connectionPrivate
->lock
);
1434 #if !TARGET_OS_SIMULATOR
1435 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1436 __block xpc_object_t xstats
= NULL
;
1437 ne_session_t ne_session
= connectionPrivate
->ne_session
;
1439 ne_session_retain(ne_session
);
1440 pthread_mutex_unlock(&connectionPrivate
->lock
);
1442 dispatch_semaphore_t ne_sema
= dispatch_semaphore_create(0);
1443 ne_session_get_info(ne_session
, NESessionInfoTypeStatistics
, __SCNetworkConnectionQueue(), ^(xpc_object_t result
) {
1444 if (result
!= NULL
) {
1445 xstats
= xpc_retain(result
);
1447 ne_session_release(ne_session
);
1448 dispatch_semaphore_signal(ne_sema
);
1450 dispatch_semaphore_wait(ne_sema
, DISPATCH_TIME_FOREVER
);
1451 dispatch_release(ne_sema
);
1453 if (xstats
!= NULL
) {
1454 statistics
= _CFXPCCreateCFObjectFromXPCObject(xstats
);
1455 xpc_release(xstats
);
1457 _SCErrorSet(kSCStatusFailed
);
1462 #endif /* !TARGET_OS_SIMULATOR */
1466 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1467 if (session_port
== MACH_PORT_NULL
) {
1471 status
= pppcontroller_copystatistics(session_port
, &data
, &datalen
, &sc_status
);
1472 if (__SCNetworkConnectionNeedsRetry(connection
,
1473 "SCNetworkConnectionCopyStatistics()",
1480 if (!_SCUnserialize(&statistics
, NULL
, data
, datalen
)) {
1481 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
1483 if ((sc_status
== kSCStatusOK
) && !isA_CFDictionary(statistics
)) {
1484 sc_status
= kSCStatusFailed
;
1488 if (sc_status
!= kSCStatusOK
) {
1489 if (statistics
!= NULL
) {
1490 CFRelease(statistics
);
1493 _SCErrorSet(sc_status
);
1498 pthread_mutex_unlock(&connectionPrivate
->lock
);
1504 SCNetworkConnectionGetService(SCNetworkConnectionRef connection
)
1506 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1508 if (!isA_SCNetworkConnection(connection
)) {
1509 _SCErrorSet(kSCStatusInvalidArgument
);
1513 return connectionPrivate
->service
;
1517 SCNetworkConnectionStatus
1518 SCNetworkConnectionGetStatus(SCNetworkConnectionRef connection
)
1520 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1521 SCNetworkConnectionStatus nc_status
= kSCNetworkConnectionInvalid
;
1523 int sc_status
= kSCStatusFailed
;
1524 mach_port_t session_port
;
1525 kern_return_t status
;
1526 CFStringRef serviceID
;
1528 if (!isA_SCNetworkConnection(connection
)) {
1529 _SCErrorSet(kSCStatusInvalidArgument
);
1530 return kSCNetworkConnectionInvalid
;
1533 if (connectionPrivate
->service
== NULL
) {
1534 _SCErrorSet(kSCStatusConnectionNoService
);
1535 return kSCNetworkConnectionInvalid
;
1538 // skip retry and return immediately if we know no service is to be found.
1539 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
1540 if (CFStringGetLength(serviceID
) == 0) {
1541 _SCErrorSet(kSCStatusConnectionNoService
);
1542 return kSCNetworkConnectionInvalid
;
1545 pthread_mutex_lock(&connectionPrivate
->lock
);
1547 #if !TARGET_OS_SIMULATOR
1548 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1549 __block ne_session_status_t ne_status
;
1550 ne_session_t ne_session
= connectionPrivate
->ne_session
;
1552 ne_session_retain(ne_session
);
1553 pthread_mutex_unlock(&connectionPrivate
->lock
);
1555 dispatch_semaphore_t ne_sema
= dispatch_semaphore_create(0);
1556 ne_session_get_status(ne_session
, __SCNetworkConnectionQueue(), ^(ne_session_status_t status
) {
1558 ne_session_release(ne_session
);
1559 dispatch_semaphore_signal(ne_sema
);
1561 dispatch_semaphore_wait(ne_sema
, DISPATCH_TIME_FOREVER
);
1562 dispatch_release(ne_sema
);
1564 return SCNetworkConnectionGetStatusFromNEStatus(ne_status
);
1566 #endif /* !TARGET_OS_SIMULATOR */
1570 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1571 if (session_port
== MACH_PORT_NULL
) {
1572 nc_status
= kSCNetworkConnectionInvalid
;
1576 status
= pppcontroller_getstatus(session_port
, &nc_status
, &sc_status
);
1577 if (__SCNetworkConnectionNeedsRetry(connection
,
1578 "SCNetworkConnectionGetStatus()",
1584 // wait up to 250 ms for the network service to become available
1585 if (!connectionPrivate
->haveStatus
&&
1586 (sc_status
== kSCStatusConnectionNoService
) &&
1587 ((retry
+= 10) < 250)) {
1588 usleep(10 * 1000); // sleep 10ms between attempts
1592 if (sc_status
== kSCStatusOK
) {
1593 connectionPrivate
->haveStatus
= TRUE
;
1595 _SCErrorSet(sc_status
);
1596 nc_status
= kSCNetworkConnectionInvalid
;
1601 pthread_mutex_unlock(&connectionPrivate
->lock
);
1607 SCNetworkConnectionCopyExtendedStatus(SCNetworkConnectionRef connection
)
1609 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1610 xmlDataOut_t data
= NULL
;
1611 mach_msg_type_number_t datalen
= 0;
1612 CFPropertyListRef extstatus
= NULL
;
1614 int sc_status
= kSCStatusFailed
;
1615 mach_port_t session_port
;
1616 kern_return_t status
;
1617 CFStringRef serviceID
;
1619 if (!isA_SCNetworkConnection(connection
)) {
1620 _SCErrorSet(kSCStatusInvalidArgument
);
1624 if (connectionPrivate
->service
== NULL
) {
1625 _SCErrorSet(kSCStatusConnectionNoService
);
1629 // skip retry and return immediately if we know no service is to be found.
1630 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
1631 if (CFStringGetLength(serviceID
) == 0) {
1632 _SCErrorSet(kSCStatusConnectionNoService
);
1636 pthread_mutex_lock(&connectionPrivate
->lock
);
1638 #if !TARGET_OS_SIMULATOR
1639 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1640 __block CFDictionaryRef statusDictionary
= NULL
;
1641 ne_session_t ne_session
= connectionPrivate
->ne_session
;
1643 ne_session_retain(ne_session
);
1644 pthread_mutex_unlock(&connectionPrivate
->lock
);
1646 dispatch_semaphore_t ne_sema
= dispatch_semaphore_create(0);
1647 ne_session_get_info(ne_session
, NESessionInfoTypeExtendedStatus
, __SCNetworkConnectionQueue(), ^(xpc_object_t extended_status
) {
1648 if (extended_status
!= NULL
) {
1649 statusDictionary
= _CFXPCCreateCFObjectFromXPCObject(extended_status
);
1650 ne_session_release(ne_session
);
1651 dispatch_semaphore_signal(ne_sema
);
1653 ne_session_get_status(ne_session
, __SCNetworkConnectionQueue(), ^(ne_session_status_t ne_status
) {
1654 SCNetworkConnectionStatus status
= SCNetworkConnectionGetStatusFromNEStatus(ne_status
);
1655 if (status
!= kSCNetworkConnectionInvalid
) {
1656 CFStringRef keys
[1] = { kSCNetworkConnectionStatus
};
1657 CFNumberRef values
[1] = { NULL
};
1658 values
[0] = CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &status
);
1659 statusDictionary
= CFDictionaryCreate(kCFAllocatorDefault
, (const void **)keys
, (const void **)values
, sizeof(values
) / sizeof(values
[0]), &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1660 CFRelease(values
[0]);
1662 ne_session_release(ne_session
);
1663 dispatch_semaphore_signal(ne_sema
);
1667 dispatch_semaphore_wait(ne_sema
, DISPATCH_TIME_FOREVER
);
1668 dispatch_release(ne_sema
);
1670 if (statusDictionary
!= NULL
) {
1671 extstatus
= (CFPropertyListRef
)statusDictionary
;
1673 _SCErrorSet(kSCStatusFailed
);
1682 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1683 if (session_port
== MACH_PORT_NULL
) {
1687 status
= pppcontroller_copyextendedstatus(session_port
, &data
, &datalen
, &sc_status
);
1688 if (__SCNetworkConnectionNeedsRetry(connection
,
1689 "SCNetworkConnectionCopyExtendedStatus()",
1696 if (!_SCUnserialize(&extstatus
, NULL
, data
, datalen
)) {
1697 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
1699 if ((sc_status
== kSCStatusOK
) && !isA_CFDictionary(extstatus
)) {
1700 sc_status
= kSCStatusFailed
;
1704 // wait up to 250 ms for the network service to become available
1705 if (!connectionPrivate
->haveStatus
&&
1706 (sc_status
== kSCStatusConnectionNoService
) &&
1707 ((retry
+= 10) < 250)) {
1708 usleep(10 * 1000); // sleep 10ms between attempts
1712 if (sc_status
== kSCStatusOK
) {
1713 connectionPrivate
->haveStatus
= TRUE
;
1715 if (extstatus
!= NULL
) {
1716 CFRelease(extstatus
);
1719 _SCErrorSet(sc_status
);
1724 pthread_mutex_unlock(&connectionPrivate
->lock
);
1730 _SCNetworkConnectionMergeDictionaries (const void *key
, const void *value
, void *context
)
1732 /* Add value only if not present */
1733 CFDictionaryAddValue((CFMutableDictionaryRef
)context
, (CFStringRef
)key
, (CFTypeRef
)value
);
1738 SCNetworkConnectionStart(SCNetworkConnectionRef connection
,
1739 CFDictionaryRef userOptions
,
1742 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1743 CFDataRef dataref
= NULL
;
1745 CFIndex datalen
= 0;
1747 int sc_status
= kSCStatusFailed
;
1748 mach_port_t session_port
;
1749 kern_return_t status
;
1751 if (!isA_SCNetworkConnection(connection
)) {
1752 _SCErrorSet(kSCStatusInvalidArgument
);
1756 if ((userOptions
!= NULL
) && !isA_CFDictionary(userOptions
)) {
1757 _SCErrorSet(kSCStatusInvalidArgument
);
1761 if (userOptions
== NULL
) {
1762 userOptions
= connectionPrivate
->on_demand_user_options
;
1763 } else if (connectionPrivate
->on_demand_user_options
!= NULL
) {
1764 CFDictionaryRef localUserOptions
= NULL
;
1766 localUserOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, userOptions
);
1767 if (localUserOptions
) {
1768 CFDictionaryApplyFunction(connectionPrivate
->on_demand_user_options
,
1769 _SCNetworkConnectionMergeDictionaries
,
1770 (void *)localUserOptions
);
1771 CFRelease(connectionPrivate
->on_demand_user_options
);
1772 userOptions
= connectionPrivate
->on_demand_user_options
= localUserOptions
;
1777 CFMutableDictionaryRef mdict
= NULL
;
1779 SC_log(LOG_INFO
, "SCNetworkConnectionStart (%p)", connectionPrivate
);
1781 if (userOptions
!= NULL
) {
1782 CFDictionaryRef dict
;
1783 CFStringRef encryption
;
1784 CFMutableDictionaryRef new_dict
;
1786 /* special code to remove secret information */
1787 mdict
= CFDictionaryCreateMutableCopy(NULL
, 0, userOptions
);
1789 dict
= CFDictionaryGetValue(mdict
, kSCEntNetPPP
);
1790 if (isA_CFDictionary(dict
)) {
1791 encryption
= CFDictionaryGetValue(dict
, kSCPropNetPPPAuthPasswordEncryption
);
1792 if (!isA_CFString(encryption
) ||
1793 !CFEqual(encryption
, kSCValNetPPPAuthPasswordEncryptionKeychain
)) {
1794 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1795 CFDictionaryReplaceValue(new_dict
, kSCPropNetPPPAuthPassword
, CFSTR("******"));
1796 CFDictionarySetValue(mdict
, kSCEntNetPPP
, new_dict
);
1797 CFRelease(new_dict
);
1801 dict
= CFDictionaryGetValue(mdict
, kSCEntNetL2TP
);
1802 if (isA_CFDictionary(dict
)) {
1803 encryption
= CFDictionaryGetValue(dict
, kSCPropNetL2TPIPSecSharedSecretEncryption
);
1804 if (!isA_CFString(encryption
) ||
1805 !CFEqual(encryption
, kSCValNetL2TPIPSecSharedSecretEncryptionKeychain
)) {
1806 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1807 CFDictionaryReplaceValue(new_dict
, kSCPropNetL2TPIPSecSharedSecret
, CFSTR("******"));
1808 CFDictionarySetValue(mdict
, kSCEntNetL2TP
, new_dict
);
1809 CFRelease(new_dict
);
1813 dict
= CFDictionaryGetValue(mdict
, kSCEntNetIPSec
);
1814 if (isA_CFDictionary(dict
)) {
1815 encryption
= CFDictionaryGetValue(dict
, kSCPropNetIPSecSharedSecretEncryption
);
1816 if (!isA_CFString(encryption
) ||
1817 !CFEqual(encryption
, kSCValNetIPSecSharedSecretEncryptionKeychain
)) {
1818 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1819 CFDictionaryReplaceValue(new_dict
, kSCPropNetIPSecSharedSecret
, CFSTR("******"));
1820 CFDictionarySetValue(mdict
, kSCEntNetIPSec
, new_dict
);
1821 CFRelease(new_dict
);
1826 SC_log(LOG_INFO
, "User options: %@", mdict
);
1827 if (mdict
!= NULL
) CFRelease(mdict
);
1830 pthread_mutex_lock(&connectionPrivate
->lock
);
1832 /* Clear out any cached flow divert token parameters */
1833 if (connectionPrivate
->flow_divert_token_params
!= NULL
) {
1834 CFRelease(connectionPrivate
->flow_divert_token_params
);
1835 connectionPrivate
->flow_divert_token_params
= NULL
;
1838 #if !TARGET_OS_SIMULATOR
1839 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1840 xpc_object_t xuser_options
= NULL
;
1842 if (userOptions
!= NULL
) {
1843 xuser_options
= _CFXPCCreateXPCObjectFromCFObject(userOptions
);
1846 if (connectionPrivate
->client_bootstrap_port
!= MACH_PORT_NULL
) {
1847 #if NE_SESSION_VERSION > 2
1848 ne_session_start_on_behalf_of(connectionPrivate
->ne_session
,
1850 connectionPrivate
->client_bootstrap_port
,
1851 connectionPrivate
->client_audit_session
,
1852 connectionPrivate
->client_uid
,
1853 connectionPrivate
->client_gid
,
1854 connectionPrivate
->client_pid
);
1856 ne_session_start_on_behalf_of(connectionPrivate
->ne_session
,
1858 connectionPrivate
->client_bootstrap_port
,
1859 connectionPrivate
->client_audit_session
,
1860 connectionPrivate
->client_uid
,
1861 connectionPrivate
->client_gid
);
1864 ne_session_start_with_options(connectionPrivate
->ne_session
, xuser_options
);
1867 /* make sure the xpc_message goes through */
1868 ne_session_send_barrier(connectionPrivate
->ne_session
);
1870 if (xuser_options
!= NULL
) {
1871 xpc_release(xuser_options
);
1877 #endif /* !TARGET_OS_SIMULATOR */
1879 if (userOptions
&& !_SCSerialize(userOptions
, &dataref
, &data
, &datalen
)) {
1885 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1886 if (session_port
== MACH_PORT_NULL
) {
1887 if (dataref
) CFRelease(dataref
);
1891 status
= pppcontroller_start(session_port
,
1893 (mach_msg_type_number_t
)datalen
,
1896 if (__SCNetworkConnectionNeedsRetry(connection
,
1897 "SCNetworkConnectionStart()",
1903 if (dataref
) CFRelease(dataref
);
1906 SC_log(LOG_INFO
, "SCNetworkConnectionStart (%p), return: %d", connectionPrivate
, sc_status
);
1909 if (sc_status
!= kSCStatusOK
) {
1910 _SCErrorSet(sc_status
);
1914 /* connection is now started */
1918 pthread_mutex_unlock(&connectionPrivate
->lock
);
1924 SCNetworkConnectionStop(SCNetworkConnectionRef connection
,
1925 Boolean forceDisconnect
)
1927 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1929 int sc_status
= kSCStatusFailed
;
1930 mach_port_t session_port
;
1931 kern_return_t status
;
1933 if (!isA_SCNetworkConnection(connection
)) {
1934 _SCErrorSet(kSCStatusInvalidArgument
);
1939 SC_log(LOG_INFO
, "SCNetworkConnectionStop (%p)", connectionPrivate
);
1942 pthread_mutex_lock(&connectionPrivate
->lock
);
1944 #if !TARGET_OS_SIMULATOR
1945 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1946 ne_session_stop(connectionPrivate
->ne_session
);
1947 /* make sure the xpc_message goes through */
1948 ne_session_send_barrier(connectionPrivate
->ne_session
);
1952 #endif /* !TARGET_OS_SIMULATOR */
1956 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1957 if (session_port
== MACH_PORT_NULL
) {
1961 status
= pppcontroller_stop(session_port
, forceDisconnect
, &sc_status
);
1962 if (__SCNetworkConnectionNeedsRetry(connection
,
1963 "SCNetworkConnectionStop()",
1970 SC_log(LOG_INFO
, "SCNetworkConnectionStop (%p), return: %d", connectionPrivate
, sc_status
);
1973 if (sc_status
!= kSCStatusOK
) {
1974 _SCErrorSet(sc_status
);
1978 /* connection is now disconnecting */
1983 pthread_mutex_unlock(&connectionPrivate
->lock
);
1989 SCNetworkConnectionSuspend(SCNetworkConnectionRef connection
)
1991 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1993 int sc_status
= kSCStatusFailed
;
1994 mach_port_t session_port
;
1995 kern_return_t status
;
1997 if (!isA_SCNetworkConnection(connection
)) {
1998 _SCErrorSet(kSCStatusInvalidArgument
);
2003 SC_log(LOG_INFO
, "SCNetworkConnectionSuspend (%p)", connectionPrivate
);
2006 pthread_mutex_lock(&connectionPrivate
->lock
);
2008 #if !!TARGET_OS_SIMULATOR
2009 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2010 /* Suspend only applies to PPPSerial and PPPoE */
2014 #endif /* !TARGET_OS_SIMULATOR */
2018 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2019 if (session_port
== MACH_PORT_NULL
) {
2023 status
= pppcontroller_suspend(session_port
, &sc_status
);
2024 if (__SCNetworkConnectionNeedsRetry(connection
,
2025 "SCNetworkConnectionSuspend()",
2032 SC_log(LOG_INFO
, "SCNetworkConnectionSuspend (%p), return: %d", connectionPrivate
, sc_status
);
2035 if (sc_status
!= kSCStatusOK
) {
2036 _SCErrorSet(sc_status
);
2040 /* connection is now suspended */
2045 pthread_mutex_unlock(&connectionPrivate
->lock
);
2051 SCNetworkConnectionResume(SCNetworkConnectionRef connection
)
2053 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2055 int sc_status
= kSCStatusFailed
;
2056 mach_port_t session_port
;
2057 kern_return_t status
;
2059 if (!isA_SCNetworkConnection(connection
)) {
2060 _SCErrorSet(kSCStatusInvalidArgument
);
2065 SC_log(LOG_INFO
, "SCNetworkConnectionResume (%p)", connectionPrivate
);
2068 pthread_mutex_lock(&connectionPrivate
->lock
);
2070 #if !TARGET_OS_SIMULATOR
2071 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2072 /* Resume only applies to PPPSerial and PPPoE */
2076 #endif /* !TARGET_OS_SIMULATOR */
2080 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2081 if (session_port
== MACH_PORT_NULL
) {
2085 status
= pppcontroller_resume(session_port
, &sc_status
);
2086 if (__SCNetworkConnectionNeedsRetry(connection
,
2087 "SCNetworkConnectionResume()",
2094 SC_log(LOG_INFO
, "SCNetworkConnectionResume (%p), return: %d", connectionPrivate
, sc_status
);
2097 if (sc_status
!= kSCStatusOK
) {
2098 _SCErrorSet(sc_status
);
2102 /* connection is now resume */
2107 pthread_mutex_unlock(&connectionPrivate
->lock
);
2112 #if !TARGET_OS_SIMULATOR
2114 SCNetworkConnectionRefreshOnDemandState(SCNetworkConnectionRef connection
)
2116 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2119 int sc_status
= kSCStatusFailed
;
2120 mach_port_t server_port
= __SCNetworkConnectionGetCurrentServerPort();
2121 kern_return_t status
= KERN_SUCCESS
;
2123 if (!isA_SCNetworkConnection(connection
)) {
2124 _SCErrorSet(kSCStatusInvalidArgument
);
2129 SC_log(LOG_INFO
, "SCNetworkConnectionRefreshOnDemandState (%p)", connectionPrivate
);
2132 pthread_mutex_lock(&connectionPrivate
->lock
);
2135 if (server_port
== MACH_PORT_NULL
) {
2136 server_port
= __SCNetworkConnectionRefreshServerPort(server_port
, &sc_status
);
2137 if (server_port
== MACH_PORT_NULL
) {
2138 // if server not available
2139 if (sc_status
== BOOTSTRAP_UNKNOWN_SERVICE
) {
2140 // wait up to 2.5 seconds for the [SCNetworkConnection] server
2142 if ((retry
+= 50) < 2500) {
2143 usleep(50 * 1000); // sleep 50ms between attempts
2151 status
= pppcontroller_ondemand_refresh_state(server_port
, &sc_status
);
2152 if (status
== KERN_SUCCESS
)
2155 if (status
== MACH_SEND_INVALID_DEST
) {
2156 // the server is not yet available
2157 SC_log(LOG_NOTICE
, "SCNetworkConnectionRefreshOnDemandState (!dest) (%p)", connectionPrivate
);
2158 } else if (status
== MIG_SERVER_DIED
) {
2159 // the server we were using is gone
2160 SC_log(LOG_NOTICE
, "SCNetworkConnectionRefreshOnDemandState (!mig) (%p)", connectionPrivate
);
2162 // if we got an unexpected error, don't retry
2169 SC_log(LOG_INFO
, "SCNetworkConnectionRefreshOnDemandState (%p), return: %d/%d", connectionPrivate
, status
, sc_status
);
2172 if (sc_status
!= kSCStatusOK
) {
2173 _SCErrorSet(sc_status
);
2181 pthread_mutex_unlock(&connectionPrivate
->lock
);
2184 #endif /* !TARGET_OS_SIMULATOR */
2188 SCNetworkConnectionCopyUserOptions(SCNetworkConnectionRef connection
)
2190 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2191 xmlDataOut_t data
= NULL
;
2192 mach_msg_type_number_t datalen
= 0;
2193 int sc_status
= kSCStatusFailed
;
2194 mach_port_t session_port
;
2195 kern_return_t status
;
2196 CFPropertyListRef userOptions
= NULL
;
2198 if (!isA_SCNetworkConnection(connection
)) {
2199 _SCErrorSet(kSCStatusInvalidArgument
);
2203 pthread_mutex_lock(&connectionPrivate
->lock
);
2205 #if !TARGET_OS_SIMULATOR
2206 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2207 __block xpc_object_t config
= NULL
;
2208 ne_session_t ne_session
= connectionPrivate
->ne_session
;
2210 ne_session_retain(ne_session
);
2211 pthread_mutex_unlock(&connectionPrivate
->lock
);
2213 dispatch_semaphore_t ne_sema
= dispatch_semaphore_create(0);
2214 ne_session_get_info(ne_session
, NESessionInfoTypeConfiguration
, __SCNetworkConnectionQueue(), ^(xpc_object_t result
) {
2215 if (result
!= NULL
) {
2216 config
= xpc_retain(result
);
2218 ne_session_release(ne_session
);
2219 dispatch_semaphore_signal(ne_sema
);
2221 dispatch_semaphore_wait(ne_sema
, DISPATCH_TIME_FOREVER
);
2222 dispatch_release(ne_sema
);
2224 if (config
!= NULL
) {
2225 xpc_object_t xoptions
= xpc_dictionary_get_value(config
, NESMSessionLegacyUserConfigurationKey
);
2226 if (xoptions
!= NULL
) {
2227 userOptions
= _CFXPCCreateCFObjectFromXPCObject(xoptions
);
2229 xpc_release(config
);
2233 #endif /* !TARGET_OS_SIMULATOR */
2237 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2238 if (session_port
== MACH_PORT_NULL
) {
2242 status
= pppcontroller_copyuseroptions(session_port
, &data
, &datalen
, &sc_status
);
2243 if (__SCNetworkConnectionNeedsRetry(connection
,
2244 "SCNetworkConnectionCopyUserOptions()",
2251 if (!_SCUnserialize(&userOptions
, NULL
, data
, datalen
)) {
2252 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
2254 if ((sc_status
== kSCStatusOK
) && (userOptions
!= NULL
) && !isA_CFDictionary(userOptions
)) {
2255 sc_status
= kSCStatusFailed
;
2259 if (sc_status
== kSCStatusOK
) {
2260 if (userOptions
== NULL
) {
2261 // if no user options, return an empty dictionary
2262 userOptions
= CFDictionaryCreate(NULL
,
2266 &kCFTypeDictionaryKeyCallBacks
,
2267 &kCFTypeDictionaryValueCallBacks
);
2271 CFRelease(userOptions
);
2274 _SCErrorSet(sc_status
);
2279 pthread_mutex_unlock(&connectionPrivate
->lock
);
2285 __SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection
,
2286 CFRunLoopRef runLoop
,
2287 CFStringRef runLoopMode
,
2288 dispatch_queue_t queue
)
2290 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2292 int sc_status
= kSCStatusFailed
;
2293 mach_port_t session_port
;
2294 kern_return_t status
;
2296 pthread_mutex_lock(&connectionPrivate
->lock
);
2298 if (connectionPrivate
->rlsFunction
== NULL
) {
2299 _SCErrorSet(kSCStatusInvalidArgument
);
2303 if ((connectionPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled on a dispatch queue
2304 ((queue
!= NULL
) && connectionPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
2305 _SCErrorSet(kSCStatusInvalidArgument
);
2309 if (!connectionPrivate
->scheduled
) {
2313 if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2314 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2315 if (session_port
== MACH_PORT_NULL
) {
2319 status
= pppcontroller_notification(session_port
, 1, &sc_status
);
2320 if (__SCNetworkConnectionNeedsRetry(connection
,
2321 "__SCNetworkConnectionScheduleWithRunLoop()",
2327 if (sc_status
!= kSCStatusOK
) {
2328 _SCErrorSet(sc_status
);
2332 if (runLoop
!= NULL
) {
2333 connectionPrivate
->rls
= CFMachPortCreateRunLoopSource(NULL
, connectionPrivate
->notify_port
, 0);
2334 connectionPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2336 } else if (runLoop
!= NULL
) {
2337 CFRunLoopSourceContext rlsContext
= {
2339 (void *)connection
, // info
2342 NULL
, // copy description
2347 __SCNetworkConnectionCallBack
, // perform
2350 connectionPrivate
->rls
= CFRunLoopSourceCreate(kCFAllocatorDefault
, 0, &rlsContext
);
2351 connectionPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2354 connectionPrivate
->scheduled
= TRUE
;
2357 if (queue
!= NULL
) {
2358 // retain the dispatch queue
2359 connectionPrivate
->dispatchQueue
= queue
;
2360 dispatch_retain(connectionPrivate
->dispatchQueue
);
2362 if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2363 dispatch_group_t group
= NULL
;
2365 dispatch_source_t source
;
2368 // We've taken a reference to the caller's dispatch_queue and we
2369 // want to hold on to that reference until we've processed any/all
2370 // notifications. To facilitate this we create a group, dispatch
2371 // any notification blocks via that group, and when the caller
2372 // has told us to stop the notifications (unschedule) we wait for
2373 // the group to empty and use the group's finalizer to release
2374 // our reference to the SCNetworkConnection.
2376 group
= dispatch_group_create();
2377 connectionPrivate
->dispatchGroup
= group
;
2378 CFRetain(connection
);
2379 dispatch_set_context(connectionPrivate
->dispatchGroup
, (void *)connection
);
2380 dispatch_set_finalizer_f(connectionPrivate
->dispatchGroup
, (dispatch_function_t
)CFRelease
);
2382 mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
2383 source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
, mp
, 0, queue
);
2384 if (source
== NULL
) {
2385 SC_log(LOG_NOTICE
, "dispatch_source_create() failed");
2386 _SCErrorSet(kSCStatusFailed
);
2390 // have our dispatch source hold a reference to the notification CFMachPort
2391 CFRetain(connectionPrivate
->notify_port
);
2392 dispatch_set_context(source
, (void *)connectionPrivate
->notify_port
);
2393 dispatch_set_finalizer_f(source
, (dispatch_function_t
)CFRelease
);
2395 dispatch_source_set_event_handler(source
, ^{
2398 u_int8_t buf
[sizeof(mach_msg_empty_t
) + MAX_TRAILER_SIZE
];
2399 mach_msg_empty_rcv_t msg
;
2400 mach_no_senders_notification_t no_senders
;
2401 } *notify_message_t
;
2402 CFMachPortRef notify_port
;
2403 notify_message_t notify_msg
;
2405 notify_msg
= (notify_message_t
)malloc(sizeof(*notify_msg
));
2407 kr
= mach_msg(¬ify_msg
->msg
.header
, // msg
2408 MACH_RCV_MSG
, // options
2410 sizeof(*notify_msg
), // rcv_size
2412 MACH_MSG_TIMEOUT_NONE
, // timeout
2413 MACH_PORT_NULL
); // notify
2414 if (kr
!= KERN_SUCCESS
) {
2415 SC_log(LOG_NOTICE
, "SCDynamicStore notification handler, kr=0x%x", kr
);
2420 CFRetain(connection
);
2421 notify_port
= dispatch_get_context(source
);
2423 dispatch_group_async(group
, queue
, ^{
2424 __SCNetworkConnectionMachCallBack(notify_port
,
2426 sizeof(*notify_msg
),
2427 (void *)connection
);
2429 CFRelease(connection
);
2433 dispatch_source_set_cancel_handler(source
, ^{
2434 dispatch_release(source
);
2437 connectionPrivate
->dispatchSource
= source
;
2438 dispatch_resume(source
);
2441 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, connectionPrivate
->rlList
)) {
2443 * if we do not already have notifications scheduled with
2444 * this runLoop / runLoopMode
2446 CFRunLoopAddSource(runLoop
, connectionPrivate
->rls
, runLoopMode
);
2449 _SC_schedule(connection
, runLoop
, runLoopMode
, connectionPrivate
->rlList
);
2452 #if !TARGET_OS_SIMULATOR
2453 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2454 CFRetain(connection
);
2455 ne_session_set_event_handler(connectionPrivate
->ne_session
, __SCNetworkConnectionQueue(), ^(ne_session_event_t event
, void *event_data
) {
2456 #pragma unused(event_data)
2457 if (event
== NESessionEventStatusChanged
) {
2458 CFRetain(connection
); /* Released in __SCNetworkConnectionCallBack */
2459 pthread_mutex_lock(&connectionPrivate
->lock
);
2460 if (connectionPrivate
->rls
!= NULL
) {
2461 CFRunLoopSourceSignal(connectionPrivate
->rls
);
2462 _SC_signalRunLoop(connection
, connectionPrivate
->rls
, connectionPrivate
->rlList
);
2463 } else if (connectionPrivate
->dispatchQueue
!= NULL
) {
2464 dispatch_async(connectionPrivate
->dispatchQueue
, ^{
2465 __SCNetworkConnectionCallBack((void *)connection
);
2468 pthread_mutex_unlock(&connectionPrivate
->lock
);
2469 } else if (event
== NESessionEventCanceled
) {
2470 CFRelease(connection
);
2474 #endif /* !TARGET_OS_SIMULATOR */
2480 pthread_mutex_unlock(&connectionPrivate
->lock
);
2486 __SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection
,
2487 CFRunLoopRef runLoop
,
2488 CFStringRef runLoopMode
,
2489 dispatch_queue_t queue
)
2491 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2492 dispatch_group_t drainGroup
= NULL
;
2493 dispatch_queue_t drainQueue
= NULL
;
2494 int sc_status
= kSCStatusFailed
;
2497 kern_return_t status
;
2499 // hold a reference while we unschedule
2500 CFRetain(connection
);
2502 pthread_mutex_lock(&connectionPrivate
->lock
);
2504 if ((runLoop
!= NULL
) && !connectionPrivate
->scheduled
) { // if we should be scheduled (but are not)
2505 _SCErrorSet(kSCStatusInvalidArgument
);
2509 if (((runLoop
== NULL
) && (connectionPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
2510 ((runLoop
!= NULL
) && (connectionPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
2511 _SCErrorSet(kSCStatusInvalidArgument
);
2515 if (connectionPrivate
->dispatchQueue
!= NULL
) {
2516 if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2517 // cancel dispatchSource
2518 if (connectionPrivate
->dispatchSource
!= NULL
) {
2519 dispatch_source_cancel(connectionPrivate
->dispatchSource
);
2520 connectionPrivate
->dispatchSource
= NULL
;
2523 // save dispatchQueue/group, release reference when all queue'd blocks
2524 // have been processed, allow re-scheduling
2525 drainGroup
= connectionPrivate
->dispatchGroup
;
2526 connectionPrivate
->dispatchGroup
= NULL
;
2527 drainQueue
= connectionPrivate
->dispatchQueue
;
2528 connectionPrivate
->dispatchQueue
= NULL
;
2530 dispatch_release(connectionPrivate
->dispatchQueue
);
2531 connectionPrivate
->dispatchQueue
= NULL
;
2534 if (!_SC_unschedule(connection
, runLoop
, runLoopMode
, connectionPrivate
->rlList
, FALSE
)) {
2535 // if not currently scheduled on this runLoop / runLoopMode
2536 _SCErrorSet(kSCStatusFailed
);
2540 n
= CFArrayGetCount(connectionPrivate
->rlList
);
2541 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, connectionPrivate
->rlList
)) {
2543 * if we are no longer scheduled to receive notifications for
2544 * this runLoop / runLoopMode
2546 CFRunLoopRemoveSource(runLoop
, connectionPrivate
->rls
, runLoopMode
);
2549 // if *all* notifications have been unscheduled
2550 CFRelease(connectionPrivate
->rlList
);
2551 connectionPrivate
->rlList
= NULL
;
2552 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
2553 CFRelease(connectionPrivate
->rls
);
2554 connectionPrivate
->rls
= NULL
;
2560 // if *all* notifications have been unscheduled
2561 connectionPrivate
->scheduled
= FALSE
;
2563 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2564 #if !TARGET_OS_SIMULATOR
2565 ne_session_cancel(connectionPrivate
->ne_session
);
2566 #endif /* !TARGET_OS_SIMULATOR */
2568 mach_port_t session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2569 if (session_port
== MACH_PORT_NULL
) {
2573 status
= pppcontroller_notification(session_port
, 0, &sc_status
);
2574 if (__SCNetworkConnectionNeedsRetry(connection
,
2575 "__SCNetworkConnectionUnscheduleFromRunLoop pppcontroller_notification()",
2578 sc_status
= kSCStatusOK
;
2579 status
= KERN_SUCCESS
;
2582 if ((status
!= KERN_SUCCESS
) || (sc_status
!= kSCStatusOK
)) {
2583 _SCErrorSet(sc_status
);
2593 pthread_mutex_unlock(&connectionPrivate
->lock
);
2595 if (drainGroup
!= NULL
) {
2596 dispatch_group_notify(drainGroup
, drainQueue
, ^{
2597 // release group/queue references
2598 dispatch_release(drainQueue
);
2599 dispatch_release(drainGroup
); // releases our connection reference
2603 // release our reference
2604 CFRelease(connection
);
2611 SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection
,
2612 CFRunLoopRef runLoop
,
2613 CFStringRef runLoopMode
)
2615 if (!isA_SCNetworkConnection(connection
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
2616 _SCErrorSet(kSCStatusInvalidArgument
);
2620 return __SCNetworkConnectionScheduleWithRunLoop(connection
, runLoop
, runLoopMode
, NULL
);
2625 SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection
,
2626 CFRunLoopRef runLoop
,
2627 CFStringRef runLoopMode
)
2629 if (!isA_SCNetworkConnection(connection
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
2630 _SCErrorSet(kSCStatusInvalidArgument
);
2634 return __SCNetworkConnectionUnscheduleFromRunLoop(connection
, runLoop
, runLoopMode
, NULL
);
2639 SCNetworkConnectionSetDispatchQueue(SCNetworkConnectionRef connection
,
2640 dispatch_queue_t queue
)
2644 if (!isA_SCNetworkConnection(connection
)) {
2645 _SCErrorSet(kSCStatusInvalidArgument
);
2649 if (queue
!= NULL
) {
2650 ok
= __SCNetworkConnectionScheduleWithRunLoop(connection
, NULL
, NULL
, queue
);
2652 ok
= __SCNetworkConnectionUnscheduleFromRunLoop(connection
, NULL
, NULL
, NULL
);
2659 /* Requires having called SCNetworkConnectionSelectServiceWithOptions previously */
2661 SCNetworkConnectionIsOnDemandSuspended(SCNetworkConnectionRef connection
)
2663 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2665 if (!isA_SCNetworkConnection(connection
)) {
2666 _SCErrorSet(kSCStatusInvalidArgument
);
2670 if (connectionPrivate
->on_demand_info
!= NULL
) {
2671 uint32_t isSuspended
= 0;
2672 CFNumberRef num
= NULL
;
2674 num
= CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCPropNetVPNOnDemandSuspended
);
2675 if (isA_CFNumber(num
) &&
2676 CFNumberGetValue(num
, kCFNumberSInt32Type
, &isSuspended
) &&
2677 (isSuspended
!= 0)) {
2682 _SCErrorSet(kSCStatusOK
);
2687 SCNetworkConnectionTriggerOnDemandIfNeeded (CFStringRef hostName
,
2688 Boolean afterDNSFail
,
2692 #if !TARGET_OS_SIMULATOR
2693 __block Boolean triggeredOnDemand
= FALSE
;
2694 struct proc_uniqidentifierinfo procu
;
2695 void *policy_match
= NULL
;
2696 char *hostname
= NULL
;
2697 pid_t pid
= getpid();
2698 uid_t uid
= geteuid();
2700 /* Require hostName, require non-root user */
2701 if (hostName
== NULL
|| geteuid() == 0) {
2705 hostname
= _SC_cfstring_to_cstring(hostName
, NULL
, 0, kCFStringEncodingUTF8
);
2707 if (proc_pidinfo(pid
, PROC_PIDUNIQIDENTIFIERINFO
, 1, &procu
, sizeof(procu
)) != sizeof(procu
)) {
2711 policy_match
= ne_session_copy_policy_match(hostname
, NULL
, NULL
, procu
.p_uuid
, procu
.p_uuid
, pid
, uid
, 0, trafficClass
);
2713 NEPolicyServiceActionType action_type
= ne_session_policy_match_get_service_action(policy_match
);
2714 if (action_type
== NESessionPolicyActionTrigger
||
2715 (afterDNSFail
&& action_type
== NESessionPolicyActionTriggerIfNeeded
)) {
2717 if (ne_session_policy_match_get_service(policy_match
, config_id
)) {
2718 xpc_object_t start_options
= xpc_dictionary_create(NULL
, NULL
, 0);
2719 if (start_options
!= NULL
) {
2720 xpc_dictionary_set_bool(start_options
, NESessionStartOptionIsOnDemandKey
, true);
2721 xpc_dictionary_set_string(start_options
, NESessionStartOptionMatchHostnameKey
, hostname
);
2723 ne_session_t new_session
= ne_session_create(config_id
, ne_session_policy_match_get_service_type(policy_match
));
2724 if (new_session
!= NULL
) {
2725 dispatch_semaphore_t wait_for_session
= dispatch_semaphore_create(0);
2726 dispatch_retain(wait_for_session
);
2727 xpc_retain(start_options
);
2728 ne_session_get_status(new_session
, __SCNetworkConnectionQueue(),
2729 ^(ne_session_status_t status
) {
2730 if (status
== NESessionStatusDisconnected
) {
2731 dispatch_retain(wait_for_session
);
2732 ne_session_set_event_handler(new_session
, __SCNetworkConnectionQueue(),
2733 ^(ne_session_event_t event
, void *event_data
) {
2734 os_activity_t activity
;
2736 activity
= os_activity_create("processing ne_session notification",
2737 OS_ACTIVITY_CURRENT
,
2738 OS_ACTIVITY_FLAG_DEFAULT
);
2739 os_activity_scope(activity
);
2741 if (event
== NESessionEventStatusChanged
) {
2742 dispatch_retain(wait_for_session
);
2743 ne_session_get_status(new_session
, __SCNetworkConnectionQueue(),
2744 ^(ne_session_status_t new_status
) {
2745 if (new_status
!= NESessionStatusConnecting
) {
2746 if (status
== NESessionStatusConnected
) {
2747 triggeredOnDemand
= TRUE
;
2749 ne_session_cancel(new_session
);
2751 dispatch_release(wait_for_session
);
2753 } else if (event
== NESessionEventCanceled
) {
2754 dispatch_semaphore_signal(wait_for_session
);
2755 dispatch_release(wait_for_session
);
2758 os_release(activity
);
2760 ne_session_start_with_options(new_session
, start_options
);
2762 dispatch_semaphore_signal(wait_for_session
);
2764 dispatch_release(wait_for_session
);
2765 xpc_release(start_options
);
2767 dispatch_semaphore_wait(wait_for_session
, timeout
? dispatch_time(DISPATCH_TIME_NOW
, (int64_t)timeout
* NSEC_PER_SEC
) : DISPATCH_TIME_FOREVER
);
2768 dispatch_release(wait_for_session
);
2769 ne_session_release(new_session
);
2772 xpc_release(start_options
);
2778 CFAllocatorDeallocate(NULL
, hostname
);
2785 return triggeredOnDemand
;
2787 #pragma unused(hostName, afterDNSFail, timeout, trafficClass)
2794 SCNetworkConnectionCopyOnDemandInfo(SCNetworkConnectionRef connection
,
2795 CFStringRef
*onDemandRemoteAddress
,
2796 SCNetworkConnectionStatus
*onDemandConnectionStatus
)
2798 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2800 if (!isA_SCNetworkConnection(connection
)) {
2801 _SCErrorSet(kSCStatusInvalidArgument
);
2805 if (connectionPrivate
->service
== NULL
) {
2806 _SCErrorSet(kSCStatusConnectionNoService
);
2810 if (onDemandRemoteAddress
!= NULL
) {
2811 *onDemandRemoteAddress
= NULL
;
2814 if (onDemandConnectionStatus
!= NULL
) {
2815 *onDemandConnectionStatus
= kSCNetworkConnectionInvalid
;
2818 if (connectionPrivate
->on_demand_info
!= NULL
) {
2819 if (onDemandRemoteAddress
!= NULL
) {
2820 CFStringRef address
=
2821 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandRemoteAddress
);
2822 if (isA_CFString(address
)) {
2823 *onDemandRemoteAddress
= address
;
2824 CFRetain(*onDemandRemoteAddress
);
2828 if (onDemandConnectionStatus
!= NULL
) {
2830 CFNumberRef status_num
=
2831 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandStatus
);
2832 if (isA_CFNumber(status_num
) && CFNumberGetValue(status_num
, kCFNumberIntType
, &num
)) {
2833 *onDemandConnectionStatus
= num
;
2838 return connectionPrivate
->on_demand
;
2843 SCNetworkConnectionGetReachabilityInfo(SCNetworkConnectionRef connection
,
2844 SCNetworkReachabilityFlags
*reach_flags
,
2845 unsigned int *reach_if_index
)
2847 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2849 if (!isA_SCNetworkConnection(connection
)) {
2850 _SCErrorSet(kSCStatusInvalidArgument
);
2854 if (connectionPrivate
->service
== NULL
) {
2855 _SCErrorSet(kSCStatusConnectionNoService
);
2859 if (reach_flags
!= NULL
) {
2863 if (reach_if_index
!= NULL
) {
2864 *reach_if_index
= 0;
2867 if (connectionPrivate
->on_demand_info
!= NULL
) {
2868 if (reach_flags
!= NULL
) {
2870 CFNumberRef flags_num
=
2871 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandReachFlags
);
2872 if (isA_CFNumber(flags_num
) && CFNumberGetValue(flags_num
, kCFNumberIntType
, &num
)) {
2877 if (reach_if_index
!= NULL
) {
2879 CFNumberRef if_index_num
=
2880 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandReachInterfaceIndex
);
2881 if (isA_CFNumber(if_index_num
) && CFNumberGetValue(if_index_num
, kCFNumberIntType
, &num
)) {
2882 *reach_if_index
= num
;
2891 SCNetworkConnectionType
2892 SCNetworkConnectionGetType(SCNetworkConnectionRef connection
)
2894 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2896 if (!isA_SCNetworkConnection(connection
)) {
2897 _SCErrorSet(kSCStatusInvalidArgument
);
2898 return kSCNetworkConnectionTypeUnknown
;
2901 if (connectionPrivate
->service
== NULL
) {
2902 _SCErrorSet(kSCStatusConnectionNoService
);
2903 return kSCNetworkConnectionTypeUnknown
;
2906 _SCErrorSet(kSCStatusOK
);
2908 return connectionPrivate
->type
;
2913 SCNetworkConnectionCopyFlowDivertToken(SCNetworkConnectionRef connection
,
2914 CFDictionaryRef flowProperties
)
2916 #pragma unused(connection, flowProperties)
2917 _SCErrorSet(kSCStatusFailed
);
2923 SCNetworkConnectionGetServiceIdentifier (SCNetworkConnectionRef connection
)
2925 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2926 int service_identifier
= -1;
2928 if (connectionPrivate
->service
!= NULL
) {
2929 service_identifier
= 0;
2930 if (connectionPrivate
->on_demand_info
!= NULL
) {
2931 CFNumberRef id_num
= CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCPropNetDNSServiceIdentifier
);
2933 if (isA_CFNumber(id_num
)) {
2934 CFNumberGetValue(id_num
, kCFNumberIntType
, &service_identifier
);
2939 return service_identifier
;
2943 #if !TARGET_OS_SIMULATOR
2944 SCNetworkConnectionStatus
2945 SCNetworkConnectionGetStatusFromNEStatus(ne_session_status_t status
)
2948 case NESessionStatusInvalid
:
2949 return kSCNetworkConnectionInvalid
;
2950 case NESessionStatusDisconnected
:
2951 return kSCNetworkConnectionDisconnected
;
2952 case NESessionStatusConnecting
:
2953 case NESessionStatusReasserting
:
2954 return kSCNetworkConnectionConnecting
;
2955 case NESessionStatusConnected
:
2956 return kSCNetworkConnectionConnected
;
2957 case NESessionStatusDisconnecting
:
2958 return kSCNetworkConnectionDisconnecting
;
2961 return kSCNetworkConnectionInvalid
;
2963 #endif /* !TARGET_OS_SIMULATOR */
2967 #pragma mark User level "dial" API
2970 #define k_NetworkConnect_Notification "com.apple.networkConnect"
2971 #define k_NetworkConnect_Pref_File CFSTR("com.apple.networkConnect")
2972 #define k_InterentConnect_Pref_File CFSTR("com.apple.internetconnect")
2974 #define k_Dial_Default_Key CFSTR("ConnectByDefault") // needs to go into SC
2975 #define k_Last_Service_Id_Key CFSTR("ServiceID")
2976 #define k_Unique_Id_Key CFSTR("UniqueIdentifier")
2979 /* Private Prototypes */
2980 static Boolean
SCNetworkConnectionPrivateCopyDefaultServiceIDForDial (CFStringRef
*serviceID
);
2981 static Boolean
SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore (CFStringRef
*serviceID
);
2982 static Boolean
SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray
, CFDictionaryRef
*userOptions
);
2983 static Boolean
SCNetworkConnectionPrivateIsPPPService (CFStringRef serviceID
, CFStringRef subType1
, CFStringRef subType2
);
2984 static void addPasswordFromKeychain (CFStringRef serviceID
, CFDictionaryRef
*userOptions
);
2985 static CFStringRef
copyPasswordFromKeychain (CFStringRef uniqueID
);
2987 static int notify_userprefs_token
= -1;
2989 static CFDictionaryRef onDemand_configuration
= NULL
;
2990 static Boolean onDemand_force_refresh
= FALSE
;
2991 static pthread_mutex_t onDemand_notify_lock
= PTHREAD_MUTEX_INITIALIZER
;
2992 static int onDemand_notify_token
= -1;
2996 * return TRUE if domain1 ends with domain2, and will check for trailing "."
2998 #define WILD_CARD_MATCH_STR CFSTR("*")
3000 _SC_domainEndsWithDomain(CFStringRef compare_domain
, CFStringRef match_domain
)
3003 Boolean ret
= FALSE
;
3004 CFStringRef s1
= NULL
;
3005 Boolean s1_created
= FALSE
;
3006 CFStringRef s2
= NULL
;
3007 Boolean s2_created
= FALSE
;
3008 CFStringRef s3
= NULL
;
3010 if (CFEqual(match_domain
, WILD_CARD_MATCH_STR
)) {
3014 if (CFStringHasSuffix(compare_domain
, CFSTR("."))) {
3016 range
.length
= CFStringGetLength(compare_domain
) - 1;
3017 s1
= CFStringCreateWithSubstring(NULL
, compare_domain
, range
);
3023 s1
= compare_domain
;
3026 if (CFStringHasSuffix(match_domain
, CFSTR("."))) {
3028 range
.length
= CFStringGetLength(match_domain
) - 1;
3029 s2
= CFStringCreateWithSubstring(NULL
, match_domain
, range
);
3038 if (CFStringHasPrefix(s2
, CFSTR("*."))) {
3040 range
.length
= CFStringGetLength(s2
)-2;
3041 s3
= CFStringCreateWithSubstring(NULL
, s2
, range
);
3052 ret
= CFStringHasSuffix(s1
, s2
);
3056 if (s1_created
) CFRelease(s1
);
3057 if (s2_created
) CFRelease(s2
);
3061 static CFCharacterSetRef
3062 _SC_getNotDotOrStarCharacterSet (void)
3064 static CFCharacterSetRef notDotOrStar
= NULL
;
3065 if (notDotOrStar
== NULL
) {
3066 CFCharacterSetRef dotOrStar
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, CFSTR(".*"));
3068 notDotOrStar
= CFCharacterSetCreateInvertedSet(kCFAllocatorDefault
, dotOrStar
);
3069 CFRelease(dotOrStar
);
3072 return notDotOrStar
;
3075 static CFMutableStringRef
3076 _SC_createStringByTrimmingDotsAndStars (CFStringRef string
)
3078 CFCharacterSetRef notDotOrStar
= _SC_getNotDotOrStarCharacterSet();
3079 CFRange entireString
= CFRangeMake(0, CFStringGetLength(string
));
3080 CFMutableStringRef result
= CFStringCreateMutableCopy(kCFAllocatorDefault
, entireString
.length
, string
);
3082 CFRange end
= CFRangeMake(entireString
.length
, 0);
3084 if (CFStringFindCharacterFromSet(string
, notDotOrStar
, entireString
, 0, &start
) &&
3085 CFStringFindCharacterFromSet(string
, notDotOrStar
, entireString
, kCFCompareBackwards
, &end
)) {
3086 if (start
.location
== kCFNotFound
|| end
.location
== kCFNotFound
|| start
.location
> end
.location
) {
3092 if ((end
.location
+ 1) < entireString
.length
) {
3093 CFStringReplace(result
, CFRangeMake(end
.location
+ 1, entireString
.length
- (end
.location
+ 1)), CFSTR(""));
3095 if (start
.location
> 0) {
3096 CFStringReplace(result
, CFRangeMake(0, start
.location
), CFSTR(""));
3103 _SC_getCountOfStringInString (CFStringRef string
, CFStringRef substring
)
3106 CFArrayRef ranges
= CFStringCreateArrayWithFindResults(kCFAllocatorDefault
, string
, substring
, CFRangeMake(0, CFStringGetLength(string
)), 0);
3107 if (ranges
!= NULL
) {
3108 count
= CFArrayGetCount(ranges
);
3115 _SC_hostMatchesDomain(CFStringRef hostname
, CFStringRef domain
)
3117 Boolean result
= FALSE
;
3118 CFMutableStringRef trimmedHostname
= NULL
;
3119 CFMutableStringRef trimmedDomain
= NULL
;
3121 if (!isA_CFString(hostname
) || !isA_CFString(domain
)) {
3125 trimmedHostname
= _SC_createStringByTrimmingDotsAndStars(hostname
);
3126 trimmedDomain
= _SC_createStringByTrimmingDotsAndStars(domain
);
3128 if (!isA_CFString(trimmedHostname
) || !isA_CFString(trimmedDomain
)) {
3132 CFIndex numHostnameDots
= _SC_getCountOfStringInString(trimmedHostname
, CFSTR("."));
3133 CFIndex numDomainDots
= _SC_getCountOfStringInString(trimmedDomain
, CFSTR("."));
3134 if (numHostnameDots
== numDomainDots
) {
3135 result
= CFEqual(trimmedHostname
, trimmedDomain
);
3136 } else if (numDomainDots
> 0 && numDomainDots
< numHostnameDots
) {
3137 CFStringReplace(trimmedDomain
, CFRangeMake(0, 0), CFSTR("."));
3138 result
= CFStringHasSuffix(trimmedHostname
, trimmedDomain
);
3144 if (trimmedHostname
) {
3145 CFRelease(trimmedHostname
);
3147 if (trimmedDomain
) {
3148 CFRelease(trimmedDomain
);
3155 static CFDictionaryRef
3156 __SCNetworkConnectionCopyOnDemandConfiguration(void)
3160 uint64_t triggersCount
= 0;
3161 CFDictionaryRef configuration
;
3163 pthread_mutex_lock(&onDemand_notify_lock
);
3164 if (onDemand_notify_token
== -1) {
3165 status
= notify_register_check(kSCNETWORKCONNECTION_ONDEMAND_NOTIFY_KEY
, &onDemand_notify_token
);
3166 if (status
!= NOTIFY_STATUS_OK
) {
3167 SC_log(LOG_NOTICE
, "notify_register_check() failed, status=%d", status
);
3168 onDemand_notify_token
= -1;
3172 if (onDemand_notify_token
!= -1) {
3173 status
= notify_check(onDemand_notify_token
, &changed
);
3174 if (status
!= NOTIFY_STATUS_OK
) {
3175 SC_log(LOG_NOTICE
, "notify_check() failed, status=%d", status
);
3176 (void)notify_cancel(onDemand_notify_token
);
3177 onDemand_notify_token
= -1;
3181 if (changed
&& (onDemand_notify_token
!= -1)) {
3182 status
= notify_get_state(onDemand_notify_token
, &triggersCount
);
3183 if (status
!= NOTIFY_STATUS_OK
) {
3184 SC_log(LOG_NOTICE
, "notify_get_state() failed, status=%d", status
);
3185 (void)notify_cancel(onDemand_notify_token
);
3186 onDemand_notify_token
= -1;
3190 if (changed
|| onDemand_force_refresh
) {
3193 SC_log(LOG_INFO
, "OnDemand information %s",
3194 (onDemand_configuration
== NULL
) ? "fetched" : "updated");
3196 if (onDemand_configuration
!= NULL
) {
3197 CFRelease(onDemand_configuration
);
3198 onDemand_configuration
= NULL
;
3201 if ((triggersCount
> 0) || onDemand_force_refresh
) {
3202 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, kSCDynamicStoreDomainState
, kSCEntNetOnDemand
);
3203 onDemand_configuration
= SCDynamicStoreCopyValue(NULL
, key
);
3205 if ((onDemand_configuration
!= NULL
) && !isA_CFDictionary(onDemand_configuration
)) {
3206 CFRelease(onDemand_configuration
);
3207 onDemand_configuration
= NULL
;
3211 onDemand_force_refresh
= FALSE
;
3214 configuration
= (onDemand_configuration
!= NULL
) ? CFRetain(onDemand_configuration
) : NULL
;
3215 pthread_mutex_unlock(&onDemand_notify_lock
);
3217 return configuration
;
3223 __SCNetworkConnectionForceOnDemandConfigurationRefresh(void)
3225 pthread_mutex_lock(&onDemand_notify_lock
);
3226 onDemand_force_refresh
= TRUE
;
3227 pthread_mutex_unlock(&onDemand_notify_lock
);
3234 __SCNetworkConnectionShouldNeverMatch(CFDictionaryRef trigger
, CFStringRef hostName
, pid_t client_pid
)
3236 CFArrayRef exceptedProcesses
;
3237 CFIndex exceptedProcessesCount
;
3238 CFIndex exceptedProcessesIndex
;
3239 CFArrayRef exceptions
;
3240 CFIndex exceptionsCount
;
3241 int exceptionsIndex
;
3243 // we have a matching domain, check against exception list
3244 exceptions
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandMatchDomainsNever
);
3245 exceptionsCount
= isA_CFArray(exceptions
) ? CFArrayGetCount(exceptions
) : 0;
3246 for (exceptionsIndex
= 0; exceptionsIndex
< exceptionsCount
; exceptionsIndex
++) {
3247 CFStringRef exception
;
3249 exception
= CFArrayGetValueAtIndex(exceptions
, exceptionsIndex
);
3250 if (isA_CFString(exception
) && _SC_domainEndsWithDomain(hostName
, exception
)) {
3251 // found matching exception
3252 SC_log(LOG_INFO
, "OnDemand match exception");
3257 if (client_pid
!= 0) {
3258 exceptedProcesses
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandPluginPIDs
);
3259 exceptedProcessesCount
= isA_CFArray(exceptedProcesses
) ? CFArrayGetCount(exceptedProcesses
) : 0;
3260 for (exceptedProcessesIndex
= 0; exceptedProcessesIndex
< exceptedProcessesCount
; exceptedProcessesIndex
++) {
3264 pidRef
= CFArrayGetValueAtIndex(exceptedProcesses
, exceptedProcessesIndex
);
3265 if (isA_CFNumber(pidRef
) && CFNumberGetValue(pidRef
, kCFNumberIntType
, &pid
)) {
3266 if (pid
== client_pid
) {
3277 __SCNetworkConnectionDomainGetMatchWithParameters(CFStringRef action
, CFPropertyListRef actionParameters
, CFStringRef hostName
, CFStringRef
*probeString
)
3279 CFArrayRef actionArray
= NULL
;
3280 CFIndex actionArraySize
= 0;
3282 CFStringRef matchDomain
= NULL
;
3284 /* For now, only support EvaluateConnection, which takes a CFArray */
3285 if (!CFEqual(action
, kSCValNetVPNOnDemandRuleActionEvaluateConnection
) || !isA_CFArray(actionParameters
)) {
3289 actionArray
= (CFArrayRef
)actionParameters
;
3290 actionArraySize
= CFArrayGetCount(actionArray
);
3292 /* Process domain rules, with actions of ConnectIfNeeded and NeverConnect */
3293 for (i
= 0; i
< actionArraySize
; i
++) {
3294 CFStringRef domainAction
= NULL
;
3295 CFDictionaryRef domainRule
= CFArrayGetValueAtIndex(actionArray
, i
);
3296 CFArrayRef domains
= NULL
;
3297 CFIndex domainsCount
= 0;
3298 CFIndex domainsIndex
;
3300 if (!isA_CFDictionary(domainRule
)) {
3304 domains
= CFDictionaryGetValue(domainRule
, kSCPropNetVPNOnDemandRuleActionParametersDomains
);
3305 if (!isA_CFArray(domains
)) {
3309 domainsCount
= CFArrayGetCount(domains
);
3310 for (domainsIndex
= 0; domainsIndex
< domainsCount
; domainsIndex
++) {
3312 domain
= CFArrayGetValueAtIndex(domains
, domainsIndex
);
3313 if (isA_CFString(domain
) && _SC_domainEndsWithDomain(hostName
, domain
)) {
3314 matchDomain
= domain
;
3320 domainAction
= CFDictionaryGetValue(domainRule
, kSCPropNetVPNOnDemandRuleActionParametersDomainAction
);
3321 if (isA_CFString(domainAction
) && CFEqual(domainAction
, kSCValNetVPNOnDemandRuleActionParametersDomainActionNeverConnect
)) {
3324 /* If we found a match, save the optional probe string as well */
3326 *probeString
= CFDictionaryGetValue(domainRule
, kSCPropNetVPNOnDemandRuleActionParametersRequiredURLStringProbe
);
3337 __SCNetworkConnectionDomainGetMatch(CFDictionaryRef trigger
, CFStringRef hostName
, Boolean onDemandRetry
)
3340 CFIndex domainsCount
;
3343 CFStringRef match_domain
= NULL
;
3345 /* Old configuration: always, never, on retry lists */
3346 key
= onDemandRetry
? kSCNetworkConnectionOnDemandMatchDomainsOnRetry
: kSCNetworkConnectionOnDemandMatchDomainsAlways
;
3348 domains
= CFDictionaryGetValue(trigger
, key
);
3349 domainsCount
= isA_CFArray(domains
) ? CFArrayGetCount(domains
) : 0;
3350 for (domainsIndex
= 0; domainsIndex
< domainsCount
; domainsIndex
++) {
3353 domain
= CFArrayGetValueAtIndex(domains
, domainsIndex
);
3354 if (isA_CFString(domain
) && _SC_domainEndsWithDomain(hostName
, domain
)) {
3355 match_domain
= domain
;
3360 return match_domain
;
3365 __SCNetworkConnectionShouldAlwaysConnect(CFDictionaryRef trigger
)
3367 CFStringRef action
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleAction
);
3368 return (isA_CFString(action
) && CFEqual(action
, kSCValNetVPNOnDemandRuleActionConnect
));
3373 __SCNetworkConnectionShouldIgnoreTrigger(CFDictionaryRef trigger
)
3375 CFStringRef action
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleAction
);
3377 if (isA_CFString(action
) &&
3378 (CFEqual(action
, kSCValNetVPNOnDemandRuleActionIgnore
) ||
3379 CFEqual(action
, kSCValNetVPNOnDemandRuleActionDisconnect
))) {
3387 static CFDictionaryRef
3388 __SCNetworkConnectionCopyMatchingTriggerWithName(CFDictionaryRef configuration
,
3389 CFStringRef hostName
,
3391 Boolean onDemandRetry
,
3392 CFDictionaryRef
*match_info
,
3393 Boolean
*triggerNow
,
3394 CFStringRef
*probe_string
)
3396 CFDictionaryRef result
= NULL
;
3397 int sc_status
= kSCStatusOK
;
3398 CFArrayRef triggers
;
3399 uint64_t triggersCount
= 0;
3401 Boolean usedOnDemandRetry
= FALSE
;
3403 if (triggerNow
!= NULL
) {
3404 *triggerNow
= FALSE
;
3407 if (match_info
!= NULL
) {
3411 triggers
= CFDictionaryGetValue(configuration
, kSCNetworkConnectionOnDemandTriggers
);
3412 triggersCount
= isA_CFArray(triggers
) ? CFArrayGetCount(triggers
) : 0;
3413 for (triggersIndex
= 0; triggersIndex
< triggersCount
; triggersIndex
++) {
3414 CFStringRef matched_domain
= NULL
;
3415 CFStringRef matched_probe_string
= NULL
;
3416 CFDictionaryRef trigger
;
3417 Boolean trigger_matched
= FALSE
;
3419 usedOnDemandRetry
= FALSE
;
3421 trigger
= CFArrayGetValueAtIndex(triggers
, triggersIndex
);
3422 if (!isA_CFDictionary(trigger
)) {
3423 // if not a valid "OnDemand" configuration
3427 if (__SCNetworkConnectionShouldAlwaysConnect(trigger
)) {
3428 /* If the trigger action is 'Connect', always match this trigger */
3429 /* First check the never match list */
3430 if (__SCNetworkConnectionShouldNeverMatch(trigger
, hostName
, client_pid
)) {
3433 trigger_matched
= TRUE
;
3434 } else if (__SCNetworkConnectionShouldIgnoreTrigger(trigger
)) {
3435 /* If the trigger action is 'Ignore' or 'Disconnect', skip this trigger */
3436 sc_status
= kSCStatusConnectionIgnore
;
3439 CFStringRef action
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleAction
);
3440 CFArrayRef actionParameters
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleActionParameters
);
3441 if (action
&& actionParameters
) {
3442 matched_domain
= __SCNetworkConnectionDomainGetMatchWithParameters(action
, actionParameters
, hostName
, &matched_probe_string
);
3443 usedOnDemandRetry
= TRUE
;
3445 if (onDemandRetry
) {
3446 matched_domain
= __SCNetworkConnectionDomainGetMatch(trigger
, hostName
, TRUE
);
3447 usedOnDemandRetry
= TRUE
;
3449 matched_domain
= __SCNetworkConnectionDomainGetMatch(trigger
, hostName
, FALSE
);
3450 if (matched_domain
== NULL
&& result
== NULL
) {
3451 /* Check the retry list if Always failed */
3452 matched_domain
= __SCNetworkConnectionDomainGetMatch(trigger
, hostName
, TRUE
);
3453 usedOnDemandRetry
= TRUE
;
3458 if (matched_domain
) {
3459 if (__SCNetworkConnectionShouldNeverMatch(trigger
, hostName
, client_pid
)) {
3460 matched_domain
= NULL
;
3463 trigger_matched
= TRUE
;
3468 if (trigger_matched
) {
3469 // if we have a matching domain and there were no exceptions
3470 // then we pass back the OnDemand info
3471 if (match_info
!= NULL
) {
3472 CFMutableDictionaryRef minfo
;
3473 SCNetworkConnectionType type
= kSCNetworkConnectionTypeIPLayerVPN
;
3474 CFNumberRef type_num
;
3476 if (*match_info
!= NULL
) {
3477 CFRelease(*match_info
);
3481 minfo
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
3483 &kCFTypeDictionaryKeyCallBacks
,
3484 &kCFTypeDictionaryValueCallBacks
);
3486 type_num
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &type
);
3487 CFDictionarySetValue(minfo
, kSCNetworkConnectionOnDemandMatchInfoVPNType
, type_num
);
3488 CFRelease(type_num
);
3489 if (matched_domain
) {
3490 CFDictionarySetValue(minfo
, kSCNetworkConnectionOnDemandMatchInfoDomain
, matched_domain
);
3492 CFDictionarySetValue(minfo
,
3493 kSCNetworkConnectionOnDemandMatchInfoOnRetry
,
3494 (usedOnDemandRetry
? kCFBooleanTrue
: kCFBooleanFalse
));
3496 *match_info
= minfo
;
3499 if (probe_string
!= NULL
) {
3500 if (*probe_string
!= NULL
) {
3501 CFRelease(*probe_string
);
3502 *probe_string
= NULL
;
3505 if (matched_probe_string
) {
3506 *probe_string
= CFRetain(matched_probe_string
);
3512 /* If retry was requested, or we found Always match, trigger now */
3513 if (onDemandRetry
|| !usedOnDemandRetry
) {
3514 if (triggerNow
!= NULL
) {
3520 /* If we matched the Retry list, but Always was requested,
3521 keep going through triggers in case one matches an Always */
3529 _SCErrorSet(sc_status
);
3534 static CFDictionaryRef
3535 __SCNetworkConnectionCopyTriggerWithService(CFDictionaryRef configuration
,
3536 CFStringRef service_id
)
3538 CFArrayRef triggers
;
3539 uint64_t triggersCount
= 0;
3542 triggers
= CFDictionaryGetValue(configuration
, kSCNetworkConnectionOnDemandTriggers
);
3543 triggersCount
= isA_CFArray(triggers
) ? CFArrayGetCount(triggers
) : 0;
3544 for (triggersIndex
= 0; triggersIndex
< triggersCount
; triggersIndex
++) {
3545 CFDictionaryRef trigger
;
3546 CFStringRef trigger_service_id
;
3548 trigger
= CFArrayGetValueAtIndex(triggers
, triggersIndex
);
3549 if (!isA_CFDictionary(trigger
)) {
3550 // if not a valid "OnDemand" configuration
3554 trigger_service_id
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandServiceID
);
3555 if (isA_CFString(trigger_service_id
) && CFEqual(trigger_service_id
, service_id
)) {
3565 __private_extern__ CFDictionaryRef
3566 __SCNetworkConnectionCopyTokenParameters(SCNetworkConnectionRef connection
)
3568 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
3569 CFDictionaryRef parameters
= NULL
;
3570 uint8_t params_buffer
[PPP_MACH_MAX_INLINE_DATA
];
3571 uint32_t params_buffer_len
= sizeof(params_buffer
);
3572 int sc_status
= kSCStatusOK
;
3573 mach_port_t session_port
;
3574 kern_return_t status
;
3576 pthread_mutex_lock(&connectionPrivate
->lock
);
3578 parameters
= connectionPrivate
->flow_divert_token_params
;
3579 if (parameters
!= NULL
) {
3580 CFRetain(parameters
);
3585 if (parameters
!= NULL
) {
3586 CFRelease(parameters
);
3590 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
3591 if (session_port
== MACH_PORT_NULL
) {
3595 status
= pppcontroller_flow_divert_copy_token_parameters(session_port
, params_buffer
, ¶ms_buffer_len
);
3596 if (status
== KERN_SUCCESS
) {
3597 if (params_buffer_len
> 0) {
3598 CFDataRef params_data
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
,
3602 parameters
= CFPropertyListCreateWithData(kCFAllocatorDefault
,
3604 kCFPropertyListImmutable
,
3607 CFRelease(params_data
);
3611 if (__SCNetworkConnectionNeedsRetry(connection
, "__SCNetworkConnectionCopyTokenParameters()", status
, &sc_status
)) {
3615 if (sc_status
!= kSCStatusOK
) {
3616 _SCErrorSet(sc_status
);
3620 if (parameters
!= NULL
&& connectionPrivate
->flow_divert_token_params
== NULL
) {
3621 connectionPrivate
->flow_divert_token_params
= (CFDictionaryRef
)CFRetain(parameters
);
3624 pthread_mutex_unlock(&connectionPrivate
->lock
);
3630 __SCNetworkConnectionCopyOnDemandInfoWithName(SCDynamicStoreRef
*storeP
,
3631 CFStringRef hostName
,
3632 Boolean onDemandRetry
,
3633 CFStringRef
*connectionServiceID
,
3634 SCNetworkConnectionStatus
*connectionStatus
,
3635 CFStringRef
*vpnRemoteAddress
) /* CFDictionaryRef *info */
3637 CFDictionaryRef configuration
;
3639 int sc_status
= kSCStatusOK
;
3640 CFDictionaryRef trigger
;
3641 Boolean trigger_now
= FALSE
;
3643 configuration
= __SCNetworkConnectionCopyOnDemandConfiguration();
3644 if (configuration
== NULL
) {
3645 _SCErrorSet(sc_status
);
3649 trigger
= __SCNetworkConnectionCopyMatchingTriggerWithName(configuration
, hostName
, 0, onDemandRetry
, NULL
, &trigger_now
, NULL
);
3650 if (trigger
!= NULL
&& trigger_now
) {
3652 SCNetworkConnectionStatus onDemandStatus
= kSCNetworkConnectionDisconnected
;
3656 if (!CFDictionaryGetValueIfPresent(trigger
, kSCNetworkConnectionOnDemandStatus
, (const void **)&num
) ||
3657 !isA_CFNumber(num
) ||
3658 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &onDemandStatus
)) {
3659 onDemandStatus
= kSCNetworkConnectionDisconnected
;
3661 if (connectionStatus
!= NULL
) {
3662 *connectionStatus
= onDemandStatus
;
3665 if (connectionServiceID
!= NULL
) {
3666 *connectionServiceID
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandServiceID
);
3667 *connectionServiceID
= isA_CFString(*connectionServiceID
);
3668 if ((*connectionServiceID
!= NULL
) && (CFStringGetLength(*connectionServiceID
) > 0)) {
3669 CFRetain(*connectionServiceID
);
3671 SC_log(LOG_INFO
, "OnDemand%s configuration error, no serviceID",
3672 onDemandRetry
? " (on retry)" : "");
3673 *connectionServiceID
= NULL
;
3678 if (vpnRemoteAddress
!= NULL
) {
3679 *vpnRemoteAddress
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandRemoteAddress
);
3680 *vpnRemoteAddress
= isA_CFString(*vpnRemoteAddress
);
3681 if ((*vpnRemoteAddress
!= NULL
) && (CFStringGetLength(*vpnRemoteAddress
) > 0)) {
3682 CFRetain(*vpnRemoteAddress
);
3684 SC_log(LOG_INFO
, "OnDemand%s configuration error, no server address",
3685 onDemandRetry
? " (on retry)" : "");
3686 *vpnRemoteAddress
= NULL
;
3692 if ((connectionServiceID
!= NULL
) && (*connectionServiceID
!= NULL
)) {
3693 CFRelease(*connectionServiceID
);
3694 *connectionServiceID
= NULL
;
3696 if ((vpnRemoteAddress
!= NULL
) && (*vpnRemoteAddress
!= NULL
)) {
3697 CFRelease(*vpnRemoteAddress
);
3698 *vpnRemoteAddress
= NULL
;
3700 sc_status
= kSCStatusFailed
;
3702 SC_log(LOG_INFO
, "OnDemand%s match, connection status = %d",
3703 onDemandRetry
? " (on retry)" : "",
3712 // SC_log(LOG_INFO, "OnDemand domain name(s) not matched");
3714 if (configuration
!= NULL
) CFRelease(configuration
);
3716 _SCErrorSet(sc_status
);
3722 __SCNetworkConnectionCopyUserPreferencesInternal(CFDictionaryRef selectionOptions
,
3723 CFStringRef
*serviceID
,
3724 CFDictionaryRef
*userOptions
)
3726 int prefsChanged
= 1;
3728 Boolean success
= FALSE
;
3730 if (notify_userprefs_token
== -1) {
3731 status
= notify_register_check(k_NetworkConnect_Notification
, ¬ify_userprefs_token
);
3732 if (status
!= NOTIFY_STATUS_OK
) {
3733 SC_log(LOG_NOTICE
, "notify_register_check() failed, status=%d", status
);
3734 (void)notify_cancel(notify_userprefs_token
);
3735 notify_userprefs_token
= -1;
3737 // clear the "something has changed" state
3738 (void) notify_check(notify_userprefs_token
, &prefsChanged
);
3742 if (notify_userprefs_token
!= -1) {
3743 status
= notify_check(notify_userprefs_token
, &prefsChanged
);
3744 if (status
!= NOTIFY_STATUS_OK
) {
3745 SC_log(LOG_NOTICE
, "notify_check() failed, status=%d", status
);
3746 (void)notify_cancel(notify_userprefs_token
);
3747 notify_userprefs_token
= -1;
3753 *userOptions
= NULL
;
3755 if (selectionOptions
!= NULL
) {
3756 Boolean catchAllFound
= FALSE
;
3757 CFIndex catchAllService
= 0;
3758 CFIndex catchAllConfig
= 0;
3759 CFStringRef hostName
= NULL
;
3760 CFStringRef priority
= NULL
;
3761 CFArrayRef serviceNames
= NULL
;
3762 CFDictionaryRef services
= NULL
;
3763 CFIndex serviceIndex
;
3764 CFIndex servicesCount
;
3766 hostName
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
);
3767 if (hostName
== NULL
) {
3768 hostName
= CFDictionaryGetValue(selectionOptions
, kSCPropNetPPPOnDemandHostName
);
3770 hostName
= isA_CFString(hostName
);
3771 if (hostName
== NULL
)
3772 goto done_selection
; // if no hostname for matching
3774 priority
= CFDictionaryGetValue(selectionOptions
, kSCPropNetPPPOnDemandPriority
);
3775 if (!isA_CFString(priority
))
3776 priority
= kSCValNetPPPOnDemandPriorityDefault
;
3779 if (!isA_CFArray(serviceNames
))
3780 goto done_selection
;
3783 if (!isA_CFDictionary(services
)) {
3784 goto done_selection
;
3787 servicesCount
= CFArrayGetCount(serviceNames
);
3788 for (serviceIndex
= 0; serviceIndex
< servicesCount
; serviceIndex
++) {
3789 CFIndex configIndex
;
3790 CFIndex configsCount
;
3791 CFArrayRef serviceConfigs
;
3792 CFStringRef serviceName
;
3795 serviceName
= CFArrayGetValueAtIndex(serviceNames
, serviceIndex
);
3796 if (!isA_CFString(serviceName
)) {
3800 serviceConfigs
= CFDictionaryGetValue(services
, serviceName
);
3801 if (!isA_CFArray(serviceConfigs
)) {
3805 configsCount
= CFArrayGetCount(serviceConfigs
);
3806 for (configIndex
= 0; configIndex
< configsCount
; configIndex
++) {
3807 CFNumberRef autodial
;
3808 CFDictionaryRef config
;
3809 CFDictionaryRef pppConfig
;
3811 config
= CFArrayGetValueAtIndex(serviceConfigs
, configIndex
);
3812 if (!isA_CFDictionary(config
)) {
3816 pppConfig
= CFDictionaryGetValue(config
, kSCEntNetPPP
);
3817 if (!isA_CFDictionary(pppConfig
)) {
3821 autodial
= CFDictionaryGetValue(pppConfig
, kSCPropNetPPPOnDemandEnabled
);
3822 if (!isA_CFNumber(autodial
)) {
3826 CFNumberGetValue(autodial
, kCFNumberIntType
, &val
);
3829 CFIndex domainsCount
;
3830 CFIndex domainsIndex
;
3832 /* we found an conditional connection enabled configuration */
3835 domains
= CFDictionaryGetValue(pppConfig
, kSCPropNetPPPOnDemandDomains
);
3836 if (!isA_CFArray(domains
)) {
3840 domainsCount
= CFArrayGetCount(domains
);
3841 for (domainsIndex
= 0; domainsIndex
< domainsCount
; domainsIndex
++) {
3844 domain
= CFArrayGetValueAtIndex(domains
, domainsIndex
);
3845 if (!isA_CFString(domain
)) {
3849 if (!catchAllFound
&&
3850 (CFStringCompare(domain
, CFSTR(""), 0) == kCFCompareEqualTo
3851 || CFStringCompare(domain
, CFSTR("."), 0) == kCFCompareEqualTo
))
3853 // found a catch all
3854 catchAllFound
= TRUE
;
3855 catchAllService
= serviceIndex
;
3856 catchAllConfig
= configIndex
;
3859 if (_SC_domainEndsWithDomain(hostName
, domain
)) {
3860 // found matching configuration
3861 *serviceID
= serviceName
;
3862 CFRetain(*serviceID
);
3863 *userOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, config
);
3864 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
3865 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCPropNetPPPOnDemandPriority
, priority
);
3866 addPasswordFromKeychain(*serviceID
, userOptions
);
3868 goto done_selection
;
3875 // config not found, do we have a catchall ?
3876 if (catchAllFound
) {
3877 CFDictionaryRef config
;
3878 CFArrayRef serviceConfigs
;
3879 CFStringRef serviceName
;
3881 serviceName
= CFArrayGetValueAtIndex(serviceNames
, catchAllService
);
3882 serviceConfigs
= CFDictionaryGetValue(services
, serviceName
);
3883 config
= CFArrayGetValueAtIndex(serviceConfigs
, catchAllConfig
);
3885 *serviceID
= serviceName
;
3886 CFRetain(*serviceID
);
3887 *userOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, config
);
3888 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
3889 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCPropNetPPPOnDemandPriority
, priority
);
3890 addPasswordFromKeychain(*serviceID
, userOptions
);
3892 goto done_selection
;
3898 CFRelease(serviceNames
);
3901 CFRelease(services
);
3905 SC_log(LOG_INFO
, "SCNetworkConnectionCopyUserPreferences %s", success
? "succeeded" : "failed");
3906 SC_log(LOG_INFO
, "Selection options: %@", selectionOptions
);
3912 /* we don't have selection options */
3914 // (1) Figure out which service ID we care about, allocate it into passed "serviceID"
3915 success
= SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(serviceID
);
3917 if (success
&& (*serviceID
!= NULL
)) {
3918 // (2) Get the list of user data for this service ID
3919 CFPropertyListRef userServices
= NULL
;
3922 // (3) We are expecting an array if the user has defined records for this service ID or NULL if the user hasn't
3923 if (userServices
!= NULL
) {
3924 if (isA_CFArray(userServices
)) {
3925 // (4) Get the default set of user options for this service
3926 success
= SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray((CFArrayRef
)userServices
,
3929 addPasswordFromKeychain(*serviceID
, userOptions
);
3932 SC_log(LOG_INFO
, "Error, userServices are not of type CFArray!");
3935 CFRelease(userServices
); // this is OK because SCNetworkConnectionPrivateISExpectedCFType() checks for NULL
3940 SC_log(LOG_INFO
, "SCNetworkConnectionCopyUserPreferences %@, no selection options",
3941 success
? CFSTR("succeeded") : CFSTR("failed"));
3949 SCNetworkConnectionCopyUserPreferences(CFDictionaryRef selectionOptions
,
3950 CFStringRef
*serviceID
,
3951 CFDictionaryRef
*userOptions
)
3953 Boolean success
= FALSE
;
3955 /* initialize runtime */
3956 pthread_once(&initialized
, __SCNetworkConnectionInitialize
);
3958 /* first check for new VPN OnDemand style */
3959 if (selectionOptions
!= NULL
) {
3960 CFStringRef hostName
;
3962 hostName
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
);
3963 if (isA_CFString(hostName
)) {
3964 CFStringRef connectionServiceID
= NULL
;
3965 SCNetworkConnectionStatus connectionStatus
= kSCNetworkConnectionInvalid
;
3966 Boolean onDemandRetry
;
3969 val
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandRetry
);
3970 onDemandRetry
= isA_CFBoolean(val
) ? CFBooleanGetValue(val
) : TRUE
;
3972 success
= __SCNetworkConnectionCopyOnDemandInfoWithName(NULL
,
3975 &connectionServiceID
,
3979 SC_log(LOG_INFO
, "__SCNetworkConnectionCopyOnDemandInfoWithName: return %d, status %d",
3985 // if the hostname matches an OnDemand domain
3986 if (connectionStatus
== kSCNetworkConnectionConnected
) {
3987 // if we are already connected
3988 if (connectionServiceID
!= NULL
) {
3989 CFRelease(connectionServiceID
);
3994 *serviceID
= connectionServiceID
;
3995 *userOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3996 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
3998 } else if (!onDemandRetry
) {
3999 // if the hostname does not match an OnDemand domain and we have
4000 // not yet issued an initial DNS query (i.e. it's not a query
4001 // being retried after the VPN has been established) then we're
4008 return __SCNetworkConnectionCopyUserPreferencesInternal(selectionOptions
, serviceID
, userOptions
);
4013 SCNetworkConnectionOnDemandShouldRetryOnFailure (SCNetworkConnectionRef connection
)
4015 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
4016 CFDictionaryRef match_info
= NULL
;
4018 if (!isA_SCNetworkConnection(connection
)) {
4019 _SCErrorSet(kSCStatusInvalidArgument
);
4023 if (connectionPrivate
->service
== NULL
) {
4024 _SCErrorSet(kSCStatusConnectionNoService
);
4028 if (isA_CFDictionary(connectionPrivate
->on_demand_user_options
)) {
4029 match_info
= CFDictionaryGetValue(connectionPrivate
->on_demand_user_options
, kSCNetworkConnectionSelectionOptionOnDemandMatchInfo
);
4030 if (isA_CFDictionary(match_info
)) {
4031 CFBooleanRef onRetry
= CFDictionaryGetValue(match_info
, kSCNetworkConnectionOnDemandMatchInfoOnRetry
);
4032 if (isA_CFBoolean(onRetry
)) {
4033 return CFBooleanGetValue(onRetry
);
4043 // Mask is optional in routes dictionary; if not present, whole addresses are matched
4045 __SCNetworkConnectionIPv4AddressMatchesRoutes (struct sockaddr_in
*addr_in
, CFDictionaryRef routes
)
4049 CFDataRef maskData
= NULL
;
4050 struct in_addr
*maskDataArray
;
4051 CFDataRef routeaddrData
= NULL
;
4052 struct in_addr
*routeaddrDataArray
;
4054 if (!isA_CFDictionary(routes
)) {
4058 routeaddrData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoAddresses
);
4059 maskData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoMasks
);
4061 /* routeaddrData and maskData are packed arrays of addresses; make sure they have the same length */
4062 if (!isA_CFData(routeaddrData
) || (maskData
&& (!isA_CFData(maskData
) || CFDataGetLength(routeaddrData
) != CFDataGetLength(maskData
)))) {
4066 routeaddrDataArray
= (struct in_addr
*)(void*)CFDataGetBytePtr(routeaddrData
);
4068 maskDataArray
= (struct in_addr
*)(void*)CFDataGetBytePtr(maskData
);
4071 count
= CFDataGetLength(routeaddrData
) / sizeof(struct in_addr
);
4072 for (i
=0; i
<count
; i
++) {
4073 struct in_addr routeAddr
= *routeaddrDataArray
;
4076 struct in_addr mask
= *maskDataArray
;
4078 if ((addr_in
->sin_addr
.s_addr
& mask
.s_addr
) == (routeAddr
.s_addr
& mask
.s_addr
)) {
4083 if (addr_in
->sin_addr
.s_addr
== routeAddr
.s_addr
) {
4087 routeaddrDataArray
++;
4094 __SCNetworkConnectionMaskIPv6Address(struct in6_addr
*addr
, struct in6_addr
*mask
)
4098 for (i
= 0; i
< sizeof(struct in6_addr
); i
++)
4099 addr
->s6_addr
[i
] &= mask
->s6_addr
[i
];
4103 // Mask is optional in routes dictionary; if not present, whole addresses are matched
4105 __SCNetworkConnectionIPv6AddressMatchesRoutes (struct sockaddr_in6
*addr_in6
, CFDictionaryRef routes
)
4109 CFDataRef maskData
= NULL
;
4110 struct in6_addr
*maskDataArray
;
4111 CFDataRef routeaddrData
= NULL
;
4112 struct in6_addr
*routeaddrDataArray
;
4114 if (!isA_CFDictionary(routes
)) {
4118 routeaddrData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoAddresses
);
4119 maskData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoMasks
);
4121 /* routeaddrData and maskData are packed arrays of addresses; make sure they have the same length */
4122 if (!isA_CFData(routeaddrData
) || (maskData
&& (!isA_CFData(maskData
) || CFDataGetLength(routeaddrData
) != CFDataGetLength(maskData
)))) {
4126 routeaddrDataArray
= (struct in6_addr
*)(void*)CFDataGetBytePtr(routeaddrData
);
4128 maskDataArray
= (struct in6_addr
*)(void*)CFDataGetBytePtr(maskData
);
4131 count
= CFDataGetLength(routeaddrData
) / sizeof(struct in6_addr
);
4132 for (i
=0; i
<count
; i
++) {
4134 struct in6_addr cmpAddr
;
4135 struct in6_addr
*mask
= maskDataArray
;
4136 struct in6_addr routeAddr
;
4138 memcpy(&routeAddr
, routeaddrDataArray
, sizeof(routeAddr
));
4139 memcpy(&cmpAddr
, &addr_in6
->sin6_addr
, sizeof(cmpAddr
));
4140 __SCNetworkConnectionMaskIPv6Address(&routeAddr
, mask
);
4141 __SCNetworkConnectionMaskIPv6Address(&cmpAddr
, mask
);
4143 if (!memcmp(&routeAddr
, &cmpAddr
, sizeof(routeAddr
))) {
4147 if (!memcmp(routeaddrDataArray
, &addr_in6
->sin6_addr
, sizeof(struct in6_addr
))) {
4152 routeaddrDataArray
++;
4159 __SCNetworkConnectionAddressMatchesRedirectedDNS(CFDictionaryRef trigger
, const struct sockaddr
*input_addr
)
4161 CFBooleanRef redirectedRef
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandDNSRedirectDetected
);
4163 if (isA_CFBoolean(redirectedRef
) && CFBooleanGetValue(redirectedRef
)) {
4164 /* DNS is redirected. Look for address list. */
4165 CFDictionaryRef redirectedAddressesRef
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandDNSRedirectedAddresses
);
4167 if (isA_CFDictionary(redirectedAddressesRef
)) {
4168 if (input_addr
->sa_family
== AF_INET
) {
4169 return __SCNetworkConnectionIPv4AddressMatchesRoutes((struct sockaddr_in
*)(void*)input_addr
, CFDictionaryGetValue(redirectedAddressesRef
, kSCNetworkConnectionNetworkInfoIPv4
));
4170 } else if (input_addr
->sa_family
== AF_INET6
) {
4171 return __SCNetworkConnectionIPv6AddressMatchesRoutes((struct sockaddr_in6
*)(void*)input_addr
, CFDictionaryGetValue(redirectedAddressesRef
, kSCNetworkConnectionNetworkInfoIPv6
));
4179 /* If the required probe has failed, we need to tunnel the address. Equivalent to redirected DNS. */
4181 __SCNetworkConnectionRequiredProbeFailed (CFDictionaryRef trigger
, CFStringRef probeString
)
4183 CFDictionaryRef probeResults
= NULL
;
4185 if (!isA_CFString(probeString
)) {
4189 probeResults
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandProbeResults
);
4190 if (!isA_CFDictionary(probeResults
)) {
4194 CFBooleanRef result
= CFDictionaryGetValue(probeResults
, probeString
);
4196 /* Only a value of kCFBooleanFalse marks the probe as failed */
4197 return (isA_CFBoolean(result
) && !CFBooleanGetValue(result
));
4201 SCNetworkConnectionCanTunnelAddress (SCNetworkConnectionRef connection
, const struct sockaddr
*address
, Boolean
*startImmediately
)
4203 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
4204 CFStringRef serviceID
= NULL
;
4205 CFDictionaryRef configuration
= NULL
;
4206 CFDictionaryRef trigger
= NULL
;
4207 CFDictionaryRef tunneledNetworks
= NULL
;
4208 sa_family_t address_family
= AF_UNSPEC
;
4209 Boolean success
= FALSE
;
4211 if (startImmediately
) {
4212 *startImmediately
= FALSE
;
4215 if (address
== NULL
) {
4219 address_family
= address
->sa_family
;
4220 if (address_family
!= AF_INET
&& address_family
!= AF_INET6
) {
4224 if (!isA_SCNetworkConnection(connection
)) {
4225 _SCErrorSet(kSCStatusInvalidArgument
);
4229 if (connectionPrivate
->service
== NULL
) {
4230 _SCErrorSet(kSCStatusConnectionNoService
);
4234 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
4235 if (!isA_CFString(serviceID
)) {
4239 configuration
= __SCNetworkConnectionCopyOnDemandConfiguration();
4240 if (configuration
== NULL
) {
4244 trigger
= __SCNetworkConnectionCopyTriggerWithService(configuration
, serviceID
);
4245 if (trigger
== NULL
) {
4249 if (__SCNetworkConnectionRequiredProbeFailed(trigger
, connectionPrivate
->on_demand_required_probe
)) {
4250 /* If probe failed, we can't trust DNS - connect now */
4251 if (startImmediately
) {
4252 *startImmediately
= TRUE
;
4258 if (__SCNetworkConnectionAddressMatchesRedirectedDNS(trigger
, address
)) {
4259 if (startImmediately
) {
4260 *startImmediately
= TRUE
;
4266 tunneledNetworks
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandTunneledNetworks
);
4267 if (!isA_CFDictionary(tunneledNetworks
)) {
4271 if (address_family
== AF_INET
) {
4272 CFDictionaryRef ip_dict
;
4273 Boolean matches
= FALSE
;
4274 struct sockaddr_in
*addr_in
= (struct sockaddr_in
*)(void*)address
;
4276 ip_dict
= CFDictionaryGetValue(tunneledNetworks
, kSCNetworkConnectionNetworkInfoIPv4
);
4277 if (!isA_CFDictionary(ip_dict
)) {
4281 matches
= __SCNetworkConnectionIPv4AddressMatchesRoutes(addr_in
, CFDictionaryGetValue(ip_dict
, kSCNetworkConnectionNetworkInfoIncludedRoutes
));
4284 if (!__SCNetworkConnectionIPv4AddressMatchesRoutes(addr_in
, CFDictionaryGetValue(ip_dict
, kSCNetworkConnectionNetworkInfoExcludedRoutes
))) {
4290 CFDictionaryRef ip6_dict
;
4291 Boolean matches
= FALSE
;
4292 struct sockaddr_in6
*addr_in6
= (struct sockaddr_in6
*)(void*)address
;
4294 ip6_dict
= CFDictionaryGetValue(tunneledNetworks
, kSCNetworkConnectionNetworkInfoIPv6
);
4295 if (!isA_CFDictionary(ip6_dict
)) {
4299 matches
= __SCNetworkConnectionIPv6AddressMatchesRoutes(addr_in6
, CFDictionaryGetValue(ip6_dict
, kSCNetworkConnectionNetworkInfoIncludedRoutes
));
4302 if (!__SCNetworkConnectionIPv6AddressMatchesRoutes(addr_in6
, CFDictionaryGetValue(ip6_dict
, kSCNetworkConnectionNetworkInfoExcludedRoutes
))) {
4309 if (configuration
) {
4310 CFRelease(configuration
);
4319 SCNetworkConnectionSelectServiceWithOptions(SCNetworkConnectionRef connection
, CFDictionaryRef selectionOptions
)
4321 CFStringRef account_identifier
= NULL
;
4322 CFDictionaryRef configuration
= NULL
;
4323 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
4324 CFDictionaryRef found_trigger
= NULL
;
4325 CFStringRef host_name
= NULL
;
4326 Boolean is_retry
= TRUE
;
4327 CFDictionaryRef match_info
= NULL
;
4328 CFMutableDictionaryRef new_user_options
= NULL
;
4329 SCNetworkConnectionStatus on_demand_status
= kSCNetworkConnectionInvalid
;
4330 CFStringRef requiredProbe
= NULL
;
4331 CFStringRef service_id
= NULL
;
4332 Boolean skip_prefs
= FALSE
;
4333 Boolean success
= TRUE
;
4334 CFDictionaryRef user_options
= NULL
;
4336 if (!isA_SCNetworkConnection(connection
)) {
4337 _SCErrorSet(kSCStatusInvalidArgument
);
4342 /* Can't call this on a connection that is already associated with a service */
4343 if (connectionPrivate
->service
!= NULL
) {
4344 _SCErrorSet(kSCStatusInvalidArgument
);
4349 if (isA_CFDictionary(selectionOptions
)) {
4350 CFBooleanRef no_user_prefs
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionNoUserPrefs
);
4351 CFBooleanRef retry
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandRetry
);
4353 account_identifier
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandAccountIdentifier
);
4354 host_name
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
);
4355 skip_prefs
= (isA_CFBoolean(no_user_prefs
) && CFBooleanGetValue(no_user_prefs
));
4357 if (isA_CFBoolean(retry
)) {
4358 is_retry
= CFBooleanGetValue(retry
);
4362 configuration
= __SCNetworkConnectionCopyOnDemandConfiguration();
4364 /* First, check for a match with the App Layer rules */
4365 service_id
= VPNAppLayerCopyMatchingService(connectionPrivate
->client_audit_token
,
4366 connectionPrivate
->client_pid
,
4367 connectionPrivate
->client_uuid
,
4368 connectionPrivate
->client_bundle_id
,
4372 if (service_id
!= NULL
) {
4373 Boolean use_app_layer
= TRUE
;
4375 if (isA_CFDictionary(configuration
)) {
4376 found_trigger
= __SCNetworkConnectionCopyTriggerWithService(configuration
, service_id
);
4377 if (found_trigger
!= NULL
) {
4378 CFNumberRef status_num
;
4380 if (!CFDictionaryGetValueIfPresent(found_trigger
,
4381 kSCNetworkConnectionOnDemandStatus
,
4382 (const void **) &status_num
) ||
4383 !isA_CFNumber(status_num
) ||
4384 !CFNumberGetValue(status_num
, kCFNumberSInt32Type
, &on_demand_status
)) {
4385 on_demand_status
= kSCNetworkConnectionInvalid
;
4389 * If the trigger should be ignored, still use App Layer VPN if it is already connected or
4390 * is in the process of connecting.
4392 if (__SCNetworkConnectionShouldIgnoreTrigger(found_trigger
) &&
4393 (on_demand_status
!= kSCNetworkConnectionConnecting
) &&
4394 (on_demand_status
!= kSCNetworkConnectionConnected
)) {
4395 use_app_layer
= FALSE
;
4400 if (use_app_layer
) {
4401 /* If this is not the 'OnRetry' call, and the service has not yet started, the match may need to return false */
4403 match_info
!= NULL
&&
4404 on_demand_status
!= kSCNetworkConnectionConnecting
&&
4405 on_demand_status
!= kSCNetworkConnectionConnected
) {
4406 CFBooleanRef matchedOnRetry
= CFDictionaryGetValue(match_info
, kSCNetworkConnectionOnDemandMatchInfoOnRetry
);
4407 if (matchedOnRetry
&& CFBooleanGetValue(matchedOnRetry
)) {
4408 /* Don't return that we matched always; wait for SCNetworkConnectionOnDemandShouldRetryOnFailure */
4412 connectionPrivate
->type
= kSCNetworkConnectionTypeAppLayerVPN
;
4415 CFRelease(service_id
);
4417 if (match_info
!= NULL
) {
4418 CFRelease(match_info
);
4421 if (found_trigger
!= NULL
) {
4422 CFRelease(found_trigger
);
4423 found_trigger
= NULL
;
4428 /* Next, check the IP layer rules */
4429 if (isA_CFDictionary(configuration
) && host_name
!= NULL
) {
4430 Boolean triggerNow
= FALSE
;
4432 found_trigger
= __SCNetworkConnectionCopyMatchingTriggerWithName(configuration
, host_name
, connectionPrivate
->client_pid
, is_retry
, &match_info
, &triggerNow
, &requiredProbe
);
4433 if (found_trigger
!= NULL
) {
4434 service_id
= CFDictionaryGetValue(found_trigger
, kSCNetworkConnectionOnDemandServiceID
);
4435 if (isA_CFString(service_id
)) {
4436 CFRetain(service_id
);
4437 connectionPrivate
->type
= kSCNetworkConnectionTypeIPLayerVPN
;
4445 } else if (!is_retry
) {
4449 if (match_info
!= NULL
) {
4450 CFRelease(match_info
);
4455 /* Next, check the user preferences */
4456 if (!skip_prefs
&& __SCNetworkConnectionCopyUserPreferencesInternal(selectionOptions
, &service_id
, &user_options
)) {
4457 CFMutableDictionaryRef minfo
;
4458 CFNumberRef type_num
;
4460 if (isA_CFDictionary(configuration
)) {
4461 found_trigger
= __SCNetworkConnectionCopyTriggerWithService(configuration
, service_id
);
4463 connectionPrivate
->type
= kSCNetworkConnectionTypePPP
;
4465 minfo
= CFDictionaryCreateMutable(NULL
,
4467 &kCFTypeDictionaryKeyCallBacks
,
4468 &kCFTypeDictionaryValueCallBacks
);
4469 type_num
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &connectionPrivate
->type
);
4470 CFDictionarySetValue(minfo
, kSCNetworkConnectionOnDemandMatchInfoVPNType
, type_num
);
4471 CFRelease(type_num
);
4477 if (service_id
== NULL
) {
4478 _SCErrorSet(kSCStatusOK
);
4483 connectionPrivate
->service
= _SCNetworkServiceCopyActive(NULL
, service_id
);
4484 if (connectionPrivate
->service
== NULL
) {
4485 _SCErrorSet(kSCStatusOK
);
4490 if (found_trigger
!= NULL
) {
4491 if (connectionPrivate
->on_demand_info
) {
4492 CFRelease(connectionPrivate
->on_demand_info
);
4494 connectionPrivate
->on_demand_info
= found_trigger
;
4495 CFRetain(connectionPrivate
->on_demand_info
);
4497 if (on_demand_status
== kSCNetworkConnectionInvalid
) {
4498 CFNumberRef status_num
;
4500 if (!CFDictionaryGetValueIfPresent(found_trigger
,
4501 kSCNetworkConnectionOnDemandStatus
,
4502 (const void **) &status_num
) ||
4503 !isA_CFNumber(status_num
) ||
4504 !CFNumberGetValue(status_num
, kCFNumberSInt32Type
, &on_demand_status
)) {
4505 on_demand_status
= kSCNetworkConnectionInvalid
;
4509 if (on_demand_status
!= kSCNetworkConnectionConnected
) {
4510 if (connectionPrivate
->type
== kSCNetworkConnectionTypeAppLayerVPN
) {
4511 /* Check App Layer OnDemand flag */
4512 CFBooleanRef app_on_demand_enabled
=
4513 CFDictionaryGetValue(found_trigger
, kSCNetworkConnectionOnDemandMatchAppEnabled
);
4514 if (isA_CFBoolean(app_on_demand_enabled
) && CFBooleanGetValue(app_on_demand_enabled
)) {
4515 connectionPrivate
->on_demand
= TRUE
;
4518 connectionPrivate
->on_demand
= TRUE
;
4521 } else if (connectionPrivate
->type
== kSCNetworkConnectionTypePPP
) {
4522 /* If we got the service from __SCNetworkConnectionCopyUserPreferencesInternal, then it's on demand */
4523 connectionPrivate
->on_demand
= TRUE
;
4526 if (user_options
== NULL
) {
4527 new_user_options
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
4529 &kCFTypeDictionaryKeyCallBacks
,
4530 &kCFTypeDictionaryValueCallBacks
);
4532 new_user_options
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, user_options
);
4535 if (host_name
!= NULL
) {
4536 CFDictionarySetValue(new_user_options
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, host_name
);
4539 if (connectionPrivate
->on_demand
&& match_info
!= NULL
) {
4540 CFDictionarySetValue(new_user_options
, kSCNetworkConnectionSelectionOptionOnDemandMatchInfo
, match_info
);
4543 connectionPrivate
->on_demand_user_options
= new_user_options
;
4544 CFRetain(connectionPrivate
->on_demand_user_options
);
4546 if (requiredProbe
) {
4547 connectionPrivate
->on_demand_required_probe
= requiredProbe
;
4548 CFRetain(connectionPrivate
->on_demand_required_probe
);
4552 if (service_id
!= NULL
) {
4553 CFRelease(service_id
);
4556 if (configuration
!= NULL
) {
4557 CFRelease(configuration
);
4560 if (found_trigger
!= NULL
) {
4561 CFRelease(found_trigger
);
4564 if (user_options
!= NULL
) {
4565 CFRelease(user_options
);
4568 if (new_user_options
!= NULL
) {
4569 CFRelease(new_user_options
);
4572 if (match_info
!= NULL
) {
4573 CFRelease(match_info
);
4576 if (requiredProbe
!= NULL
) {
4577 CFRelease(requiredProbe
);
4583 //*******************************************************************************************
4584 // SCNetworkConnectionPrivateCopyDefaultServiceIDForDial
4585 // ----------------------------------------------------
4586 // Try to find the service id to connect
4587 // (1) Start by looking at the last service used in Network Pref / Network menu extra
4588 // (2) If Network Pref / Network menu extra has not been used, find the PPP service
4589 // with the highest ordering
4590 //********************************************************************************************
4592 SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(CFStringRef
*serviceID
)
4594 Boolean foundService
= FALSE
;
4595 CFPropertyListRef lastServiceSelectedInIC
= NULL
;
4598 // we found the service the user last had open in IC
4599 if (lastServiceSelectedInIC
!= NULL
) {
4600 // make sure its a PPP service
4601 if (SCNetworkConnectionPrivateIsPPPService(lastServiceSelectedInIC
, kSCValNetInterfaceSubTypePPPSerial
, kSCValNetInterfaceSubTypePPPoE
)) {
4602 // make sure the service that we found is valid
4603 CFDictionaryRef dict
;
4606 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4607 kSCDynamicStoreDomainSetup
,
4608 lastServiceSelectedInIC
,
4609 kSCEntNetInterface
);
4610 dict
= SCDynamicStoreCopyValue(NULL
, key
);
4614 *serviceID
= CFRetain(lastServiceSelectedInIC
);
4615 foundService
= TRUE
;
4618 CFRelease(lastServiceSelectedInIC
);
4621 if (!foundService
) {
4622 foundService
= SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(serviceID
);
4625 return foundService
;
4628 //********************************************************************************
4629 // SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore
4630 // -------------------------------------------------------
4631 // Find the highest ordered PPP service in the dynamic store
4632 //********************************************************************************
4634 SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(CFStringRef
*serviceID
)
4636 CFDictionaryRef dict
= NULL
;
4637 CFStringRef key
= NULL
;
4638 CFArrayRef serviceIDs
= NULL
;
4639 Boolean success
= FALSE
;
4647 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, kSCDynamicStoreDomainSetup
, kSCEntNetIPv4
);
4649 fprintf(stderr
, "Error, Setup Key == NULL!\n");
4653 dict
= SCDynamicStoreCopyValue(NULL
, key
);
4654 if (!isA_CFDictionary(dict
)) {
4655 fprintf(stderr
, "no global IPv4 entity\n");
4659 serviceIDs
= CFDictionaryGetValue(dict
, kSCPropNetServiceOrder
); // array of service id's
4660 if (!isA_CFArray(serviceIDs
)) {
4661 fprintf(stderr
, "service order not specified\n");
4665 count
= CFArrayGetCount(serviceIDs
);
4666 for (i
= 0; i
< count
; i
++) {
4667 CFStringRef service
= CFArrayGetValueAtIndex(serviceIDs
, i
);
4669 if (SCNetworkConnectionPrivateIsPPPService(service
, kSCValNetInterfaceSubTypePPPSerial
, kSCValNetInterfaceSubTypePPPoE
)) {
4670 *serviceID
= CFRetain(service
);
4677 if (key
!= NULL
) CFRelease(key
);
4678 if (dict
!= NULL
) CFRelease(dict
);
4683 //********************************************************************************
4684 // SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray
4685 // ---------------------------------------------------------
4686 // Copy over user preferences for a particular service if they exist
4687 //********************************************************************************
4689 SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray
, CFDictionaryRef
*userOptions
)
4691 CFIndex count
= CFArrayGetCount(userOptionsArray
);
4694 for (i
= 0; i
< count
; i
++) {
4695 // (1) Find the dictionary
4696 CFPropertyListRef propertyList
= CFArrayGetValueAtIndex(userOptionsArray
, i
);
4698 if (isA_CFDictionary(propertyList
) != NULL
) {
4699 // See if there's a value for dial on demand
4700 CFPropertyListRef value
;
4702 value
= CFDictionaryGetValue((CFDictionaryRef
)propertyList
, k_Dial_Default_Key
);
4703 if (isA_CFBoolean(value
) != NULL
) {
4704 if (CFBooleanGetValue(value
)) {
4705 // we found the default user options
4706 *userOptions
= CFDictionaryCreateCopy(NULL
,
4707 (CFDictionaryRef
)propertyList
);
4717 //********************************************************************************
4718 // SCNetworkConnectionPrivateIsServiceType
4719 // --------------------------------------
4720 // Check and see if the service is a PPP service of the given types
4721 //********************************************************************************
4723 SCNetworkConnectionPrivateIsPPPService(CFStringRef serviceID
, CFStringRef subType1
, CFStringRef subType2
)
4725 CFStringRef entityKey
;
4726 Boolean isPPPService
= FALSE
;
4727 Boolean isMatchingSubType
= FALSE
;
4728 CFDictionaryRef serviceDict
;
4730 entityKey
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4731 kSCDynamicStoreDomainSetup
,
4733 kSCEntNetInterface
);
4734 if (entityKey
== NULL
) {
4738 serviceDict
= SCDynamicStoreCopyValue(NULL
, entityKey
);
4739 if (serviceDict
!= NULL
) {
4740 if (isA_CFDictionary(serviceDict
)) {
4742 CFStringRef subtype
;
4744 type
= CFDictionaryGetValue(serviceDict
, kSCPropNetInterfaceType
);
4745 if (isA_CFString(type
)) {
4746 isPPPService
= CFEqual(type
, kSCValNetInterfaceTypePPP
);
4749 subtype
= CFDictionaryGetValue(serviceDict
, kSCPropNetInterfaceSubType
);
4750 if (isA_CFString(subtype
)) {
4751 isMatchingSubType
= CFEqual(subtype
, subType1
);
4752 if (!isMatchingSubType
&& subType2
)
4753 isMatchingSubType
= CFEqual(subtype
, subType2
);
4756 CFRelease(serviceDict
);
4758 CFRelease(entityKey
);
4760 return (isPPPService
&& isMatchingSubType
);
4763 //********************************************************************************
4764 // addPasswordFromKeychain
4765 // --------------------------------------
4766 // Get the password and shared secret out of the keychain and add
4767 // them to the PPP and IPSec dictionaries
4768 //********************************************************************************
4770 addPasswordFromKeychain(CFStringRef serviceID
, CFDictionaryRef
*userOptions
)
4772 CFPropertyListRef uniqueID
;
4773 CFStringRef password
;
4774 CFStringRef sharedsecret
= NULL
;
4776 /* user options must exist */
4777 if (*userOptions
== NULL
)
4780 /* first, get the unique identifier used to store passwords in the keychain */
4781 uniqueID
= CFDictionaryGetValue(*userOptions
, k_Unique_Id_Key
);
4782 if (!isA_CFString(uniqueID
))
4785 /* first, get the PPP password */
4786 password
= copyPasswordFromKeychain(uniqueID
);
4788 /* then, if necessary, get the IPSec Shared Secret */
4789 if (SCNetworkConnectionPrivateIsPPPService(serviceID
, kSCValNetInterfaceSubTypeL2TP
, 0)) {
4790 CFMutableStringRef uniqueIDSS
;
4792 uniqueIDSS
= CFStringCreateMutableCopy(NULL
, 0, uniqueID
);
4793 CFStringAppend(uniqueIDSS
, CFSTR(".SS"));
4794 sharedsecret
= copyPasswordFromKeychain(uniqueIDSS
);
4795 CFRelease(uniqueIDSS
);
4798 /* did we find our information in the key chain ? */
4799 if ((password
!= NULL
) || (sharedsecret
!= NULL
)) {
4800 CFMutableDictionaryRef newOptions
;
4802 newOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, *userOptions
);
4805 if (password
!= NULL
) {
4806 CFDictionaryRef entity
;
4807 CFMutableDictionaryRef newEntity
;
4809 entity
= CFDictionaryGetValue(*userOptions
, kSCEntNetPPP
);
4810 if (isA_CFDictionary(entity
))
4811 newEntity
= CFDictionaryCreateMutableCopy(NULL
, 0, entity
);
4813 newEntity
= CFDictionaryCreateMutable(NULL
,
4815 &kCFTypeDictionaryKeyCallBacks
,
4816 &kCFTypeDictionaryValueCallBacks
);
4819 /* set the PPP password */
4820 CFDictionarySetValue(newEntity
, kSCPropNetPPPAuthPassword
, uniqueID
);
4821 CFDictionarySetValue(newEntity
, kSCPropNetPPPAuthPasswordEncryption
, kSCValNetPPPAuthPasswordEncryptionKeychain
);
4822 CFRelease(password
);
4824 /* update the PPP entity */
4825 CFDictionarySetValue(newOptions
, kSCEntNetPPP
, newEntity
);
4826 CFRelease(newEntity
);
4829 /* IPSec Shared Secret */
4830 if (sharedsecret
!= NULL
) {
4831 CFDictionaryRef entity
;
4832 CFMutableDictionaryRef newEntity
;
4834 entity
= CFDictionaryGetValue(*userOptions
, kSCEntNetIPSec
);
4835 if (isA_CFDictionary(entity
))
4836 newEntity
= CFDictionaryCreateMutableCopy(NULL
, 0, entity
);
4838 newEntity
= CFDictionaryCreateMutable(NULL
,
4840 &kCFTypeDictionaryKeyCallBacks
,
4841 &kCFTypeDictionaryValueCallBacks
);
4843 /* set the IPSec Shared Secret */
4844 CFDictionarySetValue(newEntity
, kSCPropNetIPSecSharedSecret
, sharedsecret
);
4845 CFRelease(sharedsecret
);
4847 /* update the IPSec entity */
4848 CFDictionarySetValue(newOptions
, kSCEntNetIPSec
, newEntity
);
4849 CFRelease(newEntity
);
4852 /* update the userOptions dictionary */
4853 CFRelease(*userOptions
);
4854 *userOptions
= CFDictionaryCreateCopy(NULL
, newOptions
);
4855 CFRelease(newOptions
);
4860 #if !TARGET_OS_IPHONE
4861 //********************************************************************************
4862 // copyKeychainEnumerator
4863 // --------------------------------------
4864 // Gather Keychain Enumerator
4865 //********************************************************************************
4867 copyKeychainEnumerator(CFStringRef uniqueIdentifier
)
4869 CFArrayRef itemArray
= NULL
;
4870 CFMutableDictionaryRef query
;
4873 query
= CFDictionaryCreateMutable(NULL
,
4875 &kCFTypeDictionaryKeyCallBacks
,
4876 &kCFTypeDictionaryValueCallBacks
);
4877 CFDictionarySetValue(query
, kSecClass
, kSecClassGenericPassword
);
4878 CFDictionarySetValue(query
, kSecAttrService
, uniqueIdentifier
);
4879 CFDictionarySetValue(query
, kSecReturnRef
, kCFBooleanTrue
);
4880 CFDictionarySetValue(query
, kSecMatchLimit
, kSecMatchLimitAll
);
4881 result
= SecItemCopyMatching(query
, (CFTypeRef
*)&itemArray
);
4883 if ((result
!= noErr
) && (itemArray
!= NULL
)) {
4884 CFRelease(itemArray
);
4890 #endif // !TARGET_OS_IPHONE
4892 //********************************************************************************
4893 // copyPasswordFromKeychain
4894 // --------------------------------------
4895 // Given a uniqueID, retrieve the password from the keychain
4896 //********************************************************************************
4898 copyPasswordFromKeychain(CFStringRef uniqueID
)
4900 #if !TARGET_OS_IPHONE
4901 CFArrayRef enumerator
;
4903 CFStringRef password
= NULL
;
4905 enumerator
= copyKeychainEnumerator(uniqueID
);
4906 if (enumerator
== NULL
) {
4907 return NULL
; // if no keychain enumerator
4910 n
= CFArrayGetCount(enumerator
);
4914 SecKeychainItemRef itemRef
;
4917 itemRef
= (SecKeychainItemRef
)CFArrayGetValueAtIndex(enumerator
, 0);
4918 result
= SecKeychainItemCopyContent(itemRef
, // itemRef
4922 (void *)&data
); // outData
4923 if ((result
== noErr
) && (data
!= NULL
) && (dataLen
> 0)) {
4924 password
= CFStringCreateWithBytes(NULL
, data
, dataLen
, kCFStringEncodingUTF8
, TRUE
);
4925 (void) SecKeychainItemFreeContent(NULL
, data
);
4930 CFRelease(enumerator
);
4933 #else // !TARGET_OS_IPHONE
4935 #endif // !TARGET_OS_IPHONE
4941 __SCNetworkConnectionGetControllerPortName(void)
4943 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 70000)) && !TARGET_OS_SIMULATOR
4944 if (scnc_server_name
== NULL
){
4945 if (!(sandbox_check(getpid(), "mach-lookup", SANDBOX_FILTER_GLOBAL_NAME
| SANDBOX_CHECK_NO_REPORT
, PPPCONTROLLER_SERVER_PRIV
))){
4946 scnc_server_name
= PPPCONTROLLER_SERVER_PRIV
;
4948 scnc_server_name
= PPPCONTROLLER_SERVER
;
4952 scnc_server_name
= PPPCONTROLLER_SERVER
;
4954 return scnc_server_name
;