2 * Copyright (c) 2003-2011 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 /* ref to PPP controller for control messages */
92 mach_port_t session_port
;
94 /* ref to PPP controller for notification messages */
95 CFMachPortRef notify_port
;
97 /* keep track of whether we're acquired the initial status */
100 /* run loop source, callout, context, rl scheduling info */
102 CFRunLoopSourceRef rls
;
103 SCNetworkConnectionCallBack rlsFunction
;
104 SCNetworkConnectionContext rlsContext
;
105 CFMutableArrayRef rlList
;
107 /* SCNetworkConnectionSetDispatchQueue */
108 dispatch_queue_t dispatchQueue
;
109 dispatch_queue_t callbackQueue
;
110 dispatch_source_t callbackSource
;
112 } SCNetworkConnectionPrivate
, *SCNetworkConnectionPrivateRef
;
115 static __inline__ CFTypeRef
116 isA_SCNetworkConnection(CFTypeRef obj
)
118 return (isA_CFType(obj
, SCNetworkConnectionGetTypeID()));
123 __SCNetworkConnectionCopyDescription(CFTypeRef cf
)
125 CFAllocatorRef allocator
= CFGetAllocator(cf
);
126 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)cf
;
127 CFMutableStringRef result
;
129 result
= CFStringCreateMutable(allocator
, 0);
130 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkConnection, %p [%p]> {"), cf
, allocator
);
131 CFStringAppendFormat(result
, NULL
, CFSTR("service = %p"), connectionPrivate
->service
);
132 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
133 CFStringAppendFormat(result
, NULL
, CFSTR(", server port = %p"), connectionPrivate
->session_port
);
135 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
142 __SCNetworkConnectionDeallocate(CFTypeRef cf
)
144 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)cf
;
146 /* release resources */
147 pthread_mutex_destroy(&connectionPrivate
->lock
);
149 if (connectionPrivate
->rls
!= NULL
) {
150 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
151 CFRelease(connectionPrivate
->rls
);
154 if (connectionPrivate
->rlList
!= NULL
) {
155 CFRelease(connectionPrivate
->rlList
);
158 if (connectionPrivate
->notify_port
!= NULL
) {
159 mach_port_t mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
161 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionDeallocate notify_port", mp
);
162 CFMachPortInvalidate(connectionPrivate
->notify_port
);
163 CFRelease(connectionPrivate
->notify_port
);
164 mach_port_mod_refs(mach_task_self(), mp
, MACH_PORT_RIGHT_RECEIVE
, -1);
167 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
168 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionDeallocate session_port", connectionPrivate
->session_port
);
169 (void) mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
172 if (connectionPrivate
->rlsContext
.release
!= NULL
)
173 (*connectionPrivate
->rlsContext
.release
)(connectionPrivate
->rlsContext
.info
);
175 CFRelease(connectionPrivate
->service
);
181 static CFTypeID __kSCNetworkConnectionTypeID
= _kCFRuntimeNotATypeID
;
183 static const CFRuntimeClass __SCNetworkConnectionClass
= {
185 "SCNetworkConnection", // className
188 __SCNetworkConnectionDeallocate
, // dealloc
191 NULL
, // copyFormattingDesc
192 __SCNetworkConnectionCopyDescription
// copyDebugDesc
199 /* the process has forked (and we are the child process) */
201 scnc_server
= MACH_PORT_NULL
;
207 __SCNetworkConnectionInitialize(void)
211 /* get the debug environment variable */
212 env
= getenv("PPPDebug");
214 if (sscanf(env
, "%d", &debug
) != 1) {
215 /* PPPDebug value is not valid (or non-numeric), set debug to 1 */
220 /* register with CoreFoundation */
221 __kSCNetworkConnectionTypeID
= _CFRuntimeRegisterClass(&__SCNetworkConnectionClass
);
223 /* add handler to cleanup after fork() */
224 (void) pthread_atfork(NULL
, NULL
, childForkHandler
);
231 __SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection
);
235 __SCNetworkConnectionCallBack(CFMachPortRef port
, void * msg
, CFIndex size
, void * info
)
237 mach_no_senders_notification_t
*buf
= msg
;
238 mach_msg_id_t msgid
= buf
->not_header
.msgh_id
;
240 SCNetworkConnectionRef connection
= (SCNetworkConnectionRef
)info
;
241 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
243 void (*context_release
)(const void *);
244 SCNetworkConnectionCallBack rlsFunction
= NULL
;
245 SCNetworkConnectionStatus nc_status
= kSCNetworkConnectionInvalid
;
247 if (msgid
== MACH_NOTIFY_NO_SENDERS
) {
248 // re-establish notification
249 SCLog(_sc_verbose
, LOG_DEBUG
, CFSTR("__SCNetworkConnectionCallBack: PPPController server died"));
250 (void)__SCNetworkConnectionReconnectNotifications(connection
);
253 pthread_mutex_lock(&connectionPrivate
->lock
);
255 if (!connectionPrivate
->scheduled
) {
256 // if not currently scheduled
260 rlsFunction
= connectionPrivate
->rlsFunction
;
261 if (rlsFunction
== NULL
) {
265 if ((connectionPrivate
->rlsContext
.retain
!= NULL
) && (connectionPrivate
->rlsContext
.info
!= NULL
)) {
266 context_info
= (void *)(*connectionPrivate
->rlsContext
.retain
)(connectionPrivate
->rlsContext
.info
);
267 context_release
= connectionPrivate
->rlsContext
.release
;
269 context_info
= connectionPrivate
->rlsContext
.info
;
270 context_release
= NULL
;
275 pthread_mutex_unlock(&connectionPrivate
->lock
);
277 if (rlsFunction
== NULL
) {
281 nc_status
= SCNetworkConnectionGetStatus(connection
);
282 (*rlsFunction
)(connection
, nc_status
, context_info
);
283 if ((context_release
!= NULL
) && (context_info
!= NULL
)) {
284 (*context_release
)(context_info
);
292 #pragma mark SCNetworkConnection APIs
296 pppMPCopyDescription(const void *info
)
298 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)info
;
300 return CFStringCreateWithFormat(NULL
,
302 CFSTR("<SCNetworkConnection MP %p> {service = %@, callout = %p}"),
304 connectionPrivate
->service
,
305 connectionPrivate
->rlsFunction
);
309 static SCNetworkConnectionPrivateRef
310 __SCNetworkConnectionCreatePrivate(CFAllocatorRef allocator
,
311 SCNetworkServiceRef service
,
312 SCNetworkConnectionCallBack callout
,
313 SCNetworkConnectionContext
*context
)
315 SCNetworkConnectionPrivateRef connectionPrivate
= NULL
;
319 /* initialize runtime */
320 pthread_once(&initialized
, __SCNetworkConnectionInitialize
);
322 /* allocate NetworkConnection */
323 size
= sizeof(SCNetworkConnectionPrivate
) - sizeof(CFRuntimeBase
);
324 connectionPrivate
= (SCNetworkConnectionPrivateRef
)_CFRuntimeCreateInstance(allocator
, __kSCNetworkConnectionTypeID
, size
, NULL
);
325 if (connectionPrivate
== NULL
) {
329 /* zero the data structure */
330 bzero(((u_char
*)connectionPrivate
)+sizeof(CFRuntimeBase
), size
);
332 pthread_mutex_init(&connectionPrivate
->lock
, NULL
);
334 /* save the service */
335 connectionPrivate
->service
= CFRetain(service
);
337 connectionPrivate
->rlsFunction
= callout
;
340 bcopy(context
, &connectionPrivate
->rlsContext
, sizeof(SCNetworkConnectionContext
));
341 if (context
->retain
!= NULL
) {
342 connectionPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
346 /* success, return the connection reference */
347 return connectionPrivate
;
351 /* failure, clean up and leave */
352 if (connectionPrivate
!= NULL
) {
353 CFRelease(connectionPrivate
);
356 _SCErrorSet(kSCStatusFailed
);
362 __SCNetworkConnectionServerPort(kern_return_t
*status
)
364 mach_port_t server
= MACH_PORT_NULL
;
366 #ifdef BOOTSTRAP_PRIVILEGED_SERVER
367 *status
= bootstrap_look_up2(bootstrap_port
,
368 PPPCONTROLLER_SERVER
,
371 BOOTSTRAP_PRIVILEGED_SERVER
);
372 #else // BOOTSTRAP_PRIVILEGED_SERVER
373 *status
= bootstrap_look_up(bootstrap_port
, PPPCONTROLLER_SERVER
, &server
);
374 #endif // BOOTSTRAP_PRIVILEGED_SERVER
377 case BOOTSTRAP_SUCCESS
:
378 // service currently registered, "a good thing" (tm)
380 case BOOTSTRAP_NOT_PRIVILEGED
:
381 // the service is not privileged
383 case BOOTSTRAP_UNKNOWN_SERVICE
:
384 // service not currently registered, try again later
388 SCLog(_sc_verbose
, LOG_DEBUG
,
389 CFSTR("SCNetworkConnection bootstrap_look_up() failed: status=%s"),
390 bootstrap_strerror(*status
));
395 return MACH_PORT_NULL
;
400 __SCNetworkConnectionSessionPort(SCNetworkConnectionPrivateRef connectionPrivate
)
404 CFDataRef dataRef
= NULL
;
405 mach_port_t notify_port
= MACH_PORT_NULL
;
406 mach_port_t oldNotify
= MACH_PORT_NULL
;
408 int sc_status
= kSCStatusFailed
;
409 mach_port_t server
= scnc_server
;
410 kern_return_t status
= KERN_SUCCESS
;
411 mach_port_t au_session
= MACH_PORT_NULL
;
413 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
414 return connectionPrivate
->session_port
;
417 if (!_SCSerializeString(SCNetworkServiceGetServiceID(connectionPrivate
->service
), &dataRef
, &data
, &dataLen
)) {
421 if (connectionPrivate
->notify_port
!= NULL
) {
422 mach_port_t mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
424 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionReconnectNotifications mp", mp
);
425 CFMachPortInvalidate(connectionPrivate
->notify_port
);
426 CFRelease(connectionPrivate
->notify_port
);
427 connectionPrivate
->notify_port
= NULL
;
428 mach_port_mod_refs(mach_task_self(), mp
, MACH_PORT_RIGHT_RECEIVE
, -1);
431 au_session
= audit_session_self();
433 // open a new session with the server
435 if ((connectionPrivate
->rlsFunction
!= NULL
) && (notify_port
== MACH_PORT_NULL
)) {
436 // allocate port (for server response)
437 status
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE
, ¬ify_port
);
438 if (status
!= KERN_SUCCESS
) {
439 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkConnectionSessionPort mach_port_allocate(): %s"), mach_error_string(status
));
444 // add send right (passed to the server)
445 status
= mach_port_insert_right(mach_task_self(),
448 MACH_MSG_TYPE_MAKE_SEND
);
449 if (status
!= KERN_SUCCESS
) {
450 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkConnectionSessionPort mach_port_insert_right(): %s"), mach_error_string(status
));
451 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
457 if (server
!= MACH_PORT_NULL
) {
458 status
= pppcontroller_attach(server
,
464 &connectionPrivate
->session_port
,
466 if (status
== KERN_SUCCESS
) {
467 if (sc_status
!= kSCStatusOK
) {
468 SCLog(TRUE
, LOG_DEBUG
,
469 CFSTR("__SCNetworkConnectionSessionPort : attach w/error, sc_status=%s%s"),
470 SCErrorString(sc_status
),
471 (connectionPrivate
->session_port
!= MACH_PORT_NULL
) ? ", w/session_port!=MACH_PORT_NULL" : "");
473 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
474 __MACH_PORT_DEBUG(TRUE
,
475 "*** __SCNetworkConnectionSessionPort session_port (attach w/error, cleanup)",
476 connectionPrivate
->session_port
);
477 mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
478 connectionPrivate
->session_port
= MACH_PORT_NULL
;
481 if (notify_port
!= MACH_PORT_NULL
) {
482 __MACH_PORT_DEBUG(TRUE
,
483 "*** __SCNetworkConnectionSessionPort notify_port (attach w/error, cleanup)",
485 (void) mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
486 notify_port
= MACH_PORT_NULL
;
492 // our [cached] server port is not valid
493 SCLog(TRUE
, LOG_DEBUG
, CFSTR("__SCNetworkConnectionSessionPort : !attach: %s"), SCErrorString(status
));
494 if (status
== MACH_SEND_INVALID_DEST
) {
495 // the server is not yet available
496 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionSessionPort notify_port (!dest)", notify_port
);
497 } else if (status
== MIG_SERVER_DIED
) {
498 __MACH_PORT_DEBUG(TRUE
, "*** __SCNetworkConnectionSessionPort notify_port (!mig)", notify_port
);
499 // the server we were using is gone and we've lost our send right
500 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
501 notify_port
= MACH_PORT_NULL
;
503 // if we got an unexpected error, don't retry
509 pthread_mutex_lock(&scnc_lock
);
510 if (scnc_server
!= MACH_PORT_NULL
) {
511 if (server
== scnc_server
) {
512 // if the server we tried returned the error
513 (void)mach_port_deallocate(mach_task_self(), scnc_server
);
514 scnc_server
= __SCNetworkConnectionServerPort(&sc_status
);
516 // another thread has refreshed the server port
519 scnc_server
= __SCNetworkConnectionServerPort(&sc_status
);
521 server
= scnc_server
;
522 pthread_mutex_unlock(&scnc_lock
);
524 if (server
== MACH_PORT_NULL
) {
525 // if server not available
526 if (sc_status
== BOOTSTRAP_UNKNOWN_SERVICE
) {
527 // if first retry attempt, wait for SCDynamicStore server
529 SCDynamicStoreRef store
;
531 store
= SCDynamicStoreCreate(NULL
,
532 CFSTR("SCNetworkConnection connect"),
540 // wait up to 2.5 seconds for the [SCNetworkConnection] server
542 if ((retry
+= 50) < 2500) {
543 usleep(50 * 1000); // sleep 50ms between attempts
551 if (notify_port
!= MACH_PORT_NULL
) {
552 if (connectionPrivate
->session_port
!= MACH_PORT_NULL
) {
553 CFMachPortContext context
= { 0
554 , (void *)connectionPrivate
557 , pppMPCopyDescription
560 // request a notification when/if the server dies
561 status
= mach_port_request_notification(mach_task_self(),
563 MACH_NOTIFY_NO_SENDERS
,
566 MACH_MSG_TYPE_MAKE_SEND_ONCE
,
568 if (status
!= KERN_SUCCESS
) {
569 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkConnectionSessionPort mach_port_request_notification(): %s"), mach_error_string(status
));
570 mach_port_mod_refs(mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
575 if (oldNotify
!= MACH_PORT_NULL
) {
576 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkConnectionSessionPort(): oldNotify != MACH_PORT_NULL"));
579 // create CFMachPort for SCNetworkConnection notification callback
580 connectionPrivate
->notify_port
= _SC_CFMachPortCreateWithPort("SCNetworkConnection",
582 __SCNetworkConnectionCallBack
,
585 // we need to try a bit harder to acquire the initial status
586 connectionPrivate
->haveStatus
= FALSE
;
588 // with no server port, release the notification port we allocated
589 __MACH_PORT_DEBUG(TRUE
,
590 "*** __SCNetworkConnectionSessionPort notify_port (!server)",
592 (void) mach_port_mod_refs (mach_task_self(), notify_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
593 (void) mach_port_deallocate(mach_task_self(), notify_port
);
594 notify_port
= MACH_PORT_NULL
;
601 if (au_session
!= MACH_PORT_NULL
) {
602 (void)mach_port_deallocate(mach_task_self(), au_session
);
605 if (dataRef
!= NULL
) CFRelease(dataRef
);
609 __MACH_PORT_DEBUG(connectionPrivate
->session_port
!= MACH_PORT_NULL
,
610 "*** __SCNetworkConnectionSessionPort session_port",
611 connectionPrivate
->session_port
);
612 __MACH_PORT_DEBUG(notify_port
!= MACH_PORT_NULL
,
613 "*** __SCNetworkConnectionSessionPort notify_port",
616 case BOOTSTRAP_UNKNOWN_SERVICE
:
618 (status
== KERN_SUCCESS
) ? LOG_DEBUG
: LOG_ERR
,
619 CFSTR("PPPController not available"));
623 (status
== KERN_SUCCESS
) ? LOG_DEBUG
: LOG_ERR
,
624 CFSTR("__SCNetworkConnectionSessionPort pppcontroller_attach(): %s"),
625 SCErrorString(sc_status
));
629 if (sc_status
!= kSCStatusOK
) {
630 _SCErrorSet(sc_status
);
633 return connectionPrivate
->session_port
;
638 __SCNetworkConnectionReconnect(SCNetworkConnectionRef connection
)
640 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
643 port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
644 return (port
!= MACH_PORT_NULL
);
649 __SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection
)
651 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
652 dispatch_queue_t dispatchQueue
= NULL
;
654 CFArrayRef rlList
= NULL
;
656 // Before we fully tearing down our [old] notifications, make sure
657 // we have retained any information that is needed to re-register the
658 // [new] notifications.
660 pthread_mutex_lock(&connectionPrivate
->lock
);
662 if (connectionPrivate
->rlList
!= NULL
) {
663 rlList
= CFArrayCreateCopy(NULL
, connectionPrivate
->rlList
);
665 if (connectionPrivate
->dispatchQueue
!= NULL
) {
666 dispatchQueue
= connectionPrivate
->dispatchQueue
;
667 dispatch_retain(dispatchQueue
);
670 // cancel [old] notifications
671 if (connectionPrivate
->rlList
!= NULL
) {
672 CFRelease(connectionPrivate
->rlList
);
673 connectionPrivate
->rlList
= NULL
;
675 if (connectionPrivate
->rls
!= NULL
) {
676 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
677 CFRelease(connectionPrivate
->rls
);
678 connectionPrivate
->rls
= NULL
;
680 if (connectionPrivate
->callbackSource
!= NULL
) {
681 dispatch_source_cancel(connectionPrivate
->callbackSource
);
682 if (connectionPrivate
->callbackQueue
!= dispatch_get_current_queue()) {
683 // ensure the cancellation has completed
684 dispatch_sync(connectionPrivate
->callbackQueue
, ^{});
686 dispatch_release(connectionPrivate
->callbackSource
);
687 connectionPrivate
->callbackSource
= NULL
;
689 if (connectionPrivate
->callbackQueue
!= NULL
) {
690 dispatch_release(connectionPrivate
->callbackQueue
);
691 connectionPrivate
->callbackQueue
= NULL
;
693 if (connectionPrivate
->dispatchQueue
!= NULL
) {
694 dispatch_release(connectionPrivate
->dispatchQueue
);
695 connectionPrivate
->dispatchQueue
= NULL
;
698 connectionPrivate
->scheduled
= FALSE
;
700 pthread_mutex_unlock(&connectionPrivate
->lock
);
703 if (rlList
!= NULL
) {
707 n
= CFArrayGetCount(rlList
);
708 for (i
= 0; i
< n
; i
+= 3) {
709 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(rlList
, i
+1);
710 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(rlList
, i
+2);
712 ok
= SCNetworkConnectionScheduleWithRunLoop(connection
, rl
, rlMode
);
714 SCLog((SCError() != BOOTSTRAP_UNKNOWN_SERVICE
),
716 CFSTR("__SCNetworkConnectionReconnectNotifications: SCNetworkConnectionScheduleWithRunLoop() failed"));
720 } else if (dispatchQueue
!= NULL
) {
721 ok
= SCNetworkConnectionSetDispatchQueue(connection
, dispatchQueue
);
723 SCLog((SCError() != BOOTSTRAP_UNKNOWN_SERVICE
),
725 CFSTR("__SCNetworkConnectionReconnectNotifications: SCNetworkConnectionSetDispatchQueue() failed"));
735 if (rlList
!= NULL
) {
738 if (dispatchQueue
!= NULL
) {
739 dispatch_release(dispatchQueue
);
744 CFSTR("SCNetworkConnection server %s, notification not restored"),
745 (SCError() == BOOTSTRAP_UNKNOWN_SERVICE
) ? "shutdown" : "failed");
753 __SCNetworkConnectionNeedsRetry(SCNetworkConnectionRef connection
,
754 const char *error_label
,
755 kern_return_t status
,
758 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
760 if (status
== KERN_SUCCESS
) {
764 if ((status
== MACH_SEND_INVALID_DEST
) || (status
== MIG_SERVER_DIED
)) {
765 // the server's gone and our session port's dead, remove the dead name right
766 (void) mach_port_deallocate(mach_task_self(), connectionPrivate
->session_port
);
768 // we got an unexpected error, leave the [session] port alone
769 SCLog(TRUE
, LOG_ERR
, CFSTR("%s: %s"), error_label
, mach_error_string(status
));
771 connectionPrivate
->session_port
= MACH_PORT_NULL
;
772 if ((status
== MACH_SEND_INVALID_DEST
) || (status
== MIG_SERVER_DIED
)) {
773 if (__SCNetworkConnectionReconnect(connection
)) {
784 SCNetworkConnectionGetTypeID(void) {
785 pthread_once(&initialized
, __SCNetworkConnectionInitialize
); /* initialize runtime */
786 return __kSCNetworkConnectionTypeID
;
790 CFArrayRef
/* of SCNetworkServiceRef's */
791 SCNetworkConnectionCopyAvailableServices(SCNetworkSetRef set
)
793 CFMutableArrayRef available
;
794 Boolean tempSet
= FALSE
;
797 SCPreferencesRef prefs
;
799 prefs
= SCPreferencesCreate(NULL
, CFSTR("SCNetworkConnectionCopyAvailableServices"), NULL
);
801 set
= SCNetworkSetCopyCurrent(prefs
);
807 available
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
812 services
= SCNetworkSetCopyServices(set
);
813 if (services
!= NULL
) {
817 n
= CFArrayGetCount(services
);
818 for (i
= 0; i
< n
; i
++) {
819 SCNetworkInterfaceRef interface
;
820 CFStringRef interfaceType
;
821 SCNetworkServiceRef service
;
823 service
= CFArrayGetValueAtIndex(services
, i
);
824 interface
= SCNetworkServiceGetInterface(service
);
825 if (interface
== NULL
) {
829 interfaceType
= SCNetworkInterfaceGetInterfaceType(interface
);
830 if (CFEqual(interfaceType
, kSCNetworkInterfaceTypePPP
) ||
831 CFEqual(interfaceType
, kSCNetworkInterfaceTypeVPN
) ||
832 CFEqual(interfaceType
, kSCNetworkInterfaceTypeIPSec
)) {
833 CFArrayAppendValue(available
, service
);
841 if (tempSet
&& (set
!= NULL
)) {
848 SCNetworkConnectionRef
849 SCNetworkConnectionCreateWithService(CFAllocatorRef allocator
,
850 SCNetworkServiceRef service
,
851 SCNetworkConnectionCallBack callout
,
852 SCNetworkConnectionContext
*context
)
854 SCNetworkConnectionPrivateRef connectionPrivate
;
856 if (!isA_SCNetworkService(service
)) {
857 _SCErrorSet(kSCStatusInvalidArgument
);
861 connectionPrivate
= __SCNetworkConnectionCreatePrivate(allocator
, service
, callout
, context
);
862 return (SCNetworkConnectionRef
)connectionPrivate
;
866 SCNetworkConnectionRef
867 SCNetworkConnectionCreateWithServiceID(CFAllocatorRef allocator
,
868 CFStringRef serviceID
,
869 SCNetworkConnectionCallBack callout
,
870 SCNetworkConnectionContext
*context
)
872 SCNetworkConnectionRef connection
;
873 SCNetworkServiceRef service
;
875 if (!isA_CFString(serviceID
)) {
876 _SCErrorSet(kSCStatusInvalidArgument
);
880 service
= _SCNetworkServiceCopyActive(NULL
, serviceID
);
881 if (service
== NULL
) {
885 connection
= SCNetworkConnectionCreateWithService(allocator
, service
, callout
, context
);
893 SCNetworkConnectionCopyServiceID(SCNetworkConnectionRef connection
)
895 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
896 CFStringRef serviceID
;
898 if (!isA_SCNetworkConnection(connection
)) {
899 _SCErrorSet(kSCStatusInvalidArgument
);
903 serviceID
= SCNetworkServiceGetServiceID(connectionPrivate
->service
);
904 return CFRetain(serviceID
);
909 SCNetworkConnectionCopyStatistics(SCNetworkConnectionRef connection
)
911 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
912 xmlDataOut_t data
= NULL
;
913 mach_msg_type_number_t datalen
= 0;
914 int sc_status
= kSCStatusFailed
;
915 mach_port_t session_port
;
916 CFPropertyListRef statistics
= NULL
;
917 kern_return_t status
;
919 if (!isA_SCNetworkConnection(connection
)) {
920 _SCErrorSet(kSCStatusInvalidArgument
);
924 pthread_mutex_lock(&connectionPrivate
->lock
);
928 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
929 if (session_port
== MACH_PORT_NULL
) {
933 status
= pppcontroller_copystatistics(session_port
, &data
, &datalen
, &sc_status
);
934 if (__SCNetworkConnectionNeedsRetry(connection
,
935 "SCNetworkConnectionCopyStatistics()",
942 if (!_SCUnserialize(&statistics
, NULL
, data
, datalen
)) {
943 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
945 if ((sc_status
== kSCStatusOK
) && !isA_CFDictionary(statistics
)) {
946 sc_status
= kSCStatusFailed
;
950 if (sc_status
!= kSCStatusOK
) {
951 if (statistics
!= NULL
) {
952 CFRelease(statistics
);
955 _SCErrorSet(sc_status
);
960 pthread_mutex_unlock(&connectionPrivate
->lock
);
966 SCNetworkConnectionGetService(SCNetworkConnectionRef connection
)
968 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
970 if (!isA_SCNetworkConnection(connection
)) {
971 _SCErrorSet(kSCStatusInvalidArgument
);
975 return connectionPrivate
->service
;
979 SCNetworkConnectionStatus
980 SCNetworkConnectionGetStatus(SCNetworkConnectionRef connection
)
982 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
983 SCNetworkConnectionStatus nc_status
= kSCNetworkConnectionInvalid
;
985 int sc_status
= kSCStatusFailed
;
986 mach_port_t session_port
;
987 kern_return_t status
;
989 if (!isA_SCNetworkConnection(connection
)) {
990 _SCErrorSet(kSCStatusInvalidArgument
);
991 return kSCNetworkConnectionInvalid
;
994 pthread_mutex_lock(&connectionPrivate
->lock
);
998 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
999 if (session_port
== MACH_PORT_NULL
) {
1000 nc_status
= kSCNetworkConnectionInvalid
;
1004 status
= pppcontroller_getstatus(session_port
, &nc_status
, &sc_status
);
1005 if (__SCNetworkConnectionNeedsRetry(connection
,
1006 "SCNetworkConnectionGetStatus()",
1012 // wait up to 250 ms for the network service to become available
1013 if (!connectionPrivate
->haveStatus
&&
1014 (sc_status
== kSCStatusConnectionNoService
) &&
1015 ((retry
+= 10) < 250)) {
1016 usleep(10 * 1000); // sleep 10ms between attempts
1020 if (sc_status
== kSCStatusOK
) {
1021 connectionPrivate
->haveStatus
= TRUE
;
1023 _SCErrorSet(sc_status
);
1024 nc_status
= kSCNetworkConnectionInvalid
;
1029 pthread_mutex_unlock(&connectionPrivate
->lock
);
1035 SCNetworkConnectionCopyExtendedStatus(SCNetworkConnectionRef connection
)
1037 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1038 xmlDataOut_t data
= NULL
;
1039 mach_msg_type_number_t datalen
= 0;
1040 CFPropertyListRef extstatus
= NULL
;
1042 int sc_status
= kSCStatusFailed
;
1043 mach_port_t session_port
;
1044 kern_return_t status
;
1046 if (!isA_SCNetworkConnection(connection
)) {
1047 _SCErrorSet(kSCStatusInvalidArgument
);
1051 pthread_mutex_lock(&connectionPrivate
->lock
);
1055 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1056 if (session_port
== MACH_PORT_NULL
) {
1060 status
= pppcontroller_copyextendedstatus(session_port
, &data
, &datalen
, &sc_status
);
1061 if (__SCNetworkConnectionNeedsRetry(connection
,
1062 "SCNetworkConnectionCopyExtendedStatus()",
1069 if (!_SCUnserialize(&extstatus
, NULL
, data
, datalen
)) {
1070 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
1072 if ((sc_status
== kSCStatusOK
) && !isA_CFDictionary(extstatus
)) {
1073 sc_status
= kSCStatusFailed
;
1077 // wait up to 250 ms for the network service to become available
1078 if (!connectionPrivate
->haveStatus
&&
1079 (sc_status
== kSCStatusConnectionNoService
) &&
1080 ((retry
+= 10) < 250)) {
1081 usleep(10 * 1000); // sleep 10ms between attempts
1085 if (sc_status
== kSCStatusOK
) {
1086 connectionPrivate
->haveStatus
= TRUE
;
1088 if (extstatus
!= NULL
) {
1089 CFRelease(extstatus
);
1092 _SCErrorSet(sc_status
);
1097 pthread_mutex_unlock(&connectionPrivate
->lock
);
1103 SCNetworkConnectionStart(SCNetworkConnectionRef connection
,
1104 CFDictionaryRef userOptions
,
1107 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1108 CFDataRef dataref
= NULL
;
1110 CFIndex datalen
= 0;
1112 int sc_status
= kSCStatusFailed
;
1113 mach_port_t session_port
;
1114 kern_return_t status
;
1116 if (!isA_SCNetworkConnection(connection
)) {
1117 _SCErrorSet(kSCStatusInvalidArgument
);
1121 if ((userOptions
!= NULL
) && !isA_CFDictionary(userOptions
)) {
1122 _SCErrorSet(kSCStatusInvalidArgument
);
1127 CFMutableDictionaryRef mdict
= NULL
;
1129 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionStart (0x%x)"), connectionPrivate
);
1131 if (userOptions
!= NULL
) {
1132 CFDictionaryRef dict
;
1133 CFStringRef encryption
;
1134 CFMutableDictionaryRef new_dict
;
1136 /* special code to remove secret information */
1137 mdict
= CFDictionaryCreateMutableCopy(NULL
, 0, userOptions
);
1139 dict
= CFDictionaryGetValue(mdict
, kSCEntNetPPP
);
1140 if (isA_CFDictionary(dict
)) {
1141 encryption
= CFDictionaryGetValue(dict
, kSCPropNetPPPAuthPasswordEncryption
);
1142 if (!isA_CFString(encryption
) ||
1143 !CFEqual(encryption
, kSCValNetPPPAuthPasswordEncryptionKeychain
)) {
1144 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1145 CFDictionaryReplaceValue(new_dict
, kSCPropNetPPPAuthPassword
, CFSTR("******"));
1146 CFDictionarySetValue(mdict
, kSCEntNetPPP
, new_dict
);
1147 CFRelease(new_dict
);
1151 dict
= CFDictionaryGetValue(mdict
, kSCEntNetL2TP
);
1152 if (isA_CFDictionary(dict
)) {
1153 encryption
= CFDictionaryGetValue(dict
, kSCPropNetL2TPIPSecSharedSecretEncryption
);
1154 if (!isA_CFString(encryption
) ||
1155 !CFEqual(encryption
, kSCValNetL2TPIPSecSharedSecretEncryptionKeychain
)) {
1156 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1157 CFDictionaryReplaceValue(new_dict
, kSCPropNetL2TPIPSecSharedSecret
, CFSTR("******"));
1158 CFDictionarySetValue(mdict
, kSCEntNetL2TP
, new_dict
);
1159 CFRelease(new_dict
);
1163 dict
= CFDictionaryGetValue(mdict
, kSCEntNetIPSec
);
1164 if (isA_CFDictionary(dict
)) {
1165 encryption
= CFDictionaryGetValue(dict
, kSCPropNetIPSecSharedSecretEncryption
);
1166 if (!isA_CFString(encryption
) ||
1167 !CFEqual(encryption
, kSCValNetIPSecSharedSecretEncryptionKeychain
)) {
1168 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
1169 CFDictionaryReplaceValue(new_dict
, kSCPropNetIPSecSharedSecret
, CFSTR("******"));
1170 CFDictionarySetValue(mdict
, kSCEntNetIPSec
, new_dict
);
1171 CFRelease(new_dict
);
1176 SCLog(TRUE
, LOG_DEBUG
, CFSTR("User options: %@"), mdict
);
1177 if (mdict
!= NULL
) CFRelease(mdict
);
1180 if (userOptions
&& !_SCSerialize(userOptions
, &dataref
, &data
, &datalen
)) {
1184 pthread_mutex_lock(&connectionPrivate
->lock
);
1188 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1189 if (session_port
== MACH_PORT_NULL
) {
1190 if (dataref
) CFRelease(dataref
);
1194 status
= pppcontroller_start(session_port
, data
, datalen
, linger
, &sc_status
);
1195 if (__SCNetworkConnectionNeedsRetry(connection
,
1196 "SCNetworkConnectionStart()",
1202 if (dataref
) CFRelease(dataref
);
1205 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionStart (0x%x), return: %d"), connectionPrivate
, sc_status
);
1208 if (sc_status
!= kSCStatusOK
) {
1209 _SCErrorSet(sc_status
);
1213 /* connection is now started */
1217 pthread_mutex_unlock(&connectionPrivate
->lock
);
1223 SCNetworkConnectionStop(SCNetworkConnectionRef connection
,
1224 Boolean forceDisconnect
)
1226 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1228 int sc_status
= kSCStatusFailed
;
1229 mach_port_t session_port
;
1230 kern_return_t status
;
1232 if (!isA_SCNetworkConnection(connection
)) {
1233 _SCErrorSet(kSCStatusInvalidArgument
);
1238 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionStop (0x%x)"), connectionPrivate
);
1241 pthread_mutex_lock(&connectionPrivate
->lock
);
1245 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1246 if (session_port
== MACH_PORT_NULL
) {
1250 status
= pppcontroller_stop(session_port
, forceDisconnect
, &sc_status
);
1251 if (__SCNetworkConnectionNeedsRetry(connection
,
1252 "SCNetworkConnectionStop()",
1259 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionStop (0x%x), return: %d"), connectionPrivate
, sc_status
);
1262 if (sc_status
!= kSCStatusOK
) {
1263 _SCErrorSet(sc_status
);
1267 /* connection is now disconnecting */
1272 pthread_mutex_unlock(&connectionPrivate
->lock
);
1278 SCNetworkConnectionSuspend(SCNetworkConnectionRef connection
)
1280 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1282 int sc_status
= kSCStatusFailed
;
1283 mach_port_t session_port
;
1284 kern_return_t status
;
1286 if (!isA_SCNetworkConnection(connection
)) {
1287 _SCErrorSet(kSCStatusInvalidArgument
);
1292 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionSuspend (0x%x)"), connectionPrivate
);
1295 pthread_mutex_lock(&connectionPrivate
->lock
);
1299 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1300 if (session_port
== MACH_PORT_NULL
) {
1304 status
= pppcontroller_suspend(session_port
, &sc_status
);
1305 if (__SCNetworkConnectionNeedsRetry(connection
,
1306 "SCNetworkConnectionSuspend()",
1313 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionSuspend (0x%x), return: %d"), connectionPrivate
, sc_status
);
1316 if (sc_status
!= kSCStatusOK
) {
1317 _SCErrorSet(sc_status
);
1321 /* connection is now suspended */
1326 pthread_mutex_unlock(&connectionPrivate
->lock
);
1332 SCNetworkConnectionResume(SCNetworkConnectionRef connection
)
1334 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1336 int sc_status
= kSCStatusFailed
;
1337 mach_port_t session_port
;
1338 kern_return_t status
;
1340 if (!isA_SCNetworkConnection(connection
)) {
1341 _SCErrorSet(kSCStatusInvalidArgument
);
1346 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionResume (0x%x)"), connectionPrivate
);
1349 pthread_mutex_lock(&connectionPrivate
->lock
);
1353 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1354 if (session_port
== MACH_PORT_NULL
) {
1358 status
= pppcontroller_resume(session_port
, &sc_status
);
1359 if (__SCNetworkConnectionNeedsRetry(connection
,
1360 "SCNetworkConnectionResume()",
1367 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionResume (0x%x), return: %d"), connectionPrivate
, sc_status
);
1370 if (sc_status
!= kSCStatusOK
) {
1371 _SCErrorSet(sc_status
);
1375 /* connection is now resume */
1380 pthread_mutex_unlock(&connectionPrivate
->lock
);
1386 SCNetworkConnectionCopyUserOptions(SCNetworkConnectionRef connection
)
1388 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1389 xmlDataOut_t data
= NULL
;
1390 mach_msg_type_number_t datalen
= 0;
1391 int sc_status
= kSCStatusFailed
;
1392 mach_port_t session_port
;
1393 kern_return_t status
;
1394 CFPropertyListRef userOptions
= NULL
;
1396 if (!isA_SCNetworkConnection(connection
)) {
1397 _SCErrorSet(kSCStatusInvalidArgument
);
1401 pthread_mutex_lock(&connectionPrivate
->lock
);
1405 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1406 if (session_port
== MACH_PORT_NULL
) {
1410 status
= pppcontroller_copyuseroptions(session_port
, &data
, &datalen
, &sc_status
);
1411 if (__SCNetworkConnectionNeedsRetry(connection
,
1412 "SCNetworkConnectionCopyUserOptions()",
1419 if (!_SCUnserialize(&userOptions
, NULL
, data
, datalen
)) {
1420 if (sc_status
!= kSCStatusOK
) sc_status
= SCError();
1422 if ((sc_status
== kSCStatusOK
) && (userOptions
!= NULL
) && !isA_CFDictionary(userOptions
)) {
1423 sc_status
= kSCStatusFailed
;
1427 if (sc_status
== kSCStatusOK
) {
1428 if (userOptions
== NULL
) {
1429 // if no user options, return an empty dictionary
1430 userOptions
= CFDictionaryCreate(NULL
,
1434 &kCFTypeDictionaryKeyCallBacks
,
1435 &kCFTypeDictionaryValueCallBacks
);
1439 CFRelease(userOptions
);
1442 _SCErrorSet(sc_status
);
1447 pthread_mutex_unlock(&connectionPrivate
->lock
);
1453 SCNetworkConnectionNotifyMIGCallback(mach_msg_header_t
*message
, mach_msg_header_t
*reply
)
1455 SCNetworkConnectionPrivateRef connectionPrivate
= dispatch_get_context(dispatch_get_current_queue());
1457 if (connectionPrivate
!= NULL
) {
1458 mach_msg_empty_rcv_t
*buf
= malloc(sizeof(*buf
));
1460 bcopy(message
, buf
, sizeof(*buf
));
1461 CFRetain(connectionPrivate
);
1462 dispatch_async(connectionPrivate
->dispatchQueue
, ^{
1463 __SCNetworkConnectionCallBack(connectionPrivate
->notify_port
,
1467 CFRelease(connectionPrivate
);
1471 reply
->msgh_remote_port
= MACH_PORT_NULL
;
1477 __SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection
,
1478 CFRunLoopRef runLoop
,
1479 CFStringRef runLoopMode
,
1480 dispatch_queue_t queue
)
1482 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1484 int sc_status
= kSCStatusFailed
;
1485 mach_port_t session_port
;
1486 kern_return_t status
;
1488 pthread_mutex_lock(&connectionPrivate
->lock
);
1490 if (connectionPrivate
->rlsFunction
== NULL
) {
1491 _SCErrorSet(kSCStatusInvalidArgument
);
1495 if ((connectionPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled on a dispatch queue
1496 ((queue
!= NULL
) && connectionPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
1497 _SCErrorSet(kSCStatusInvalidArgument
);
1501 if (!connectionPrivate
->scheduled
) {
1505 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1506 if (session_port
== MACH_PORT_NULL
) {
1510 status
= pppcontroller_notification(session_port
, 1, &sc_status
);
1511 if (__SCNetworkConnectionNeedsRetry(connection
,
1512 "__SCNetworkConnectionScheduleWithRunLoop()",
1518 if (sc_status
!= kSCStatusOK
) {
1519 _SCErrorSet(sc_status
);
1523 if (runLoop
!= NULL
) {
1524 connectionPrivate
->rls
= CFMachPortCreateRunLoopSource(NULL
, connectionPrivate
->notify_port
, 0);
1525 connectionPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1528 connectionPrivate
->scheduled
= TRUE
;
1531 if (queue
!= NULL
) {
1535 connectionPrivate
->dispatchQueue
= queue
;
1536 dispatch_retain(connectionPrivate
->dispatchQueue
);
1538 snprintf(qname
, sizeof(qname
), "com.apple.SCNetworkConnection.%p", connection
);
1539 connectionPrivate
->callbackQueue
= dispatch_queue_create(qname
, NULL
);
1540 if (connectionPrivate
->callbackQueue
== NULL
){
1541 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkConnection dispatch_queue_create() failed"));
1544 CFRetain(connection
); // Note: will be released when the dispatch queue is released
1545 dispatch_set_context(connectionPrivate
->callbackQueue
, connectionPrivate
);
1546 dispatch_set_finalizer_f(connectionPrivate
->callbackQueue
, (dispatch_function_t
)CFRelease
);
1548 mp
= CFMachPortGetPort(connectionPrivate
->notify_port
);
1549 connectionPrivate
->callbackSource
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
,
1552 connectionPrivate
->callbackQueue
);
1553 if (connectionPrivate
->callbackSource
== NULL
) {
1554 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkConnection dispatch_source_create() failed"));
1557 dispatch_source_set_event_handler(connectionPrivate
->callbackSource
, ^{
1559 mach_msg_empty_rcv_t normal
;
1560 mach_no_senders_notification_t no_senders
;
1563 dispatch_mig_server(connectionPrivate
->callbackSource
,
1564 sizeof(union MaxMsgSize
),
1565 SCNetworkConnectionNotifyMIGCallback
);
1567 dispatch_resume(connectionPrivate
->callbackSource
);
1569 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, connectionPrivate
->rlList
)) {
1571 * if we do not already have notifications scheduled with
1572 * this runLoop / runLoopMode
1574 CFRunLoopAddSource(runLoop
, connectionPrivate
->rls
, runLoopMode
);
1577 _SC_schedule(connection
, runLoop
, runLoopMode
, connectionPrivate
->rlList
);
1585 if (connectionPrivate
->callbackSource
!= NULL
) {
1586 dispatch_source_cancel(connectionPrivate
->callbackSource
);
1587 dispatch_release(connectionPrivate
->callbackSource
);
1588 connectionPrivate
->callbackSource
= NULL
;
1590 if (connectionPrivate
->callbackQueue
!= NULL
) {
1591 dispatch_release(connectionPrivate
->callbackQueue
);
1592 connectionPrivate
->callbackQueue
= NULL
;
1594 if (connectionPrivate
->dispatchQueue
!= NULL
) {
1595 dispatch_release(connectionPrivate
->dispatchQueue
);
1596 connectionPrivate
->dispatchQueue
= NULL
;
1598 _SCErrorSet(kSCStatusFailed
);
1602 pthread_mutex_unlock(&connectionPrivate
->lock
);
1608 __SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection
,
1609 CFRunLoopRef runLoop
,
1610 CFStringRef runLoopMode
,
1611 dispatch_queue_t queue
)
1613 SCNetworkConnectionPrivateRef connectionPrivate
= (SCNetworkConnectionPrivateRef
)connection
;
1614 int sc_status
= kSCStatusFailed
;
1617 mach_port_t session_port
;
1618 kern_return_t status
;
1620 pthread_mutex_lock(&connectionPrivate
->lock
);
1622 if ((runLoop
!= NULL
) && !connectionPrivate
->scheduled
) { // if we should be scheduled (but are not)
1623 _SCErrorSet(kSCStatusInvalidArgument
);
1627 if (((runLoop
== NULL
) && (connectionPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
1628 ((runLoop
!= NULL
) && (connectionPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
1629 _SCErrorSet(kSCStatusInvalidArgument
);
1633 session_port
= __SCNetworkConnectionSessionPort(connectionPrivate
);
1634 if (session_port
== MACH_PORT_NULL
) {
1638 if (runLoop
== NULL
) {
1639 dispatch_source_cancel(connectionPrivate
->callbackSource
);
1640 if (connectionPrivate
->callbackQueue
!= dispatch_get_current_queue()) {
1641 // ensure the cancellation has completed
1642 dispatch_sync(connectionPrivate
->callbackQueue
, ^{});
1644 dispatch_release(connectionPrivate
->callbackSource
);
1645 connectionPrivate
->callbackSource
= NULL
;
1646 dispatch_release(connectionPrivate
->callbackQueue
);
1647 connectionPrivate
->callbackQueue
= NULL
;
1648 dispatch_release(connectionPrivate
->dispatchQueue
);
1649 connectionPrivate
->dispatchQueue
= NULL
;
1651 if (!_SC_unschedule(connection
, runLoop
, runLoopMode
, connectionPrivate
->rlList
, FALSE
)) {
1652 // if not currently scheduled on this runLoop / runLoopMode
1653 _SCErrorSet(kSCStatusFailed
);
1657 n
= CFArrayGetCount(connectionPrivate
->rlList
);
1658 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, connectionPrivate
->rlList
)) {
1660 * if we are no longer scheduled to receive notifications for
1661 * this runLoop / runLoopMode
1663 CFRunLoopRemoveSource(runLoop
, connectionPrivate
->rls
, runLoopMode
);
1666 // if *all* notifications have been unscheduled
1667 CFRelease(connectionPrivate
->rlList
);
1668 connectionPrivate
->rlList
= NULL
;
1669 CFRunLoopSourceInvalidate(connectionPrivate
->rls
);
1670 CFRelease(connectionPrivate
->rls
);
1671 connectionPrivate
->rls
= NULL
;
1677 // if *all* notifications have been unscheduled
1678 connectionPrivate
->scheduled
= FALSE
;
1680 status
= pppcontroller_notification(session_port
, 0, &sc_status
);
1681 if (__SCNetworkConnectionNeedsRetry(connection
,
1682 "__SCNetworkConnectionUnscheduleFromRunLoop pppcontroller_notification()",
1685 sc_status
= kSCStatusOK
;
1686 status
= KERN_SUCCESS
;
1689 if ((status
!= KERN_SUCCESS
) || (sc_status
!= kSCStatusOK
)) {
1690 _SCErrorSet(sc_status
);
1699 pthread_mutex_unlock(&connectionPrivate
->lock
);
1705 SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection
,
1706 CFRunLoopRef runLoop
,
1707 CFStringRef runLoopMode
)
1709 if (!isA_SCNetworkConnection(connection
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1710 _SCErrorSet(kSCStatusInvalidArgument
);
1714 return __SCNetworkConnectionScheduleWithRunLoop(connection
, runLoop
, runLoopMode
, NULL
);
1719 SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection
,
1720 CFRunLoopRef runLoop
,
1721 CFStringRef runLoopMode
)
1723 if (!isA_SCNetworkConnection(connection
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1724 _SCErrorSet(kSCStatusInvalidArgument
);
1728 return __SCNetworkConnectionUnscheduleFromRunLoop(connection
, runLoop
, runLoopMode
, NULL
);
1733 SCNetworkConnectionSetDispatchQueue(SCNetworkConnectionRef connection
,
1734 dispatch_queue_t queue
)
1738 if (!isA_SCNetworkConnection(connection
)) {
1739 _SCErrorSet(kSCStatusInvalidArgument
);
1743 if (queue
!= NULL
) {
1744 ok
= __SCNetworkConnectionScheduleWithRunLoop(connection
, NULL
, NULL
, queue
);
1746 ok
= __SCNetworkConnectionUnscheduleFromRunLoop(connection
, NULL
, NULL
, NULL
);
1754 #pragma mark User level "dial" API
1757 #define k_NetworkConnect_Notification "com.apple.networkConnect"
1758 #define k_NetworkConnect_Pref_File CFSTR("com.apple.networkConnect")
1759 #define k_InterentConnect_Pref_File CFSTR("com.apple.internetconnect")
1761 #define k_Dial_Default_Key CFSTR("ConnectByDefault") // needs to go into SC
1762 #define k_Last_Service_Id_Key CFSTR("ServiceID")
1763 #define k_Unique_Id_Key CFSTR("UniqueIdentifier")
1766 /* Private Prototypes */
1767 static Boolean
SCNetworkConnectionPrivateCopyDefaultServiceIDForDial (SCDynamicStoreRef session
, CFStringRef
*serviceID
);
1768 static Boolean
SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore (SCDynamicStoreRef session
, CFStringRef
*serviceID
);
1769 static Boolean
SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray
, CFDictionaryRef
*userOptions
);
1770 static Boolean
SCNetworkConnectionPrivateIsPPPService (SCDynamicStoreRef session
, CFStringRef serviceID
, CFStringRef subType1
, CFStringRef subType2
);
1771 static void addPasswordFromKeychain (SCDynamicStoreRef session
, CFStringRef serviceID
, CFDictionaryRef
*userOptions
);
1772 static CFStringRef
copyPasswordFromKeychain (CFStringRef uniqueID
);
1774 static int notify_userprefs_token
= -1;
1776 static CFDictionaryRef onDemand_configuration
= NULL
;
1777 static pthread_mutex_t onDemand_notify_lock
= PTHREAD_MUTEX_INITIALIZER
;
1778 static int onDemand_notify_token
= -1;
1781 * return TRUE if domain1 ends with domain2, and will check for trailing "."
1784 domainEndsWithDomain(CFStringRef domain1
, CFStringRef domain2
)
1787 Boolean ret
= FALSE
;
1788 CFStringRef s1
= NULL
;
1789 Boolean s1_created
= FALSE
;
1790 CFStringRef s2
= NULL
;
1791 Boolean s2_created
= FALSE
;
1793 if (CFStringHasSuffix(domain1
, CFSTR("."))) {
1795 range
.length
= CFStringGetLength(domain1
) - 1;
1796 s1
= CFStringCreateWithSubstring(NULL
, domain1
, range
);
1805 if (CFStringHasSuffix(domain2
, CFSTR("."))) {
1807 range
.length
= CFStringGetLength(domain2
) - 1;
1808 s2
= CFStringCreateWithSubstring(NULL
, domain2
, range
);
1817 ret
= CFStringHasSuffix(s1
, s2
);
1821 if (s1_created
) CFRelease(s1
);
1822 if (s2_created
) CFRelease(s2
);
1829 __SCNetworkConnectionCopyOnDemandInfoWithName(SCDynamicStoreRef
*storeP
,
1830 CFStringRef hostName
,
1831 Boolean onDemandRetry
,
1832 CFStringRef
*connectionServiceID
,
1833 SCNetworkConnectionStatus
*connectionStatus
,
1834 CFStringRef
*vpnRemoteAddress
) /* CFDictionaryRef *info */
1837 CFDictionaryRef configuration
;
1840 SCDynamicStoreRef store
= *storeP
;
1841 CFArrayRef triggers
;
1842 uint64_t triggersCount
= 0;
1845 pthread_mutex_lock(&onDemand_notify_lock
);
1846 if (onDemand_notify_token
== -1) {
1847 status
= notify_register_check(kSCNETWORKCONNECTION_ONDEMAND_NOTIFY_KEY
, &onDemand_notify_token
);
1848 if (status
!= NOTIFY_STATUS_OK
) {
1849 SCLog(TRUE
, LOG_ERR
, CFSTR("notify_register_check() failed, status=%lu"), status
);
1850 onDemand_notify_token
= -1;
1853 if (onDemand_notify_token
!= -1) {
1854 status
= notify_check(onDemand_notify_token
, &changed
);
1855 if (status
!= NOTIFY_STATUS_OK
) {
1856 SCLog(TRUE
, LOG_ERR
, CFSTR("notify_check() failed, status=%lu"), status
);
1857 (void)notify_cancel(onDemand_notify_token
);
1858 onDemand_notify_token
= -1;
1862 if (changed
&& (onDemand_notify_token
!= -1)) {
1863 status
= notify_get_state(onDemand_notify_token
, &triggersCount
);
1864 if (status
!= NOTIFY_STATUS_OK
) {
1865 SCLog(TRUE
, LOG_ERR
, CFSTR("notify_get_state() failed, status=%lu"), status
);
1866 (void)notify_cancel(onDemand_notify_token
);
1867 onDemand_notify_token
= -1;
1874 if (_sc_debug
|| (debug
> 0)) {
1875 SCLog(TRUE
, LOG_INFO
,
1876 CFSTR("OnDemand information %s"),
1877 (onDemand_configuration
== NULL
) ? "fetched" : "updated");
1880 if (onDemand_configuration
!= NULL
) {
1881 CFRelease(onDemand_configuration
);
1882 onDemand_configuration
= NULL
;
1885 if (triggersCount
> 0) {
1886 if (store
== NULL
) {
1887 store
= SCDynamicStoreCreate(NULL
, CFSTR("__SCNetworkConnectionCopyOnDemandInfoWithName"), NULL
, NULL
);
1888 if (store
== NULL
) {
1889 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkConnectionCopyOnDemandInfoWithName SCDynamicStoreCreate() failed"));
1891 // force retry on next check
1892 if (onDemand_notify_token
!= -1) {
1893 (void)notify_cancel(onDemand_notify_token
);
1894 onDemand_notify_token
= -1;
1896 pthread_mutex_unlock(&onDemand_notify_lock
);
1902 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, kSCDynamicStoreDomainState
, kSCEntNetOnDemand
);
1903 onDemand_configuration
= SCDynamicStoreCopyValue(store
, key
);
1905 if ((onDemand_configuration
!= NULL
) && !isA_CFDictionary(onDemand_configuration
)) {
1906 CFRelease(onDemand_configuration
);
1907 onDemand_configuration
= NULL
;
1912 configuration
= (onDemand_configuration
!= NULL
) ? CFRetain(onDemand_configuration
) : NULL
;
1913 pthread_mutex_unlock(&onDemand_notify_lock
);
1915 if (configuration
== NULL
) {
1916 // if no "OnDemand" configurations
1920 triggers
= CFDictionaryGetValue(configuration
, kSCNetworkConnectionOnDemandTriggers
);
1921 triggersCount
= isA_CFArray(triggers
) ? CFArrayGetCount(triggers
) : 0;
1922 for (triggersIndex
= 0; triggersIndex
< triggersCount
; triggersIndex
++) {
1927 CFDictionaryRef trigger
;
1929 trigger
= CFArrayGetValueAtIndex(triggers
, triggersIndex
);
1930 if (!isA_CFDictionary(trigger
)) {
1931 // if not a valid "OnDemand" configuration
1936 * If we haven't tried a resulution yet, we only want to check for a name
1937 * match for domains that require to always connect.
1939 key
= onDemandRetry
? kSCNetworkConnectionOnDemandMatchDomainsOnRetry
1940 : kSCNetworkConnectionOnDemandMatchDomainsAlways
;
1941 domains
= CFDictionaryGetValue(trigger
, key
);
1942 domainsCount
= isA_CFArray(domains
) ? CFArrayGetCount(domains
) : 0;
1943 for (domainsIndex
= 0; domainsIndex
< domainsCount
; domainsIndex
++) {
1946 domain
= CFArrayGetValueAtIndex(domains
, domainsIndex
);
1947 if (!isA_CFString(domain
)) {
1948 // if not a valid match domain
1952 if (domainEndsWithDomain(hostName
, domain
)) {
1953 CFArrayRef exceptions
;
1954 int exceptionsCount
;
1955 int exceptionsIndex
;
1957 SCNetworkConnectionStatus onDemandStatus
= kSCNetworkConnectionDisconnected
;
1959 // we have a matching domain, check against exception list
1960 exceptions
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandMatchDomainsNever
);
1961 exceptionsCount
= isA_CFArray(exceptions
) ? CFArrayGetCount(exceptions
) : 0;
1962 for (exceptionsIndex
= 0; exceptionsIndex
< exceptionsCount
; exceptionsIndex
++) {
1963 CFStringRef exception
;
1965 exception
= CFArrayGetValueAtIndex(exceptions
, exceptionsIndex
);
1966 if (!isA_CFString(exception
)) {
1967 // if not a valid match exception
1971 if (domainEndsWithDomain(hostName
, exception
)) {
1972 // found matching exception
1973 if (_sc_debug
|| (debug
> 0)) {
1974 SCLog(TRUE
, LOG_INFO
, CFSTR("OnDemand match exception"));
1980 // if we have a matching domain and there were no exceptions
1981 // then we pass back the OnDemand info
1983 if (!CFDictionaryGetValueIfPresent(trigger
,
1984 kSCNetworkConnectionOnDemandStatus
,
1985 (const void **)&num
) ||
1986 !isA_CFNumber(num
) ||
1987 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &onDemandStatus
)) {
1988 onDemandStatus
= kSCNetworkConnectionDisconnected
;
1990 if (connectionStatus
!= NULL
) {
1991 *connectionStatus
= onDemandStatus
;
1994 if (connectionServiceID
!= NULL
) {
1995 *connectionServiceID
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandServiceID
);
1996 *connectionServiceID
= isA_CFString(*connectionServiceID
);
1997 if (*connectionServiceID
!= NULL
) {
1998 CFRetain(*connectionServiceID
);
2003 if (vpnRemoteAddress
!= NULL
) {
2004 *vpnRemoteAddress
= CFDictionaryGetValue(trigger
, kSCNetworkConnectionOnDemandRemoteAddress
);
2005 *vpnRemoteAddress
= isA_CFString(*vpnRemoteAddress
);
2006 if (*vpnRemoteAddress
!= NULL
) {
2007 CFRetain(*vpnRemoteAddress
);
2011 if (_sc_debug
|| (debug
> 0)) {
2012 SCLog(TRUE
, LOG_INFO
,
2013 CFSTR("OnDemand%s match, connection status = %d"),
2014 onDemandRetry
? " (on retry)" : "",
2024 // if (_sc_debug || (debug > 0)) {
2025 // SCLog(TRUE, LOG_INFO, CFSTR("OnDemand domain name(s) not matched"));
2030 if (configuration
!= NULL
) CFRelease(configuration
);
2036 SCNetworkConnectionCopyUserPreferences(CFDictionaryRef selectionOptions
,
2037 CFStringRef
*serviceID
,
2038 CFDictionaryRef
*userOptions
)
2040 int prefsChanged
= 1;
2041 SCDynamicStoreRef session
= NULL
;
2042 Boolean success
= FALSE
;
2046 /* initialize runtime */
2047 pthread_once(&initialized
, __SCNetworkConnectionInitialize
);
2049 /* first check for new VPN OnDemand style */
2050 if (selectionOptions
!= NULL
) {
2051 CFStringRef hostName
;
2053 hostName
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
);
2054 if (isA_CFString(hostName
)) {
2055 CFStringRef connectionServiceID
= NULL
;
2056 SCNetworkConnectionStatus connectionStatus
;
2057 Boolean onDemandRetry
;
2060 val
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandRetry
);
2061 onDemandRetry
= isA_CFBoolean(val
) ? CFBooleanGetValue(val
) : TRUE
;
2063 success
= __SCNetworkConnectionCopyOnDemandInfoWithName(&session
,
2066 &connectionServiceID
,
2070 SCLog(TRUE
, LOG_DEBUG
,
2071 CFSTR("SCNetworkConnectionCopyUserPreferences __SCNetworkConnectionCopyOnDemandInfoWithName returns %d w/status %d"),
2077 // if the hostname matches an OnDemand domain
2078 if (session
!= NULL
) {
2081 if (connectionStatus
== kSCNetworkConnectionConnected
) {
2082 // if we are already connected
2083 if (connectionServiceID
!= NULL
) {
2084 CFRelease(connectionServiceID
);
2089 *serviceID
= connectionServiceID
;
2090 *userOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2091 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
2093 } else if (!onDemandRetry
) {
2094 // if the hostname does not match an OnDemand domain and we have
2095 // not yet issued an initial DNS query (i.e. it's not a query
2096 // being retried after the VPN has been established) than we're
2098 if (session
!= NULL
) {
2106 if (notify_userprefs_token
== -1) {
2107 status
= notify_register_check(k_NetworkConnect_Notification
, ¬ify_userprefs_token
);
2108 if (status
!= NOTIFY_STATUS_OK
) {
2109 SCLog(TRUE
, LOG_ERR
, CFSTR("notify_register_check() failed, status=%lu"), status
);
2110 (void)notify_cancel(notify_userprefs_token
);
2111 notify_userprefs_token
= -1;
2113 // clear the "something has changed" state
2114 (void) notify_check(notify_userprefs_token
, &prefsChanged
);
2118 if (notify_userprefs_token
!= -1) {
2119 status
= notify_check(notify_userprefs_token
, &prefsChanged
);
2120 if (status
!= NOTIFY_STATUS_OK
) {
2121 SCLog(TRUE
, LOG_ERR
, CFSTR("notify_check() failed, status=%lu"), status
);
2122 (void)notify_cancel(notify_userprefs_token
);
2123 notify_userprefs_token
= -1;
2129 *userOptions
= NULL
;
2131 if (session
== NULL
) {
2132 session
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkConnection"), NULL
, NULL
);
2134 if (session
== NULL
) {
2135 SCLog(TRUE
, LOG_ERR
, CFSTR("Error, SCNetworkConnectionCopyUserPreferences, SCDynamicStoreCreate() returned NULL!"));
2139 if (selectionOptions
!= NULL
) {
2140 Boolean catchAllFound
= FALSE
;
2141 CFIndex catchAllService
= 0;
2142 CFIndex catchAllConfig
= 0;
2143 CFStringRef hostName
= NULL
;
2144 CFStringRef priority
= NULL
;
2145 CFArrayRef serviceNames
= NULL
;
2146 CFDictionaryRef services
= NULL
;
2147 CFIndex serviceIndex
;
2148 CFIndex servicesCount
;
2150 hostName
= CFDictionaryGetValue(selectionOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
);
2151 if (hostName
== NULL
) {
2152 hostName
= CFDictionaryGetValue(selectionOptions
, kSCPropNetPPPOnDemandHostName
);
2154 hostName
= isA_CFString(hostName
);
2155 if (hostName
== NULL
)
2156 goto done_selection
; // if no hostname for matching
2158 priority
= CFDictionaryGetValue(selectionOptions
, kSCPropNetPPPOnDemandPriority
);
2159 if (!isA_CFString(priority
))
2160 priority
= kSCValNetPPPOnDemandPriorityDefault
;
2163 if (!isA_CFArray(serviceNames
))
2164 goto done_selection
;
2167 if (!isA_CFDictionary(services
))
2168 goto done_selection
;
2170 servicesCount
= CFArrayGetCount(serviceNames
);
2171 for (serviceIndex
= 0; serviceIndex
< servicesCount
; serviceIndex
++) {
2172 CFIndex configIndex
;
2173 CFIndex configsCount
;
2174 CFArrayRef serviceConfigs
;
2175 CFStringRef serviceName
;
2178 serviceName
= CFArrayGetValueAtIndex(serviceNames
, serviceIndex
);
2179 if (!isA_CFString(serviceName
))
2182 serviceConfigs
= CFDictionaryGetValue(services
, serviceName
);
2183 if (!isA_CFArray(serviceConfigs
))
2186 configsCount
= CFArrayGetCount(serviceConfigs
);
2187 for (configIndex
= 0; configIndex
< configsCount
; configIndex
++) {
2188 CFNumberRef autodial
;
2189 CFDictionaryRef config
;
2190 CFDictionaryRef pppConfig
;
2192 config
= CFArrayGetValueAtIndex(serviceConfigs
, configIndex
);
2193 if (!isA_CFDictionary(config
))
2196 pppConfig
= CFDictionaryGetValue(config
, kSCEntNetPPP
);
2197 if (!isA_CFDictionary(pppConfig
))
2200 autodial
= CFDictionaryGetValue(pppConfig
, kSCPropNetPPPOnDemandEnabled
);
2201 if (!isA_CFNumber(autodial
))
2204 CFNumberGetValue(autodial
, kCFNumberIntType
, &val
);
2207 CFIndex domainsCount
;
2208 CFIndex domainsIndex
;
2210 /* we found an conditional connection enabled configuration */
2213 domains
= CFDictionaryGetValue(pppConfig
, kSCPropNetPPPOnDemandDomains
);
2214 if (!isA_CFArray(domains
))
2217 domainsCount
= CFArrayGetCount(domains
);
2218 for (domainsIndex
= 0; domainsIndex
< domainsCount
; domainsIndex
++) {
2221 domain
= CFArrayGetValueAtIndex(domains
, domainsIndex
);
2222 if (!isA_CFString(domain
))
2225 if (!catchAllFound
&&
2226 (CFStringCompare(domain
, CFSTR(""), 0) == kCFCompareEqualTo
2227 || CFStringCompare(domain
, CFSTR("."), 0) == kCFCompareEqualTo
)) {
2228 // found a catch all
2229 catchAllFound
= TRUE
;
2230 catchAllService
= serviceIndex
;
2231 catchAllConfig
= configIndex
;
2234 if (domainEndsWithDomain(hostName
, domain
)) {
2235 // found matching configuration
2236 *serviceID
= serviceName
;
2237 CFRetain(*serviceID
);
2238 *userOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, config
);
2239 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
2240 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCPropNetPPPOnDemandPriority
, priority
);
2241 addPasswordFromKeychain(session
, *serviceID
, userOptions
);
2243 goto done_selection
;
2250 // config not found, do we have a catchall ?
2251 if (catchAllFound
) {
2252 CFDictionaryRef config
;
2253 CFArrayRef serviceConfigs
;
2254 CFStringRef serviceName
;
2256 serviceName
= CFArrayGetValueAtIndex(serviceNames
, catchAllService
);
2257 serviceConfigs
= CFDictionaryGetValue(services
, serviceName
);
2258 config
= CFArrayGetValueAtIndex(serviceConfigs
, catchAllConfig
);
2260 *serviceID
= serviceName
;
2261 CFRetain(*serviceID
);
2262 *userOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, config
);
2263 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, hostName
);
2264 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCPropNetPPPOnDemandPriority
, priority
);
2265 addPasswordFromKeychain(session
, *serviceID
, userOptions
);
2267 goto done_selection
;
2273 CFRelease(serviceNames
);
2275 CFRelease(services
);
2279 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionCopyUserPreferences %@"), success
? CFSTR("succeeded") : CFSTR("failed"));
2280 SCLog(TRUE
, LOG_DEBUG
, CFSTR("Selection options: %@"), selectionOptions
);
2286 /* we don't have selection options */
2288 // (1) Figure out which service ID we care about, allocate it into passed "serviceID"
2289 success
= SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(session
, serviceID
);
2291 if (success
&& (*serviceID
!= NULL
)) {
2292 // (2) Get the list of user data for this service ID
2293 CFPropertyListRef userServices
= NULL
;
2296 // (3) We are expecting an array if the user has defined records for this service ID or NULL if the user hasn't
2297 if (userServices
!= NULL
) {
2298 if (isA_CFArray(userServices
)) {
2299 // (4) Get the default set of user options for this service
2300 success
= SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray((CFArrayRef
)userServices
,
2302 if(success
&& (userOptions
!= NULL
)) {
2303 addPasswordFromKeychain(session
, *serviceID
, userOptions
);
2306 SCLog(TRUE
, LOG_DEBUG
, CFSTR("Error, userServices are not of type CFArray!"));
2309 CFRelease(userServices
); // this is OK because SCNetworkConnectionPrivateISExpectedCFType() checks for NULL
2314 SCLog(TRUE
, LOG_DEBUG
, CFSTR("SCNetworkConnectionCopyUserPreferences %@, no selection options"), success
? CFSTR("succeeded") : CFSTR("failed"));
2322 //*******************************************************************************************
2323 // SCNetworkConnectionPrivateCopyDefaultServiceIDForDial
2324 // ----------------------------------------------------
2325 // Try to find the service id to connect
2326 // (1) Start by looking at the last service used in Network Pref / Network menu extra
2327 // (2) If Network Pref / Network menu extra has not been used, find the PPP service
2328 // with the highest ordering
2329 //********************************************************************************************
2331 SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(SCDynamicStoreRef session
, CFStringRef
*serviceID
)
2333 Boolean foundService
= FALSE
;
2334 CFPropertyListRef lastServiceSelectedInIC
= NULL
;
2338 // we found the service the user last had open in IC
2339 if (lastServiceSelectedInIC
!= NULL
) {
2340 // make sure its a PPP service
2341 if (SCNetworkConnectionPrivateIsPPPService(session
, lastServiceSelectedInIC
, kSCValNetInterfaceSubTypePPPSerial
, kSCValNetInterfaceSubTypePPPoE
)) {
2342 // make sure the service that we found is valid
2343 CFDictionaryRef dict
;
2346 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2347 kSCDynamicStoreDomainSetup
,
2348 lastServiceSelectedInIC
,
2349 kSCEntNetInterface
);
2350 dict
= SCDynamicStoreCopyValue(session
, key
);
2354 *serviceID
= CFRetain(lastServiceSelectedInIC
);
2355 foundService
= TRUE
;
2358 CFRelease(lastServiceSelectedInIC
);
2361 if (!foundService
) {
2362 foundService
= SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(session
, serviceID
);
2365 return foundService
;
2368 //********************************************************************************
2369 // SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore
2370 // -------------------------------------------------------
2371 // Find the highest ordered PPP service in the dynamic store
2372 //********************************************************************************
2374 SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(SCDynamicStoreRef session
, CFStringRef
*serviceID
)
2376 CFDictionaryRef dict
= NULL
;
2377 CFStringRef key
= NULL
;
2378 CFArrayRef serviceIDs
= NULL
;
2379 Boolean success
= FALSE
;
2387 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, kSCDynamicStoreDomainSetup
, kSCEntNetIPv4
);
2389 fprintf(stderr
, "Error, Setup Key == NULL!\n");
2393 dict
= SCDynamicStoreCopyValue(session
, key
);
2394 if (!isA_CFDictionary(dict
)) {
2395 fprintf(stderr
, "no global IPv4 entity\n");
2399 serviceIDs
= CFDictionaryGetValue(dict
, kSCPropNetServiceOrder
); // array of service id's
2400 if (!isA_CFArray(serviceIDs
)) {
2401 fprintf(stderr
, "service order not specified\n");
2405 count
= CFArrayGetCount(serviceIDs
);
2406 for (i
= 0; i
< count
; i
++) {
2407 CFStringRef service
= CFArrayGetValueAtIndex(serviceIDs
, i
);
2409 if (SCNetworkConnectionPrivateIsPPPService(session
, service
, kSCValNetInterfaceSubTypePPPSerial
, kSCValNetInterfaceSubTypePPPoE
)) {
2410 *serviceID
= CFRetain(service
);
2417 if (key
!= NULL
) CFRelease(key
);
2418 if (dict
!= NULL
) CFRelease(dict
);
2423 //********************************************************************************
2424 // SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray
2425 // ---------------------------------------------------------
2426 // Copy over user preferences for a particular service if they exist
2427 //********************************************************************************
2429 SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray
, CFDictionaryRef
*userOptions
)
2431 CFIndex count
= CFArrayGetCount(userOptionsArray
);
2434 for (i
= 0; i
< count
; i
++) {
2435 // (1) Find the dictionary
2436 CFPropertyListRef propertyList
= CFArrayGetValueAtIndex(userOptionsArray
, i
);
2438 if (isA_CFDictionary(propertyList
) != NULL
) {
2439 // See if there's a value for dial on demand
2440 CFPropertyListRef value
;
2442 value
= CFDictionaryGetValue((CFDictionaryRef
)propertyList
, k_Dial_Default_Key
);
2443 if (isA_CFBoolean(value
) != NULL
) {
2444 if (CFBooleanGetValue(value
)) {
2445 // we found the default user options
2446 *userOptions
= CFDictionaryCreateCopy(NULL
,
2447 (CFDictionaryRef
)propertyList
);
2457 //********************************************************************************
2458 // SCNetworkConnectionPrivateIsServiceType
2459 // --------------------------------------
2460 // Check and see if the service is a PPP service of the given types
2461 //********************************************************************************
2463 SCNetworkConnectionPrivateIsPPPService(SCDynamicStoreRef session
, CFStringRef serviceID
, CFStringRef subType1
, CFStringRef subType2
)
2465 CFStringRef entityKey
;
2466 Boolean isPPPService
= FALSE
;
2467 Boolean isMatchingSubType
= FALSE
;
2468 CFDictionaryRef serviceDict
;
2470 entityKey
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2471 kSCDynamicStoreDomainSetup
,
2473 kSCEntNetInterface
);
2474 if (entityKey
== NULL
) {
2478 serviceDict
= SCDynamicStoreCopyValue(session
, entityKey
);
2479 if (serviceDict
!= NULL
) {
2480 if (isA_CFDictionary(serviceDict
)) {
2482 CFStringRef subtype
;
2484 type
= CFDictionaryGetValue(serviceDict
, kSCPropNetInterfaceType
);
2485 if (isA_CFString(type
)) {
2486 isPPPService
= CFEqual(type
, kSCValNetInterfaceTypePPP
);
2489 subtype
= CFDictionaryGetValue(serviceDict
, kSCPropNetInterfaceSubType
);
2490 if (isA_CFString(subtype
)) {
2491 isMatchingSubType
= CFEqual(subtype
, subType1
);
2492 if (!isMatchingSubType
&& subType2
)
2493 isMatchingSubType
= CFEqual(subtype
, subType2
);
2496 CFRelease(serviceDict
);
2498 CFRelease(entityKey
);
2500 return (isPPPService
&& isMatchingSubType
);
2503 //********************************************************************************
2504 // addPasswordFromKeychain
2505 // --------------------------------------
2506 // Get the password and shared secret out of the keychain and add
2507 // them to the PPP and IPSec dictionaries
2508 //********************************************************************************
2510 addPasswordFromKeychain(SCDynamicStoreRef session
, CFStringRef serviceID
, CFDictionaryRef
*userOptions
)
2512 CFPropertyListRef uniqueID
;
2513 CFStringRef password
;
2514 CFStringRef sharedsecret
= NULL
;
2516 /* user options must exist */
2517 if (*userOptions
== NULL
)
2520 /* first, get the unique identifier used to store passwords in the keychain */
2521 uniqueID
= CFDictionaryGetValue(*userOptions
, k_Unique_Id_Key
);
2522 if (!isA_CFString(uniqueID
))
2525 /* first, get the PPP password */
2526 password
= copyPasswordFromKeychain(uniqueID
);
2528 /* then, if necessary, get the IPSec Shared Secret */
2529 if (SCNetworkConnectionPrivateIsPPPService(session
, serviceID
, kSCValNetInterfaceSubTypeL2TP
, 0)) {
2530 CFMutableStringRef uniqueIDSS
;
2532 uniqueIDSS
= CFStringCreateMutableCopy(NULL
, 0, uniqueID
);
2533 CFStringAppend(uniqueIDSS
, CFSTR(".SS"));
2534 sharedsecret
= copyPasswordFromKeychain(uniqueIDSS
);
2535 CFRelease(uniqueIDSS
);
2538 /* did we find our information in the key chain ? */
2539 if ((password
!= NULL
) || (sharedsecret
!= NULL
)) {
2540 CFMutableDictionaryRef newOptions
;
2542 newOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, *userOptions
);
2545 if (password
!= NULL
) {
2546 CFDictionaryRef entity
;
2547 CFMutableDictionaryRef newEntity
;
2549 entity
= CFDictionaryGetValue(*userOptions
, kSCEntNetPPP
);
2550 if (isA_CFDictionary(entity
))
2551 newEntity
= CFDictionaryCreateMutableCopy(NULL
, 0, entity
);
2553 newEntity
= CFDictionaryCreateMutable(NULL
,
2555 &kCFTypeDictionaryKeyCallBacks
,
2556 &kCFTypeDictionaryValueCallBacks
);
2559 /* set the PPP password */
2560 CFDictionarySetValue(newEntity
, kSCPropNetPPPAuthPassword
, uniqueID
);
2561 CFDictionarySetValue(newEntity
, kSCPropNetPPPAuthPasswordEncryption
, kSCValNetPPPAuthPasswordEncryptionKeychain
);
2562 CFRelease(password
);
2564 /* update the PPP entity */
2565 CFDictionarySetValue(newOptions
, kSCEntNetPPP
, newEntity
);
2566 CFRelease(newEntity
);
2569 /* IPSec Shared Secret */
2570 if (sharedsecret
!= NULL
) {
2571 CFDictionaryRef entity
;
2572 CFMutableDictionaryRef newEntity
;
2574 entity
= CFDictionaryGetValue(*userOptions
, kSCEntNetIPSec
);
2575 if (isA_CFDictionary(entity
))
2576 newEntity
= CFDictionaryCreateMutableCopy(NULL
, 0, entity
);
2578 newEntity
= CFDictionaryCreateMutable(NULL
,
2580 &kCFTypeDictionaryKeyCallBacks
,
2581 &kCFTypeDictionaryValueCallBacks
);
2583 /* set the IPSec Shared Secret */
2584 CFDictionarySetValue(newEntity
, kSCPropNetIPSecSharedSecret
, sharedsecret
);
2585 CFRelease(sharedsecret
);
2587 /* update the IPSec entity */
2588 CFDictionarySetValue(newOptions
, kSCEntNetIPSec
, newEntity
);
2589 CFRelease(newEntity
);
2592 /* update the userOptions dictionary */
2593 CFRelease(*userOptions
);
2594 *userOptions
= CFDictionaryCreateCopy(NULL
, newOptions
);
2595 CFRelease(newOptions
);
2600 #if !TARGET_OS_IPHONE
2601 //********************************************************************************
2602 // copyKeychainEnumerator
2603 // --------------------------------------
2604 // Gather Keychain Enumerator
2605 //********************************************************************************
2607 copyKeychainEnumerator(CFStringRef uniqueIdentifier
)
2609 CFArrayRef itemArray
= NULL
;
2610 CFMutableDictionaryRef query
;
2613 query
= CFDictionaryCreateMutable(NULL
,
2615 &kCFTypeDictionaryKeyCallBacks
,
2616 &kCFTypeDictionaryValueCallBacks
);
2617 CFDictionarySetValue(query
, kSecClass
, kSecClassGenericPassword
);
2618 CFDictionarySetValue(query
, kSecAttrService
, uniqueIdentifier
);
2619 CFDictionarySetValue(query
, kSecReturnRef
, kCFBooleanTrue
);
2620 CFDictionarySetValue(query
, kSecMatchLimit
, kSecMatchLimitAll
);
2621 result
= SecItemCopyMatching(query
, (CFTypeRef
*)&itemArray
);
2623 if ((result
!= noErr
) && (itemArray
!= NULL
)) {
2624 CFRelease(itemArray
);
2630 #endif // !TARGET_OS_IPHONE
2632 //********************************************************************************
2633 // copyPasswordFromKeychain
2634 // --------------------------------------
2635 // Given a uniqueID, retrieve the password from the keychain
2636 //********************************************************************************
2638 copyPasswordFromKeychain(CFStringRef uniqueID
)
2640 #if !TARGET_OS_IPHONE
2641 CFArrayRef enumerator
;
2643 CFStringRef password
= NULL
;
2645 enumerator
= copyKeychainEnumerator(uniqueID
);
2646 if (enumerator
== NULL
) {
2647 return NULL
; // if no keychain enumerator
2650 n
= CFArrayGetCount(enumerator
);
2654 SecKeychainItemRef itemRef
;
2657 itemRef
= (SecKeychainItemRef
)CFArrayGetValueAtIndex(enumerator
, 0);
2658 result
= SecKeychainItemCopyContent(itemRef
, // itemRef
2662 (void *)&data
); // outData
2663 if ((result
== noErr
) && (data
!= NULL
) && (dataLen
> 0)) {
2664 password
= CFStringCreateWithBytes(NULL
, data
, dataLen
, kCFStringEncodingUTF8
, TRUE
);
2665 (void) SecKeychainItemFreeContent(NULL
, data
);
2670 CFRelease(enumerator
);
2673 #else // !TARGET_OS_IPHONE
2675 #endif // !TARGET_OS_IPHONE