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