]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCNetworkConnection.c
configd-395.6.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCNetworkConnection.c
1 /*
2 * Copyright (c) 2003-2011 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 <SystemConfiguration/SystemConfiguration.h>
45 #include <SystemConfiguration/SCPrivate.h>
46 #include <SystemConfiguration/SCValidation.h>
47
48 #if !TARGET_OS_IPHONE
49 #include <Security/Security.h>
50 #include "dy_framework.h"
51 #endif // !TARGET_OS_IPHONE
52
53 #include <servers/bootstrap.h>
54 #include <bootstrap_priv.h>
55
56 #include <pthread.h>
57 #include <notify.h>
58 #include <netinet/in.h>
59 #include <arpa/inet.h>
60 #include <netdb.h>
61 #include <unistd.h>
62 #include <sys/ioctl.h>
63 #include <sys/socket.h>
64 #include <net/if.h>
65 #include <mach/mach.h>
66 #include <bsm/audit.h>
67
68 #include <ppp/ppp_msg.h>
69 #include "pppcontroller.h"
70 #include <ppp/pppcontroller_types.h>
71
72
73
74 static int debug = 0;
75 static pthread_once_t initialized = PTHREAD_ONCE_INIT;
76 static pthread_mutex_t scnc_lock = PTHREAD_MUTEX_INITIALIZER;
77 static mach_port_t scnc_server = MACH_PORT_NULL;
78
79
80 typedef struct {
81
82 /* base CFType information */
83 CFRuntimeBase cfBase;
84
85 /* lock */
86 pthread_mutex_t lock;
87
88 /* service */
89 SCNetworkServiceRef service;
90
91 /* ref to PPP controller for control messages */
92 mach_port_t session_port;
93
94 /* ref to PPP controller for notification messages */
95 CFMachPortRef notify_port;
96
97 /* keep track of whether we're acquired the initial status */
98 Boolean haveStatus;
99
100 /* run loop source, callout, context, rl scheduling info */
101 Boolean scheduled;
102 CFRunLoopSourceRef rls;
103 SCNetworkConnectionCallBack rlsFunction;
104 SCNetworkConnectionContext rlsContext;
105 CFMutableArrayRef rlList;
106
107 /* SCNetworkConnectionSetDispatchQueue */
108 dispatch_queue_t dispatchQueue;
109 dispatch_queue_t callbackQueue;
110 dispatch_source_t callbackSource;
111
112 } SCNetworkConnectionPrivate, *SCNetworkConnectionPrivateRef;
113
114
115 static __inline__ CFTypeRef
116 isA_SCNetworkConnection(CFTypeRef obj)
117 {
118 return (isA_CFType(obj, SCNetworkConnectionGetTypeID()));
119 }
120
121
122 static CFStringRef
123 __SCNetworkConnectionCopyDescription(CFTypeRef cf)
124 {
125 CFAllocatorRef allocator = CFGetAllocator(cf);
126 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)cf;
127 CFMutableStringRef result;
128
129 result = CFStringCreateMutable(allocator, 0);
130 CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkConnection, %p [%p]> {"), cf, allocator);
131 CFStringAppendFormat(result, NULL, CFSTR("service = %p"), connectionPrivate->service);
132 if (connectionPrivate->session_port != MACH_PORT_NULL) {
133 CFStringAppendFormat(result, NULL, CFSTR(", server port = %p"), connectionPrivate->session_port);
134 }
135 CFStringAppendFormat(result, NULL, CFSTR("}"));
136
137 return result;
138 }
139
140
141 static void
142 __SCNetworkConnectionDeallocate(CFTypeRef cf)
143 {
144 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)cf;
145
146 /* release resources */
147 pthread_mutex_destroy(&connectionPrivate->lock);
148
149 if (connectionPrivate->rls != NULL) {
150 CFRunLoopSourceInvalidate(connectionPrivate->rls);
151 CFRelease(connectionPrivate->rls);
152 }
153
154 if (connectionPrivate->rlList != NULL) {
155 CFRelease(connectionPrivate->rlList);
156 }
157
158 if (connectionPrivate->notify_port != NULL) {
159 mach_port_t mp = CFMachPortGetPort(connectionPrivate->notify_port);
160
161 __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionDeallocate notify_port", mp);
162 CFMachPortInvalidate(connectionPrivate->notify_port);
163 CFRelease(connectionPrivate->notify_port);
164 mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1);
165 }
166
167 if (connectionPrivate->session_port != MACH_PORT_NULL) {
168 __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionDeallocate session_port", connectionPrivate->session_port);
169 (void) mach_port_deallocate(mach_task_self(), connectionPrivate->session_port);
170 }
171
172 if (connectionPrivate->rlsContext.release != NULL)
173 (*connectionPrivate->rlsContext.release)(connectionPrivate->rlsContext.info);
174
175 CFRelease(connectionPrivate->service);
176
177 return;
178 }
179
180
181 static CFTypeID __kSCNetworkConnectionTypeID = _kCFRuntimeNotATypeID;
182
183 static const CFRuntimeClass __SCNetworkConnectionClass = {
184 0, // version
185 "SCNetworkConnection", // className
186 NULL, // init
187 NULL, // copy
188 __SCNetworkConnectionDeallocate, // dealloc
189 NULL, // equal
190 NULL, // hash
191 NULL, // copyFormattingDesc
192 __SCNetworkConnectionCopyDescription // copyDebugDesc
193 };
194
195
196 static void
197 childForkHandler()
198 {
199 /* the process has forked (and we are the child process) */
200
201 scnc_server = MACH_PORT_NULL;
202 return;
203 }
204
205
206 static void
207 __SCNetworkConnectionInitialize(void)
208 {
209 char *env;
210
211 /* get the debug environment variable */
212 env = getenv("PPPDebug");
213 if (env != NULL) {
214 if (sscanf(env, "%d", &debug) != 1) {
215 /* PPPDebug value is not valid (or non-numeric), set debug to 1 */
216 debug = 1;
217 }
218 }
219
220 /* register with CoreFoundation */
221 __kSCNetworkConnectionTypeID = _CFRuntimeRegisterClass(&__SCNetworkConnectionClass);
222
223 /* add handler to cleanup after fork() */
224 (void) pthread_atfork(NULL, NULL, childForkHandler);
225
226 return;
227 }
228
229
230 static Boolean
231 __SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection);
232
233
234 static void
235 __SCNetworkConnectionCallBack(CFMachPortRef port, void * msg, CFIndex size, void * info)
236 {
237 mach_no_senders_notification_t *buf = msg;
238 mach_msg_id_t msgid = buf->not_header.msgh_id;
239
240 SCNetworkConnectionRef connection = (SCNetworkConnectionRef)info;
241 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
242 void *context_info;
243 void (*context_release)(const void *);
244 SCNetworkConnectionCallBack rlsFunction = NULL;
245 SCNetworkConnectionStatus nc_status = kSCNetworkConnectionInvalid;
246
247 if (msgid == MACH_NOTIFY_NO_SENDERS) {
248 // re-establish notification
249 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCNetworkConnectionCallBack: PPPController server died"));
250 (void)__SCNetworkConnectionReconnectNotifications(connection);
251 }
252
253 pthread_mutex_lock(&connectionPrivate->lock);
254
255 if (!connectionPrivate->scheduled) {
256 // if not currently scheduled
257 goto doit;
258 }
259
260 rlsFunction = connectionPrivate->rlsFunction;
261 if (rlsFunction == NULL) {
262 goto doit;
263 }
264
265 if ((connectionPrivate->rlsContext.retain != NULL) && (connectionPrivate->rlsContext.info != NULL)) {
266 context_info = (void *)(*connectionPrivate->rlsContext.retain)(connectionPrivate->rlsContext.info);
267 context_release = connectionPrivate->rlsContext.release;
268 } else {
269 context_info = connectionPrivate->rlsContext.info;
270 context_release = NULL;
271 }
272
273 doit :
274
275 pthread_mutex_unlock(&connectionPrivate->lock);
276
277 if (rlsFunction == NULL) {
278 return;
279 }
280
281 nc_status = SCNetworkConnectionGetStatus(connection);
282 (*rlsFunction)(connection, nc_status, context_info);
283 if ((context_release != NULL) && (context_info != NULL)) {
284 (*context_release)(context_info);
285 }
286
287 return;
288 }
289
290
291 #pragma mark -
292 #pragma mark SCNetworkConnection APIs
293
294
295 static CFStringRef
296 pppMPCopyDescription(const void *info)
297 {
298 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)info;
299
300 return CFStringCreateWithFormat(NULL,
301 NULL,
302 CFSTR("<SCNetworkConnection MP %p> {service = %@, callout = %p}"),
303 connectionPrivate,
304 connectionPrivate->service,
305 connectionPrivate->rlsFunction);
306 }
307
308
309 static SCNetworkConnectionPrivateRef
310 __SCNetworkConnectionCreatePrivate(CFAllocatorRef allocator,
311 SCNetworkServiceRef service,
312 SCNetworkConnectionCallBack callout,
313 SCNetworkConnectionContext *context)
314 {
315 SCNetworkConnectionPrivateRef connectionPrivate = NULL;
316 uint32_t size;
317
318
319 /* initialize runtime */
320 pthread_once(&initialized, __SCNetworkConnectionInitialize);
321
322 /* allocate NetworkConnection */
323 size = sizeof(SCNetworkConnectionPrivate) - sizeof(CFRuntimeBase);
324 connectionPrivate = (SCNetworkConnectionPrivateRef)_CFRuntimeCreateInstance(allocator, __kSCNetworkConnectionTypeID, size, NULL);
325 if (connectionPrivate == NULL) {
326 goto fail;
327 }
328
329 /* zero the data structure */
330 bzero(((u_char*)connectionPrivate)+sizeof(CFRuntimeBase), size);
331
332 pthread_mutex_init(&connectionPrivate->lock, NULL);
333
334 /* save the service */
335 connectionPrivate->service = CFRetain(service);
336
337 connectionPrivate->rlsFunction = callout;
338
339 if (context) {
340 bcopy(context, &connectionPrivate->rlsContext, sizeof(SCNetworkConnectionContext));
341 if (context->retain != NULL) {
342 connectionPrivate->rlsContext.info = (void *)(*context->retain)(context->info);
343 }
344 }
345
346 /* success, return the connection reference */
347 return connectionPrivate;
348
349 fail:
350
351 /* failure, clean up and leave */
352 if (connectionPrivate != NULL) {
353 CFRelease(connectionPrivate);
354 }
355
356 _SCErrorSet(kSCStatusFailed);
357 return NULL;
358 }
359
360
361 static mach_port_t
362 __SCNetworkConnectionServerPort(kern_return_t *status)
363 {
364 mach_port_t server = MACH_PORT_NULL;
365
366 #ifdef BOOTSTRAP_PRIVILEGED_SERVER
367 *status = bootstrap_look_up2(bootstrap_port,
368 PPPCONTROLLER_SERVER,
369 &server,
370 0,
371 BOOTSTRAP_PRIVILEGED_SERVER);
372 #else // BOOTSTRAP_PRIVILEGED_SERVER
373 *status = bootstrap_look_up(bootstrap_port, PPPCONTROLLER_SERVER, &server);
374 #endif // BOOTSTRAP_PRIVILEGED_SERVER
375
376 switch (*status) {
377 case BOOTSTRAP_SUCCESS :
378 // service currently registered, "a good thing" (tm)
379 return server;
380 case BOOTSTRAP_NOT_PRIVILEGED :
381 // the service is not privileged
382 break;
383 case BOOTSTRAP_UNKNOWN_SERVICE :
384 // service not currently registered, try again later
385 break;
386 default :
387 #ifdef DEBUG
388 SCLog(_sc_verbose, LOG_DEBUG,
389 CFSTR("SCNetworkConnection bootstrap_look_up() failed: status=%s"),
390 bootstrap_strerror(*status));
391 #endif // DEBUG
392 break;
393 }
394
395 return MACH_PORT_NULL;
396 }
397
398
399 static mach_port_t
400 __SCNetworkConnectionSessionPort(SCNetworkConnectionPrivateRef connectionPrivate)
401 {
402 void *data = NULL;
403 CFIndex dataLen = 0;
404 CFDataRef dataRef = NULL;
405 mach_port_t notify_port = MACH_PORT_NULL;
406 mach_port_t oldNotify = MACH_PORT_NULL;
407 int retry = 0;
408 int sc_status = kSCStatusFailed;
409 mach_port_t server = scnc_server;
410 kern_return_t status = KERN_SUCCESS;
411 mach_port_t au_session = MACH_PORT_NULL;
412
413 if (connectionPrivate->session_port != MACH_PORT_NULL) {
414 return connectionPrivate->session_port;
415 }
416
417 if (!_SCSerializeString(SCNetworkServiceGetServiceID(connectionPrivate->service), &dataRef, &data, &dataLen)) {
418 goto done;
419 }
420
421 if (connectionPrivate->notify_port != NULL) {
422 mach_port_t mp = CFMachPortGetPort(connectionPrivate->notify_port);
423
424 __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionReconnectNotifications mp", mp);
425 CFMachPortInvalidate(connectionPrivate->notify_port);
426 CFRelease(connectionPrivate->notify_port);
427 connectionPrivate->notify_port = NULL;
428 mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1);
429 }
430
431 au_session = audit_session_self();
432
433 // open a new session with the server
434 while (TRUE) {
435 if ((connectionPrivate->rlsFunction != NULL) && (notify_port == MACH_PORT_NULL)) {
436 // allocate port (for server response)
437 status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &notify_port);
438 if (status != KERN_SUCCESS) {
439 SCLog(TRUE, LOG_ERR, CFSTR("__SCNetworkConnectionSessionPort mach_port_allocate(): %s"), mach_error_string(status));
440 sc_status = status;
441 goto done;
442 }
443
444 // add send right (passed to the server)
445 status = mach_port_insert_right(mach_task_self(),
446 notify_port,
447 notify_port,
448 MACH_MSG_TYPE_MAKE_SEND);
449 if (status != KERN_SUCCESS) {
450 SCLog(TRUE, LOG_ERR, CFSTR("__SCNetworkConnectionSessionPort mach_port_insert_right(): %s"), mach_error_string(status));
451 mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
452 sc_status = status;
453 goto done;
454 }
455 }
456
457 if (server != MACH_PORT_NULL) {
458 status = pppcontroller_attach(server,
459 data,
460 dataLen,
461 bootstrap_port,
462 notify_port,
463 au_session,
464 &connectionPrivate->session_port,
465 &sc_status);
466 if (status == KERN_SUCCESS) {
467 if (sc_status != kSCStatusOK) {
468 SCLog(TRUE, LOG_DEBUG,
469 CFSTR("__SCNetworkConnectionSessionPort : attach w/error, sc_status=%s%s"),
470 SCErrorString(sc_status),
471 (connectionPrivate->session_port != MACH_PORT_NULL) ? ", w/session_port!=MACH_PORT_NULL" : "");
472
473 if (connectionPrivate->session_port != MACH_PORT_NULL) {
474 __MACH_PORT_DEBUG(TRUE,
475 "*** __SCNetworkConnectionSessionPort session_port (attach w/error, cleanup)",
476 connectionPrivate->session_port);
477 mach_port_deallocate(mach_task_self(), connectionPrivate->session_port);
478 connectionPrivate->session_port = MACH_PORT_NULL;
479 }
480
481 if (notify_port != MACH_PORT_NULL) {
482 __MACH_PORT_DEBUG(TRUE,
483 "*** __SCNetworkConnectionSessionPort notify_port (attach w/error, cleanup)",
484 notify_port);
485 (void) mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
486 notify_port = MACH_PORT_NULL;
487 }
488 }
489 break;
490 }
491
492 // our [cached] server port is not valid
493 SCLog(TRUE, LOG_DEBUG, CFSTR("__SCNetworkConnectionSessionPort : !attach: %s"), SCErrorString(status));
494 if (status == MACH_SEND_INVALID_DEST) {
495 // the server is not yet available
496 __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionSessionPort notify_port (!dest)", notify_port);
497 } else if (status == MIG_SERVER_DIED) {
498 __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionSessionPort notify_port (!mig)", notify_port);
499 // the server we were using is gone and we've lost our send right
500 mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
501 notify_port = MACH_PORT_NULL;
502 } else {
503 // if we got an unexpected error, don't retry
504 sc_status = status;
505 break;
506 }
507 }
508
509 pthread_mutex_lock(&scnc_lock);
510 if (scnc_server != MACH_PORT_NULL) {
511 if (server == scnc_server) {
512 // if the server we tried returned the error
513 (void)mach_port_deallocate(mach_task_self(), scnc_server);
514 scnc_server = __SCNetworkConnectionServerPort(&sc_status);
515 } else {
516 // another thread has refreshed the server port
517 }
518 } else {
519 scnc_server = __SCNetworkConnectionServerPort(&sc_status);
520 }
521 server = scnc_server;
522 pthread_mutex_unlock(&scnc_lock);
523
524 if (server == MACH_PORT_NULL) {
525 // if server not available
526 if (sc_status == BOOTSTRAP_UNKNOWN_SERVICE) {
527 // if first retry attempt, wait for SCDynamicStore server
528 if (retry == 0) {
529 SCDynamicStoreRef store;
530
531 store = SCDynamicStoreCreate(NULL,
532 CFSTR("SCNetworkConnection connect"),
533 NULL,
534 NULL);
535 if (store != NULL) {
536 CFRelease(store);
537 }
538 }
539
540 // wait up to 2.5 seconds for the [SCNetworkConnection] server
541 // to startup
542 if ((retry += 50) < 2500) {
543 usleep(50 * 1000); // sleep 50ms between attempts
544 continue;
545 }
546 }
547 break;
548 }
549 }
550
551 if (notify_port != MACH_PORT_NULL) {
552 if (connectionPrivate->session_port != MACH_PORT_NULL) {
553 CFMachPortContext context = { 0
554 , (void *)connectionPrivate
555 , NULL
556 , NULL
557 , pppMPCopyDescription
558 };
559
560 // request a notification when/if the server dies
561 status = mach_port_request_notification(mach_task_self(),
562 notify_port,
563 MACH_NOTIFY_NO_SENDERS,
564 1,
565 notify_port,
566 MACH_MSG_TYPE_MAKE_SEND_ONCE,
567 &oldNotify);
568 if (status != KERN_SUCCESS) {
569 SCLog(TRUE, LOG_ERR, CFSTR("__SCNetworkConnectionSessionPort mach_port_request_notification(): %s"), mach_error_string(status));
570 mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
571 sc_status = status;
572 goto done;
573 }
574
575 if (oldNotify != MACH_PORT_NULL) {
576 SCLog(TRUE, LOG_ERR, CFSTR("__SCNetworkConnectionSessionPort(): oldNotify != MACH_PORT_NULL"));
577 }
578
579 // create CFMachPort for SCNetworkConnection notification callback
580 connectionPrivate->notify_port = _SC_CFMachPortCreateWithPort("SCNetworkConnection",
581 notify_port,
582 __SCNetworkConnectionCallBack,
583 &context);
584
585 // we need to try a bit harder to acquire the initial status
586 connectionPrivate->haveStatus = FALSE;
587 } else {
588 // with no server port, release the notification port we allocated
589 __MACH_PORT_DEBUG(TRUE,
590 "*** __SCNetworkConnectionSessionPort notify_port (!server)",
591 notify_port);
592 (void) mach_port_mod_refs (mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
593 (void) mach_port_deallocate(mach_task_self(), notify_port);
594 notify_port = MACH_PORT_NULL;
595 }
596 }
597
598 done :
599
600 // clean up
601 if (au_session != MACH_PORT_NULL) {
602 (void)mach_port_deallocate(mach_task_self(), au_session);
603 }
604
605 if (dataRef != NULL) CFRelease(dataRef);
606
607 switch (sc_status) {
608 case kSCStatusOK :
609 __MACH_PORT_DEBUG(connectionPrivate->session_port != MACH_PORT_NULL,
610 "*** __SCNetworkConnectionSessionPort session_port",
611 connectionPrivate->session_port);
612 __MACH_PORT_DEBUG(notify_port != MACH_PORT_NULL,
613 "*** __SCNetworkConnectionSessionPort notify_port",
614 notify_port);
615 break;
616 case BOOTSTRAP_UNKNOWN_SERVICE :
617 SCLog(TRUE,
618 (status == KERN_SUCCESS) ? LOG_DEBUG : LOG_ERR,
619 CFSTR("PPPController not available"));
620 break;
621 default :
622 SCLog(TRUE,
623 (status == KERN_SUCCESS) ? LOG_DEBUG : LOG_ERR,
624 CFSTR("__SCNetworkConnectionSessionPort pppcontroller_attach(): %s"),
625 SCErrorString(sc_status));
626 break;
627 }
628
629 if (sc_status != kSCStatusOK) {
630 _SCErrorSet(sc_status);
631 }
632
633 return connectionPrivate->session_port;
634 }
635
636
637 static Boolean
638 __SCNetworkConnectionReconnect(SCNetworkConnectionRef connection)
639 {
640 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
641 mach_port_t port;
642
643 port = __SCNetworkConnectionSessionPort(connectionPrivate);
644 return (port != MACH_PORT_NULL);
645 }
646
647
648 static Boolean
649 __SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection)
650 {
651 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
652 dispatch_queue_t dispatchQueue = NULL;
653 Boolean ok = TRUE;
654 CFArrayRef rlList = NULL;
655
656 // Before we fully tearing down our [old] notifications, make sure
657 // we have retained any information that is needed to re-register the
658 // [new] notifications.
659
660 pthread_mutex_lock(&connectionPrivate->lock);
661
662 if (connectionPrivate->rlList != NULL) {
663 rlList = CFArrayCreateCopy(NULL, connectionPrivate->rlList);
664 }
665 if (connectionPrivate->dispatchQueue != NULL) {
666 dispatchQueue = connectionPrivate->dispatchQueue;
667 dispatch_retain(dispatchQueue);
668 }
669
670 // cancel [old] notifications
671 if (connectionPrivate->rlList != NULL) {
672 CFRelease(connectionPrivate->rlList);
673 connectionPrivate->rlList = NULL;
674 }
675 if (connectionPrivate->rls != NULL) {
676 CFRunLoopSourceInvalidate(connectionPrivate->rls);
677 CFRelease(connectionPrivate->rls);
678 connectionPrivate->rls = NULL;
679 }
680 if (connectionPrivate->callbackSource != NULL) {
681 dispatch_source_cancel(connectionPrivate->callbackSource);
682 if (connectionPrivate->callbackQueue != dispatch_get_current_queue()) {
683 // ensure the cancellation has completed
684 dispatch_sync(connectionPrivate->callbackQueue, ^{});
685 }
686 dispatch_release(connectionPrivate->callbackSource);
687 connectionPrivate->callbackSource = NULL;
688 }
689 if (connectionPrivate->callbackQueue != NULL) {
690 dispatch_release(connectionPrivate->callbackQueue);
691 connectionPrivate->callbackQueue = NULL;
692 }
693 if (connectionPrivate->dispatchQueue != NULL) {
694 dispatch_release(connectionPrivate->dispatchQueue);
695 connectionPrivate->dispatchQueue = NULL;
696 }
697
698 connectionPrivate->scheduled = FALSE;
699
700 pthread_mutex_unlock(&connectionPrivate->lock);
701
702 // re-schedule
703 if (rlList != NULL) {
704 CFIndex i;
705 CFIndex n;
706
707 n = CFArrayGetCount(rlList);
708 for (i = 0; i < n; i += 3) {
709 CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(rlList, i+1);
710 CFStringRef rlMode = (CFStringRef) CFArrayGetValueAtIndex(rlList, i+2);
711
712 ok = SCNetworkConnectionScheduleWithRunLoop(connection, rl, rlMode);
713 if (!ok) {
714 SCLog((SCError() != BOOTSTRAP_UNKNOWN_SERVICE),
715 LOG_ERR,
716 CFSTR("__SCNetworkConnectionReconnectNotifications: SCNetworkConnectionScheduleWithRunLoop() failed"));
717 goto done;
718 }
719 }
720 } else if (dispatchQueue != NULL) {
721 ok = SCNetworkConnectionSetDispatchQueue(connection, dispatchQueue);
722 if (!ok) {
723 SCLog((SCError() != BOOTSTRAP_UNKNOWN_SERVICE),
724 LOG_ERR,
725 CFSTR("__SCNetworkConnectionReconnectNotifications: SCNetworkConnectionSetDispatchQueue() failed"));
726 goto done;
727 }
728 } else {
729 ok = FALSE;
730 }
731
732 done :
733
734 // cleanup
735 if (rlList != NULL) {
736 CFRelease(rlList);
737 }
738 if (dispatchQueue != NULL) {
739 dispatch_release(dispatchQueue);
740 }
741
742 if (!ok) {
743 SCLog(TRUE, LOG_ERR,
744 CFSTR("SCNetworkConnection server %s, notification not restored"),
745 (SCError() == BOOTSTRAP_UNKNOWN_SERVICE) ? "shutdown" : "failed");
746 }
747
748 return ok;
749 }
750
751
752 static Boolean
753 __SCNetworkConnectionNeedsRetry(SCNetworkConnectionRef connection,
754 const char *error_label,
755 kern_return_t status,
756 int *sc_status)
757 {
758 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
759
760 if (status == KERN_SUCCESS) {
761 return FALSE;
762 }
763
764 if ((status == MACH_SEND_INVALID_DEST) || (status == MIG_SERVER_DIED)) {
765 // the server's gone and our session port's dead, remove the dead name right
766 (void) mach_port_deallocate(mach_task_self(), connectionPrivate->session_port);
767 } else {
768 // we got an unexpected error, leave the [session] port alone
769 SCLog(TRUE, LOG_ERR, CFSTR("%s: %s"), error_label, mach_error_string(status));
770 }
771 connectionPrivate->session_port = MACH_PORT_NULL;
772 if ((status == MACH_SEND_INVALID_DEST) || (status == MIG_SERVER_DIED)) {
773 if (__SCNetworkConnectionReconnect(connection)) {
774 return TRUE;
775 }
776 }
777 *sc_status = status;
778
779 return FALSE;
780 }
781
782
783 CFTypeID
784 SCNetworkConnectionGetTypeID(void) {
785 pthread_once(&initialized, __SCNetworkConnectionInitialize); /* initialize runtime */
786 return __kSCNetworkConnectionTypeID;
787 }
788
789
790 CFArrayRef /* of SCNetworkServiceRef's */
791 SCNetworkConnectionCopyAvailableServices(SCNetworkSetRef set)
792 {
793 CFMutableArrayRef available;
794 Boolean tempSet = FALSE;
795
796 if (set == NULL) {
797 SCPreferencesRef prefs;
798
799 prefs = SCPreferencesCreate(NULL, CFSTR("SCNetworkConnectionCopyAvailableServices"), NULL);
800 if (prefs != NULL) {
801 set = SCNetworkSetCopyCurrent(prefs);
802 CFRelease(prefs);
803 }
804 tempSet = TRUE;
805 }
806
807 available = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
808
809 if (set != NULL) {
810 CFArrayRef services;
811
812 services = SCNetworkSetCopyServices(set);
813 if (services != NULL) {
814 CFIndex i;
815 CFIndex n;
816
817 n = CFArrayGetCount(services);
818 for (i = 0; i < n; i++) {
819 SCNetworkInterfaceRef interface;
820 CFStringRef interfaceType;
821 SCNetworkServiceRef service;
822
823 service = CFArrayGetValueAtIndex(services, i);
824 interface = SCNetworkServiceGetInterface(service);
825 if (interface == NULL) {
826 continue;
827 }
828
829 interfaceType = SCNetworkInterfaceGetInterfaceType(interface);
830 if (CFEqual(interfaceType, kSCNetworkInterfaceTypePPP) ||
831 CFEqual(interfaceType, kSCNetworkInterfaceTypeVPN) ||
832 CFEqual(interfaceType, kSCNetworkInterfaceTypeIPSec)) {
833 CFArrayAppendValue(available, service);
834 }
835 }
836
837 CFRelease(services);
838 }
839 }
840
841 if (tempSet && (set != NULL)) {
842 CFRelease(set);
843 }
844 return available;
845 }
846
847
848 SCNetworkConnectionRef
849 SCNetworkConnectionCreateWithService(CFAllocatorRef allocator,
850 SCNetworkServiceRef service,
851 SCNetworkConnectionCallBack callout,
852 SCNetworkConnectionContext *context)
853 {
854 SCNetworkConnectionPrivateRef connectionPrivate;
855
856 if (!isA_SCNetworkService(service)) {
857 _SCErrorSet(kSCStatusInvalidArgument);
858 return FALSE;
859 }
860
861 connectionPrivate = __SCNetworkConnectionCreatePrivate(allocator, service, callout, context);
862 return (SCNetworkConnectionRef)connectionPrivate;
863 }
864
865
866 SCNetworkConnectionRef
867 SCNetworkConnectionCreateWithServiceID(CFAllocatorRef allocator,
868 CFStringRef serviceID,
869 SCNetworkConnectionCallBack callout,
870 SCNetworkConnectionContext *context)
871 {
872 SCNetworkConnectionRef connection;
873 SCNetworkServiceRef service;
874
875 if (!isA_CFString(serviceID)) {
876 _SCErrorSet(kSCStatusInvalidArgument);
877 return NULL;
878 }
879
880 service = _SCNetworkServiceCopyActive(NULL, serviceID);
881 if (service == NULL) {
882 return NULL;
883 }
884
885 connection = SCNetworkConnectionCreateWithService(allocator, service, callout, context);
886 CFRelease(service);
887
888 return connection;
889 }
890
891
892 CFStringRef
893 SCNetworkConnectionCopyServiceID(SCNetworkConnectionRef connection)
894 {
895 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
896 CFStringRef serviceID;
897
898 if (!isA_SCNetworkConnection(connection)) {
899 _SCErrorSet(kSCStatusInvalidArgument);
900 return NULL;
901 }
902
903 serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service);
904 return CFRetain(serviceID);
905 }
906
907
908 CFDictionaryRef
909 SCNetworkConnectionCopyStatistics(SCNetworkConnectionRef connection)
910 {
911 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
912 xmlDataOut_t data = NULL;
913 mach_msg_type_number_t datalen = 0;
914 int sc_status = kSCStatusFailed;
915 mach_port_t session_port;
916 CFPropertyListRef statistics = NULL;
917 kern_return_t status;
918
919 if (!isA_SCNetworkConnection(connection)) {
920 _SCErrorSet(kSCStatusInvalidArgument);
921 return NULL;
922 }
923
924 pthread_mutex_lock(&connectionPrivate->lock);
925
926 retry :
927
928 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
929 if (session_port == MACH_PORT_NULL) {
930 goto done;
931 }
932
933 status = pppcontroller_copystatistics(session_port, &data, &datalen, &sc_status);
934 if (__SCNetworkConnectionNeedsRetry(connection,
935 "SCNetworkConnectionCopyStatistics()",
936 status,
937 &sc_status)) {
938 goto retry;
939 }
940
941 if (data != NULL) {
942 if (!_SCUnserialize(&statistics, NULL, data, datalen)) {
943 if (sc_status != kSCStatusOK) sc_status = SCError();
944 }
945 if ((sc_status == kSCStatusOK) && !isA_CFDictionary(statistics)) {
946 sc_status = kSCStatusFailed;
947 }
948 }
949
950 if (sc_status != kSCStatusOK) {
951 if (statistics != NULL) {
952 CFRelease(statistics);
953 statistics = NULL;
954 }
955 _SCErrorSet(sc_status);
956 }
957
958 done :
959
960 pthread_mutex_unlock(&connectionPrivate->lock);
961 return statistics;
962 }
963
964
965 SCNetworkServiceRef
966 SCNetworkConnectionGetService(SCNetworkConnectionRef connection)
967 {
968 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
969
970 if (!isA_SCNetworkConnection(connection)) {
971 _SCErrorSet(kSCStatusInvalidArgument);
972 return NULL;
973 }
974
975 return connectionPrivate->service;
976 }
977
978
979 SCNetworkConnectionStatus
980 SCNetworkConnectionGetStatus(SCNetworkConnectionRef connection)
981 {
982 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
983 SCNetworkConnectionStatus nc_status = kSCNetworkConnectionInvalid;
984 int retry = 0;
985 int sc_status = kSCStatusFailed;
986 mach_port_t session_port;
987 kern_return_t status;
988
989 if (!isA_SCNetworkConnection(connection)) {
990 _SCErrorSet(kSCStatusInvalidArgument);
991 return kSCNetworkConnectionInvalid;
992 }
993
994 pthread_mutex_lock(&connectionPrivate->lock);
995
996 retry :
997
998 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
999 if (session_port == MACH_PORT_NULL) {
1000 nc_status = kSCNetworkConnectionInvalid;
1001 goto done;
1002 }
1003
1004 status = pppcontroller_getstatus(session_port, &nc_status, &sc_status);
1005 if (__SCNetworkConnectionNeedsRetry(connection,
1006 "SCNetworkConnectionGetStatus()",
1007 status,
1008 &sc_status)) {
1009 goto retry;
1010 }
1011
1012 // wait up to 250 ms for the network service to become available
1013 if (!connectionPrivate->haveStatus &&
1014 (sc_status == kSCStatusConnectionNoService) &&
1015 ((retry += 10) < 250)) {
1016 usleep(10 * 1000); // sleep 10ms between attempts
1017 goto retry;
1018 }
1019
1020 if (sc_status == kSCStatusOK) {
1021 connectionPrivate->haveStatus = TRUE;
1022 } else {
1023 _SCErrorSet(sc_status);
1024 nc_status = kSCNetworkConnectionInvalid;
1025 }
1026
1027 done :
1028
1029 pthread_mutex_unlock(&connectionPrivate->lock);
1030 return nc_status;
1031 }
1032
1033
1034 CFDictionaryRef
1035 SCNetworkConnectionCopyExtendedStatus(SCNetworkConnectionRef connection)
1036 {
1037 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1038 xmlDataOut_t data = NULL;
1039 mach_msg_type_number_t datalen = 0;
1040 CFPropertyListRef extstatus = NULL;
1041 int retry = 0;
1042 int sc_status = kSCStatusFailed;
1043 mach_port_t session_port;
1044 kern_return_t status;
1045
1046 if (!isA_SCNetworkConnection(connection)) {
1047 _SCErrorSet(kSCStatusInvalidArgument);
1048 return NULL;
1049 }
1050
1051 pthread_mutex_lock(&connectionPrivate->lock);
1052
1053 retry :
1054
1055 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1056 if (session_port == MACH_PORT_NULL) {
1057 goto done;
1058 }
1059
1060 status = pppcontroller_copyextendedstatus(session_port, &data, &datalen, &sc_status);
1061 if (__SCNetworkConnectionNeedsRetry(connection,
1062 "SCNetworkConnectionCopyExtendedStatus()",
1063 status,
1064 &sc_status)) {
1065 goto retry;
1066 }
1067
1068 if (data != NULL) {
1069 if (!_SCUnserialize(&extstatus, NULL, data, datalen)) {
1070 if (sc_status != kSCStatusOK) sc_status = SCError();
1071 }
1072 if ((sc_status == kSCStatusOK) && !isA_CFDictionary(extstatus)) {
1073 sc_status = kSCStatusFailed;
1074 }
1075 }
1076
1077 // wait up to 250 ms for the network service to become available
1078 if (!connectionPrivate->haveStatus &&
1079 (sc_status == kSCStatusConnectionNoService) &&
1080 ((retry += 10) < 250)) {
1081 usleep(10 * 1000); // sleep 10ms between attempts
1082 goto retry;
1083 }
1084
1085 if (sc_status == kSCStatusOK) {
1086 connectionPrivate->haveStatus = TRUE;
1087 } else {
1088 if (extstatus != NULL) {
1089 CFRelease(extstatus);
1090 extstatus = NULL;
1091 }
1092 _SCErrorSet(sc_status);
1093 }
1094
1095 done :
1096
1097 pthread_mutex_unlock(&connectionPrivate->lock);
1098 return extstatus;
1099 }
1100
1101
1102 Boolean
1103 SCNetworkConnectionStart(SCNetworkConnectionRef connection,
1104 CFDictionaryRef userOptions,
1105 Boolean linger)
1106 {
1107 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1108 CFDataRef dataref = NULL;
1109 void *data = NULL;
1110 CFIndex datalen = 0;
1111 Boolean ok = FALSE;
1112 int sc_status = kSCStatusFailed;
1113 mach_port_t session_port;
1114 kern_return_t status;
1115
1116 if (!isA_SCNetworkConnection(connection)) {
1117 _SCErrorSet(kSCStatusInvalidArgument);
1118 return FALSE;
1119 }
1120
1121 if ((userOptions != NULL) && !isA_CFDictionary(userOptions)) {
1122 _SCErrorSet(kSCStatusInvalidArgument);
1123 return FALSE;
1124 }
1125
1126 if (debug > 0) {
1127 CFMutableDictionaryRef mdict = NULL;
1128
1129 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionStart (0x%x)"), connectionPrivate);
1130
1131 if (userOptions != NULL) {
1132 CFDictionaryRef dict;
1133 CFStringRef encryption;
1134 CFMutableDictionaryRef new_dict;
1135
1136 /* special code to remove secret information */
1137 mdict = CFDictionaryCreateMutableCopy(NULL, 0, userOptions);
1138
1139 dict = CFDictionaryGetValue(mdict, kSCEntNetPPP);
1140 if (isA_CFDictionary(dict)) {
1141 encryption = CFDictionaryGetValue(dict, kSCPropNetPPPAuthPasswordEncryption);
1142 if (!isA_CFString(encryption) ||
1143 !CFEqual(encryption, kSCValNetPPPAuthPasswordEncryptionKeychain)) {
1144 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
1145 CFDictionaryReplaceValue(new_dict, kSCPropNetPPPAuthPassword, CFSTR("******"));
1146 CFDictionarySetValue(mdict, kSCEntNetPPP, new_dict);
1147 CFRelease(new_dict);
1148 }
1149 }
1150
1151 dict = CFDictionaryGetValue(mdict, kSCEntNetL2TP);
1152 if (isA_CFDictionary(dict)) {
1153 encryption = CFDictionaryGetValue(dict, kSCPropNetL2TPIPSecSharedSecretEncryption);
1154 if (!isA_CFString(encryption) ||
1155 !CFEqual(encryption, kSCValNetL2TPIPSecSharedSecretEncryptionKeychain)) {
1156 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
1157 CFDictionaryReplaceValue(new_dict, kSCPropNetL2TPIPSecSharedSecret, CFSTR("******"));
1158 CFDictionarySetValue(mdict, kSCEntNetL2TP, new_dict);
1159 CFRelease(new_dict);
1160 }
1161 }
1162
1163 dict = CFDictionaryGetValue(mdict, kSCEntNetIPSec);
1164 if (isA_CFDictionary(dict)) {
1165 encryption = CFDictionaryGetValue(dict, kSCPropNetIPSecSharedSecretEncryption);
1166 if (!isA_CFString(encryption) ||
1167 !CFEqual(encryption, kSCValNetIPSecSharedSecretEncryptionKeychain)) {
1168 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
1169 CFDictionaryReplaceValue(new_dict, kSCPropNetIPSecSharedSecret, CFSTR("******"));
1170 CFDictionarySetValue(mdict, kSCEntNetIPSec, new_dict);
1171 CFRelease(new_dict);
1172 }
1173 }
1174 }
1175
1176 SCLog(TRUE, LOG_DEBUG, CFSTR("User options: %@"), mdict);
1177 if (mdict != NULL) CFRelease(mdict);
1178 }
1179
1180 if (userOptions && !_SCSerialize(userOptions, &dataref, &data, &datalen)) {
1181 return FALSE;
1182 }
1183
1184 pthread_mutex_lock(&connectionPrivate->lock);
1185
1186 retry :
1187
1188 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1189 if (session_port == MACH_PORT_NULL) {
1190 if (dataref) CFRelease(dataref);
1191 goto done;
1192 }
1193
1194 status = pppcontroller_start(session_port, data, datalen, linger, &sc_status);
1195 if (__SCNetworkConnectionNeedsRetry(connection,
1196 "SCNetworkConnectionStart()",
1197 status,
1198 &sc_status)) {
1199 goto retry;
1200 }
1201
1202 if (dataref) CFRelease(dataref);
1203
1204 if (debug > 0) {
1205 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionStart (0x%x), return: %d"), connectionPrivate, sc_status);
1206 }
1207
1208 if (sc_status != kSCStatusOK) {
1209 _SCErrorSet(sc_status);
1210 goto done;
1211 }
1212
1213 /* connection is now started */
1214 ok = TRUE;
1215
1216 done:
1217 pthread_mutex_unlock(&connectionPrivate->lock);
1218 return ok;
1219 }
1220
1221
1222 Boolean
1223 SCNetworkConnectionStop(SCNetworkConnectionRef connection,
1224 Boolean forceDisconnect)
1225 {
1226 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1227 Boolean ok = FALSE;
1228 int sc_status = kSCStatusFailed;
1229 mach_port_t session_port;
1230 kern_return_t status;
1231
1232 if (!isA_SCNetworkConnection(connection)) {
1233 _SCErrorSet(kSCStatusInvalidArgument);
1234 return FALSE;
1235 }
1236
1237 if (debug > 0) {
1238 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionStop (0x%x)"), connectionPrivate);
1239 }
1240
1241 pthread_mutex_lock(&connectionPrivate->lock);
1242
1243 retry :
1244
1245 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1246 if (session_port == MACH_PORT_NULL) {
1247 goto done;
1248 }
1249
1250 status = pppcontroller_stop(session_port, forceDisconnect, &sc_status);
1251 if (__SCNetworkConnectionNeedsRetry(connection,
1252 "SCNetworkConnectionStop()",
1253 status,
1254 &sc_status)) {
1255 goto retry;
1256 }
1257
1258 if (debug > 0) {
1259 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionStop (0x%x), return: %d"), connectionPrivate, sc_status);
1260 }
1261
1262 if (sc_status != kSCStatusOK) {
1263 _SCErrorSet(sc_status);
1264 goto done;
1265 }
1266
1267 /* connection is now disconnecting */
1268 ok = TRUE;
1269
1270 done :
1271
1272 pthread_mutex_unlock(&connectionPrivate->lock);
1273 return ok;
1274 }
1275
1276
1277 Boolean
1278 SCNetworkConnectionSuspend(SCNetworkConnectionRef connection)
1279 {
1280 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1281 Boolean ok = FALSE;
1282 int sc_status = kSCStatusFailed;
1283 mach_port_t session_port;
1284 kern_return_t status;
1285
1286 if (!isA_SCNetworkConnection(connection)) {
1287 _SCErrorSet(kSCStatusInvalidArgument);
1288 return FALSE;
1289 }
1290
1291 if (debug > 0) {
1292 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionSuspend (0x%x)"), connectionPrivate);
1293 }
1294
1295 pthread_mutex_lock(&connectionPrivate->lock);
1296
1297 retry :
1298
1299 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1300 if (session_port == MACH_PORT_NULL) {
1301 goto done;
1302 }
1303
1304 status = pppcontroller_suspend(session_port, &sc_status);
1305 if (__SCNetworkConnectionNeedsRetry(connection,
1306 "SCNetworkConnectionSuspend()",
1307 status,
1308 &sc_status)) {
1309 goto retry;
1310 }
1311
1312 if (debug > 0) {
1313 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionSuspend (0x%x), return: %d"), connectionPrivate, sc_status);
1314 }
1315
1316 if (sc_status != kSCStatusOK) {
1317 _SCErrorSet(sc_status);
1318 goto done;
1319 }
1320
1321 /* connection is now suspended */
1322 ok = TRUE;
1323
1324 done :
1325
1326 pthread_mutex_unlock(&connectionPrivate->lock);
1327 return ok;
1328 }
1329
1330
1331 Boolean
1332 SCNetworkConnectionResume(SCNetworkConnectionRef connection)
1333 {
1334 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1335 Boolean ok = FALSE;
1336 int sc_status = kSCStatusFailed;
1337 mach_port_t session_port;
1338 kern_return_t status;
1339
1340 if (!isA_SCNetworkConnection(connection)) {
1341 _SCErrorSet(kSCStatusInvalidArgument);
1342 return FALSE;
1343 }
1344
1345 if (debug > 0) {
1346 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionResume (0x%x)"), connectionPrivate);
1347 }
1348
1349 pthread_mutex_lock(&connectionPrivate->lock);
1350
1351 retry :
1352
1353 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1354 if (session_port == MACH_PORT_NULL) {
1355 goto done;
1356 }
1357
1358 status = pppcontroller_resume(session_port, &sc_status);
1359 if (__SCNetworkConnectionNeedsRetry(connection,
1360 "SCNetworkConnectionResume()",
1361 status,
1362 &sc_status)) {
1363 goto retry;
1364 }
1365
1366 if (debug > 0) {
1367 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionResume (0x%x), return: %d"), connectionPrivate, sc_status);
1368 }
1369
1370 if (sc_status != kSCStatusOK) {
1371 _SCErrorSet(sc_status);
1372 goto done;
1373 }
1374
1375 /* connection is now resume */
1376 ok = TRUE;
1377
1378 done :
1379
1380 pthread_mutex_unlock(&connectionPrivate->lock);
1381 return ok;
1382 }
1383
1384
1385 CFDictionaryRef
1386 SCNetworkConnectionCopyUserOptions(SCNetworkConnectionRef connection)
1387 {
1388 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1389 xmlDataOut_t data = NULL;
1390 mach_msg_type_number_t datalen = 0;
1391 int sc_status = kSCStatusFailed;
1392 mach_port_t session_port;
1393 kern_return_t status;
1394 CFPropertyListRef userOptions = NULL;
1395
1396 if (!isA_SCNetworkConnection(connection)) {
1397 _SCErrorSet(kSCStatusInvalidArgument);
1398 return NULL;
1399 }
1400
1401 pthread_mutex_lock(&connectionPrivate->lock);
1402
1403 retry :
1404
1405 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1406 if (session_port == MACH_PORT_NULL) {
1407 goto done;
1408 }
1409
1410 status = pppcontroller_copyuseroptions(session_port, &data, &datalen, &sc_status);
1411 if (__SCNetworkConnectionNeedsRetry(connection,
1412 "SCNetworkConnectionCopyUserOptions()",
1413 status,
1414 &sc_status)) {
1415 goto retry;
1416 }
1417
1418 if (data != NULL) {
1419 if (!_SCUnserialize(&userOptions, NULL, data, datalen)) {
1420 if (sc_status != kSCStatusOK) sc_status = SCError();
1421 }
1422 if ((sc_status == kSCStatusOK) && (userOptions != NULL) && !isA_CFDictionary(userOptions)) {
1423 sc_status = kSCStatusFailed;
1424 }
1425 }
1426
1427 if (sc_status == kSCStatusOK) {
1428 if (userOptions == NULL) {
1429 // if no user options, return an empty dictionary
1430 userOptions = CFDictionaryCreate(NULL,
1431 NULL,
1432 NULL,
1433 0,
1434 &kCFTypeDictionaryKeyCallBacks,
1435 &kCFTypeDictionaryValueCallBacks);
1436 }
1437 } else {
1438 if (userOptions) {
1439 CFRelease(userOptions);
1440 userOptions = NULL;
1441 }
1442 _SCErrorSet(sc_status);
1443 }
1444
1445 done :
1446
1447 pthread_mutex_unlock(&connectionPrivate->lock);
1448 return userOptions;
1449 }
1450
1451
1452 static boolean_t
1453 SCNetworkConnectionNotifyMIGCallback(mach_msg_header_t *message, mach_msg_header_t *reply)
1454 {
1455 SCNetworkConnectionPrivateRef connectionPrivate = dispatch_get_context(dispatch_get_current_queue());
1456
1457 if (connectionPrivate != NULL) {
1458 mach_msg_empty_rcv_t *buf = malloc(sizeof(*buf));
1459
1460 bcopy(message, buf, sizeof(*buf));
1461 CFRetain(connectionPrivate);
1462 dispatch_async(connectionPrivate->dispatchQueue, ^{
1463 __SCNetworkConnectionCallBack(connectionPrivate->notify_port,
1464 buf,
1465 sizeof(*buf),
1466 connectionPrivate);
1467 CFRelease(connectionPrivate);
1468 free(buf);
1469 });
1470 }
1471 reply->msgh_remote_port = MACH_PORT_NULL;
1472 return false;
1473 }
1474
1475
1476 static Boolean
1477 __SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection,
1478 CFRunLoopRef runLoop,
1479 CFStringRef runLoopMode,
1480 dispatch_queue_t queue)
1481 {
1482 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1483 Boolean ok = FALSE;
1484 int sc_status = kSCStatusFailed;
1485 mach_port_t session_port;
1486 kern_return_t status;
1487
1488 pthread_mutex_lock(&connectionPrivate->lock);
1489
1490 if (connectionPrivate->rlsFunction == NULL) {
1491 _SCErrorSet(kSCStatusInvalidArgument);
1492 goto done;
1493 }
1494
1495 if ((connectionPrivate->dispatchQueue != NULL) || // if we are already scheduled on a dispatch queue
1496 ((queue != NULL) && connectionPrivate->scheduled)) { // if we are already scheduled on a CFRunLoop
1497 _SCErrorSet(kSCStatusInvalidArgument);
1498 goto done;
1499 }
1500
1501 if (!connectionPrivate->scheduled) {
1502
1503 retry :
1504
1505 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1506 if (session_port == MACH_PORT_NULL) {
1507 goto done;
1508 }
1509
1510 status = pppcontroller_notification(session_port, 1, &sc_status);
1511 if (__SCNetworkConnectionNeedsRetry(connection,
1512 "__SCNetworkConnectionScheduleWithRunLoop()",
1513 status,
1514 &sc_status)) {
1515 goto retry;
1516 }
1517
1518 if (sc_status != kSCStatusOK) {
1519 _SCErrorSet(sc_status);
1520 goto done;
1521 }
1522
1523 if (runLoop != NULL) {
1524 connectionPrivate->rls = CFMachPortCreateRunLoopSource(NULL, connectionPrivate->notify_port, 0);
1525 connectionPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1526 }
1527
1528 connectionPrivate->scheduled = TRUE;
1529 }
1530
1531 if (queue != NULL) {
1532 mach_port_t mp;
1533 char qname[256];
1534
1535 connectionPrivate->dispatchQueue = queue;
1536 dispatch_retain(connectionPrivate->dispatchQueue);
1537
1538 snprintf(qname, sizeof(qname), "com.apple.SCNetworkConnection.%p", connection);
1539 connectionPrivate->callbackQueue = dispatch_queue_create(qname, NULL);
1540 if (connectionPrivate->callbackQueue == NULL){
1541 SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkConnection dispatch_queue_create() failed"));
1542 goto fail;
1543 }
1544 CFRetain(connection); // Note: will be released when the dispatch queue is released
1545 dispatch_set_context(connectionPrivate->callbackQueue, connectionPrivate);
1546 dispatch_set_finalizer_f(connectionPrivate->callbackQueue, (dispatch_function_t)CFRelease);
1547
1548 mp = CFMachPortGetPort(connectionPrivate->notify_port);
1549 connectionPrivate->callbackSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV,
1550 mp,
1551 0,
1552 connectionPrivate->callbackQueue);
1553 if (connectionPrivate->callbackSource == NULL) {
1554 SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkConnection dispatch_source_create() failed"));
1555 goto fail;
1556 }
1557 dispatch_source_set_event_handler(connectionPrivate->callbackSource, ^{
1558 union MaxMsgSize {
1559 mach_msg_empty_rcv_t normal;
1560 mach_no_senders_notification_t no_senders;
1561 };
1562
1563 dispatch_mig_server(connectionPrivate->callbackSource,
1564 sizeof(union MaxMsgSize),
1565 SCNetworkConnectionNotifyMIGCallback);
1566 });
1567 dispatch_resume(connectionPrivate->callbackSource);
1568 } else {
1569 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, connectionPrivate->rlList)) {
1570 /*
1571 * if we do not already have notifications scheduled with
1572 * this runLoop / runLoopMode
1573 */
1574 CFRunLoopAddSource(runLoop, connectionPrivate->rls, runLoopMode);
1575 }
1576
1577 _SC_schedule(connection, runLoop, runLoopMode, connectionPrivate->rlList);
1578 }
1579
1580 ok = TRUE;
1581 goto done;
1582
1583 fail :
1584
1585 if (connectionPrivate->callbackSource != NULL) {
1586 dispatch_source_cancel(connectionPrivate->callbackSource);
1587 dispatch_release(connectionPrivate->callbackSource);
1588 connectionPrivate->callbackSource = NULL;
1589 }
1590 if (connectionPrivate->callbackQueue != NULL) {
1591 dispatch_release(connectionPrivate->callbackQueue);
1592 connectionPrivate->callbackQueue = NULL;
1593 }
1594 if (connectionPrivate->dispatchQueue != NULL) {
1595 dispatch_release(connectionPrivate->dispatchQueue);
1596 connectionPrivate->dispatchQueue = NULL;
1597 }
1598 _SCErrorSet(kSCStatusFailed);
1599
1600 done :
1601
1602 pthread_mutex_unlock(&connectionPrivate->lock);
1603 return ok;
1604 }
1605
1606
1607 static Boolean
1608 __SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection,
1609 CFRunLoopRef runLoop,
1610 CFStringRef runLoopMode,
1611 dispatch_queue_t queue)
1612 {
1613 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1614 int sc_status = kSCStatusFailed;
1615 CFIndex n = 0;
1616 Boolean ok = FALSE;
1617 mach_port_t session_port;
1618 kern_return_t status;
1619
1620 pthread_mutex_lock(&connectionPrivate->lock);
1621
1622 if ((runLoop != NULL) && !connectionPrivate->scheduled) { // if we should be scheduled (but are not)
1623 _SCErrorSet(kSCStatusInvalidArgument);
1624 goto done;
1625 }
1626
1627 if (((runLoop == NULL) && (connectionPrivate->dispatchQueue == NULL)) || // if we should be scheduled on a dispatch queue (but are not)
1628 ((runLoop != NULL) && (connectionPrivate->dispatchQueue != NULL))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
1629 _SCErrorSet(kSCStatusInvalidArgument);
1630 goto done;
1631 }
1632
1633 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1634 if (session_port == MACH_PORT_NULL) {
1635 goto done;
1636 }
1637
1638 if (runLoop == NULL) {
1639 dispatch_source_cancel(connectionPrivate->callbackSource);
1640 if (connectionPrivate->callbackQueue != dispatch_get_current_queue()) {
1641 // ensure the cancellation has completed
1642 dispatch_sync(connectionPrivate->callbackQueue, ^{});
1643 }
1644 dispatch_release(connectionPrivate->callbackSource);
1645 connectionPrivate->callbackSource = NULL;
1646 dispatch_release(connectionPrivate->callbackQueue);
1647 connectionPrivate->callbackQueue = NULL;
1648 dispatch_release(connectionPrivate->dispatchQueue);
1649 connectionPrivate->dispatchQueue = NULL;
1650 } else {
1651 if (!_SC_unschedule(connection, runLoop, runLoopMode, connectionPrivate->rlList, FALSE)) {
1652 // if not currently scheduled on this runLoop / runLoopMode
1653 _SCErrorSet(kSCStatusFailed);
1654 goto done;
1655 }
1656
1657 n = CFArrayGetCount(connectionPrivate->rlList);
1658 if (n == 0 || !_SC_isScheduled(NULL, runLoop, runLoopMode, connectionPrivate->rlList)) {
1659 /*
1660 * if we are no longer scheduled to receive notifications for
1661 * this runLoop / runLoopMode
1662 */
1663 CFRunLoopRemoveSource(runLoop, connectionPrivate->rls, runLoopMode);
1664
1665 if (n == 0) {
1666 // if *all* notifications have been unscheduled
1667 CFRelease(connectionPrivate->rlList);
1668 connectionPrivate->rlList = NULL;
1669 CFRunLoopSourceInvalidate(connectionPrivate->rls);
1670 CFRelease(connectionPrivate->rls);
1671 connectionPrivate->rls = NULL;
1672 }
1673 }
1674 }
1675
1676 if (n == 0) {
1677 // if *all* notifications have been unscheduled
1678 connectionPrivate->scheduled = FALSE;
1679
1680 status = pppcontroller_notification(session_port, 0, &sc_status);
1681 if (__SCNetworkConnectionNeedsRetry(connection,
1682 "__SCNetworkConnectionUnscheduleFromRunLoop pppcontroller_notification()",
1683 status,
1684 &sc_status)) {
1685 sc_status = kSCStatusOK;
1686 status = KERN_SUCCESS;
1687 }
1688
1689 if ((status != KERN_SUCCESS) || (sc_status != kSCStatusOK)) {
1690 _SCErrorSet(sc_status);
1691 goto done;
1692 }
1693 }
1694
1695 ok = TRUE;
1696
1697 done :
1698
1699 pthread_mutex_unlock(&connectionPrivate->lock);
1700 return ok;
1701 }
1702
1703
1704 Boolean
1705 SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection,
1706 CFRunLoopRef runLoop,
1707 CFStringRef runLoopMode)
1708 {
1709 if (!isA_SCNetworkConnection(connection) || (runLoop == NULL) || (runLoopMode == NULL)) {
1710 _SCErrorSet(kSCStatusInvalidArgument);
1711 return FALSE;
1712 }
1713
1714 return __SCNetworkConnectionScheduleWithRunLoop(connection, runLoop, runLoopMode, NULL);
1715 }
1716
1717
1718 Boolean
1719 SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection,
1720 CFRunLoopRef runLoop,
1721 CFStringRef runLoopMode)
1722 {
1723 if (!isA_SCNetworkConnection(connection) || (runLoop == NULL) || (runLoopMode == NULL)) {
1724 _SCErrorSet(kSCStatusInvalidArgument);
1725 return FALSE;
1726 }
1727
1728 return __SCNetworkConnectionUnscheduleFromRunLoop(connection, runLoop, runLoopMode, NULL);
1729 }
1730
1731
1732 Boolean
1733 SCNetworkConnectionSetDispatchQueue(SCNetworkConnectionRef connection,
1734 dispatch_queue_t queue)
1735 {
1736 Boolean ok = FALSE;
1737
1738 if (!isA_SCNetworkConnection(connection)) {
1739 _SCErrorSet(kSCStatusInvalidArgument);
1740 return FALSE;
1741 }
1742
1743 if (queue != NULL) {
1744 ok = __SCNetworkConnectionScheduleWithRunLoop(connection, NULL, NULL, queue);
1745 } else {
1746 ok = __SCNetworkConnectionUnscheduleFromRunLoop(connection, NULL, NULL, NULL);
1747 }
1748
1749 return ok;
1750 }
1751
1752
1753 #pragma mark -
1754 #pragma mark User level "dial" API
1755
1756
1757 #define k_NetworkConnect_Notification "com.apple.networkConnect"
1758 #define k_NetworkConnect_Pref_File CFSTR("com.apple.networkConnect")
1759 #define k_InterentConnect_Pref_File CFSTR("com.apple.internetconnect")
1760
1761 #define k_Dial_Default_Key CFSTR("ConnectByDefault") // needs to go into SC
1762 #define k_Last_Service_Id_Key CFSTR("ServiceID")
1763 #define k_Unique_Id_Key CFSTR("UniqueIdentifier")
1764
1765
1766 /* Private Prototypes */
1767 static Boolean SCNetworkConnectionPrivateCopyDefaultServiceIDForDial (SCDynamicStoreRef session, CFStringRef *serviceID);
1768 static Boolean SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore (SCDynamicStoreRef session, CFStringRef *serviceID);
1769 static Boolean SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray, CFDictionaryRef *userOptions);
1770 static Boolean SCNetworkConnectionPrivateIsPPPService (SCDynamicStoreRef session, CFStringRef serviceID, CFStringRef subType1, CFStringRef subType2);
1771 static void addPasswordFromKeychain (SCDynamicStoreRef session, CFStringRef serviceID, CFDictionaryRef *userOptions);
1772 static CFStringRef copyPasswordFromKeychain (CFStringRef uniqueID);
1773
1774 static int notify_userprefs_token = -1;
1775
1776 static CFDictionaryRef onDemand_configuration = NULL;
1777 static pthread_mutex_t onDemand_notify_lock = PTHREAD_MUTEX_INITIALIZER;
1778 static int onDemand_notify_token = -1;
1779
1780 /*
1781 * return TRUE if domain1 ends with domain2, and will check for trailing "."
1782 */
1783 static Boolean
1784 domainEndsWithDomain(CFStringRef domain1, CFStringRef domain2)
1785 {
1786 CFRange range;
1787 Boolean ret = FALSE;
1788 CFStringRef s1 = NULL;
1789 Boolean s1_created = FALSE;
1790 CFStringRef s2 = NULL;
1791 Boolean s2_created = FALSE;
1792
1793 if (CFStringHasSuffix(domain1, CFSTR("."))) {
1794 range.location = 0;
1795 range.length = CFStringGetLength(domain1) - 1;
1796 s1 = CFStringCreateWithSubstring(NULL, domain1, range);
1797 if (s1 == NULL) {
1798 goto done;
1799 }
1800 s1_created = TRUE;
1801 } else {
1802 s1 = domain1;
1803 }
1804
1805 if (CFStringHasSuffix(domain2, CFSTR("."))) {
1806 range.location = 0;
1807 range.length = CFStringGetLength(domain2) - 1;
1808 s2 = CFStringCreateWithSubstring(NULL, domain2, range);
1809 if (s2 == NULL) {
1810 goto done;
1811 }
1812 s2_created = TRUE;
1813 } else {
1814 s2 = domain2;
1815 }
1816
1817 ret = CFStringHasSuffix(s1, s2);
1818
1819 done :
1820
1821 if (s1_created) CFRelease(s1);
1822 if (s2_created) CFRelease(s2);
1823 return ret;
1824 }
1825
1826 /* VPN On Demand */
1827
1828 Boolean
1829 __SCNetworkConnectionCopyOnDemandInfoWithName(SCDynamicStoreRef *storeP,
1830 CFStringRef hostName,
1831 Boolean onDemandRetry,
1832 CFStringRef *connectionServiceID,
1833 SCNetworkConnectionStatus *connectionStatus,
1834 CFStringRef *vpnRemoteAddress) /* CFDictionaryRef *info */
1835 {
1836 int changed = 1;
1837 CFDictionaryRef configuration;
1838 Boolean ok = FALSE;
1839 int status;
1840 SCDynamicStoreRef store = *storeP;
1841 CFArrayRef triggers;
1842 uint64_t triggersCount = 0;
1843 int triggersIndex;
1844
1845 pthread_mutex_lock(&onDemand_notify_lock);
1846 if (onDemand_notify_token == -1) {
1847 status = notify_register_check(kSCNETWORKCONNECTION_ONDEMAND_NOTIFY_KEY, &onDemand_notify_token);
1848 if (status != NOTIFY_STATUS_OK) {
1849 SCLog(TRUE, LOG_ERR, CFSTR("notify_register_check() failed, status=%lu"), status);
1850 onDemand_notify_token = -1;
1851 }
1852 }
1853 if (onDemand_notify_token != -1) {
1854 status = notify_check(onDemand_notify_token, &changed);
1855 if (status != NOTIFY_STATUS_OK) {
1856 SCLog(TRUE, LOG_ERR, CFSTR("notify_check() failed, status=%lu"), status);
1857 (void)notify_cancel(onDemand_notify_token);
1858 onDemand_notify_token = -1;
1859 }
1860 }
1861
1862 if (changed && (onDemand_notify_token != -1)) {
1863 status = notify_get_state(onDemand_notify_token, &triggersCount);
1864 if (status != NOTIFY_STATUS_OK) {
1865 SCLog(TRUE, LOG_ERR, CFSTR("notify_get_state() failed, status=%lu"), status);
1866 (void)notify_cancel(onDemand_notify_token);
1867 onDemand_notify_token = -1;
1868 }
1869 }
1870
1871 if (changed) {
1872 CFStringRef key;
1873
1874 if (_sc_debug || (debug > 0)) {
1875 SCLog(TRUE, LOG_INFO,
1876 CFSTR("OnDemand information %s"),
1877 (onDemand_configuration == NULL) ? "fetched" : "updated");
1878 }
1879
1880 if (onDemand_configuration != NULL) {
1881 CFRelease(onDemand_configuration);
1882 onDemand_configuration = NULL;
1883 }
1884
1885 if (triggersCount > 0) {
1886 if (store == NULL) {
1887 store = SCDynamicStoreCreate(NULL, CFSTR("__SCNetworkConnectionCopyOnDemandInfoWithName"), NULL, NULL);
1888 if (store == NULL) {
1889 SCLog(TRUE, LOG_ERR, CFSTR("__SCNetworkConnectionCopyOnDemandInfoWithName SCDynamicStoreCreate() failed"));
1890
1891 // force retry on next check
1892 if (onDemand_notify_token != -1) {
1893 (void)notify_cancel(onDemand_notify_token);
1894 onDemand_notify_token = -1;
1895 }
1896 pthread_mutex_unlock(&onDemand_notify_lock);
1897 return FALSE;
1898 }
1899 *storeP = store;
1900 }
1901
1902 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetOnDemand);
1903 onDemand_configuration = SCDynamicStoreCopyValue(store, key);
1904 CFRelease(key);
1905 if ((onDemand_configuration != NULL) && !isA_CFDictionary(onDemand_configuration)) {
1906 CFRelease(onDemand_configuration);
1907 onDemand_configuration = NULL;
1908 }
1909 }
1910 }
1911
1912 configuration = (onDemand_configuration != NULL) ? CFRetain(onDemand_configuration) : NULL;
1913 pthread_mutex_unlock(&onDemand_notify_lock);
1914
1915 if (configuration == NULL) {
1916 // if no "OnDemand" configurations
1917 return FALSE;
1918 }
1919
1920 triggers = CFDictionaryGetValue(configuration, kSCNetworkConnectionOnDemandTriggers);
1921 triggersCount = isA_CFArray(triggers) ? CFArrayGetCount(triggers) : 0;
1922 for (triggersIndex = 0; triggersIndex < triggersCount; triggersIndex++) {
1923 CFArrayRef domains;
1924 int domainsCount;
1925 int domainsIndex;
1926 CFStringRef key;
1927 CFDictionaryRef trigger;
1928
1929 trigger = CFArrayGetValueAtIndex(triggers, triggersIndex);
1930 if (!isA_CFDictionary(trigger)) {
1931 // if not a valid "OnDemand" configuration
1932 continue;
1933 }
1934
1935 /*
1936 * If we haven't tried a resulution yet, we only want to check for a name
1937 * match for domains that require to always connect.
1938 */
1939 key = onDemandRetry ? kSCNetworkConnectionOnDemandMatchDomainsOnRetry
1940 : kSCNetworkConnectionOnDemandMatchDomainsAlways;
1941 domains = CFDictionaryGetValue(trigger, key);
1942 domainsCount = isA_CFArray(domains) ? CFArrayGetCount(domains) : 0;
1943 for (domainsIndex = 0; domainsIndex < domainsCount; domainsIndex++) {
1944 CFStringRef domain;
1945
1946 domain = CFArrayGetValueAtIndex(domains, domainsIndex);
1947 if (!isA_CFString(domain)) {
1948 // if not a valid match domain
1949 continue;
1950 }
1951
1952 if (domainEndsWithDomain(hostName, domain)) {
1953 CFArrayRef exceptions;
1954 int exceptionsCount;
1955 int exceptionsIndex;
1956 CFNumberRef num;
1957 SCNetworkConnectionStatus onDemandStatus = kSCNetworkConnectionDisconnected;
1958
1959 // we have a matching domain, check against exception list
1960 exceptions = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandMatchDomainsNever);
1961 exceptionsCount = isA_CFArray(exceptions) ? CFArrayGetCount(exceptions) : 0;
1962 for (exceptionsIndex = 0; exceptionsIndex < exceptionsCount; exceptionsIndex++) {
1963 CFStringRef exception;
1964
1965 exception = CFArrayGetValueAtIndex(exceptions, exceptionsIndex);
1966 if (!isA_CFString(exception)) {
1967 // if not a valid match exception
1968 continue;
1969 }
1970
1971 if (domainEndsWithDomain(hostName, exception)) {
1972 // found matching exception
1973 if (_sc_debug || (debug > 0)) {
1974 SCLog(TRUE, LOG_INFO, CFSTR("OnDemand match exception"));
1975 }
1976 goto done;
1977 }
1978 }
1979
1980 // if we have a matching domain and there were no exceptions
1981 // then we pass back the OnDemand info
1982
1983 if (!CFDictionaryGetValueIfPresent(trigger,
1984 kSCNetworkConnectionOnDemandStatus,
1985 (const void **)&num) ||
1986 !isA_CFNumber(num) ||
1987 !CFNumberGetValue(num, kCFNumberSInt32Type, &onDemandStatus)) {
1988 onDemandStatus = kSCNetworkConnectionDisconnected;
1989 }
1990 if (connectionStatus != NULL) {
1991 *connectionStatus = onDemandStatus;
1992 }
1993
1994 if (connectionServiceID != NULL) {
1995 *connectionServiceID = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandServiceID);
1996 *connectionServiceID = isA_CFString(*connectionServiceID);
1997 if (*connectionServiceID != NULL) {
1998 CFRetain(*connectionServiceID);
1999 }
2000
2001 }
2002
2003 if (vpnRemoteAddress != NULL) {
2004 *vpnRemoteAddress = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandRemoteAddress);
2005 *vpnRemoteAddress = isA_CFString(*vpnRemoteAddress);
2006 if (*vpnRemoteAddress != NULL) {
2007 CFRetain(*vpnRemoteAddress);
2008 }
2009 }
2010
2011 if (_sc_debug || (debug > 0)) {
2012 SCLog(TRUE, LOG_INFO,
2013 CFSTR("OnDemand%s match, connection status = %d"),
2014 onDemandRetry ? " (on retry)" : "",
2015 onDemandStatus);
2016 }
2017
2018 ok = TRUE;
2019 goto done;
2020 }
2021 }
2022 }
2023
2024 // if (_sc_debug || (debug > 0)) {
2025 // SCLog(TRUE, LOG_INFO, CFSTR("OnDemand domain name(s) not matched"));
2026 // }
2027
2028 done :
2029
2030 if (configuration != NULL) CFRelease(configuration);
2031 return ok;
2032 }
2033
2034
2035 Boolean
2036 SCNetworkConnectionCopyUserPreferences(CFDictionaryRef selectionOptions,
2037 CFStringRef *serviceID,
2038 CFDictionaryRef *userOptions)
2039 {
2040 int prefsChanged = 1;
2041 SCDynamicStoreRef session = NULL;
2042 Boolean success = FALSE;
2043 int status;
2044
2045
2046 /* initialize runtime */
2047 pthread_once(&initialized, __SCNetworkConnectionInitialize);
2048
2049 /* first check for new VPN OnDemand style */
2050 if (selectionOptions != NULL) {
2051 CFStringRef hostName;
2052
2053 hostName = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName);
2054 if (isA_CFString(hostName)) {
2055 CFStringRef connectionServiceID = NULL;
2056 SCNetworkConnectionStatus connectionStatus;
2057 Boolean onDemandRetry;
2058 CFTypeRef val;
2059
2060 val = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandRetry);
2061 onDemandRetry = isA_CFBoolean(val) ? CFBooleanGetValue(val) : TRUE;
2062
2063 success = __SCNetworkConnectionCopyOnDemandInfoWithName(&session,
2064 hostName,
2065 onDemandRetry,
2066 &connectionServiceID,
2067 &connectionStatus,
2068 NULL);
2069 if (debug > 1) {
2070 SCLog(TRUE, LOG_DEBUG,
2071 CFSTR("SCNetworkConnectionCopyUserPreferences __SCNetworkConnectionCopyOnDemandInfoWithName returns %d w/status %d"),
2072 success,
2073 connectionStatus);
2074 }
2075
2076 if (success) {
2077 // if the hostname matches an OnDemand domain
2078 if (session != NULL) {
2079 CFRelease(session);
2080 }
2081 if (connectionStatus == kSCNetworkConnectionConnected) {
2082 // if we are already connected
2083 if (connectionServiceID != NULL) {
2084 CFRelease(connectionServiceID);
2085 }
2086 return FALSE;
2087 }
2088
2089 *serviceID = connectionServiceID;
2090 *userOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
2091 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, hostName);
2092 return TRUE;
2093 } else if (!onDemandRetry) {
2094 // if the hostname does not match an OnDemand domain and we have
2095 // not yet issued an initial DNS query (i.e. it's not a query
2096 // being retried after the VPN has been established) than we're
2097 // done
2098 if (session != NULL) {
2099 CFRelease(session);
2100 }
2101 return FALSE;
2102 }
2103 }
2104 }
2105
2106 if (notify_userprefs_token == -1) {
2107 status = notify_register_check(k_NetworkConnect_Notification, &notify_userprefs_token);
2108 if (status != NOTIFY_STATUS_OK) {
2109 SCLog(TRUE, LOG_ERR, CFSTR("notify_register_check() failed, status=%lu"), status);
2110 (void)notify_cancel(notify_userprefs_token);
2111 notify_userprefs_token = -1;
2112 } else {
2113 // clear the "something has changed" state
2114 (void) notify_check(notify_userprefs_token, &prefsChanged);
2115 prefsChanged = 1;
2116 }
2117 }
2118 if (notify_userprefs_token != -1) {
2119 status = notify_check(notify_userprefs_token, &prefsChanged);
2120 if (status != NOTIFY_STATUS_OK) {
2121 SCLog(TRUE, LOG_ERR, CFSTR("notify_check() failed, status=%lu"), status);
2122 (void)notify_cancel(notify_userprefs_token);
2123 notify_userprefs_token = -1;
2124 }
2125 }
2126
2127
2128 *serviceID = NULL;
2129 *userOptions = NULL;
2130
2131 if (session == NULL) {
2132 session = SCDynamicStoreCreate(NULL, CFSTR("SCNetworkConnection"), NULL, NULL);
2133 }
2134 if (session == NULL) {
2135 SCLog(TRUE, LOG_ERR, CFSTR("Error, SCNetworkConnectionCopyUserPreferences, SCDynamicStoreCreate() returned NULL!"));
2136 return FALSE;
2137 }
2138
2139 if (selectionOptions != NULL) {
2140 Boolean catchAllFound = FALSE;
2141 CFIndex catchAllService = 0;
2142 CFIndex catchAllConfig = 0;
2143 CFStringRef hostName = NULL;
2144 CFStringRef priority = NULL;
2145 CFArrayRef serviceNames = NULL;
2146 CFDictionaryRef services = NULL;
2147 CFIndex serviceIndex;
2148 CFIndex servicesCount;
2149
2150 hostName = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName);
2151 if (hostName == NULL) {
2152 hostName = CFDictionaryGetValue(selectionOptions, kSCPropNetPPPOnDemandHostName);
2153 }
2154 hostName = isA_CFString(hostName);
2155 if (hostName == NULL)
2156 goto done_selection; // if no hostname for matching
2157
2158 priority = CFDictionaryGetValue(selectionOptions, kSCPropNetPPPOnDemandPriority);
2159 if (!isA_CFString(priority))
2160 priority = kSCValNetPPPOnDemandPriorityDefault;
2161
2162
2163 if (!isA_CFArray(serviceNames))
2164 goto done_selection;
2165
2166
2167 if (!isA_CFDictionary(services))
2168 goto done_selection;
2169
2170 servicesCount = CFArrayGetCount(serviceNames);
2171 for (serviceIndex = 0; serviceIndex < servicesCount; serviceIndex++) {
2172 CFIndex configIndex;
2173 CFIndex configsCount;
2174 CFArrayRef serviceConfigs;
2175 CFStringRef serviceName;
2176 int val;
2177
2178 serviceName = CFArrayGetValueAtIndex(serviceNames, serviceIndex);
2179 if (!isA_CFString(serviceName))
2180 continue;
2181
2182 serviceConfigs = CFDictionaryGetValue(services, serviceName);
2183 if (!isA_CFArray(serviceConfigs))
2184 continue;
2185
2186 configsCount = CFArrayGetCount(serviceConfigs);
2187 for (configIndex = 0; configIndex < configsCount; configIndex++) {
2188 CFNumberRef autodial;
2189 CFDictionaryRef config;
2190 CFDictionaryRef pppConfig;
2191
2192 config = CFArrayGetValueAtIndex(serviceConfigs, configIndex);
2193 if (!isA_CFDictionary(config))
2194 continue;
2195
2196 pppConfig = CFDictionaryGetValue(config, kSCEntNetPPP);
2197 if (!isA_CFDictionary(pppConfig))
2198 continue;
2199
2200 autodial = CFDictionaryGetValue(pppConfig, kSCPropNetPPPOnDemandEnabled);
2201 if (!isA_CFNumber(autodial))
2202 continue;
2203
2204 CFNumberGetValue(autodial, kCFNumberIntType, &val);
2205 if (val) {
2206 CFArrayRef domains;
2207 CFIndex domainsCount;
2208 CFIndex domainsIndex;
2209
2210 /* we found an conditional connection enabled configuration */
2211
2212 /* check domain */
2213 domains = CFDictionaryGetValue(pppConfig, kSCPropNetPPPOnDemandDomains);
2214 if (!isA_CFArray(domains))
2215 continue;
2216
2217 domainsCount = CFArrayGetCount(domains);
2218 for (domainsIndex = 0; domainsIndex < domainsCount; domainsIndex++) {
2219 CFStringRef domain;
2220
2221 domain = CFArrayGetValueAtIndex(domains, domainsIndex);
2222 if (!isA_CFString(domain))
2223 continue;
2224
2225 if (!catchAllFound &&
2226 (CFStringCompare(domain, CFSTR(""), 0) == kCFCompareEqualTo
2227 || CFStringCompare(domain, CFSTR("."), 0) == kCFCompareEqualTo)) {
2228 // found a catch all
2229 catchAllFound = TRUE;
2230 catchAllService = serviceIndex;
2231 catchAllConfig = configIndex;
2232 }
2233
2234 if (domainEndsWithDomain(hostName, domain)) {
2235 // found matching configuration
2236 *serviceID = serviceName;
2237 CFRetain(*serviceID);
2238 *userOptions = CFDictionaryCreateMutableCopy(NULL, 0, config);
2239 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, hostName);
2240 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCPropNetPPPOnDemandPriority, priority);
2241 addPasswordFromKeychain(session, *serviceID, userOptions);
2242 success = TRUE;
2243 goto done_selection;
2244 }
2245 }
2246 }
2247 }
2248 }
2249
2250 // config not found, do we have a catchall ?
2251 if (catchAllFound) {
2252 CFDictionaryRef config;
2253 CFArrayRef serviceConfigs;
2254 CFStringRef serviceName;
2255
2256 serviceName = CFArrayGetValueAtIndex(serviceNames, catchAllService);
2257 serviceConfigs = CFDictionaryGetValue(services, serviceName);
2258 config = CFArrayGetValueAtIndex(serviceConfigs, catchAllConfig);
2259
2260 *serviceID = serviceName;
2261 CFRetain(*serviceID);
2262 *userOptions = CFDictionaryCreateMutableCopy(NULL, 0, config);
2263 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, hostName);
2264 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCPropNetPPPOnDemandPriority, priority);
2265 addPasswordFromKeychain(session, *serviceID, userOptions);
2266 success = TRUE;
2267 goto done_selection;
2268 }
2269
2270 done_selection:
2271
2272 if (serviceNames)
2273 CFRelease(serviceNames);
2274 if (services)
2275 CFRelease(services);
2276 CFRelease(session);
2277
2278 if (debug > 1) {
2279 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionCopyUserPreferences %@"), success ? CFSTR("succeeded") : CFSTR("failed"));
2280 SCLog(TRUE, LOG_DEBUG, CFSTR("Selection options: %@"), selectionOptions);
2281 }
2282
2283 return success;
2284 }
2285
2286 /* we don't have selection options */
2287
2288 // (1) Figure out which service ID we care about, allocate it into passed "serviceID"
2289 success = SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(session, serviceID);
2290
2291 if (success && (*serviceID != NULL)) {
2292 // (2) Get the list of user data for this service ID
2293 CFPropertyListRef userServices = NULL;
2294
2295
2296 // (3) We are expecting an array if the user has defined records for this service ID or NULL if the user hasn't
2297 if (userServices != NULL) {
2298 if (isA_CFArray(userServices)) {
2299 // (4) Get the default set of user options for this service
2300 success = SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray((CFArrayRef)userServices,
2301 userOptions);
2302 if(success && (userOptions != NULL)) {
2303 addPasswordFromKeychain(session, *serviceID, userOptions);
2304 }
2305 } else {
2306 SCLog(TRUE, LOG_DEBUG, CFSTR("Error, userServices are not of type CFArray!"));
2307 }
2308
2309 CFRelease(userServices); // this is OK because SCNetworkConnectionPrivateISExpectedCFType() checks for NULL
2310 }
2311 }
2312
2313 if (debug > 1) {
2314 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionCopyUserPreferences %@, no selection options"), success ? CFSTR("succeeded") : CFSTR("failed"));
2315 }
2316
2317 CFRelease(session);
2318 return success;
2319 }
2320
2321
2322 //*******************************************************************************************
2323 // SCNetworkConnectionPrivateCopyDefaultServiceIDForDial
2324 // ----------------------------------------------------
2325 // Try to find the service id to connect
2326 // (1) Start by looking at the last service used in Network Pref / Network menu extra
2327 // (2) If Network Pref / Network menu extra has not been used, find the PPP service
2328 // with the highest ordering
2329 //********************************************************************************************
2330 static Boolean
2331 SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(SCDynamicStoreRef session, CFStringRef *serviceID)
2332 {
2333 Boolean foundService = FALSE;
2334 CFPropertyListRef lastServiceSelectedInIC = NULL;
2335
2336
2337
2338 // we found the service the user last had open in IC
2339 if (lastServiceSelectedInIC != NULL) {
2340 // make sure its a PPP service
2341 if (SCNetworkConnectionPrivateIsPPPService(session, lastServiceSelectedInIC, kSCValNetInterfaceSubTypePPPSerial, kSCValNetInterfaceSubTypePPPoE)) {
2342 // make sure the service that we found is valid
2343 CFDictionaryRef dict;
2344 CFStringRef key;
2345
2346 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
2347 kSCDynamicStoreDomainSetup,
2348 lastServiceSelectedInIC,
2349 kSCEntNetInterface);
2350 dict = SCDynamicStoreCopyValue(session, key);
2351 CFRelease(key);
2352 if (dict != NULL) {
2353 CFRelease(dict);
2354 *serviceID = CFRetain(lastServiceSelectedInIC);
2355 foundService = TRUE;
2356 }
2357 }
2358 CFRelease(lastServiceSelectedInIC);
2359 }
2360
2361 if (!foundService) {
2362 foundService = SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(session, serviceID);
2363 }
2364
2365 return foundService;
2366 }
2367
2368 //********************************************************************************
2369 // SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore
2370 // -------------------------------------------------------
2371 // Find the highest ordered PPP service in the dynamic store
2372 //********************************************************************************
2373 static Boolean
2374 SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(SCDynamicStoreRef session, CFStringRef *serviceID)
2375 {
2376 CFDictionaryRef dict = NULL;
2377 CFStringRef key = NULL;
2378 CFArrayRef serviceIDs = NULL;
2379 Boolean success = FALSE;
2380
2381 *serviceID = NULL;
2382
2383 do {
2384 CFIndex count;
2385 CFIndex i;
2386
2387 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainSetup, kSCEntNetIPv4);
2388 if (key == NULL) {
2389 fprintf(stderr, "Error, Setup Key == NULL!\n");
2390 break;
2391 }
2392
2393 dict = SCDynamicStoreCopyValue(session, key);
2394 if (!isA_CFDictionary(dict)) {
2395 fprintf(stderr, "no global IPv4 entity\n");
2396 break;
2397 }
2398
2399 serviceIDs = CFDictionaryGetValue(dict, kSCPropNetServiceOrder); // array of service id's
2400 if (!isA_CFArray(serviceIDs)) {
2401 fprintf(stderr, "service order not specified\n");
2402 break;
2403 }
2404
2405 count = CFArrayGetCount(serviceIDs);
2406 for (i = 0; i < count; i++) {
2407 CFStringRef service = CFArrayGetValueAtIndex(serviceIDs, i);
2408
2409 if (SCNetworkConnectionPrivateIsPPPService(session, service, kSCValNetInterfaceSubTypePPPSerial, kSCValNetInterfaceSubTypePPPoE)) {
2410 *serviceID = CFRetain(service);
2411 success = TRUE;
2412 break;
2413 }
2414 }
2415 } while (FALSE);
2416
2417 if (key != NULL) CFRelease(key);
2418 if (dict != NULL) CFRelease(dict);
2419
2420 return success;
2421 }
2422
2423 //********************************************************************************
2424 // SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray
2425 // ---------------------------------------------------------
2426 // Copy over user preferences for a particular service if they exist
2427 //********************************************************************************
2428 static Boolean
2429 SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray, CFDictionaryRef *userOptions)
2430 {
2431 CFIndex count = CFArrayGetCount(userOptionsArray);
2432 int i;
2433
2434 for (i = 0; i < count; i++) {
2435 // (1) Find the dictionary
2436 CFPropertyListRef propertyList = CFArrayGetValueAtIndex(userOptionsArray, i);
2437
2438 if (isA_CFDictionary(propertyList) != NULL) {
2439 // See if there's a value for dial on demand
2440 CFPropertyListRef value;
2441
2442 value = CFDictionaryGetValue((CFDictionaryRef)propertyList, k_Dial_Default_Key);
2443 if (isA_CFBoolean(value) != NULL) {
2444 if (CFBooleanGetValue(value)) {
2445 // we found the default user options
2446 *userOptions = CFDictionaryCreateCopy(NULL,
2447 (CFDictionaryRef)propertyList);
2448 break;
2449 }
2450 }
2451 }
2452 }
2453
2454 return TRUE;
2455 }
2456
2457 //********************************************************************************
2458 // SCNetworkConnectionPrivateIsServiceType
2459 // --------------------------------------
2460 // Check and see if the service is a PPP service of the given types
2461 //********************************************************************************
2462 static Boolean
2463 SCNetworkConnectionPrivateIsPPPService(SCDynamicStoreRef session, CFStringRef serviceID, CFStringRef subType1, CFStringRef subType2)
2464 {
2465 CFStringRef entityKey;
2466 Boolean isPPPService = FALSE;
2467 Boolean isMatchingSubType = FALSE;
2468 CFDictionaryRef serviceDict;
2469
2470 entityKey = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
2471 kSCDynamicStoreDomainSetup,
2472 serviceID,
2473 kSCEntNetInterface);
2474 if (entityKey == NULL) {
2475 return FALSE;
2476 }
2477
2478 serviceDict = SCDynamicStoreCopyValue(session, entityKey);
2479 if (serviceDict != NULL) {
2480 if (isA_CFDictionary(serviceDict)) {
2481 CFStringRef type;
2482 CFStringRef subtype;
2483
2484 type = CFDictionaryGetValue(serviceDict, kSCPropNetInterfaceType);
2485 if (isA_CFString(type)) {
2486 isPPPService = CFEqual(type, kSCValNetInterfaceTypePPP);
2487 }
2488
2489 subtype = CFDictionaryGetValue(serviceDict, kSCPropNetInterfaceSubType);
2490 if (isA_CFString(subtype)) {
2491 isMatchingSubType = CFEqual(subtype, subType1);
2492 if (!isMatchingSubType && subType2)
2493 isMatchingSubType = CFEqual(subtype, subType2);
2494 }
2495 }
2496 CFRelease(serviceDict);
2497 }
2498 CFRelease(entityKey);
2499
2500 return (isPPPService && isMatchingSubType);
2501 }
2502
2503 //********************************************************************************
2504 // addPasswordFromKeychain
2505 // --------------------------------------
2506 // Get the password and shared secret out of the keychain and add
2507 // them to the PPP and IPSec dictionaries
2508 //********************************************************************************
2509 static void
2510 addPasswordFromKeychain(SCDynamicStoreRef session, CFStringRef serviceID, CFDictionaryRef *userOptions)
2511 {
2512 CFPropertyListRef uniqueID;
2513 CFStringRef password;
2514 CFStringRef sharedsecret = NULL;
2515
2516 /* user options must exist */
2517 if (*userOptions == NULL)
2518 return;
2519
2520 /* first, get the unique identifier used to store passwords in the keychain */
2521 uniqueID = CFDictionaryGetValue(*userOptions, k_Unique_Id_Key);
2522 if (!isA_CFString(uniqueID))
2523 return;
2524
2525 /* first, get the PPP password */
2526 password = copyPasswordFromKeychain(uniqueID);
2527
2528 /* then, if necessary, get the IPSec Shared Secret */
2529 if (SCNetworkConnectionPrivateIsPPPService(session, serviceID, kSCValNetInterfaceSubTypeL2TP, 0)) {
2530 CFMutableStringRef uniqueIDSS;
2531
2532 uniqueIDSS = CFStringCreateMutableCopy(NULL, 0, uniqueID);
2533 CFStringAppend(uniqueIDSS, CFSTR(".SS"));
2534 sharedsecret = copyPasswordFromKeychain(uniqueIDSS);
2535 CFRelease(uniqueIDSS);
2536 }
2537
2538 /* did we find our information in the key chain ? */
2539 if ((password != NULL) || (sharedsecret != NULL)) {
2540 CFMutableDictionaryRef newOptions;
2541
2542 newOptions = CFDictionaryCreateMutableCopy(NULL, 0, *userOptions);
2543
2544 /* PPP password */
2545 if (password != NULL) {
2546 CFDictionaryRef entity;
2547 CFMutableDictionaryRef newEntity;
2548
2549 entity = CFDictionaryGetValue(*userOptions, kSCEntNetPPP);
2550 if (isA_CFDictionary(entity))
2551 newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity);
2552 else
2553 newEntity = CFDictionaryCreateMutable(NULL,
2554 0,
2555 &kCFTypeDictionaryKeyCallBacks,
2556 &kCFTypeDictionaryValueCallBacks);
2557
2558
2559 /* set the PPP password */
2560 CFDictionarySetValue(newEntity, kSCPropNetPPPAuthPassword, uniqueID);
2561 CFDictionarySetValue(newEntity, kSCPropNetPPPAuthPasswordEncryption, kSCValNetPPPAuthPasswordEncryptionKeychain);
2562 CFRelease(password);
2563
2564 /* update the PPP entity */
2565 CFDictionarySetValue(newOptions, kSCEntNetPPP, newEntity);
2566 CFRelease(newEntity);
2567 }
2568
2569 /* IPSec Shared Secret */
2570 if (sharedsecret != NULL) {
2571 CFDictionaryRef entity;
2572 CFMutableDictionaryRef newEntity;
2573
2574 entity = CFDictionaryGetValue(*userOptions, kSCEntNetIPSec);
2575 if (isA_CFDictionary(entity))
2576 newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity);
2577 else
2578 newEntity = CFDictionaryCreateMutable(NULL,
2579 0,
2580 &kCFTypeDictionaryKeyCallBacks,
2581 &kCFTypeDictionaryValueCallBacks);
2582
2583 /* set the IPSec Shared Secret */
2584 CFDictionarySetValue(newEntity, kSCPropNetIPSecSharedSecret, sharedsecret);
2585 CFRelease(sharedsecret);
2586
2587 /* update the IPSec entity */
2588 CFDictionarySetValue(newOptions, kSCEntNetIPSec, newEntity);
2589 CFRelease(newEntity);
2590 }
2591
2592 /* update the userOptions dictionary */
2593 CFRelease(*userOptions);
2594 *userOptions = CFDictionaryCreateCopy(NULL, newOptions);
2595 CFRelease(newOptions);
2596 }
2597
2598 }
2599
2600 #if !TARGET_OS_IPHONE
2601 //********************************************************************************
2602 // copyKeychainEnumerator
2603 // --------------------------------------
2604 // Gather Keychain Enumerator
2605 //********************************************************************************
2606 static CFArrayRef
2607 copyKeychainEnumerator(CFStringRef uniqueIdentifier)
2608 {
2609 CFArrayRef itemArray = NULL;
2610 CFMutableDictionaryRef query;
2611 OSStatus result;
2612
2613 query = CFDictionaryCreateMutable(NULL,
2614 0,
2615 &kCFTypeDictionaryKeyCallBacks,
2616 &kCFTypeDictionaryValueCallBacks);
2617 CFDictionarySetValue(query, kSecClass , kSecClassGenericPassword);
2618 CFDictionarySetValue(query, kSecAttrService, uniqueIdentifier);
2619 CFDictionarySetValue(query, kSecReturnRef , kCFBooleanTrue);
2620 CFDictionarySetValue(query, kSecMatchLimit , kSecMatchLimitAll);
2621 result = SecItemCopyMatching(query, (CFTypeRef *)&itemArray);
2622 CFRelease(query);
2623 if ((result != noErr) && (itemArray != NULL)) {
2624 CFRelease(itemArray);
2625 itemArray = NULL;
2626 }
2627
2628 return itemArray;
2629 }
2630 #endif // !TARGET_OS_IPHONE
2631
2632 //********************************************************************************
2633 // copyPasswordFromKeychain
2634 // --------------------------------------
2635 // Given a uniqueID, retrieve the password from the keychain
2636 //********************************************************************************
2637 static CFStringRef
2638 copyPasswordFromKeychain(CFStringRef uniqueID)
2639 {
2640 #if !TARGET_OS_IPHONE
2641 CFArrayRef enumerator;
2642 CFIndex n;
2643 CFStringRef password = NULL;
2644
2645 enumerator = copyKeychainEnumerator(uniqueID);
2646 if (enumerator == NULL) {
2647 return NULL; // if no keychain enumerator
2648 }
2649
2650 n = CFArrayGetCount(enumerator);
2651 if (n > 0) {
2652 void *data = NULL;
2653 UInt32 dataLen = 0;
2654 SecKeychainItemRef itemRef;
2655 OSStatus result;
2656
2657 itemRef = (SecKeychainItemRef)CFArrayGetValueAtIndex(enumerator, 0);
2658 result = SecKeychainItemCopyContent(itemRef, // itemRef
2659 NULL, // itemClass
2660 NULL, // attrList
2661 &dataLen, // length
2662 (void *)&data); // outData
2663 if ((result == noErr) && (data != NULL) && (dataLen > 0)) {
2664 password = CFStringCreateWithBytes(NULL, data, dataLen, kCFStringEncodingUTF8, TRUE);
2665 (void) SecKeychainItemFreeContent(NULL, data);
2666 }
2667
2668 }
2669
2670 CFRelease(enumerator);
2671
2672 return password;
2673 #else // !TARGET_OS_IPHONE
2674 return NULL;
2675 #endif // !TARGET_OS_IPHONE
2676 }