2 * Copyright (c) 2003-2012 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * April 14, 2004 Christophe Allie <callie@apple.com>
30 * December 20, 2002 Christophe Allie <callie@apple.com>
35 //#define DEBUG_MACH_PORT_ALLOCATIONS
38 #include <Availability.h>
39 #include <TargetConditionals.h>
40 #include <sys/cdefs.h>
41 #include <dispatch/dispatch.h>
42 #include <CoreFoundation/CoreFoundation.h>
43 #include <CoreFoundation/CFRuntime.h>
44 #include <SystemConfiguration/SystemConfiguration.h>
45 #include <SystemConfiguration/SCPrivate.h>
46 #include <SystemConfiguration/SCValidation.h>
49 #include <Security/Security.h>
50 #include "dy_framework.h"
51 #endif // !TARGET_OS_IPHONE
53 #include <servers/bootstrap.h>
54 #include <bootstrap_priv.h>
58 #include <netinet/in.h>
59 #include <arpa/inet.h>
62 #include <sys/ioctl.h>
63 #include <sys/socket.h>
65 #include <mach/mach.h>
66 #include <bsm/audit.h>
68 #include <ppp/ppp_msg.h>
69 #include "pppcontroller.h"
70 #include <ppp/pppcontroller_types.h>
75 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
76 static pthread_mutex_t scnc_lock
= PTHREAD_MUTEX_INITIALIZER
;
77 static mach_port_t scnc_server
= MACH_PORT_NULL
;
82 /* base CFType information */
89 SCNetworkServiceRef service
;
91 /* client info (if we are proxying for another process */
92 mach_port_t client_audit_session
;
97 /* ref to PPP controller for control messages */
98 mach_port_t session_port
;
100 /* ref to PPP controller for notification messages */
101 CFMachPortRef notify_port
;
103 /* keep track of whether we're acquired the initial status */
106 /* run loop source, callout, context, rl scheduling info */
108 CFRunLoopSourceRef rls
;
109 SCNetworkConnectionCallBack rlsFunction
;
110 SCNetworkConnectionContext rlsContext
;
111 CFMutableArrayRef rlList
;
113 /* SCNetworkConnectionSetDispatchQueue */
114 dispatch_group_t dispatchGroup
;
115 dispatch_queue_t dispatchQueue
;
116 dispatch_source_t dispatchSource
;
118 } SCNetworkConnectionPrivate
, *SCNetworkConnectionPrivateRef
;
121 static __inline__ CFTypeRef
122 isA_SCNetworkConnection(CFTypeRef obj
)
124 return (isA_CFType(obj
, SCNetworkConnectionGetTypeID()));
129 __SCNetworkConnectionCopyDescription(CFTypeRef cf
)
131 CFAllocatorRef allocator
= CFGetAllocator(cf
);
132 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)cf
;
133 CFMutableStringRef result
;
135 result
= CFStringCreateMutable(allocator
, 0);
136 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkConnection, %p [%p]> {"), cf
, allocator
);
137 CFStringAppendFormat(result
, NULL
, CFSTR("service = %p"), connectionPrivate
->service
);
138 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
139 CFStringAppendFormat(result
, NULL
, CFSTR(", server port = %p"), connectionPrivate
->session_port
);
141 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
148 __SCNetworkConnectionDeallocate(CFTypeRef cf
)
150 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)cf
;
152 /* release resources */
153 pthread_mutex_destroy(&connectionPrivate
->lock
);
155 if (connectionPrivate
->client_audit_session
!= MACH_PORT_NULL
) {
156 mach_port_mod_refs(mach_task_self(),
157 connectionPrivate
->client_audit_session
,
158 MACH_PORT_RIGHT_SEND
,
162 if (connectionPrivate
->rls
!= NULL
) {
163 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
164 CFRelease(connectionPrivate
->rls
);
167 if (connectionPrivate
->rlList
!= NULL
) {
168 CFRelease(connectionPrivate
->rlList
);
171 if (connectionPrivate
->notify_port
!= NULL
) {
172 mach_port_t mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
174 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionDeallocate notify_port", mp
);
175 CFMachPortInvalidate(connectionPrivate
->notify_port
);
176 CFRelease(connectionPrivate
->notify_port
);
177 mach_port_mod_refs(mach_task_self(), mp
, MACH_PORT_RIGHT_RECEIVE
, -1);
180 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
181 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionDeallocate session_port", connectionPrivate
->session_port
);
182 (void) mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
185 if (connectionPrivate
->rlsContext
.release
!= NULL
)
186 (*connectionPrivate
->rlsContext
.release
)(connectionPrivate
->rlsContext
.info
);
188 CFRelease(connectionPrivate
->service
);
194 static CFTypeID __kSCNetworkConnectionTypeID
= _kCFRuntimeNotATypeID
;
196 static const CFRuntimeClass __SCNetworkConnectionClass
= {
198 "SCNetworkConnection", // className
201 __SCNetworkConnectionDeallocate
, // dealloc
204 NULL
, // copyFormattingDesc
205 __SCNetworkConnectionCopyDescription
// copyDebugDesc
212 /* the process has forked (and we are the child process) */
214 scnc_server
= MACH_PORT_NULL
;
220 __SCNetworkConnectionInitialize(void)
224 /* get the debug environment variable */
225 env
= getenv("PPPDebug");
227 if (sscanf(env
, "%d", &debug
) != 1) {
228 /* PPPDebug value is not valid (or non-numeric), set debug to 1 */
233 /* register with CoreFoundation */
234 __kSCNetworkConnectionTypeID
= _CFRuntimeRegisterClass(&__SCNetworkConnectionClass
);
236 /* add handler to cleanup after fork() */
237 (void) pthread_atfork(NULL
, NULL
, childForkHandler
);
244 __SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection
);
248 __SCNetworkConnectionCallBack(CFMachPortRef port
, void * msg
, CFIndex size
, void * info
)
250 mach_no_senders_notification_t
*buf
= msg
;
251 mach_msg_id_t msgid
= buf
->not_header
.msgh_id
;
253 SCNetworkConnectionRef connection
= (SCNetworkConnectionRef
)info
;
254 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
256 void (*context_release
)(const void *);
257 SCNetworkConnectionCallBack rlsFunction
= NULL
;
258 SCNetworkConnectionStatus nc_status
= kSCNetworkConnectionInvalid
;
260 if (msgid
== MACH_NOTIFY_NO_SENDERS
) {
261 // re-establish notification
262 SCLog(_sc_verbose
, LOG_DEBUG
, CFSTR("__SCNetworkConnectionCallBack: PPPController server died"));
263 (void)__SCNetworkConnectionReconnectNotifications(connection
);
266 pthread_mutex_lock(&connectionPrivate
->lock
);
268 if (!connectionPrivate
->scheduled
) {
269 // if not currently scheduled
273 rlsFunction
= connectionPrivate
->rlsFunction
;
274 if (rlsFunction
== NULL
) {
278 if ((connectionPrivate
->rlsContext
.retain
!= NULL
) && (connectionPrivate
->rlsContext
.info
!= NULL
)) {
279 context_info
= (void *)(*connectionPrivate
->rlsContext
.retain
)(connectionPrivate
->rlsContext
.info
);
280 context_release
= connectionPrivate
->rlsContext
.release
;
282 context_info
= connectionPrivate
->rlsContext
.info
;
283 context_release
= NULL
;
288 pthread_mutex_unlock(&connectionPrivate
->lock
);
290 if (rlsFunction
== NULL
) {
294 nc_status
= SCNetworkConnectionGetStatus(connection
);
295 (*rlsFunction
)(connection
, nc_status
, context_info
);
296 if ((context_release
!= NULL
) && (context_info
!= NULL
)) {
297 (*context_release
)(context_info
);
305 #pragma mark SCNetworkConnection APIs
309 pppMPCopyDescription(const void *info
)
311 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)info
;
313 return CFStringCreateWithFormat(NULL
,
315 CFSTR("<SCNetworkConnection MP %p> {service = %@, callout = %p}"),
317 connectionPrivate
->service
,
318 connectionPrivate
->rlsFunction
);
322 static SCNetworkConnectionPrivateRef
323 __SCNetworkConnectionCreatePrivate(CFAllocatorRef allocator
,
324 SCNetworkServiceRef service
,
325 SCNetworkConnectionCallBack callout
,
326 SCNetworkConnectionContext
*context
)
328 SCNetworkConnectionPrivateRef connectionPrivate
= NULL
;
332 /* initialize runtime */
333 pthread_once(&initialized
, __SCNetworkConnectionInitialize
);
335 /* allocate NetworkConnection */
336 size
= sizeof(SCNetworkConnectionPrivate
) - sizeof(CFRuntimeBase
);
337 connectionPrivate
= (SCNetworkConnectionPrivateRef
)_CFRuntimeCreateInstance(allocator
, __kSCNetworkConnectionTypeID
, size
, NULL
);
338 if (connectionPrivate
== NULL
) {
342 /* zero the data structure */
343 bzero(((u_char
*)connectionPrivate
)+sizeof(CFRuntimeBase
), size
);
345 pthread_mutex_init(&connectionPrivate
->lock
, NULL
);
347 /* save the service */
348 connectionPrivate
->service
= CFRetain(service
);
350 connectionPrivate
->client_audit_session
= MACH_PORT_NULL
;
351 connectionPrivate
->client_uid
= geteuid();
352 connectionPrivate
->client_gid
= getegid();
353 connectionPrivate
->client_pid
= getpid();
355 connectionPrivate
->rlsFunction
= callout
;
358 bcopy(context
, &connectionPrivate
->rlsContext
, sizeof(SCNetworkConnectionContext
));
359 if (context
->retain
!= NULL
) {
360 connectionPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
364 /* success, return the connection reference */
365 return connectionPrivate
;
369 /* failure, clean up and leave */
370 if (connectionPrivate
!= NULL
) {
371 CFRelease(connectionPrivate
);
374 _SCErrorSet(kSCStatusFailed
);
380 __SCNetworkConnectionServerPort(kern_return_t
*status
)
382 mach_port_t server
= MACH_PORT_NULL
;
384 #ifdef BOOTSTRAP_PRIVILEGED_SERVER
385 *status
= bootstrap_look_up2(bootstrap_port
,
386 PPPCONTROLLER_SERVER
,
389 BOOTSTRAP_PRIVILEGED_SERVER
);
390 #else // BOOTSTRAP_PRIVILEGED_SERVER
391 *status
= bootstrap_look_up(bootstrap_port
, PPPCONTROLLER_SERVER
, &server
);
392 #endif // BOOTSTRAP_PRIVILEGED_SERVER
395 case BOOTSTRAP_SUCCESS
:
396 // service currently registered, "a good thing" (tm)
398 case BOOTSTRAP_NOT_PRIVILEGED
:
399 // the service is not privileged
401 case BOOTSTRAP_UNKNOWN_SERVICE
:
402 // service not currently registered, try again later
406 SCLog(_sc_verbose
, LOG_DEBUG
,
407 CFSTR("SCNetworkConnection bootstrap_look_up() failed: status=%s"),
408 bootstrap_strerror(*status
));
413 return MACH_PORT_NULL
;
417 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && !TARGET_IPHONE_SIMULATOR
418 #define HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
422 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && !TARGET_IPHONE_SIMULATOR
423 #define HAVE_PPPCONTROLLER_ATTACHWITHPROXY
428 __SCNetworkConnectionSessionPort(SCNetworkConnectionPrivateRef connectionPrivate
)
432 CFDataRef dataRef
= NULL
;
433 mach_port_t notify_port
= MACH_PORT_NULL
;
434 mach_port_t oldNotify
= MACH_PORT_NULL
;
436 int sc_status
= kSCStatusFailed
;
437 mach_port_t server
= scnc_server
;
438 kern_return_t status
= KERN_SUCCESS
;
440 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
441 mach_port_t au_session
= MACH_PORT_NULL
;
442 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
444 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
445 return connectionPrivate
->session_port
;
448 if (!_SCSerializeString(SCNetworkServiceGetServiceID(connectionPrivate
->service
), &dataRef
, &data
, &dataLen
)) {
452 if (connectionPrivate
->notify_port
!= NULL
) {
453 mach_port_t mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
455 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionReconnectNotifications mp", mp
);
456 CFMachPortInvalidate(connectionPrivate
->notify_port
);
457 CFRelease(connectionPrivate
->notify_port
);
458 connectionPrivate
->notify_port
= NULL
;
459 mach_port_mod_refs(mach_task_self(), mp
, MACH_PORT_RIGHT_RECEIVE
, -1);
462 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
463 au_session
= audit_session_self();
464 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
466 // open a new session with the server
468 if ((connectionPrivate
->rlsFunction
!= NULL
) && (notify_port
== MACH_PORT_NULL
)) {
469 // allocate port (for server response)
470 status
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE
, ¬ify_port
);
471 if (status
!= KERN_SUCCESS
) {
472 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkConnectionSessionPort mach_port_allocate(): %s"), mach_error_string(status
));
477 // add send right (passed to the server)
478 status
= mach_port_insert_right(mach_task_self(),
481 MACH_MSG_TYPE_MAKE_SEND
);
482 if (status
!= KERN_SUCCESS
) {
483 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkConnectionSessionPort mach_port_insert_right(): %s"), mach_error_string(status
));
484 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
490 if (server
!= MACH_PORT_NULL
) {
491 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHPROXY
492 if ((connectionPrivate
->client_audit_session
== MACH_PORT_NULL
) &&
493 (connectionPrivate
->client_uid
== geteuid()) &&
494 (connectionPrivate
->client_gid
== getegid()) &&
495 (connectionPrivate
->client_pid
== getpid())
497 #endif // HAVE_PPPCONTROLLER_ATTACHWITHPROXY
498 status
= pppcontroller_attach(server
,
503 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
505 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
506 &connectionPrivate
->session_port
,
508 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHPROXY
510 mach_port_t client_au_session
;
512 if (connectionPrivate
->client_audit_session
== MACH_PORT_NULL
) {
513 client_au_session
= au_session
;
515 client_au_session
= connectionPrivate
->client_audit_session
;
518 status
= pppcontroller_attach_proxy(server
,
524 connectionPrivate
->client_uid
,
525 connectionPrivate
->client_gid
,
526 connectionPrivate
->client_pid
,
527 &connectionPrivate
->session_port
,
530 #endif // HAVE_PPPCONTROLLER_ATTACHWITHPROXY
531 if (status
== KERN_SUCCESS
) {
532 if (sc_status
!= kSCStatusOK
) {
533 SCLog(TRUE
, LOG_DEBUG
,
534 CFSTR("__SCNetworkConnectionSessionPort : attach w/error, sc_status=%s%s"),
535 SCErrorString(sc_status
),
536 (connectionPrivate
->session_port
!= MACH_PORT_NULL
) ? ", w/session_port!=MACH_PORT_NULL" : "");
538 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
539 __MACH_PORT_DEBUG(TRUE
,
540 "*** __SCNetworkConnectionSessionPort session_port (attach w/error, cleanup)",
541 connectionPrivate
->session_port
);
542 mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
543 connectionPrivate
->session_port
= MACH_PORT_NULL
;
546 if (notify_port
!= MACH_PORT_NULL
) {
547 __MACH_PORT_DEBUG(TRUE
,
548 "*** __SCNetworkConnectionSessionPort notify_port (attach w/error, cleanup)",
550 (void) mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
551 notify_port
= MACH_PORT_NULL
;
557 // our [cached] server port is not valid
558 SCLog(TRUE
, LOG_DEBUG
, CFSTR("__SCNetworkConnectionSessionPort : !attach: %s"), SCErrorString(status
));
559 if (status
== MACH_SEND_INVALID_DEST
) {
560 // the server is not yet available
561 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionSessionPort notify_port (!dest)", notify_port
);
562 } else if (status
== MIG_SERVER_DIED
) {
563 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionSessionPort notify_port (!mig)", notify_port
);
564 // the server we were using is gone and we've lost our send right
565 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
566 notify_port
= MACH_PORT_NULL
;
568 // if we got an unexpected error, don't retry
574 pthread_mutex_lock(&scnc_lock
);
575 if (scnc_server
!= MACH_PORT_NULL
) {
576 if (server
== scnc_server
) {
577 // if the server we tried returned the error
578 (void)mach_port_deallocate(mach_task_self(), scnc_server
);
579 scnc_server
= __SCNetworkConnectionServerPort(&sc_status
);
581 // another thread has refreshed the server port
584 scnc_server
= __SCNetworkConnectionServerPort(&sc_status
);
586 server
= scnc_server
;
587 pthread_mutex_unlock(&scnc_lock
);
589 if (server
== MACH_PORT_NULL
) {
590 // if server not available
591 if (sc_status
== BOOTSTRAP_UNKNOWN_SERVICE
) {
592 // if first retry attempt, wait for SCDynamicStore server
594 SCDynamicStoreRef store
;
596 store
= SCDynamicStoreCreate(NULL
,
597 CFSTR("SCNetworkConnection connect"),
605 // wait up to 2.5 seconds for the [SCNetworkConnection] server
607 if ((retry
+= 50) < 2500) {
608 usleep(50 * 1000); // sleep 50ms between attempts
616 if (notify_port
!= MACH_PORT_NULL
) {
617 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
618 CFMachPortContext context
= { 0
619 , (void *)connectionPrivate
622 , pppMPCopyDescription
625 // request a notification when/if the server dies
626 status
= mach_port_request_notification(mach_task_self(),
628 MACH_NOTIFY_NO_SENDERS
,
631 MACH_MSG_TYPE_MAKE_SEND_ONCE
,
633 if (status
!= KERN_SUCCESS
) {
634 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkConnectionSessionPort mach_port_request_notification(): %s"), mach_error_string(status
));
635 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
640 if (oldNotify
!= MACH_PORT_NULL
) {
641 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkConnectionSessionPort(): oldNotify != MACH_PORT_NULL"));
644 // create CFMachPort for SCNetworkConnection notification callback
645 connectionPrivate
->notify_port
= _SC_CFMachPortCreateWithPort("SCNetworkConnection",
647 __SCNetworkConnectionCallBack
,
650 // we need to try a bit harder to acquire the initial status
651 connectionPrivate
->haveStatus
= FALSE
;
653 // with no server port, release the notification port we allocated
654 __MACH_PORT_DEBUG(TRUE
,
655 "*** __SCNetworkConnectionSessionPort notify_port (!server)",
657 (void) mach_port_mod_refs (mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
658 (void) mach_port_deallocate(mach_task_self(), notify_port
);
659 notify_port
= MACH_PORT_NULL
;
667 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
668 if (au_session
!= MACH_PORT_NULL
){
669 (void)mach_port_deallocate(mach_task_self(), au_session
);
671 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
673 if (dataRef
!= NULL
) CFRelease(dataRef
);
677 __MACH_PORT_DEBUG(connectionPrivate
->session_port
!= MACH_PORT_NULL
,
678 "*** __SCNetworkConnectionSessionPort session_port",
679 connectionPrivate
->session_port
);
680 __MACH_PORT_DEBUG(notify_port
!= MACH_PORT_NULL
,
681 "*** __SCNetworkConnectionSessionPort notify_port",
684 case BOOTSTRAP_UNKNOWN_SERVICE
:
686 (status
== KERN_SUCCESS
) ? LOG_DEBUG
: LOG_ERR
,
687 CFSTR("PPPController not available"));
691 (status
== KERN_SUCCESS
) ? LOG_DEBUG
: LOG_ERR
,
692 CFSTR("__SCNetworkConnectionSessionPort pppcontroller_attach(): %s"),
693 SCErrorString(sc_status
));
697 if (sc_status
!= kSCStatusOK
) {
698 _SCErrorSet(sc_status
);
701 return connectionPrivate
->session_port
;
706 __SCNetworkConnectionReconnect(SCNetworkConnectionRef connection
)
708 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
711 port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
712 return (port
!= MACH_PORT_NULL
);
717 __SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection
)
719 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
720 dispatch_group_t dispatchGroup
= NULL
;
721 dispatch_queue_t dispatchQueue
= NULL
;
723 CFArrayRef rlList
= NULL
;
725 // Before we fully tearing down our [old] notifications, make sure
726 // we have retained any information that is needed to re-register the
727 // [new] notifications.
729 pthread_mutex_lock(&connectionPrivate
->lock
);
731 if (connectionPrivate
->rlList
!= NULL
) {
732 rlList
= CFArrayCreateCopy(NULL
, connectionPrivate
->rlList
);
734 if (connectionPrivate
->dispatchQueue
!= NULL
) {
735 // save dispatchQueue, release reference when we've queue'd blocks
736 // complete, allow re-scheduling
737 dispatchGroup
= connectionPrivate
->dispatchGroup
;
738 connectionPrivate
->dispatchGroup
= NULL
;
739 dispatchQueue
= connectionPrivate
->dispatchQueue
;
740 connectionPrivate
->dispatchQueue
= NULL
;
742 // and take an extra reference for rescheduling
743 dispatch_retain(dispatchQueue
);
746 // cancel [old] notifications
747 if (connectionPrivate
->rlList
!= NULL
) {
748 CFRelease(connectionPrivate
->rlList
);
749 connectionPrivate
->rlList
= NULL
;
751 if (connectionPrivate
->rls
!= NULL
) {
752 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
753 CFRelease(connectionPrivate
->rls
);
754 connectionPrivate
->rls
= NULL
;
756 if (connectionPrivate
->dispatchSource
!= NULL
) {
757 dispatch_source_cancel(connectionPrivate
->dispatchSource
);
758 connectionPrivate
->dispatchSource
= NULL
;
761 connectionPrivate
->scheduled
= FALSE
;
763 pthread_mutex_unlock(&connectionPrivate
->lock
);
765 if (dispatchGroup
!= NULL
) {
766 dispatch_group_notify(dispatchGroup
, dispatchQueue
, ^{
767 // release group/queue references
768 dispatch_release(dispatchQueue
);
769 dispatch_release(dispatchGroup
); // releases our connection reference
774 if (rlList
!= NULL
) {
778 n
= CFArrayGetCount(rlList
);
779 for (i
= 0; i
< n
; i
+= 3) {
780 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(rlList
, i
+1);
781 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(rlList
, i
+2);
783 ok
= SCNetworkConnectionScheduleWithRunLoop(connection
, rl
, rlMode
);
785 SCLog((SCError() != BOOTSTRAP_UNKNOWN_SERVICE
),
787 CFSTR("__SCNetworkConnectionReconnectNotifications: SCNetworkConnectionScheduleWithRunLoop() failed"));
791 } else if (dispatchQueue
!= NULL
) {
792 ok
= SCNetworkConnectionSetDispatchQueue(connection
, dispatchQueue
);
794 SCLog((SCError() != BOOTSTRAP_UNKNOWN_SERVICE
),
796 CFSTR("__SCNetworkConnectionReconnectNotifications: SCNetworkConnectionSetDispatchQueue() failed"));
806 if (rlList
!= NULL
) {
809 if (dispatchQueue
!= NULL
) {
810 dispatch_release(dispatchQueue
);
815 CFSTR("SCNetworkConnection server %s, notification not restored"),
816 (SCError() == BOOTSTRAP_UNKNOWN_SERVICE
) ? "shutdown" : "failed");
824 __SCNetworkConnectionNeedsRetry(SCNetworkConnectionRef connection
,
825 const char *error_label
,
826 kern_return_t status
,
829 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
831 if (status
== KERN_SUCCESS
) {
835 if ((status
== MACH_SEND_INVALID_DEST
) || (status
== MIG_SERVER_DIED
)) {
836 // the server's gone and our session port's dead, remove the dead name right
837 (void) mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
839 // we got an unexpected error, leave the [session] port alone
840 SCLog(TRUE
, LOG_ERR
, CFSTR("%s: %s"), error_label
, mach_error_string(status
));
842 connectionPrivate
->session_port
= MACH_PORT_NULL
;
843 if ((status
== MACH_SEND_INVALID_DEST
) || (status
== MIG_SERVER_DIED
)) {
844 if (__SCNetworkConnectionReconnect(connection
)) {
855 SCNetworkConnectionGetTypeID(void) {
856 pthread_once(&initialized
, __SCNetworkConnectionInitialize
); /* initialize runtime */
857 return __kSCNetworkConnectionTypeID
;
861 CFArrayRef
/* of SCNetworkServiceRef's */
862 SCNetworkConnectionCopyAvailableServices(SCNetworkSetRef set
)
864 CFMutableArrayRef available
;
865 Boolean tempSet
= FALSE
;
868 SCPreferencesRef prefs
;
870 prefs
= SCPreferencesCreate(NULL
, CFSTR("SCNetworkConnectionCopyAvailableServices"), NULL
);
872 set
= SCNetworkSetCopyCurrent(prefs
);
878 available
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
883 services
= SCNetworkSetCopyServices(set
);
884 if (services
!= NULL
) {
888 n
= CFArrayGetCount(services
);
889 for (i
= 0; i
< n
; i
++) {
890 SCNetworkInterfaceRef interface
;
891 CFStringRef interfaceType
;
892 SCNetworkServiceRef service
;
894 service
= CFArrayGetValueAtIndex(services
, i
);
895 interface
= SCNetworkServiceGetInterface(service
);
896 if (interface
== NULL
) {
900 interfaceType
= SCNetworkInterfaceGetInterfaceType(interface
);
901 if (CFEqual(interfaceType
, kSCNetworkInterfaceTypePPP
) ||
902 CFEqual(interfaceType
, kSCNetworkInterfaceTypeVPN
) ||
903 CFEqual(interfaceType
, kSCNetworkInterfaceTypeIPSec
)) {
904 CFArrayAppendValue(available
, service
);
912 if (tempSet
&& (set
!= NULL
)) {
919 SCNetworkConnectionRef
920 SCNetworkConnectionCreateWithService(CFAllocatorRef allocator
,
921 SCNetworkServiceRef service
,
922 SCNetworkConnectionCallBack callout
,
923 SCNetworkConnectionContext
*context
)
925 SCNetworkConnectionPrivateRef connectionPrivate
;
927 if (!isA_SCNetworkService(service
)) {
928 _SCErrorSet(kSCStatusInvalidArgument
);
932 connectionPrivate
= __SCNetworkConnectionCreatePrivate(allocator
, service
, callout
, context
);
933 return (SCNetworkConnectionRef
)connectionPrivate
;
937 SCNetworkConnectionRef
938 SCNetworkConnectionCreateWithServiceID(CFAllocatorRef allocator
,
939 CFStringRef serviceID
,
940 SCNetworkConnectionCallBack callout
,
941 SCNetworkConnectionContext
*context
)
943 SCNetworkConnectionRef connection
;
944 SCNetworkServiceRef service
;
946 if (!isA_CFString(serviceID
)) {
947 _SCErrorSet(kSCStatusInvalidArgument
);
951 service
= _SCNetworkServiceCopyActive(NULL
, serviceID
);
952 if (service
== NULL
) {
956 connection
= SCNetworkConnectionCreateWithService(allocator
, service
, callout
, context
);
964 SCNetworkConnectionCopyServiceID(SCNetworkConnectionRef connection
)
966 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
967 CFStringRef serviceID
;
969 if (!isA_SCNetworkConnection(connection
)) {
970 _SCErrorSet(kSCStatusInvalidArgument
);
974 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
975 return CFRetain(serviceID
);
980 SCNetworkConnectionSetClientInfo(SCNetworkConnectionRef connection
,
981 mach_port_t client_audit_session
,
986 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
988 if (!isA_SCNetworkConnection(connection
)) {
989 _SCErrorSet(kSCStatusInvalidArgument
);
993 // save client bootstrap port
994 if (connectionPrivate
->client_audit_session
!= MACH_PORT_NULL
) {
995 mach_port_mod_refs(mach_task_self(),
996 connectionPrivate
->client_audit_session
,
997 MACH_PORT_RIGHT_SEND
,
999 connectionPrivate
->client_audit_session
= MACH_PORT_NULL
;
1001 connectionPrivate
->client_audit_session
= client_audit_session
;
1002 if (connectionPrivate
->client_audit_session
!= MACH_PORT_NULL
) {
1003 mach_port_mod_refs(mach_task_self(),
1004 connectionPrivate
->client_audit_session
,
1005 MACH_PORT_RIGHT_SEND
,
1009 // save client UID, GID, and PID
1010 connectionPrivate
->client_uid
= client_uid
;
1011 connectionPrivate
->client_gid
= client_gid
;
1012 connectionPrivate
->client_pid
= client_pid
;
1019 SCNetworkConnectionCopyStatistics(SCNetworkConnectionRef connection
)
1021 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1022 xmlDataOut_t data
= NULL
;
1023 mach_msg_type_number_t datalen
= 0;
1024 int sc_status
= kSCStatusFailed
;
1025 mach_port_t session_port
;
1026 CFPropertyListRef statistics
= NULL
;
1027 kern_return_t status
;
1029 if (!isA_SCNetworkConnection(connection
)) {
1030 _SCErrorSet(kSCStatusInvalidArgument
);
1034 pthread_mutex_lock(&connectionPrivate
->lock
);
1038 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1039 if (session_port
== MACH_PORT_NULL
) {
1043 status
= pppcontroller_copystatistics(session_port
, &data
, &datalen
, &sc_status
);
1044 if (__SCNetworkConnectionNeedsRetry(connection
,
1045 "SCNetworkConnectionCopyStatistics()",
1052 if (!_SCUnserialize(&statistics
, NULL
, data
, datalen
)) {
1053 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
1055 if ((sc_status
== kSCStatusOK
) && !isA_CFDictionary(statistics
)) {
1056 sc_status
= kSCStatusFailed
;
1060 if (sc_status
!= kSCStatusOK
) {
1061 if (statistics
!= NULL
) {
1062 CFRelease(statistics
);
1065 _SCErrorSet(sc_status
);
1070 pthread_mutex_unlock(&connectionPrivate
->lock
);
1076 SCNetworkConnectionGetService(SCNetworkConnectionRef connection
)
1078 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1080 if (!isA_SCNetworkConnection(connection
)) {
1081 _SCErrorSet(kSCStatusInvalidArgument
);
1085 return connectionPrivate
->service
;
1089 SCNetworkConnectionStatus
1090 SCNetworkConnectionGetStatus(SCNetworkConnectionRef connection
)
1092 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1093 SCNetworkConnectionStatus nc_status
= kSCNetworkConnectionInvalid
;
1095 int sc_status
= kSCStatusFailed
;
1096 mach_port_t session_port
;
1097 kern_return_t status
;
1099 if (!isA_SCNetworkConnection(connection
)) {
1100 _SCErrorSet(kSCStatusInvalidArgument
);
1101 return kSCNetworkConnectionInvalid
;
1104 pthread_mutex_lock(&connectionPrivate
->lock
);
1108 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1109 if (session_port
== MACH_PORT_NULL
) {
1110 nc_status
= kSCNetworkConnectionInvalid
;
1114 status
= pppcontroller_getstatus(session_port
, &nc_status
, &sc_status
);
1115 if (__SCNetworkConnectionNeedsRetry(connection
,
1116 "SCNetworkConnectionGetStatus()",
1122 // wait up to 250 ms for the network service to become available
1123 if (!connectionPrivate
->haveStatus
&&
1124 (sc_status
== kSCStatusConnectionNoService
) &&
1125 ((retry
+= 10) < 250)) {
1126 usleep(10 * 1000); // sleep 10ms between attempts
1130 if (sc_status
== kSCStatusOK
) {
1131 connectionPrivate
->haveStatus
= TRUE
;
1133 _SCErrorSet(sc_status
);
1134 nc_status
= kSCNetworkConnectionInvalid
;
1139 pthread_mutex_unlock(&connectionPrivate
->lock
);
1145 SCNetworkConnectionCopyExtendedStatus(SCNetworkConnectionRef connection
)
1147 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1148 xmlDataOut_t data
= NULL
;
1149 mach_msg_type_number_t datalen
= 0;
1150 CFPropertyListRef extstatus
= NULL
;
1152 int sc_status
= kSCStatusFailed
;
1153 mach_port_t session_port
;
1154 kern_return_t status
;
1156 if (!isA_SCNetworkConnection(connection
)) {
1157 _SCErrorSet(kSCStatusInvalidArgument
);
1161 pthread_mutex_lock(&connectionPrivate
->lock
);
1165 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1166 if (session_port
== MACH_PORT_NULL
) {
1170 status
= pppcontroller_copyextendedstatus(session_port
, &data
, &datalen
, &sc_status
);
1171 if (__SCNetworkConnectionNeedsRetry(connection
,
1172 "SCNetworkConnectionCopyExtendedStatus()",
1179 if (!_SCUnserialize(&extstatus
, NULL
, data
, datalen
)) {
1180 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
1182 if ((sc_status
== kSCStatusOK
) && !isA_CFDictionary(extstatus
)) {
1183 sc_status
= kSCStatusFailed
;
1187 // wait up to 250 ms for the network service to become available
1188 if (!connectionPrivate
->haveStatus
&&
1189 (sc_status
== kSCStatusConnectionNoService
) &&
1190 ((retry
+= 10) < 250)) {
1191 usleep(10 * 1000); // sleep 10ms between attempts
1195 if (sc_status
== kSCStatusOK
) {
1196 connectionPrivate
->haveStatus
= TRUE
;
1198 if (extstatus
!= NULL
) {
1199 CFRelease(extstatus
);
1202 _SCErrorSet(sc_status
);
1207 pthread_mutex_unlock(&connectionPrivate
->lock
);
1213 SCNetworkConnectionStart(SCNetworkConnectionRef connection
,
1214 CFDictionaryRef userOptions
,
1217 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1218 CFDataRef dataref
= NULL
;
1220 CFIndex datalen
= 0;
1222 int sc_status
= kSCStatusFailed
;
1223 mach_port_t session_port
;
1224 kern_return_t status
;
1226 if (!isA_SCNetworkConnection(connection
)) {
1227 _SCErrorSet(kSCStatusInvalidArgument
);
1231 if ((userOptions
!= NULL
) && !isA_CFDictionary(userOptions
)) {
1232 _SCErrorSet(kSCStatusInvalidArgument
);
1237 CFMutableDictionaryRef mdict
= NULL
;
1239 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionStart (0x%x)"), connectionPrivate
);
1241 if (userOptions
!= NULL
) {
1242 CFDictionaryRef dict
;
1243 CFStringRef encryption
;
1244 CFMutableDictionaryRef new_dict
;
1246 /* special code to remove secret information */
1247 mdict
= CFDictionaryCreateMutableCopy(NULL
, 0, userOptions
);
1249 dict
= CFDictionaryGetValue(mdict
, kSCEntNetPPP
);
1250 if (isA_CFDictionary(dict
)) {
1251 encryption
= CFDictionaryGetValue(dict
, kSCPropNetPPPAuthPasswordEncryption
);
1252 if (!isA_CFString(encryption
) ||
1253 !CFEqual(encryption
, kSCValNetPPPAuthPasswordEncryptionKeychain
)) {
1254 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1255 CFDictionaryReplaceValue(new_dict
, kSCPropNetPPPAuthPassword
, CFSTR("******"));
1256 CFDictionarySetValue(mdict
, kSCEntNetPPP
, new_dict
);
1257 CFRelease(new_dict
);
1261 dict
= CFDictionaryGetValue(mdict
, kSCEntNetL2TP
);
1262 if (isA_CFDictionary(dict
)) {
1263 encryption
= CFDictionaryGetValue(dict
, kSCPropNetL2TPIPSecSharedSecretEncryption
);
1264 if (!isA_CFString(encryption
) ||
1265 !CFEqual(encryption
, kSCValNetL2TPIPSecSharedSecretEncryptionKeychain
)) {
1266 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1267 CFDictionaryReplaceValue(new_dict
, kSCPropNetL2TPIPSecSharedSecret
, CFSTR("******"));
1268 CFDictionarySetValue(mdict
, kSCEntNetL2TP
, new_dict
);
1269 CFRelease(new_dict
);
1273 dict
= CFDictionaryGetValue(mdict
, kSCEntNetIPSec
);
1274 if (isA_CFDictionary(dict
)) {
1275 encryption
= CFDictionaryGetValue(dict
, kSCPropNetIPSecSharedSecretEncryption
);
1276 if (!isA_CFString(encryption
) ||
1277 !CFEqual(encryption
, kSCValNetIPSecSharedSecretEncryptionKeychain
)) {
1278 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1279 CFDictionaryReplaceValue(new_dict
, kSCPropNetIPSecSharedSecret
, CFSTR("******"));
1280 CFDictionarySetValue(mdict
, kSCEntNetIPSec
, new_dict
);
1281 CFRelease(new_dict
);
1286 SCLog(TRUE
, LOG_DEBUG
, CFSTR("User options: %@"), mdict
);
1287 if (mdict
!= NULL
) CFRelease(mdict
);
1290 if (userOptions
&& !_SCSerialize(userOptions
, &dataref
, &data
, &datalen
)) {
1294 pthread_mutex_lock(&connectionPrivate
->lock
);
1298 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1299 if (session_port
== MACH_PORT_NULL
) {
1300 if (dataref
) CFRelease(dataref
);
1304 status
= pppcontroller_start(session_port
, data
, datalen
, linger
, &sc_status
);
1305 if (__SCNetworkConnectionNeedsRetry(connection
,
1306 "SCNetworkConnectionStart()",
1312 if (dataref
) CFRelease(dataref
);
1315 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionStart (0x%x), return: %d"), connectionPrivate
, sc_status
);
1318 if (sc_status
!= kSCStatusOK
) {
1319 _SCErrorSet(sc_status
);
1323 /* connection is now started */
1327 pthread_mutex_unlock(&connectionPrivate
->lock
);
1333 SCNetworkConnectionStop(SCNetworkConnectionRef connection
,
1334 Boolean forceDisconnect
)
1336 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1338 int sc_status
= kSCStatusFailed
;
1339 mach_port_t session_port
;
1340 kern_return_t status
;
1342 if (!isA_SCNetworkConnection(connection
)) {
1343 _SCErrorSet(kSCStatusInvalidArgument
);
1348 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionStop (0x%x)"), connectionPrivate
);
1351 pthread_mutex_lock(&connectionPrivate
->lock
);
1355 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1356 if (session_port
== MACH_PORT_NULL
) {
1360 status
= pppcontroller_stop(session_port
, forceDisconnect
, &sc_status
);
1361 if (__SCNetworkConnectionNeedsRetry(connection
,
1362 "SCNetworkConnectionStop()",
1369 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionStop (0x%x), return: %d"), connectionPrivate
, sc_status
);
1372 if (sc_status
!= kSCStatusOK
) {
1373 _SCErrorSet(sc_status
);
1377 /* connection is now disconnecting */
1382 pthread_mutex_unlock(&connectionPrivate
->lock
);
1388 SCNetworkConnectionSuspend(SCNetworkConnectionRef connection
)
1390 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1392 int sc_status
= kSCStatusFailed
;
1393 mach_port_t session_port
;
1394 kern_return_t status
;
1396 if (!isA_SCNetworkConnection(connection
)) {
1397 _SCErrorSet(kSCStatusInvalidArgument
);
1402 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionSuspend (0x%x)"), connectionPrivate
);
1405 pthread_mutex_lock(&connectionPrivate
->lock
);
1409 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1410 if (session_port
== MACH_PORT_NULL
) {
1414 status
= pppcontroller_suspend(session_port
, &sc_status
);
1415 if (__SCNetworkConnectionNeedsRetry(connection
,
1416 "SCNetworkConnectionSuspend()",
1423 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionSuspend (0x%x), return: %d"), connectionPrivate
, sc_status
);
1426 if (sc_status
!= kSCStatusOK
) {
1427 _SCErrorSet(sc_status
);
1431 /* connection is now suspended */
1436 pthread_mutex_unlock(&connectionPrivate
->lock
);
1442 SCNetworkConnectionResume(SCNetworkConnectionRef connection
)
1444 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1446 int sc_status
= kSCStatusFailed
;
1447 mach_port_t session_port
;
1448 kern_return_t status
;
1450 if (!isA_SCNetworkConnection(connection
)) {
1451 _SCErrorSet(kSCStatusInvalidArgument
);
1456 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionResume (0x%x)"), connectionPrivate
);
1459 pthread_mutex_lock(&connectionPrivate
->lock
);
1463 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1464 if (session_port
== MACH_PORT_NULL
) {
1468 status
= pppcontroller_resume(session_port
, &sc_status
);
1469 if (__SCNetworkConnectionNeedsRetry(connection
,
1470 "SCNetworkConnectionResume()",
1477 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionResume (0x%x), return: %d"), connectionPrivate
, sc_status
);
1480 if (sc_status
!= kSCStatusOK
) {
1481 _SCErrorSet(sc_status
);
1485 /* connection is now resume */
1490 pthread_mutex_unlock(&connectionPrivate
->lock
);
1496 SCNetworkConnectionCopyUserOptions(SCNetworkConnectionRef connection
)
1498 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1499 xmlDataOut_t data
= NULL
;
1500 mach_msg_type_number_t datalen
= 0;
1501 int sc_status
= kSCStatusFailed
;
1502 mach_port_t session_port
;
1503 kern_return_t status
;
1504 CFPropertyListRef userOptions
= NULL
;
1506 if (!isA_SCNetworkConnection(connection
)) {
1507 _SCErrorSet(kSCStatusInvalidArgument
);
1511 pthread_mutex_lock(&connectionPrivate
->lock
);
1515 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1516 if (session_port
== MACH_PORT_NULL
) {
1520 status
= pppcontroller_copyuseroptions(session_port
, &data
, &datalen
, &sc_status
);
1521 if (__SCNetworkConnectionNeedsRetry(connection
,
1522 "SCNetworkConnectionCopyUserOptions()",
1529 if (!_SCUnserialize(&userOptions
, NULL
, data
, datalen
)) {
1530 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
1532 if ((sc_status
== kSCStatusOK
) && (userOptions
!= NULL
) && !isA_CFDictionary(userOptions
)) {
1533 sc_status
= kSCStatusFailed
;
1537 if (sc_status
== kSCStatusOK
) {
1538 if (userOptions
== NULL
) {
1539 // if no user options, return an empty dictionary
1540 userOptions
= CFDictionaryCreate(NULL
,
1544 &kCFTypeDictionaryKeyCallBacks
,
1545 &kCFTypeDictionaryValueCallBacks
);
1549 CFRelease(userOptions
);
1552 _SCErrorSet(sc_status
);
1557 pthread_mutex_unlock(&connectionPrivate
->lock
);
1563 __SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection
,
1564 CFRunLoopRef runLoop
,
1565 CFStringRef runLoopMode
,
1566 dispatch_queue_t queue
)
1568 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1570 int sc_status
= kSCStatusFailed
;
1571 mach_port_t session_port
;
1572 kern_return_t status
;
1574 pthread_mutex_lock(&connectionPrivate
->lock
);
1576 if (connectionPrivate
->rlsFunction
== NULL
) {
1577 _SCErrorSet(kSCStatusInvalidArgument
);
1581 if ((connectionPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled on a dispatch queue
1582 ((queue
!= NULL
) && connectionPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
1583 _SCErrorSet(kSCStatusInvalidArgument
);
1587 if (!connectionPrivate
->scheduled
) {
1591 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1592 if (session_port
== MACH_PORT_NULL
) {
1596 status
= pppcontroller_notification(session_port
, 1, &sc_status
);
1597 if (__SCNetworkConnectionNeedsRetry(connection
,
1598 "__SCNetworkConnectionScheduleWithRunLoop()",
1604 if (sc_status
!= kSCStatusOK
) {
1605 _SCErrorSet(sc_status
);
1609 if (runLoop
!= NULL
) {
1610 connectionPrivate
->rls
= CFMachPortCreateRunLoopSource(NULL
, connectionPrivate
->notify_port
, 0);
1611 connectionPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1614 connectionPrivate
->scheduled
= TRUE
;
1617 if (queue
!= NULL
) {
1619 dispatch_source_t source
;
1621 mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
1622 source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
, mp
, 0, queue
);
1623 if (source
== NULL
) {
1624 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkConnection dispatch_source_create() failed"));
1625 _SCErrorSet(kSCStatusFailed
);
1629 // have our dispatch source hold a reference to the notification CFMachPort
1630 CFRetain(connectionPrivate
->notify_port
);
1631 dispatch_set_context(source
, (void *)connectionPrivate
->notify_port
);
1632 dispatch_set_finalizer_f(source
, (dispatch_function_t
)CFRelease
);
1634 // retain the dispatch queue
1635 connectionPrivate
->dispatchQueue
= queue
;
1636 dispatch_retain(connectionPrivate
->dispatchQueue
);
1639 // We've taken a reference to the callers dispatch_queue and we
1640 // want to hold on to that reference until we've processed any/all
1641 // notifications. To facilitate this we create a group, dispatch
1642 // any notification blocks to via that group, and when the caller
1643 // has told us to stop the notifications (unschedule) we wait for
1644 // the group to empty and use the group's finalizer to release
1645 // our reference to the SCNetworkConnection.
1647 connectionPrivate
->dispatchGroup
= dispatch_group_create();
1648 CFRetain(connection
);
1649 dispatch_set_context(connectionPrivate
->dispatchGroup
, (void *)connection
);
1650 dispatch_set_finalizer_f(connectionPrivate
->dispatchGroup
, (dispatch_function_t
)CFRelease
);
1652 dispatch_source_set_event_handler(source
, ^{
1655 u_int8_t buf
[sizeof(mach_msg_empty_t
) + MAX_TRAILER_SIZE
];
1656 mach_msg_empty_rcv_t msg
;
1657 mach_no_senders_notification_t no_senders
;
1659 CFMachPortRef notify_port
;
1661 kr
= mach_msg(¬ify_msg
.msg
.header
, // msg
1662 MACH_RCV_MSG
, // options
1664 sizeof(notify_msg
), // rcv_size
1666 MACH_MSG_TIMEOUT_NONE
, // timeout
1667 MACH_PORT_NULL
); // notify
1668 if (kr
!= KERN_SUCCESS
) {
1669 SCLog(TRUE
, LOG_ERR
,
1670 CFSTR("SCDynamicStore notification handler, kr=0x%x"),
1675 CFRetain(connection
);
1676 notify_port
= dispatch_get_context(source
);
1677 dispatch_group_async(connectionPrivate
->dispatchGroup
, connectionPrivate
->dispatchQueue
, ^{
1678 __SCNetworkConnectionCallBack(notify_port
,
1679 (void *)¬ify_msg
.msg
,
1681 (void *)connection
);
1682 CFRelease(connection
);
1686 dispatch_source_set_cancel_handler(source
, ^{
1687 dispatch_release(source
);
1690 connectionPrivate
->dispatchSource
= source
;
1691 dispatch_resume(source
);
1693 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, connectionPrivate
->rlList
)) {
1695 * if we do not already have notifications scheduled with
1696 * this runLoop / runLoopMode
1698 CFRunLoopAddSource(runLoop
, connectionPrivate
->rls
, runLoopMode
);
1701 _SC_schedule(connection
, runLoop
, runLoopMode
, connectionPrivate
->rlList
);
1708 pthread_mutex_unlock(&connectionPrivate
->lock
);
1714 __SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection
,
1715 CFRunLoopRef runLoop
,
1716 CFStringRef runLoopMode
,
1717 dispatch_queue_t queue
)
1719 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1720 dispatch_group_t drainGroup
= NULL
;
1721 dispatch_queue_t drainQueue
= NULL
;
1722 int sc_status
= kSCStatusFailed
;
1725 mach_port_t session_port
;
1726 kern_return_t status
;
1728 // hold a reference while we unschedule
1729 CFRetain(connection
);
1731 pthread_mutex_lock(&connectionPrivate
->lock
);
1733 if ((runLoop
!= NULL
) && !connectionPrivate
->scheduled
) { // if we should be scheduled (but are not)
1734 _SCErrorSet(kSCStatusInvalidArgument
);
1738 if (((runLoop
== NULL
) && (connectionPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
1739 ((runLoop
!= NULL
) && (connectionPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
1740 _SCErrorSet(kSCStatusInvalidArgument
);
1744 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1745 if (session_port
== MACH_PORT_NULL
) {
1749 if (connectionPrivate
->dispatchQueue
!= NULL
) {
1750 // cancel dispatchSource
1751 if (connectionPrivate
->dispatchSource
!= NULL
) {
1752 dispatch_source_cancel(connectionPrivate
->dispatchSource
);
1753 connectionPrivate
->dispatchSource
= NULL
;
1756 // save dispatchQueue/group, release reference when all queue'd blocks
1757 // have been processed, allow re-scheduling
1758 drainGroup
= connectionPrivate
->dispatchGroup
;
1759 connectionPrivate
->dispatchGroup
= NULL
;
1760 drainQueue
= connectionPrivate
->dispatchQueue
;
1761 connectionPrivate
->dispatchQueue
= NULL
;
1763 if (!_SC_unschedule(connection
, runLoop
, runLoopMode
, connectionPrivate
->rlList
, FALSE
)) {
1764 // if not currently scheduled on this runLoop / runLoopMode
1765 _SCErrorSet(kSCStatusFailed
);
1769 n
= CFArrayGetCount(connectionPrivate
->rlList
);
1770 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, connectionPrivate
->rlList
)) {
1772 * if we are no longer scheduled to receive notifications for
1773 * this runLoop / runLoopMode
1775 CFRunLoopRemoveSource(runLoop
, connectionPrivate
->rls
, runLoopMode
);
1778 // if *all* notifications have been unscheduled
1779 CFRelease(connectionPrivate
->rlList
);
1780 connectionPrivate
->rlList
= NULL
;
1781 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
1782 CFRelease(connectionPrivate
->rls
);
1783 connectionPrivate
->rls
= NULL
;
1789 // if *all* notifications have been unscheduled
1790 connectionPrivate
->scheduled
= FALSE
;
1792 status
= pppcontroller_notification(session_port
, 0, &sc_status
);
1793 if (__SCNetworkConnectionNeedsRetry(connection
,
1794 "__SCNetworkConnectionUnscheduleFromRunLoop pppcontroller_notification()",
1797 sc_status
= kSCStatusOK
;
1798 status
= KERN_SUCCESS
;
1801 if ((status
!= KERN_SUCCESS
) || (sc_status
!= kSCStatusOK
)) {
1802 _SCErrorSet(sc_status
);
1811 pthread_mutex_unlock(&connectionPrivate
->lock
);
1813 if (drainGroup
!= NULL
) {
1814 dispatch_group_notify(drainGroup
, drainQueue
, ^{
1815 // release group/queue references
1816 dispatch_release(drainQueue
);
1817 dispatch_release(drainGroup
); // releases our connection reference
1821 // release our reference
1822 CFRelease(connection
);
1829 SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection
,
1830 CFRunLoopRef runLoop
,
1831 CFStringRef runLoopMode
)
1833 if (!isA_SCNetworkConnection(connection
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1834 _SCErrorSet(kSCStatusInvalidArgument
);
1838 return __SCNetworkConnectionScheduleWithRunLoop(connection
, runLoop
, runLoopMode
, NULL
);
1843 SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection
,
1844 CFRunLoopRef runLoop
,
1845 CFStringRef runLoopMode
)
1847 if (!isA_SCNetworkConnection(connection
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1848 _SCErrorSet(kSCStatusInvalidArgument
);
1852 return __SCNetworkConnectionUnscheduleFromRunLoop(connection
, runLoop
, runLoopMode
, NULL
);
1857 SCNetworkConnectionSetDispatchQueue(SCNetworkConnectionRef connection
,
1858 dispatch_queue_t queue
)
1862 if (!isA_SCNetworkConnection(connection
)) {
1863 _SCErrorSet(kSCStatusInvalidArgument
);
1867 if (queue
!= NULL
) {
1868 ok
= __SCNetworkConnectionScheduleWithRunLoop(connection
, NULL
, NULL
, queue
);
1870 ok
= __SCNetworkConnectionUnscheduleFromRunLoop(connection
, NULL
, NULL
, NULL
);
1878 #pragma mark User level "dial" API
1881 #define k_NetworkConnect_Notification "com.apple.networkConnect"
1882 #define k_NetworkConnect_Pref_File CFSTR("com.apple.networkConnect")
1883 #define k_InterentConnect_Pref_File CFSTR("com.apple.internetconnect")
1885 #define k_Dial_Default_Key CFSTR("ConnectByDefault") // needs to go into SC
1886 #define k_Last_Service_Id_Key CFSTR("ServiceID")
1887 #define k_Unique_Id_Key CFSTR("UniqueIdentifier")
1890 /* Private Prototypes */
1891 static Boolean
SCNetworkConnectionPrivateCopyDefaultServiceIDForDial (SCDynamicStoreRef session
, CFStringRef
*serviceID
);
1892 static Boolean
SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore (SCDynamicStoreRef session
, CFStringRef
*serviceID
);
1893 static Boolean
SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray
, CFDictionaryRef
*userOptions
);
1894 static Boolean
SCNetworkConnectionPrivateIsPPPService (SCDynamicStoreRef session
, CFStringRef serviceID
, CFStringRef subType1
, CFStringRef subType2
);
1895 static void addPasswordFromKeychain (SCDynamicStoreRef session
, CFStringRef serviceID
, CFDictionaryRef
*userOptions
);
1896 static CFStringRef
copyPasswordFromKeychain (CFStringRef uniqueID
);
1898 static int notify_userprefs_token
= -1;
1900 static CFDictionaryRef onDemand_configuration
= NULL
;
1901 static pthread_mutex_t onDemand_notify_lock
= PTHREAD_MUTEX_INITIALIZER
;
1902 static int onDemand_notify_token
= -1;
1905 * return TRUE if domain1 ends with domain2, and will check for trailing "."
1908 _SC_domainEndsWithDomain(CFStringRef compare_domain
, CFStringRef match_domain
)
1911 Boolean ret
= FALSE
;
1912 CFStringRef s1
= NULL
;
1913 Boolean s1_created
= FALSE
;
1914 CFStringRef s2
= NULL
;
1915 Boolean s2_created
= FALSE
;
1916 CFStringRef s3
= NULL
;
1918 if (CFStringHasSuffix(compare_domain
, CFSTR("."))) {
1920 range
.length
= CFStringGetLength(compare_domain
) - 1;
1921 s1
= CFStringCreateWithSubstring(NULL
, compare_domain
, range
);
1927 s1
= compare_domain
;
1930 if (CFStringHasSuffix(match_domain
, CFSTR("."))) {
1932 range
.length
= CFStringGetLength(match_domain
) - 1;
1933 s2
= CFStringCreateWithSubstring(NULL
, match_domain
, range
);
1942 if (CFStringHasPrefix(s2
, CFSTR("*."))) {
1944 range
.length
= CFStringGetLength(s2
)-2;
1945 s3
= CFStringCreateWithSubstring(NULL
, s2
, range
);
1956 ret
= CFStringHasSuffix(s1
, s2
);
1960 if (s1_created
) CFRelease(s1
);
1961 if (s2_created
) CFRelease(s2
);
1968 __SCNetworkConnectionCopyOnDemandInfoWithName(SCDynamicStoreRef
*storeP
,
1969 CFStringRef hostName
,
1970 Boolean onDemandRetry
,
1971 CFStringRef
*connectionServiceID
,
1972 SCNetworkConnectionStatus
*connectionStatus
,
1973 CFStringRef
*vpnRemoteAddress
) /* CFDictionaryRef *info */
1976 CFDictionaryRef configuration
;
1979 SCDynamicStoreRef store
= *storeP
;
1980 CFArrayRef triggers
;
1981 uint64_t triggersCount
= 0;
1984 pthread_mutex_lock(&onDemand_notify_lock
);
1985 if (onDemand_notify_token
== -1) {
1986 status
= notify_register_check(kSCNETWORKCONNECTION_ONDEMAND_NOTIFY_KEY
, &onDemand_notify_token
);
1987 if (status
!= NOTIFY_STATUS_OK
) {
1988 SCLog(TRUE
, LOG_ERR
, CFSTR("notify_register_check() failed, status=%lu"), status
);
1989 onDemand_notify_token
= -1;
1992 if (onDemand_notify_token
!= -1) {
1993 status
= notify_check(onDemand_notify_token
, &changed
);
1994 if (status
!= NOTIFY_STATUS_OK
) {
1995 SCLog(TRUE
, LOG_ERR
, CFSTR("notify_check() failed, status=%lu"), status
);
1996 (void)notify_cancel(onDemand_notify_token
);
1997 onDemand_notify_token
= -1;
2001 if (changed
&& (onDemand_notify_token
!= -1)) {
2002 status
= notify_get_state(onDemand_notify_token
, &triggersCount
);
2003 if (status
!= NOTIFY_STATUS_OK
) {
2004 SCLog(TRUE
, LOG_ERR
, CFSTR("notify_get_state() failed, status=%lu"), status
);
2005 (void)notify_cancel(onDemand_notify_token
);
2006 onDemand_notify_token
= -1;
2013 if (_sc_debug
|| (debug
> 0)) {
2014 SCLog(TRUE
, LOG_INFO
,
2015 CFSTR("OnDemand information %s"),
2016 (onDemand_configuration
== NULL
) ? "fetched" : "updated");
2019 if (onDemand_configuration
!= NULL
) {
2020 CFRelease(onDemand_configuration
);
2021 onDemand_configuration
= NULL
;
2024 if (triggersCount
> 0) {
2025 if (store
== NULL
) {
2026 store
= SCDynamicStoreCreate(NULL
, CFSTR("__SCNetworkConnectionCopyOnDemandInfoWithName"), NULL
, NULL
);
2027 if (store
== NULL
) {
2028 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkConnectionCopyOnDemandInfoWithName SCDynamicStoreCreate() failed"));
2030 // force retry on next check
2031 if (onDemand_notify_token
!= -1) {
2032 (void)notify_cancel(onDemand_notify_token
);
2033 onDemand_notify_token
= -1;
2035 pthread_mutex_unlock(&onDemand_notify_lock
);
2041 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, kSCDynamicStoreDomainState
, kSCEntNetOnDemand
);
2042 onDemand_configuration
= SCDynamicStoreCopyValue(store
, key
);
2044 if ((onDemand_configuration
!= NULL
) && !isA_CFDictionary(onDemand_configuration
)) {
2045 CFRelease(onDemand_configuration
);
2046 onDemand_configuration
= NULL
;
2051 configuration
= (onDemand_configuration
!= NULL
) ? CFRetain(onDemand_configuration
) : NULL
;
2052 pthread_mutex_unlock(&onDemand_notify_lock
);
2054 if (configuration
== NULL
) {
2055 // if no "OnDemand" configurations
2059 triggers
= CFDictionaryGetValue(configuration
, kSCNetworkConnectionOnDemandTriggers
);
2060 triggersCount
= isA_CFArray(triggers
) ? CFArrayGetCount(triggers
) : 0;
2061 for (triggersIndex
= 0; triggersIndex
< triggersCount
; triggersIndex
++) {
2066 CFDictionaryRef trigger
;
2068 trigger
= CFArrayGetValueAtIndex(triggers
, triggersIndex
);
2069 if (!isA_CFDictionary(trigger
)) {
2070 // if not a valid "OnDemand" configuration
2075 * If we haven't tried a resulution yet, we only want to check for a name
2076 * match for domains that require to always connect.
2078 key
= onDemandRetry
? kSCNetworkConnectionOnDemandMatchDomainsOnRetry
2079 : kSCNetworkConnectionOnDemandMatchDomainsAlways
;
2080 domains
= CFDictionaryGetValue(trigger
, key
);
2081 domainsCount
= isA_CFArray(domains
) ? CFArrayGetCount(domains
) : 0;
2082 for (domainsIndex
= 0; domainsIndex
< domainsCount
; domainsIndex
++) {
2085 domain
= CFArrayGetValueAtIndex(domains
, domainsIndex
);
2086 if (!isA_CFString(domain
)) {
2087 // if not a valid match domain
2091 if (_SC_domainEndsWithDomain(hostName
, domain
)) {
2092 CFArrayRef exceptions
;
2093 int exceptionsCount
;
2094 int exceptionsIndex
;
2096 SCNetworkConnectionStatus onDemandStatus
= kSCNetworkConnectionDisconnected
;
2098 // we have a matching domain, check against exception list
2099 exceptions
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandMatchDomainsNever
);
2100 exceptionsCount
= isA_CFArray(exceptions
) ? CFArrayGetCount(exceptions
) : 0;
2101 for (exceptionsIndex
= 0; exceptionsIndex
< exceptionsCount
; exceptionsIndex
++) {
2102 CFStringRef exception
;
2104 exception
= CFArrayGetValueAtIndex(exceptions
, exceptionsIndex
);
2105 if (!isA_CFString(exception
)) {
2106 // if not a valid match exception
2110 if (_SC_domainEndsWithDomain(hostName
, exception
)) {
2111 // found matching exception
2112 if (_sc_debug
|| (debug
> 0)) {
2113 SCLog(TRUE
, LOG_INFO
, CFSTR("OnDemand match exception"));
2119 // if we have a matching domain and there were no exceptions
2120 // then we pass back the OnDemand info
2124 if (!CFDictionaryGetValueIfPresent(trigger
,
2125 kSCNetworkConnectionOnDemandStatus
,
2126 (const void **)&num
) ||
2127 !isA_CFNumber(num
) ||
2128 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &onDemandStatus
)) {
2129 onDemandStatus
= kSCNetworkConnectionDisconnected
;
2131 if (connectionStatus
!= NULL
) {
2132 *connectionStatus
= onDemandStatus
;
2135 if (connectionServiceID
!= NULL
) {
2136 *connectionServiceID
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandServiceID
);
2137 *connectionServiceID
= isA_CFString(*connectionServiceID
);
2138 if ((*connectionServiceID
!= NULL
) && (CFStringGetLength(*connectionServiceID
) > 0)) {
2139 CFRetain(*connectionServiceID
);
2141 SCLog(TRUE
, LOG_INFO
,
2142 CFSTR("OnDemand%s configuration error, no serviceID"),
2143 onDemandRetry
? " (on retry)" : "");
2145 *connectionServiceID
= NULL
;
2150 if (vpnRemoteAddress
!= NULL
) {
2151 *vpnRemoteAddress
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandRemoteAddress
);
2152 *vpnRemoteAddress
= isA_CFString(*vpnRemoteAddress
);
2153 if ((*vpnRemoteAddress
!= NULL
) && (CFStringGetLength(*vpnRemoteAddress
) > 0)) {
2154 CFRetain(*vpnRemoteAddress
);
2156 SCLog(TRUE
, LOG_INFO
,
2157 CFSTR("OnDemand%s configuration error, no server address"),
2158 onDemandRetry
? " (on retry)" : "");
2160 *vpnRemoteAddress
= NULL
;
2166 if ((connectionServiceID
!= NULL
) && (*connectionServiceID
!= NULL
)) {
2167 CFRelease(*connectionServiceID
);
2168 *connectionServiceID
= NULL
;
2170 if ((vpnRemoteAddress
!= NULL
) && (*vpnRemoteAddress
!= NULL
)) {
2171 CFRelease(*vpnRemoteAddress
);
2172 *vpnRemoteAddress
= NULL
;
2177 if (_sc_debug
|| (debug
> 0)) {
2178 SCLog(TRUE
, LOG_INFO
,
2179 CFSTR("OnDemand%s match, connection status = %d"),
2180 onDemandRetry
? " (on retry)" : "",
2189 // if (_sc_debug || (debug > 0)) {
2190 // SCLog(TRUE, LOG_INFO, CFSTR("OnDemand domain name(s) not matched"));
2195 if (configuration
!= NULL
) CFRelease(configuration
);
2201 SCNetworkConnectionCopyUserPreferences(CFDictionaryRef selectionOptions
,
2202 CFStringRef
*serviceID
,
2203 CFDictionaryRef
*userOptions
)
2205 int prefsChanged
= 1;
2206 SCDynamicStoreRef session
= NULL
;
2207 Boolean success
= FALSE
;
2211 /* initialize runtime */
2212 pthread_once(&initialized
, __SCNetworkConnectionInitialize
);
2214 /* first check for new VPN OnDemand style */
2215 if (selectionOptions
!= NULL
) {
2216 CFStringRef hostName
;
2218 hostName
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
);
2219 if (isA_CFString(hostName
)) {
2220 CFStringRef connectionServiceID
= NULL
;
2221 SCNetworkConnectionStatus connectionStatus
;
2222 Boolean onDemandRetry
;
2225 val
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandRetry
);
2226 onDemandRetry
= isA_CFBoolean(val
) ? CFBooleanGetValue(val
) : TRUE
;
2228 success
= __SCNetworkConnectionCopyOnDemandInfoWithName(&session
,
2231 &connectionServiceID
,
2235 SCLog(TRUE
, LOG_DEBUG
,
2236 CFSTR("SCNetworkConnectionCopyUserPreferences __SCNetworkConnectionCopyOnDemandInfoWithName returns %d w/status %d"),
2242 // if the hostname matches an OnDemand domain
2243 if (session
!= NULL
) {
2246 if (connectionStatus
== kSCNetworkConnectionConnected
) {
2247 // if we are already connected
2248 if (connectionServiceID
!= NULL
) {
2249 CFRelease(connectionServiceID
);
2254 *serviceID
= connectionServiceID
;
2255 *userOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2256 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
2258 } else if (!onDemandRetry
) {
2259 // if the hostname does not match an OnDemand domain and we have
2260 // not yet issued an initial DNS query (i.e. it's not a query
2261 // being retried after the VPN has been established) than we're
2263 if (session
!= NULL
) {
2271 if (notify_userprefs_token
== -1) {
2272 status
= notify_register_check(k_NetworkConnect_Notification
, ¬ify_userprefs_token
);
2273 if (status
!= NOTIFY_STATUS_OK
) {
2274 SCLog(TRUE
, LOG_ERR
, CFSTR("notify_register_check() failed, status=%lu"), status
);
2275 (void)notify_cancel(notify_userprefs_token
);
2276 notify_userprefs_token
= -1;
2278 // clear the "something has changed" state
2279 (void) notify_check(notify_userprefs_token
, &prefsChanged
);
2283 if (notify_userprefs_token
!= -1) {
2284 status
= notify_check(notify_userprefs_token
, &prefsChanged
);
2285 if (status
!= NOTIFY_STATUS_OK
) {
2286 SCLog(TRUE
, LOG_ERR
, CFSTR("notify_check() failed, status=%lu"), status
);
2287 (void)notify_cancel(notify_userprefs_token
);
2288 notify_userprefs_token
= -1;
2294 *userOptions
= NULL
;
2296 if (session
== NULL
) {
2297 session
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkConnection"), NULL
, NULL
);
2299 if (session
== NULL
) {
2300 SCLog(TRUE
, LOG_ERR
, CFSTR("Error, SCNetworkConnectionCopyUserPreferences, SCDynamicStoreCreate() returned NULL!"));
2304 if (selectionOptions
!= NULL
) {
2305 Boolean catchAllFound
= FALSE
;
2306 CFIndex catchAllService
= 0;
2307 CFIndex catchAllConfig
= 0;
2308 CFStringRef hostName
= NULL
;
2309 CFStringRef priority
= NULL
;
2310 CFArrayRef serviceNames
= NULL
;
2311 CFDictionaryRef services
= NULL
;
2312 CFIndex serviceIndex
;
2313 CFIndex servicesCount
;
2315 hostName
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
);
2316 if (hostName
== NULL
) {
2317 hostName
= CFDictionaryGetValue(selectionOptions
, kSCPropNetPPPOnDemandHostName
);
2319 hostName
= isA_CFString(hostName
);
2320 if (hostName
== NULL
)
2321 goto done_selection
; // if no hostname for matching
2323 priority
= CFDictionaryGetValue(selectionOptions
, kSCPropNetPPPOnDemandPriority
);
2324 if (!isA_CFString(priority
))
2325 priority
= kSCValNetPPPOnDemandPriorityDefault
;
2328 if (!isA_CFArray(serviceNames
))
2329 goto done_selection
;
2332 if (!isA_CFDictionary(services
))
2333 goto done_selection
;
2335 servicesCount
= CFArrayGetCount(serviceNames
);
2336 for (serviceIndex
= 0; serviceIndex
< servicesCount
; serviceIndex
++) {
2337 CFIndex configIndex
;
2338 CFIndex configsCount
;
2339 CFArrayRef serviceConfigs
;
2340 CFStringRef serviceName
;
2343 serviceName
= CFArrayGetValueAtIndex(serviceNames
, serviceIndex
);
2344 if (!isA_CFString(serviceName
))
2347 serviceConfigs
= CFDictionaryGetValue(services
, serviceName
);
2348 if (!isA_CFArray(serviceConfigs
))
2351 configsCount
= CFArrayGetCount(serviceConfigs
);
2352 for (configIndex
= 0; configIndex
< configsCount
; configIndex
++) {
2353 CFNumberRef autodial
;
2354 CFDictionaryRef config
;
2355 CFDictionaryRef pppConfig
;
2357 config
= CFArrayGetValueAtIndex(serviceConfigs
, configIndex
);
2358 if (!isA_CFDictionary(config
))
2361 pppConfig
= CFDictionaryGetValue(config
, kSCEntNetPPP
);
2362 if (!isA_CFDictionary(pppConfig
))
2365 autodial
= CFDictionaryGetValue(pppConfig
, kSCPropNetPPPOnDemandEnabled
);
2366 if (!isA_CFNumber(autodial
))
2369 CFNumberGetValue(autodial
, kCFNumberIntType
, &val
);
2372 CFIndex domainsCount
;
2373 CFIndex domainsIndex
;
2375 /* we found an conditional connection enabled configuration */
2378 domains
= CFDictionaryGetValue(pppConfig
, kSCPropNetPPPOnDemandDomains
);
2379 if (!isA_CFArray(domains
))
2382 domainsCount
= CFArrayGetCount(domains
);
2383 for (domainsIndex
= 0; domainsIndex
< domainsCount
; domainsIndex
++) {
2386 domain
= CFArrayGetValueAtIndex(domains
, domainsIndex
);
2387 if (!isA_CFString(domain
))
2390 if (!catchAllFound
&&
2391 (CFStringCompare(domain
, CFSTR(""), 0) == kCFCompareEqualTo
2392 || CFStringCompare(domain
, CFSTR("."), 0) == kCFCompareEqualTo
)) {
2393 // found a catch all
2394 catchAllFound
= TRUE
;
2395 catchAllService
= serviceIndex
;
2396 catchAllConfig
= configIndex
;
2399 if (_SC_domainEndsWithDomain(hostName
, domain
)) {
2400 // found matching configuration
2401 *serviceID
= serviceName
;
2402 CFRetain(*serviceID
);
2403 *userOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, config
);
2404 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
2405 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCPropNetPPPOnDemandPriority
, priority
);
2406 addPasswordFromKeychain(session
, *serviceID
, userOptions
);
2408 goto done_selection
;
2415 // config not found, do we have a catchall ?
2416 if (catchAllFound
) {
2417 CFDictionaryRef config
;
2418 CFArrayRef serviceConfigs
;
2419 CFStringRef serviceName
;
2421 serviceName
= CFArrayGetValueAtIndex(serviceNames
, catchAllService
);
2422 serviceConfigs
= CFDictionaryGetValue(services
, serviceName
);
2423 config
= CFArrayGetValueAtIndex(serviceConfigs
, catchAllConfig
);
2425 *serviceID
= serviceName
;
2426 CFRetain(*serviceID
);
2427 *userOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, config
);
2428 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
2429 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCPropNetPPPOnDemandPriority
, priority
);
2430 addPasswordFromKeychain(session
, *serviceID
, userOptions
);
2432 goto done_selection
;
2438 CFRelease(serviceNames
);
2440 CFRelease(services
);
2444 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionCopyUserPreferences %@"), success
? CFSTR("succeeded") : CFSTR("failed"));
2445 SCLog(TRUE
, LOG_DEBUG
, CFSTR("Selection options: %@"), selectionOptions
);
2451 /* we don't have selection options */
2453 // (1) Figure out which service ID we care about, allocate it into passed "serviceID"
2454 success
= SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(session
, serviceID
);
2456 if (success
&& (*serviceID
!= NULL
)) {
2457 // (2) Get the list of user data for this service ID
2458 CFPropertyListRef userServices
= NULL
;
2461 // (3) We are expecting an array if the user has defined records for this service ID or NULL if the user hasn't
2462 if (userServices
!= NULL
) {
2463 if (isA_CFArray(userServices
)) {
2464 // (4) Get the default set of user options for this service
2465 success
= SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray((CFArrayRef
)userServices
,
2467 if(success
&& (userOptions
!= NULL
)) {
2468 addPasswordFromKeychain(session
, *serviceID
, userOptions
);
2471 SCLog(TRUE
, LOG_DEBUG
, CFSTR("Error, userServices are not of type CFArray!"));
2474 CFRelease(userServices
); // this is OK because SCNetworkConnectionPrivateISExpectedCFType() checks for NULL
2479 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionCopyUserPreferences %@, no selection options"), success
? CFSTR("succeeded") : CFSTR("failed"));
2487 //*******************************************************************************************
2488 // SCNetworkConnectionPrivateCopyDefaultServiceIDForDial
2489 // ----------------------------------------------------
2490 // Try to find the service id to connect
2491 // (1) Start by looking at the last service used in Network Pref / Network menu extra
2492 // (2) If Network Pref / Network menu extra has not been used, find the PPP service
2493 // with the highest ordering
2494 //********************************************************************************************
2496 SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(SCDynamicStoreRef session
, CFStringRef
*serviceID
)
2498 Boolean foundService
= FALSE
;
2499 CFPropertyListRef lastServiceSelectedInIC
= NULL
;
2503 // we found the service the user last had open in IC
2504 if (lastServiceSelectedInIC
!= NULL
) {
2505 // make sure its a PPP service
2506 if (SCNetworkConnectionPrivateIsPPPService(session
, lastServiceSelectedInIC
, kSCValNetInterfaceSubTypePPPSerial
, kSCValNetInterfaceSubTypePPPoE
)) {
2507 // make sure the service that we found is valid
2508 CFDictionaryRef dict
;
2511 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2512 kSCDynamicStoreDomainSetup
,
2513 lastServiceSelectedInIC
,
2514 kSCEntNetInterface
);
2515 dict
= SCDynamicStoreCopyValue(session
, key
);
2519 *serviceID
= CFRetain(lastServiceSelectedInIC
);
2520 foundService
= TRUE
;
2523 CFRelease(lastServiceSelectedInIC
);
2526 if (!foundService
) {
2527 foundService
= SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(session
, serviceID
);
2530 return foundService
;
2533 //********************************************************************************
2534 // SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore
2535 // -------------------------------------------------------
2536 // Find the highest ordered PPP service in the dynamic store
2537 //********************************************************************************
2539 SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(SCDynamicStoreRef session
, CFStringRef
*serviceID
)
2541 CFDictionaryRef dict
= NULL
;
2542 CFStringRef key
= NULL
;
2543 CFArrayRef serviceIDs
= NULL
;
2544 Boolean success
= FALSE
;
2552 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, kSCDynamicStoreDomainSetup
, kSCEntNetIPv4
);
2554 fprintf(stderr
, "Error, Setup Key == NULL!\n");
2558 dict
= SCDynamicStoreCopyValue(session
, key
);
2559 if (!isA_CFDictionary(dict
)) {
2560 fprintf(stderr
, "no global IPv4 entity\n");
2564 serviceIDs
= CFDictionaryGetValue(dict
, kSCPropNetServiceOrder
); // array of service id's
2565 if (!isA_CFArray(serviceIDs
)) {
2566 fprintf(stderr
, "service order not specified\n");
2570 count
= CFArrayGetCount(serviceIDs
);
2571 for (i
= 0; i
< count
; i
++) {
2572 CFStringRef service
= CFArrayGetValueAtIndex(serviceIDs
, i
);
2574 if (SCNetworkConnectionPrivateIsPPPService(session
, service
, kSCValNetInterfaceSubTypePPPSerial
, kSCValNetInterfaceSubTypePPPoE
)) {
2575 *serviceID
= CFRetain(service
);
2582 if (key
!= NULL
) CFRelease(key
);
2583 if (dict
!= NULL
) CFRelease(dict
);
2588 //********************************************************************************
2589 // SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray
2590 // ---------------------------------------------------------
2591 // Copy over user preferences for a particular service if they exist
2592 //********************************************************************************
2594 SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray
, CFDictionaryRef
*userOptions
)
2596 CFIndex count
= CFArrayGetCount(userOptionsArray
);
2599 for (i
= 0; i
< count
; i
++) {
2600 // (1) Find the dictionary
2601 CFPropertyListRef propertyList
= CFArrayGetValueAtIndex(userOptionsArray
, i
);
2603 if (isA_CFDictionary(propertyList
) != NULL
) {
2604 // See if there's a value for dial on demand
2605 CFPropertyListRef value
;
2607 value
= CFDictionaryGetValue((CFDictionaryRef
)propertyList
, k_Dial_Default_Key
);
2608 if (isA_CFBoolean(value
) != NULL
) {
2609 if (CFBooleanGetValue(value
)) {
2610 // we found the default user options
2611 *userOptions
= CFDictionaryCreateCopy(NULL
,
2612 (CFDictionaryRef
)propertyList
);
2622 //********************************************************************************
2623 // SCNetworkConnectionPrivateIsServiceType
2624 // --------------------------------------
2625 // Check and see if the service is a PPP service of the given types
2626 //********************************************************************************
2628 SCNetworkConnectionPrivateIsPPPService(SCDynamicStoreRef session
, CFStringRef serviceID
, CFStringRef subType1
, CFStringRef subType2
)
2630 CFStringRef entityKey
;
2631 Boolean isPPPService
= FALSE
;
2632 Boolean isMatchingSubType
= FALSE
;
2633 CFDictionaryRef serviceDict
;
2635 entityKey
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2636 kSCDynamicStoreDomainSetup
,
2638 kSCEntNetInterface
);
2639 if (entityKey
== NULL
) {
2643 serviceDict
= SCDynamicStoreCopyValue(session
, entityKey
);
2644 if (serviceDict
!= NULL
) {
2645 if (isA_CFDictionary(serviceDict
)) {
2647 CFStringRef subtype
;
2649 type
= CFDictionaryGetValue(serviceDict
, kSCPropNetInterfaceType
);
2650 if (isA_CFString(type
)) {
2651 isPPPService
= CFEqual(type
, kSCValNetInterfaceTypePPP
);
2654 subtype
= CFDictionaryGetValue(serviceDict
, kSCPropNetInterfaceSubType
);
2655 if (isA_CFString(subtype
)) {
2656 isMatchingSubType
= CFEqual(subtype
, subType1
);
2657 if (!isMatchingSubType
&& subType2
)
2658 isMatchingSubType
= CFEqual(subtype
, subType2
);
2661 CFRelease(serviceDict
);
2663 CFRelease(entityKey
);
2665 return (isPPPService
&& isMatchingSubType
);
2668 //********************************************************************************
2669 // addPasswordFromKeychain
2670 // --------------------------------------
2671 // Get the password and shared secret out of the keychain and add
2672 // them to the PPP and IPSec dictionaries
2673 //********************************************************************************
2675 addPasswordFromKeychain(SCDynamicStoreRef session
, CFStringRef serviceID
, CFDictionaryRef
*userOptions
)
2677 CFPropertyListRef uniqueID
;
2678 CFStringRef password
;
2679 CFStringRef sharedsecret
= NULL
;
2681 /* user options must exist */
2682 if (*userOptions
== NULL
)
2685 /* first, get the unique identifier used to store passwords in the keychain */
2686 uniqueID
= CFDictionaryGetValue(*userOptions
, k_Unique_Id_Key
);
2687 if (!isA_CFString(uniqueID
))
2690 /* first, get the PPP password */
2691 password
= copyPasswordFromKeychain(uniqueID
);
2693 /* then, if necessary, get the IPSec Shared Secret */
2694 if (SCNetworkConnectionPrivateIsPPPService(session
, serviceID
, kSCValNetInterfaceSubTypeL2TP
, 0)) {
2695 CFMutableStringRef uniqueIDSS
;
2697 uniqueIDSS
= CFStringCreateMutableCopy(NULL
, 0, uniqueID
);
2698 CFStringAppend(uniqueIDSS
, CFSTR(".SS"));
2699 sharedsecret
= copyPasswordFromKeychain(uniqueIDSS
);
2700 CFRelease(uniqueIDSS
);
2703 /* did we find our information in the key chain ? */
2704 if ((password
!= NULL
) || (sharedsecret
!= NULL
)) {
2705 CFMutableDictionaryRef newOptions
;
2707 newOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, *userOptions
);
2710 if (password
!= NULL
) {
2711 CFDictionaryRef entity
;
2712 CFMutableDictionaryRef newEntity
;
2714 entity
= CFDictionaryGetValue(*userOptions
, kSCEntNetPPP
);
2715 if (isA_CFDictionary(entity
))
2716 newEntity
= CFDictionaryCreateMutableCopy(NULL
, 0, entity
);
2718 newEntity
= CFDictionaryCreateMutable(NULL
,
2720 &kCFTypeDictionaryKeyCallBacks
,
2721 &kCFTypeDictionaryValueCallBacks
);
2724 /* set the PPP password */
2725 CFDictionarySetValue(newEntity
, kSCPropNetPPPAuthPassword
, uniqueID
);
2726 CFDictionarySetValue(newEntity
, kSCPropNetPPPAuthPasswordEncryption
, kSCValNetPPPAuthPasswordEncryptionKeychain
);
2727 CFRelease(password
);
2729 /* update the PPP entity */
2730 CFDictionarySetValue(newOptions
, kSCEntNetPPP
, newEntity
);
2731 CFRelease(newEntity
);
2734 /* IPSec Shared Secret */
2735 if (sharedsecret
!= NULL
) {
2736 CFDictionaryRef entity
;
2737 CFMutableDictionaryRef newEntity
;
2739 entity
= CFDictionaryGetValue(*userOptions
, kSCEntNetIPSec
);
2740 if (isA_CFDictionary(entity
))
2741 newEntity
= CFDictionaryCreateMutableCopy(NULL
, 0, entity
);
2743 newEntity
= CFDictionaryCreateMutable(NULL
,
2745 &kCFTypeDictionaryKeyCallBacks
,
2746 &kCFTypeDictionaryValueCallBacks
);
2748 /* set the IPSec Shared Secret */
2749 CFDictionarySetValue(newEntity
, kSCPropNetIPSecSharedSecret
, sharedsecret
);
2750 CFRelease(sharedsecret
);
2752 /* update the IPSec entity */
2753 CFDictionarySetValue(newOptions
, kSCEntNetIPSec
, newEntity
);
2754 CFRelease(newEntity
);
2757 /* update the userOptions dictionary */
2758 CFRelease(*userOptions
);
2759 *userOptions
= CFDictionaryCreateCopy(NULL
, newOptions
);
2760 CFRelease(newOptions
);
2765 #if !TARGET_OS_IPHONE
2766 //********************************************************************************
2767 // copyKeychainEnumerator
2768 // --------------------------------------
2769 // Gather Keychain Enumerator
2770 //********************************************************************************
2772 copyKeychainEnumerator(CFStringRef uniqueIdentifier
)
2774 CFArrayRef itemArray
= NULL
;
2775 CFMutableDictionaryRef query
;
2778 query
= CFDictionaryCreateMutable(NULL
,
2780 &kCFTypeDictionaryKeyCallBacks
,
2781 &kCFTypeDictionaryValueCallBacks
);
2782 CFDictionarySetValue(query
, kSecClass
, kSecClassGenericPassword
);
2783 CFDictionarySetValue(query
, kSecAttrService
, uniqueIdentifier
);
2784 CFDictionarySetValue(query
, kSecReturnRef
, kCFBooleanTrue
);
2785 CFDictionarySetValue(query
, kSecMatchLimit
, kSecMatchLimitAll
);
2786 result
= SecItemCopyMatching(query
, (CFTypeRef
*)&itemArray
);
2788 if ((result
!= noErr
) && (itemArray
!= NULL
)) {
2789 CFRelease(itemArray
);
2795 #endif // !TARGET_OS_IPHONE
2797 //********************************************************************************
2798 // copyPasswordFromKeychain
2799 // --------------------------------------
2800 // Given a uniqueID, retrieve the password from the keychain
2801 //********************************************************************************
2803 copyPasswordFromKeychain(CFStringRef uniqueID
)
2805 #if !TARGET_OS_IPHONE
2806 CFArrayRef enumerator
;
2808 CFStringRef password
= NULL
;
2810 enumerator
= copyKeychainEnumerator(uniqueID
);
2811 if (enumerator
== NULL
) {
2812 return NULL
; // if no keychain enumerator
2815 n
= CFArrayGetCount(enumerator
);
2819 SecKeychainItemRef itemRef
;
2822 itemRef
= (SecKeychainItemRef
)CFArrayGetValueAtIndex(enumerator
, 0);
2823 result
= SecKeychainItemCopyContent(itemRef
, // itemRef
2827 (void *)&data
); // outData
2828 if ((result
== noErr
) && (data
!= NULL
) && (dataLen
> 0)) {
2829 password
= CFStringCreateWithBytes(NULL
, data
, dataLen
, kCFStringEncodingUTF8
, TRUE
);
2830 (void) SecKeychainItemFreeContent(NULL
, data
);
2835 CFRelease(enumerator
);
2838 #else // !TARGET_OS_IPHONE
2840 #endif // !TARGET_OS_IPHONE