1/*
2 * Copyright (c) 2003-2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * Modification History
26 *
27 * April 14, 2004		Christophe Allie <callie@apple.com>
28 * - use mach messages
29
30 * December 20, 2002		Christophe Allie <callie@apple.com>
31 * - initial revision
32 */
33
34
35//#define DEBUG_MACH_PORT_ALLOCATIONS
36
37
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 <CoreFoundation/CFXPCBridge.h>
45#include <SystemConfiguration/SystemConfiguration.h>
46#include <SystemConfiguration/SCPrivate.h>
47#include <SystemConfiguration/SCValidation.h>
48#include <SystemConfiguration/VPNAppLayerPrivate.h>
49#include <SystemConfiguration/VPNTunnel.h>
50
51#if	!TARGET_OS_IPHONE
52#include <Security/Security.h>
53#include "dy_framework.h"
54#endif	// !TARGET_OS_IPHONE
55
56#include <bootstrap.h>
57
58#include <pthread.h>
59#include <notify.h>
60#include <netinet/in.h>
61#include <arpa/inet.h>
62#include <netdb.h>
63#include <unistd.h>
64#include <sys/ioctl.h>
65#include <sys/socket.h>
66#include <net/if.h>
67#include <mach/mach.h>
68#include <bsm/audit.h>
69#include <bsm/libbsm.h>
70#include <sandbox.h>
71#include <sys/proc_info.h>
72#include <libproc.h>
73
74#include <ppp/ppp_msg.h>
75#include "pppcontroller.h"
76#include <ppp/pppcontroller_types.h>
77
78#ifndef	PPPCONTROLLER_SERVER_PRIV
79#define	PPPCONTROLLER_SERVER_PRIV	PPPCONTROLLER_SERVER
80#endif	// !PPPCONTROLLER_SERVER_PRIV
81
82
83#include "SCNetworkConnectionInternal.h"
84
85static int		debug			= 0;
86static pthread_once_t	initialized		= PTHREAD_ONCE_INIT;
87static pthread_mutex_t	scnc_lock		= PTHREAD_MUTEX_INITIALIZER;
88static mach_port_t	scnc_server		= MACH_PORT_NULL;
89static char		*scnc_server_name	= NULL;
90
91
92typedef struct {
93
94	/* base CFType information */
95	CFRuntimeBase			cfBase;
96
97	/* lock */
98	pthread_mutex_t			lock;
99
100	/* service */
101	SCNetworkServiceRef		service;
102
103	/* client info (if we are proxying for another process */
104	mach_port_t			client_audit_session;
105	audit_token_t			client_audit_token;
106	mach_port_t			client_bootstrap_port;
107	uid_t				client_uid;
108	gid_t				client_gid;
109	pid_t				client_pid;
110	uuid_t				client_uuid;
111	CFStringRef			client_bundle_id;
112
113	/* ref to PPP controller for control messages */
114	mach_port_t			session_port;
115
116	/* ref to PPP controller for notification messages */
117	CFMachPortRef			notify_port;
118
119	/* keep track of whether we're acquired the initial status */
120	Boolean				haveStatus;
121
122	/* run loop source, callout, context, rl scheduling info */
123	Boolean				scheduled;
124	CFRunLoopSourceRef		rls;
125	SCNetworkConnectionCallBack	rlsFunction;
126	SCNetworkConnectionContext	rlsContext;
127	CFMutableArrayRef		rlList;
128
129	/* SCNetworkConnectionSetDispatchQueue */
130	dispatch_group_t		dispatchGroup;
131	dispatch_queue_t		dispatchQueue;
132	dispatch_source_t		dispatchSource;
133
134	SCNetworkConnectionType		type;
135	Boolean				on_demand;
136	CFDictionaryRef			on_demand_info;
137	CFDictionaryRef			on_demand_user_options;
138	CFStringRef			on_demand_required_probe;
139
140	/* Flow Divert support info */
141	CFDictionaryRef			flow_divert_token_params;
142
143#if !TARGET_IPHONE_SIMULATOR
144	/* NetworkExtension data structures */
145	ne_session_t			ne_session;
146#endif /* !TARGET_IPHONE_SIMULATOR */
147} SCNetworkConnectionPrivate, *SCNetworkConnectionPrivateRef;
148
149
150static __inline__ CFTypeRef
151isA_SCNetworkConnection(CFTypeRef obj)
152{
153	return (isA_CFType(obj, SCNetworkConnectionGetTypeID()));
154}
155
156
157#if !TARGET_IPHONE_SIMULATOR
158Boolean
159__SCNetworkConnectionUseNetworkExtension(SCNetworkConnectionPrivateRef connectionPrivate)
160{
161	Boolean result = FALSE;
162
163	if (ne_session_use_as_system_vpn() && connectionPrivate->service != NULL) {
164		_SCErrorSet(kSCStatusOK);
165		result = _SCNetworkServiceIsVPN(connectionPrivate->service);
166		/*
167		 * SCNetworkServiceGetInterface (called by _SCNetworkServiceIsVPN) will set the SC error to kSCStatusInvalidArgument if the service does not have an associated prefs object.
168		 * In that case, we try to get the service type/subtype from the dynamic store.
169		 */
170		if (!result && SCError() == kSCStatusInvalidArgument) {
171			CFStringRef interfaceKey = SCDynamicStoreKeyCreateNetworkServiceEntity(kCFAllocatorDefault,
172											     kSCDynamicStoreDomainSetup,
173											     SCNetworkServiceGetServiceID(connectionPrivate->service),
174											     kSCEntNetInterface);
175			CFDictionaryRef interfaceDict = SCDynamicStoreCopyValue(NULL, interfaceKey);
176			if (isA_CFDictionary(interfaceDict)) {
177				CFStringRef interfaceType = CFDictionaryGetValue(interfaceDict, kSCPropNetInterfaceType);
178				if (isA_CFString(interfaceType)) {
179					if (CFEqual(interfaceType, kSCNetworkInterfaceTypePPP)) {
180						CFStringRef interfaceSubType = CFDictionaryGetValue(interfaceDict, kSCPropNetInterfaceSubType);
181						result = (isA_CFString(interfaceSubType) && (CFEqual(interfaceSubType, kSCValNetInterfaceSubTypePPTP) || CFEqual(interfaceSubType, kSCValNetInterfaceSubTypeL2TP)));
182					} else {
183						result = (CFEqual(interfaceType, kSCNetworkInterfaceTypeVPN) || CFEqual(interfaceType, kSCNetworkInterfaceTypeIPSec));
184					}
185				}
186			}
187			if (interfaceDict != NULL) {
188			    CFRelease(interfaceDict);
189			}
190			CFRelease(interfaceKey);
191		}
192	}
193
194	return result;
195}
196#endif /* !TARGET_IPHONE_SIMULATOR */
197
198
199Boolean
200__SCNetworkConnectionUsingNetworkExtension(SCNetworkConnectionPrivateRef connectionPrivate)
201{
202#if !TARGET_IPHONE_SIMULATOR
203    return (connectionPrivate->ne_session != NULL);
204#else
205	return FALSE;
206#endif /* !TARGET_IPHONE_SIMULATOR */
207}
208
209
210static CFStringRef
211__SCNetworkConnectionCopyDescription(CFTypeRef cf)
212{
213	CFAllocatorRef			allocator		= CFGetAllocator(cf);
214	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)cf;
215	CFMutableStringRef		result;
216
217	result = CFStringCreateMutable(allocator, 0);
218	CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkConnection, %p [%p]> {"), cf, allocator);
219	CFStringAppendFormat(result, NULL, CFSTR("service = %p"), connectionPrivate->service);
220	if (connectionPrivate->session_port != MACH_PORT_NULL) {
221		CFStringAppendFormat(result, NULL, CFSTR(", server port = 0x%x"), connectionPrivate->session_port);
222	}
223	CFStringAppendFormat(result, NULL, CFSTR("using NetworkExtension = %s"), (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate) ? "yes" : "no"));
224	CFStringAppendFormat(result, NULL, CFSTR("}"));
225
226	return result;
227}
228
229
230static void
231__SCNetworkConnectionDeallocate(CFTypeRef cf)
232{
233	SCNetworkConnectionPrivateRef	connectionPrivate = (SCNetworkConnectionPrivateRef)cf;
234
235	/* release resources */
236	pthread_mutex_destroy(&connectionPrivate->lock);
237
238	if (connectionPrivate->client_audit_session != MACH_PORT_NULL) {
239		mach_port_mod_refs(mach_task_self(),
240				   connectionPrivate->client_audit_session,
241				   MACH_PORT_RIGHT_SEND,
242				   -1);
243	}
244
245	if (connectionPrivate->client_bootstrap_port != MACH_PORT_NULL) {
246		mach_port_mod_refs(mach_task_self(),
247				   connectionPrivate->client_bootstrap_port,
248				   MACH_PORT_RIGHT_SEND,
249				   -1);
250	}
251
252	if (connectionPrivate->client_bundle_id != NULL) {
253		CFRelease(connectionPrivate->client_bundle_id);
254	}
255
256	if (connectionPrivate->rls != NULL) {
257		CFRunLoopSourceInvalidate(connectionPrivate->rls);
258		CFRelease(connectionPrivate->rls);
259	}
260
261	if (connectionPrivate->rlList != NULL) {
262		CFRelease(connectionPrivate->rlList);
263	}
264
265	if (connectionPrivate->notify_port != NULL) {
266		mach_port_t	mp	= CFMachPortGetPort(connectionPrivate->notify_port);
267
268		__MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionDeallocate notify_port", mp);
269		CFMachPortInvalidate(connectionPrivate->notify_port);
270		CFRelease(connectionPrivate->notify_port);
271		mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1);
272	}
273
274	if (connectionPrivate->session_port != MACH_PORT_NULL) {
275		__MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionDeallocate session_port", connectionPrivate->session_port);
276		(void) mach_port_deallocate(mach_task_self(), connectionPrivate->session_port);
277	}
278
279	if (connectionPrivate->rlsContext.release != NULL)
280		(*connectionPrivate->rlsContext.release)(connectionPrivate->rlsContext.info);
281
282	if (connectionPrivate->service != NULL) {
283		CFRelease(connectionPrivate->service);
284	}
285
286	if (connectionPrivate->on_demand_info != NULL) {
287		CFRelease(connectionPrivate->on_demand_info);
288	}
289
290	if (connectionPrivate->on_demand_user_options != NULL) {
291		CFRelease(connectionPrivate->on_demand_user_options);
292	}
293
294	if (connectionPrivate->on_demand_required_probe != NULL) {
295		CFRelease(connectionPrivate->on_demand_required_probe);
296	}
297
298	if (connectionPrivate->flow_divert_token_params != NULL) {
299		CFRelease(connectionPrivate->flow_divert_token_params);
300	}
301
302#if !TARGET_IPHONE_SIMULATOR
303	if (connectionPrivate->ne_session != NULL) {
304		ne_session_set_event_handler(connectionPrivate->ne_session, NULL, NULL);
305		ne_session_release(connectionPrivate->ne_session);
306	}
307#endif /* !TARGET_IPHONE_SIMULATOR */
308
309	return;
310}
311
312
313static CFTypeID __kSCNetworkConnectionTypeID	= _kCFRuntimeNotATypeID;
314
315static const CFRuntimeClass __SCNetworkConnectionClass = {
316	0,					// version
317	"SCNetworkConnection",			// className
318	NULL,					// init
319	NULL,					// copy
320	__SCNetworkConnectionDeallocate,	// dealloc
321	NULL,					// equal
322	NULL,					// hash
323	NULL,					// copyFormattingDesc
324	__SCNetworkConnectionCopyDescription	// copyDebugDesc
325};
326
327
328static void
329childForkHandler()
330{
331	/* the process has forked (and we are the child process) */
332
333	scnc_server = MACH_PORT_NULL;
334	scnc_server_name = NULL;
335	return;
336}
337
338
339static void
340__SCNetworkConnectionInitialize(void)
341{
342	char	*env;
343
344	/* get the debug environment variable */
345	env = getenv("PPPDebug");
346	if (env != NULL) {
347		if (sscanf(env, "%d", &debug) != 1) {
348			/* PPPDebug value is not valid (or non-numeric), set debug to 1 */
349			debug = 1;
350		}
351	}
352
353	/* register with CoreFoundation */
354	__kSCNetworkConnectionTypeID = _CFRuntimeRegisterClass(&__SCNetworkConnectionClass);
355
356	/* add handler to cleanup after fork() */
357	(void) pthread_atfork(NULL, NULL, childForkHandler);
358
359	return;
360}
361
362
363static Boolean
364__SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection);
365
366#define SC_NETWORK_CONNECTION_QUEUE "SCNetworkConnectionQueue"
367
368static dispatch_queue_t
369__SCNetworkConnectionQueue()
370{
371	static dispatch_once_t	once;
372	static dispatch_queue_t	q;
373
374	dispatch_once(&once, ^{
375		q = dispatch_queue_create(SC_NETWORK_CONNECTION_QUEUE, NULL);
376	});
377
378	return q;
379}
380
381
382static void
383__SCNetworkConnectionCallBackRunLoopPerform(SCNetworkConnectionRef connection,
384					    CFRunLoopRef rl,
385					    CFStringRef rl_mode,
386					    SCNetworkConnectionCallBack rlsFunction,
387					    void (*context_release)(const void *),
388					    void *context_info)
389{
390	SCNetworkConnectionStatus	nc_status	= kSCNetworkConnectionInvalid;
391
392	nc_status = SCNetworkConnectionGetStatus(connection);
393	CFRunLoopPerformBlock(rl, rl_mode,
394			      ^{
395				      (*rlsFunction)(connection, nc_status, context_info);
396				      if ((context_release != NULL) && (context_info != NULL)) {
397					      (*context_release)(context_info);
398				      }
399				      CFRelease(rl);
400				      CFRelease(rl_mode);
401				      CFRelease(connection);
402			      });
403	CFRunLoopWakeUp(rl);
404	return;
405}
406
407static void
408__SCNetworkConnectionCallBackDispatchPerform(SCNetworkConnectionRef connection,
409					     dispatch_queue_t q,
410					     SCNetworkConnectionCallBack rlsFunction,
411					     void (*context_release)(const void *),
412					     void *context_info)
413{
414	SCNetworkConnectionStatus	nc_status	= kSCNetworkConnectionInvalid;
415
416	nc_status = SCNetworkConnectionGetStatus(connection);
417	dispatch_async(q,
418		       ^{
419			       (*rlsFunction)(connection, nc_status, context_info);
420			       if ((context_release != NULL) && (context_info != NULL)) {
421				       (*context_release)(context_info);
422			       }
423			       dispatch_release(q);
424			       CFRelease(connection);
425		       });
426	return;
427}
428
429
430static void
431__SCNetworkConnectionCallBack(void *connection)
432{
433	boolean_t			exec_async		= FALSE;
434	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
435	void				*context_info;
436	void				(*context_release)(const void *);
437	CFRunLoopRef			rl			= NULL;
438	CFStringRef			rl_mode;
439	SCNetworkConnectionCallBack	rlsFunction		= NULL;
440	dispatch_queue_t		q			= NULL;
441	SCNetworkConnectionStatus	nc_status		= kSCNetworkConnectionInvalid;
442
443	pthread_mutex_lock(&connectionPrivate->lock);
444
445	if (!connectionPrivate->scheduled) {
446		// if not currently scheduled
447		pthread_mutex_unlock(&connectionPrivate->lock);
448		return;
449	}
450
451	rlsFunction = connectionPrivate->rlsFunction;
452	if (rlsFunction == NULL) {
453		pthread_mutex_unlock(&connectionPrivate->lock);
454		return;
455	}
456
457	if ((connectionPrivate->rlsContext.retain != NULL) && (connectionPrivate->rlsContext.info != NULL)) {
458		context_info	= (void *)(*connectionPrivate->rlsContext.retain)(connectionPrivate->rlsContext.info);
459		context_release	= connectionPrivate->rlsContext.release;
460	} else {
461		context_info	= connectionPrivate->rlsContext.info;
462		context_release	= NULL;
463	}
464
465#if !TARGET_IPHONE_SIMULATOR
466	if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
467		pthread_mutex_unlock(&connectionPrivate->lock);
468
469		nc_status = SCNetworkConnectionGetStatus(connection);
470		(*rlsFunction)(connection, nc_status, context_info);
471		if ((context_release != NULL) && (context_info != NULL)) {
472			(*context_release)(context_info);
473		}
474		CFRelease(connection); /* This releases the reference that we took in the NESessionEventStatusChanged event handler */
475		return;
476	}
477#endif /* !TARGET_IPHONE_SIMULATOR */
478
479	// Do we need to spin a new thread? (either we are running on the main
480	// dispatch queue or main runloop)
481	if (connectionPrivate->rlList == NULL) {
482		// if we are performing the callback on a dispatch queue
483		q = connectionPrivate->dispatchQueue;
484		if (q == dispatch_get_main_queue()) {
485			exec_async = TRUE;
486		}
487	} else {
488		rl = CFRunLoopGetCurrent();
489		if (rl == CFRunLoopGetMain()) {
490			exec_async = TRUE;
491		}
492	}
493
494	CFRetain(connection);
495	pthread_mutex_unlock(&connectionPrivate->lock);
496
497	if (!exec_async) {
498		nc_status = SCNetworkConnectionGetStatus(connection);
499		(*rlsFunction)(connection, nc_status, context_info);
500		if ((context_release != NULL) && (context_info != NULL)) {
501			(*context_release)(context_info);
502		}
503		CFRelease(connection);
504		return;
505	}
506
507	if (connectionPrivate->rlList == NULL) {
508		assert(q != NULL);
509		dispatch_retain(q);
510		dispatch_async(__SCNetworkConnectionQueue(), ^{
511				       __SCNetworkConnectionCallBackDispatchPerform(connection,
512										    q,
513										    rlsFunction,
514										    context_release,
515										    context_info);
516		});
517	} else {
518		assert(rl != NULL);
519		CFRetain(rl);
520		rl_mode = CFRunLoopCopyCurrentMode(rl);
521		dispatch_async(__SCNetworkConnectionQueue(), ^{
522			__SCNetworkConnectionCallBackRunLoopPerform(connection,
523								    rl,
524								    rl_mode,
525								    rlsFunction,
526								    context_release,
527								    context_info);
528		});
529	}
530
531	return;
532}
533
534
535static void
536__SCNetworkConnectionMachCallBack(CFMachPortRef port, void * msg, CFIndex size, void * info)
537{
538	mach_no_senders_notification_t	*buf			= msg;
539	mach_msg_id_t			msgid			= buf->not_header.msgh_id;
540	SCNetworkConnectionRef		connection		= (SCNetworkConnectionRef)info;
541
542	if (msgid == MACH_NOTIFY_NO_SENDERS) {
543		// re-establish notification
544		SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCNetworkConnectionMachCallBack: PPPController server died"));
545		(void)__SCNetworkConnectionReconnectNotifications(connection);
546	}
547
548	__SCNetworkConnectionCallBack(info);
549}
550
551
552#pragma mark -
553#pragma mark SCNetworkConnection APIs
554
555
556static CFStringRef
557pppMPCopyDescription(const void *info)
558{
559	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)info;
560
561	return CFStringCreateWithFormat(NULL,
562					NULL,
563					CFSTR("<SCNetworkConnection MP %p> {service = %@, callout = %p}"),
564					connectionPrivate,
565					connectionPrivate->service,
566					connectionPrivate->rlsFunction);
567}
568
569
570static SCNetworkConnectionPrivateRef
571__SCNetworkConnectionCreatePrivate(CFAllocatorRef		allocator,
572				   SCNetworkServiceRef		service,
573				   SCNetworkConnectionCallBack	callout,
574				   SCNetworkConnectionContext	*context)
575{
576	SCNetworkConnectionPrivateRef	connectionPrivate	= NULL;
577	uint32_t			size;
578
579
580	/* initialize runtime */
581	pthread_once(&initialized, __SCNetworkConnectionInitialize);
582
583	/* allocate NetworkConnection */
584	size = sizeof(SCNetworkConnectionPrivate) - sizeof(CFRuntimeBase);
585	connectionPrivate = (SCNetworkConnectionPrivateRef)_CFRuntimeCreateInstance(allocator, __kSCNetworkConnectionTypeID, size, NULL);
586	if (connectionPrivate == NULL) {
587		goto fail;
588	}
589
590	/* zero the data structure */
591	bzero(((u_char*)connectionPrivate)+sizeof(CFRuntimeBase), size);
592
593	pthread_mutex_init(&connectionPrivate->lock, NULL);
594
595	/* save the service */
596	if (service != NULL) {
597		connectionPrivate->service = CFRetain(service);
598	}
599
600	connectionPrivate->client_audit_session = MACH_PORT_NULL;
601	connectionPrivate->client_bootstrap_port = MACH_PORT_NULL;
602	connectionPrivate->client_uid = geteuid();
603	connectionPrivate->client_gid = getegid();
604	connectionPrivate->client_pid = getpid();
605	connectionPrivate->client_bundle_id = NULL;
606	uuid_clear(connectionPrivate->client_uuid);
607
608	connectionPrivate->rlsFunction = callout;
609
610	if (context) {
611		bcopy(context, &connectionPrivate->rlsContext, sizeof(SCNetworkConnectionContext));
612		if (context->retain != NULL) {
613			connectionPrivate->rlsContext.info = (void *)(*context->retain)(context->info);
614		}
615	}
616
617	connectionPrivate->type = kSCNetworkConnectionTypeUnknown;
618
619#if !TARGET_IPHONE_SIMULATOR
620	if (__SCNetworkConnectionUseNetworkExtension(connectionPrivate)) {
621		CFStringRef serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service);
622		if (serviceID != NULL) {
623			uuid_string_t service_uuid_str;
624			if (CFStringGetCString(serviceID, service_uuid_str, sizeof(service_uuid_str), kCFStringEncodingUTF8)) {
625				uuid_t config_id;
626				if (uuid_parse(service_uuid_str, config_id) == 0) {
627					connectionPrivate->ne_session = ne_session_create(config_id, NESessionTypeVPN);
628				}
629			}
630		}
631
632		if (connectionPrivate->ne_session == NULL) {
633			SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkConnection failed to create an ne_session: service ID %@ is not a valid UUID"), serviceID);
634			goto fail;
635		}
636	}
637#endif /* !TARGET_IPHONE_SIMULATOR */
638
639	/* success, return the connection reference */
640	return connectionPrivate;
641
642    fail:
643
644	/* failure, clean up and leave */
645	if (connectionPrivate != NULL) {
646		CFRelease(connectionPrivate);
647	}
648
649	_SCErrorSet(kSCStatusFailed);
650	return NULL;
651}
652
653
654static mach_port_t
655__SCNetworkConnectionServerPort(kern_return_t *status)
656{
657	mach_port_t	server	= MACH_PORT_NULL;
658
659#ifdef	BOOTSTRAP_PRIVILEGED_SERVER
660	*status = bootstrap_look_up2(bootstrap_port,
661				     __SCNetworkConnectionGetControllerPortName(),
662				     &server,
663				     0,
664				     BOOTSTRAP_PRIVILEGED_SERVER);
665#else	// BOOTSTRAP_PRIVILEGED_SERVER
666	*status = bootstrap_look_up(bootstrap_port, __SCNetworkConnectionGetControllerPortName(), &server);
667#endif	// BOOTSTRAP_PRIVILEGED_SERVER
668
669	switch (*status) {
670		case BOOTSTRAP_SUCCESS :
671			// service currently registered, "a good thing" (tm)
672			return server;
673		case BOOTSTRAP_NOT_PRIVILEGED :
674			// the service is not privileged
675			break;
676		case BOOTSTRAP_UNKNOWN_SERVICE :
677			// service not currently registered, try again later
678			break;
679		default :
680#ifdef	DEBUG
681			SCLog(_sc_verbose, LOG_DEBUG,
682			      CFSTR("SCNetworkConnection bootstrap_look_up() failed: status=%s"),
683			      bootstrap_strerror(*status));
684#endif	// DEBUG
685			break;
686	}
687
688	scnc_server_name = NULL;		/* reset pppcontroller server */
689	return MACH_PORT_NULL;
690}
691
692static mach_port_t
693__SCNetworkConnectionGetCurrentServerPort(void)
694{
695	return scnc_server;
696}
697
698static mach_port_t
699__SCNetworkConnectionRefreshServerPort(mach_port_t current_server, int *mach_result)
700{
701	mach_port_t new_server;
702
703	pthread_mutex_lock(&scnc_lock);
704	if (scnc_server != MACH_PORT_NULL) {
705		if (current_server == scnc_server) {
706			scnc_server_name = NULL;
707			// if the server we tried returned the error
708			(void)mach_port_deallocate(mach_task_self(), scnc_server);
709			scnc_server = __SCNetworkConnectionServerPort(mach_result);
710		} else {
711			// another thread has refreshed the server port
712		}
713	} else {
714		scnc_server = __SCNetworkConnectionServerPort(mach_result);
715	}
716	new_server = scnc_server;
717	pthread_mutex_unlock(&scnc_lock);
718
719	return new_server;
720}
721
722#if	((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && (!TARGET_IPHONE_SIMULATOR || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 70000))
723#define	HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
724#endif
725
726
727#if	((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && !TARGET_IPHONE_SIMULATOR
728#define	HAVE_PPPCONTROLLER_ATTACHWITHPROXY
729#endif
730
731static mach_port_t
732__SCNetworkConnectionSessionPort(SCNetworkConnectionPrivateRef connectionPrivate)
733{
734	void		*data		= NULL;
735	CFIndex		dataLen		= 0;
736	CFDataRef	dataRef		= NULL;
737	mach_port_t	notify_port	= MACH_PORT_NULL;
738	mach_port_t	oldNotify	= MACH_PORT_NULL;
739	int		retry		= 0;
740	int		sc_status	= kSCStatusFailed;
741	mach_port_t	server		= __SCNetworkConnectionGetCurrentServerPort();
742	kern_return_t	status		= KERN_SUCCESS;
743
744#ifdef	HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
745	mach_port_t	au_session	= MACH_PORT_NULL;
746#endif	// HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
747
748	if (connectionPrivate->session_port != MACH_PORT_NULL) {
749		return connectionPrivate->session_port;
750	}
751
752	if (connectionPrivate->service == NULL) {
753		sc_status = kSCStatusConnectionNoService;
754		goto done;
755	}
756
757	if (!_SCSerializeString(SCNetworkServiceGetServiceID(connectionPrivate->service), &dataRef, &data, &dataLen)) {
758		goto done;
759	}
760
761	if (connectionPrivate->notify_port != NULL) {
762		mach_port_t	mp	= CFMachPortGetPort(connectionPrivate->notify_port);
763
764		__MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionSessionPort mp", mp);
765		CFMachPortInvalidate(connectionPrivate->notify_port);
766		CFRelease(connectionPrivate->notify_port);
767		connectionPrivate->notify_port = NULL;
768		mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1);
769	}
770
771#ifdef	HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
772	au_session = audit_session_self();
773#endif	// HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
774
775	// open a new session with the server
776	while (TRUE) {
777		if ((connectionPrivate->rlsFunction != NULL) && (notify_port == MACH_PORT_NULL)) {
778			// allocate port (for server response)
779			status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &notify_port);
780			if (status != KERN_SUCCESS) {
781				SCLog(TRUE, LOG_ERR, CFSTR("__SCNetworkConnectionSessionPort mach_port_allocate(): %s"), mach_error_string(status));
782				sc_status = status;
783				goto done;
784			}
785
786			// add send right (passed to the server)
787			status = mach_port_insert_right(mach_task_self(),
788							notify_port,
789							notify_port,
790							MACH_MSG_TYPE_MAKE_SEND);
791			if (status != KERN_SUCCESS) {
792				SCLog(TRUE, LOG_ERR, CFSTR("__SCNetworkConnectionSessionPort mach_port_insert_right(): %s"), mach_error_string(status));
793				mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
794				sc_status = status;
795				goto done;
796			}
797		}
798
799		if (server != MACH_PORT_NULL) {
800#ifdef	HAVE_PPPCONTROLLER_ATTACHWITHPROXY
801			if ((connectionPrivate->client_audit_session == MACH_PORT_NULL) &&
802			    (connectionPrivate->client_bootstrap_port == MACH_PORT_NULL) &&
803			    (connectionPrivate->client_uid == geteuid()) &&
804			    (connectionPrivate->client_gid == getegid()) &&
805			    (connectionPrivate->client_pid == getpid())
806			   ) {
807#endif	// HAVE_PPPCONTROLLER_ATTACHWITHPROXY
808				status = pppcontroller_attach(server,
809							      data,
810							       (mach_msg_type_number_t)dataLen,
811							      bootstrap_port,
812							      notify_port,
813#ifdef	HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
814							      au_session,
815#endif	// HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
816							      &connectionPrivate->session_port,
817							      &sc_status);
818#ifdef	HAVE_PPPCONTROLLER_ATTACHWITHPROXY
819			} else {
820				mach_port_t	client_au_session;
821				mach_port_t	client_bootstrap_port;
822
823				if (connectionPrivate->client_audit_session == MACH_PORT_NULL) {
824					client_au_session = au_session;
825				} else {
826					client_au_session = connectionPrivate->client_audit_session;
827				}
828
829				if (connectionPrivate->client_bootstrap_port == MACH_PORT_NULL) {
830					client_bootstrap_port = bootstrap_port;
831				} else {
832					client_bootstrap_port = connectionPrivate->client_bootstrap_port;
833				}
834
835				status = pppcontroller_attach_proxy(server,
836								    data,
837								    (mach_msg_type_number_t)dataLen,
838								    client_bootstrap_port,
839								    notify_port,
840								    client_au_session,
841								    connectionPrivate->client_uid,
842								    connectionPrivate->client_gid,
843								    connectionPrivate->client_pid,
844								    &connectionPrivate->session_port,
845								    &sc_status);
846			}
847#endif	// HAVE_PPPCONTROLLER_ATTACHWITHPROXY
848			if (status == KERN_SUCCESS) {
849				if (sc_status != kSCStatusOK) {
850					SCLog(TRUE, LOG_DEBUG,
851					      CFSTR("__SCNetworkConnectionSessionPort : attach w/error, sc_status=%s%s"),
852					      SCErrorString(sc_status),
853					      (connectionPrivate->session_port != MACH_PORT_NULL) ? ", w/session_port!=MACH_PORT_NULL" : "");
854
855					if (connectionPrivate->session_port != MACH_PORT_NULL) {
856						__MACH_PORT_DEBUG(TRUE,
857								  "*** __SCNetworkConnectionSessionPort session_port (attach w/error, cleanup)",
858								  connectionPrivate->session_port);
859						mach_port_deallocate(mach_task_self(), connectionPrivate->session_port);
860						connectionPrivate->session_port = MACH_PORT_NULL;
861					}
862
863					if (notify_port != MACH_PORT_NULL) {
864						__MACH_PORT_DEBUG(TRUE,
865								  "*** __SCNetworkConnectionSessionPort notify_port (attach w/error, cleanup)",
866								  notify_port);
867						(void) mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
868						notify_port = MACH_PORT_NULL;
869					}
870				}
871				break;
872			}
873
874			// our [cached] server port is not valid
875			SCLog(TRUE, LOG_DEBUG, CFSTR("__SCNetworkConnectionSessionPort : !attach: %s"), SCErrorString(status));
876			if (status == MACH_SEND_INVALID_DEST) {
877				// the server is not yet available
878				__MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionSessionPort notify_port (!dest)", notify_port);
879			} else if (status == MIG_SERVER_DIED) {
880				__MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionSessionPort notify_port (!mig)", notify_port);
881				// the server we were using is gone and we've lost our send right
882				mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
883				notify_port = MACH_PORT_NULL;
884			} else {
885				// if we got an unexpected error, don't retry
886				sc_status = status;
887				break;
888			}
889		}
890
891		server = __SCNetworkConnectionRefreshServerPort(server, &sc_status);
892		if (server == MACH_PORT_NULL) {
893			// if server not available
894			if (sc_status == BOOTSTRAP_UNKNOWN_SERVICE) {
895				// if first retry attempt, wait for SCDynamicStore server
896				if (retry == 0) {
897					SCDynamicStoreRef	store;
898
899					store = SCDynamicStoreCreate(NULL,
900								     CFSTR("SCNetworkConnection connect"),
901								     NULL,
902								     NULL);
903					if (store != NULL) {
904						CFRelease(store);
905					}
906				}
907
908				// wait up to 2.5 seconds for the [SCNetworkConnection] server
909				// to startup
910				if ((retry += 50) < 2500) {
911					usleep(50 * 1000);	// sleep 50ms between attempts
912					continue;
913				}
914			}
915			break;
916		}
917	}
918
919	if (notify_port != MACH_PORT_NULL) {
920		if (connectionPrivate->session_port != MACH_PORT_NULL) {
921			CFMachPortContext	context	= { 0
922							  , (void *)connectionPrivate
923							  , NULL
924							  , NULL
925							  , pppMPCopyDescription
926			};
927
928			// request a notification when/if the server dies
929			status = mach_port_request_notification(mach_task_self(),
930								notify_port,
931								MACH_NOTIFY_NO_SENDERS,
932								1,
933								notify_port,
934								MACH_MSG_TYPE_MAKE_SEND_ONCE,
935								&oldNotify);
936			if (status != KERN_SUCCESS) {
937				SCLog(TRUE, LOG_ERR, CFSTR("__SCNetworkConnectionSessionPort mach_port_request_notification(): %s"), mach_error_string(status));
938				mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
939				sc_status = status;
940				goto done;
941			}
942
943			if (oldNotify != MACH_PORT_NULL) {
944				SCLog(TRUE, LOG_ERR, CFSTR("__SCNetworkConnectionSessionPort(): oldNotify != MACH_PORT_NULL"));
945			}
946
947			// create CFMachPort for SCNetworkConnection notification callback
948			connectionPrivate->notify_port = _SC_CFMachPortCreateWithPort("SCNetworkConnection",
949										      notify_port,
950										      __SCNetworkConnectionMachCallBack,
951										      &context);
952
953			// we need to try a bit harder to acquire the initial status
954			connectionPrivate->haveStatus = FALSE;
955		} else {
956			// with no server port, release the notification port we allocated
957			__MACH_PORT_DEBUG(TRUE,
958					  "*** __SCNetworkConnectionSessionPort notify_port (!server)",
959					  notify_port);
960			(void) mach_port_mod_refs  (mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
961			(void) mach_port_deallocate(mach_task_self(), notify_port);
962			notify_port = MACH_PORT_NULL;
963		}
964	}
965
966    done :
967
968	// clean up
969
970#ifdef	HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
971	if (au_session != MACH_PORT_NULL) {
972		(void)mach_port_deallocate(mach_task_self(), au_session);
973	}
974#endif	// HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
975
976	if (dataRef != NULL)	CFRelease(dataRef);
977
978	switch (sc_status) {
979		case kSCStatusOK :
980			__MACH_PORT_DEBUG(connectionPrivate->session_port != MACH_PORT_NULL,
981					  "*** __SCNetworkConnectionSessionPort session_port",
982					  connectionPrivate->session_port);
983			__MACH_PORT_DEBUG(notify_port != MACH_PORT_NULL,
984					  "*** __SCNetworkConnectionSessionPort notify_port",
985					  notify_port);
986			break;
987		case BOOTSTRAP_UNKNOWN_SERVICE :
988			SCLog(TRUE,
989			      (status == KERN_SUCCESS) ? LOG_DEBUG : LOG_ERR,
990			      CFSTR("PPPController not available"));
991			break;
992		default :
993			SCLog(TRUE,
994			      (status == KERN_SUCCESS) ? LOG_DEBUG : LOG_ERR,
995			      CFSTR("__SCNetworkConnectionSessionPort pppcontroller_attach(): %s"),
996			      SCErrorString(sc_status));
997			break;
998	}
999
1000	if (sc_status != kSCStatusOK) {
1001		_SCErrorSet(sc_status);
1002	}
1003
1004	return connectionPrivate->session_port;
1005}
1006
1007
1008static Boolean
1009__SCNetworkConnectionReconnect(SCNetworkConnectionRef connection)
1010{
1011	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
1012	mach_port_t			port;
1013
1014	port = __SCNetworkConnectionSessionPort(connectionPrivate);
1015	return (port != MACH_PORT_NULL);
1016}
1017
1018
1019static Boolean
1020__SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection)
1021{
1022	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
1023	dispatch_group_t		dispatchGroup		= NULL;
1024	dispatch_queue_t		dispatchQueue		= NULL;
1025	Boolean				ok			= TRUE;
1026	CFArrayRef			rlList			= NULL;
1027
1028	// Before we fully tearing down our [old] notifications, make sure
1029	// we have retained any information that is needed to re-register the
1030	// [new] notifications.
1031
1032	pthread_mutex_lock(&connectionPrivate->lock);
1033
1034	// save and cancel [old] notifications
1035	if (connectionPrivate->rlList != NULL) {
1036		rlList = connectionPrivate->rlList;
1037		connectionPrivate->rlList = NULL;
1038	}
1039	if (connectionPrivate->rls != NULL) {
1040		CFRunLoopSourceInvalidate(connectionPrivate->rls);
1041		CFRelease(connectionPrivate->rls);
1042		connectionPrivate->rls = NULL;
1043	}
1044	if (connectionPrivate->dispatchSource != NULL) {
1045		dispatch_source_cancel(connectionPrivate->dispatchSource);
1046		connectionPrivate->dispatchSource = NULL;
1047	}
1048
1049	// make sure dispatchSource is cancelled before removing group/queue
1050	if (connectionPrivate->dispatchQueue != NULL) {
1051		// save dispatchQueue, release reference when we've queue'd blocks
1052		// complete, allow re-scheduling
1053		dispatchGroup = connectionPrivate->dispatchGroup;
1054		connectionPrivate->dispatchGroup = NULL;
1055		dispatchQueue = connectionPrivate->dispatchQueue;
1056		connectionPrivate->dispatchQueue = NULL;
1057
1058		// and take an extra reference for rescheduling
1059		dispatch_retain(dispatchQueue);
1060	}
1061
1062	connectionPrivate->scheduled = FALSE;
1063
1064	pthread_mutex_unlock(&connectionPrivate->lock);
1065
1066	if (dispatchGroup != NULL) {
1067		dispatch_group_notify(dispatchGroup, dispatchQueue, ^{
1068			// release group/queue references
1069			dispatch_release(dispatchQueue);
1070			dispatch_release(dispatchGroup);	// releases our connection reference
1071		});
1072	}
1073
1074	// re-schedule
1075	if (rlList != NULL) {
1076		CFIndex	i;
1077		CFIndex	n;
1078
1079		n = CFArrayGetCount(rlList);
1080		for (i = 0; i < n; i += 3) {
1081			CFRunLoopRef	rl	= (CFRunLoopRef)CFArrayGetValueAtIndex(rlList, i+1);
1082			CFStringRef	rlMode	= (CFStringRef) CFArrayGetValueAtIndex(rlList, i+2);
1083
1084			ok = SCNetworkConnectionScheduleWithRunLoop(connection, rl, rlMode);
1085			if (!ok) {
1086				SCLog((SCError() != BOOTSTRAP_UNKNOWN_SERVICE),
1087				      LOG_ERR,
1088				      CFSTR("__SCNetworkConnectionReconnectNotifications: SCNetworkConnectionScheduleWithRunLoop() failed"));
1089				goto done;
1090			}
1091		}
1092	} else if (dispatchQueue != NULL) {
1093		ok = SCNetworkConnectionSetDispatchQueue(connection, dispatchQueue);
1094		if (!ok) {
1095			SCLog((SCError() != BOOTSTRAP_UNKNOWN_SERVICE),
1096			      LOG_ERR,
1097			      CFSTR("__SCNetworkConnectionReconnectNotifications: SCNetworkConnectionSetDispatchQueue() failed"));
1098			goto done;
1099		}
1100	} else {
1101		ok = FALSE;
1102	}
1103
1104    done :
1105
1106	// cleanup
1107	if (rlList != NULL) {
1108		CFRelease(rlList);
1109	}
1110	if (dispatchQueue != NULL) {
1111		dispatch_release(dispatchQueue);
1112	}
1113
1114	if (!ok) {
1115		SCLog(TRUE, LOG_ERR,
1116		      CFSTR("SCNetworkConnection server %s, notification not restored"),
1117		      (SCError() == BOOTSTRAP_UNKNOWN_SERVICE) ? "shutdown" : "failed");
1118	}
1119
1120	return ok;
1121}
1122
1123
1124static Boolean
1125__SCNetworkConnectionNeedsRetry(SCNetworkConnectionRef	connection,
1126				const char		*error_label,
1127				kern_return_t		status,
1128				int			*sc_status)
1129{
1130	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
1131
1132	if (status == KERN_SUCCESS) {
1133		return FALSE;
1134	}
1135
1136	if ((status == MACH_SEND_INVALID_DEST) || (status == MIG_SERVER_DIED)) {
1137		// the server's gone and our session port's dead, remove the dead name right
1138		(void) mach_port_deallocate(mach_task_self(), connectionPrivate->session_port);
1139	} else {
1140		// we got an unexpected error, leave the [session] port alone
1141		SCLog(TRUE, LOG_ERR, CFSTR("%s: %s"), error_label, mach_error_string(status));
1142	}
1143	connectionPrivate->session_port = MACH_PORT_NULL;
1144	if ((status == MACH_SEND_INVALID_DEST) || (status == MIG_SERVER_DIED)) {
1145		if (__SCNetworkConnectionReconnect(connection)) {
1146			return TRUE;
1147		}
1148	}
1149	*sc_status = status;
1150
1151	return FALSE;
1152}
1153
1154
1155CFTypeID
1156SCNetworkConnectionGetTypeID(void) {
1157	pthread_once(&initialized, __SCNetworkConnectionInitialize);	/* initialize runtime */
1158	return __kSCNetworkConnectionTypeID;
1159}
1160
1161
1162CFArrayRef /* of SCNetworkServiceRef's */
1163SCNetworkConnectionCopyAvailableServices(SCNetworkSetRef set)
1164{
1165	CFMutableArrayRef	available;
1166	Boolean			tempSet	= FALSE;
1167
1168	if (set == NULL) {
1169		SCPreferencesRef	prefs;
1170
1171		prefs = SCPreferencesCreate(NULL, CFSTR("SCNetworkConnectionCopyAvailableServices"), NULL);
1172		if (prefs != NULL) {
1173			set = SCNetworkSetCopyCurrent(prefs);
1174			CFRelease(prefs);
1175		}
1176		tempSet = TRUE;
1177	}
1178
1179	available = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1180
1181	if (set != NULL) {
1182		CFArrayRef	services;
1183
1184		services = SCNetworkSetCopyServices(set);
1185		if (services != NULL) {
1186			CFIndex		i;
1187			CFIndex		n;
1188
1189			n = CFArrayGetCount(services);
1190			for (i = 0; i < n; i++) {
1191				SCNetworkInterfaceRef	interface;
1192				CFStringRef		interfaceType;
1193				SCNetworkServiceRef	service;
1194
1195				service       = CFArrayGetValueAtIndex(services, i);
1196				interface     = SCNetworkServiceGetInterface(service);
1197				if (interface == NULL) {
1198					continue;
1199				}
1200
1201				interfaceType = SCNetworkInterfaceGetInterfaceType(interface);
1202				if (CFEqual(interfaceType, kSCNetworkInterfaceTypePPP) ||
1203				    CFEqual(interfaceType, kSCNetworkInterfaceTypeVPN) ||
1204				    CFEqual(interfaceType, kSCNetworkInterfaceTypeIPSec)) {
1205					CFArrayAppendValue(available, service);
1206				}
1207			}
1208
1209			CFRelease(services);
1210		}
1211	}
1212
1213	if (tempSet && (set != NULL)) {
1214		CFRelease(set);
1215	}
1216	return available;
1217}
1218
1219
1220SCNetworkConnectionRef
1221SCNetworkConnectionCreateWithService(CFAllocatorRef			allocator,
1222				     SCNetworkServiceRef		service,
1223				     SCNetworkConnectionCallBack	callout,
1224				     SCNetworkConnectionContext		*context)
1225{
1226	SCNetworkConnectionPrivateRef	connectionPrivate;
1227
1228	if (!isA_SCNetworkService(service)) {
1229		_SCErrorSet(kSCStatusInvalidArgument);
1230		return FALSE;
1231	}
1232
1233	connectionPrivate = __SCNetworkConnectionCreatePrivate(allocator, service, callout, context);
1234	return (SCNetworkConnectionRef)connectionPrivate;
1235}
1236
1237
1238SCNetworkConnectionRef
1239SCNetworkConnectionCreateWithServiceID(CFAllocatorRef			allocator,
1240				       CFStringRef			serviceID,
1241				       SCNetworkConnectionCallBack	callout,
1242				       SCNetworkConnectionContext	*context)
1243{
1244	SCNetworkConnectionRef	connection;
1245	SCNetworkServiceRef	service;
1246
1247	if (!isA_CFString(serviceID)) {
1248		_SCErrorSet(kSCStatusInvalidArgument);
1249		return NULL;
1250	}
1251
1252	service = _SCNetworkServiceCopyActive(NULL, serviceID);
1253	if (service == NULL) {
1254		return NULL;
1255	}
1256
1257	connection = SCNetworkConnectionCreateWithService(allocator, service, callout, context);
1258	CFRelease(service);
1259
1260	return connection;
1261}
1262
1263
1264SCNetworkConnectionRef
1265SCNetworkConnectionCreate(CFAllocatorRef		allocator,
1266			  SCNetworkConnectionCallBack	callout,
1267			  SCNetworkConnectionContext	*context)
1268{
1269	SCNetworkConnectionPrivateRef	connectionPrivate = __SCNetworkConnectionCreatePrivate(allocator, NULL, callout, context);
1270	return (SCNetworkConnectionRef)connectionPrivate;
1271}
1272
1273
1274CFStringRef
1275SCNetworkConnectionCopyServiceID(SCNetworkConnectionRef connection)
1276{
1277	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
1278	CFStringRef			serviceID;
1279
1280	if (!isA_SCNetworkConnection(connection)) {
1281		_SCErrorSet(kSCStatusInvalidArgument);
1282		return NULL;
1283	}
1284
1285	if (connectionPrivate->service == NULL) {
1286		_SCErrorSet(kSCStatusConnectionNoService);
1287		return NULL;
1288	}
1289
1290	serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service);
1291	return CFRetain(serviceID);
1292}
1293
1294
1295Boolean
1296SCNetworkConnectionSetClientInfo(SCNetworkConnectionRef	connection,
1297				 mach_port_t		client_audit_session,
1298				 uid_t			client_uid,
1299				 gid_t			client_gid,
1300				 pid_t			client_pid)
1301{
1302	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
1303
1304	if (!isA_SCNetworkConnection(connection)) {
1305		_SCErrorSet(kSCStatusInvalidArgument);
1306		return FALSE;
1307	}
1308
1309	// save client audit session port
1310	if (connectionPrivate->client_audit_session != MACH_PORT_NULL) {
1311		mach_port_mod_refs(mach_task_self(),
1312				   connectionPrivate->client_audit_session,
1313				   MACH_PORT_RIGHT_SEND,
1314				   -1);
1315		connectionPrivate->client_audit_session = MACH_PORT_NULL;
1316	}
1317	connectionPrivate->client_audit_session = client_audit_session;
1318	if (connectionPrivate->client_audit_session != MACH_PORT_NULL) {
1319		mach_port_mod_refs(mach_task_self(),
1320				   connectionPrivate->client_audit_session,
1321				   MACH_PORT_RIGHT_SEND,
1322				   1);
1323	}
1324
1325	// save client UID, GID, and PID
1326	connectionPrivate->client_uid = client_uid;
1327	connectionPrivate->client_gid = client_gid;
1328	connectionPrivate->client_pid = client_pid;
1329
1330	return TRUE;
1331}
1332
1333
1334Boolean
1335SCNetworkConnectionSetClientAuditInfo(SCNetworkConnectionRef	connection,
1336				      audit_token_t		client_audit_token,
1337				      mach_port_t		audit_session,
1338				      mach_port_t		bootstrap_port,
1339				      pid_t			client_pid,
1340				      const uuid_t		uuid,
1341				      const char		*bundle_id)
1342{
1343	const audit_token_t		null_audit		= KERNEL_AUDIT_TOKEN_VALUE;
1344	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
1345	gid_t				gid			= 0;
1346	pid_t				pid			= 0;
1347	uid_t				uid			= 0;
1348
1349	if (memcmp(&client_audit_token, &null_audit, sizeof(client_audit_token))) {
1350#if	TARGET_OS_IPHONE
1351		audit_token_to_au32(client_audit_token, NULL, &uid, &gid, NULL, NULL, &pid, NULL, NULL);
1352#else	// TARGET_OS_IPHONE
1353		uid = audit_token_to_euid(client_audit_token);
1354		gid = audit_token_to_egid(client_audit_token);
1355		pid = audit_token_to_pid(client_audit_token);
1356#endif	// TARGET_OS_IPHONE
1357	} else {
1358		pid = client_pid;
1359	}
1360
1361	if (!SCNetworkConnectionSetClientInfo(connection, audit_session, uid, gid, pid)) {
1362		return FALSE;
1363	}
1364
1365	if (connectionPrivate->client_bootstrap_port != MACH_PORT_NULL) {
1366		mach_port_mod_refs(mach_task_self(),
1367				   connectionPrivate->client_bootstrap_port,
1368				   MACH_PORT_RIGHT_SEND,
1369				   -1);
1370		connectionPrivate->client_bootstrap_port = MACH_PORT_NULL;
1371	}
1372
1373	connectionPrivate->client_bootstrap_port = bootstrap_port;
1374	if (connectionPrivate->client_bootstrap_port != MACH_PORT_NULL) {
1375		mach_port_mod_refs(mach_task_self(),
1376				   connectionPrivate->client_bootstrap_port,
1377				   MACH_PORT_RIGHT_SEND,
1378				   1);
1379	}
1380
1381	memcpy(&connectionPrivate->client_audit_token, &client_audit_token, sizeof(connectionPrivate->client_audit_token));
1382
1383	if (uuid != NULL && !uuid_is_null(uuid)) {
1384		uuid_copy(connectionPrivate->client_uuid, uuid);
1385	}
1386
1387	if (connectionPrivate->client_bundle_id != NULL) {
1388		CFRelease(connectionPrivate->client_bundle_id);
1389		connectionPrivate->client_bundle_id = NULL;
1390	}
1391
1392	if (bundle_id != NULL) {
1393		connectionPrivate->client_bundle_id = CFStringCreateWithCString(kCFAllocatorDefault, bundle_id, kCFStringEncodingUTF8);
1394	}
1395
1396	return TRUE;
1397}
1398
1399
1400CFDictionaryRef
1401SCNetworkConnectionCopyStatistics(SCNetworkConnectionRef connection)
1402{
1403	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
1404	xmlDataOut_t			data			= NULL;
1405	mach_msg_type_number_t		datalen			= 0;
1406	int				sc_status		= kSCStatusFailed;
1407	mach_port_t			session_port;
1408	CFPropertyListRef		statistics		= NULL;
1409	kern_return_t			status;
1410
1411	if (!isA_SCNetworkConnection(connection)) {
1412		_SCErrorSet(kSCStatusInvalidArgument);
1413		return NULL;
1414	}
1415
1416	pthread_mutex_lock(&connectionPrivate->lock);
1417
1418#if !TARGET_IPHONE_SIMULATOR
1419	if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
1420		__block xpc_object_t xstats = NULL;
1421		ne_session_t ne_session = connectionPrivate->ne_session;
1422
1423		ne_session_retain(ne_session);
1424		pthread_mutex_unlock(&connectionPrivate->lock);
1425
1426		dispatch_semaphore_t ne_sema = dispatch_semaphore_create(0);
1427		ne_session_get_info(ne_session, NESessionInfoTypeStatistics, __SCNetworkConnectionQueue(), ^(xpc_object_t result) {
1428			if (result != NULL) {
1429				xstats = xpc_retain(result);
1430			}
1431			ne_session_release(ne_session);
1432			dispatch_semaphore_signal(ne_sema);
1433		});
1434		dispatch_semaphore_wait(ne_sema, DISPATCH_TIME_FOREVER);
1435		dispatch_release(ne_sema);
1436
1437		if (xstats != NULL) {
1438			statistics = _CFXPCCreateCFObjectFromXPCObject(xstats);
1439			xpc_release(xstats);
1440		} else {
1441			_SCErrorSet(kSCStatusFailed);
1442		}
1443
1444		return statistics;
1445	}
1446#endif /* !TARGET_IPHONE_SIMULATOR */
1447
1448    retry :
1449
1450	session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1451	if (session_port == MACH_PORT_NULL) {
1452		goto done;
1453	}
1454
1455	status = pppcontroller_copystatistics(session_port, &data, &datalen, &sc_status);
1456	if (__SCNetworkConnectionNeedsRetry(connection,
1457					    "SCNetworkConnectionCopyStatistics()",
1458					    status,
1459					    &sc_status)) {
1460		goto retry;
1461	}
1462
1463	if (data != NULL) {
1464		if (!_SCUnserialize(&statistics, NULL, data, datalen)) {
1465			if (sc_status != kSCStatusOK) sc_status = SCError();
1466		}
1467		if ((sc_status == kSCStatusOK) && !isA_CFDictionary(statistics)) {
1468			sc_status = kSCStatusFailed;
1469		}
1470	}
1471
1472	if (sc_status != kSCStatusOK) {
1473		if (statistics != NULL)	{
1474			CFRelease(statistics);
1475			statistics = NULL;
1476		}
1477		_SCErrorSet(sc_status);
1478	}
1479
1480    done :
1481
1482	pthread_mutex_unlock(&connectionPrivate->lock);
1483	return statistics;
1484}
1485
1486
1487SCNetworkServiceRef
1488SCNetworkConnectionGetService(SCNetworkConnectionRef connection)
1489{
1490	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
1491
1492	if (!isA_SCNetworkConnection(connection)) {
1493		_SCErrorSet(kSCStatusInvalidArgument);
1494		return NULL;
1495	}
1496
1497	return connectionPrivate->service;
1498}
1499
1500
1501SCNetworkConnectionStatus
1502SCNetworkConnectionGetStatus(SCNetworkConnectionRef connection)
1503{
1504	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
1505	SCNetworkConnectionStatus	nc_status		= kSCNetworkConnectionInvalid;
1506	int				retry			= 0;
1507	int				sc_status		= kSCStatusFailed;
1508	mach_port_t			session_port;
1509	kern_return_t			status;
1510	CFStringRef			serviceID;
1511
1512	if (!isA_SCNetworkConnection(connection)) {
1513		_SCErrorSet(kSCStatusInvalidArgument);
1514		return kSCNetworkConnectionInvalid;
1515	}
1516
1517	if (connectionPrivate->service == NULL) {
1518		_SCErrorSet(kSCStatusConnectionNoService);
1519		return kSCNetworkConnectionInvalid;
1520	}
1521
1522	// skip retry and return immediately if we know no service is to be found.
1523	serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service);
1524	if (CFStringGetLength(serviceID) == 0) {
1525		_SCErrorSet(kSCStatusConnectionNoService);
1526		return kSCNetworkConnectionInvalid;
1527	}
1528
1529	pthread_mutex_lock(&connectionPrivate->lock);
1530
1531#if !TARGET_IPHONE_SIMULATOR
1532	if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
1533		__block ne_session_status_t ne_status;
1534		ne_session_t ne_session = connectionPrivate->ne_session;
1535
1536		ne_session_retain(ne_session);
1537		pthread_mutex_unlock(&connectionPrivate->lock);
1538
1539		dispatch_semaphore_t ne_sema = dispatch_semaphore_create(0);
1540		ne_session_get_status(ne_session, __SCNetworkConnectionQueue(), ^(ne_session_status_t status) {
1541			ne_status = status;
1542			ne_session_release(ne_session);
1543			dispatch_semaphore_signal(ne_sema);
1544		});
1545		dispatch_semaphore_wait(ne_sema, DISPATCH_TIME_FOREVER);
1546		dispatch_release(ne_sema);
1547
1548		return SCNetworkConnectionGetStatusFromNEStatus(ne_status);
1549	}
1550#endif /* !TARGET_IPHONE_SIMULATOR */
1551
1552    retry :
1553
1554	session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1555	if (session_port == MACH_PORT_NULL) {
1556		nc_status = kSCNetworkConnectionInvalid;
1557		goto done;
1558	}
1559
1560	status = pppcontroller_getstatus(session_port, &nc_status, &sc_status);
1561	if (__SCNetworkConnectionNeedsRetry(connection,
1562					    "SCNetworkConnectionGetStatus()",
1563					    status,
1564					    &sc_status)) {
1565		goto retry;
1566	}
1567
1568	// wait up to 250 ms for the network service to become available
1569	if (!connectionPrivate->haveStatus &&
1570	    (sc_status == kSCStatusConnectionNoService) &&
1571	    ((retry += 10) < 250)) {
1572		usleep(10 * 1000);	// sleep 10ms between attempts
1573		goto retry;
1574	}
1575
1576	if (sc_status == kSCStatusOK) {
1577		connectionPrivate->haveStatus = TRUE;
1578	} else {
1579		_SCErrorSet(sc_status);
1580		nc_status = kSCNetworkConnectionInvalid;
1581	}
1582
1583    done :
1584
1585	pthread_mutex_unlock(&connectionPrivate->lock);
1586	return nc_status;
1587}
1588
1589
1590CFDictionaryRef
1591SCNetworkConnectionCopyExtendedStatus(SCNetworkConnectionRef connection)
1592{
1593	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
1594	xmlDataOut_t			data			= NULL;
1595	mach_msg_type_number_t		datalen			= 0;
1596	CFPropertyListRef		extstatus		= NULL;
1597	int				retry			= 0;
1598	int				sc_status		= kSCStatusFailed;
1599	mach_port_t			session_port;
1600	kern_return_t			status;
1601	CFStringRef			serviceID;
1602
1603	if (!isA_SCNetworkConnection(connection)) {
1604		_SCErrorSet(kSCStatusInvalidArgument);
1605		return NULL;
1606	}
1607
1608	if (connectionPrivate->service == NULL) {
1609		_SCErrorSet(kSCStatusConnectionNoService);
1610		return NULL;
1611	}
1612
1613	// skip retry and return immediately if we know no service is to be found.
1614	serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service);
1615	if (CFStringGetLength(serviceID) == 0) {
1616		_SCErrorSet(kSCStatusConnectionNoService);
1617		return NULL;
1618	}
1619
1620	pthread_mutex_lock(&connectionPrivate->lock);
1621
1622#if !TARGET_IPHONE_SIMULATOR
1623	if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
1624		__block CFDictionaryRef statusDictionary = NULL;
1625		ne_session_t ne_session = connectionPrivate->ne_session;
1626
1627		ne_session_retain(ne_session);
1628		pthread_mutex_unlock(&connectionPrivate->lock);
1629
1630		dispatch_semaphore_t ne_sema = dispatch_semaphore_create(0);
1631		ne_session_get_info(ne_session, NESessionInfoTypeExtendedStatus, __SCNetworkConnectionQueue(), ^(xpc_object_t extended_status) {
1632			if (extended_status != NULL) {
1633				statusDictionary = _CFXPCCreateCFObjectFromXPCObject(extended_status);
1634				ne_session_release(ne_session);
1635				dispatch_semaphore_signal(ne_sema);
1636			} else {
1637				ne_session_get_status(ne_session, __SCNetworkConnectionQueue(), ^(ne_session_status_t ne_status) {
1638					SCNetworkConnectionStatus status = SCNetworkConnectionGetStatusFromNEStatus(ne_status);
1639					if (status != kSCNetworkConnectionInvalid) {
1640						CFStringRef keys[1] = { kSCNetworkConnectionStatus };
1641						CFNumberRef values[1] = { NULL };
1642						values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &status);
1643						statusDictionary = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void **)values, sizeof(values) / sizeof(values[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1644						CFRelease(values[0]);
1645					}
1646					ne_session_release(ne_session);
1647					dispatch_semaphore_signal(ne_sema);
1648				});
1649			}
1650		});
1651		dispatch_semaphore_wait(ne_sema, DISPATCH_TIME_FOREVER);
1652		dispatch_release(ne_sema);
1653
1654		if (statusDictionary != NULL) {
1655			extstatus = (CFPropertyListRef)statusDictionary;
1656		} else {
1657			_SCErrorSet(kSCStatusFailed);
1658		}
1659
1660		return extstatus;
1661	}
1662#endif
1663
1664    retry :
1665
1666	session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1667	if (session_port == MACH_PORT_NULL) {
1668		goto done;
1669	}
1670
1671	status = pppcontroller_copyextendedstatus(session_port, &data, &datalen, &sc_status);
1672	if (__SCNetworkConnectionNeedsRetry(connection,
1673					    "SCNetworkConnectionCopyExtendedStatus()",
1674					    status,
1675					    &sc_status)) {
1676		goto retry;
1677	}
1678
1679	if (data != NULL) {
1680		if (!_SCUnserialize(&extstatus, NULL, data, datalen)) {
1681			if (sc_status != kSCStatusOK) sc_status = SCError();
1682		}
1683		if ((sc_status == kSCStatusOK) && !isA_CFDictionary(extstatus)) {
1684			sc_status = kSCStatusFailed;
1685		}
1686	}
1687
1688	// wait up to 250 ms for the network service to become available
1689	if (!connectionPrivate->haveStatus &&
1690	    (sc_status == kSCStatusConnectionNoService) &&
1691	    ((retry += 10) < 250)) {
1692		usleep(10 * 1000);	// sleep 10ms between attempts
1693		goto retry;
1694	}
1695
1696	if (sc_status == kSCStatusOK) {
1697		connectionPrivate->haveStatus = TRUE;
1698	} else {
1699		if (extstatus != NULL)	{
1700			CFRelease(extstatus);
1701			extstatus = NULL;
1702		}
1703		_SCErrorSet(sc_status);
1704	}
1705
1706    done :
1707
1708	pthread_mutex_unlock(&connectionPrivate->lock);
1709	return extstatus;
1710}
1711
1712
1713static void
1714_SCNetworkConnectionMergeDictionaries (const void *key, const void *value, void *context)
1715{
1716	/* Add value only if not present */
1717	CFDictionaryAddValue((CFMutableDictionaryRef)context, (CFStringRef)key, (CFTypeRef)value);
1718}
1719
1720
1721Boolean
1722SCNetworkConnectionStart(SCNetworkConnectionRef	connection,
1723			 CFDictionaryRef	userOptions,
1724			 Boolean		linger)
1725{
1726	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
1727	CFDataRef			dataref			= NULL;
1728	void				*data			= NULL;
1729	CFIndex				datalen			= 0;
1730	Boolean				ok			= FALSE;
1731	int				sc_status		= kSCStatusFailed;
1732	mach_port_t			session_port;
1733	kern_return_t			status;
1734
1735	if (!isA_SCNetworkConnection(connection)) {
1736		_SCErrorSet(kSCStatusInvalidArgument);
1737		return FALSE;
1738	}
1739
1740	if ((userOptions != NULL) && !isA_CFDictionary(userOptions)) {
1741		_SCErrorSet(kSCStatusInvalidArgument);
1742		return FALSE;
1743	}
1744
1745	if (userOptions == NULL) {
1746		userOptions = connectionPrivate->on_demand_user_options;
1747	} else if (connectionPrivate->on_demand_user_options != NULL) {
1748		CFDictionaryRef	localUserOptions	= NULL;
1749
1750		localUserOptions = CFDictionaryCreateMutableCopy(NULL, 0, userOptions);
1751		if (localUserOptions) {
1752			CFDictionaryApplyFunction(connectionPrivate->on_demand_user_options,
1753						  _SCNetworkConnectionMergeDictionaries,
1754						  (void *)localUserOptions);
1755			CFRelease(connectionPrivate->on_demand_user_options);
1756			userOptions = connectionPrivate->on_demand_user_options = localUserOptions;
1757		}
1758	}
1759
1760	if (debug > 0) {
1761		CFMutableDictionaryRef	mdict = NULL;
1762
1763		SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionStart (%p)"), connectionPrivate);
1764
1765		if (userOptions != NULL) {
1766			CFDictionaryRef		dict;
1767			CFStringRef		encryption;
1768			CFMutableDictionaryRef	new_dict;
1769
1770			/* special code to remove secret information */
1771			mdict = CFDictionaryCreateMutableCopy(NULL, 0, userOptions);
1772
1773			dict = CFDictionaryGetValue(mdict, kSCEntNetPPP);
1774			if (isA_CFDictionary(dict)) {
1775				encryption = CFDictionaryGetValue(dict, kSCPropNetPPPAuthPasswordEncryption);
1776				if (!isA_CFString(encryption) ||
1777				    !CFEqual(encryption, kSCValNetPPPAuthPasswordEncryptionKeychain)) {
1778					new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
1779					CFDictionaryReplaceValue(new_dict, kSCPropNetPPPAuthPassword, CFSTR("******"));
1780					CFDictionarySetValue(mdict, kSCEntNetPPP, new_dict);
1781					CFRelease(new_dict);
1782				}
1783			}
1784
1785			dict = CFDictionaryGetValue(mdict, kSCEntNetL2TP);
1786			if (isA_CFDictionary(dict)) {
1787				encryption = CFDictionaryGetValue(dict, kSCPropNetL2TPIPSecSharedSecretEncryption);
1788				if (!isA_CFString(encryption) ||
1789				    !CFEqual(encryption, kSCValNetL2TPIPSecSharedSecretEncryptionKeychain)) {
1790					new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
1791					CFDictionaryReplaceValue(new_dict, kSCPropNetL2TPIPSecSharedSecret, CFSTR("******"));
1792					CFDictionarySetValue(mdict, kSCEntNetL2TP, new_dict);
1793					CFRelease(new_dict);
1794				}
1795			}
1796
1797			dict = CFDictionaryGetValue(mdict, kSCEntNetIPSec);
1798			if (isA_CFDictionary(dict)) {
1799				encryption = CFDictionaryGetValue(dict, kSCPropNetIPSecSharedSecretEncryption);
1800				if (!isA_CFString(encryption) ||
1801				    !CFEqual(encryption, kSCValNetIPSecSharedSecretEncryptionKeychain)) {
1802					new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
1803					CFDictionaryReplaceValue(new_dict, kSCPropNetIPSecSharedSecret, CFSTR("******"));
1804					CFDictionarySetValue(mdict, kSCEntNetIPSec, new_dict);
1805					CFRelease(new_dict);
1806				}
1807			}
1808		}
1809
1810		SCLog(TRUE, LOG_DEBUG, CFSTR("User options: %@"), mdict);
1811		if (mdict != NULL) CFRelease(mdict);
1812	}
1813
1814	pthread_mutex_lock(&connectionPrivate->lock);
1815
1816	/* Clear out any cached flow divert token parameters */
1817	if (connectionPrivate->flow_divert_token_params != NULL) {
1818	    CFRelease(connectionPrivate->flow_divert_token_params);
1819	    connectionPrivate->flow_divert_token_params = NULL;
1820	}
1821
1822#if !TARGET_IPHONE_SIMULATOR
1823	if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
1824		xpc_object_t xuser_options = NULL;
1825
1826		if (userOptions != NULL) {
1827			xuser_options = _CFXPCCreateXPCObjectFromCFObject(userOptions);
1828		}
1829
1830		if (connectionPrivate->client_bootstrap_port != MACH_PORT_NULL) {
1831#if NE_SESSION_VERSION > 2
1832			ne_session_start_on_behalf_of(connectionPrivate->ne_session,
1833						      xuser_options,
1834						      connectionPrivate->client_bootstrap_port,
1835						      connectionPrivate->client_audit_session,
1836						      connectionPrivate->client_uid,
1837						      connectionPrivate->client_gid,
1838						      connectionPrivate->client_pid);
1839#else
1840			ne_session_start_on_behalf_of(connectionPrivate->ne_session,
1841						      xuser_options,
1842						      connectionPrivate->client_bootstrap_port,
1843						      connectionPrivate->client_audit_session,
1844						      connectionPrivate->client_uid,
1845						      connectionPrivate->client_gid);
1846#endif
1847		} else {
1848			ne_session_start_with_options(connectionPrivate->ne_session, xuser_options);
1849		}
1850
1851		if (xuser_options != NULL) {
1852			xpc_release(xuser_options);
1853		}
1854
1855		ok = TRUE;
1856		goto done;
1857	}
1858#endif /* !TARGET_IPHONE_SIMULATOR */
1859
1860	if (userOptions && !_SCSerialize(userOptions, &dataref, &data, &datalen)) {
1861		return FALSE;
1862	}
1863
1864    retry :
1865
1866	session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1867	if (session_port == MACH_PORT_NULL) {
1868		if (dataref)	CFRelease(dataref);
1869		goto done;
1870	}
1871
1872	status = pppcontroller_start(session_port,
1873				     data,
1874				     (mach_msg_type_number_t)datalen,
1875				     linger,
1876				     &sc_status);
1877	if (__SCNetworkConnectionNeedsRetry(connection,
1878					    "SCNetworkConnectionStart()",
1879					    status,
1880					    &sc_status)) {
1881		goto retry;
1882	}
1883
1884	if (dataref)	CFRelease(dataref);
1885
1886	if (debug > 0) {
1887		SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionStart (%p), return: %d"), connectionPrivate, sc_status);
1888	}
1889
1890	if (sc_status != kSCStatusOK) {
1891		_SCErrorSet(sc_status);
1892		goto done;
1893	}
1894
1895	/* connection is now started */
1896	ok = TRUE;
1897
1898    done:
1899	pthread_mutex_unlock(&connectionPrivate->lock);
1900	return ok;
1901}
1902
1903
1904Boolean
1905SCNetworkConnectionStop(SCNetworkConnectionRef	connection,
1906			Boolean			forceDisconnect)
1907{
1908	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
1909	Boolean				ok			= FALSE;
1910	int				sc_status		= kSCStatusFailed;
1911	mach_port_t			session_port;
1912	kern_return_t			status;
1913
1914	if (!isA_SCNetworkConnection(connection)) {
1915		_SCErrorSet(kSCStatusInvalidArgument);
1916		return FALSE;
1917	}
1918
1919	if (debug > 0) {
1920		SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionStop (%p)"), connectionPrivate);
1921	}
1922
1923	pthread_mutex_lock(&connectionPrivate->lock);
1924
1925#if !TARGET_IPHONE_SIMULATOR
1926	if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
1927		ne_session_stop(connectionPrivate->ne_session);
1928		ok = TRUE;
1929		goto done;
1930	}
1931#endif /* !TARGET_IPHONE_SIMULATOR */
1932
1933    retry :
1934
1935	session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1936	if (session_port == MACH_PORT_NULL) {
1937		goto done;
1938	}
1939
1940	status = pppcontroller_stop(session_port, forceDisconnect, &sc_status);
1941	if (__SCNetworkConnectionNeedsRetry(connection,
1942					    "SCNetworkConnectionStop()",
1943					    status,
1944					    &sc_status)) {
1945		goto retry;
1946	}
1947
1948	if (debug > 0) {
1949		SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionStop (%p), return: %d"), connectionPrivate, sc_status);
1950	}
1951
1952	if (sc_status != kSCStatusOK) {
1953		_SCErrorSet(sc_status);
1954		goto done;
1955	}
1956
1957	/* connection is now disconnecting */
1958	ok = TRUE;
1959
1960    done :
1961
1962	pthread_mutex_unlock(&connectionPrivate->lock);
1963	return ok;
1964}
1965
1966
1967Boolean
1968SCNetworkConnectionSuspend(SCNetworkConnectionRef connection)
1969{
1970	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
1971	Boolean				ok			= FALSE;
1972	int				sc_status		= kSCStatusFailed;
1973	mach_port_t			session_port;
1974	kern_return_t			status;
1975
1976	if (!isA_SCNetworkConnection(connection)) {
1977		_SCErrorSet(kSCStatusInvalidArgument);
1978		return FALSE;
1979	}
1980
1981	if (debug > 0) {
1982		SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionSuspend (%p)"), connectionPrivate);
1983	}
1984
1985	pthread_mutex_lock(&connectionPrivate->lock);
1986
1987#if !!TARGET_IPHONE_SIMULATOR
1988	if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
1989		/* Suspend only applies to PPPSerial and PPPoE */
1990		ok = TRUE;
1991		goto done;
1992	}
1993#endif /* !TARGET_IPHONE_SIMULATOR */
1994
1995    retry :
1996
1997	session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1998	if (session_port == MACH_PORT_NULL) {
1999		goto done;
2000	}
2001
2002	status = pppcontroller_suspend(session_port, &sc_status);
2003	if (__SCNetworkConnectionNeedsRetry(connection,
2004					    "SCNetworkConnectionSuspend()",
2005					    status,
2006					    &sc_status)) {
2007		goto retry;
2008	}
2009
2010	if (debug > 0) {
2011		SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionSuspend (%p), return: %d"), connectionPrivate, sc_status);
2012	}
2013
2014	if (sc_status != kSCStatusOK) {
2015		_SCErrorSet(sc_status);
2016		goto done;
2017	}
2018
2019	/* connection is now suspended */
2020	ok = TRUE;
2021
2022    done :
2023
2024	pthread_mutex_unlock(&connectionPrivate->lock);
2025	return ok;
2026}
2027
2028
2029Boolean
2030SCNetworkConnectionResume(SCNetworkConnectionRef connection)
2031{
2032	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
2033	Boolean				ok			= FALSE;
2034	int				sc_status		= kSCStatusFailed;
2035	mach_port_t			session_port;
2036	kern_return_t			status;
2037
2038	if (!isA_SCNetworkConnection(connection)) {
2039		_SCErrorSet(kSCStatusInvalidArgument);
2040		return FALSE;
2041	}
2042
2043	if (debug > 0) {
2044		SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionResume (%p)"), connectionPrivate);
2045	}
2046
2047	pthread_mutex_lock(&connectionPrivate->lock);
2048
2049#if !TARGET_IPHONE_SIMULATOR
2050	if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
2051		/* Resume only applies to PPPSerial and PPPoE */
2052		ok = TRUE;
2053		goto done;
2054	}
2055#endif /* !TARGET_IPHONE_SIMULATOR */
2056
2057    retry :
2058
2059	session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
2060	if (session_port == MACH_PORT_NULL) {
2061		goto done;
2062	}
2063
2064	status = pppcontroller_resume(session_port, &sc_status);
2065	if (__SCNetworkConnectionNeedsRetry(connection,
2066					    "SCNetworkConnectionResume()",
2067					    status,
2068					    &sc_status)) {
2069		goto retry;
2070	}
2071
2072	if (debug > 0) {
2073		SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionResume (%p), return: %d"), connectionPrivate, sc_status);
2074	}
2075
2076	if (sc_status != kSCStatusOK) {
2077		_SCErrorSet(sc_status);
2078		goto done;
2079	}
2080
2081	/* connection is now resume */
2082	ok = TRUE;
2083
2084    done :
2085
2086	pthread_mutex_unlock(&connectionPrivate->lock);
2087	return ok;
2088}
2089
2090
2091#if	!TARGET_IPHONE_SIMULATOR
2092Boolean
2093SCNetworkConnectionRefreshOnDemandState(SCNetworkConnectionRef connection)
2094{
2095	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
2096	Boolean				ok			= FALSE;
2097	uint32_t			retry			= 0;
2098	int				sc_status		= kSCStatusFailed;
2099	mach_port_t			server_port		= __SCNetworkConnectionGetCurrentServerPort();
2100	kern_return_t			status			= KERN_SUCCESS;
2101
2102	if (!isA_SCNetworkConnection(connection)) {
2103		_SCErrorSet(kSCStatusInvalidArgument);
2104		return FALSE;
2105	}
2106
2107	if (debug > 0) {
2108		SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionRefreshOnDemandState (%p)"), connectionPrivate);
2109	}
2110
2111	pthread_mutex_lock(&connectionPrivate->lock);
2112
2113	while (TRUE) {
2114		if (server_port == MACH_PORT_NULL) {
2115			server_port = __SCNetworkConnectionRefreshServerPort(server_port, &sc_status);
2116			if (server_port == MACH_PORT_NULL) {
2117				// if server not available
2118				if (sc_status == BOOTSTRAP_UNKNOWN_SERVICE) {
2119					// wait up to 2.5 seconds for the [SCNetworkConnection] server
2120					// to startup
2121					if ((retry += 50) < 2500) {
2122						usleep(50 * 1000);	// sleep 50ms between attempts
2123						continue;
2124					}
2125				}
2126				break;
2127			}
2128		}
2129
2130		status = pppcontroller_ondemand_refresh_state(server_port, &sc_status);
2131		if (status == KERN_SUCCESS)
2132			break;
2133
2134		if (status == MACH_SEND_INVALID_DEST) {
2135			// the server is not yet available
2136			SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkConnectionRefreshOnDemandState (!dest) (%p)"), connectionPrivate);
2137		} else if (status == MIG_SERVER_DIED) {
2138			// the server we were using is gone
2139			SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkConnectionRefreshOnDemandState (!mig) (%p)"), connectionPrivate);
2140		} else {
2141			// if we got an unexpected error, don't retry
2142			sc_status = status;
2143			break;
2144		}
2145	}
2146
2147	if (debug > 0) {
2148		SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionRefreshOnDemandState (%p), return: %d/%d"), connectionPrivate, status, sc_status);
2149	}
2150
2151	if (sc_status != kSCStatusOK) {
2152		_SCErrorSet(sc_status);
2153		goto done;
2154	}
2155
2156	ok = TRUE;
2157
2158    done :
2159
2160	pthread_mutex_unlock(&connectionPrivate->lock);
2161	return ok;
2162}
2163#endif	/* !TARGET_IPHONE_SIMULATOR */
2164
2165
2166CFDictionaryRef
2167SCNetworkConnectionCopyUserOptions(SCNetworkConnectionRef connection)
2168{
2169	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
2170	xmlDataOut_t			data			= NULL;
2171	mach_msg_type_number_t		datalen			= 0;
2172	int				sc_status		= kSCStatusFailed;
2173	mach_port_t			session_port;
2174	kern_return_t			status;
2175	CFPropertyListRef 		userOptions		= NULL;
2176
2177	if (!isA_SCNetworkConnection(connection)) {
2178		_SCErrorSet(kSCStatusInvalidArgument);
2179		return NULL;
2180	}
2181
2182	pthread_mutex_lock(&connectionPrivate->lock);
2183
2184#if !TARGET_IPHONE_SIMULATOR
2185	if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
2186		__block xpc_object_t config = NULL;
2187		ne_session_t ne_session = connectionPrivate->ne_session;
2188
2189		ne_session_retain(ne_session);
2190		pthread_mutex_unlock(&connectionPrivate->lock);
2191
2192		dispatch_semaphore_t ne_sema = dispatch_semaphore_create(0);
2193		ne_session_get_info(ne_session, NESessionInfoTypeConfiguration, __SCNetworkConnectionQueue(), ^(xpc_object_t result) {
2194			if (result != NULL) {
2195				config = xpc_retain(result);
2196			}
2197			ne_session_release(ne_session);
2198			dispatch_semaphore_signal(ne_sema);
2199		});
2200		dispatch_semaphore_wait(ne_sema, DISPATCH_TIME_FOREVER);
2201		dispatch_release(ne_sema);
2202
2203		if (config != NULL) {
2204			xpc_object_t xoptions = xpc_dictionary_get_value(config, NESMSessionLegacyUserConfigurationKey);
2205			if (xoptions != NULL) {
2206				userOptions = _CFXPCCreateCFObjectFromXPCObject(xoptions);
2207			}
2208			xpc_release(config);
2209		}
2210		return userOptions;
2211	}
2212#endif /* !TARGET_IPHONE_SIMULATOR */
2213
2214    retry :
2215
2216	session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
2217	if (session_port == MACH_PORT_NULL) {
2218		goto done;
2219	}
2220
2221	status = pppcontroller_copyuseroptions(session_port, &data, &datalen, &sc_status);
2222	if (__SCNetworkConnectionNeedsRetry(connection,
2223					    "SCNetworkConnectionCopyUserOptions()",
2224					    status,
2225					    &sc_status)) {
2226		goto retry;
2227	}
2228
2229	if (data != NULL) {
2230		if (!_SCUnserialize(&userOptions, NULL, data, datalen)) {
2231			if (sc_status != kSCStatusOK) sc_status = SCError();
2232		}
2233		if ((sc_status == kSCStatusOK) && (userOptions != NULL) && !isA_CFDictionary(userOptions)) {
2234			sc_status = kSCStatusFailed;
2235		}
2236	}
2237
2238	if (sc_status == kSCStatusOK) {
2239		if (userOptions == NULL) {
2240			// if no user options, return an empty dictionary
2241			userOptions = CFDictionaryCreate(NULL,
2242							 NULL,
2243							 NULL,
2244							 0,
2245							 &kCFTypeDictionaryKeyCallBacks,
2246							 &kCFTypeDictionaryValueCallBacks);
2247		}
2248	} else {
2249		if (userOptions) {
2250			CFRelease(userOptions);
2251			userOptions = NULL;
2252		}
2253		_SCErrorSet(sc_status);
2254	}
2255
2256    done :
2257
2258	pthread_mutex_unlock(&connectionPrivate->lock);
2259	return userOptions;
2260}
2261
2262
2263static Boolean
2264__SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef	connection,
2265					 CFRunLoopRef		runLoop,
2266					 CFStringRef		runLoopMode,
2267					 dispatch_queue_t	queue)
2268{
2269	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
2270	Boolean				ok			= FALSE;
2271	int				sc_status		= kSCStatusFailed;
2272	mach_port_t			session_port;
2273	kern_return_t			status;
2274
2275	pthread_mutex_lock(&connectionPrivate->lock);
2276
2277	if (connectionPrivate->rlsFunction == NULL) {
2278		_SCErrorSet(kSCStatusInvalidArgument);
2279		goto done;
2280	}
2281
2282	if ((connectionPrivate->dispatchQueue != NULL) ||		// if we are already scheduled on a dispatch queue
2283	    ((queue != NULL) && connectionPrivate->scheduled)) {	// if we are already scheduled on a CFRunLoop
2284		_SCErrorSet(kSCStatusInvalidArgument);
2285		goto done;
2286	}
2287
2288	if (!connectionPrivate->scheduled) {
2289
2290	    retry :
2291
2292		if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
2293			session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
2294			if (session_port == MACH_PORT_NULL) {
2295				goto done;
2296			}
2297
2298			status = pppcontroller_notification(session_port, 1, &sc_status);
2299			if (__SCNetworkConnectionNeedsRetry(connection,
2300							    "__SCNetworkConnectionScheduleWithRunLoop()",
2301							    status,
2302							    &sc_status)) {
2303				goto retry;
2304			}
2305
2306			if (sc_status != kSCStatusOK) {
2307				_SCErrorSet(sc_status);
2308				goto done;
2309			}
2310
2311			if (runLoop != NULL) {
2312				connectionPrivate->rls = CFMachPortCreateRunLoopSource(NULL, connectionPrivate->notify_port, 0);
2313				connectionPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2314			}
2315		} else if (runLoop != NULL) {
2316			CFRunLoopSourceContext rlsContext = {
2317			    0,					// version
2318			    (void *)connection,			// info
2319			    NULL,				// retain
2320			    NULL,				// release
2321			    NULL,				// copy description
2322			    NULL,				// equal
2323			    NULL,				// hash
2324			    NULL,				// schedule
2325			    NULL,				// cancel
2326			    __SCNetworkConnectionCallBack,	// perform
2327			};
2328
2329			connectionPrivate->rls = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &rlsContext);
2330			connectionPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2331		}
2332
2333		connectionPrivate->scheduled = TRUE;
2334	}
2335
2336	if (queue != NULL) {
2337		// retain the dispatch queue
2338		connectionPrivate->dispatchQueue = queue;
2339		dispatch_retain(connectionPrivate->dispatchQueue);
2340
2341		if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
2342			dispatch_group_t	group	= NULL;
2343			mach_port_t		mp;
2344			dispatch_source_t	source;
2345
2346			//
2347			// We've taken a reference to the caller's dispatch_queue and we
2348			// want to hold on to that reference until we've processed any/all
2349			// notifications.  To facilitate this we create a group, dispatch
2350			// any notification blocks via that group, and when the caller
2351			// has told us to stop the notifications (unschedule) we wait for
2352			// the group to empty and use the group's finalizer to release
2353			// our reference to the SCNetworkConnection.
2354			//
2355			group = dispatch_group_create();
2356			connectionPrivate->dispatchGroup = group;
2357			CFRetain(connection);
2358			dispatch_set_context(connectionPrivate->dispatchGroup, (void *)connection);
2359			dispatch_set_finalizer_f(connectionPrivate->dispatchGroup, (dispatch_function_t)CFRelease);
2360
2361			mp = CFMachPortGetPort(connectionPrivate->notify_port);
2362			source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, mp, 0, queue);
2363			if (source == NULL) {
2364				SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkConnection dispatch_source_create() failed"));
2365				_SCErrorSet(kSCStatusFailed);
2366				goto done;
2367			}
2368
2369			// have our dispatch source hold a reference to the notification CFMachPort
2370			CFRetain(connectionPrivate->notify_port);
2371			dispatch_set_context(source, (void *)connectionPrivate->notify_port);
2372			dispatch_set_finalizer_f(source, (dispatch_function_t)CFRelease);
2373
2374			dispatch_source_set_event_handler(source, ^{
2375				kern_return_t	kr;
2376				typedef union {
2377					u_int8_t			buf[sizeof(mach_msg_empty_t) + MAX_TRAILER_SIZE];
2378					mach_msg_empty_rcv_t		msg;
2379					mach_no_senders_notification_t	no_senders;
2380				} *notify_message_t;
2381				CFMachPortRef		notify_port;
2382				notify_message_t	notify_msg;
2383
2384				notify_msg = (notify_message_t)malloc(sizeof(*notify_msg));
2385
2386				kr = mach_msg(&notify_msg->msg.header,	// msg
2387					      MACH_RCV_MSG,		// options
2388					      0,			// send_size
2389					      sizeof(*notify_msg),	// rcv_size
2390					      mp,			// rcv_name
2391					      MACH_MSG_TIMEOUT_NONE,	// timeout
2392					      MACH_PORT_NULL);		// notify
2393				if (kr != KERN_SUCCESS) {
2394					SCLog(TRUE, LOG_ERR,
2395					      CFSTR("SCDynamicStore notification handler, kr=0x%x"),
2396					      kr);
2397					return;
2398				}
2399
2400				CFRetain(connection);
2401				notify_port = dispatch_get_context(source);
2402
2403				dispatch_group_async(group, queue, ^{
2404					__SCNetworkConnectionMachCallBack(notify_port,
2405									  (void *)notify_msg,
2406									  sizeof(*notify_msg),
2407									  (void *)connection);
2408					free(notify_msg);
2409					CFRelease(connection);
2410				});
2411			});
2412
2413			dispatch_source_set_cancel_handler(source, ^{
2414				dispatch_release(source);
2415			});
2416
2417			connectionPrivate->dispatchSource = source;
2418			dispatch_resume(source);
2419		}
2420	} else {
2421		if (!_SC_isScheduled(NULL, runLoop, runLoopMode, connectionPrivate->rlList)) {
2422			/*
2423			 * if we do not already have notifications scheduled with
2424			 * this runLoop / runLoopMode
2425			 */
2426			CFRunLoopAddSource(runLoop, connectionPrivate->rls, runLoopMode);
2427		}
2428
2429		_SC_schedule(connection, runLoop, runLoopMode, connectionPrivate->rlList);
2430	}
2431
2432#if !TARGET_IPHONE_SIMULATOR
2433	if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
2434		CFRetain(connection);
2435		ne_session_set_event_handler(connectionPrivate->ne_session, __SCNetworkConnectionQueue(), ^(ne_session_event_t event, void *event_data) {
2436			#pragma unused(event_data)
2437			if (event == NESessionEventStatusChanged) {
2438				CFRetain(connection); /* Released in __SCNetworkConnectionCallBack */
2439				pthread_mutex_lock(&connectionPrivate->lock);
2440				if (connectionPrivate->rls != NULL) {
2441					CFRunLoopSourceSignal(connectionPrivate->rls);
2442					_SC_signalRunLoop(connection, connectionPrivate->rls, connectionPrivate->rlList);
2443				} else if (connectionPrivate->dispatchQueue != NULL) {
2444					dispatch_async(connectionPrivate->dispatchQueue, ^{ __SCNetworkConnectionCallBack((void *)connection); });
2445				}
2446				pthread_mutex_unlock(&connectionPrivate->lock);
2447			} else if (event == NESessionEventCanceled) {
2448				CFRelease(connection);
2449			}
2450		});
2451	}
2452#endif /* !TARGET_IPHONE_SIMULATOR */
2453
2454	ok = TRUE;
2455
2456    done :
2457
2458	pthread_mutex_unlock(&connectionPrivate->lock);
2459	return ok;
2460}
2461
2462
2463static Boolean
2464__SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef	connection,
2465					   CFRunLoopRef			runLoop,
2466					   CFStringRef			runLoopMode,
2467					   dispatch_queue_t		queue)
2468{
2469	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
2470	dispatch_group_t		drainGroup		= NULL;
2471	dispatch_queue_t		drainQueue		= NULL;
2472	int				sc_status		= kSCStatusFailed;
2473	CFIndex				n			= 0;
2474	Boolean				ok			= FALSE;
2475	kern_return_t			status;
2476
2477	// hold a reference while we unschedule
2478	CFRetain(connection);
2479
2480	pthread_mutex_lock(&connectionPrivate->lock);
2481
2482	if ((runLoop != NULL) && !connectionPrivate->scheduled) {			// if we should be scheduled (but are not)
2483		_SCErrorSet(kSCStatusInvalidArgument);
2484		goto done;
2485	}
2486
2487	if (((runLoop == NULL) && (connectionPrivate->dispatchQueue == NULL)) ||	// if we should be scheduled on a dispatch queue (but are not)
2488	    ((runLoop != NULL) && (connectionPrivate->dispatchQueue != NULL))) {	// if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
2489		_SCErrorSet(kSCStatusInvalidArgument);
2490		goto done;
2491	}
2492
2493	if (connectionPrivate->dispatchQueue != NULL) {
2494		if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
2495			// cancel dispatchSource
2496			if (connectionPrivate->dispatchSource != NULL) {
2497				dispatch_source_cancel(connectionPrivate->dispatchSource);
2498				connectionPrivate->dispatchSource = NULL;
2499			}
2500
2501			// save dispatchQueue/group, release reference when all queue'd blocks
2502			// have been processed, allow re-scheduling
2503			drainGroup = connectionPrivate->dispatchGroup;
2504			connectionPrivate->dispatchGroup = NULL;
2505			drainQueue = connectionPrivate->dispatchQueue;
2506			connectionPrivate->dispatchQueue = NULL;
2507		} else {
2508			dispatch_release(connectionPrivate->dispatchQueue);
2509			connectionPrivate->dispatchQueue = NULL;
2510		}
2511	} else {
2512		if (!_SC_unschedule(connection, runLoop, runLoopMode, connectionPrivate->rlList, FALSE)) {
2513			// if not currently scheduled on this runLoop / runLoopMode
2514			_SCErrorSet(kSCStatusFailed);
2515			goto done;
2516		}
2517
2518		n = CFArrayGetCount(connectionPrivate->rlList);
2519		if (n == 0 || !_SC_isScheduled(NULL, runLoop, runLoopMode, connectionPrivate->rlList)) {
2520			/*
2521			 * if we are no longer scheduled to receive notifications for
2522			 * this runLoop / runLoopMode
2523			 */
2524			CFRunLoopRemoveSource(runLoop, connectionPrivate->rls, runLoopMode);
2525
2526			if (n == 0) {
2527				// if *all* notifications have been unscheduled
2528				CFRelease(connectionPrivate->rlList);
2529				connectionPrivate->rlList = NULL;
2530				CFRunLoopSourceInvalidate(connectionPrivate->rls);
2531				CFRelease(connectionPrivate->rls);
2532				connectionPrivate->rls = NULL;
2533			}
2534		}
2535	}
2536
2537	if (n == 0) {
2538		// if *all* notifications have been unscheduled
2539		connectionPrivate->scheduled = FALSE;
2540
2541		if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {
2542#if !TARGET_IPHONE_SIMULATOR
2543			ne_session_cancel(connectionPrivate->ne_session);
2544#endif /* !TARGET_IPHONE_SIMULATOR */
2545		} else {
2546			mach_port_t session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
2547			if (session_port == MACH_PORT_NULL) {
2548				goto done;
2549			}
2550
2551			status = pppcontroller_notification(session_port, 0, &sc_status);
2552			if (__SCNetworkConnectionNeedsRetry(connection,
2553							    "__SCNetworkConnectionUnscheduleFromRunLoop pppcontroller_notification()",
2554							    status,
2555							    &sc_status)) {
2556				sc_status = kSCStatusOK;
2557				status = KERN_SUCCESS;
2558			}
2559
2560			if ((status != KERN_SUCCESS) || (sc_status != kSCStatusOK)) {
2561				_SCErrorSet(sc_status);
2562				goto done;
2563			}
2564		}
2565	}
2566
2567	ok = TRUE;
2568
2569    done :
2570
2571	pthread_mutex_unlock(&connectionPrivate->lock);
2572
2573	if (drainGroup != NULL) {
2574		dispatch_group_notify(drainGroup, drainQueue, ^{
2575			// release group/queue references
2576			dispatch_release(drainQueue);
2577			dispatch_release(drainGroup);	// releases our connection reference
2578		});
2579	}
2580
2581	// release our reference
2582	CFRelease(connection);
2583
2584	return ok;
2585}
2586
2587
2588Boolean
2589SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef	connection,
2590				       CFRunLoopRef		runLoop,
2591				       CFStringRef		runLoopMode)
2592{
2593	if (!isA_SCNetworkConnection(connection) || (runLoop == NULL) || (runLoopMode == NULL)) {
2594		_SCErrorSet(kSCStatusInvalidArgument);
2595		return FALSE;
2596	}
2597
2598	return __SCNetworkConnectionScheduleWithRunLoop(connection, runLoop, runLoopMode, NULL);
2599}
2600
2601
2602Boolean
2603SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef		connection,
2604					 CFRunLoopRef			runLoop,
2605					 CFStringRef			runLoopMode)
2606{
2607	if (!isA_SCNetworkConnection(connection) || (runLoop == NULL) || (runLoopMode == NULL)) {
2608		_SCErrorSet(kSCStatusInvalidArgument);
2609		return FALSE;
2610	}
2611
2612	return __SCNetworkConnectionUnscheduleFromRunLoop(connection, runLoop, runLoopMode, NULL);
2613}
2614
2615
2616Boolean
2617SCNetworkConnectionSetDispatchQueue(SCNetworkConnectionRef	connection,
2618				    dispatch_queue_t		queue)
2619{
2620	Boolean	ok	= FALSE;
2621
2622	if (!isA_SCNetworkConnection(connection)) {
2623		_SCErrorSet(kSCStatusInvalidArgument);
2624		return FALSE;
2625	}
2626
2627	if (queue != NULL) {
2628		ok = __SCNetworkConnectionScheduleWithRunLoop(connection, NULL, NULL, queue);
2629	} else {
2630		ok = __SCNetworkConnectionUnscheduleFromRunLoop(connection, NULL, NULL, NULL);
2631	}
2632
2633	return ok;
2634}
2635
2636
2637/* Requires having called SCNetworkConnectionSelectServiceWithOptions previously */
2638Boolean
2639SCNetworkConnectionIsOnDemandSuspended(SCNetworkConnectionRef connection)
2640{
2641	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
2642
2643	if (!isA_SCNetworkConnection(connection)) {
2644		_SCErrorSet(kSCStatusInvalidArgument);
2645		return FALSE;
2646	}
2647
2648	if (connectionPrivate->on_demand_info != NULL) {
2649		uint32_t	isSuspended	= 0;
2650		CFNumberRef	num		= NULL;
2651
2652		num = CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCPropNetVPNOnDemandSuspended);
2653		if (isA_CFNumber(num) &&
2654		    CFNumberGetValue(num, kCFNumberSInt32Type, &isSuspended) &&
2655		    (isSuspended != 0)) {
2656			return TRUE;
2657		}
2658	}
2659
2660	_SCErrorSet(kSCStatusOK);
2661	return FALSE;
2662}
2663
2664Boolean
2665SCNetworkConnectionTriggerOnDemandIfNeeded	(CFStringRef			hostName,
2666						 Boolean			afterDNSFail,
2667						 int				timeout,
2668						 int				trafficClass)
2669{
2670#if !TARGET_IPHONE_SIMULATOR
2671	__block Boolean triggeredOnDemand = FALSE;
2672	struct proc_uniqidentifierinfo procu;
2673	void *policy_match = NULL;
2674	char *hostname = NULL;
2675	CFIndex hostnameSize = 0;
2676	pid_t pid = getpid();
2677	uid_t uid = geteuid();
2678
2679	/* Require hostName, require non-root user */
2680	if (hostName == NULL || geteuid() == 0) {
2681		goto done;
2682	}
2683
2684	hostnameSize = CFStringGetLength(hostName);
2685	if (hostnameSize == 0) {
2686		goto done;
2687	}
2688
2689	hostname = malloc(hostnameSize + 1);
2690	CFStringGetCString(hostName, hostname, hostnameSize + 1, kCFStringEncodingUTF8);
2691
2692	if (proc_pidinfo(pid, PROC_PIDUNIQIDENTIFIERINFO, 1, &procu, sizeof(procu)) != sizeof(procu)) {
2693		goto done;
2694	}
2695
2696	policy_match = ne_session_copy_policy_match(hostname, NULL, NULL, procu.p_uuid, procu.p_uuid, pid, uid, 0, trafficClass);
2697
2698	NEPolicyServiceActionType action_type = ne_session_policy_match_get_service_action(policy_match);
2699	if (action_type == NESessionPolicyActionTrigger ||
2700	    (afterDNSFail && action_type == NESessionPolicyActionTriggerIfNeeded)) {
2701		uuid_t config_id;
2702		if (ne_session_policy_match_get_service(policy_match, config_id)) {
2703			xpc_object_t start_options = xpc_dictionary_create(NULL, NULL, 0);
2704			if (start_options != NULL) {
2705				xpc_dictionary_set_bool(start_options, NESessionStartOptionIsOnDemandKey, true);
2706				xpc_dictionary_set_string(start_options, NESessionStartOptionMatchHostnameKey, hostname);
2707
2708				ne_session_t new_session = ne_session_create(config_id, ne_session_policy_match_get_service_type(policy_match));
2709				if (new_session != NULL) {
2710					dispatch_semaphore_t wait_for_session = dispatch_semaphore_create(0);
2711					dispatch_retain(wait_for_session);
2712					xpc_retain(start_options);
2713					ne_session_get_status(new_session, __SCNetworkConnectionQueue(),
2714						^(ne_session_status_t status) {
2715							if (status == NESessionStatusDisconnected) {
2716								dispatch_retain(wait_for_session);
2717								ne_session_set_event_handler(new_session, __SCNetworkConnectionQueue(),
2718									^(ne_session_event_t event, void *event_data) {
2719										if (event == NESessionEventStatusChanged) {
2720											dispatch_retain(wait_for_session);
2721											ne_session_get_status(new_session, __SCNetworkConnectionQueue(),
2722												^(ne_session_status_t new_status) {
2723													if (new_status != NESessionStatusConnecting) {
2724														if (status == NESessionStatusConnected) {
2725															triggeredOnDemand = TRUE;
2726														}
2727														ne_session_cancel(new_session);
2728													}
2729													dispatch_release(wait_for_session);
2730												});
2731										} else if (event == NESessionEventCanceled) {
2732											dispatch_semaphore_signal(wait_for_session);
2733											dispatch_release(wait_for_session);
2734										}
2735									});
2736								ne_session_start_with_options(new_session, start_options);
2737							} else {
2738								dispatch_semaphore_signal(wait_for_session);
2739							}
2740							dispatch_release(wait_for_session);
2741							xpc_release(start_options);
2742						});
2743					dispatch_semaphore_wait(wait_for_session, timeout ? dispatch_time(DISPATCH_TIME_NOW, (int64_t)timeout * NSEC_PER_SEC) : DISPATCH_TIME_FOREVER);
2744					dispatch_release(wait_for_session);
2745					ne_session_release(new_session);
2746				}
2747
2748				xpc_release(start_options);
2749			}
2750		}
2751	}
2752done:
2753	if (hostname) {
2754		free(hostname);
2755	}
2756
2757	if (policy_match) {
2758		free(policy_match);
2759	}
2760
2761	return triggeredOnDemand;
2762#else
2763#pragma unused(hostName, afterDNSFail, timeout, trafficClass)
2764	return FALSE;
2765#endif
2766}
2767
2768
2769Boolean
2770SCNetworkConnectionCopyOnDemandInfo(SCNetworkConnectionRef	connection,
2771				    CFStringRef			*onDemandRemoteAddress,
2772				    SCNetworkConnectionStatus	*onDemandConnectionStatus)
2773{
2774	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
2775
2776	if (!isA_SCNetworkConnection(connection)) {
2777		_SCErrorSet(kSCStatusInvalidArgument);
2778		return FALSE;
2779	}
2780
2781	if (connectionPrivate->service == NULL) {
2782		_SCErrorSet(kSCStatusConnectionNoService);
2783		return FALSE;
2784	}
2785
2786	if (onDemandRemoteAddress != NULL) {
2787		*onDemandRemoteAddress = NULL;
2788	}
2789
2790	if (onDemandConnectionStatus != NULL) {
2791		*onDemandConnectionStatus = kSCNetworkConnectionInvalid;
2792	}
2793
2794	if (connectionPrivate->on_demand_info != NULL) {
2795		if (onDemandRemoteAddress != NULL) {
2796			CFStringRef address =
2797			    CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCNetworkConnectionOnDemandRemoteAddress);
2798			if (isA_CFString(address)) {
2799				*onDemandRemoteAddress = address;
2800				CFRetain(*onDemandRemoteAddress);
2801			}
2802		}
2803
2804		if (onDemandConnectionStatus != NULL) {
2805			int num;
2806			CFNumberRef status_num =
2807			    CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCNetworkConnectionOnDemandStatus);
2808			if (isA_CFNumber(status_num) && CFNumberGetValue(status_num, kCFNumberIntType, &num)) {
2809				*onDemandConnectionStatus = num;
2810			}
2811		}
2812	}
2813
2814	return connectionPrivate->on_demand;
2815}
2816
2817
2818Boolean
2819SCNetworkConnectionGetReachabilityInfo(SCNetworkConnectionRef		connection,
2820				       SCNetworkReachabilityFlags	*reach_flags,
2821				       unsigned int			*reach_if_index)
2822{
2823	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
2824
2825	if (!isA_SCNetworkConnection(connection)) {
2826		_SCErrorSet(kSCStatusInvalidArgument);
2827		return FALSE;
2828	}
2829
2830	if (connectionPrivate->service == NULL) {
2831		_SCErrorSet(kSCStatusConnectionNoService);
2832		return FALSE;
2833	}
2834
2835	if (reach_flags != NULL) {
2836		*reach_flags = 0;
2837	}
2838
2839	if (reach_if_index != NULL) {
2840		*reach_if_index = 0;
2841	}
2842
2843	if (connectionPrivate->on_demand_info != NULL) {
2844		if (reach_flags != NULL) {
2845			int num;
2846			CFNumberRef flags_num =
2847			    CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCNetworkConnectionOnDemandReachFlags);
2848			if (isA_CFNumber(flags_num) && CFNumberGetValue(flags_num, kCFNumberIntType, &num)) {
2849				*reach_flags = num;
2850			}
2851		}
2852
2853		if (reach_if_index != NULL) {
2854			int num;
2855			CFNumberRef if_index_num =
2856			    CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCNetworkConnectionOnDemandReachInterfaceIndex);
2857			if (isA_CFNumber(if_index_num) && CFNumberGetValue(if_index_num, kCFNumberIntType, &num)) {
2858				*reach_if_index = num;
2859			}
2860		}
2861	}
2862
2863	return TRUE;
2864}
2865
2866
2867SCNetworkConnectionType
2868SCNetworkConnectionGetType(SCNetworkConnectionRef connection)
2869{
2870	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
2871
2872	if (!isA_SCNetworkConnection(connection)) {
2873		_SCErrorSet(kSCStatusInvalidArgument);
2874		return kSCNetworkConnectionTypeUnknown;
2875	}
2876
2877	if (connectionPrivate->service == NULL) {
2878		_SCErrorSet(kSCStatusConnectionNoService);
2879		return kSCNetworkConnectionTypeUnknown;
2880	}
2881
2882	_SCErrorSet(kSCStatusOK);
2883
2884	return connectionPrivate->type;
2885}
2886
2887
2888static Boolean
2889validate_flow_properties(CFDictionaryRef flowProperties)
2890{
2891	CFStringRef			host_name_str;
2892	CFDataRef			host_address_data;
2893	CFNumberRef			host_port_num;
2894
2895	if (!isA_CFDictionary(flowProperties)) {
2896		return FALSE;
2897	}
2898
2899	/* Validate the host name if one was given */
2900	host_name_str = CFDictionaryGetValue(flowProperties, kSCNetworkConnectionFlowPropertyHostName);
2901	if (host_name_str != NULL && (!isA_CFString(host_name_str) || CFStringGetLength(host_name_str) == 0)) {
2902		return FALSE;
2903	}
2904
2905	/* Validate the address if one was given */
2906	host_address_data = CFDictionaryGetValue(flowProperties, kSCNetworkConnectionFlowPropertyHostAddress);
2907	if (host_address_data != NULL) {
2908		struct sockaddr *sock_addr;
2909
2910		if (!isA_CFData(host_address_data) || CFDataGetLength(host_address_data) < sizeof(struct sockaddr)) {
2911			return FALSE;
2912		}
2913
2914		sock_addr = (struct sockaddr *)CFDataGetBytePtr(host_address_data);
2915		if (CFDataGetLength(host_address_data) < sock_addr->sa_len) {
2916			return FALSE;
2917		}
2918
2919		if (sock_addr->sa_family == AF_INET) {
2920			if (sock_addr->sa_len >= sizeof(struct sockaddr_in)) {
2921				struct sockaddr_in *sa_in = (struct sockaddr_in *)(void *)sock_addr;
2922				in_addr_t any = { INADDR_ANY };
2923				if (memcmp(&sa_in->sin_addr, &any, sizeof(any)) == 0) {
2924					return FALSE;
2925				}
2926			} else {
2927				return FALSE;
2928			}
2929		} else if (sock_addr->sa_family == AF_INET6) {
2930			if (sock_addr->sa_len >= sizeof(struct sockaddr_in6)) {
2931				struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)(void *)sock_addr;
2932				struct in6_addr any = IN6ADDR_ANY_INIT;
2933				if (memcmp(&sa_in6->sin6_addr, &any, sizeof(any)) == 0) {
2934					return FALSE;
2935				}
2936			}
2937		}
2938	}
2939
2940	/* We must have either a host name or an address */
2941	if (host_name_str == NULL && host_address_data == NULL) {
2942		return FALSE;
2943	}
2944
2945	/* Validate the port */
2946	host_port_num = CFDictionaryGetValue(flowProperties, kSCNetworkConnectionFlowPropertyHostPort);
2947	if (host_port_num != NULL) {
2948		int num;
2949		if (!isA_CFNumber(host_port_num) || !CFNumberGetValue(host_port_num, kCFNumberIntType, &num)) {
2950			return FALSE;
2951		}
2952
2953		if (num == 0) {
2954			return FALSE;
2955		}
2956	} else {
2957		return FALSE;
2958	}
2959
2960	return TRUE;
2961}
2962
2963
2964CFDataRef
2965SCNetworkConnectionCopyFlowDivertToken(SCNetworkConnectionRef	connection,
2966				       CFDictionaryRef		flowProperties)
2967{
2968	CFDictionaryRef			app_properties		= NULL;
2969	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
2970	CFDataRef			token			= NULL;
2971
2972	if (!isA_SCNetworkConnection(connection)) {
2973		_SCErrorSet(kSCStatusInvalidArgument);
2974		goto done;
2975	}
2976
2977	if (connectionPrivate->service == NULL) {
2978		_SCErrorSet(kSCStatusConnectionNoService);
2979		goto done;
2980	}
2981
2982	if (connectionPrivate->type != kSCNetworkConnectionTypeAppLayerVPN) {
2983		_SCErrorSet(kSCStatusInvalidArgument);
2984		goto done;
2985	}
2986
2987	if (!validate_flow_properties(flowProperties)) {
2988		_SCErrorSet(kSCStatusInvalidArgument);
2989		goto done;
2990	}
2991
2992	app_properties = VPNAppLayerCopyCachedAppProperties(connectionPrivate->client_audit_token,
2993							    connectionPrivate->client_pid,
2994							    connectionPrivate->client_uuid,
2995							    connectionPrivate->client_bundle_id);
2996	if (app_properties == NULL) {
2997		SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkConnectionCopyFlowDivertToken: no cached app properties available"));
2998		_SCErrorSet(kSCStatusFailed);
2999		goto done;
3000	}
3001
3002	token = VPNAppLayerCreateFlowDivertToken(connection, app_properties, flowProperties);
3003
3004done:
3005	if (app_properties != NULL) {
3006		CFRelease(app_properties);
3007	}
3008
3009	return token;
3010}
3011
3012
3013int
3014SCNetworkConnectionGetServiceIdentifier		(SCNetworkConnectionRef		connection)
3015{
3016	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
3017	int				service_identifier	= -1;
3018
3019	if (connectionPrivate->service != NULL) {
3020		service_identifier = 0;
3021		if (connectionPrivate->on_demand_info != NULL) {
3022			CFNumberRef id_num = CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCPropNetDNSServiceIdentifier);
3023
3024			if (isA_CFNumber(id_num)) {
3025				CFNumberGetValue(id_num, kCFNumberIntType, &service_identifier);
3026			}
3027		}
3028	}
3029
3030	return service_identifier;
3031}
3032
3033
3034#if !TARGET_IPHONE_SIMULATOR
3035SCNetworkConnectionStatus
3036SCNetworkConnectionGetStatusFromNEStatus(ne_session_status_t status)
3037{
3038	switch (status) {
3039		case NESessionStatusInvalid:
3040			return kSCNetworkConnectionInvalid;
3041		case NESessionStatusDisconnected:
3042			return kSCNetworkConnectionDisconnected;
3043		case NESessionStatusConnecting:
3044		case NESessionStatusReasserting:
3045			return kSCNetworkConnectionConnecting;
3046		case NESessionStatusConnected:
3047			return kSCNetworkConnectionConnected;
3048		case NESessionStatusDisconnecting:
3049			return kSCNetworkConnectionDisconnecting;
3050	}
3051
3052	return kSCNetworkConnectionInvalid;
3053}
3054#endif /* !TARGET_IPHONE_SIMULATOR */
3055
3056
3057#pragma mark -
3058#pragma mark User level "dial" API
3059
3060
3061#define k_NetworkConnect_Notification	"com.apple.networkConnect"
3062#define k_NetworkConnect_Pref_File	CFSTR("com.apple.networkConnect")
3063#define k_InterentConnect_Pref_File	CFSTR("com.apple.internetconnect")
3064
3065#define k_Dial_Default_Key		CFSTR("ConnectByDefault") // needs to go into SC
3066#define k_Last_Service_Id_Key		CFSTR("ServiceID")
3067#define k_Unique_Id_Key	 		CFSTR("UniqueIdentifier")
3068
3069
3070/* Private Prototypes */
3071static Boolean SCNetworkConnectionPrivateCopyDefaultServiceIDForDial	(CFStringRef *serviceID);
3072static Boolean SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore	(CFStringRef *serviceID);
3073static Boolean SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray, CFDictionaryRef *userOptions);
3074static Boolean SCNetworkConnectionPrivateIsPPPService			(CFStringRef serviceID, CFStringRef subType1, CFStringRef subType2);
3075static void addPasswordFromKeychain					(CFStringRef serviceID, CFDictionaryRef *userOptions);
3076static CFStringRef copyPasswordFromKeychain				(CFStringRef uniqueID);
3077
3078static int		notify_userprefs_token	= -1;
3079
3080static CFDictionaryRef	onDemand_configuration	= NULL;
3081static Boolean		onDemand_force_refresh	= FALSE;
3082static pthread_mutex_t	onDemand_notify_lock	= PTHREAD_MUTEX_INITIALIZER;
3083static int		onDemand_notify_token	= -1;
3084
3085
3086/*
3087 *	return TRUE if domain1 ends with domain2, and will check for trailing "."
3088 */
3089#define WILD_CARD_MATCH_STR CFSTR("*")
3090Boolean
3091_SC_domainEndsWithDomain(CFStringRef compare_domain, CFStringRef match_domain)
3092{
3093	CFRange		range;
3094	Boolean		ret		= FALSE;
3095	CFStringRef	s1		= NULL;
3096	Boolean		s1_created	= FALSE;
3097	CFStringRef	s2		= NULL;
3098	Boolean		s2_created	= FALSE;
3099	CFStringRef	s3		= NULL;
3100
3101	if (CFEqual(match_domain, WILD_CARD_MATCH_STR)) {
3102		return TRUE;
3103	}
3104
3105	if (CFStringHasSuffix(compare_domain, CFSTR("."))) {
3106		range.location = 0;
3107		range.length = CFStringGetLength(compare_domain) - 1;
3108		s1 = CFStringCreateWithSubstring(NULL, compare_domain, range);
3109		if (s1 == NULL) {
3110			goto done;
3111		}
3112		s1_created = TRUE;
3113	} else {
3114		s1 = compare_domain;
3115	}
3116
3117	if (CFStringHasSuffix(match_domain, CFSTR("."))) {
3118		range.location = 0;
3119		range.length = CFStringGetLength(match_domain) - 1;
3120		s2 = CFStringCreateWithSubstring(NULL, match_domain, range);
3121		if (s2 == NULL) {
3122			goto done;
3123		}
3124		s2_created = TRUE;
3125	} else {
3126		s2 = match_domain;
3127	}
3128
3129	if (CFStringHasPrefix(s2, CFSTR("*."))) {
3130		range.location = 2;
3131		range.length = CFStringGetLength(s2)-2;
3132		s3 = CFStringCreateWithSubstring(NULL, s2, range);
3133		if (s3 == NULL) {
3134			goto done;
3135		}
3136		if (s2_created) {
3137			CFRelease(s2);
3138		}
3139		s2 = s3;
3140		s2_created = TRUE;
3141	}
3142
3143	ret = CFStringHasSuffix(s1, s2);
3144
3145    done :
3146
3147	if (s1_created)	CFRelease(s1);
3148	if (s2_created)	CFRelease(s2);
3149	return ret;
3150}
3151
3152static CFCharacterSetRef
3153_SC_getNotDotOrStarCharacterSet (void)
3154{
3155	static CFCharacterSetRef notDotOrStar = NULL;
3156	if (notDotOrStar == NULL) {
3157		CFCharacterSetRef dotOrStar = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, CFSTR(".*"));
3158		if (dotOrStar) {
3159			notDotOrStar = CFCharacterSetCreateInvertedSet(kCFAllocatorDefault, dotOrStar);
3160			CFRelease(dotOrStar);
3161		}
3162	}
3163	return notDotOrStar;
3164}
3165
3166static CFMutableStringRef
3167_SC_createStringByTrimmingDotsAndStars (CFStringRef string)
3168{
3169	CFCharacterSetRef notDotOrStar = _SC_getNotDotOrStarCharacterSet();
3170	CFRange entireString = CFRangeMake(0, CFStringGetLength(string));
3171	CFMutableStringRef result = CFStringCreateMutableCopy(kCFAllocatorDefault, entireString.length, string);
3172	CFRange start;
3173	CFRange end = CFRangeMake(entireString.length, 0);
3174
3175	if (CFStringFindCharacterFromSet(string, notDotOrStar, entireString, 0, &start) &&
3176	    CFStringFindCharacterFromSet(string, notDotOrStar, entireString, kCFCompareBackwards, &end)) {
3177		if (start.location == kCFNotFound || end.location == kCFNotFound || start.location > end.location) {
3178			CFRelease(result);
3179			return NULL;
3180		}
3181	}
3182
3183	if ((end.location + 1) < entireString.length) {
3184		CFStringReplace(result, CFRangeMake(end.location + 1, entireString.length - (end.location + 1)), CFSTR(""));
3185	}
3186	if (start.location > 0) {
3187		CFStringReplace(result, CFRangeMake(0, start.location), CFSTR(""));
3188	}
3189
3190	return result;
3191}
3192
3193static CFIndex
3194_SC_getCountOfStringInString (CFStringRef string, CFStringRef substring)
3195{
3196	CFIndex count = 0;
3197	CFArrayRef ranges = CFStringCreateArrayWithFindResults(kCFAllocatorDefault, string, substring, CFRangeMake(0, CFStringGetLength(string)), 0);
3198	if (ranges != NULL) {
3199		count = CFArrayGetCount(ranges);
3200		CFRelease(ranges);
3201	}
3202	return count;
3203}
3204
3205Boolean
3206_SC_hostMatchesDomain(CFStringRef hostname, CFStringRef domain)
3207{
3208	Boolean			result		= FALSE;
3209	CFMutableStringRef	trimmedHostname	= NULL;
3210	CFMutableStringRef	trimmedDomain	= NULL;
3211
3212	if (!isA_CFString(hostname) || !isA_CFString(domain)) {
3213		goto done;
3214	}
3215
3216	trimmedHostname = _SC_createStringByTrimmingDotsAndStars(hostname);
3217	trimmedDomain = _SC_createStringByTrimmingDotsAndStars(domain);
3218
3219	if (!isA_CFString(trimmedHostname) || !isA_CFString(trimmedDomain)) {
3220		goto done;
3221	}
3222
3223	CFIndex numHostnameDots = _SC_getCountOfStringInString(trimmedHostname, CFSTR("."));
3224	CFIndex numDomainDots = _SC_getCountOfStringInString(trimmedDomain, CFSTR("."));
3225	if (numHostnameDots == numDomainDots) {
3226		result = CFEqual(trimmedHostname, trimmedDomain);
3227	} else if (numDomainDots > 0 && numDomainDots < numHostnameDots) {
3228		CFStringReplace(trimmedDomain, CFRangeMake(0, 0), CFSTR("."));
3229		result = CFStringHasSuffix(trimmedHostname, trimmedDomain);
3230	} else {
3231		result = FALSE;
3232	}
3233
3234done:
3235	if (trimmedHostname) {
3236		CFRelease(trimmedHostname);
3237	}
3238	if (trimmedDomain) {
3239		CFRelease(trimmedDomain);
3240	}
3241	return result;
3242}
3243
3244/* VPN On Demand */
3245
3246static CFDictionaryRef
3247__SCNetworkConnectionCopyOnDemandConfiguration(void)
3248{
3249	int			changed		= 1;
3250	int			status;
3251	uint64_t		triggersCount	= 0;
3252	CFDictionaryRef		configuration;
3253
3254	pthread_mutex_lock(&onDemand_notify_lock);
3255	if (onDemand_notify_token == -1) {
3256		status = notify_register_check(kSCNETWORKCONNECTION_ONDEMAND_NOTIFY_KEY, &onDemand_notify_token);
3257		if (status != NOTIFY_STATUS_OK) {
3258			SCLog(TRUE, LOG_ERR, CFSTR("notify_register_check() failed, status=%d"), status);
3259			onDemand_notify_token = -1;
3260		}
3261	}
3262
3263	if (onDemand_notify_token != -1) {
3264		status = notify_check(onDemand_notify_token, &changed);
3265		if (status != NOTIFY_STATUS_OK) {
3266			SCLog(TRUE, LOG_ERR, CFSTR("notify_check() failed, status=%d"), status);
3267			(void)notify_cancel(onDemand_notify_token);
3268			onDemand_notify_token = -1;
3269		}
3270	}
3271
3272	if (changed && (onDemand_notify_token != -1)) {
3273		status = notify_get_state(onDemand_notify_token, &triggersCount);
3274		if (status != NOTIFY_STATUS_OK) {
3275			SCLog(TRUE, LOG_ERR, CFSTR("notify_get_state() failed, status=%d"), status);
3276			(void)notify_cancel(onDemand_notify_token);
3277			onDemand_notify_token = -1;
3278		}
3279	}
3280
3281	if (changed || onDemand_force_refresh) {
3282		CFStringRef	key;
3283
3284		if (_sc_debug || (debug > 0)) {
3285			SCLog(TRUE, LOG_INFO,
3286			      CFSTR("OnDemand information %s"),
3287			      (onDemand_configuration == NULL) ? "fetched" : "updated");
3288		}
3289
3290		if (onDemand_configuration != NULL) {
3291			CFRelease(onDemand_configuration);
3292			onDemand_configuration = NULL;
3293		}
3294
3295		if ((triggersCount > 0) || onDemand_force_refresh) {
3296			key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetOnDemand);
3297			onDemand_configuration = SCDynamicStoreCopyValue(NULL, key);
3298			CFRelease(key);
3299			if ((onDemand_configuration != NULL) && !isA_CFDictionary(onDemand_configuration)) {
3300				CFRelease(onDemand_configuration);
3301				onDemand_configuration = NULL;
3302			}
3303		}
3304
3305		onDemand_force_refresh = FALSE;
3306	}
3307
3308	configuration = (onDemand_configuration != NULL) ? CFRetain(onDemand_configuration) : NULL;
3309	pthread_mutex_unlock(&onDemand_notify_lock);
3310
3311	return configuration;
3312}
3313
3314
3315__private_extern__
3316void
3317__SCNetworkConnectionForceOnDemandConfigurationRefresh(void)
3318{
3319	pthread_mutex_lock(&onDemand_notify_lock);
3320	onDemand_force_refresh = TRUE;
3321	pthread_mutex_unlock(&onDemand_notify_lock);
3322
3323	return;
3324}
3325
3326
3327static Boolean
3328__SCNetworkConnectionShouldNeverMatch(CFDictionaryRef trigger, CFStringRef hostName, pid_t client_pid)
3329{
3330	CFArrayRef	exceptedProcesses;
3331	CFIndex		exceptedProcessesCount;
3332	CFIndex		exceptedProcessesIndex;
3333	CFArrayRef	exceptions;
3334	CFIndex		exceptionsCount;
3335	int		exceptionsIndex;
3336
3337	// we have a matching domain, check against exception list
3338	exceptions = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandMatchDomainsNever);
3339	exceptionsCount = isA_CFArray(exceptions) ? CFArrayGetCount(exceptions) : 0;
3340	for (exceptionsIndex = 0; exceptionsIndex < exceptionsCount; exceptionsIndex++) {
3341		CFStringRef	exception;
3342
3343		exception = CFArrayGetValueAtIndex(exceptions, exceptionsIndex);
3344		if (isA_CFString(exception) && _SC_domainEndsWithDomain(hostName, exception)) {
3345			// found matching exception
3346			if (_sc_debug || (debug > 0)) {
3347				SCLog(TRUE, LOG_INFO, CFSTR("OnDemand match exception"));
3348			}
3349			return TRUE;
3350		}
3351	}
3352
3353	if (client_pid != 0) {
3354		exceptedProcesses = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandPluginPIDs);
3355		exceptedProcessesCount = isA_CFArray(exceptedProcesses) ? CFArrayGetCount(exceptedProcesses) : 0;
3356		for (exceptedProcessesIndex = 0; exceptedProcessesIndex < exceptedProcessesCount; exceptedProcessesIndex++) {
3357			int		pid;
3358			CFNumberRef	pidRef;
3359
3360			pidRef = CFArrayGetValueAtIndex(exceptedProcesses, exceptedProcessesIndex);
3361			if (isA_CFNumber(pidRef) && CFNumberGetValue(pidRef, kCFNumberIntType, &pid)) {
3362				if (pid == client_pid) {
3363					return TRUE;
3364				}
3365			}
3366		}
3367	}
3368
3369	return FALSE;
3370}
3371
3372static CFStringRef
3373__SCNetworkConnectionDomainGetMatchWithParameters(CFStringRef action, CFPropertyListRef actionParameters, CFStringRef hostName, CFStringRef *probeString)
3374{
3375	CFArrayRef	actionArray = NULL;
3376	CFIndex		actionArraySize = 0;
3377	CFIndex		i;
3378	CFStringRef	matchDomain	= NULL;
3379
3380	/* For now, only support EvaluateConnection, which takes a CFArray */
3381	if (!CFEqual(action, kSCValNetVPNOnDemandRuleActionEvaluateConnection) || !isA_CFArray(actionParameters)) {
3382		return NULL;
3383	}
3384
3385	actionArray = (CFArrayRef)actionParameters;
3386	actionArraySize = CFArrayGetCount(actionArray);
3387
3388	/* Process domain rules, with actions of ConnectIfNeeded and NeverConnect */
3389	for (i = 0; i < actionArraySize; i++) {
3390		CFStringRef	domainAction = NULL;
3391		CFDictionaryRef	domainRule = CFArrayGetValueAtIndex(actionArray, i);
3392		CFArrayRef	domains = NULL;
3393		CFIndex		domainsCount = 0;
3394		CFIndex		domainsIndex;
3395
3396		if (!isA_CFDictionary(domainRule)) {
3397			continue;
3398		}
3399
3400		domains = CFDictionaryGetValue(domainRule, kSCPropNetVPNOnDemandRuleActionParametersDomains);
3401		if (!isA_CFArray(domains)) {
3402			continue;
3403		}
3404
3405		domainsCount = CFArrayGetCount(domains);
3406		for (domainsIndex = 0; domainsIndex < domainsCount; domainsIndex++) {
3407			CFStringRef	domain;
3408			domain = CFArrayGetValueAtIndex(domains, domainsIndex);
3409			if (isA_CFString(domain) && _SC_domainEndsWithDomain(hostName, domain)) {
3410				matchDomain = domain;
3411				break;
3412			}
3413		}
3414
3415		if (matchDomain) {
3416			domainAction = CFDictionaryGetValue(domainRule, kSCPropNetVPNOnDemandRuleActionParametersDomainAction);
3417			if (isA_CFString(domainAction) && CFEqual(domainAction, kSCValNetVPNOnDemandRuleActionParametersDomainActionNeverConnect)) {
3418				return NULL;
3419			} else {
3420				/* If we found a match, save the optional probe string as well */
3421				if (probeString) {
3422					*probeString = CFDictionaryGetValue(domainRule, kSCPropNetVPNOnDemandRuleActionParametersRequiredURLStringProbe);
3423				}
3424				break;
3425			}
3426		}
3427	}
3428
3429	return matchDomain;
3430}
3431
3432static CFStringRef
3433__SCNetworkConnectionDomainGetMatch(CFDictionaryRef trigger, CFStringRef hostName, Boolean onDemandRetry)
3434{
3435	CFArrayRef	domains;
3436	CFIndex		domainsCount;
3437	int		domainsIndex;
3438	CFStringRef	key;
3439	CFStringRef	match_domain	= NULL;
3440
3441	/* Old configuration: always, never, on retry lists */
3442	key = onDemandRetry ? kSCNetworkConnectionOnDemandMatchDomainsOnRetry : kSCNetworkConnectionOnDemandMatchDomainsAlways;
3443
3444	domains = CFDictionaryGetValue(trigger, key);
3445	domainsCount = isA_CFArray(domains) ? CFArrayGetCount(domains) : 0;
3446	for (domainsIndex = 0; domainsIndex < domainsCount; domainsIndex++) {
3447		CFStringRef	domain;
3448
3449		domain = CFArrayGetValueAtIndex(domains, domainsIndex);
3450		if (isA_CFString(domain) && _SC_domainEndsWithDomain(hostName, domain)) {
3451			match_domain = domain;
3452			break;
3453		}
3454	}
3455
3456	return match_domain;
3457}
3458
3459
3460static Boolean
3461__SCNetworkConnectionShouldAlwaysConnect(CFDictionaryRef trigger)
3462{
3463	CFStringRef action = CFDictionaryGetValue(trigger, kSCPropNetVPNOnDemandRuleAction);
3464	return (isA_CFString(action) && CFEqual(action, kSCValNetVPNOnDemandRuleActionConnect));
3465}
3466
3467
3468static Boolean
3469__SCNetworkConnectionShouldIgnoreTrigger(CFDictionaryRef trigger)
3470{
3471	CFStringRef	action		= CFDictionaryGetValue(trigger, kSCPropNetVPNOnDemandRuleAction);
3472
3473	if (isA_CFString(action) &&
3474	    (CFEqual(action, kSCValNetVPNOnDemandRuleActionIgnore) ||
3475	     CFEqual(action, kSCValNetVPNOnDemandRuleActionDisconnect))) {
3476		    return TRUE;
3477	}
3478
3479	return FALSE;
3480}
3481
3482
3483static CFDictionaryRef
3484__SCNetworkConnectionCopyMatchingTriggerWithName(CFDictionaryRef	configuration,
3485						 CFStringRef		hostName,
3486						 pid_t			client_pid,
3487						 Boolean		onDemandRetry,
3488						 CFDictionaryRef	*match_info,
3489						 Boolean		*triggerNow,
3490						 CFStringRef		*probe_string)
3491{
3492	CFDictionaryRef	result		= NULL;
3493	int		sc_status	= kSCStatusOK;
3494	CFArrayRef	triggers;
3495	uint64_t	triggersCount	= 0;
3496	int		triggersIndex;
3497	Boolean		usedOnDemandRetry = FALSE;
3498
3499	if (triggerNow != NULL) {
3500		*triggerNow = FALSE;
3501	}
3502
3503	if (match_info != NULL) {
3504		*match_info = NULL;
3505	}
3506
3507	triggers = CFDictionaryGetValue(configuration, kSCNetworkConnectionOnDemandTriggers);
3508	triggersCount = isA_CFArray(triggers) ? CFArrayGetCount(triggers) : 0;
3509	for (triggersIndex = 0; triggersIndex < triggersCount; triggersIndex++) {
3510		CFStringRef	matched_domain	= NULL;
3511		CFStringRef	matched_probe_string = NULL;
3512		CFDictionaryRef	trigger;
3513		Boolean		trigger_matched	= FALSE;
3514
3515		usedOnDemandRetry = FALSE;
3516
3517		trigger = CFArrayGetValueAtIndex(triggers, triggersIndex);
3518		if (!isA_CFDictionary(trigger)) {
3519			// if not a valid "OnDemand" configuration
3520			continue;
3521		}
3522
3523		if (__SCNetworkConnectionShouldAlwaysConnect(trigger)) {
3524			/* If the trigger action is 'Connect', always match this trigger */
3525			/* First check the never match list */
3526			if (__SCNetworkConnectionShouldNeverMatch(trigger, hostName, client_pid)) {
3527				continue;
3528			}
3529			trigger_matched = TRUE;
3530		} else if (__SCNetworkConnectionShouldIgnoreTrigger(trigger)) {
3531			/* If the trigger action is 'Ignore' or 'Disconnect', skip this trigger */
3532			sc_status = kSCStatusConnectionIgnore;
3533			continue;
3534		} else {
3535			CFStringRef action = CFDictionaryGetValue(trigger, kSCPropNetVPNOnDemandRuleAction);
3536			CFArrayRef actionParameters = CFDictionaryGetValue(trigger, kSCPropNetVPNOnDemandRuleActionParameters);
3537			if (action && actionParameters) {
3538				matched_domain = __SCNetworkConnectionDomainGetMatchWithParameters(action, actionParameters, hostName, &matched_probe_string);
3539				usedOnDemandRetry = TRUE;
3540			} else {
3541				if (onDemandRetry) {
3542					matched_domain = __SCNetworkConnectionDomainGetMatch(trigger, hostName, TRUE);
3543					usedOnDemandRetry = TRUE;
3544				} else {
3545					matched_domain = __SCNetworkConnectionDomainGetMatch(trigger, hostName, FALSE);
3546					if (matched_domain == NULL && result == NULL) {
3547						/* Check the retry list if Always failed */
3548						matched_domain = __SCNetworkConnectionDomainGetMatch(trigger, hostName, TRUE);
3549						usedOnDemandRetry = TRUE;
3550					}
3551				}
3552			}
3553
3554			if (matched_domain) {
3555				if (__SCNetworkConnectionShouldNeverMatch(trigger, hostName, client_pid)) {
3556					matched_domain = NULL;
3557					continue;
3558				} else {
3559					trigger_matched = TRUE;
3560				}
3561			}
3562		}
3563
3564		if (trigger_matched) {
3565			// if we have a matching domain and there were no exceptions
3566			// then we pass back the OnDemand info
3567			if (match_info != NULL) {
3568				CFMutableDictionaryRef	minfo;
3569				SCNetworkConnectionType	type	= kSCNetworkConnectionTypeIPLayerVPN;
3570				CFNumberRef		type_num;
3571
3572				if (*match_info != NULL) {
3573					CFRelease(*match_info);
3574					*match_info = NULL;
3575				}
3576
3577				minfo = CFDictionaryCreateMutable(kCFAllocatorDefault,
3578								  0,
3579								  &kCFTypeDictionaryKeyCallBacks,
3580								  &kCFTypeDictionaryValueCallBacks);
3581
3582				type_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &type);
3583				CFDictionarySetValue(minfo, kSCNetworkConnectionOnDemandMatchInfoVPNType, type_num);
3584				CFRelease(type_num);
3585				if (matched_domain) {
3586					CFDictionarySetValue(minfo, kSCNetworkConnectionOnDemandMatchInfoDomain, matched_domain);
3587				}
3588				CFDictionarySetValue(minfo,
3589						     kSCNetworkConnectionOnDemandMatchInfoOnRetry,
3590						     (usedOnDemandRetry ? kCFBooleanTrue : kCFBooleanFalse));
3591
3592				*match_info = minfo;
3593			}
3594
3595			if (probe_string != NULL) {
3596				if (*probe_string != NULL) {
3597					CFRelease(*probe_string);
3598					*probe_string = NULL;
3599				}
3600
3601				if (matched_probe_string) {
3602					*probe_string = CFRetain(matched_probe_string);
3603				}
3604			}
3605
3606			result = trigger;
3607
3608			/* If retry was requested, or we found Always match, trigger now */
3609			if (onDemandRetry || !usedOnDemandRetry) {
3610				if (triggerNow != NULL) {
3611					*triggerNow = TRUE;
3612				}
3613				break;
3614			}
3615
3616			/* If we matched the Retry list, but Always was requested,
3617			 keep going through triggers in case one matches an Always */
3618		}
3619	}
3620
3621	if (result) {
3622		CFRetain(result);
3623	}
3624
3625	_SCErrorSet(sc_status);
3626	return result;
3627}
3628
3629
3630static CFDictionaryRef
3631__SCNetworkConnectionCopyTriggerWithService(CFDictionaryRef	configuration,
3632					    CFStringRef		service_id)
3633{
3634	CFArrayRef	triggers;
3635	uint64_t	triggersCount	= 0;
3636	int		triggersIndex;
3637
3638	triggers = CFDictionaryGetValue(configuration, kSCNetworkConnectionOnDemandTriggers);
3639	triggersCount = isA_CFArray(triggers) ? CFArrayGetCount(triggers) : 0;
3640	for (triggersIndex = 0; triggersIndex < triggersCount; triggersIndex++) {
3641		CFDictionaryRef	trigger;
3642		CFStringRef	trigger_service_id;
3643
3644		trigger = CFArrayGetValueAtIndex(triggers, triggersIndex);
3645		if (!isA_CFDictionary(trigger)) {
3646			// if not a valid "OnDemand" configuration
3647			continue;
3648		}
3649
3650		trigger_service_id = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandServiceID);
3651		if (isA_CFString(trigger_service_id) && CFEqual(trigger_service_id, service_id)) {
3652			CFRetain(trigger);
3653			return trigger;
3654		}
3655	}
3656
3657	return NULL;
3658}
3659
3660
3661__private_extern__ CFDictionaryRef
3662__SCNetworkConnectionCopyTokenParameters(SCNetworkConnectionRef connection)
3663{
3664	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
3665	CFDictionaryRef			parameters		= NULL;
3666	uint8_t     			params_buffer[PPP_MACH_MAX_INLINE_DATA];
3667	uint32_t    			params_buffer_len	= sizeof(params_buffer);
3668	int				sc_status		= kSCStatusOK;
3669	mach_port_t			session_port;
3670	kern_return_t			status;
3671
3672	pthread_mutex_lock(&connectionPrivate->lock);
3673
3674	parameters = connectionPrivate->flow_divert_token_params;
3675	if (parameters != NULL) {
3676	    CFRetain(parameters);
3677	    goto done;
3678	}
3679
3680retry:
3681	if (parameters != NULL) {
3682		CFRelease(parameters);
3683		parameters = NULL;
3684	}
3685
3686	session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
3687	if (session_port == MACH_PORT_NULL) {
3688		goto done;
3689	}
3690
3691	status = pppcontroller_flow_divert_copy_token_parameters(session_port, params_buffer, &params_buffer_len);
3692	if (status == KERN_SUCCESS) {
3693		if (params_buffer_len > 0) {
3694			CFDataRef params_data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
3695									    params_buffer,
3696									    params_buffer_len,
3697									    kCFAllocatorNull);
3698			parameters = CFPropertyListCreateWithData(kCFAllocatorDefault,
3699								  params_data,
3700								  kCFPropertyListImmutable,
3701								  NULL,
3702								  NULL);
3703			CFRelease(params_data);
3704		}
3705	}
3706
3707	if (__SCNetworkConnectionNeedsRetry(connection, "__SCNetworkConnectionCopyTokenParameters()", status, &sc_status)) {
3708		goto retry;
3709	}
3710
3711	if (sc_status != kSCStatusOK) {
3712		_SCErrorSet(sc_status);
3713	}
3714
3715done:
3716	if (parameters != NULL && connectionPrivate->flow_divert_token_params == NULL) {
3717	    connectionPrivate->flow_divert_token_params = (CFDictionaryRef)CFRetain(parameters);
3718	}
3719
3720	pthread_mutex_unlock(&connectionPrivate->lock);
3721
3722	return parameters;
3723}
3724
3725Boolean
3726__SCNetworkConnectionCopyOnDemandInfoWithName(SCDynamicStoreRef		*storeP,
3727					      CFStringRef		hostName,
3728					      Boolean			onDemandRetry,
3729					      CFStringRef		*connectionServiceID,
3730					      SCNetworkConnectionStatus	*connectionStatus,
3731					      CFStringRef		*vpnRemoteAddress)	/*  CFDictionaryRef *info */
3732{
3733	CFDictionaryRef		configuration;
3734	Boolean			ok		= FALSE;
3735	int			sc_status	= kSCStatusOK;
3736	CFDictionaryRef		trigger;
3737	Boolean			trigger_now	= FALSE;
3738
3739	configuration = __SCNetworkConnectionCopyOnDemandConfiguration();
3740	if (configuration == NULL) {
3741		_SCErrorSet(sc_status);
3742		return ok;
3743	}
3744
3745	trigger = __SCNetworkConnectionCopyMatchingTriggerWithName(configuration, hostName, 0, onDemandRetry, NULL, &trigger_now, NULL);
3746	if (trigger != NULL && trigger_now) {
3747		CFNumberRef			num;
3748		SCNetworkConnectionStatus	onDemandStatus	= kSCNetworkConnectionDisconnected;
3749
3750		ok = TRUE;
3751
3752		if (!CFDictionaryGetValueIfPresent(trigger, kSCNetworkConnectionOnDemandStatus, (const void **)&num) ||
3753		    !isA_CFNumber(num) ||
3754		    !CFNumberGetValue(num, kCFNumberSInt32Type, &onDemandStatus)) {
3755			onDemandStatus = kSCNetworkConnectionDisconnected;
3756		}
3757		if (connectionStatus != NULL) {
3758			*connectionStatus = onDemandStatus;
3759		}
3760
3761		if (connectionServiceID != NULL) {
3762			*connectionServiceID = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandServiceID);
3763			*connectionServiceID = isA_CFString(*connectionServiceID);
3764			if ((*connectionServiceID != NULL) && (CFStringGetLength(*connectionServiceID) > 0)) {
3765				CFRetain(*connectionServiceID);
3766			} else {
3767				SCLog(TRUE, LOG_INFO, CFSTR("OnDemand%s configuration error, no serviceID"),
3768				      onDemandRetry ? " (on retry)" : "");
3769				*connectionServiceID = NULL;
3770				ok = FALSE;
3771			}
3772		}
3773
3774		if (vpnRemoteAddress != NULL) {
3775			*vpnRemoteAddress = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandRemoteAddress);
3776			*vpnRemoteAddress = isA_CFString(*vpnRemoteAddress);
3777			if ((*vpnRemoteAddress != NULL) && (CFStringGetLength(*vpnRemoteAddress) > 0)) {
3778				CFRetain(*vpnRemoteAddress);
3779			} else {
3780				SCLog(TRUE, LOG_INFO, CFSTR("OnDemand%s configuration error, no server address"),
3781				      onDemandRetry ? " (on retry)" : "");
3782				*vpnRemoteAddress = NULL;
3783				ok = FALSE;
3784			}
3785		}
3786
3787		if (!ok) {
3788			if ((connectionServiceID != NULL) && (*connectionServiceID != NULL)) {
3789				CFRelease(*connectionServiceID);
3790				*connectionServiceID = NULL;
3791			}
3792			if ((vpnRemoteAddress != NULL) && (*vpnRemoteAddress != NULL)) {
3793				CFRelease(*vpnRemoteAddress);
3794				*vpnRemoteAddress = NULL;
3795			}
3796			sc_status = kSCStatusFailed;
3797		} else {
3798			if (_sc_debug || (debug > 0)) {
3799				SCLog(TRUE, LOG_INFO, CFSTR("OnDemand%s match, connection status = %d"),
3800				      onDemandRetry ? " (on retry)" : "",
3801				      onDemandStatus);
3802			}
3803		}
3804	}
3805
3806	if (trigger) {
3807		CFRelease(trigger);
3808	}
3809
3810//	if (_sc_debug || (debug > 0)) {
3811//		SCLog(TRUE, LOG_INFO, CFSTR("OnDemand domain name(s) not matched"));
3812//	}
3813
3814	if (configuration != NULL) CFRelease(configuration);
3815	if (!ok) {
3816		_SCErrorSet(sc_status);
3817	}
3818	return ok;
3819}
3820
3821static Boolean
3822__SCNetworkConnectionCopyUserPreferencesInternal(CFDictionaryRef	selectionOptions,
3823						 CFStringRef		*serviceID,
3824						 CFDictionaryRef	*userOptions)
3825{
3826	int		prefsChanged	= 1;
3827	int		status;
3828	Boolean		success		= FALSE;
3829
3830	if (notify_userprefs_token == -1) {
3831		status = notify_register_check(k_NetworkConnect_Notification, &notify_userprefs_token);
3832		if (status != NOTIFY_STATUS_OK) {
3833			SCLog(TRUE, LOG_ERR, CFSTR("notify_register_check() failed, status=%d"), status);
3834			(void)notify_cancel(notify_userprefs_token);
3835			notify_userprefs_token = -1;
3836		} else {
3837			// clear the "something has changed" state
3838			(void) notify_check(notify_userprefs_token, &prefsChanged);
3839			prefsChanged = 1;
3840		}
3841	}
3842	if (notify_userprefs_token != -1) {
3843		status = notify_check(notify_userprefs_token, &prefsChanged);
3844		if (status != NOTIFY_STATUS_OK) {
3845			SCLog(TRUE, LOG_ERR, CFSTR("notify_check() failed, status=%d"), status);
3846			(void)notify_cancel(notify_userprefs_token);
3847			notify_userprefs_token = -1;
3848		}
3849	}
3850
3851
3852	*serviceID = NULL;
3853	*userOptions = NULL;
3854
3855	if (selectionOptions != NULL) {
3856		Boolean		catchAllFound	= FALSE;
3857		CFIndex		catchAllService	= 0;
3858		CFIndex		catchAllConfig	= 0;
3859		CFStringRef	hostName	= NULL;
3860		CFStringRef	priority	= NULL;
3861		CFArrayRef	serviceNames	= NULL;
3862		CFDictionaryRef	services	= NULL;
3863		CFIndex		serviceIndex;
3864		CFIndex		servicesCount;
3865
3866		hostName = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName);
3867		if (hostName == NULL) {
3868			hostName = CFDictionaryGetValue(selectionOptions, kSCPropNetPPPOnDemandHostName);
3869		}
3870		hostName = isA_CFString(hostName);
3871		if (hostName == NULL)
3872			goto done_selection;	// if no hostname for matching
3873
3874		priority = CFDictionaryGetValue(selectionOptions, kSCPropNetPPPOnDemandPriority);
3875		if (!isA_CFString(priority))
3876			priority = kSCValNetPPPOnDemandPriorityDefault;
3877
3878
3879		if (!isA_CFArray(serviceNames))
3880			goto done_selection;
3881
3882
3883		if (!isA_CFDictionary(services)) {
3884			goto done_selection;
3885		}
3886
3887		servicesCount = CFArrayGetCount(serviceNames);
3888		for (serviceIndex = 0; serviceIndex < servicesCount; serviceIndex++) {
3889			CFIndex		configIndex;
3890			CFIndex		configsCount;
3891			CFArrayRef	serviceConfigs;
3892			CFStringRef	serviceName;
3893			int		val;
3894
3895			serviceName = CFArrayGetValueAtIndex(serviceNames, serviceIndex);
3896			if (!isA_CFString(serviceName)) {
3897				continue;
3898			}
3899
3900			serviceConfigs = CFDictionaryGetValue(services, serviceName);
3901			if (!isA_CFArray(serviceConfigs)) {
3902				continue;
3903			}
3904
3905			configsCount = CFArrayGetCount(serviceConfigs);
3906			for (configIndex = 0; configIndex < configsCount; configIndex++) {
3907				CFNumberRef	autodial;
3908				CFDictionaryRef config;
3909				CFDictionaryRef pppConfig;
3910
3911				config = CFArrayGetValueAtIndex(serviceConfigs, configIndex);
3912				if (!isA_CFDictionary(config)) {
3913					continue;
3914				}
3915
3916				pppConfig = CFDictionaryGetValue(config, kSCEntNetPPP);
3917				if (!isA_CFDictionary(pppConfig)) {
3918					continue;
3919				}
3920
3921				autodial = CFDictionaryGetValue(pppConfig, kSCPropNetPPPOnDemandEnabled);
3922				if (!isA_CFNumber(autodial)) {
3923					continue;
3924				}
3925
3926				CFNumberGetValue(autodial, kCFNumberIntType, &val);
3927				if (val) {
3928					CFArrayRef	domains;
3929					CFIndex		domainsCount;
3930					CFIndex		domainsIndex;
3931
3932					/* we found an conditional connection enabled configuration */
3933
3934					/* check domain */
3935					domains = CFDictionaryGetValue(pppConfig, kSCPropNetPPPOnDemandDomains);
3936					if (!isA_CFArray(domains)) {
3937						continue;
3938					}
3939
3940					domainsCount = CFArrayGetCount(domains);
3941					for (domainsIndex = 0; domainsIndex < domainsCount; domainsIndex++) {
3942						CFStringRef	domain;
3943
3944						domain = CFArrayGetValueAtIndex(domains, domainsIndex);
3945						if (!isA_CFString(domain)) {
3946							continue;
3947						}
3948
3949						if (!catchAllFound &&
3950						    (CFStringCompare(domain, CFSTR(""), 0) == kCFCompareEqualTo
3951							|| CFStringCompare(domain, CFSTR("."), 0) == kCFCompareEqualTo))
3952						{
3953							// found a catch all
3954							catchAllFound = TRUE;
3955							catchAllService = serviceIndex;
3956							catchAllConfig = configIndex;
3957						}
3958
3959						if (_SC_domainEndsWithDomain(hostName, domain)) {
3960							// found matching configuration
3961							*serviceID = serviceName;
3962							CFRetain(*serviceID);
3963							*userOptions = CFDictionaryCreateMutableCopy(NULL, 0, config);
3964							CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, hostName);
3965							CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCPropNetPPPOnDemandPriority, priority);
3966							addPasswordFromKeychain(*serviceID, userOptions);
3967							success = TRUE;
3968							goto done_selection;
3969						}
3970					}
3971				}
3972			}
3973		}
3974
3975		// config not found, do we have a catchall ?
3976		if (catchAllFound) {
3977			CFDictionaryRef config;
3978			CFArrayRef	serviceConfigs;
3979			CFStringRef	serviceName;
3980
3981			serviceName = CFArrayGetValueAtIndex(serviceNames, catchAllService);
3982			serviceConfigs = CFDictionaryGetValue(services, serviceName);
3983			config = CFArrayGetValueAtIndex(serviceConfigs, catchAllConfig);
3984
3985			*serviceID = serviceName;
3986			CFRetain(*serviceID);
3987			*userOptions = CFDictionaryCreateMutableCopy(NULL, 0, config);
3988			CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, hostName);
3989			CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCPropNetPPPOnDemandPriority, priority);
3990			addPasswordFromKeychain(*serviceID, userOptions);
3991			success = TRUE;
3992			goto done_selection;
3993		}
3994
3995	    done_selection:
3996
3997		if (serviceNames) {
3998			CFRelease(serviceNames);
3999		}
4000		if (services) {
4001			CFRelease(services);
4002		}
4003
4004		if (debug > 1) {
4005			SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionCopyUserPreferences %@"), success ? CFSTR("succeeded") : CFSTR("failed"));
4006			SCLog(TRUE, LOG_DEBUG, CFSTR("Selection options: %@"), selectionOptions);
4007		}
4008
4009		return success;
4010	}
4011
4012	/* we don't have selection options */
4013
4014	// (1) Figure out which service ID we care about, allocate it into passed "serviceID"
4015	success = SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(serviceID);
4016
4017	if (success && (*serviceID != NULL)) {
4018		// (2) Get the list of user data for this service ID
4019		CFPropertyListRef	userServices	= NULL;
4020
4021
4022		// (3) We are expecting an array if the user has defined records for this service ID or NULL if the user hasn't
4023		if (userServices != NULL) {
4024			if (isA_CFArray(userServices)) {
4025				// (4) Get the default set of user options for this service
4026				success = SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray((CFArrayRef)userServices,
4027												    userOptions);
4028				if(success && (userOptions != NULL)) {
4029					addPasswordFromKeychain(*serviceID, userOptions);
4030				}
4031			} else {
4032				SCLog(TRUE, LOG_DEBUG, CFSTR("Error, userServices are not of type CFArray!"));
4033			}
4034
4035			CFRelease(userServices); // this is OK because SCNetworkConnectionPrivateISExpectedCFType() checks for NULL
4036		}
4037	}
4038
4039	if (debug > 1) {
4040		SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionCopyUserPreferences %@, no selection options"), success ? CFSTR("succeeded") : CFSTR("failed"));
4041	}
4042
4043	return success;
4044}
4045
4046
4047Boolean
4048SCNetworkConnectionCopyUserPreferences(CFDictionaryRef	selectionOptions,
4049				       CFStringRef	*serviceID,
4050				       CFDictionaryRef	*userOptions)
4051{
4052	Boolean	success	= FALSE;
4053
4054
4055	/* initialize runtime */
4056	pthread_once(&initialized, __SCNetworkConnectionInitialize);
4057
4058	/* first check for new VPN OnDemand style */
4059	if (selectionOptions != NULL) {
4060		CFStringRef	hostName;
4061
4062		hostName = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName);
4063		if (isA_CFString(hostName)) {
4064			CFStringRef			connectionServiceID	= NULL;
4065			SCNetworkConnectionStatus	connectionStatus	= kSCNetworkConnectionInvalid;
4066			Boolean				onDemandRetry;
4067			CFTypeRef			val;
4068
4069			val = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandRetry);
4070			onDemandRetry = isA_CFBoolean(val) ? CFBooleanGetValue(val) : TRUE;
4071
4072			success = __SCNetworkConnectionCopyOnDemandInfoWithName(NULL,
4073										hostName,
4074										onDemandRetry,
4075										&connectionServiceID,
4076										&connectionStatus,
4077										NULL);
4078			if (debug > 1) {
4079				SCLog(TRUE, LOG_DEBUG,
4080				      CFSTR("SCNetworkConnectionCopyUserPreferences __SCNetworkConnectionCopyOnDemandInfoWithName returns %d w/status %d"),
4081				      success,
4082				      connectionStatus);
4083			}
4084
4085			if (success) {
4086				// if the hostname matches an OnDemand domain
4087				if (connectionStatus == kSCNetworkConnectionConnected) {
4088					// if we are already connected
4089					if (connectionServiceID != NULL) {
4090						CFRelease(connectionServiceID);
4091					}
4092					return FALSE;
4093				}
4094
4095				*serviceID   = connectionServiceID;
4096				*userOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
4097				CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, hostName);
4098				return TRUE;
4099			} else if (!onDemandRetry) {
4100				// if the hostname does not match an OnDemand domain and we have
4101				// not yet issued an initial DNS query (i.e. it's not a query
4102				// being retried after the VPN has been established) then we're
4103				// done
4104				return FALSE;
4105			}
4106		}
4107	}
4108
4109	return __SCNetworkConnectionCopyUserPreferencesInternal(selectionOptions, serviceID, userOptions);
4110}
4111
4112
4113Boolean
4114SCNetworkConnectionOnDemandShouldRetryOnFailure (SCNetworkConnectionRef connection)
4115{
4116	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
4117	CFDictionaryRef			match_info		= NULL;
4118
4119	if (!isA_SCNetworkConnection(connection)) {
4120		_SCErrorSet(kSCStatusInvalidArgument);
4121		goto fail;
4122	}
4123
4124	if (connectionPrivate->service == NULL) {
4125		_SCErrorSet(kSCStatusConnectionNoService);
4126		goto fail;
4127	}
4128
4129	if (isA_CFDictionary(connectionPrivate->on_demand_user_options)) {
4130		match_info = CFDictionaryGetValue(connectionPrivate->on_demand_user_options, kSCNetworkConnectionSelectionOptionOnDemandMatchInfo);
4131		if (isA_CFDictionary(match_info)) {
4132			CFBooleanRef onRetry = CFDictionaryGetValue(match_info, kSCNetworkConnectionOnDemandMatchInfoOnRetry);
4133			if (isA_CFBoolean(onRetry)) {
4134				return CFBooleanGetValue(onRetry);
4135			}
4136		}
4137	}
4138
4139    fail:
4140	return FALSE;
4141}
4142
4143
4144// Mask is optional in routes dictionary; if not present, whole addresses are matched
4145Boolean
4146__SCNetworkConnectionIPv4AddressMatchesRoutes (struct sockaddr_in *addr_in, CFDictionaryRef routes)
4147{
4148	CFIndex		count;
4149	CFIndex		i;
4150	CFDataRef	maskData	= NULL;
4151	struct in_addr	*maskDataArray;
4152	CFDataRef	routeaddrData	= NULL;
4153	struct in_addr	*routeaddrDataArray;
4154
4155	if (!isA_CFDictionary(routes)) {
4156		return FALSE;
4157	}
4158
4159	routeaddrData = CFDictionaryGetValue(routes, kSCNetworkConnectionNetworkInfoAddresses);
4160	maskData = CFDictionaryGetValue(routes, kSCNetworkConnectionNetworkInfoMasks);
4161
4162	/* routeaddrData and maskData are packed arrays of addresses; make sure they have the same length */
4163	if (!isA_CFData(routeaddrData) || (maskData && (!isA_CFData(maskData) || CFDataGetLength(routeaddrData) != CFDataGetLength(maskData)))) {
4164		return FALSE;
4165	}
4166
4167	routeaddrDataArray = (struct in_addr*)(void*)CFDataGetBytePtr(routeaddrData);
4168	if (maskData) {
4169		maskDataArray = (struct in_addr*)(void*)CFDataGetBytePtr(maskData);
4170	}
4171
4172	count = CFDataGetLength(routeaddrData) / sizeof(struct in_addr);
4173	for (i=0; i<count; i++) {
4174		struct in_addr	routeAddr	= *routeaddrDataArray;
4175
4176		if (maskData) {
4177			struct in_addr	mask	= *maskDataArray;
4178
4179			if ((addr_in->sin_addr.s_addr & mask.s_addr) == (routeAddr.s_addr & mask.s_addr)) {
4180				return TRUE;
4181			}
4182			maskDataArray++;
4183		} else {
4184			if (addr_in->sin_addr.s_addr == routeAddr.s_addr) {
4185				return TRUE;
4186			}
4187		}
4188		routeaddrDataArray++;
4189	}
4190	return FALSE;
4191}
4192
4193
4194void
4195__SCNetworkConnectionMaskIPv6Address(struct in6_addr *addr, struct in6_addr *mask)
4196{
4197	int	i;
4198
4199	for (i = 0; i < sizeof(struct in6_addr); i++)
4200		addr->s6_addr[i] &= mask->s6_addr[i];
4201}
4202
4203
4204// Mask is optional in routes dictionary; if not present, whole addresses are matched
4205Boolean
4206__SCNetworkConnectionIPv6AddressMatchesRoutes (struct sockaddr_in6 *addr_in6, CFDictionaryRef routes)
4207{
4208	CFIndex		count;
4209	CFIndex		i;
4210	CFDataRef	maskData	= NULL;
4211	struct in6_addr	*maskDataArray;
4212	CFDataRef	routeaddrData	= NULL;
4213	struct in6_addr	*routeaddrDataArray;
4214
4215	if (!isA_CFDictionary(routes)) {
4216		return FALSE;
4217	}
4218
4219	routeaddrData = CFDictionaryGetValue(routes, kSCNetworkConnectionNetworkInfoAddresses);
4220	maskData = CFDictionaryGetValue(routes, kSCNetworkConnectionNetworkInfoMasks);
4221
4222	/* routeaddrData and maskData are packed arrays of addresses; make sure they have the same length */
4223	if (!isA_CFData(routeaddrData) || (maskData && (!isA_CFData(maskData) || CFDataGetLength(routeaddrData) != CFDataGetLength(maskData)))) {
4224		return FALSE;
4225	}
4226
4227	routeaddrDataArray = (struct in6_addr*)(void*)CFDataGetBytePtr(routeaddrData);
4228	if (maskData) {
4229		maskDataArray = (struct in6_addr*)(void*)CFDataGetBytePtr(maskData);
4230	}
4231
4232	count = CFDataGetLength(routeaddrData) / sizeof(struct in6_addr);
4233	for (i=0; i<count; i++) {
4234		if (maskData) {
4235			struct in6_addr	cmpAddr;
4236			struct in6_addr	*mask = maskDataArray;
4237			struct in6_addr	routeAddr;
4238
4239			memcpy(&routeAddr, routeaddrDataArray, sizeof(routeAddr));
4240			memcpy(&cmpAddr, &addr_in6->sin6_addr, sizeof(cmpAddr));
4241			__SCNetworkConnectionMaskIPv6Address(&routeAddr, mask);
4242			__SCNetworkConnectionMaskIPv6Address(&cmpAddr, mask);
4243			maskDataArray++;
4244			if (!memcmp(&routeAddr, &cmpAddr, sizeof(routeAddr))) {
4245				return TRUE;
4246			}
4247		} else {
4248			if (!memcmp(routeaddrDataArray, &addr_in6->sin6_addr, sizeof(struct in6_addr))) {
4249				return TRUE;
4250			}
4251		}
4252
4253		routeaddrDataArray++;
4254	}
4255	return FALSE;
4256}
4257
4258
4259static Boolean
4260__SCNetworkConnectionAddressMatchesRedirectedDNS(CFDictionaryRef trigger, const struct sockaddr *input_addr)
4261{
4262	CFBooleanRef redirectedRef = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandDNSRedirectDetected);
4263
4264	if (isA_CFBoolean(redirectedRef) && CFBooleanGetValue(redirectedRef)) {
4265		/* DNS is redirected. Look for address list. */
4266		CFDictionaryRef redirectedAddressesRef = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandDNSRedirectedAddresses);
4267
4268		if (isA_CFDictionary(redirectedAddressesRef)) {
4269			if (input_addr->sa_family == AF_INET) {
4270				return __SCNetworkConnectionIPv4AddressMatchesRoutes((struct sockaddr_in*)(void*)input_addr, CFDictionaryGetValue(redirectedAddressesRef, kSCNetworkConnectionNetworkInfoIPv4));
4271			} else if (input_addr->sa_family == AF_INET6) {
4272				return __SCNetworkConnectionIPv6AddressMatchesRoutes((struct sockaddr_in6*)(void*)input_addr, CFDictionaryGetValue(redirectedAddressesRef, kSCNetworkConnectionNetworkInfoIPv6));
4273			}
4274		}
4275	}
4276
4277	return FALSE;
4278}
4279
4280/* If the required probe has failed, we need to tunnel the address. Equivalent to redirected DNS. */
4281static Boolean
4282__SCNetworkConnectionRequiredProbeFailed (CFDictionaryRef trigger, CFStringRef probeString)
4283{
4284	CFDictionaryRef probeResults = NULL;
4285
4286	if (!isA_CFString(probeString)) {
4287		return FALSE;
4288	}
4289
4290	probeResults = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandProbeResults);
4291	if (!isA_CFDictionary(probeResults)) {
4292		return TRUE;
4293	}
4294
4295	CFBooleanRef result = CFDictionaryGetValue(probeResults, probeString);
4296
4297	/* Only a value of kCFBooleanFalse marks the probe as failed  */
4298	return (isA_CFBoolean(result) && !CFBooleanGetValue(result));
4299}
4300
4301Boolean
4302SCNetworkConnectionCanTunnelAddress (SCNetworkConnectionRef connection, const struct sockaddr *address, Boolean *startImmediately)
4303{
4304	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
4305	CFStringRef			serviceID		= NULL;
4306	CFDictionaryRef			configuration		= NULL;
4307	CFDictionaryRef			trigger			= NULL;
4308	CFDictionaryRef			tunneledNetworks	= NULL;
4309	sa_family_t			address_family		= AF_UNSPEC;
4310	Boolean				success			= FALSE;
4311
4312	if (startImmediately) {
4313		*startImmediately = FALSE;
4314	}
4315
4316	if (address == NULL) {
4317		goto done;
4318	}
4319
4320	address_family = address->sa_family;
4321	if (address_family != AF_INET && address_family != AF_INET6) {
4322		goto done;
4323	}
4324
4325	if (!isA_SCNetworkConnection(connection)) {
4326		_SCErrorSet(kSCStatusInvalidArgument);
4327		goto done;
4328	}
4329
4330	if (connectionPrivate->service == NULL) {
4331		_SCErrorSet(kSCStatusConnectionNoService);
4332		goto done;
4333	}
4334
4335	serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service);
4336	if (!isA_CFString(serviceID)) {
4337		goto done;
4338	}
4339
4340	configuration = __SCNetworkConnectionCopyOnDemandConfiguration();
4341	if (configuration == NULL) {
4342		goto done;
4343	}
4344
4345	trigger = __SCNetworkConnectionCopyTriggerWithService(configuration, serviceID);
4346	if (trigger == NULL) {
4347		goto done;
4348	}
4349
4350	if (__SCNetworkConnectionRequiredProbeFailed(trigger, connectionPrivate->on_demand_required_probe)) {
4351		/* If probe failed, we can't trust DNS - connect now */
4352		if (startImmediately) {
4353			*startImmediately = TRUE;
4354		}
4355		success = TRUE;
4356		goto done;
4357	}
4358
4359	if (__SCNetworkConnectionAddressMatchesRedirectedDNS(trigger, address)) {
4360		if (startImmediately) {
4361			*startImmediately = TRUE;
4362		}
4363		success = TRUE;
4364		goto done;
4365	}
4366
4367	tunneledNetworks = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandTunneledNetworks);
4368	if (!isA_CFDictionary(tunneledNetworks)) {
4369		goto done;
4370	}
4371
4372	if (address_family == AF_INET) {
4373		CFDictionaryRef ip_dict;
4374		Boolean matches = FALSE;
4375		struct sockaddr_in *addr_in = (struct sockaddr_in *)(void*)address;
4376
4377		ip_dict = CFDictionaryGetValue(tunneledNetworks, kSCNetworkConnectionNetworkInfoIPv4);
4378		if (!isA_CFDictionary(ip_dict)) {
4379			goto done;
4380		}
4381
4382		matches = __SCNetworkConnectionIPv4AddressMatchesRoutes(addr_in, CFDictionaryGetValue(ip_dict, kSCNetworkConnectionNetworkInfoIncludedRoutes));
4383
4384		if (matches) {
4385			if (!__SCNetworkConnectionIPv4AddressMatchesRoutes(addr_in, CFDictionaryGetValue(ip_dict, kSCNetworkConnectionNetworkInfoExcludedRoutes))) {
4386				success = TRUE;
4387				goto done;
4388			}
4389		}
4390	} else {
4391		CFDictionaryRef ip6_dict;
4392		Boolean matches = FALSE;
4393		struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)(void*)address;
4394
4395		ip6_dict = CFDictionaryGetValue(tunneledNetworks, kSCNetworkConnectionNetworkInfoIPv6);
4396		if (!isA_CFDictionary(ip6_dict)) {
4397			goto done;
4398		}
4399
4400		matches = __SCNetworkConnectionIPv6AddressMatchesRoutes(addr_in6, CFDictionaryGetValue(ip6_dict, kSCNetworkConnectionNetworkInfoIncludedRoutes));
4401
4402		if (matches) {
4403			if (!__SCNetworkConnectionIPv6AddressMatchesRoutes(addr_in6, CFDictionaryGetValue(ip6_dict, kSCNetworkConnectionNetworkInfoExcludedRoutes))) {
4404				success = TRUE;
4405				goto done;
4406			}
4407		}
4408	}
4409done:
4410	if (configuration) {
4411		CFRelease(configuration);
4412	}
4413	if (trigger) {
4414		CFRelease(trigger);
4415	}
4416	return success;
4417}
4418
4419Boolean
4420SCNetworkConnectionSelectServiceWithOptions(SCNetworkConnectionRef connection, CFDictionaryRef selectionOptions)
4421{
4422	CFStringRef			account_identifier	= NULL;
4423	CFDictionaryRef			configuration		= NULL;
4424	SCNetworkConnectionPrivateRef	connectionPrivate	= (SCNetworkConnectionPrivateRef)connection;
4425	CFDictionaryRef			found_trigger		= NULL;
4426	CFStringRef			host_name		= NULL;
4427	Boolean				is_retry		= TRUE;
4428	CFDictionaryRef			match_info		= NULL;
4429	CFMutableDictionaryRef		new_user_options	= NULL;
4430	SCNetworkConnectionStatus	on_demand_status	= kSCNetworkConnectionInvalid;
4431	CFStringRef			requiredProbe		= NULL;
4432	CFStringRef			service_id		= NULL;
4433	Boolean				skip_prefs		= FALSE;
4434	Boolean				success			= TRUE;
4435	CFDictionaryRef			user_options		= NULL;
4436
4437	if (!isA_SCNetworkConnection(connection)) {
4438		_SCErrorSet(kSCStatusInvalidArgument);
4439		success = FALSE;
4440		goto done;
4441	}
4442
4443	/* Can't call this on a connection that is already associated with a service */
4444	if (connectionPrivate->service != NULL) {
4445		_SCErrorSet(kSCStatusInvalidArgument);
4446		success = FALSE;
4447		goto done;
4448	}
4449
4450	if (isA_CFDictionary(selectionOptions)) {
4451		CFBooleanRef no_user_prefs = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionNoUserPrefs);
4452		CFBooleanRef retry = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandRetry);
4453
4454		account_identifier = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandAccountIdentifier);
4455		host_name = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName);
4456		skip_prefs = (isA_CFBoolean(no_user_prefs) && CFBooleanGetValue(no_user_prefs));
4457
4458		if (isA_CFBoolean(retry)) {
4459			is_retry = CFBooleanGetValue(retry);
4460		}
4461	}
4462
4463	configuration = __SCNetworkConnectionCopyOnDemandConfiguration();
4464
4465	/* First, check for a match with the App Layer rules */
4466	service_id = VPNAppLayerCopyMatchingService(connectionPrivate->client_audit_token,
4467						    connectionPrivate->client_pid,
4468						    connectionPrivate->client_uuid,
4469						    connectionPrivate->client_bundle_id,
4470						    host_name,
4471						    account_identifier,
4472						    &match_info);
4473	if (service_id != NULL) {
4474		Boolean	use_app_layer	= TRUE;
4475
4476		if (isA_CFDictionary(configuration)) {
4477			found_trigger = __SCNetworkConnectionCopyTriggerWithService(configuration, service_id);
4478			if (found_trigger != NULL) {
4479				CFNumberRef status_num = CFDictionaryGetValue(found_trigger, kSCNetworkConnectionOnDemandStatus);
4480				if (isA_CFNumber(status_num)) {
4481					CFNumberGetValue(status_num, kCFNumberIntType, &on_demand_status);
4482				}
4483				/*
4484				 * If the trigger should be ignored, still use App Layer VPN if it is already connected or
4485				 * is in the process of connecting.
4486				 */
4487				if (__SCNetworkConnectionShouldIgnoreTrigger(found_trigger) &&
4488				    on_demand_status != kSCNetworkConnectionConnecting &&
4489				    on_demand_status != kSCNetworkConnectionConnected)
4490				{
4491					use_app_layer = FALSE;
4492				}
4493			}
4494		}
4495
4496		if (use_app_layer) {
4497			/* If this is not the 'OnRetry' call, and the service has not yet started, the match may need to return false */
4498			if (!is_retry &&
4499			    match_info != NULL &&
4500			    on_demand_status != kSCNetworkConnectionConnecting &&
4501			    on_demand_status != kSCNetworkConnectionConnected) {
4502				CFBooleanRef matchedOnRetry = CFDictionaryGetValue(match_info, kSCNetworkConnectionOnDemandMatchInfoOnRetry);
4503				if (matchedOnRetry && CFBooleanGetValue(matchedOnRetry)) {
4504					/* Don't return that we matched always; wait for SCNetworkConnectionOnDemandShouldRetryOnFailure */
4505					success = FALSE;
4506				}
4507			}
4508			connectionPrivate->type = kSCNetworkConnectionTypeAppLayerVPN;
4509			goto search_done;
4510		} else {
4511			CFRelease(service_id);
4512			service_id = NULL;
4513			if (match_info != NULL) {
4514				CFRelease(match_info);
4515				match_info = NULL;
4516			}
4517			if (found_trigger != NULL) {
4518				CFRelease(found_trigger);
4519				found_trigger = NULL;
4520			}
4521		}
4522	}
4523
4524	/* Next, check the IP layer rules */
4525	if (isA_CFDictionary(configuration) && host_name != NULL) {
4526		Boolean	triggerNow	= FALSE;
4527
4528		found_trigger = __SCNetworkConnectionCopyMatchingTriggerWithName(configuration, host_name, connectionPrivate->client_pid, is_retry, &match_info, &triggerNow, &requiredProbe);
4529		if (found_trigger != NULL) {
4530			service_id = CFDictionaryGetValue(found_trigger, kSCNetworkConnectionOnDemandServiceID);
4531			if (isA_CFString(service_id)) {
4532				CFRetain(service_id);
4533				connectionPrivate->type = kSCNetworkConnectionTypeIPLayerVPN;
4534			} else {
4535				service_id = NULL;
4536			}
4537			if (!triggerNow) {
4538				success = FALSE;
4539			}
4540			goto search_done;
4541		} else if (!is_retry) {
4542			goto search_done;
4543		}
4544
4545		if (match_info != NULL) {
4546			CFRelease(match_info);
4547			match_info = NULL;
4548		}
4549	}
4550
4551	/* Next, check the user preferences */
4552	if (!skip_prefs && __SCNetworkConnectionCopyUserPreferencesInternal(selectionOptions, &service_id, &user_options)) {
4553		CFMutableDictionaryRef	minfo;
4554		CFNumberRef		type_num;
4555
4556		if (isA_CFDictionary(configuration)) {
4557			found_trigger = __SCNetworkConnectionCopyTriggerWithService(configuration, service_id);
4558		}
4559		connectionPrivate->type = kSCNetworkConnectionTypePPP;
4560
4561		minfo = CFDictionaryCreateMutable(NULL,
4562						  0,
4563						  &kCFTypeDictionaryKeyCallBacks,
4564						  &kCFTypeDictionaryValueCallBacks);
4565		type_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &connectionPrivate->type);
4566		CFDictionarySetValue(minfo, kSCNetworkConnectionOnDemandMatchInfoVPNType, type_num);
4567		CFRelease(type_num);
4568		match_info = minfo;
4569		goto search_done;
4570	}
4571
4572    search_done:
4573	if (service_id == NULL) {
4574		_SCErrorSet(kSCStatusOK);
4575		success = FALSE;
4576		goto done;
4577	}
4578
4579	connectionPrivate->service = _SCNetworkServiceCopyActive(NULL, service_id);
4580	if (connectionPrivate->service == NULL) {
4581		_SCErrorSet(kSCStatusOK);
4582		success = FALSE;
4583		goto done;
4584	}
4585
4586	if (found_trigger != NULL) {
4587		if (connectionPrivate->on_demand_info) {
4588		      CFRelease(connectionPrivate->on_demand_info);
4589		}
4590		connectionPrivate->on_demand_info = found_trigger;
4591		CFRetain(connectionPrivate->on_demand_info);
4592
4593		if (on_demand_status == kSCNetworkConnectionInvalid) {
4594			CFNumberRef status_num = CFDictionaryGetValue(found_trigger, kSCNetworkConnectionOnDemandStatus);
4595			if (isA_CFNumber(status_num)) {
4596				CFNumberGetValue(status_num, kCFNumberIntType, &on_demand_status);
4597			}
4598		}
4599
4600		if (on_demand_status != kSCNetworkConnectionConnected) {
4601			if (connectionPrivate->type == kSCNetworkConnectionTypeAppLayerVPN) {
4602				/* Check App Layer OnDemand flag */
4603				CFBooleanRef app_on_demand_enabled =
4604					CFDictionaryGetValue(found_trigger, kSCNetworkConnectionOnDemandMatchAppEnabled);
4605				if (isA_CFBoolean(app_on_demand_enabled) && CFBooleanGetValue(app_on_demand_enabled)) {
4606					connectionPrivate->on_demand = TRUE;
4607				}
4608			} else {
4609				connectionPrivate->on_demand = TRUE;
4610			}
4611		}
4612	} else if (connectionPrivate->type == kSCNetworkConnectionTypePPP) {
4613		/* If we got the service from __SCNetworkConnectionCopyUserPreferencesInternal, then it's on demand */
4614		connectionPrivate->on_demand = TRUE;
4615	}
4616
4617	if (user_options == NULL) {
4618		new_user_options = CFDictionaryCreateMutable(kCFAllocatorDefault,
4619							     0,
4620							     &kCFTypeDictionaryKeyCallBacks,
4621							     &kCFTypeDictionaryValueCallBacks);
4622	} else {
4623		new_user_options = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, user_options);
4624	}
4625
4626	if (host_name != NULL) {
4627		CFDictionarySetValue(new_user_options, kSCNetworkConnectionSelectionOptionOnDemandHostName, host_name);
4628	}
4629
4630	if (connectionPrivate->on_demand && match_info != NULL) {
4631		CFDictionarySetValue(new_user_options, kSCNetworkConnectionSelectionOptionOnDemandMatchInfo, match_info);
4632	}
4633
4634	connectionPrivate->on_demand_user_options = new_user_options;
4635	CFRetain(connectionPrivate->on_demand_user_options);
4636
4637	if (requiredProbe) {
4638		connectionPrivate->on_demand_required_probe = requiredProbe;
4639		CFRetain(connectionPrivate->on_demand_required_probe);
4640	}
4641
4642done:
4643	if (service_id != NULL) {
4644		CFRelease(service_id);
4645	}
4646
4647	if (configuration != NULL) {
4648		CFRelease(configuration);
4649	}
4650
4651	if (found_trigger != NULL) {
4652		CFRelease(found_trigger);
4653	}
4654
4655	if (user_options != NULL) {
4656		CFRelease(user_options);
4657	}
4658
4659	if (new_user_options != NULL) {
4660		CFRelease(new_user_options);
4661	}
4662
4663	if (match_info != NULL) {
4664		CFRelease(match_info);
4665	}
4666
4667	if (requiredProbe != NULL) {
4668		CFRelease(requiredProbe);
4669	}
4670
4671	return success;
4672}
4673
4674//*******************************************************************************************
4675// SCNetworkConnectionPrivateCopyDefaultServiceIDForDial
4676// ----------------------------------------------------
4677// Try to find the service id to connect
4678// (1) Start by looking at the last service used in Network Pref / Network menu extra
4679// (2) If Network Pref / Network menu extra has not been used, find the PPP service
4680//     with the highest ordering
4681//********************************************************************************************
4682static Boolean
4683SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(CFStringRef *serviceID)
4684{
4685	Boolean			foundService		= FALSE;
4686	CFPropertyListRef	lastServiceSelectedInIC = NULL;
4687
4688
4689
4690	// we found the service the user last had open in IC
4691	if (lastServiceSelectedInIC != NULL) {
4692		// make sure its a PPP service
4693		if (SCNetworkConnectionPrivateIsPPPService(lastServiceSelectedInIC, kSCValNetInterfaceSubTypePPPSerial, kSCValNetInterfaceSubTypePPPoE)) {
4694			// make sure the service that we found is valid
4695			CFDictionaryRef	dict;
4696			CFStringRef	key;
4697
4698			key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
4699									  kSCDynamicStoreDomainSetup,
4700									  lastServiceSelectedInIC,
4701									  kSCEntNetInterface);
4702			dict = SCDynamicStoreCopyValue(NULL, key);
4703			CFRelease(key);
4704			if (dict != NULL) {
4705				CFRelease(dict);
4706				*serviceID = CFRetain(lastServiceSelectedInIC);
4707				foundService = TRUE;
4708			}
4709		}
4710		CFRelease(lastServiceSelectedInIC);
4711	}
4712
4713	if (!foundService) {
4714		foundService = SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(serviceID);
4715	}
4716
4717	return foundService;
4718}
4719
4720//********************************************************************************
4721// SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore
4722// -------------------------------------------------------
4723// Find the highest ordered PPP service in the dynamic store
4724//********************************************************************************
4725static Boolean
4726SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(CFStringRef *serviceID)
4727{
4728	CFDictionaryRef	dict		= NULL;
4729	CFStringRef	key		= NULL;
4730	CFArrayRef	serviceIDs	= NULL;
4731	Boolean		success		= FALSE;
4732
4733	*serviceID = NULL;
4734
4735	do {
4736		CFIndex count;
4737		CFIndex i;
4738
4739		key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainSetup, kSCEntNetIPv4);
4740		if (key == NULL) {
4741			fprintf(stderr, "Error, Setup Key == NULL!\n");
4742			break;
4743		}
4744
4745		dict = SCDynamicStoreCopyValue(NULL, key);
4746		if (!isA_CFDictionary(dict)) {
4747			fprintf(stderr, "no global IPv4 entity\n");
4748			break;
4749		}
4750
4751		serviceIDs = CFDictionaryGetValue(dict, kSCPropNetServiceOrder); // array of service id's
4752		if (!isA_CFArray(serviceIDs)) {
4753			fprintf(stderr, "service order not specified\n");
4754			break;
4755		}
4756
4757		count = CFArrayGetCount(serviceIDs);
4758		for (i = 0; i < count; i++) {
4759			CFStringRef service = CFArrayGetValueAtIndex(serviceIDs, i);
4760
4761			if (SCNetworkConnectionPrivateIsPPPService(service, kSCValNetInterfaceSubTypePPPSerial, kSCValNetInterfaceSubTypePPPoE)) {
4762				*serviceID = CFRetain(service);
4763				success = TRUE;
4764				break;
4765			}
4766		}
4767	} while (FALSE);
4768
4769	if (key != NULL)	CFRelease(key);
4770	if (dict != NULL)	CFRelease(dict);
4771
4772	return success;
4773}
4774
4775//********************************************************************************
4776// SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray
4777// ---------------------------------------------------------
4778// Copy over user preferences for a particular service if they exist
4779//********************************************************************************
4780static Boolean
4781SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray, CFDictionaryRef *userOptions)
4782{
4783	CFIndex	count	= CFArrayGetCount(userOptionsArray);
4784	int	i;
4785
4786	for (i = 0; i < count; i++) {
4787		// (1) Find the dictionary
4788		CFPropertyListRef propertyList = CFArrayGetValueAtIndex(userOptionsArray, i);
4789
4790		if (isA_CFDictionary(propertyList) != NULL) {
4791			// See if there's a value for dial on demand
4792			CFPropertyListRef value;
4793
4794			value = CFDictionaryGetValue((CFDictionaryRef)propertyList, k_Dial_Default_Key);
4795			if (isA_CFBoolean(value) != NULL) {
4796				if (CFBooleanGetValue(value)) {
4797					// we found the default user options
4798					*userOptions = CFDictionaryCreateCopy(NULL,
4799									      (CFDictionaryRef)propertyList);
4800					break;
4801				}
4802			}
4803		}
4804	}
4805
4806	return TRUE;
4807}
4808
4809//********************************************************************************
4810// SCNetworkConnectionPrivateIsServiceType
4811// --------------------------------------
4812// Check and see if the service is a PPP service of the given types
4813//********************************************************************************
4814static Boolean
4815SCNetworkConnectionPrivateIsPPPService(CFStringRef serviceID, CFStringRef subType1, CFStringRef subType2)
4816{
4817	CFStringRef	entityKey;
4818	Boolean		isPPPService		= FALSE;
4819	Boolean		isMatchingSubType	= FALSE;
4820	CFDictionaryRef	serviceDict;
4821
4822	entityKey = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
4823								kSCDynamicStoreDomainSetup,
4824								serviceID,
4825								kSCEntNetInterface);
4826	if (entityKey == NULL) {
4827		return FALSE;
4828	}
4829
4830	serviceDict = SCDynamicStoreCopyValue(NULL, entityKey);
4831	if (serviceDict != NULL) {
4832		if (isA_CFDictionary(serviceDict)) {
4833			CFStringRef	type;
4834			CFStringRef	subtype;
4835
4836			type = CFDictionaryGetValue(serviceDict, kSCPropNetInterfaceType);
4837			if (isA_CFString(type)) {
4838				isPPPService = CFEqual(type, kSCValNetInterfaceTypePPP);
4839			}
4840
4841			subtype = CFDictionaryGetValue(serviceDict, kSCPropNetInterfaceSubType);
4842			if (isA_CFString(subtype)) {
4843				isMatchingSubType = CFEqual(subtype, subType1);
4844				if (!isMatchingSubType && subType2)
4845					isMatchingSubType = CFEqual(subtype, subType2);
4846			}
4847		}
4848		CFRelease(serviceDict);
4849	}
4850	CFRelease(entityKey);
4851
4852	return (isPPPService && isMatchingSubType);
4853}
4854
4855//********************************************************************************
4856// addPasswordFromKeychain
4857// --------------------------------------
4858// Get the password and shared secret out of the keychain and add
4859// them to the PPP and IPSec dictionaries
4860//********************************************************************************
4861static void
4862addPasswordFromKeychain(CFStringRef serviceID, CFDictionaryRef *userOptions)
4863{
4864	CFPropertyListRef	uniqueID;
4865	CFStringRef		password;
4866	CFStringRef		sharedsecret	= NULL;
4867
4868	/* user options must exist */
4869	if (*userOptions == NULL)
4870		return;
4871
4872	/* first, get the unique identifier used to store passwords in the keychain */
4873	uniqueID = CFDictionaryGetValue(*userOptions, k_Unique_Id_Key);
4874	if (!isA_CFString(uniqueID))
4875		return;
4876
4877	/* first, get the PPP password */
4878	password = copyPasswordFromKeychain(uniqueID);
4879
4880	/* then, if necessary, get the IPSec Shared Secret */
4881	if (SCNetworkConnectionPrivateIsPPPService(serviceID, kSCValNetInterfaceSubTypeL2TP, 0)) {
4882		CFMutableStringRef	uniqueIDSS;
4883
4884		uniqueIDSS = CFStringCreateMutableCopy(NULL, 0, uniqueID);
4885		CFStringAppend(uniqueIDSS, CFSTR(".SS"));
4886		sharedsecret = copyPasswordFromKeychain(uniqueIDSS);
4887		CFRelease(uniqueIDSS);
4888	}
4889
4890	/* did we find our information in the key chain ? */
4891	if ((password != NULL) || (sharedsecret != NULL)) {
4892		CFMutableDictionaryRef	newOptions;
4893
4894		newOptions = CFDictionaryCreateMutableCopy(NULL, 0, *userOptions);
4895
4896		/* PPP password */
4897		if (password != NULL) {
4898			CFDictionaryRef		entity;
4899			CFMutableDictionaryRef	newEntity;
4900
4901			entity = CFDictionaryGetValue(*userOptions, kSCEntNetPPP);
4902			if (isA_CFDictionary(entity))
4903				newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity);
4904			else
4905				newEntity = CFDictionaryCreateMutable(NULL,
4906								      0,
4907								      &kCFTypeDictionaryKeyCallBacks,
4908								      &kCFTypeDictionaryValueCallBacks);
4909
4910
4911			/* set the PPP password */
4912			CFDictionarySetValue(newEntity, kSCPropNetPPPAuthPassword, uniqueID);
4913			CFDictionarySetValue(newEntity, kSCPropNetPPPAuthPasswordEncryption, kSCValNetPPPAuthPasswordEncryptionKeychain);
4914			CFRelease(password);
4915
4916			/* update the PPP entity */
4917			CFDictionarySetValue(newOptions, kSCEntNetPPP, newEntity);
4918			CFRelease(newEntity);
4919		}
4920
4921		/* IPSec Shared Secret */
4922		if (sharedsecret != NULL) {
4923			CFDictionaryRef		entity;
4924			CFMutableDictionaryRef	newEntity;
4925
4926			entity = CFDictionaryGetValue(*userOptions, kSCEntNetIPSec);
4927			if (isA_CFDictionary(entity))
4928				newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity);
4929			else
4930				newEntity = CFDictionaryCreateMutable(NULL,
4931								      0,
4932								      &kCFTypeDictionaryKeyCallBacks,
4933								      &kCFTypeDictionaryValueCallBacks);
4934
4935			/* set the IPSec Shared Secret */
4936			CFDictionarySetValue(newEntity, kSCPropNetIPSecSharedSecret, sharedsecret);
4937			CFRelease(sharedsecret);
4938
4939			/* update the IPSec entity */
4940			CFDictionarySetValue(newOptions, kSCEntNetIPSec, newEntity);
4941			CFRelease(newEntity);
4942		}
4943
4944		/* update the userOptions dictionary */
4945		CFRelease(*userOptions);
4946		*userOptions = CFDictionaryCreateCopy(NULL, newOptions);
4947		CFRelease(newOptions);
4948	}
4949
4950}
4951
4952#if	!TARGET_OS_IPHONE
4953//********************************************************************************
4954// copyKeychainEnumerator
4955// --------------------------------------
4956// Gather Keychain Enumerator
4957//********************************************************************************
4958static CFArrayRef
4959copyKeychainEnumerator(CFStringRef uniqueIdentifier)
4960{
4961	CFArrayRef		itemArray	= NULL;
4962	CFMutableDictionaryRef	query;
4963	OSStatus		result;
4964
4965	query = CFDictionaryCreateMutable(NULL,
4966					  0,
4967					  &kCFTypeDictionaryKeyCallBacks,
4968					  &kCFTypeDictionaryValueCallBacks);
4969	CFDictionarySetValue(query, kSecClass      , kSecClassGenericPassword);
4970	CFDictionarySetValue(query, kSecAttrService, uniqueIdentifier);
4971	CFDictionarySetValue(query, kSecReturnRef  , kCFBooleanTrue);
4972	CFDictionarySetValue(query, kSecMatchLimit , kSecMatchLimitAll);
4973	result = SecItemCopyMatching(query, (CFTypeRef *)&itemArray);
4974	CFRelease(query);
4975	if ((result != noErr) && (itemArray != NULL)) {
4976		CFRelease(itemArray);
4977		itemArray = NULL;
4978	}
4979
4980	return itemArray;
4981}
4982#endif	// !TARGET_OS_IPHONE
4983
4984//********************************************************************************
4985// copyPasswordFromKeychain
4986// --------------------------------------
4987// Given a uniqueID, retrieve the password from the keychain
4988//********************************************************************************
4989static CFStringRef
4990copyPasswordFromKeychain(CFStringRef uniqueID)
4991{
4992#if	!TARGET_OS_IPHONE
4993	CFArrayRef	enumerator;
4994	CFIndex		n;
4995	CFStringRef	password = NULL;
4996
4997	enumerator = copyKeychainEnumerator(uniqueID);
4998	if (enumerator == NULL) {
4999		return NULL;		// if no keychain enumerator
5000	}
5001
5002	n = CFArrayGetCount(enumerator);
5003	if (n > 0) {
5004		void			*data	= NULL;
5005		UInt32			dataLen	= 0;
5006		SecKeychainItemRef	itemRef;
5007		OSStatus		result;
5008
5009		itemRef = (SecKeychainItemRef)CFArrayGetValueAtIndex(enumerator, 0);
5010		result = SecKeychainItemCopyContent(itemRef,		// itemRef
5011						    NULL,		// itemClass
5012						    NULL,		// attrList
5013						    &dataLen,		// length
5014						    (void *)&data);	// outData
5015		if ((result == noErr) && (data != NULL) && (dataLen > 0)) {
5016			password = CFStringCreateWithBytes(NULL, data, dataLen, kCFStringEncodingUTF8, TRUE);
5017			(void) SecKeychainItemFreeContent(NULL, data);
5018		}
5019
5020	}
5021
5022	CFRelease(enumerator);
5023
5024	return password;
5025#else	// !TARGET_OS_IPHONE
5026	return NULL;
5027#endif	// !TARGET_OS_IPHONE
5028}
5029
5030
5031__private_extern__
5032char *
5033__SCNetworkConnectionGetControllerPortName(void)
5034{
5035#if	((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 70000)) && !TARGET_IPHONE_SIMULATOR
5036	if (scnc_server_name == NULL){
5037		if (!(sandbox_check(getpid(), "mach-lookup", SANDBOX_FILTER_GLOBAL_NAME | SANDBOX_CHECK_NO_REPORT, PPPCONTROLLER_SERVER_PRIV))){
5038			scnc_server_name = PPPCONTROLLER_SERVER_PRIV;
5039		}
5040		else{
5041			scnc_server_name = PPPCONTROLLER_SERVER;
5042		}
5043		SCLog(TRUE, LOG_DEBUG, CFSTR("__SCNetworkConnectionGetControllerPortName() returns port: %s"), scnc_server_name);
5044	}
5045#else
5046	scnc_server_name = PPPCONTROLLER_SERVER;
5047#endif
5048	return scnc_server_name;
5049}
5050
5051