2 * Copyright (c) 2003-2019 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 <TargetConditionals.h>
39 #include <sys/cdefs.h>
40 #include <dispatch/dispatch.h>
41 #include <CoreFoundation/CFXPCBridge.h>
43 #include "SCNetworkConnectionInternal.h"
44 #include "SCNetworkConfigurationInternal.h"
47 #include <SystemConfiguration/VPNAppLayerPrivate.h>
48 #include <SystemConfiguration/VPNTunnel.h>
51 #include <Security/Security.h>
52 #include "dy_framework.h"
53 #endif // !TARGET_OS_IPHONE
55 #include <bootstrap.h>
59 #include <netinet/in.h>
60 #include <arpa/inet.h>
63 #include <sys/ioctl.h>
64 #include <sys/socket.h>
66 #include <mach/mach.h>
67 #include <bsm/audit.h>
68 #include <bsm/libbsm.h>
70 #include <sys/proc_info.h>
73 #include <ppp/ppp_msg.h>
74 #include "pppcontroller.h"
75 #include <ppp/pppcontroller_types.h>
77 #ifndef PPPCONTROLLER_SERVER_PRIV
78 #define PPPCONTROLLER_SERVER_PRIV PPPCONTROLLER_SERVER
79 #endif // !PPPCONTROLLER_SERVER_PRIV
82 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
83 static pthread_mutex_t scnc_lock
= PTHREAD_MUTEX_INITIALIZER
;
84 static mach_port_t scnc_server
= MACH_PORT_NULL
;
85 static char *scnc_server_name
= NULL
;
90 /* base CFType information */
97 SCNetworkServiceRef service
;
99 /* client info (if we are proxying for another process */
100 mach_port_t client_audit_session
;
101 audit_token_t client_audit_token
;
102 mach_port_t client_bootstrap_port
;
107 CFStringRef client_bundle_id
;
109 /* ref to PPP controller for control messages */
110 mach_port_t session_port
;
112 /* ref to PPP controller for notification messages */
113 CFMachPortRef notify_port
;
115 /* keep track of whether we're acquired the initial status */
118 /* run loop source, callout, context, rl scheduling info */
120 CFRunLoopSourceRef rls
;
121 SCNetworkConnectionCallBack rlsFunction
;
122 SCNetworkConnectionContext rlsContext
;
123 CFMutableArrayRef rlList
;
125 /* SCNetworkConnectionSetDispatchQueue */
126 dispatch_group_t dispatchGroup
;
127 dispatch_queue_t dispatchQueue
;
128 dispatch_source_t dispatchSource
;
130 SCNetworkConnectionType type
;
132 CFDictionaryRef on_demand_info
;
133 CFDictionaryRef on_demand_user_options
;
134 CFStringRef on_demand_required_probe
;
136 /* Flow Divert support info */
137 CFDictionaryRef flow_divert_token_params
;
139 #if !TARGET_OS_SIMULATOR
140 /* NetworkExtension data structures */
141 ne_session_t ne_session
;
142 #endif /* !TARGET_OS_SIMULATOR */
143 } SCNetworkConnectionPrivate
, *SCNetworkConnectionPrivateRef
;
146 __private_extern__ os_log_t
147 __log_SCNetworkConnection(void)
149 static os_log_t log
= NULL
;
152 log
= os_log_create("com.apple.SystemConfiguration", "SCNetworkConnection");
159 static __inline__ CFTypeRef
160 isA_SCNetworkConnection(CFTypeRef obj
)
162 return (isA_CFType(obj
, SCNetworkConnectionGetTypeID()));
166 #if !TARGET_OS_SIMULATOR
168 __SCNetworkConnectionUseNetworkExtension(SCNetworkConnectionPrivateRef connectionPrivate
)
170 Boolean result
= FALSE
;
172 if (ne_session_use_as_system_vpn() && connectionPrivate
->service
!= NULL
) {
173 _SCErrorSet(kSCStatusOK
);
174 result
= _SCNetworkServiceIsVPN(connectionPrivate
->service
);
176 * SCNetworkServiceGetInterface (called by _SCNetworkServiceIsVPN) will set the SC error to kSCStatusInvalidArgument if the service does not have an associated prefs object.
177 * In that case, we try to get the service type/subtype from the dynamic store.
179 if (!result
&& SCError() == kSCStatusInvalidArgument
) {
180 CFStringRef interfaceKey
;
181 CFDictionaryRef interfaceDict
;
182 CFStringRef serviceID
;
184 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
185 interfaceKey
= SCDynamicStoreKeyCreateNetworkServiceEntity(kCFAllocatorDefault
,
186 kSCDynamicStoreDomainSetup
,
189 interfaceDict
= SCDynamicStoreCopyValue(NULL
, interfaceKey
);
190 if (isA_CFDictionary(interfaceDict
)) {
191 CFStringRef interfaceType
= CFDictionaryGetValue(interfaceDict
, kSCPropNetInterfaceType
);
192 if (isA_CFString(interfaceType
)) {
193 if (CFEqual(interfaceType
, kSCNetworkInterfaceTypePPP
)) {
194 CFStringRef interfaceSubType
= CFDictionaryGetValue(interfaceDict
, kSCPropNetInterfaceSubType
);
195 #pragma GCC diagnostic push
196 #pragma GCC diagnostic ignored "-Wdeprecated"
197 result
= (isA_CFString(interfaceSubType
) &&
198 (CFEqual(interfaceSubType
, kSCValNetInterfaceSubTypePPTP
) ||
199 CFEqual(interfaceSubType
, kSCValNetInterfaceSubTypeL2TP
)));
200 #pragma GCC diagnostic pop
202 result
= (CFEqual(interfaceType
, kSCNetworkInterfaceTypeVPN
) || CFEqual(interfaceType
, kSCNetworkInterfaceTypeIPSec
));
206 if (interfaceDict
!= NULL
) {
207 CFRelease(interfaceDict
);
209 CFRelease(interfaceKey
);
215 #endif /* !TARGET_OS_SIMULATOR */
219 __SCNetworkConnectionUsingNetworkExtension(SCNetworkConnectionPrivateRef connectionPrivate
)
221 #if !TARGET_OS_SIMULATOR
222 return (connectionPrivate
->ne_session
!= NULL
);
224 #pragma unused(connectionPrivate)
226 #endif /* !TARGET_OS_SIMULATOR */
231 __SCNetworkConnectionCopyDescription(CFTypeRef cf
)
233 CFAllocatorRef allocator
= CFGetAllocator(cf
);
234 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)cf
;
235 CFMutableStringRef result
;
237 result
= CFStringCreateMutable(allocator
, 0);
238 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkConnection, %p [%p]> {"), cf
, allocator
);
239 CFStringAppendFormat(result
, NULL
, CFSTR("service = %p"), connectionPrivate
->service
);
240 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
241 CFStringAppendFormat(result
, NULL
, CFSTR(", server port = 0x%x"), connectionPrivate
->session_port
);
243 CFStringAppendFormat(result
, NULL
, CFSTR("using NetworkExtension = %s"), (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
) ? "yes" : "no"));
244 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
251 __SCNetworkConnectionDeallocate(CFTypeRef cf
)
253 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)cf
;
255 /* release resources */
256 pthread_mutex_destroy(&connectionPrivate
->lock
);
258 if (connectionPrivate
->client_audit_session
!= MACH_PORT_NULL
) {
259 mach_port_deallocate(mach_task_self(),
260 connectionPrivate
->client_audit_session
);
263 if (connectionPrivate
->client_bootstrap_port
!= MACH_PORT_NULL
) {
264 mach_port_deallocate(mach_task_self(),
265 connectionPrivate
->client_bootstrap_port
);
268 if (connectionPrivate
->client_bundle_id
!= NULL
) {
269 CFRelease(connectionPrivate
->client_bundle_id
);
272 if (connectionPrivate
->rls
!= NULL
) {
273 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
274 CFRelease(connectionPrivate
->rls
);
277 if (connectionPrivate
->rlList
!= NULL
) {
278 CFRelease(connectionPrivate
->rlList
);
281 if (connectionPrivate
->notify_port
!= NULL
) {
282 mach_port_t mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
284 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionDeallocate notify_port", mp
);
285 CFMachPortInvalidate(connectionPrivate
->notify_port
);
286 CFRelease(connectionPrivate
->notify_port
);
287 mach_port_mod_refs(mach_task_self(), mp
, MACH_PORT_RIGHT_RECEIVE
, -1);
290 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
291 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionDeallocate session_port", connectionPrivate
->session_port
);
292 (void) mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
295 if (connectionPrivate
->rlsContext
.release
!= NULL
)
296 (*connectionPrivate
->rlsContext
.release
)(connectionPrivate
->rlsContext
.info
);
298 if (connectionPrivate
->service
!= NULL
) {
299 CFRelease(connectionPrivate
->service
);
302 if (connectionPrivate
->on_demand_info
!= NULL
) {
303 CFRelease(connectionPrivate
->on_demand_info
);
306 if (connectionPrivate
->on_demand_user_options
!= NULL
) {
307 CFRelease(connectionPrivate
->on_demand_user_options
);
310 if (connectionPrivate
->on_demand_required_probe
!= NULL
) {
311 CFRelease(connectionPrivate
->on_demand_required_probe
);
314 if (connectionPrivate
->flow_divert_token_params
!= NULL
) {
315 CFRelease(connectionPrivate
->flow_divert_token_params
);
318 #if !TARGET_OS_SIMULATOR
319 if (connectionPrivate
->ne_session
!= NULL
) {
320 ne_session_set_event_handler(connectionPrivate
->ne_session
, NULL
, NULL
);
321 ne_session_release(connectionPrivate
->ne_session
);
323 #endif /* !TARGET_OS_SIMULATOR */
329 static CFTypeID __kSCNetworkConnectionTypeID
= _kCFRuntimeNotATypeID
;
331 static const CFRuntimeClass __SCNetworkConnectionClass
= {
333 "SCNetworkConnection", // className
336 __SCNetworkConnectionDeallocate
, // dealloc
339 NULL
, // copyFormattingDesc
340 __SCNetworkConnectionCopyDescription
// copyDebugDesc
347 /* the process has forked (and we are the child process) */
349 scnc_server
= MACH_PORT_NULL
;
350 scnc_server_name
= NULL
;
356 __SCNetworkConnectionInitialize(void)
360 /* get the debug environment variable */
361 env
= getenv("PPPDebug");
363 if (sscanf(env
, "%d", &debug
) != 1) {
364 /* PPPDebug value is not valid (or non-numeric), set debug to 1 */
369 /* register with CoreFoundation */
370 __kSCNetworkConnectionTypeID
= _CFRuntimeRegisterClass(&__SCNetworkConnectionClass
);
372 /* add handler to cleanup after fork() */
373 (void) pthread_atfork(NULL
, NULL
, childForkHandler
);
380 __SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection
);
382 #define SC_NETWORK_CONNECTION_QUEUE "SCNetworkConnectionQueue"
384 static dispatch_queue_t
385 __SCNetworkConnectionQueue()
387 static dispatch_once_t once
;
388 static dispatch_queue_t q
;
390 dispatch_once(&once
, ^{
391 q
= dispatch_queue_create(SC_NETWORK_CONNECTION_QUEUE
, NULL
);
399 __SCNetworkConnectionNotify(SCNetworkConnectionRef connection
,
400 SCNetworkConnectionCallBack rlsFunction
,
401 SCNetworkConnectionStatus nc_status
,
402 void (*context_release
)(const void *),
405 SC_log(LOG_DEBUG
, "exec SCNetworkConnection callout");
406 (*rlsFunction
)(connection
, nc_status
, context_info
);
407 if ((context_release
!= NULL
) && (context_info
!= NULL
)) {
408 (*context_release
)(context_info
);
416 __SCNetworkConnectionCallBackRunLoopPerform(SCNetworkConnectionRef connection
,
419 SCNetworkConnectionCallBack rlsFunction
,
420 void (*context_release
)(const void *),
423 SCNetworkConnectionStatus nc_status
;
425 nc_status
= SCNetworkConnectionGetStatus(connection
);
426 CFRunLoopPerformBlock(rl
, rl_mode
, ^{
427 __SCNetworkConnectionNotify(connection
, rlsFunction
, nc_status
, context_release
, context_info
);
428 CFRelease(connection
);
435 __SCNetworkConnectionCallBackDispatchPerform(SCNetworkConnectionRef connection
,
437 SCNetworkConnectionCallBack rlsFunction
,
438 void (*context_release
)(const void *),
441 SCNetworkConnectionStatus nc_status
;
443 nc_status
= SCNetworkConnectionGetStatus(connection
);
445 __SCNetworkConnectionNotify(connection
, rlsFunction
, nc_status
, context_release
, context_info
);
446 CFRelease(connection
);
453 __SCNetworkConnectionCallBack(void *connection
)
455 boolean_t exec_async
= FALSE
;
456 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
458 void (*context_release
)(const void *);
459 CFRunLoopRef rl
= NULL
;
461 SCNetworkConnectionCallBack rlsFunction
= NULL
;
462 dispatch_queue_t q
= NULL
;
463 SCNetworkConnectionStatus nc_status
= kSCNetworkConnectionInvalid
;
465 pthread_mutex_lock(&connectionPrivate
->lock
);
467 if (!connectionPrivate
->scheduled
) {
468 // if not currently scheduled
469 pthread_mutex_unlock(&connectionPrivate
->lock
);
473 rlsFunction
= connectionPrivate
->rlsFunction
;
474 if (rlsFunction
== NULL
) {
475 pthread_mutex_unlock(&connectionPrivate
->lock
);
479 if ((connectionPrivate
->rlsContext
.retain
!= NULL
) && (connectionPrivate
->rlsContext
.info
!= NULL
)) {
480 context_info
= (void *)(*connectionPrivate
->rlsContext
.retain
)(connectionPrivate
->rlsContext
.info
);
481 context_release
= connectionPrivate
->rlsContext
.release
;
483 context_info
= connectionPrivate
->rlsContext
.info
;
484 context_release
= NULL
;
487 #if !TARGET_OS_SIMULATOR
488 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
489 pthread_mutex_unlock(&connectionPrivate
->lock
);
491 nc_status
= SCNetworkConnectionGetStatus(connection
);
492 __SCNetworkConnectionNotify(connection
, rlsFunction
, nc_status
, context_release
, context_info
);
493 CFRelease(connection
); /* This releases the reference that we took in the NESessionEventStatusChanged event handler */
496 #endif /* !TARGET_OS_SIMULATOR */
498 // Do we need to spin a new thread? (either we are running on the main
499 // dispatch queue or main runloop)
500 if (connectionPrivate
->rlList
== NULL
) {
501 // if we are performing the callback on a dispatch queue
502 q
= connectionPrivate
->dispatchQueue
;
503 if (q
== dispatch_get_main_queue()) {
507 rl
= CFRunLoopGetCurrent();
508 if (rl
== CFRunLoopGetMain()) {
513 CFRetain(connection
);
514 pthread_mutex_unlock(&connectionPrivate
->lock
);
517 nc_status
= SCNetworkConnectionGetStatus(connection
);
518 __SCNetworkConnectionNotify(connection
, rlsFunction
, nc_status
, context_release
, context_info
);
519 CFRelease(connection
);
523 if (connectionPrivate
->rlList
== NULL
) {
526 dispatch_async(__SCNetworkConnectionQueue(), ^{
527 __SCNetworkConnectionCallBackDispatchPerform(connection
,
537 rl_mode
= CFRunLoopCopyCurrentMode(rl
);
538 dispatch_async(__SCNetworkConnectionQueue(), ^{
539 __SCNetworkConnectionCallBackRunLoopPerform(connection
,
555 __SCNetworkConnectionMachCallBack(CFMachPortRef port
, void * msg
, CFIndex size
, void * info
)
559 mach_no_senders_notification_t
*buf
= msg
;
560 mach_msg_id_t msgid
= buf
->not_header
.msgh_id
;
561 SCNetworkConnectionRef connection
= (SCNetworkConnectionRef
)info
;
563 if (msgid
== MACH_NOTIFY_NO_SENDERS
) {
564 // re-establish notification
565 SC_log(LOG_INFO
, "PPPController server died");
566 (void)__SCNetworkConnectionReconnectNotifications(connection
);
569 __SCNetworkConnectionCallBack(info
);
576 #pragma mark SCNetworkConnection APIs
580 pppMPCopyDescription(const void *info
)
582 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)info
;
584 return CFStringCreateWithFormat(NULL
,
586 CFSTR("<SCNetworkConnection MP %p> {service = %@, callout = %p}"),
588 connectionPrivate
->service
,
589 connectionPrivate
->rlsFunction
);
593 static SCNetworkConnectionPrivateRef
594 __SCNetworkConnectionCreatePrivate(CFAllocatorRef allocator
,
595 SCNetworkServiceRef service
,
596 SCNetworkConnectionCallBack callout
,
597 SCNetworkConnectionContext
*context
)
599 SCNetworkConnectionPrivateRef connectionPrivate
= NULL
;
602 /* initialize runtime */
603 pthread_once(&initialized
, __SCNetworkConnectionInitialize
);
605 /* allocate NetworkConnection */
606 size
= sizeof(SCNetworkConnectionPrivate
) - sizeof(CFRuntimeBase
);
607 connectionPrivate
= (SCNetworkConnectionPrivateRef
)_CFRuntimeCreateInstance(allocator
, __kSCNetworkConnectionTypeID
, size
, NULL
);
608 if (connectionPrivate
== NULL
) {
612 /* initialize non-zero/NULL members */
613 pthread_mutex_init(&connectionPrivate
->lock
, NULL
);
614 if (service
!= NULL
) {
615 connectionPrivate
->service
= CFRetain(service
);
617 connectionPrivate
->client_uid
= geteuid();
618 connectionPrivate
->client_gid
= getegid();
619 connectionPrivate
->client_pid
= getpid();
620 connectionPrivate
->rlsFunction
= callout
;
622 memcpy(&connectionPrivate
->rlsContext
, context
, sizeof(SCNetworkConnectionContext
));
623 if (context
->retain
!= NULL
) {
624 connectionPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
627 connectionPrivate
->type
= kSCNetworkConnectionTypeUnknown
;
629 #if !TARGET_OS_SIMULATOR
630 if (__SCNetworkConnectionUseNetworkExtension(connectionPrivate
)) {
631 CFStringRef serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
632 if (serviceID
!= NULL
) {
633 uuid_string_t service_uuid_str
;
634 if (CFStringGetCString(serviceID
, service_uuid_str
, sizeof(service_uuid_str
), kCFStringEncodingUTF8
)) {
636 if (uuid_parse(service_uuid_str
, config_id
) == 0) {
637 connectionPrivate
->ne_session
= ne_session_create(config_id
, NESessionTypeVPN
);
642 if (connectionPrivate
->ne_session
== NULL
) {
644 "SCNetworkConnection failed to create an ne_session: service ID %@ is not a valid UUID",
649 #endif /* !TARGET_OS_SIMULATOR */
651 /* success, return the connection reference */
652 return connectionPrivate
;
656 /* failure, clean up and leave */
657 if (connectionPrivate
!= NULL
) {
658 CFRelease(connectionPrivate
);
661 _SCErrorSet(kSCStatusFailed
);
667 __SCNetworkConnectionServerPort(kern_return_t
*status
)
669 mach_port_t server
= MACH_PORT_NULL
;
671 #ifdef BOOTSTRAP_PRIVILEGED_SERVER
672 *status
= bootstrap_look_up2(bootstrap_port
,
673 __SCNetworkConnectionGetControllerPortName(),
676 BOOTSTRAP_PRIVILEGED_SERVER
);
677 #else // BOOTSTRAP_PRIVILEGED_SERVER
678 *status
= bootstrap_look_up(bootstrap_port
, __SCNetworkConnectionGetControllerPortName(), &server
);
679 #endif // BOOTSTRAP_PRIVILEGED_SERVER
682 case BOOTSTRAP_SUCCESS
:
683 // service currently registered, "a good thing" (tm)
685 case BOOTSTRAP_NOT_PRIVILEGED
:
686 // the service is not privileged
688 case BOOTSTRAP_UNKNOWN_SERVICE
:
689 // service not currently registered, try again later
693 SC_log(LOG_DEBUG
, "bootstrap_look_up() failed: status=%s",
694 bootstrap_strerror(*status
));
699 scnc_server_name
= NULL
; /* reset pppcontroller server */
700 return MACH_PORT_NULL
;
704 __SCNetworkConnectionGetCurrentServerPort(void)
710 __SCNetworkConnectionRefreshServerPort(mach_port_t current_server
, int *mach_result
)
712 mach_port_t new_server
;
714 pthread_mutex_lock(&scnc_lock
);
715 if (scnc_server
!= MACH_PORT_NULL
) {
716 if (current_server
== scnc_server
) {
717 scnc_server_name
= NULL
;
718 // if the server we tried returned the error
719 (void)mach_port_deallocate(mach_task_self(), scnc_server
);
720 scnc_server
= __SCNetworkConnectionServerPort(mach_result
);
722 // another thread has refreshed the server port
725 scnc_server
= __SCNetworkConnectionServerPort(mach_result
);
727 new_server
= scnc_server
;
728 pthread_mutex_unlock(&scnc_lock
);
733 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && (!TARGET_OS_SIMULATOR || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 70000))
734 #define HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
738 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && !TARGET_OS_SIMULATOR
739 #define HAVE_PPPCONTROLLER_ATTACHWITHPROXY
743 __SCNetworkConnectionSessionPort(SCNetworkConnectionPrivateRef connectionPrivate
)
747 CFDataRef dataRef
= NULL
;
748 mach_port_t notify_port
= MACH_PORT_NULL
;
749 mach_port_t oldNotify
= MACH_PORT_NULL
;
751 int sc_status
= kSCStatusFailed
;
752 mach_port_t server
= __SCNetworkConnectionGetCurrentServerPort();
753 kern_return_t status
= KERN_SUCCESS
;
755 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
756 mach_port_t au_session
= MACH_PORT_NULL
;
757 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
759 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
760 return connectionPrivate
->session_port
;
763 if (connectionPrivate
->service
== NULL
) {
764 sc_status
= kSCStatusConnectionNoService
;
768 if (!_SCSerializeString(SCNetworkServiceGetServiceID(connectionPrivate
->service
), &dataRef
, &data
, &dataLen
)) {
772 if (connectionPrivate
->notify_port
!= NULL
) {
773 mach_port_t mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
775 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionSessionPort mp", mp
);
776 CFMachPortInvalidate(connectionPrivate
->notify_port
);
777 CFRelease(connectionPrivate
->notify_port
);
778 connectionPrivate
->notify_port
= NULL
;
779 mach_port_mod_refs(mach_task_self(), mp
, MACH_PORT_RIGHT_RECEIVE
, -1);
782 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
783 au_session
= audit_session_self();
784 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
786 // open a new session with the server
788 if ((connectionPrivate
->rlsFunction
!= NULL
) && (notify_port
== MACH_PORT_NULL
)) {
789 // allocate port (for server response)
790 status
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE
, ¬ify_port
);
791 if (status
!= KERN_SUCCESS
) {
792 SC_log(LOG_ERR
, "mach_port_allocate() failed: %s", mach_error_string(status
));
797 // add send right (passed to the server)
798 status
= mach_port_insert_right(mach_task_self(),
801 MACH_MSG_TYPE_MAKE_SEND
);
802 if (status
!= KERN_SUCCESS
) {
803 SC_log(LOG_NOTICE
, "mach_port_insert_right() failed: %s", mach_error_string(status
));
804 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
810 if (server
!= MACH_PORT_NULL
) {
811 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHPROXY
812 if ((connectionPrivate
->client_audit_session
== MACH_PORT_NULL
) &&
813 (connectionPrivate
->client_bootstrap_port
== MACH_PORT_NULL
) &&
814 (connectionPrivate
->client_uid
== geteuid()) &&
815 (connectionPrivate
->client_gid
== getegid()) &&
816 (connectionPrivate
->client_pid
== getpid())
818 #endif // HAVE_PPPCONTROLLER_ATTACHWITHPROXY
819 status
= pppcontroller_attach(server
,
821 (mach_msg_type_number_t
)dataLen
,
824 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
826 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
827 &connectionPrivate
->session_port
,
829 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHPROXY
831 mach_port_t client_au_session
;
832 mach_port_t client_bootstrap_port
;
834 if (connectionPrivate
->client_audit_session
== MACH_PORT_NULL
) {
835 client_au_session
= au_session
;
837 client_au_session
= connectionPrivate
->client_audit_session
;
840 if (connectionPrivate
->client_bootstrap_port
== MACH_PORT_NULL
) {
841 client_bootstrap_port
= bootstrap_port
;
843 client_bootstrap_port
= connectionPrivate
->client_bootstrap_port
;
846 status
= pppcontroller_attach_proxy(server
,
848 (mach_msg_type_number_t
)dataLen
,
849 client_bootstrap_port
,
852 connectionPrivate
->client_uid
,
853 connectionPrivate
->client_gid
,
854 connectionPrivate
->client_pid
,
855 &connectionPrivate
->session_port
,
858 #endif // HAVE_PPPCONTROLLER_ATTACHWITHPROXY
859 if (status
== KERN_SUCCESS
) {
860 if (sc_status
!= kSCStatusOK
) {
861 SC_log(LOG_DEBUG
, "attach w/error, sc_status=%s%s",
862 SCErrorString(sc_status
),
863 (connectionPrivate
->session_port
!= MACH_PORT_NULL
) ? ", w/session_port!=MACH_PORT_NULL" : "");
865 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
866 __MACH_PORT_DEBUG(TRUE
,
867 "*** __SCNetworkConnectionSessionPort session_port (attach w/error, cleanup)",
868 connectionPrivate
->session_port
);
869 mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
870 connectionPrivate
->session_port
= MACH_PORT_NULL
;
873 if (notify_port
!= MACH_PORT_NULL
) {
874 __MACH_PORT_DEBUG(TRUE
,
875 "*** __SCNetworkConnectionSessionPort notify_port (attach w/error, cleanup)",
877 (void) mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
878 notify_port
= MACH_PORT_NULL
;
884 // our [cached] server port is not valid
885 SC_log(LOG_INFO
, "!attach: %s", SCErrorString(status
));
886 if (status
== MACH_SEND_INVALID_DEST
) {
887 // the server is not yet available
888 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionSessionPort notify_port (!dest)", notify_port
);
889 } else if (status
== MIG_SERVER_DIED
) {
890 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionSessionPort notify_port (!mig)", notify_port
);
891 // the server we were using is gone and we've lost our send right
892 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
893 notify_port
= MACH_PORT_NULL
;
895 // if we got an unexpected error, don't retry
901 server
= __SCNetworkConnectionRefreshServerPort(server
, &sc_status
);
902 if (server
== MACH_PORT_NULL
) {
903 // if server not available
904 if (sc_status
== BOOTSTRAP_UNKNOWN_SERVICE
) {
905 // if first retry attempt, wait for SCDynamicStore server
907 SCDynamicStoreRef store
;
909 store
= SCDynamicStoreCreate(NULL
,
910 CFSTR("SCNetworkConnection connect"),
918 // wait up to 2.5 seconds for the [SCNetworkConnection] server
920 if ((retry
+= 50) < 2500) {
921 usleep(50 * 1000); // sleep 50ms between attempts
929 if (notify_port
!= MACH_PORT_NULL
) {
930 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
931 CFMachPortContext context
= { 0
932 , (void *)connectionPrivate
935 , pppMPCopyDescription
938 // request a notification when/if the server dies
939 status
= mach_port_request_notification(mach_task_self(),
941 MACH_NOTIFY_NO_SENDERS
,
944 MACH_MSG_TYPE_MAKE_SEND_ONCE
,
946 if (status
!= KERN_SUCCESS
) {
947 SC_log(LOG_NOTICE
, "mach_port_request_notification() failed: %s", mach_error_string(status
));
948 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
953 if (oldNotify
!= MACH_PORT_NULL
) {
954 SC_log(LOG_NOTICE
, "oldNotify != MACH_PORT_NULL");
957 // create CFMachPort for SCNetworkConnection notification callback
958 connectionPrivate
->notify_port
= _SC_CFMachPortCreateWithPort("SCNetworkConnection",
960 __SCNetworkConnectionMachCallBack
,
963 // we need to try a bit harder to acquire the initial status
964 connectionPrivate
->haveStatus
= FALSE
;
966 // with no server port, release the notification port we allocated
967 __MACH_PORT_DEBUG(TRUE
,
968 "*** __SCNetworkConnectionSessionPort notify_port (!server)",
970 (void) mach_port_mod_refs (mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
971 (void) mach_port_deallocate(mach_task_self(), notify_port
);
972 notify_port
= MACH_PORT_NULL
;
980 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
981 if (au_session
!= MACH_PORT_NULL
) {
982 (void)mach_port_deallocate(mach_task_self(), au_session
);
984 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
986 if (dataRef
!= NULL
) CFRelease(dataRef
);
990 __MACH_PORT_DEBUG(connectionPrivate
->session_port
!= MACH_PORT_NULL
,
991 "*** __SCNetworkConnectionSessionPort session_port",
992 connectionPrivate
->session_port
);
993 __MACH_PORT_DEBUG(notify_port
!= MACH_PORT_NULL
,
994 "*** __SCNetworkConnectionSessionPort notify_port",
997 case BOOTSTRAP_UNKNOWN_SERVICE
:
998 SC_log((status
== KERN_SUCCESS
) ? LOG_NOTICE
: LOG_ERR
, "PPPController not available");
1001 SC_log((status
== KERN_SUCCESS
) ? LOG_NOTICE
: LOG_ERR
, "pppcontroller_attach() failed: %s",
1002 SCErrorString(sc_status
));
1006 if (sc_status
!= kSCStatusOK
) {
1007 _SCErrorSet(sc_status
);
1010 return connectionPrivate
->session_port
;
1015 __SCNetworkConnectionReconnect(SCNetworkConnectionRef connection
)
1017 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1020 port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1021 return (port
!= MACH_PORT_NULL
);
1026 __SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection
)
1028 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1029 dispatch_group_t dispatchGroup
= NULL
;
1030 dispatch_queue_t dispatchQueue
= NULL
;
1032 CFArrayRef rlList
= NULL
;
1034 // Before we fully tearing down our [old] notifications, make sure
1035 // we have retained any information that is needed to re-register the
1036 // [new] notifications.
1038 pthread_mutex_lock(&connectionPrivate
->lock
);
1040 // save and cancel [old] notifications
1041 if (connectionPrivate
->rlList
!= NULL
) {
1042 rlList
= connectionPrivate
->rlList
;
1043 connectionPrivate
->rlList
= NULL
;
1045 if (connectionPrivate
->rls
!= NULL
) {
1046 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
1047 CFRelease(connectionPrivate
->rls
);
1048 connectionPrivate
->rls
= NULL
;
1050 if (connectionPrivate
->dispatchSource
!= NULL
) {
1051 dispatch_source_cancel(connectionPrivate
->dispatchSource
);
1052 connectionPrivate
->dispatchSource
= NULL
;
1055 // make sure dispatchSource is cancelled before removing group/queue
1056 if (connectionPrivate
->dispatchQueue
!= NULL
) {
1057 // save dispatchQueue, release reference when we've queue'd blocks
1058 // complete, allow re-scheduling
1059 dispatchGroup
= connectionPrivate
->dispatchGroup
;
1060 connectionPrivate
->dispatchGroup
= NULL
;
1061 dispatchQueue
= connectionPrivate
->dispatchQueue
;
1062 connectionPrivate
->dispatchQueue
= NULL
;
1064 // and take an extra reference for rescheduling
1065 dispatch_retain(dispatchQueue
);
1068 connectionPrivate
->scheduled
= FALSE
;
1070 pthread_mutex_unlock(&connectionPrivate
->lock
);
1072 if (dispatchGroup
!= NULL
) {
1073 dispatch_group_notify(dispatchGroup
, dispatchQueue
, ^{
1074 // release group/queue references
1075 dispatch_release(dispatchQueue
);
1076 dispatch_release(dispatchGroup
); // releases our connection reference
1081 if (rlList
!= NULL
) {
1085 n
= CFArrayGetCount(rlList
);
1086 for (i
= 0; i
< n
; i
+= 3) {
1087 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(rlList
, i
+1);
1088 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(rlList
, i
+2);
1090 ok
= SCNetworkConnectionScheduleWithRunLoop(connection
, rl
, rlMode
);
1092 if (SCError() != BOOTSTRAP_UNKNOWN_SERVICE
) {
1093 SC_log(LOG_NOTICE
, "SCNetworkConnectionScheduleWithRunLoop() failed");
1098 } else if (dispatchQueue
!= NULL
) {
1099 ok
= SCNetworkConnectionSetDispatchQueue(connection
, dispatchQueue
);
1101 if (SCError() != BOOTSTRAP_UNKNOWN_SERVICE
) {
1102 SC_log(LOG_NOTICE
, "SCNetworkConnectionSetDispatchQueue() failed");
1113 if (rlList
!= NULL
) {
1116 if (dispatchQueue
!= NULL
) {
1117 dispatch_release(dispatchQueue
);
1121 SC_log(LOG_NOTICE
, "SCNetworkConnection server %s, notification not restored",
1122 (SCError() == BOOTSTRAP_UNKNOWN_SERVICE
) ? "shutdown" : "failed");
1130 __SCNetworkConnectionNeedsRetry(SCNetworkConnectionRef connection
,
1131 const char *error_label
,
1132 kern_return_t status
,
1135 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1137 if (status
== KERN_SUCCESS
) {
1141 if ((status
== MACH_SEND_INVALID_DEST
) || (status
== MIG_SERVER_DIED
)) {
1142 // the server's gone and our session port's dead, remove the dead name right
1143 (void) mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
1145 // we got an unexpected error, leave the [session] port alone
1146 SC_log(LOG_NOTICE
, "%s: %s", error_label
, mach_error_string(status
));
1148 connectionPrivate
->session_port
= MACH_PORT_NULL
;
1149 if ((status
== MACH_SEND_INVALID_DEST
) || (status
== MIG_SERVER_DIED
)) {
1150 if (__SCNetworkConnectionReconnect(connection
)) {
1154 *sc_status
= status
;
1161 SCNetworkConnectionGetTypeID(void) {
1162 pthread_once(&initialized
, __SCNetworkConnectionInitialize
); /* initialize runtime */
1163 return __kSCNetworkConnectionTypeID
;
1167 CFArrayRef
/* of SCNetworkServiceRef's */
1168 SCNetworkConnectionCopyAvailableServices(SCNetworkSetRef set
)
1170 CFMutableArrayRef available
;
1171 Boolean tempSet
= FALSE
;
1174 SCPreferencesRef prefs
;
1176 prefs
= SCPreferencesCreate(NULL
, CFSTR("SCNetworkConnectionCopyAvailableServices"), NULL
);
1177 if (prefs
!= NULL
) {
1178 set
= SCNetworkSetCopyCurrent(prefs
);
1184 available
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1187 CFArrayRef services
;
1189 services
= SCNetworkSetCopyServices(set
);
1190 if (services
!= NULL
) {
1194 n
= CFArrayGetCount(services
);
1195 for (i
= 0; i
< n
; i
++) {
1196 SCNetworkInterfaceRef interface
;
1197 CFStringRef interfaceType
;
1198 SCNetworkServiceRef service
;
1200 service
= CFArrayGetValueAtIndex(services
, i
);
1201 interface
= SCNetworkServiceGetInterface(service
);
1202 if (interface
== NULL
) {
1206 interfaceType
= SCNetworkInterfaceGetInterfaceType(interface
);
1207 if (CFEqual(interfaceType
, kSCNetworkInterfaceTypePPP
) ||
1208 CFEqual(interfaceType
, kSCNetworkInterfaceTypeVPN
) ||
1209 CFEqual(interfaceType
, kSCNetworkInterfaceTypeIPSec
)) {
1210 CFArrayAppendValue(available
, service
);
1214 CFRelease(services
);
1218 if (tempSet
&& (set
!= NULL
)) {
1225 SCNetworkConnectionRef
1226 SCNetworkConnectionCreateWithService(CFAllocatorRef allocator
,
1227 SCNetworkServiceRef service
,
1228 SCNetworkConnectionCallBack callout
,
1229 SCNetworkConnectionContext
*context
)
1231 SCNetworkConnectionPrivateRef connectionPrivate
;
1233 if (!isA_SCNetworkService(service
)) {
1234 _SCErrorSet(kSCStatusInvalidArgument
);
1238 if (__SCNetworkServiceIsPPTP(service
)) {
1239 SC_log(LOG_INFO
, "PPTP VPNs are no longer supported");
1240 _SCErrorSet(kSCStatusConnectionIgnore
);
1244 connectionPrivate
= __SCNetworkConnectionCreatePrivate(allocator
, service
, callout
, context
);
1245 return (SCNetworkConnectionRef
)connectionPrivate
;
1249 SCNetworkConnectionRef
1250 SCNetworkConnectionCreateWithServiceID(CFAllocatorRef allocator
,
1251 CFStringRef serviceID
,
1252 SCNetworkConnectionCallBack callout
,
1253 SCNetworkConnectionContext
*context
)
1255 SCNetworkConnectionRef connection
;
1256 SCNetworkServiceRef service
;
1258 if (!isA_CFString(serviceID
)) {
1259 _SCErrorSet(kSCStatusInvalidArgument
);
1263 service
= _SCNetworkServiceCopyActive(NULL
, serviceID
);
1264 if (service
== NULL
) {
1268 connection
= SCNetworkConnectionCreateWithService(allocator
, service
, callout
, context
);
1275 SCNetworkConnectionRef
1276 SCNetworkConnectionCreate(CFAllocatorRef allocator
,
1277 SCNetworkConnectionCallBack callout
,
1278 SCNetworkConnectionContext
*context
)
1280 SCNetworkConnectionPrivateRef connectionPrivate
= __SCNetworkConnectionCreatePrivate(allocator
, NULL
, callout
, context
);
1281 return (SCNetworkConnectionRef
)connectionPrivate
;
1286 SCNetworkConnectionCopyServiceID(SCNetworkConnectionRef connection
)
1288 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1289 CFStringRef serviceID
;
1291 if (!isA_SCNetworkConnection(connection
)) {
1292 _SCErrorSet(kSCStatusInvalidArgument
);
1296 if (connectionPrivate
->service
== NULL
) {
1297 _SCErrorSet(kSCStatusConnectionNoService
);
1301 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
1302 return CFRetain(serviceID
);
1307 SCNetworkConnectionSetClientInfo(SCNetworkConnectionRef connection
,
1308 mach_port_t client_audit_session
,
1313 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1315 if (!isA_SCNetworkConnection(connection
)) {
1316 _SCErrorSet(kSCStatusInvalidArgument
);
1320 // save client audit session port
1321 if (connectionPrivate
->client_audit_session
!= MACH_PORT_NULL
) {
1322 mach_port_deallocate(mach_task_self(),
1323 connectionPrivate
->client_audit_session
);
1324 connectionPrivate
->client_audit_session
= MACH_PORT_NULL
;
1326 connectionPrivate
->client_audit_session
= client_audit_session
;
1327 if (connectionPrivate
->client_audit_session
!= MACH_PORT_NULL
) {
1328 mach_port_mod_refs(mach_task_self(),
1329 connectionPrivate
->client_audit_session
,
1330 MACH_PORT_RIGHT_SEND
,
1334 // save client UID, GID, and PID
1335 connectionPrivate
->client_uid
= client_uid
;
1336 connectionPrivate
->client_gid
= client_gid
;
1337 connectionPrivate
->client_pid
= client_pid
;
1344 SCNetworkConnectionSetClientAuditInfo(SCNetworkConnectionRef connection
,
1345 audit_token_t client_audit_token
,
1346 mach_port_t audit_session
,
1347 mach_port_t bootstrap_port
,
1350 const char *bundle_id
)
1352 const audit_token_t null_audit
= KERNEL_AUDIT_TOKEN_VALUE
;
1353 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1358 if (memcmp(&client_audit_token
, &null_audit
, sizeof(client_audit_token
))) {
1359 #if TARGET_OS_IPHONE
1360 audit_token_to_au32(client_audit_token
, NULL
, &uid
, &gid
, NULL
, NULL
, &pid
, NULL
, NULL
);
1361 #else // TARGET_OS_IPHONE
1362 uid
= audit_token_to_euid(client_audit_token
);
1363 gid
= audit_token_to_egid(client_audit_token
);
1364 pid
= audit_token_to_pid(client_audit_token
);
1365 #endif // TARGET_OS_IPHONE
1370 if (!SCNetworkConnectionSetClientInfo(connection
, audit_session
, uid
, gid
, pid
)) {
1374 if (connectionPrivate
->client_bootstrap_port
!= MACH_PORT_NULL
) {
1375 mach_port_deallocate(mach_task_self(),
1376 connectionPrivate
->client_bootstrap_port
);
1377 connectionPrivate
->client_bootstrap_port
= MACH_PORT_NULL
;
1380 connectionPrivate
->client_bootstrap_port
= bootstrap_port
;
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
,
1388 memcpy(&connectionPrivate
->client_audit_token
, &client_audit_token
, sizeof(connectionPrivate
->client_audit_token
));
1390 if (uuid
!= NULL
&& !uuid_is_null(uuid
)) {
1391 uuid_copy(connectionPrivate
->client_uuid
, uuid
);
1394 if (connectionPrivate
->client_bundle_id
!= NULL
) {
1395 CFRelease(connectionPrivate
->client_bundle_id
);
1396 connectionPrivate
->client_bundle_id
= NULL
;
1399 if (bundle_id
!= NULL
) {
1400 connectionPrivate
->client_bundle_id
= CFStringCreateWithCString(kCFAllocatorDefault
, bundle_id
, kCFStringEncodingUTF8
);
1408 SCNetworkConnectionCopyStatistics(SCNetworkConnectionRef connection
)
1410 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1411 xmlDataOut_t data
= NULL
;
1412 mach_msg_type_number_t datalen
= 0;
1413 int sc_status
= kSCStatusFailed
;
1414 mach_port_t session_port
;
1415 CFPropertyListRef statistics
= NULL
;
1416 kern_return_t status
;
1418 if (!isA_SCNetworkConnection(connection
)) {
1419 _SCErrorSet(kSCStatusInvalidArgument
);
1423 pthread_mutex_lock(&connectionPrivate
->lock
);
1425 #if !TARGET_OS_SIMULATOR
1426 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1427 __block xpc_object_t xstats
= NULL
;
1428 ne_session_t ne_session
= connectionPrivate
->ne_session
;
1430 ne_session_retain(ne_session
);
1431 pthread_mutex_unlock(&connectionPrivate
->lock
);
1433 dispatch_semaphore_t ne_sema
= dispatch_semaphore_create(0);
1434 ne_session_get_info(ne_session
, NESessionInfoTypeStatistics
, __SCNetworkConnectionQueue(), ^(xpc_object_t result
) {
1435 if (result
!= NULL
) {
1436 xstats
= xpc_retain(result
);
1438 ne_session_release(ne_session
);
1439 dispatch_semaphore_signal(ne_sema
);
1441 dispatch_semaphore_wait(ne_sema
, DISPATCH_TIME_FOREVER
);
1442 dispatch_release(ne_sema
);
1444 if (xstats
!= NULL
) {
1445 statistics
= _CFXPCCreateCFObjectFromXPCObject(xstats
);
1446 xpc_release(xstats
);
1448 _SCErrorSet(kSCStatusFailed
);
1453 #endif /* !TARGET_OS_SIMULATOR */
1457 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1458 if (session_port
== MACH_PORT_NULL
) {
1462 status
= pppcontroller_copystatistics(session_port
, &data
, &datalen
, &sc_status
);
1463 if (__SCNetworkConnectionNeedsRetry(connection
,
1464 "SCNetworkConnectionCopyStatistics()",
1471 if (!_SCUnserialize(&statistics
, NULL
, data
, datalen
)) {
1472 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
1474 if ((sc_status
== kSCStatusOK
) && !isA_CFDictionary(statistics
)) {
1475 sc_status
= kSCStatusFailed
;
1479 if (sc_status
!= kSCStatusOK
) {
1480 if (statistics
!= NULL
) {
1481 CFRelease(statistics
);
1484 _SCErrorSet(sc_status
);
1489 pthread_mutex_unlock(&connectionPrivate
->lock
);
1495 SCNetworkConnectionGetService(SCNetworkConnectionRef connection
)
1497 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1499 if (!isA_SCNetworkConnection(connection
)) {
1500 _SCErrorSet(kSCStatusInvalidArgument
);
1504 return connectionPrivate
->service
;
1508 SCNetworkConnectionStatus
1509 SCNetworkConnectionGetStatus(SCNetworkConnectionRef connection
)
1511 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1512 SCNetworkConnectionStatus nc_status
= kSCNetworkConnectionInvalid
;
1514 int sc_status
= kSCStatusFailed
;
1515 mach_port_t session_port
;
1516 kern_return_t status
;
1517 CFStringRef serviceID
;
1519 if (!isA_SCNetworkConnection(connection
)) {
1520 _SCErrorSet(kSCStatusInvalidArgument
);
1521 return kSCNetworkConnectionInvalid
;
1524 if (connectionPrivate
->service
== NULL
) {
1525 _SCErrorSet(kSCStatusConnectionNoService
);
1526 return kSCNetworkConnectionInvalid
;
1529 // skip retry and return immediately if we know no service is to be found.
1530 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
1531 if (CFStringGetLength(serviceID
) == 0) {
1532 _SCErrorSet(kSCStatusConnectionNoService
);
1533 return kSCNetworkConnectionInvalid
;
1536 pthread_mutex_lock(&connectionPrivate
->lock
);
1538 #if !TARGET_OS_SIMULATOR
1539 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1540 __block ne_session_status_t ne_status
;
1541 ne_session_t ne_session
= connectionPrivate
->ne_session
;
1543 ne_session_retain(ne_session
);
1544 pthread_mutex_unlock(&connectionPrivate
->lock
);
1546 dispatch_semaphore_t ne_sema
= dispatch_semaphore_create(0);
1547 ne_session_get_status(ne_session
, __SCNetworkConnectionQueue(), ^(ne_session_status_t status
) {
1549 ne_session_release(ne_session
);
1550 dispatch_semaphore_signal(ne_sema
);
1552 dispatch_semaphore_wait(ne_sema
, DISPATCH_TIME_FOREVER
);
1553 dispatch_release(ne_sema
);
1555 return SCNetworkConnectionGetStatusFromNEStatus(ne_status
);
1557 #endif /* !TARGET_OS_SIMULATOR */
1561 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1562 if (session_port
== MACH_PORT_NULL
) {
1563 nc_status
= kSCNetworkConnectionInvalid
;
1567 status
= pppcontroller_getstatus(session_port
, &nc_status
, &sc_status
);
1568 if (__SCNetworkConnectionNeedsRetry(connection
,
1569 "SCNetworkConnectionGetStatus()",
1575 // wait up to 250 ms for the network service to become available
1576 if (!connectionPrivate
->haveStatus
&&
1577 (sc_status
== kSCStatusConnectionNoService
) &&
1578 ((retry
+= 10) < 250)) {
1579 usleep(10 * 1000); // sleep 10ms between attempts
1583 if (sc_status
== kSCStatusOK
) {
1584 connectionPrivate
->haveStatus
= TRUE
;
1586 _SCErrorSet(sc_status
);
1587 nc_status
= kSCNetworkConnectionInvalid
;
1592 pthread_mutex_unlock(&connectionPrivate
->lock
);
1598 SCNetworkConnectionCopyExtendedStatus(SCNetworkConnectionRef connection
)
1600 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1601 xmlDataOut_t data
= NULL
;
1602 mach_msg_type_number_t datalen
= 0;
1603 CFPropertyListRef extstatus
= NULL
;
1605 int sc_status
= kSCStatusFailed
;
1606 mach_port_t session_port
;
1607 kern_return_t status
;
1608 CFStringRef serviceID
;
1610 if (!isA_SCNetworkConnection(connection
)) {
1611 _SCErrorSet(kSCStatusInvalidArgument
);
1615 if (connectionPrivate
->service
== NULL
) {
1616 _SCErrorSet(kSCStatusConnectionNoService
);
1620 // skip retry and return immediately if we know no service is to be found.
1621 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
1622 if (CFStringGetLength(serviceID
) == 0) {
1623 _SCErrorSet(kSCStatusConnectionNoService
);
1627 pthread_mutex_lock(&connectionPrivate
->lock
);
1629 #if !TARGET_OS_SIMULATOR
1630 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1631 __block CFDictionaryRef statusDictionary
= NULL
;
1632 ne_session_t ne_session
= connectionPrivate
->ne_session
;
1634 ne_session_retain(ne_session
);
1635 pthread_mutex_unlock(&connectionPrivate
->lock
);
1637 dispatch_semaphore_t ne_sema
= dispatch_semaphore_create(0);
1638 ne_session_get_info(ne_session
, NESessionInfoTypeExtendedStatus
, __SCNetworkConnectionQueue(), ^(xpc_object_t extended_status
) {
1639 if (extended_status
!= NULL
) {
1640 statusDictionary
= _CFXPCCreateCFObjectFromXPCObject(extended_status
);
1641 ne_session_release(ne_session
);
1642 dispatch_semaphore_signal(ne_sema
);
1644 ne_session_get_status(ne_session
, __SCNetworkConnectionQueue(), ^(ne_session_status_t ne_status
) {
1645 SCNetworkConnectionStatus status
= SCNetworkConnectionGetStatusFromNEStatus(ne_status
);
1646 if (status
!= kSCNetworkConnectionInvalid
) {
1647 CFStringRef keys
[1] = { kSCNetworkConnectionStatus
};
1648 CFNumberRef values
[1] = { NULL
};
1649 values
[0] = CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &status
);
1650 statusDictionary
= CFDictionaryCreate(kCFAllocatorDefault
, (const void **)keys
, (const void **)values
, sizeof(values
) / sizeof(values
[0]), &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1651 CFRelease(values
[0]);
1653 ne_session_release(ne_session
);
1654 dispatch_semaphore_signal(ne_sema
);
1658 dispatch_semaphore_wait(ne_sema
, DISPATCH_TIME_FOREVER
);
1659 dispatch_release(ne_sema
);
1661 if (statusDictionary
!= NULL
) {
1662 extstatus
= (CFPropertyListRef
)statusDictionary
;
1664 _SCErrorSet(kSCStatusFailed
);
1673 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1674 if (session_port
== MACH_PORT_NULL
) {
1678 status
= pppcontroller_copyextendedstatus(session_port
, &data
, &datalen
, &sc_status
);
1679 if (__SCNetworkConnectionNeedsRetry(connection
,
1680 "SCNetworkConnectionCopyExtendedStatus()",
1687 if (!_SCUnserialize(&extstatus
, NULL
, data
, datalen
)) {
1688 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
1690 if ((sc_status
== kSCStatusOK
) && !isA_CFDictionary(extstatus
)) {
1691 sc_status
= kSCStatusFailed
;
1695 // wait up to 250 ms for the network service to become available
1696 if (!connectionPrivate
->haveStatus
&&
1697 (sc_status
== kSCStatusConnectionNoService
) &&
1698 ((retry
+= 10) < 250)) {
1699 usleep(10 * 1000); // sleep 10ms between attempts
1703 if (sc_status
== kSCStatusOK
) {
1704 connectionPrivate
->haveStatus
= TRUE
;
1706 if (extstatus
!= NULL
) {
1707 CFRelease(extstatus
);
1710 _SCErrorSet(sc_status
);
1715 pthread_mutex_unlock(&connectionPrivate
->lock
);
1721 _SCNetworkConnectionMergeDictionaries (const void *key
, const void *value
, void *context
)
1723 /* Add value only if not present */
1724 CFDictionaryAddValue((CFMutableDictionaryRef
)context
, (CFStringRef
)key
, (CFTypeRef
)value
);
1729 SCNetworkConnectionStart(SCNetworkConnectionRef connection
,
1730 CFDictionaryRef userOptions
,
1733 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1734 CFDataRef dataref
= NULL
;
1736 CFIndex datalen
= 0;
1738 int sc_status
= kSCStatusFailed
;
1739 mach_port_t session_port
;
1740 kern_return_t status
;
1742 if (!isA_SCNetworkConnection(connection
)) {
1743 _SCErrorSet(kSCStatusInvalidArgument
);
1747 if ((userOptions
!= NULL
) && !isA_CFDictionary(userOptions
)) {
1748 _SCErrorSet(kSCStatusInvalidArgument
);
1752 if (userOptions
== NULL
) {
1753 userOptions
= connectionPrivate
->on_demand_user_options
;
1754 } else if (connectionPrivate
->on_demand_user_options
!= NULL
) {
1755 CFDictionaryRef localUserOptions
= NULL
;
1757 localUserOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, userOptions
);
1758 if (localUserOptions
) {
1759 CFDictionaryApplyFunction(connectionPrivate
->on_demand_user_options
,
1760 _SCNetworkConnectionMergeDictionaries
,
1761 (void *)localUserOptions
);
1762 CFRelease(connectionPrivate
->on_demand_user_options
);
1763 userOptions
= connectionPrivate
->on_demand_user_options
= localUserOptions
;
1768 CFMutableDictionaryRef mdict
= NULL
;
1770 SC_log(LOG_INFO
, "SCNetworkConnectionStart (%p)", connectionPrivate
);
1772 if (userOptions
!= NULL
) {
1773 CFDictionaryRef dict
;
1774 CFStringRef encryption
;
1775 CFMutableDictionaryRef new_dict
;
1777 /* special code to remove secret information */
1778 mdict
= CFDictionaryCreateMutableCopy(NULL
, 0, userOptions
);
1780 dict
= CFDictionaryGetValue(mdict
, kSCEntNetPPP
);
1781 if (isA_CFDictionary(dict
)) {
1782 encryption
= CFDictionaryGetValue(dict
, kSCPropNetPPPAuthPasswordEncryption
);
1783 if (!isA_CFString(encryption
) ||
1784 !CFEqual(encryption
, kSCValNetPPPAuthPasswordEncryptionKeychain
)) {
1785 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1786 CFDictionaryReplaceValue(new_dict
, kSCPropNetPPPAuthPassword
, CFSTR("******"));
1787 CFDictionarySetValue(mdict
, kSCEntNetPPP
, new_dict
);
1788 CFRelease(new_dict
);
1792 dict
= CFDictionaryGetValue(mdict
, kSCEntNetL2TP
);
1793 if (isA_CFDictionary(dict
)) {
1794 encryption
= CFDictionaryGetValue(dict
, kSCPropNetL2TPIPSecSharedSecretEncryption
);
1795 if (!isA_CFString(encryption
) ||
1796 !CFEqual(encryption
, kSCValNetL2TPIPSecSharedSecretEncryptionKeychain
)) {
1797 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1798 CFDictionaryReplaceValue(new_dict
, kSCPropNetL2TPIPSecSharedSecret
, CFSTR("******"));
1799 CFDictionarySetValue(mdict
, kSCEntNetL2TP
, new_dict
);
1800 CFRelease(new_dict
);
1804 dict
= CFDictionaryGetValue(mdict
, kSCEntNetIPSec
);
1805 if (isA_CFDictionary(dict
)) {
1806 encryption
= CFDictionaryGetValue(dict
, kSCPropNetIPSecSharedSecretEncryption
);
1807 if (!isA_CFString(encryption
) ||
1808 !CFEqual(encryption
, kSCValNetIPSecSharedSecretEncryptionKeychain
)) {
1809 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1810 CFDictionaryReplaceValue(new_dict
, kSCPropNetIPSecSharedSecret
, CFSTR("******"));
1811 CFDictionarySetValue(mdict
, kSCEntNetIPSec
, new_dict
);
1812 CFRelease(new_dict
);
1817 SC_log(LOG_INFO
, "User options: %@", mdict
);
1818 if (mdict
!= NULL
) CFRelease(mdict
);
1821 pthread_mutex_lock(&connectionPrivate
->lock
);
1823 /* Clear out any cached flow divert token parameters */
1824 if (connectionPrivate
->flow_divert_token_params
!= NULL
) {
1825 CFRelease(connectionPrivate
->flow_divert_token_params
);
1826 connectionPrivate
->flow_divert_token_params
= NULL
;
1829 #if !TARGET_OS_SIMULATOR
1830 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1831 xpc_object_t xuser_options
= NULL
;
1833 if (userOptions
!= NULL
) {
1834 xuser_options
= _CFXPCCreateXPCObjectFromCFObject(userOptions
);
1837 if (connectionPrivate
->client_bootstrap_port
!= MACH_PORT_NULL
) {
1838 #if NE_SESSION_VERSION > 2
1839 ne_session_start_on_behalf_of(connectionPrivate
->ne_session
,
1841 connectionPrivate
->client_bootstrap_port
,
1842 connectionPrivate
->client_audit_session
,
1843 connectionPrivate
->client_uid
,
1844 connectionPrivate
->client_gid
,
1845 connectionPrivate
->client_pid
);
1847 ne_session_start_on_behalf_of(connectionPrivate
->ne_session
,
1849 connectionPrivate
->client_bootstrap_port
,
1850 connectionPrivate
->client_audit_session
,
1851 connectionPrivate
->client_uid
,
1852 connectionPrivate
->client_gid
);
1855 ne_session_start_with_options(connectionPrivate
->ne_session
, xuser_options
);
1858 /* make sure the xpc_message goes through */
1859 ne_session_send_barrier(connectionPrivate
->ne_session
);
1861 if (xuser_options
!= NULL
) {
1862 xpc_release(xuser_options
);
1868 #endif /* !TARGET_OS_SIMULATOR */
1870 if (userOptions
&& !_SCSerialize(userOptions
, &dataref
, &data
, &datalen
)) {
1876 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1877 if (session_port
== MACH_PORT_NULL
) {
1878 if (dataref
) CFRelease(dataref
);
1882 status
= pppcontroller_start(session_port
,
1884 (mach_msg_type_number_t
)datalen
,
1887 if (__SCNetworkConnectionNeedsRetry(connection
,
1888 "SCNetworkConnectionStart()",
1894 if (dataref
) CFRelease(dataref
);
1897 SC_log(LOG_INFO
, "SCNetworkConnectionStart (%p), return: %d", connectionPrivate
, sc_status
);
1900 if (sc_status
!= kSCStatusOK
) {
1901 _SCErrorSet(sc_status
);
1905 /* connection is now started */
1909 pthread_mutex_unlock(&connectionPrivate
->lock
);
1915 SCNetworkConnectionStop(SCNetworkConnectionRef connection
,
1916 Boolean forceDisconnect
)
1918 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1920 int sc_status
= kSCStatusFailed
;
1921 mach_port_t session_port
;
1922 kern_return_t status
;
1924 if (!isA_SCNetworkConnection(connection
)) {
1925 _SCErrorSet(kSCStatusInvalidArgument
);
1930 SC_log(LOG_INFO
, "SCNetworkConnectionStop (%p)", connectionPrivate
);
1933 pthread_mutex_lock(&connectionPrivate
->lock
);
1935 #if !TARGET_OS_SIMULATOR
1936 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
1937 ne_session_stop(connectionPrivate
->ne_session
);
1938 /* make sure the xpc_message goes through */
1939 ne_session_send_barrier(connectionPrivate
->ne_session
);
1943 #endif /* !TARGET_OS_SIMULATOR */
1947 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1948 if (session_port
== MACH_PORT_NULL
) {
1952 status
= pppcontroller_stop(session_port
, forceDisconnect
, &sc_status
);
1953 if (__SCNetworkConnectionNeedsRetry(connection
,
1954 "SCNetworkConnectionStop()",
1961 SC_log(LOG_INFO
, "SCNetworkConnectionStop (%p), return: %d", connectionPrivate
, sc_status
);
1964 if (sc_status
!= kSCStatusOK
) {
1965 _SCErrorSet(sc_status
);
1969 /* connection is now disconnecting */
1974 pthread_mutex_unlock(&connectionPrivate
->lock
);
1980 SCNetworkConnectionSuspend(SCNetworkConnectionRef connection
)
1982 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1984 int sc_status
= kSCStatusFailed
;
1985 mach_port_t session_port
;
1986 kern_return_t status
;
1988 if (!isA_SCNetworkConnection(connection
)) {
1989 _SCErrorSet(kSCStatusInvalidArgument
);
1994 SC_log(LOG_INFO
, "SCNetworkConnectionSuspend (%p)", connectionPrivate
);
1997 pthread_mutex_lock(&connectionPrivate
->lock
);
1999 #if !!TARGET_OS_SIMULATOR
2000 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2001 /* Suspend only applies to PPPSerial and PPPoE */
2005 #endif /* !TARGET_OS_SIMULATOR */
2009 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2010 if (session_port
== MACH_PORT_NULL
) {
2014 status
= pppcontroller_suspend(session_port
, &sc_status
);
2015 if (__SCNetworkConnectionNeedsRetry(connection
,
2016 "SCNetworkConnectionSuspend()",
2023 SC_log(LOG_INFO
, "SCNetworkConnectionSuspend (%p), return: %d", connectionPrivate
, sc_status
);
2026 if (sc_status
!= kSCStatusOK
) {
2027 _SCErrorSet(sc_status
);
2031 /* connection is now suspended */
2036 pthread_mutex_unlock(&connectionPrivate
->lock
);
2042 SCNetworkConnectionResume(SCNetworkConnectionRef connection
)
2044 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2046 int sc_status
= kSCStatusFailed
;
2047 mach_port_t session_port
;
2048 kern_return_t status
;
2050 if (!isA_SCNetworkConnection(connection
)) {
2051 _SCErrorSet(kSCStatusInvalidArgument
);
2056 SC_log(LOG_INFO
, "SCNetworkConnectionResume (%p)", connectionPrivate
);
2059 pthread_mutex_lock(&connectionPrivate
->lock
);
2061 #if !TARGET_OS_SIMULATOR
2062 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2063 /* Resume only applies to PPPSerial and PPPoE */
2067 #endif /* !TARGET_OS_SIMULATOR */
2071 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2072 if (session_port
== MACH_PORT_NULL
) {
2076 status
= pppcontroller_resume(session_port
, &sc_status
);
2077 if (__SCNetworkConnectionNeedsRetry(connection
,
2078 "SCNetworkConnectionResume()",
2085 SC_log(LOG_INFO
, "SCNetworkConnectionResume (%p), return: %d", connectionPrivate
, sc_status
);
2088 if (sc_status
!= kSCStatusOK
) {
2089 _SCErrorSet(sc_status
);
2093 /* connection is now resume */
2098 pthread_mutex_unlock(&connectionPrivate
->lock
);
2103 #if !TARGET_OS_SIMULATOR
2105 SCNetworkConnectionRefreshOnDemandState(__unused SCNetworkConnectionRef connection
)
2109 #endif /* !TARGET_OS_SIMULATOR */
2113 SCNetworkConnectionCopyUserOptions(SCNetworkConnectionRef connection
)
2115 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2116 xmlDataOut_t data
= NULL
;
2117 mach_msg_type_number_t datalen
= 0;
2118 int sc_status
= kSCStatusFailed
;
2119 mach_port_t session_port
;
2120 kern_return_t status
;
2121 CFPropertyListRef userOptions
= NULL
;
2123 if (!isA_SCNetworkConnection(connection
)) {
2124 _SCErrorSet(kSCStatusInvalidArgument
);
2128 pthread_mutex_lock(&connectionPrivate
->lock
);
2130 #if !TARGET_OS_SIMULATOR
2131 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2132 __block xpc_object_t config
= NULL
;
2133 ne_session_t ne_session
= connectionPrivate
->ne_session
;
2135 ne_session_retain(ne_session
);
2136 pthread_mutex_unlock(&connectionPrivate
->lock
);
2138 dispatch_semaphore_t ne_sema
= dispatch_semaphore_create(0);
2139 ne_session_get_info(ne_session
, NESessionInfoTypeConfiguration
, __SCNetworkConnectionQueue(), ^(xpc_object_t result
) {
2140 if (result
!= NULL
) {
2141 config
= xpc_retain(result
);
2143 ne_session_release(ne_session
);
2144 dispatch_semaphore_signal(ne_sema
);
2146 dispatch_semaphore_wait(ne_sema
, DISPATCH_TIME_FOREVER
);
2147 dispatch_release(ne_sema
);
2149 if (config
!= NULL
) {
2150 xpc_object_t xoptions
= xpc_dictionary_get_value(config
, NESMSessionLegacyUserConfigurationKey
);
2151 if (xoptions
!= NULL
) {
2152 userOptions
= _CFXPCCreateCFObjectFromXPCObject(xoptions
);
2154 xpc_release(config
);
2158 #endif /* !TARGET_OS_SIMULATOR */
2162 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2163 if (session_port
== MACH_PORT_NULL
) {
2167 status
= pppcontroller_copyuseroptions(session_port
, &data
, &datalen
, &sc_status
);
2168 if (__SCNetworkConnectionNeedsRetry(connection
,
2169 "SCNetworkConnectionCopyUserOptions()",
2176 if (!_SCUnserialize(&userOptions
, NULL
, data
, datalen
)) {
2177 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
2179 if ((sc_status
== kSCStatusOK
) && (userOptions
!= NULL
) && !isA_CFDictionary(userOptions
)) {
2180 sc_status
= kSCStatusFailed
;
2184 if (sc_status
== kSCStatusOK
) {
2185 if (userOptions
== NULL
) {
2186 // if no user options, return an empty dictionary
2187 userOptions
= CFDictionaryCreate(NULL
,
2191 &kCFTypeDictionaryKeyCallBacks
,
2192 &kCFTypeDictionaryValueCallBacks
);
2196 CFRelease(userOptions
);
2199 _SCErrorSet(sc_status
);
2204 pthread_mutex_unlock(&connectionPrivate
->lock
);
2210 __SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection
,
2211 CFRunLoopRef runLoop
,
2212 CFStringRef runLoopMode
,
2213 dispatch_queue_t queue
)
2215 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2217 int sc_status
= kSCStatusFailed
;
2218 mach_port_t session_port
;
2219 kern_return_t status
;
2221 pthread_mutex_lock(&connectionPrivate
->lock
);
2223 if (connectionPrivate
->rlsFunction
== NULL
) {
2224 _SCErrorSet(kSCStatusInvalidArgument
);
2228 if ((connectionPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled on a dispatch queue
2229 ((queue
!= NULL
) && connectionPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
2230 _SCErrorSet(kSCStatusInvalidArgument
);
2234 if (!connectionPrivate
->scheduled
) {
2238 if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2239 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2240 if (session_port
== MACH_PORT_NULL
) {
2244 status
= pppcontroller_notification(session_port
, 1, &sc_status
);
2245 if (__SCNetworkConnectionNeedsRetry(connection
,
2246 "__SCNetworkConnectionScheduleWithRunLoop()",
2252 if (sc_status
!= kSCStatusOK
) {
2253 _SCErrorSet(sc_status
);
2257 if (runLoop
!= NULL
) {
2258 connectionPrivate
->rls
= CFMachPortCreateRunLoopSource(NULL
, connectionPrivate
->notify_port
, 0);
2259 connectionPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2261 } else if (runLoop
!= NULL
) {
2262 CFRunLoopSourceContext rlsContext
= {
2264 (void *)connection
, // info
2267 NULL
, // copy description
2272 __SCNetworkConnectionCallBack
, // perform
2275 connectionPrivate
->rls
= CFRunLoopSourceCreate(kCFAllocatorDefault
, 0, &rlsContext
);
2276 connectionPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2279 connectionPrivate
->scheduled
= TRUE
;
2282 if (queue
!= NULL
) {
2283 // retain the dispatch queue
2284 connectionPrivate
->dispatchQueue
= queue
;
2285 dispatch_retain(connectionPrivate
->dispatchQueue
);
2287 if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2288 dispatch_group_t group
= NULL
;
2290 dispatch_source_t source
;
2293 // We've taken a reference to the caller's dispatch_queue and we
2294 // want to hold on to that reference until we've processed any/all
2295 // notifications. To facilitate this we create a group, dispatch
2296 // any notification blocks via that group, and when the caller
2297 // has told us to stop the notifications (unschedule) we wait for
2298 // the group to empty and use the group's finalizer to release
2299 // our reference to the SCNetworkConnection.
2301 group
= dispatch_group_create();
2302 connectionPrivate
->dispatchGroup
= group
;
2303 CFRetain(connection
);
2304 dispatch_set_context(connectionPrivate
->dispatchGroup
, (void *)connection
);
2305 dispatch_set_finalizer_f(connectionPrivate
->dispatchGroup
, (dispatch_function_t
)CFRelease
);
2307 mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
2308 source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
, mp
, 0, queue
);
2309 if (source
== NULL
) {
2310 SC_log(LOG_NOTICE
, "dispatch_source_create() failed");
2311 _SCErrorSet(kSCStatusFailed
);
2315 // have our dispatch source hold a reference to the notification CFMachPort
2316 CFRetain(connectionPrivate
->notify_port
);
2317 dispatch_set_context(source
, (void *)connectionPrivate
->notify_port
);
2318 dispatch_set_finalizer_f(source
, (dispatch_function_t
)CFRelease
);
2320 dispatch_source_set_event_handler(source
, ^{
2323 u_int8_t buf
[sizeof(mach_msg_empty_t
) + MAX_TRAILER_SIZE
];
2324 mach_msg_empty_rcv_t msg
;
2325 mach_no_senders_notification_t no_senders
;
2326 } *notify_message_t
;
2327 CFMachPortRef notify_port
;
2328 notify_message_t notify_msg
;
2330 notify_msg
= (notify_message_t
)malloc(sizeof(*notify_msg
));
2332 kr
= mach_msg(¬ify_msg
->msg
.header
, // msg
2333 MACH_RCV_MSG
, // options
2335 sizeof(*notify_msg
), // rcv_size
2337 MACH_MSG_TIMEOUT_NONE
, // timeout
2338 MACH_PORT_NULL
); // notify
2339 if (kr
!= KERN_SUCCESS
) {
2340 SC_log(LOG_NOTICE
, "SCDynamicStore notification handler, kr=0x%x", kr
);
2345 CFRetain(connection
);
2346 notify_port
= dispatch_get_context(source
);
2348 dispatch_group_async(group
, queue
, ^{
2349 __SCNetworkConnectionMachCallBack(notify_port
,
2351 sizeof(*notify_msg
),
2352 (void *)connection
);
2354 CFRelease(connection
);
2358 dispatch_source_set_cancel_handler(source
, ^{
2359 dispatch_release(source
);
2362 connectionPrivate
->dispatchSource
= source
;
2363 dispatch_resume(source
);
2366 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, connectionPrivate
->rlList
)) {
2368 * if we do not already have notifications scheduled with
2369 * this runLoop / runLoopMode
2371 CFRunLoopAddSource(runLoop
, connectionPrivate
->rls
, runLoopMode
);
2374 _SC_schedule(connection
, runLoop
, runLoopMode
, connectionPrivate
->rlList
);
2377 #if !TARGET_OS_SIMULATOR
2378 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2379 CFRetain(connection
);
2380 ne_session_set_event_handler(connectionPrivate
->ne_session
, __SCNetworkConnectionQueue(), ^(ne_session_event_t event
, void *event_data
) {
2381 #pragma unused(event_data)
2382 if (event
== NESessionEventStatusChanged
) {
2383 CFRetain(connection
); /* Released in __SCNetworkConnectionCallBack */
2384 pthread_mutex_lock(&connectionPrivate
->lock
);
2385 if (connectionPrivate
->rls
!= NULL
) {
2386 CFRunLoopSourceSignal(connectionPrivate
->rls
);
2387 _SC_signalRunLoop(connection
, connectionPrivate
->rls
, connectionPrivate
->rlList
);
2388 } else if (connectionPrivate
->dispatchQueue
!= NULL
) {
2389 dispatch_async(connectionPrivate
->dispatchQueue
, ^{
2390 __SCNetworkConnectionCallBack((void *)connection
);
2393 pthread_mutex_unlock(&connectionPrivate
->lock
);
2394 } else if (event
== NESessionEventCanceled
) {
2395 CFRelease(connection
);
2399 #endif /* !TARGET_OS_SIMULATOR */
2405 pthread_mutex_unlock(&connectionPrivate
->lock
);
2411 __SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection
,
2412 CFRunLoopRef runLoop
,
2413 CFStringRef runLoopMode
,
2414 dispatch_queue_t queue
)
2416 #pragma unused(queue)
2417 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2418 dispatch_group_t drainGroup
= NULL
;
2419 dispatch_queue_t drainQueue
= NULL
;
2420 int sc_status
= kSCStatusFailed
;
2423 kern_return_t status
;
2425 // hold a reference while we unschedule
2426 CFRetain(connection
);
2428 pthread_mutex_lock(&connectionPrivate
->lock
);
2430 if ((runLoop
!= NULL
) && !connectionPrivate
->scheduled
) { // if we should be scheduled (but are not)
2431 _SCErrorSet(kSCStatusInvalidArgument
);
2435 if (((runLoop
== NULL
) && (connectionPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
2436 ((runLoop
!= NULL
) && (connectionPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
2437 _SCErrorSet(kSCStatusInvalidArgument
);
2441 if (connectionPrivate
->dispatchQueue
!= NULL
) {
2442 if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2443 // cancel dispatchSource
2444 if (connectionPrivate
->dispatchSource
!= NULL
) {
2445 dispatch_source_cancel(connectionPrivate
->dispatchSource
);
2446 connectionPrivate
->dispatchSource
= NULL
;
2449 // save dispatchQueue/group, release reference when all queue'd blocks
2450 // have been processed, allow re-scheduling
2451 drainGroup
= connectionPrivate
->dispatchGroup
;
2452 connectionPrivate
->dispatchGroup
= NULL
;
2453 drainQueue
= connectionPrivate
->dispatchQueue
;
2454 connectionPrivate
->dispatchQueue
= NULL
;
2456 dispatch_release(connectionPrivate
->dispatchQueue
);
2457 connectionPrivate
->dispatchQueue
= NULL
;
2460 if (!_SC_unschedule(connection
, runLoop
, runLoopMode
, connectionPrivate
->rlList
, FALSE
)) {
2461 // if not currently scheduled on this runLoop / runLoopMode
2462 _SCErrorSet(kSCStatusFailed
);
2466 n
= CFArrayGetCount(connectionPrivate
->rlList
);
2467 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, connectionPrivate
->rlList
)) {
2469 * if we are no longer scheduled to receive notifications for
2470 * this runLoop / runLoopMode
2472 CFRunLoopRemoveSource(runLoop
, connectionPrivate
->rls
, runLoopMode
);
2475 // if *all* notifications have been unscheduled
2476 CFRelease(connectionPrivate
->rlList
);
2477 connectionPrivate
->rlList
= NULL
;
2478 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
2479 CFRelease(connectionPrivate
->rls
);
2480 connectionPrivate
->rls
= NULL
;
2486 // if *all* notifications have been unscheduled
2487 connectionPrivate
->scheduled
= FALSE
;
2489 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate
)) {
2490 #if !TARGET_OS_SIMULATOR
2491 ne_session_cancel(connectionPrivate
->ne_session
);
2492 #endif /* !TARGET_OS_SIMULATOR */
2494 mach_port_t session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
2495 if (session_port
== MACH_PORT_NULL
) {
2499 status
= pppcontroller_notification(session_port
, 0, &sc_status
);
2500 if (__SCNetworkConnectionNeedsRetry(connection
,
2501 "__SCNetworkConnectionUnscheduleFromRunLoop pppcontroller_notification()",
2504 sc_status
= kSCStatusOK
;
2505 status
= KERN_SUCCESS
;
2508 if ((status
!= KERN_SUCCESS
) || (sc_status
!= kSCStatusOK
)) {
2509 _SCErrorSet(sc_status
);
2519 pthread_mutex_unlock(&connectionPrivate
->lock
);
2521 if (drainGroup
!= NULL
) {
2522 dispatch_group_notify(drainGroup
, drainQueue
, ^{
2523 // release group/queue references
2524 dispatch_release(drainQueue
);
2525 dispatch_release(drainGroup
); // releases our connection reference
2529 // release our reference
2530 CFRelease(connection
);
2537 SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection
,
2538 CFRunLoopRef runLoop
,
2539 CFStringRef runLoopMode
)
2541 if (!isA_SCNetworkConnection(connection
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
2542 _SCErrorSet(kSCStatusInvalidArgument
);
2546 return __SCNetworkConnectionScheduleWithRunLoop(connection
, runLoop
, runLoopMode
, NULL
);
2551 SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection
,
2552 CFRunLoopRef runLoop
,
2553 CFStringRef runLoopMode
)
2555 if (!isA_SCNetworkConnection(connection
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
2556 _SCErrorSet(kSCStatusInvalidArgument
);
2560 return __SCNetworkConnectionUnscheduleFromRunLoop(connection
, runLoop
, runLoopMode
, NULL
);
2565 SCNetworkConnectionSetDispatchQueue(SCNetworkConnectionRef connection
,
2566 dispatch_queue_t queue
)
2570 if (!isA_SCNetworkConnection(connection
)) {
2571 _SCErrorSet(kSCStatusInvalidArgument
);
2575 if (queue
!= NULL
) {
2576 ok
= __SCNetworkConnectionScheduleWithRunLoop(connection
, NULL
, NULL
, queue
);
2578 ok
= __SCNetworkConnectionUnscheduleFromRunLoop(connection
, NULL
, NULL
, NULL
);
2585 /* Requires having called SCNetworkConnectionSelectServiceWithOptions previously */
2587 SCNetworkConnectionIsOnDemandSuspended(SCNetworkConnectionRef connection
)
2589 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2591 if (!isA_SCNetworkConnection(connection
)) {
2592 _SCErrorSet(kSCStatusInvalidArgument
);
2596 if (connectionPrivate
->on_demand_info
!= NULL
) {
2597 uint32_t isSuspended
= 0;
2598 CFNumberRef num
= NULL
;
2600 num
= CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCPropNetVPNOnDemandSuspended
);
2601 if (isA_CFNumber(num
) &&
2602 CFNumberGetValue(num
, kCFNumberSInt32Type
, &isSuspended
) &&
2603 (isSuspended
!= 0)) {
2608 _SCErrorSet(kSCStatusOK
);
2613 SCNetworkConnectionTriggerOnDemandIfNeeded (CFStringRef hostName
,
2614 Boolean afterDNSFail
,
2618 #if !TARGET_OS_SIMULATOR
2619 __block Boolean triggeredOnDemand
= FALSE
;
2620 struct proc_uniqidentifierinfo procu
;
2621 void *policy_match
= NULL
;
2622 char *hostname
= NULL
;
2623 pid_t pid
= getpid();
2624 uid_t uid
= geteuid();
2626 /* Require hostName, require non-root user */
2627 if (hostName
== NULL
|| geteuid() == 0) {
2631 hostname
= _SC_cfstring_to_cstring(hostName
, NULL
, 0, kCFStringEncodingUTF8
);
2633 if (proc_pidinfo(pid
, PROC_PIDUNIQIDENTIFIERINFO
, 1, &procu
, sizeof(procu
)) != sizeof(procu
)) {
2637 policy_match
= ne_session_copy_policy_match(hostname
, NULL
, NULL
, procu
.p_uuid
, procu
.p_uuid
, pid
, uid
, 0, trafficClass
);
2639 NEPolicyServiceActionType action_type
= ne_session_policy_match_get_service_action(policy_match
);
2640 if (action_type
== NESessionPolicyActionTrigger
||
2641 (afterDNSFail
&& action_type
== NESessionPolicyActionTriggerIfNeeded
)) {
2643 if (ne_session_policy_match_get_service(policy_match
, config_id
)) {
2644 xpc_object_t start_options
= xpc_dictionary_create(NULL
, NULL
, 0);
2645 if (start_options
!= NULL
) {
2646 xpc_dictionary_set_bool(start_options
, NESessionStartOptionIsOnDemandKey
, true);
2647 xpc_dictionary_set_string(start_options
, NESessionStartOptionMatchHostnameKey
, hostname
);
2649 ne_session_t new_session
= ne_session_create(config_id
, ne_session_policy_match_get_service_type(policy_match
));
2650 if (new_session
!= NULL
) {
2651 dispatch_semaphore_t wait_for_session
= dispatch_semaphore_create(0);
2652 dispatch_retain(wait_for_session
);
2653 xpc_retain(start_options
);
2654 ne_session_get_status(new_session
, __SCNetworkConnectionQueue(),
2655 ^(ne_session_status_t status
) {
2656 if (status
== NESessionStatusDisconnected
) {
2657 dispatch_retain(wait_for_session
);
2658 ne_session_set_event_handler(new_session
, __SCNetworkConnectionQueue(),
2659 ^(ne_session_event_t event
, void *event_data
) {
2660 #pragma unused(event_data)
2661 if (event
== NESessionEventStatusChanged
) {
2662 dispatch_retain(wait_for_session
);
2663 ne_session_get_status(new_session
, __SCNetworkConnectionQueue(),
2664 ^(ne_session_status_t new_status
) {
2665 if (new_status
!= NESessionStatusConnecting
) {
2666 if (status
== NESessionStatusConnected
) {
2667 triggeredOnDemand
= TRUE
;
2669 ne_session_cancel(new_session
);
2671 dispatch_release(wait_for_session
);
2673 } else if (event
== NESessionEventCanceled
) {
2674 dispatch_semaphore_signal(wait_for_session
);
2675 dispatch_release(wait_for_session
);
2678 ne_session_start_with_options(new_session
, start_options
);
2680 dispatch_semaphore_signal(wait_for_session
);
2682 dispatch_release(wait_for_session
);
2683 xpc_release(start_options
);
2685 dispatch_semaphore_wait(wait_for_session
, timeout
? dispatch_time(DISPATCH_TIME_NOW
, (int64_t)timeout
* NSEC_PER_SEC
) : DISPATCH_TIME_FOREVER
);
2686 dispatch_release(wait_for_session
);
2687 ne_session_release(new_session
);
2690 xpc_release(start_options
);
2696 CFAllocatorDeallocate(NULL
, hostname
);
2703 return triggeredOnDemand
;
2705 #pragma unused(hostName, afterDNSFail, timeout, trafficClass)
2712 SCNetworkConnectionCopyOnDemandInfo(SCNetworkConnectionRef connection
,
2713 CFStringRef
*onDemandRemoteAddress
,
2714 SCNetworkConnectionStatus
*onDemandConnectionStatus
)
2716 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2718 if (!isA_SCNetworkConnection(connection
)) {
2719 _SCErrorSet(kSCStatusInvalidArgument
);
2723 if (connectionPrivate
->service
== NULL
) {
2724 _SCErrorSet(kSCStatusConnectionNoService
);
2728 if (onDemandRemoteAddress
!= NULL
) {
2729 *onDemandRemoteAddress
= NULL
;
2732 if (onDemandConnectionStatus
!= NULL
) {
2733 *onDemandConnectionStatus
= kSCNetworkConnectionInvalid
;
2736 if (connectionPrivate
->on_demand_info
!= NULL
) {
2737 if (onDemandRemoteAddress
!= NULL
) {
2738 CFStringRef address
=
2739 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandRemoteAddress
);
2740 if (isA_CFString(address
)) {
2741 *onDemandRemoteAddress
= address
;
2742 CFRetain(*onDemandRemoteAddress
);
2746 if (onDemandConnectionStatus
!= NULL
) {
2748 CFNumberRef status_num
=
2749 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandStatus
);
2750 if (isA_CFNumber(status_num
) && CFNumberGetValue(status_num
, kCFNumberIntType
, &num
)) {
2751 *onDemandConnectionStatus
= num
;
2756 return connectionPrivate
->on_demand
;
2761 SCNetworkConnectionGetReachabilityInfo(SCNetworkConnectionRef connection
,
2762 SCNetworkReachabilityFlags
*reach_flags
,
2763 unsigned int *reach_if_index
)
2765 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2767 if (!isA_SCNetworkConnection(connection
)) {
2768 _SCErrorSet(kSCStatusInvalidArgument
);
2772 if (connectionPrivate
->service
== NULL
) {
2773 _SCErrorSet(kSCStatusConnectionNoService
);
2777 if (reach_flags
!= NULL
) {
2781 if (reach_if_index
!= NULL
) {
2782 *reach_if_index
= 0;
2785 if (connectionPrivate
->on_demand_info
!= NULL
) {
2786 if (reach_flags
!= NULL
) {
2788 CFNumberRef flags_num
=
2789 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandReachFlags
);
2790 if (isA_CFNumber(flags_num
) && CFNumberGetValue(flags_num
, kCFNumberIntType
, &num
)) {
2795 if (reach_if_index
!= NULL
) {
2797 CFNumberRef if_index_num
=
2798 CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCNetworkConnectionOnDemandReachInterfaceIndex
);
2799 if (isA_CFNumber(if_index_num
) && CFNumberGetValue(if_index_num
, kCFNumberIntType
, &num
)) {
2800 *reach_if_index
= num
;
2809 SCNetworkConnectionType
2810 SCNetworkConnectionGetType(SCNetworkConnectionRef connection
)
2812 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2814 if (!isA_SCNetworkConnection(connection
)) {
2815 _SCErrorSet(kSCStatusInvalidArgument
);
2816 return kSCNetworkConnectionTypeUnknown
;
2819 if (connectionPrivate
->service
== NULL
) {
2820 _SCErrorSet(kSCStatusConnectionNoService
);
2821 return kSCNetworkConnectionTypeUnknown
;
2824 _SCErrorSet(kSCStatusOK
);
2826 return connectionPrivate
->type
;
2831 SCNetworkConnectionCopyFlowDivertToken(SCNetworkConnectionRef connection
,
2832 CFDictionaryRef flowProperties
)
2834 #pragma unused(connection, flowProperties)
2835 _SCErrorSet(kSCStatusFailed
);
2841 SCNetworkConnectionGetServiceIdentifier (SCNetworkConnectionRef connection
)
2843 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
2844 int service_identifier
= -1;
2846 if (connectionPrivate
->service
!= NULL
) {
2847 service_identifier
= 0;
2848 if (connectionPrivate
->on_demand_info
!= NULL
) {
2849 CFNumberRef id_num
= CFDictionaryGetValue(connectionPrivate
->on_demand_info
, kSCPropNetDNSServiceIdentifier
);
2851 if (isA_CFNumber(id_num
)) {
2852 CFNumberGetValue(id_num
, kCFNumberIntType
, &service_identifier
);
2857 return service_identifier
;
2861 #if !TARGET_OS_SIMULATOR
2862 SCNetworkConnectionStatus
2863 SCNetworkConnectionGetStatusFromNEStatus(ne_session_status_t status
)
2866 case NESessionStatusInvalid
:
2867 return kSCNetworkConnectionInvalid
;
2868 case NESessionStatusDisconnected
:
2869 return kSCNetworkConnectionDisconnected
;
2870 case NESessionStatusConnecting
:
2871 case NESessionStatusReasserting
:
2872 return kSCNetworkConnectionConnecting
;
2873 case NESessionStatusConnected
:
2874 return kSCNetworkConnectionConnected
;
2875 case NESessionStatusDisconnecting
:
2876 return kSCNetworkConnectionDisconnecting
;
2879 return kSCNetworkConnectionInvalid
;
2881 #endif /* !TARGET_OS_SIMULATOR */
2885 #pragma mark User level "dial" API
2888 #define k_NetworkConnect_Notification "com.apple.networkConnect"
2889 #define k_NetworkConnect_Pref_File CFSTR("com.apple.networkConnect")
2890 #define k_InterentConnect_Pref_File CFSTR("com.apple.internetconnect")
2892 #define k_Dial_Default_Key CFSTR("ConnectByDefault") // needs to go into SC
2893 #define k_Last_Service_Id_Key CFSTR("ServiceID")
2894 #define k_Unique_Id_Key CFSTR("UniqueIdentifier")
2897 /* Private Prototypes */
2898 static Boolean
SCNetworkConnectionPrivateCopyDefaultServiceIDForDial (CFStringRef
*serviceID
);
2899 static Boolean
SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore (CFStringRef
*serviceID
);
2900 static Boolean
SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray
, CFDictionaryRef
*userOptions
);
2901 static Boolean
SCNetworkConnectionPrivateIsPPPService (CFStringRef serviceID
, CFStringRef subType1
, CFStringRef subType2
);
2902 static void addPasswordFromKeychain (CFStringRef serviceID
, CFDictionaryRef
*userOptions
);
2903 static CFStringRef
copyPasswordFromKeychain (CFStringRef uniqueID
);
2905 static int notify_userprefs_token
= -1;
2907 static CFDictionaryRef onDemand_configuration
= NULL
;
2908 static Boolean onDemand_force_refresh
= FALSE
;
2909 static pthread_mutex_t onDemand_notify_lock
= PTHREAD_MUTEX_INITIALIZER
;
2910 static int onDemand_notify_token
= -1;
2914 * return TRUE if domain1 ends with domain2, and will check for trailing "."
2916 #define WILD_CARD_MATCH_STR CFSTR("*")
2918 _SC_domainEndsWithDomain(CFStringRef compare_domain
, CFStringRef match_domain
)
2921 Boolean ret
= FALSE
;
2922 CFStringRef s1
= NULL
;
2923 Boolean s1_created
= FALSE
;
2924 CFStringRef s2
= NULL
;
2925 Boolean s2_created
= FALSE
;
2926 CFStringRef s3
= NULL
;
2928 if (CFEqual(match_domain
, WILD_CARD_MATCH_STR
)) {
2932 if (CFStringHasSuffix(compare_domain
, CFSTR("."))) {
2934 range
.length
= CFStringGetLength(compare_domain
) - 1;
2935 s1
= CFStringCreateWithSubstring(NULL
, compare_domain
, range
);
2941 s1
= compare_domain
;
2944 if (CFStringHasSuffix(match_domain
, CFSTR("."))) {
2946 range
.length
= CFStringGetLength(match_domain
) - 1;
2947 s2
= CFStringCreateWithSubstring(NULL
, match_domain
, range
);
2956 if (CFStringHasPrefix(s2
, CFSTR("*."))) {
2958 range
.length
= CFStringGetLength(s2
)-2;
2959 s3
= CFStringCreateWithSubstring(NULL
, s2
, range
);
2970 ret
= CFStringHasSuffix(s1
, s2
);
2974 if (s1_created
) CFRelease(s1
);
2975 if (s2_created
) CFRelease(s2
);
2979 static CFCharacterSetRef
2980 _SC_getNotDotOrStarCharacterSet (void)
2982 static CFCharacterSetRef notDotOrStar
= NULL
;
2983 if (notDotOrStar
== NULL
) {
2984 CFCharacterSetRef dotOrStar
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, CFSTR(".*"));
2986 notDotOrStar
= CFCharacterSetCreateInvertedSet(kCFAllocatorDefault
, dotOrStar
);
2987 CFRelease(dotOrStar
);
2990 return notDotOrStar
;
2993 static CFMutableStringRef
2994 _SC_createStringByTrimmingDotsAndStars (CFStringRef string
)
2996 CFCharacterSetRef notDotOrStar
= _SC_getNotDotOrStarCharacterSet();
2997 CFRange entireString
= CFRangeMake(0, CFStringGetLength(string
));
2998 CFMutableStringRef result
= CFStringCreateMutableCopy(kCFAllocatorDefault
, entireString
.length
, string
);
3000 CFRange end
= CFRangeMake(entireString
.length
, 0);
3002 if (CFStringFindCharacterFromSet(string
, notDotOrStar
, entireString
, 0, &start
) &&
3003 CFStringFindCharacterFromSet(string
, notDotOrStar
, entireString
, kCFCompareBackwards
, &end
)) {
3004 if (start
.location
== kCFNotFound
|| end
.location
== kCFNotFound
|| start
.location
> end
.location
) {
3010 if ((end
.location
+ 1) < entireString
.length
) {
3011 CFStringReplace(result
, CFRangeMake(end
.location
+ 1, entireString
.length
- (end
.location
+ 1)), CFSTR(""));
3013 if (start
.location
> 0) {
3014 CFStringReplace(result
, CFRangeMake(0, start
.location
), CFSTR(""));
3021 _SC_getCountOfStringInString (CFStringRef string
, CFStringRef substring
)
3024 CFArrayRef ranges
= CFStringCreateArrayWithFindResults(kCFAllocatorDefault
, string
, substring
, CFRangeMake(0, CFStringGetLength(string
)), 0);
3025 if (ranges
!= NULL
) {
3026 count
= CFArrayGetCount(ranges
);
3033 _SC_hostMatchesDomain(CFStringRef hostname
, CFStringRef domain
)
3035 Boolean result
= FALSE
;
3036 CFMutableStringRef trimmedHostname
= NULL
;
3037 CFMutableStringRef trimmedDomain
= NULL
;
3039 if (!isA_CFString(hostname
) || !isA_CFString(domain
)) {
3043 trimmedHostname
= _SC_createStringByTrimmingDotsAndStars(hostname
);
3044 trimmedDomain
= _SC_createStringByTrimmingDotsAndStars(domain
);
3046 if (!isA_CFString(trimmedHostname
) || !isA_CFString(trimmedDomain
)) {
3050 CFIndex numHostnameDots
= _SC_getCountOfStringInString(trimmedHostname
, CFSTR("."));
3051 CFIndex numDomainDots
= _SC_getCountOfStringInString(trimmedDomain
, CFSTR("."));
3052 if (numHostnameDots
== numDomainDots
) {
3053 result
= CFEqual(trimmedHostname
, trimmedDomain
);
3054 } else if (numDomainDots
> 0 && numDomainDots
< numHostnameDots
) {
3055 CFStringReplace(trimmedDomain
, CFRangeMake(0, 0), CFSTR("."));
3056 result
= CFStringHasSuffix(trimmedHostname
, trimmedDomain
);
3062 if (trimmedHostname
) {
3063 CFRelease(trimmedHostname
);
3065 if (trimmedDomain
) {
3066 CFRelease(trimmedDomain
);
3073 static CFDictionaryRef
3074 __SCNetworkConnectionCopyOnDemandConfiguration(void)
3078 uint64_t triggersCount
= 0;
3079 CFDictionaryRef configuration
;
3081 pthread_mutex_lock(&onDemand_notify_lock
);
3082 if (onDemand_notify_token
== -1) {
3083 status
= notify_register_check(kSCNETWORKCONNECTION_ONDEMAND_NOTIFY_KEY
, &onDemand_notify_token
);
3084 if (status
!= NOTIFY_STATUS_OK
) {
3085 SC_log(LOG_NOTICE
, "notify_register_check() failed, status=%d", status
);
3086 onDemand_notify_token
= -1;
3090 if (onDemand_notify_token
!= -1) {
3091 status
= notify_check(onDemand_notify_token
, &changed
);
3092 if (status
!= NOTIFY_STATUS_OK
) {
3093 SC_log(LOG_NOTICE
, "notify_check() failed, status=%d", status
);
3094 (void)notify_cancel(onDemand_notify_token
);
3095 onDemand_notify_token
= -1;
3099 if (changed
&& (onDemand_notify_token
!= -1)) {
3100 status
= notify_get_state(onDemand_notify_token
, &triggersCount
);
3101 if (status
!= NOTIFY_STATUS_OK
) {
3102 SC_log(LOG_NOTICE
, "notify_get_state() failed, status=%d", status
);
3103 (void)notify_cancel(onDemand_notify_token
);
3104 onDemand_notify_token
= -1;
3108 if (changed
|| onDemand_force_refresh
) {
3111 SC_log(LOG_INFO
, "OnDemand information %s",
3112 (onDemand_configuration
== NULL
) ? "fetched" : "updated");
3114 if (onDemand_configuration
!= NULL
) {
3115 CFRelease(onDemand_configuration
);
3116 onDemand_configuration
= NULL
;
3119 if ((triggersCount
> 0) || onDemand_force_refresh
) {
3120 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, kSCDynamicStoreDomainState
, kSCEntNetOnDemand
);
3121 onDemand_configuration
= SCDynamicStoreCopyValue(NULL
, key
);
3123 if ((onDemand_configuration
!= NULL
) && !isA_CFDictionary(onDemand_configuration
)) {
3124 CFRelease(onDemand_configuration
);
3125 onDemand_configuration
= NULL
;
3129 onDemand_force_refresh
= FALSE
;
3132 configuration
= (onDemand_configuration
!= NULL
) ? CFRetain(onDemand_configuration
) : NULL
;
3133 pthread_mutex_unlock(&onDemand_notify_lock
);
3135 return configuration
;
3141 __SCNetworkConnectionForceOnDemandConfigurationRefresh(void)
3143 pthread_mutex_lock(&onDemand_notify_lock
);
3144 onDemand_force_refresh
= TRUE
;
3145 pthread_mutex_unlock(&onDemand_notify_lock
);
3152 __SCNetworkConnectionShouldNeverMatch(CFDictionaryRef trigger
, CFStringRef hostName
, pid_t client_pid
)
3154 CFArrayRef exceptedProcesses
;
3155 CFIndex exceptedProcessesCount
;
3156 CFIndex exceptedProcessesIndex
;
3157 CFArrayRef exceptions
;
3158 CFIndex exceptionsCount
;
3159 int exceptionsIndex
;
3161 // we have a matching domain, check against exception list
3162 exceptions
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandMatchDomainsNever
);
3163 exceptionsCount
= isA_CFArray(exceptions
) ? CFArrayGetCount(exceptions
) : 0;
3164 for (exceptionsIndex
= 0; exceptionsIndex
< exceptionsCount
; exceptionsIndex
++) {
3165 CFStringRef exception
;
3167 exception
= CFArrayGetValueAtIndex(exceptions
, exceptionsIndex
);
3168 if (isA_CFString(exception
) && _SC_domainEndsWithDomain(hostName
, exception
)) {
3169 // found matching exception
3170 SC_log(LOG_INFO
, "OnDemand match exception");
3175 if (client_pid
!= 0) {
3176 exceptedProcesses
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandPluginPIDs
);
3177 exceptedProcessesCount
= isA_CFArray(exceptedProcesses
) ? CFArrayGetCount(exceptedProcesses
) : 0;
3178 for (exceptedProcessesIndex
= 0; exceptedProcessesIndex
< exceptedProcessesCount
; exceptedProcessesIndex
++) {
3182 pidRef
= CFArrayGetValueAtIndex(exceptedProcesses
, exceptedProcessesIndex
);
3183 if (isA_CFNumber(pidRef
) && CFNumberGetValue(pidRef
, kCFNumberIntType
, &pid
)) {
3184 if (pid
== client_pid
) {
3195 __SCNetworkConnectionDomainGetMatchWithParameters(CFStringRef action
, CFPropertyListRef actionParameters
, CFStringRef hostName
, CFStringRef
*probeString
)
3197 CFArrayRef actionArray
= NULL
;
3198 CFIndex actionArraySize
= 0;
3200 CFStringRef matchDomain
= NULL
;
3202 /* For now, only support EvaluateConnection, which takes a CFArray */
3203 if (!CFEqual(action
, kSCValNetVPNOnDemandRuleActionEvaluateConnection
) || !isA_CFArray(actionParameters
)) {
3207 actionArray
= (CFArrayRef
)actionParameters
;
3208 actionArraySize
= CFArrayGetCount(actionArray
);
3210 /* Process domain rules, with actions of ConnectIfNeeded and NeverConnect */
3211 for (i
= 0; i
< actionArraySize
; i
++) {
3212 CFStringRef domainAction
= NULL
;
3213 CFDictionaryRef domainRule
= CFArrayGetValueAtIndex(actionArray
, i
);
3214 CFArrayRef domains
= NULL
;
3215 CFIndex domainsCount
= 0;
3216 CFIndex domainsIndex
;
3218 if (!isA_CFDictionary(domainRule
)) {
3222 domains
= CFDictionaryGetValue(domainRule
, kSCPropNetVPNOnDemandRuleActionParametersDomains
);
3223 if (!isA_CFArray(domains
)) {
3227 domainsCount
= CFArrayGetCount(domains
);
3228 for (domainsIndex
= 0; domainsIndex
< domainsCount
; domainsIndex
++) {
3230 domain
= CFArrayGetValueAtIndex(domains
, domainsIndex
);
3231 if (isA_CFString(domain
) && _SC_domainEndsWithDomain(hostName
, domain
)) {
3232 matchDomain
= domain
;
3238 domainAction
= CFDictionaryGetValue(domainRule
, kSCPropNetVPNOnDemandRuleActionParametersDomainAction
);
3239 if (isA_CFString(domainAction
) && CFEqual(domainAction
, kSCValNetVPNOnDemandRuleActionParametersDomainActionNeverConnect
)) {
3242 /* If we found a match, save the optional probe string as well */
3244 *probeString
= CFDictionaryGetValue(domainRule
, kSCPropNetVPNOnDemandRuleActionParametersRequiredURLStringProbe
);
3255 __SCNetworkConnectionDomainGetMatch(CFDictionaryRef trigger
, CFStringRef hostName
, Boolean onDemandRetry
)
3258 CFIndex domainsCount
;
3261 CFStringRef match_domain
= NULL
;
3263 /* Old configuration: always, never, on retry lists */
3264 key
= onDemandRetry
? kSCNetworkConnectionOnDemandMatchDomainsOnRetry
: kSCNetworkConnectionOnDemandMatchDomainsAlways
;
3266 domains
= CFDictionaryGetValue(trigger
, key
);
3267 domainsCount
= isA_CFArray(domains
) ? CFArrayGetCount(domains
) : 0;
3268 for (domainsIndex
= 0; domainsIndex
< domainsCount
; domainsIndex
++) {
3271 domain
= CFArrayGetValueAtIndex(domains
, domainsIndex
);
3272 if (isA_CFString(domain
) && _SC_domainEndsWithDomain(hostName
, domain
)) {
3273 match_domain
= domain
;
3278 return match_domain
;
3283 __SCNetworkConnectionShouldAlwaysConnect(CFDictionaryRef trigger
)
3285 CFStringRef action
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleAction
);
3286 return (isA_CFString(action
) && CFEqual(action
, kSCValNetVPNOnDemandRuleActionConnect
));
3291 __SCNetworkConnectionShouldIgnoreTrigger(CFDictionaryRef trigger
)
3293 CFStringRef action
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleAction
);
3295 if (isA_CFString(action
) &&
3296 (CFEqual(action
, kSCValNetVPNOnDemandRuleActionIgnore
) ||
3297 CFEqual(action
, kSCValNetVPNOnDemandRuleActionDisconnect
))) {
3305 static CFDictionaryRef
3306 __SCNetworkConnectionCopyMatchingTriggerWithName(CFDictionaryRef configuration
,
3307 CFStringRef hostName
,
3309 Boolean onDemandRetry
,
3310 CFDictionaryRef
*match_info
,
3311 Boolean
*triggerNow
,
3312 CFStringRef
*probe_string
)
3314 CFDictionaryRef result
= NULL
;
3315 int sc_status
= kSCStatusOK
;
3316 CFArrayRef triggers
;
3317 CFIndex triggersCount
= 0;
3318 Boolean usedOnDemandRetry
= FALSE
;
3320 if (triggerNow
!= NULL
) {
3321 *triggerNow
= FALSE
;
3324 if (match_info
!= NULL
) {
3328 triggers
= CFDictionaryGetValue(configuration
, kSCNetworkConnectionOnDemandTriggers
);
3329 triggersCount
= isA_CFArray(triggers
) ? CFArrayGetCount(triggers
) : 0;
3330 for (CFIndex triggersIndex
= 0; triggersIndex
< triggersCount
; triggersIndex
++) {
3331 CFStringRef matched_domain
= NULL
;
3332 CFStringRef matched_probe_string
= NULL
;
3333 CFDictionaryRef trigger
;
3334 Boolean trigger_matched
= FALSE
;
3336 usedOnDemandRetry
= FALSE
;
3338 trigger
= CFArrayGetValueAtIndex(triggers
, triggersIndex
);
3339 if (!isA_CFDictionary(trigger
)) {
3340 // if not a valid "OnDemand" configuration
3344 if (__SCNetworkConnectionShouldAlwaysConnect(trigger
)) {
3345 /* If the trigger action is 'Connect', always match this trigger */
3346 /* First check the never match list */
3347 if (__SCNetworkConnectionShouldNeverMatch(trigger
, hostName
, client_pid
)) {
3350 trigger_matched
= TRUE
;
3351 } else if (__SCNetworkConnectionShouldIgnoreTrigger(trigger
)) {
3352 /* If the trigger action is 'Ignore' or 'Disconnect', skip this trigger */
3353 sc_status
= kSCStatusConnectionIgnore
;
3356 CFStringRef action
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleAction
);
3357 CFArrayRef actionParameters
= CFDictionaryGetValue(trigger
, kSCPropNetVPNOnDemandRuleActionParameters
);
3358 if (action
&& actionParameters
) {
3359 matched_domain
= __SCNetworkConnectionDomainGetMatchWithParameters(action
, actionParameters
, hostName
, &matched_probe_string
);
3360 usedOnDemandRetry
= TRUE
;
3362 if (onDemandRetry
) {
3363 matched_domain
= __SCNetworkConnectionDomainGetMatch(trigger
, hostName
, TRUE
);
3364 usedOnDemandRetry
= TRUE
;
3366 matched_domain
= __SCNetworkConnectionDomainGetMatch(trigger
, hostName
, FALSE
);
3367 if (matched_domain
== NULL
&& result
== NULL
) {
3368 /* Check the retry list if Always failed */
3369 matched_domain
= __SCNetworkConnectionDomainGetMatch(trigger
, hostName
, TRUE
);
3370 usedOnDemandRetry
= TRUE
;
3375 if (matched_domain
) {
3376 if (__SCNetworkConnectionShouldNeverMatch(trigger
, hostName
, client_pid
)) {
3377 matched_domain
= NULL
;
3380 trigger_matched
= TRUE
;
3385 if (trigger_matched
) {
3386 // if we have a matching domain and there were no exceptions
3387 // then we pass back the OnDemand info
3388 if (match_info
!= NULL
) {
3389 CFMutableDictionaryRef minfo
;
3390 SCNetworkConnectionType type
= kSCNetworkConnectionTypeIPLayerVPN
;
3391 CFNumberRef type_num
;
3393 if (*match_info
!= NULL
) {
3394 CFRelease(*match_info
);
3398 minfo
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
3400 &kCFTypeDictionaryKeyCallBacks
,
3401 &kCFTypeDictionaryValueCallBacks
);
3403 type_num
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &type
);
3404 CFDictionarySetValue(minfo
, kSCNetworkConnectionOnDemandMatchInfoVPNType
, type_num
);
3405 CFRelease(type_num
);
3406 if (matched_domain
) {
3407 CFDictionarySetValue(minfo
, kSCNetworkConnectionOnDemandMatchInfoDomain
, matched_domain
);
3409 CFDictionarySetValue(minfo
,
3410 kSCNetworkConnectionOnDemandMatchInfoOnRetry
,
3411 (usedOnDemandRetry
? kCFBooleanTrue
: kCFBooleanFalse
));
3413 *match_info
= minfo
;
3416 if (probe_string
!= NULL
) {
3417 if (*probe_string
!= NULL
) {
3418 CFRelease(*probe_string
);
3419 *probe_string
= NULL
;
3422 if (matched_probe_string
) {
3423 *probe_string
= CFRetain(matched_probe_string
);
3429 /* If retry was requested, or we found Always match, trigger now */
3430 if (onDemandRetry
|| !usedOnDemandRetry
) {
3431 if (triggerNow
!= NULL
) {
3437 /* If we matched the Retry list, but Always was requested,
3438 keep going through triggers in case one matches an Always */
3446 _SCErrorSet(sc_status
);
3451 static CFDictionaryRef
3452 __SCNetworkConnectionCopyTriggerWithService(CFDictionaryRef configuration
,
3453 CFStringRef service_id
)
3455 CFArrayRef triggers
;
3456 CFIndex triggersCount
;
3458 triggers
= CFDictionaryGetValue(configuration
, kSCNetworkConnectionOnDemandTriggers
);
3459 triggersCount
= isA_CFArray(triggers
) ? CFArrayGetCount(triggers
) : 0;
3460 for (CFIndex triggersIndex
= 0; triggersIndex
< triggersCount
; triggersIndex
++) {
3461 CFDictionaryRef trigger
;
3462 CFStringRef trigger_service_id
;
3464 trigger
= CFArrayGetValueAtIndex(triggers
, triggersIndex
);
3465 if (!isA_CFDictionary(trigger
)) {
3466 // if not a valid "OnDemand" configuration
3470 trigger_service_id
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandServiceID
);
3471 if (isA_CFString(trigger_service_id
) && CFEqual(trigger_service_id
, service_id
)) {
3482 __SCNetworkConnectionCopyOnDemandInfoWithName(SCDynamicStoreRef
*storeP
,
3483 CFStringRef hostName
,
3484 Boolean onDemandRetry
,
3485 CFStringRef
*connectionServiceID
,
3486 SCNetworkConnectionStatus
*connectionStatus
,
3487 CFStringRef
*vpnRemoteAddress
) /* CFDictionaryRef *info */
3489 #pragma unused(storeP)
3490 CFDictionaryRef configuration
;
3492 int sc_status
= kSCStatusOK
;
3493 CFDictionaryRef trigger
;
3494 Boolean trigger_now
= FALSE
;
3496 configuration
= __SCNetworkConnectionCopyOnDemandConfiguration();
3497 if (configuration
== NULL
) {
3498 _SCErrorSet(sc_status
);
3502 trigger
= __SCNetworkConnectionCopyMatchingTriggerWithName(configuration
, hostName
, 0, onDemandRetry
, NULL
, &trigger_now
, NULL
);
3503 if (trigger
!= NULL
&& trigger_now
) {
3505 SCNetworkConnectionStatus onDemandStatus
= kSCNetworkConnectionDisconnected
;
3509 if (!CFDictionaryGetValueIfPresent(trigger
, kSCNetworkConnectionOnDemandStatus
, (const void **)&num
) ||
3510 !isA_CFNumber(num
) ||
3511 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &onDemandStatus
)) {
3512 onDemandStatus
= kSCNetworkConnectionDisconnected
;
3514 if (connectionStatus
!= NULL
) {
3515 *connectionStatus
= onDemandStatus
;
3518 if (connectionServiceID
!= NULL
) {
3519 *connectionServiceID
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandServiceID
);
3520 *connectionServiceID
= isA_CFString(*connectionServiceID
);
3521 if ((*connectionServiceID
!= NULL
) && (CFStringGetLength(*connectionServiceID
) > 0)) {
3522 CFRetain(*connectionServiceID
);
3524 SC_log(LOG_INFO
, "OnDemand%s configuration error, no serviceID",
3525 onDemandRetry
? " (on retry)" : "");
3526 *connectionServiceID
= NULL
;
3531 if (vpnRemoteAddress
!= NULL
) {
3532 *vpnRemoteAddress
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandRemoteAddress
);
3533 *vpnRemoteAddress
= isA_CFString(*vpnRemoteAddress
);
3534 if ((*vpnRemoteAddress
!= NULL
) && (CFStringGetLength(*vpnRemoteAddress
) > 0)) {
3535 CFRetain(*vpnRemoteAddress
);
3537 SC_log(LOG_INFO
, "OnDemand%s configuration error, no server address",
3538 onDemandRetry
? " (on retry)" : "");
3539 *vpnRemoteAddress
= NULL
;
3545 if ((connectionServiceID
!= NULL
) && (*connectionServiceID
!= NULL
)) {
3546 CFRelease(*connectionServiceID
);
3547 *connectionServiceID
= NULL
;
3549 if ((vpnRemoteAddress
!= NULL
) && (*vpnRemoteAddress
!= NULL
)) {
3550 CFRelease(*vpnRemoteAddress
);
3551 *vpnRemoteAddress
= NULL
;
3553 sc_status
= kSCStatusFailed
;
3555 SC_log(LOG_INFO
, "OnDemand%s match, connection status = %d",
3556 onDemandRetry
? " (on retry)" : "",
3565 // SC_log(LOG_INFO, "OnDemand domain name(s) not matched");
3567 if (configuration
!= NULL
) CFRelease(configuration
);
3569 _SCErrorSet(sc_status
);
3575 __SCNetworkConnectionCopyUserPreferencesInternal(CFDictionaryRef selectionOptions
,
3576 CFStringRef
*serviceID
,
3577 CFDictionaryRef
*userOptions
)
3579 int prefsChanged
= 1;
3581 Boolean success
= FALSE
;
3583 if (notify_userprefs_token
== -1) {
3584 status
= notify_register_check(k_NetworkConnect_Notification
, ¬ify_userprefs_token
);
3585 if (status
!= NOTIFY_STATUS_OK
) {
3586 SC_log(LOG_NOTICE
, "notify_register_check() failed, status=%d", status
);
3587 (void)notify_cancel(notify_userprefs_token
);
3588 notify_userprefs_token
= -1;
3590 // clear the "something has changed" state
3591 (void) notify_check(notify_userprefs_token
, &prefsChanged
);
3595 if (notify_userprefs_token
!= -1) {
3596 status
= notify_check(notify_userprefs_token
, &prefsChanged
);
3597 if (status
!= NOTIFY_STATUS_OK
) {
3598 SC_log(LOG_NOTICE
, "notify_check() failed, status=%d", status
);
3599 (void)notify_cancel(notify_userprefs_token
);
3600 notify_userprefs_token
= -1;
3606 *userOptions
= NULL
;
3608 if (selectionOptions
!= NULL
) {
3609 Boolean catchAllFound
= FALSE
;
3610 CFIndex catchAllService
= 0;
3611 CFIndex catchAllConfig
= 0;
3612 CFStringRef hostName
= NULL
;
3613 CFStringRef priority
= NULL
;
3614 CFArrayRef serviceNames
= NULL
;
3615 CFDictionaryRef services
= NULL
;
3616 CFIndex serviceIndex
;
3617 CFIndex servicesCount
;
3619 hostName
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
);
3620 if (hostName
== NULL
) {
3621 hostName
= CFDictionaryGetValue(selectionOptions
, kSCPropNetPPPOnDemandHostName
);
3623 hostName
= isA_CFString(hostName
);
3624 if (hostName
== NULL
)
3625 goto done_selection
; // if no hostname for matching
3627 priority
= CFDictionaryGetValue(selectionOptions
, kSCPropNetPPPOnDemandPriority
);
3628 if (!isA_CFString(priority
))
3629 priority
= kSCValNetPPPOnDemandPriorityDefault
;
3632 if (!isA_CFArray(serviceNames
))
3633 goto done_selection
;
3636 if (!isA_CFDictionary(services
)) {
3637 goto done_selection
;
3640 servicesCount
= CFArrayGetCount(serviceNames
);
3641 for (serviceIndex
= 0; serviceIndex
< servicesCount
; serviceIndex
++) {
3642 CFIndex configIndex
;
3643 CFIndex configsCount
;
3644 CFArrayRef serviceConfigs
;
3645 CFStringRef serviceName
;
3648 serviceName
= CFArrayGetValueAtIndex(serviceNames
, serviceIndex
);
3649 if (!isA_CFString(serviceName
)) {
3653 serviceConfigs
= CFDictionaryGetValue(services
, serviceName
);
3654 if (!isA_CFArray(serviceConfigs
)) {
3658 configsCount
= CFArrayGetCount(serviceConfigs
);
3659 for (configIndex
= 0; configIndex
< configsCount
; configIndex
++) {
3660 CFNumberRef autodial
;
3661 CFDictionaryRef config
;
3662 CFDictionaryRef pppConfig
;
3664 config
= CFArrayGetValueAtIndex(serviceConfigs
, configIndex
);
3665 if (!isA_CFDictionary(config
)) {
3669 pppConfig
= CFDictionaryGetValue(config
, kSCEntNetPPP
);
3670 if (!isA_CFDictionary(pppConfig
)) {
3674 autodial
= CFDictionaryGetValue(pppConfig
, kSCPropNetPPPOnDemandEnabled
);
3675 if (!isA_CFNumber(autodial
)) {
3679 CFNumberGetValue(autodial
, kCFNumberIntType
, &val
);
3682 CFIndex domainsCount
;
3683 CFIndex domainsIndex
;
3685 /* we found an conditional connection enabled configuration */
3688 domains
= CFDictionaryGetValue(pppConfig
, kSCPropNetPPPOnDemandDomains
);
3689 if (!isA_CFArray(domains
)) {
3693 domainsCount
= CFArrayGetCount(domains
);
3694 for (domainsIndex
= 0; domainsIndex
< domainsCount
; domainsIndex
++) {
3697 domain
= CFArrayGetValueAtIndex(domains
, domainsIndex
);
3698 if (!isA_CFString(domain
)) {
3702 if (!catchAllFound
&&
3703 (CFStringCompare(domain
, CFSTR(""), 0) == kCFCompareEqualTo
3704 || CFStringCompare(domain
, CFSTR("."), 0) == kCFCompareEqualTo
))
3706 // found a catch all
3707 catchAllFound
= TRUE
;
3708 catchAllService
= serviceIndex
;
3709 catchAllConfig
= configIndex
;
3712 if (_SC_domainEndsWithDomain(hostName
, domain
)) {
3713 // found matching configuration
3714 *serviceID
= serviceName
;
3715 CFRetain(*serviceID
);
3716 *userOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, config
);
3717 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
3718 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCPropNetPPPOnDemandPriority
, priority
);
3719 addPasswordFromKeychain(*serviceID
, userOptions
);
3721 goto done_selection
;
3728 // config not found, do we have a catchall ?
3729 if (catchAllFound
) {
3730 CFDictionaryRef config
;
3731 CFArrayRef serviceConfigs
;
3732 CFStringRef serviceName
;
3734 serviceName
= CFArrayGetValueAtIndex(serviceNames
, catchAllService
);
3735 serviceConfigs
= CFDictionaryGetValue(services
, serviceName
);
3736 config
= CFArrayGetValueAtIndex(serviceConfigs
, catchAllConfig
);
3738 *serviceID
= serviceName
;
3739 CFRetain(*serviceID
);
3740 *userOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, config
);
3741 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
3742 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCPropNetPPPOnDemandPriority
, priority
);
3743 addPasswordFromKeychain(*serviceID
, userOptions
);
3745 goto done_selection
;
3751 CFRelease(serviceNames
);
3754 CFRelease(services
);
3758 SC_log(LOG_INFO
, "SCNetworkConnectionCopyUserPreferences %s", success
? "succeeded" : "failed");
3759 SC_log(LOG_INFO
, "Selection options: %@", selectionOptions
);
3765 /* we don't have selection options */
3767 // (1) Figure out which service ID we care about, allocate it into passed "serviceID"
3768 success
= SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(serviceID
);
3770 if (success
&& (*serviceID
!= NULL
)) {
3771 // (2) Get the list of user data for this service ID
3772 CFPropertyListRef userServices
= NULL
;
3775 // (3) We are expecting an array if the user has defined records for this service ID or NULL if the user hasn't
3776 if (userServices
!= NULL
) {
3777 if (isA_CFArray(userServices
)) {
3778 // (4) Get the default set of user options for this service
3779 success
= SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray((CFArrayRef
)userServices
,
3782 addPasswordFromKeychain(*serviceID
, userOptions
);
3785 SC_log(LOG_INFO
, "Error, userServices are not of type CFArray!");
3788 CFRelease(userServices
); // this is OK because SCNetworkConnectionPrivateISExpectedCFType() checks for NULL
3793 SC_log(LOG_INFO
, "SCNetworkConnectionCopyUserPreferences %@, no selection options",
3794 success
? CFSTR("succeeded") : CFSTR("failed"));
3802 SCNetworkConnectionCopyUserPreferences(CFDictionaryRef selectionOptions
,
3803 CFStringRef
*serviceID
,
3804 CFDictionaryRef
*userOptions
)
3806 Boolean success
= FALSE
;
3808 /* initialize runtime */
3809 pthread_once(&initialized
, __SCNetworkConnectionInitialize
);
3811 /* first check for new VPN OnDemand style */
3812 if (selectionOptions
!= NULL
) {
3813 CFStringRef hostName
;
3815 hostName
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
);
3816 if (isA_CFString(hostName
)) {
3817 CFStringRef connectionServiceID
= NULL
;
3818 SCNetworkConnectionStatus connectionStatus
= kSCNetworkConnectionInvalid
;
3819 Boolean onDemandRetry
;
3822 val
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandRetry
);
3823 onDemandRetry
= isA_CFBoolean(val
) ? CFBooleanGetValue(val
) : TRUE
;
3825 success
= __SCNetworkConnectionCopyOnDemandInfoWithName(NULL
,
3828 &connectionServiceID
,
3832 SC_log(LOG_INFO
, "__SCNetworkConnectionCopyOnDemandInfoWithName: return %d, status %d",
3838 // if the hostname matches an OnDemand domain
3839 if (connectionStatus
== kSCNetworkConnectionConnected
) {
3840 // if we are already connected
3841 if (connectionServiceID
!= NULL
) {
3842 CFRelease(connectionServiceID
);
3847 *serviceID
= connectionServiceID
;
3848 *userOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3849 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
3851 } else if (!onDemandRetry
) {
3852 // if the hostname does not match an OnDemand domain and we have
3853 // not yet issued an initial DNS query (i.e. it's not a query
3854 // being retried after the VPN has been established) then we're
3861 return __SCNetworkConnectionCopyUserPreferencesInternal(selectionOptions
, serviceID
, userOptions
);
3866 SCNetworkConnectionOnDemandShouldRetryOnFailure (SCNetworkConnectionRef connection
)
3868 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
3869 CFDictionaryRef match_info
= NULL
;
3871 if (!isA_SCNetworkConnection(connection
)) {
3872 _SCErrorSet(kSCStatusInvalidArgument
);
3876 if (connectionPrivate
->service
== NULL
) {
3877 _SCErrorSet(kSCStatusConnectionNoService
);
3881 if (isA_CFDictionary(connectionPrivate
->on_demand_user_options
)) {
3882 match_info
= CFDictionaryGetValue(connectionPrivate
->on_demand_user_options
, kSCNetworkConnectionSelectionOptionOnDemandMatchInfo
);
3883 if (isA_CFDictionary(match_info
)) {
3884 CFBooleanRef onRetry
= CFDictionaryGetValue(match_info
, kSCNetworkConnectionOnDemandMatchInfoOnRetry
);
3885 if (isA_CFBoolean(onRetry
)) {
3886 return CFBooleanGetValue(onRetry
);
3896 // Mask is optional in routes dictionary; if not present, whole addresses are matched
3898 __SCNetworkConnectionIPv4AddressMatchesRoutes (struct sockaddr_in
*addr_in
, CFDictionaryRef routes
)
3902 CFDataRef maskData
= NULL
;
3903 struct in_addr
*maskDataArray
;
3904 CFDataRef routeaddrData
= NULL
;
3905 struct in_addr
*routeaddrDataArray
;
3907 if (!isA_CFDictionary(routes
)) {
3911 routeaddrData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoAddresses
);
3912 maskData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoMasks
);
3914 /* routeaddrData and maskData are packed arrays of addresses; make sure they have the same length */
3915 if (!isA_CFData(routeaddrData
) || (maskData
&& (!isA_CFData(maskData
) || CFDataGetLength(routeaddrData
) != CFDataGetLength(maskData
)))) {
3919 routeaddrDataArray
= (struct in_addr
*)(void*)CFDataGetBytePtr(routeaddrData
);
3921 maskDataArray
= (struct in_addr
*)(void*)CFDataGetBytePtr(maskData
);
3924 count
= CFDataGetLength(routeaddrData
) / sizeof(struct in_addr
);
3925 for (i
=0; i
<count
; i
++) {
3926 struct in_addr routeAddr
= *routeaddrDataArray
;
3929 struct in_addr mask
= *maskDataArray
;
3931 if ((addr_in
->sin_addr
.s_addr
& mask
.s_addr
) == (routeAddr
.s_addr
& mask
.s_addr
)) {
3936 if (addr_in
->sin_addr
.s_addr
== routeAddr
.s_addr
) {
3940 routeaddrDataArray
++;
3947 __SCNetworkConnectionMaskIPv6Address(struct in6_addr
*addr
, struct in6_addr
*mask
)
3949 for (size_t i
= 0; i
< sizeof(struct in6_addr
); i
++)
3950 addr
->s6_addr
[i
] &= mask
->s6_addr
[i
];
3954 // Mask is optional in routes dictionary; if not present, whole addresses are matched
3956 __SCNetworkConnectionIPv6AddressMatchesRoutes (struct sockaddr_in6
*addr_in6
, CFDictionaryRef routes
)
3960 CFDataRef maskData
= NULL
;
3961 struct in6_addr
*maskDataArray
;
3962 CFDataRef routeaddrData
= NULL
;
3963 struct in6_addr
*routeaddrDataArray
;
3965 if (!isA_CFDictionary(routes
)) {
3969 routeaddrData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoAddresses
);
3970 maskData
= CFDictionaryGetValue(routes
, kSCNetworkConnectionNetworkInfoMasks
);
3972 /* routeaddrData and maskData are packed arrays of addresses; make sure they have the same length */
3973 if (!isA_CFData(routeaddrData
) || (maskData
&& (!isA_CFData(maskData
) || CFDataGetLength(routeaddrData
) != CFDataGetLength(maskData
)))) {
3977 routeaddrDataArray
= (struct in6_addr
*)(void*)CFDataGetBytePtr(routeaddrData
);
3979 maskDataArray
= (struct in6_addr
*)(void*)CFDataGetBytePtr(maskData
);
3982 count
= CFDataGetLength(routeaddrData
) / sizeof(struct in6_addr
);
3983 for (i
=0; i
<count
; i
++) {
3985 struct in6_addr cmpAddr
;
3986 struct in6_addr
*mask
= maskDataArray
;
3987 struct in6_addr routeAddr
;
3989 memcpy(&routeAddr
, routeaddrDataArray
, sizeof(routeAddr
));
3990 memcpy(&cmpAddr
, &addr_in6
->sin6_addr
, sizeof(cmpAddr
));
3991 __SCNetworkConnectionMaskIPv6Address(&routeAddr
, mask
);
3992 __SCNetworkConnectionMaskIPv6Address(&cmpAddr
, mask
);
3994 if (!memcmp(&routeAddr
, &cmpAddr
, sizeof(routeAddr
))) {
3998 if (!memcmp(routeaddrDataArray
, &addr_in6
->sin6_addr
, sizeof(struct in6_addr
))) {
4003 routeaddrDataArray
++;
4010 __SCNetworkConnectionAddressMatchesRedirectedDNS(CFDictionaryRef trigger
, const struct sockaddr
*input_addr
)
4012 CFBooleanRef redirectedRef
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandDNSRedirectDetected
);
4014 if (isA_CFBoolean(redirectedRef
) && CFBooleanGetValue(redirectedRef
)) {
4015 /* DNS is redirected. Look for address list. */
4016 CFDictionaryRef redirectedAddressesRef
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandDNSRedirectedAddresses
);
4018 if (isA_CFDictionary(redirectedAddressesRef
)) {
4019 if (input_addr
->sa_family
== AF_INET
) {
4020 return __SCNetworkConnectionIPv4AddressMatchesRoutes((struct sockaddr_in
*)(void*)input_addr
, CFDictionaryGetValue(redirectedAddressesRef
, kSCNetworkConnectionNetworkInfoIPv4
));
4021 } else if (input_addr
->sa_family
== AF_INET6
) {
4022 return __SCNetworkConnectionIPv6AddressMatchesRoutes((struct sockaddr_in6
*)(void*)input_addr
, CFDictionaryGetValue(redirectedAddressesRef
, kSCNetworkConnectionNetworkInfoIPv6
));
4030 /* If the required probe has failed, we need to tunnel the address. Equivalent to redirected DNS. */
4032 __SCNetworkConnectionRequiredProbeFailed (CFDictionaryRef trigger
, CFStringRef probeString
)
4034 CFDictionaryRef probeResults
= NULL
;
4036 if (!isA_CFString(probeString
)) {
4040 probeResults
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandProbeResults
);
4041 if (!isA_CFDictionary(probeResults
)) {
4045 CFBooleanRef result
= CFDictionaryGetValue(probeResults
, probeString
);
4047 /* Only a value of kCFBooleanFalse marks the probe as failed */
4048 return (isA_CFBoolean(result
) && !CFBooleanGetValue(result
));
4052 SCNetworkConnectionCanTunnelAddress (SCNetworkConnectionRef connection
, const struct sockaddr
*address
, Boolean
*startImmediately
)
4054 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
4055 CFStringRef serviceID
= NULL
;
4056 CFDictionaryRef configuration
= NULL
;
4057 CFDictionaryRef trigger
= NULL
;
4058 CFDictionaryRef tunneledNetworks
= NULL
;
4059 sa_family_t address_family
= AF_UNSPEC
;
4060 Boolean success
= FALSE
;
4062 if (startImmediately
) {
4063 *startImmediately
= FALSE
;
4066 if (address
== NULL
) {
4070 address_family
= address
->sa_family
;
4071 if (address_family
!= AF_INET
&& address_family
!= AF_INET6
) {
4075 if (!isA_SCNetworkConnection(connection
)) {
4076 _SCErrorSet(kSCStatusInvalidArgument
);
4080 if (connectionPrivate
->service
== NULL
) {
4081 _SCErrorSet(kSCStatusConnectionNoService
);
4085 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
4086 if (!isA_CFString(serviceID
)) {
4090 configuration
= __SCNetworkConnectionCopyOnDemandConfiguration();
4091 if (configuration
== NULL
) {
4095 trigger
= __SCNetworkConnectionCopyTriggerWithService(configuration
, serviceID
);
4096 if (trigger
== NULL
) {
4100 if (__SCNetworkConnectionRequiredProbeFailed(trigger
, connectionPrivate
->on_demand_required_probe
)) {
4101 /* If probe failed, we can't trust DNS - connect now */
4102 if (startImmediately
) {
4103 *startImmediately
= TRUE
;
4109 if (__SCNetworkConnectionAddressMatchesRedirectedDNS(trigger
, address
)) {
4110 if (startImmediately
) {
4111 *startImmediately
= TRUE
;
4117 tunneledNetworks
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandTunneledNetworks
);
4118 if (!isA_CFDictionary(tunneledNetworks
)) {
4122 if (address_family
== AF_INET
) {
4123 CFDictionaryRef ip_dict
;
4124 Boolean matches
= FALSE
;
4125 struct sockaddr_in
*addr_in
= (struct sockaddr_in
*)(void*)address
;
4127 ip_dict
= CFDictionaryGetValue(tunneledNetworks
, kSCNetworkConnectionNetworkInfoIPv4
);
4128 if (!isA_CFDictionary(ip_dict
)) {
4132 matches
= __SCNetworkConnectionIPv4AddressMatchesRoutes(addr_in
, CFDictionaryGetValue(ip_dict
, kSCNetworkConnectionNetworkInfoIncludedRoutes
));
4135 if (!__SCNetworkConnectionIPv4AddressMatchesRoutes(addr_in
, CFDictionaryGetValue(ip_dict
, kSCNetworkConnectionNetworkInfoExcludedRoutes
))) {
4141 CFDictionaryRef ip6_dict
;
4142 Boolean matches
= FALSE
;
4143 struct sockaddr_in6
*addr_in6
= (struct sockaddr_in6
*)(void*)address
;
4145 ip6_dict
= CFDictionaryGetValue(tunneledNetworks
, kSCNetworkConnectionNetworkInfoIPv6
);
4146 if (!isA_CFDictionary(ip6_dict
)) {
4150 matches
= __SCNetworkConnectionIPv6AddressMatchesRoutes(addr_in6
, CFDictionaryGetValue(ip6_dict
, kSCNetworkConnectionNetworkInfoIncludedRoutes
));
4153 if (!__SCNetworkConnectionIPv6AddressMatchesRoutes(addr_in6
, CFDictionaryGetValue(ip6_dict
, kSCNetworkConnectionNetworkInfoExcludedRoutes
))) {
4160 if (configuration
) {
4161 CFRelease(configuration
);
4170 SCNetworkConnectionSelectServiceWithOptions(SCNetworkConnectionRef connection
, CFDictionaryRef selectionOptions
)
4172 CFStringRef account_identifier
= NULL
;
4173 CFDictionaryRef configuration
= NULL
;
4174 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
4175 CFDictionaryRef found_trigger
= NULL
;
4176 CFStringRef host_name
= NULL
;
4177 Boolean is_retry
= TRUE
;
4178 CFDictionaryRef match_info
= NULL
;
4179 CFMutableDictionaryRef new_user_options
= NULL
;
4180 SCNetworkConnectionStatus on_demand_status
= kSCNetworkConnectionInvalid
;
4181 CFStringRef requiredProbe
= NULL
;
4182 CFStringRef service_id
= NULL
;
4183 Boolean skip_prefs
= FALSE
;
4184 Boolean success
= TRUE
;
4185 CFDictionaryRef user_options
= NULL
;
4187 if (!isA_SCNetworkConnection(connection
)) {
4188 _SCErrorSet(kSCStatusInvalidArgument
);
4193 /* Can't call this on a connection that is already associated with a service */
4194 if (connectionPrivate
->service
!= NULL
) {
4195 _SCErrorSet(kSCStatusInvalidArgument
);
4200 if (isA_CFDictionary(selectionOptions
)) {
4201 CFBooleanRef no_user_prefs
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionNoUserPrefs
);
4202 CFBooleanRef retry
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandRetry
);
4204 account_identifier
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandAccountIdentifier
);
4205 host_name
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
);
4206 skip_prefs
= (isA_CFBoolean(no_user_prefs
) && CFBooleanGetValue(no_user_prefs
));
4208 if (isA_CFBoolean(retry
)) {
4209 is_retry
= CFBooleanGetValue(retry
);
4213 configuration
= __SCNetworkConnectionCopyOnDemandConfiguration();
4215 /* First, check for a match with the App Layer rules */
4216 service_id
= VPNAppLayerCopyMatchingService(connectionPrivate
->client_audit_token
,
4217 connectionPrivate
->client_pid
,
4218 connectionPrivate
->client_uuid
,
4219 connectionPrivate
->client_bundle_id
,
4223 if (service_id
!= NULL
) {
4224 Boolean use_app_layer
= TRUE
;
4226 if (isA_CFDictionary(configuration
)) {
4227 found_trigger
= __SCNetworkConnectionCopyTriggerWithService(configuration
, service_id
);
4228 if (found_trigger
!= NULL
) {
4229 CFNumberRef status_num
;
4231 if (!CFDictionaryGetValueIfPresent(found_trigger
,
4232 kSCNetworkConnectionOnDemandStatus
,
4233 (const void **) &status_num
) ||
4234 !isA_CFNumber(status_num
) ||
4235 !CFNumberGetValue(status_num
, kCFNumberSInt32Type
, &on_demand_status
)) {
4236 on_demand_status
= kSCNetworkConnectionInvalid
;
4240 * If the trigger should be ignored, still use App Layer VPN if it is already connected or
4241 * is in the process of connecting.
4243 if (__SCNetworkConnectionShouldIgnoreTrigger(found_trigger
) &&
4244 (on_demand_status
!= kSCNetworkConnectionConnecting
) &&
4245 (on_demand_status
!= kSCNetworkConnectionConnected
)) {
4246 use_app_layer
= FALSE
;
4251 if (use_app_layer
) {
4252 /* If this is not the 'OnRetry' call, and the service has not yet started, the match may need to return false */
4254 match_info
!= NULL
&&
4255 on_demand_status
!= kSCNetworkConnectionConnecting
&&
4256 on_demand_status
!= kSCNetworkConnectionConnected
) {
4257 CFBooleanRef matchedOnRetry
= CFDictionaryGetValue(match_info
, kSCNetworkConnectionOnDemandMatchInfoOnRetry
);
4258 if (matchedOnRetry
&& CFBooleanGetValue(matchedOnRetry
)) {
4259 /* Don't return that we matched always; wait for SCNetworkConnectionOnDemandShouldRetryOnFailure */
4263 connectionPrivate
->type
= kSCNetworkConnectionTypeAppLayerVPN
;
4266 CFRelease(service_id
);
4268 if (match_info
!= NULL
) {
4269 CFRelease(match_info
);
4272 if (found_trigger
!= NULL
) {
4273 CFRelease(found_trigger
);
4274 found_trigger
= NULL
;
4279 /* Next, check the IP layer rules */
4280 if (isA_CFDictionary(configuration
) && host_name
!= NULL
) {
4281 Boolean triggerNow
= FALSE
;
4283 found_trigger
= __SCNetworkConnectionCopyMatchingTriggerWithName(configuration
, host_name
, connectionPrivate
->client_pid
, is_retry
, &match_info
, &triggerNow
, &requiredProbe
);
4284 if (found_trigger
!= NULL
) {
4285 service_id
= CFDictionaryGetValue(found_trigger
, kSCNetworkConnectionOnDemandServiceID
);
4286 if (isA_CFString(service_id
)) {
4287 CFRetain(service_id
);
4288 connectionPrivate
->type
= kSCNetworkConnectionTypeIPLayerVPN
;
4296 } else if (!is_retry
) {
4300 if (match_info
!= NULL
) {
4301 CFRelease(match_info
);
4306 /* Next, check the user preferences */
4307 if (!skip_prefs
&& __SCNetworkConnectionCopyUserPreferencesInternal(selectionOptions
, &service_id
, &user_options
)) {
4308 CFMutableDictionaryRef minfo
;
4309 CFNumberRef type_num
;
4311 if (isA_CFDictionary(configuration
)) {
4312 found_trigger
= __SCNetworkConnectionCopyTriggerWithService(configuration
, service_id
);
4314 connectionPrivate
->type
= kSCNetworkConnectionTypePPP
;
4316 minfo
= CFDictionaryCreateMutable(NULL
,
4318 &kCFTypeDictionaryKeyCallBacks
,
4319 &kCFTypeDictionaryValueCallBacks
);
4320 type_num
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &connectionPrivate
->type
);
4321 CFDictionarySetValue(minfo
, kSCNetworkConnectionOnDemandMatchInfoVPNType
, type_num
);
4322 CFRelease(type_num
);
4328 if (service_id
== NULL
) {
4329 _SCErrorSet(kSCStatusOK
);
4334 connectionPrivate
->service
= _SCNetworkServiceCopyActive(NULL
, service_id
);
4335 if (connectionPrivate
->service
== NULL
) {
4336 _SCErrorSet(kSCStatusOK
);
4341 if (found_trigger
!= NULL
) {
4342 if (connectionPrivate
->on_demand_info
) {
4343 CFRelease(connectionPrivate
->on_demand_info
);
4345 connectionPrivate
->on_demand_info
= found_trigger
;
4346 CFRetain(connectionPrivate
->on_demand_info
);
4348 if (on_demand_status
== kSCNetworkConnectionInvalid
) {
4349 CFNumberRef status_num
;
4351 if (!CFDictionaryGetValueIfPresent(found_trigger
,
4352 kSCNetworkConnectionOnDemandStatus
,
4353 (const void **) &status_num
) ||
4354 !isA_CFNumber(status_num
) ||
4355 !CFNumberGetValue(status_num
, kCFNumberSInt32Type
, &on_demand_status
)) {
4356 on_demand_status
= kSCNetworkConnectionInvalid
;
4360 if (on_demand_status
!= kSCNetworkConnectionConnected
) {
4361 if (connectionPrivate
->type
== kSCNetworkConnectionTypeAppLayerVPN
) {
4362 /* Check App Layer OnDemand flag */
4363 CFBooleanRef app_on_demand_enabled
=
4364 CFDictionaryGetValue(found_trigger
, kSCNetworkConnectionOnDemandMatchAppEnabled
);
4365 if (isA_CFBoolean(app_on_demand_enabled
) && CFBooleanGetValue(app_on_demand_enabled
)) {
4366 connectionPrivate
->on_demand
= TRUE
;
4369 connectionPrivate
->on_demand
= TRUE
;
4372 } else if (connectionPrivate
->type
== kSCNetworkConnectionTypePPP
) {
4373 /* If we got the service from __SCNetworkConnectionCopyUserPreferencesInternal, then it's on demand */
4374 connectionPrivate
->on_demand
= TRUE
;
4377 if (user_options
== NULL
) {
4378 new_user_options
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
4380 &kCFTypeDictionaryKeyCallBacks
,
4381 &kCFTypeDictionaryValueCallBacks
);
4383 new_user_options
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, user_options
);
4386 if (host_name
!= NULL
) {
4387 CFDictionarySetValue(new_user_options
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, host_name
);
4390 if (connectionPrivate
->on_demand
&& match_info
!= NULL
) {
4391 CFDictionarySetValue(new_user_options
, kSCNetworkConnectionSelectionOptionOnDemandMatchInfo
, match_info
);
4394 connectionPrivate
->on_demand_user_options
= new_user_options
;
4395 CFRetain(connectionPrivate
->on_demand_user_options
);
4397 if (requiredProbe
) {
4398 connectionPrivate
->on_demand_required_probe
= requiredProbe
;
4399 CFRetain(connectionPrivate
->on_demand_required_probe
);
4403 if (service_id
!= NULL
) {
4404 CFRelease(service_id
);
4407 if (configuration
!= NULL
) {
4408 CFRelease(configuration
);
4411 if (found_trigger
!= NULL
) {
4412 CFRelease(found_trigger
);
4415 if (user_options
!= NULL
) {
4416 CFRelease(user_options
);
4419 if (new_user_options
!= NULL
) {
4420 CFRelease(new_user_options
);
4423 if (match_info
!= NULL
) {
4424 CFRelease(match_info
);
4427 if (requiredProbe
!= NULL
) {
4428 CFRelease(requiredProbe
);
4434 //*******************************************************************************************
4435 // SCNetworkConnectionPrivateCopyDefaultServiceIDForDial
4436 // ----------------------------------------------------
4437 // Try to find the service id to connect
4438 // (1) Start by looking at the last service used in Network Pref / Network menu extra
4439 // (2) If Network Pref / Network menu extra has not been used, find the PPP service
4440 // with the highest ordering
4441 //********************************************************************************************
4443 SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(CFStringRef
*serviceID
)
4445 Boolean foundService
= FALSE
;
4446 CFPropertyListRef lastServiceSelectedInIC
= NULL
;
4449 // we found the service the user last had open in IC
4450 if (lastServiceSelectedInIC
!= NULL
) {
4451 // make sure its a PPP service
4452 if (SCNetworkConnectionPrivateIsPPPService(lastServiceSelectedInIC
, kSCValNetInterfaceSubTypePPPSerial
, kSCValNetInterfaceSubTypePPPoE
)) {
4453 // make sure the service that we found is valid
4454 CFDictionaryRef dict
;
4457 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4458 kSCDynamicStoreDomainSetup
,
4459 lastServiceSelectedInIC
,
4460 kSCEntNetInterface
);
4461 dict
= SCDynamicStoreCopyValue(NULL
, key
);
4465 *serviceID
= CFRetain(lastServiceSelectedInIC
);
4466 foundService
= TRUE
;
4469 CFRelease(lastServiceSelectedInIC
);
4472 if (!foundService
) {
4473 foundService
= SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(serviceID
);
4476 return foundService
;
4479 //********************************************************************************
4480 // SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore
4481 // -------------------------------------------------------
4482 // Find the highest ordered PPP service in the dynamic store
4483 //********************************************************************************
4485 SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(CFStringRef
*serviceID
)
4487 CFDictionaryRef dict
= NULL
;
4488 CFStringRef key
= NULL
;
4489 CFArrayRef serviceIDs
= NULL
;
4490 Boolean success
= FALSE
;
4498 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, kSCDynamicStoreDomainSetup
, kSCEntNetIPv4
);
4500 fprintf(stderr
, "Error, Setup Key == NULL!\n");
4504 dict
= SCDynamicStoreCopyValue(NULL
, key
);
4505 if (!isA_CFDictionary(dict
)) {
4506 fprintf(stderr
, "no global IPv4 entity\n");
4510 serviceIDs
= CFDictionaryGetValue(dict
, kSCPropNetServiceOrder
); // array of service id's
4511 if (!isA_CFArray(serviceIDs
)) {
4512 fprintf(stderr
, "service order not specified\n");
4516 count
= CFArrayGetCount(serviceIDs
);
4517 for (i
= 0; i
< count
; i
++) {
4518 CFStringRef service
= CFArrayGetValueAtIndex(serviceIDs
, i
);
4520 if (SCNetworkConnectionPrivateIsPPPService(service
, kSCValNetInterfaceSubTypePPPSerial
, kSCValNetInterfaceSubTypePPPoE
)) {
4521 *serviceID
= CFRetain(service
);
4528 if (key
!= NULL
) CFRelease(key
);
4529 if (dict
!= NULL
) CFRelease(dict
);
4534 //********************************************************************************
4535 // SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray
4536 // ---------------------------------------------------------
4537 // Copy over user preferences for a particular service if they exist
4538 //********************************************************************************
4540 SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray
, CFDictionaryRef
*userOptions
)
4542 CFIndex count
= CFArrayGetCount(userOptionsArray
);
4545 for (i
= 0; i
< count
; i
++) {
4546 // (1) Find the dictionary
4547 CFPropertyListRef propertyList
= CFArrayGetValueAtIndex(userOptionsArray
, i
);
4549 if (isA_CFDictionary(propertyList
) != NULL
) {
4550 // See if there's a value for dial on demand
4551 CFPropertyListRef value
;
4553 value
= CFDictionaryGetValue((CFDictionaryRef
)propertyList
, k_Dial_Default_Key
);
4554 if (isA_CFBoolean(value
) != NULL
) {
4555 if (CFBooleanGetValue(value
)) {
4556 // we found the default user options
4557 *userOptions
= CFDictionaryCreateCopy(NULL
,
4558 (CFDictionaryRef
)propertyList
);
4568 //********************************************************************************
4569 // SCNetworkConnectionPrivateIsServiceType
4570 // --------------------------------------
4571 // Check and see if the service is a PPP service of the given types
4572 //********************************************************************************
4574 SCNetworkConnectionPrivateIsPPPService(CFStringRef serviceID
, CFStringRef subType1
, CFStringRef subType2
)
4576 CFStringRef entityKey
;
4577 Boolean isPPPService
= FALSE
;
4578 Boolean isMatchingSubType
= FALSE
;
4579 CFDictionaryRef serviceDict
;
4581 entityKey
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4582 kSCDynamicStoreDomainSetup
,
4584 kSCEntNetInterface
);
4585 if (entityKey
== NULL
) {
4589 serviceDict
= SCDynamicStoreCopyValue(NULL
, entityKey
);
4590 if (serviceDict
!= NULL
) {
4591 if (isA_CFDictionary(serviceDict
)) {
4593 CFStringRef subtype
;
4595 type
= CFDictionaryGetValue(serviceDict
, kSCPropNetInterfaceType
);
4596 if (isA_CFString(type
)) {
4597 isPPPService
= CFEqual(type
, kSCValNetInterfaceTypePPP
);
4600 subtype
= CFDictionaryGetValue(serviceDict
, kSCPropNetInterfaceSubType
);
4601 if (isA_CFString(subtype
)) {
4602 isMatchingSubType
= CFEqual(subtype
, subType1
);
4603 if (!isMatchingSubType
&& subType2
)
4604 isMatchingSubType
= CFEqual(subtype
, subType2
);
4607 CFRelease(serviceDict
);
4609 CFRelease(entityKey
);
4611 return (isPPPService
&& isMatchingSubType
);
4614 //********************************************************************************
4615 // addPasswordFromKeychain
4616 // --------------------------------------
4617 // Get the password and shared secret out of the keychain and add
4618 // them to the PPP and IPSec dictionaries
4619 //********************************************************************************
4621 addPasswordFromKeychain(CFStringRef serviceID
, CFDictionaryRef
*userOptions
)
4623 CFPropertyListRef uniqueID
;
4624 CFStringRef password
;
4625 CFStringRef sharedsecret
= NULL
;
4627 /* user options must exist */
4628 if (*userOptions
== NULL
)
4631 /* first, get the unique identifier used to store passwords in the keychain */
4632 uniqueID
= CFDictionaryGetValue(*userOptions
, k_Unique_Id_Key
);
4633 if (!isA_CFString(uniqueID
))
4636 /* first, get the PPP password */
4637 password
= copyPasswordFromKeychain(uniqueID
);
4639 /* then, if necessary, get the IPSec Shared Secret */
4640 if (SCNetworkConnectionPrivateIsPPPService(serviceID
, kSCValNetInterfaceSubTypeL2TP
, 0)) {
4641 CFMutableStringRef uniqueIDSS
;
4643 uniqueIDSS
= CFStringCreateMutableCopy(NULL
, 0, uniqueID
);
4644 CFStringAppend(uniqueIDSS
, CFSTR(".SS"));
4645 sharedsecret
= copyPasswordFromKeychain(uniqueIDSS
);
4646 CFRelease(uniqueIDSS
);
4649 /* did we find our information in the key chain ? */
4650 if ((password
!= NULL
) || (sharedsecret
!= NULL
)) {
4651 CFMutableDictionaryRef newOptions
;
4653 newOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, *userOptions
);
4656 if (password
!= NULL
) {
4657 CFDictionaryRef entity
;
4658 CFMutableDictionaryRef newEntity
;
4660 entity
= CFDictionaryGetValue(*userOptions
, kSCEntNetPPP
);
4661 if (isA_CFDictionary(entity
))
4662 newEntity
= CFDictionaryCreateMutableCopy(NULL
, 0, entity
);
4664 newEntity
= CFDictionaryCreateMutable(NULL
,
4666 &kCFTypeDictionaryKeyCallBacks
,
4667 &kCFTypeDictionaryValueCallBacks
);
4670 /* set the PPP password */
4671 CFDictionarySetValue(newEntity
, kSCPropNetPPPAuthPassword
, uniqueID
);
4672 CFDictionarySetValue(newEntity
, kSCPropNetPPPAuthPasswordEncryption
, kSCValNetPPPAuthPasswordEncryptionKeychain
);
4673 CFRelease(password
);
4675 /* update the PPP entity */
4676 CFDictionarySetValue(newOptions
, kSCEntNetPPP
, newEntity
);
4677 CFRelease(newEntity
);
4680 /* IPSec Shared Secret */
4681 if (sharedsecret
!= NULL
) {
4682 CFDictionaryRef entity
;
4683 CFMutableDictionaryRef newEntity
;
4685 entity
= CFDictionaryGetValue(*userOptions
, kSCEntNetIPSec
);
4686 if (isA_CFDictionary(entity
))
4687 newEntity
= CFDictionaryCreateMutableCopy(NULL
, 0, entity
);
4689 newEntity
= CFDictionaryCreateMutable(NULL
,
4691 &kCFTypeDictionaryKeyCallBacks
,
4692 &kCFTypeDictionaryValueCallBacks
);
4694 /* set the IPSec Shared Secret */
4695 CFDictionarySetValue(newEntity
, kSCPropNetIPSecSharedSecret
, sharedsecret
);
4696 CFRelease(sharedsecret
);
4698 /* update the IPSec entity */
4699 CFDictionarySetValue(newOptions
, kSCEntNetIPSec
, newEntity
);
4700 CFRelease(newEntity
);
4703 /* update the userOptions dictionary */
4704 CFRelease(*userOptions
);
4705 *userOptions
= CFDictionaryCreateCopy(NULL
, newOptions
);
4706 CFRelease(newOptions
);
4711 #if !TARGET_OS_IPHONE
4712 //********************************************************************************
4713 // copyKeychainEnumerator
4714 // --------------------------------------
4715 // Gather Keychain Enumerator
4716 //********************************************************************************
4718 copyKeychainEnumerator(CFStringRef uniqueIdentifier
)
4720 CFArrayRef itemArray
= NULL
;
4721 CFMutableDictionaryRef query
;
4724 query
= CFDictionaryCreateMutable(NULL
,
4726 &kCFTypeDictionaryKeyCallBacks
,
4727 &kCFTypeDictionaryValueCallBacks
);
4728 CFDictionarySetValue(query
, kSecClass
, kSecClassGenericPassword
);
4729 CFDictionarySetValue(query
, kSecAttrService
, uniqueIdentifier
);
4730 CFDictionarySetValue(query
, kSecReturnRef
, kCFBooleanTrue
);
4731 CFDictionarySetValue(query
, kSecMatchLimit
, kSecMatchLimitAll
);
4732 result
= SecItemCopyMatching(query
, (CFTypeRef
*)&itemArray
);
4734 if ((result
!= noErr
) && (itemArray
!= NULL
)) {
4735 CFRelease(itemArray
);
4741 #endif // !TARGET_OS_IPHONE
4743 //********************************************************************************
4744 // copyPasswordFromKeychain
4745 // --------------------------------------
4746 // Given a uniqueID, retrieve the password from the keychain
4747 //********************************************************************************
4749 copyPasswordFromKeychain(CFStringRef uniqueID
)
4751 #if !TARGET_OS_IPHONE
4752 CFArrayRef enumerator
;
4754 CFStringRef password
= NULL
;
4756 enumerator
= copyKeychainEnumerator(uniqueID
);
4757 if (enumerator
== NULL
) {
4758 return NULL
; // if no keychain enumerator
4761 n
= CFArrayGetCount(enumerator
);
4765 SecKeychainItemRef itemRef
;
4768 itemRef
= (SecKeychainItemRef
)CFArrayGetValueAtIndex(enumerator
, 0);
4769 result
= SecKeychainItemCopyContent(itemRef
, // itemRef
4773 (void *)&data
); // outData
4774 if ((result
== noErr
) && (data
!= NULL
) && (dataLen
> 0)) {
4775 password
= CFStringCreateWithBytes(NULL
, data
, dataLen
, kCFStringEncodingUTF8
, TRUE
);
4776 (void) SecKeychainItemFreeContent(NULL
, data
);
4781 CFRelease(enumerator
);
4784 #else // !TARGET_OS_IPHONE
4785 #pragma unused(uniqueID)
4787 #endif // !TARGET_OS_IPHONE
4793 __SCNetworkConnectionGetControllerPortName(void)
4795 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 70000)) && !TARGET_OS_SIMULATOR
4796 if (scnc_server_name
== NULL
){
4797 if (!(sandbox_check(getpid(), "mach-lookup", SANDBOX_FILTER_GLOBAL_NAME
| SANDBOX_CHECK_NO_REPORT
, PPPCONTROLLER_SERVER_PRIV
))){
4798 scnc_server_name
= PPPCONTROLLER_SERVER_PRIV
;
4800 scnc_server_name
= PPPCONTROLLER_SERVER
;
4804 scnc_server_name
= PPPCONTROLLER_SERVER
;
4806 return scnc_server_name
;