]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCNetworkConnection.c
configd-289.2.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 dispatch_queue_attr_t attr;
1112 mach_port_t mp;
1113 long res;
1114
1115 connectionPrivate->dispatchQueue = queue;
1116 dispatch_retain(connectionPrivate->dispatchQueue);
1117
1118 attr = dispatch_queue_attr_create();
1119 res = dispatch_queue_attr_set_finalizer(attr,
1120 ^(dispatch_queue_t dq) {
1121 SCNetworkConnectionRef connection;
1122
1123 connection = (SCNetworkConnectionRef)dispatch_get_context(dq);
1124 CFRelease(connection);
1125 });
1126 if (res != 0) {
1127 SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkConnection dispatch_queue_attr_set_finalizer() failed"));
1128 dispatch_release(attr);
1129 goto fail;
1130 }
1131 connectionPrivate->callbackQueue = dispatch_queue_create("com.apple.SCNetworkConnection.notifications", attr);
1132 dispatch_release(attr);
1133 if (connectionPrivate->callbackQueue == NULL){
1134 SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkConnection dispatch_queue_create() failed"));
1135 goto fail;
1136 }
1137 CFRetain(connection); // Note: will be released when the dispatch queue is released
1138 dispatch_set_context(connectionPrivate->callbackQueue, connectionPrivate);
1139
1140 mp = CFMachPortGetPort(connectionPrivate->notify_port);
1141 connectionPrivate->callbackSource = dispatch_source_mig_create(mp, sizeof(mach_msg_header_t), NULL, connectionPrivate->callbackQueue, SCNetworkConnectionNotifyMIGCallback);
1142 if (connectionPrivate->callbackSource == NULL) {
1143 SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkConnection dispatch_source_mig_create() failed"));
1144 goto fail;
1145 }
1146 } else
1147 #endif // !TARGET_OS_IPHONE
1148 {
1149 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, connectionPrivate->rlList)) {
1150 /*
1151 * if we do not already have notifications scheduled with
1152 * this runLoop / runLoopMode
1153 */
1154 CFRunLoopAddSource(runLoop, connectionPrivate->rls, runLoopMode);
1155 }
1156
1157 _SC_schedule(connection, runLoop, runLoopMode, connectionPrivate->rlList);
1158 }
1159
1160 return TRUE;
1161
1162 #if !TARGET_OS_IPHONE
1163 fail :
1164
1165 if (connectionPrivate->callbackSource != NULL) {
1166 dispatch_cancel(connectionPrivate->callbackSource);
1167 dispatch_release(connectionPrivate->callbackSource);
1168 connectionPrivate->callbackSource = NULL;
1169 }
1170 if (connectionPrivate->callbackQueue != NULL) {
1171 dispatch_release(connectionPrivate->callbackQueue);
1172 connectionPrivate->callbackQueue = NULL;
1173 }
1174 if (connectionPrivate->dispatchQueue != NULL) {
1175 dispatch_release(connectionPrivate->dispatchQueue);
1176 connectionPrivate->dispatchQueue = NULL;
1177 }
1178 _SCErrorSet(kSCStatusFailed);
1179 return FALSE;
1180 #endif // !TARGET_OS_IPHONE
1181 }
1182
1183
1184 static Boolean
1185 __SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection,
1186 CFRunLoopRef runLoop,
1187 CFStringRef runLoopMode,
1188 #if !TARGET_OS_IPHONE
1189 dispatch_queue_t queue
1190 #else // !TARGET_OS_IPHONE
1191 void *queue
1192 #endif // !TARGET_OS_IPHONE
1193 )
1194 {
1195 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1196 int error = kSCStatusFailed;
1197 CFIndex n = 0;
1198 mach_port_t session_port;
1199 kern_return_t status;
1200
1201 if ((runLoop != NULL) && !connectionPrivate->scheduled) { // if we should be scheduled (but are not)
1202 _SCErrorSet(kSCStatusInvalidArgument);
1203 return FALSE;
1204 }
1205
1206 #if !TARGET_OS_IPHONE
1207 if (((runLoop == NULL) && (connectionPrivate->dispatchQueue == NULL)) || // if we should be scheduled on a dispatch queue (but are not)
1208 ((runLoop != NULL) && (connectionPrivate->dispatchQueue != NULL))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
1209 _SCErrorSet(kSCStatusInvalidArgument);
1210 return FALSE;
1211 }
1212 #endif // !TARGET_OS_IPHONE
1213
1214 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1215 if (session_port == MACH_PORT_NULL) {
1216 _SCErrorSet(kSCStatusInvalidArgument);
1217 return FALSE;
1218 }
1219
1220 #if !TARGET_OS_IPHONE
1221 if (runLoop == NULL) {
1222 dispatch_cancel(connectionPrivate->callbackSource);
1223 dispatch_release(connectionPrivate->callbackSource);
1224 connectionPrivate->callbackSource = NULL;
1225 dispatch_release(connectionPrivate->callbackQueue);
1226 connectionPrivate->callbackQueue = NULL;
1227 dispatch_release(connectionPrivate->dispatchQueue);
1228 connectionPrivate->dispatchQueue = NULL;
1229 } else
1230 #endif // !TARGET_OS_IPHONE
1231 {
1232 if (!_SC_unschedule(connection, runLoop, runLoopMode, connectionPrivate->rlList, FALSE)) {
1233 // if not currently scheduled on this runLoop / runLoopMode
1234 _SCErrorSet(kSCStatusFailed);
1235 return FALSE;
1236 }
1237
1238 n = CFArrayGetCount(connectionPrivate->rlList);
1239 if (n == 0 || !_SC_isScheduled(NULL, runLoop, runLoopMode, connectionPrivate->rlList)) {
1240 /*
1241 * if we are no longer scheduled to receive notifications for
1242 * this runLoop / runLoopMode
1243 */
1244 CFRunLoopRemoveSource(runLoop, connectionPrivate->rls, runLoopMode);
1245
1246 if (n == 0) {
1247 // if *all* notifications have been unscheduled
1248 CFRelease(connectionPrivate->rlList);
1249 connectionPrivate->rlList = NULL;
1250 CFRunLoopSourceInvalidate(connectionPrivate->rls);
1251 CFRelease(connectionPrivate->rls);
1252 connectionPrivate->rls = NULL;
1253 }
1254 }
1255 }
1256
1257 if (n == 0) {
1258 // if *all* notifications have been unscheduled
1259 connectionPrivate->scheduled = FALSE;
1260
1261 status = pppcontroller_notification(session_port, 0, &error);
1262 if ((status != KERN_SUCCESS) || (error != kSCStatusOK)) {
1263 _SCErrorSet(error);
1264 return FALSE;
1265 }
1266 }
1267
1268 return TRUE;
1269 }
1270
1271
1272 Boolean
1273 SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection,
1274 CFRunLoopRef runLoop,
1275 CFStringRef runLoopMode)
1276 {
1277 if (!isA_SCNetworkConnection(connection) || (runLoop == NULL) || (runLoopMode == NULL)) {
1278 _SCErrorSet(kSCStatusInvalidArgument);
1279 return FALSE;
1280 }
1281
1282 return __SCNetworkConnectionScheduleWithRunLoop(connection, runLoop, runLoopMode, NULL);
1283 }
1284
1285
1286 Boolean
1287 SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection,
1288 CFRunLoopRef runLoop,
1289 CFStringRef runLoopMode)
1290 {
1291 if (!isA_SCNetworkConnection(connection) || (runLoop == NULL) || (runLoopMode == NULL)) {
1292 _SCErrorSet(kSCStatusInvalidArgument);
1293 return FALSE;
1294 }
1295
1296 return __SCNetworkConnectionUnscheduleFromRunLoop(connection, runLoop, runLoopMode, NULL);
1297 }
1298
1299
1300 #if !TARGET_OS_IPHONE
1301 Boolean
1302 SCNetworkConnectionSetDispatchQueue(SCNetworkConnectionRef connection,
1303 dispatch_queue_t queue)
1304 {
1305 Boolean ok = FALSE;
1306
1307 if (!isA_SCNetworkConnection(connection)) {
1308 _SCErrorSet(kSCStatusInvalidArgument);
1309 return FALSE;
1310 }
1311
1312 if (queue != NULL) {
1313 ok = __SCNetworkConnectionScheduleWithRunLoop(connection, NULL, NULL, queue);
1314 } else {
1315 ok = __SCNetworkConnectionUnscheduleFromRunLoop(connection, NULL, NULL, NULL);
1316 }
1317
1318 return ok;
1319 }
1320 #endif // !TARGET_OS_IPHONE
1321
1322
1323 #pragma mark -
1324 #pragma mark User level "dial" API
1325
1326
1327 #define k_NetworkConnect_Notification "com.apple.networkConnect"
1328 #define k_NetworkConnect_Pref_File CFSTR("com.apple.networkConnect")
1329 #define k_InterentConnect_Pref_File CFSTR("com.apple.internetconnect")
1330
1331 #define k_Dial_Default_Key CFSTR("ConnectByDefault") // needs to go into SC
1332 #define k_Last_Service_Id_Key CFSTR("ServiceID")
1333 #define k_Unique_Id_Key CFSTR("UniqueIdentifier")
1334
1335
1336 /* Private Prototypes */
1337 static Boolean SCNetworkConnectionPrivateCopyDefaultServiceIDForDial (SCDynamicStoreRef session, CFStringRef *serviceID);
1338 static Boolean SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore (SCDynamicStoreRef session, CFStringRef *serviceID);
1339 static Boolean SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray, CFDictionaryRef *userOptions);
1340 static Boolean SCNetworkConnectionPrivateIsPPPService (SCDynamicStoreRef session, CFStringRef serviceID, CFStringRef subType1, CFStringRef subType2);
1341 static void addPasswordFromKeychain (SCDynamicStoreRef session, CFStringRef serviceID, CFDictionaryRef *userOptions);
1342 static CFStringRef copyPasswordFromKeychain (CFStringRef uniqueID);
1343
1344 static int notify_userprefs_token = -1;
1345
1346 static CFDictionaryRef onDemand_configuration = NULL;
1347 static pthread_mutex_t onDemand_notify_lock = PTHREAD_MUTEX_INITIALIZER;
1348 static int onDemand_notify_token = -1;
1349
1350 /*
1351 * return TRUE if domain1 ends with domain2, and will check for trailing "."
1352 */
1353 static Boolean
1354 domainEndsWithDomain(CFStringRef domain1, CFStringRef domain2)
1355 {
1356 CFRange range;
1357 Boolean ret = FALSE;
1358 CFStringRef s1 = NULL;
1359 Boolean s1_created = FALSE;
1360 CFStringRef s2 = NULL;
1361 Boolean s2_created = FALSE;
1362
1363 if (CFStringHasSuffix(domain1, CFSTR("."))) {
1364 range.location = 0;
1365 range.length = CFStringGetLength(domain1) - 1;
1366 s1 = CFStringCreateWithSubstring(NULL, domain1, range);
1367 if (s1 == NULL) {
1368 goto done;
1369 }
1370 s1_created = TRUE;
1371 } else {
1372 s1 = domain1;
1373 }
1374
1375 if (CFStringHasSuffix(domain2, CFSTR("."))) {
1376 range.location = 0;
1377 range.length = CFStringGetLength(domain2) - 1;
1378 s2 = CFStringCreateWithSubstring(NULL, domain2, range);
1379 if (s2 == NULL) {
1380 goto done;
1381 }
1382 s2_created = TRUE;
1383 } else {
1384 s2 = domain2;
1385 }
1386
1387 ret = CFStringHasSuffix(s1, s2);
1388
1389 done :
1390
1391 if (s1_created) CFRelease(s1);
1392 if (s2_created) CFRelease(s2);
1393 return ret;
1394 }
1395
1396 /* VPN On Demand */
1397
1398 Boolean
1399 __SCNetworkConnectionCopyOnDemandInfoWithName(SCDynamicStoreRef *storeP,
1400 CFStringRef hostName,
1401 Boolean onDemandRetry,
1402 CFStringRef *connectionServiceID,
1403 SCNetworkConnectionStatus *connectionStatus,
1404 CFStringRef *vpnRemoteAddress) /* CFDictionaryRef *info */
1405 {
1406 int changed = 1;
1407 CFDictionaryRef configuration;
1408 Boolean ok = FALSE;
1409 int status;
1410 SCDynamicStoreRef store = *storeP;
1411 CFArrayRef triggers;
1412 uint64_t triggersCount = 0;
1413 int triggersIndex;
1414
1415 pthread_mutex_lock(&onDemand_notify_lock);
1416 if (onDemand_notify_token == -1) {
1417 status = notify_register_check(kSCNETWORKCONNECTION_ONDEMAND_NOTIFY_KEY, &onDemand_notify_token);
1418 if (status != NOTIFY_STATUS_OK) {
1419 SCLog(TRUE, LOG_ERR, CFSTR("notify_register_check() failed, status=%lu"), status);
1420 onDemand_notify_token = -1;
1421 }
1422 }
1423 if (onDemand_notify_token != -1) {
1424 status = notify_check(onDemand_notify_token, &changed);
1425 if (status != NOTIFY_STATUS_OK) {
1426 SCLog(TRUE, LOG_ERR, CFSTR("notify_check() failed, status=%lu"), status);
1427 (void)notify_cancel(onDemand_notify_token);
1428 onDemand_notify_token = -1;
1429 }
1430 }
1431
1432 if (changed && (onDemand_notify_token != -1)) {
1433 status = notify_get_state(onDemand_notify_token, &triggersCount);
1434 if (status != NOTIFY_STATUS_OK) {
1435 SCLog(TRUE, LOG_ERR, CFSTR("notify_get_state() failed, status=%lu"), status);
1436 (void)notify_cancel(onDemand_notify_token);
1437 onDemand_notify_token = -1;
1438 }
1439 }
1440
1441 if (changed) {
1442 CFStringRef key;
1443
1444 if (_sc_debug || (debug > 0)) {
1445 SCLog(TRUE, LOG_INFO,
1446 CFSTR("OnDemand information %s"),
1447 (onDemand_configuration == NULL) ? "fetched" : "updated");
1448 }
1449
1450 if (onDemand_configuration != NULL) {
1451 CFRelease(onDemand_configuration);
1452 onDemand_configuration = NULL;
1453 }
1454
1455 if (triggersCount > 0) {
1456 if (store == NULL) {
1457 store = SCDynamicStoreCreate(NULL, CFSTR("__SCNetworkConnectionCopyOnDemandInfoWithName"), NULL, NULL);
1458 if (store == NULL) {
1459 SCLog(TRUE, LOG_ERR, CFSTR("__SCNetworkConnectionCopyOnDemandInfoWithName SCDynamicStoreCreate() failed"));
1460
1461 // force retry on next check
1462 if (onDemand_notify_token != -1) {
1463 (void)notify_cancel(onDemand_notify_token);
1464 onDemand_notify_token = -1;
1465 }
1466 pthread_mutex_unlock(&onDemand_notify_lock);
1467 return FALSE;
1468 }
1469 *storeP = store;
1470 }
1471
1472 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetOnDemand);
1473 onDemand_configuration = SCDynamicStoreCopyValue(store, key);
1474 CFRelease(key);
1475 if ((onDemand_configuration != NULL) && !isA_CFDictionary(onDemand_configuration)) {
1476 CFRelease(onDemand_configuration);
1477 onDemand_configuration = NULL;
1478 }
1479 }
1480 }
1481
1482 configuration = (onDemand_configuration != NULL) ? CFRetain(onDemand_configuration) : NULL;
1483 pthread_mutex_unlock(&onDemand_notify_lock);
1484
1485 if (configuration == NULL) {
1486 // if no "OnDemand" configurations
1487 return FALSE;
1488 }
1489
1490 triggers = CFDictionaryGetValue(configuration, kSCNetworkConnectionOnDemandTriggers);
1491 triggersCount = isA_CFArray(triggers) ? CFArrayGetCount(triggers) : 0;
1492 for (triggersIndex = 0; triggersIndex < triggersCount; triggersIndex++) {
1493 CFArrayRef domains;
1494 int domainsCount;
1495 int domainsIndex;
1496 CFStringRef key;
1497 CFDictionaryRef trigger;
1498
1499 trigger = CFArrayGetValueAtIndex(triggers, triggersIndex);
1500 if (!isA_CFDictionary(trigger)) {
1501 // if not a valid "OnDemand" configuration
1502 continue;
1503 }
1504
1505 /*
1506 * If we haven't tried a resulution yet, we only want to check for a name
1507 * match for domains that require to always connect.
1508 */
1509 key = onDemandRetry ? kSCNetworkConnectionOnDemandMatchDomainsOnRetry
1510 : kSCNetworkConnectionOnDemandMatchDomainsAlways;
1511 domains = CFDictionaryGetValue(trigger, key);
1512 domainsCount = isA_CFArray(domains) ? CFArrayGetCount(domains) : 0;
1513 for (domainsIndex = 0; domainsIndex < domainsCount; domainsIndex++) {
1514 CFStringRef domain;
1515
1516 domain = CFArrayGetValueAtIndex(domains, domainsIndex);
1517 if (!isA_CFString(domain)) {
1518 // if not a valid match domain
1519 continue;
1520 }
1521
1522 if (domainEndsWithDomain(hostName, domain)) {
1523 CFArrayRef exceptions;
1524 int exceptionsCount;
1525 int exceptionsIndex;
1526 CFNumberRef num;
1527 SCNetworkConnectionStatus onDemandStatus = kSCNetworkConnectionDisconnected;
1528
1529 // we have a matching domain, check against exception list
1530 exceptions = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandMatchDomainsNever);
1531 exceptionsCount = isA_CFArray(exceptions) ? CFArrayGetCount(exceptions) : 0;
1532 for (exceptionsIndex = 0; exceptionsIndex < exceptionsCount; exceptionsIndex++) {
1533 CFStringRef exception;
1534
1535 exception = CFArrayGetValueAtIndex(exceptions, exceptionsIndex);
1536 if (!isA_CFString(exception)) {
1537 // if not a valid match exception
1538 continue;
1539 }
1540
1541 if (domainEndsWithDomain(hostName, exception)) {
1542 // found matching exception
1543 if (_sc_debug || (debug > 0)) {
1544 SCLog(TRUE, LOG_INFO, CFSTR("OnDemand match exception"));
1545 }
1546 goto done;
1547 }
1548 }
1549
1550 // if we have a matching domain and there were no exceptions
1551 // then we pass back the OnDemand info
1552
1553 if (!CFDictionaryGetValueIfPresent(trigger,
1554 kSCNetworkConnectionOnDemandStatus,
1555 (const void **)&num) ||
1556 !isA_CFNumber(num) ||
1557 !CFNumberGetValue(num, kCFNumberSInt32Type, &onDemandStatus)) {
1558 onDemandStatus = kSCNetworkConnectionDisconnected;
1559 }
1560 if (connectionStatus != NULL) {
1561 *connectionStatus = onDemandStatus;
1562 }
1563
1564 if (connectionServiceID != NULL) {
1565 *connectionServiceID = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandServiceID);
1566 *connectionServiceID = isA_CFString(*connectionServiceID);
1567 if (*connectionServiceID != NULL) {
1568 CFRetain(*connectionServiceID);
1569 }
1570
1571 }
1572
1573 if (vpnRemoteAddress != NULL) {
1574 *vpnRemoteAddress = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandRemoteAddress);
1575 *vpnRemoteAddress = isA_CFString(*vpnRemoteAddress);
1576 if (*vpnRemoteAddress != NULL) {
1577 CFRetain(*vpnRemoteAddress);
1578 }
1579 }
1580
1581 if (_sc_debug || (debug > 0)) {
1582 SCLog(TRUE, LOG_INFO,
1583 CFSTR("OnDemand%s match, connection status = %d"),
1584 onDemandRetry ? " (on retry)" : "",
1585 onDemandStatus);
1586 }
1587
1588 ok = TRUE;
1589 goto done;
1590 }
1591 }
1592 }
1593
1594 // if (_sc_debug || (debug > 0)) {
1595 // SCLog(TRUE, LOG_INFO, CFSTR("OnDemand domain name(s) not matched"));
1596 // }
1597
1598 done :
1599
1600 if (configuration != NULL) CFRelease(configuration);
1601 return ok;
1602 }
1603
1604
1605 Boolean
1606 SCNetworkConnectionCopyUserPreferences(CFDictionaryRef selectionOptions,
1607 CFStringRef *serviceID,
1608 CFDictionaryRef *userOptions)
1609 {
1610 int prefsChanged = 1;
1611 SCDynamicStoreRef session = NULL;
1612 Boolean success = FALSE;
1613 int status;
1614
1615
1616 /* initialize runtime */
1617 pthread_once(&initialized, __SCNetworkConnectionInitialize);
1618
1619 /* first check for new VPN OnDemand style */
1620 if (selectionOptions != NULL) {
1621 CFStringRef hostName;
1622
1623 hostName = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName);
1624 if (isA_CFString(hostName)) {
1625 CFStringRef connectionServiceID = NULL;
1626 SCNetworkConnectionStatus connectionStatus;
1627 Boolean onDemandRetry;
1628 CFTypeRef val;
1629
1630 val = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandRetry);
1631 onDemandRetry = isA_CFBoolean(val) ? CFBooleanGetValue(val) : TRUE;
1632
1633 success = __SCNetworkConnectionCopyOnDemandInfoWithName(&session,
1634 hostName,
1635 onDemandRetry,
1636 &connectionServiceID,
1637 &connectionStatus,
1638 NULL);
1639 if (debug > 1) {
1640 SCLog(TRUE, LOG_DEBUG,
1641 CFSTR("SCNetworkConnectionCopyUserPreferences __SCNetworkConnectionCopyOnDemandInfoWithName returns %d w/status %d"),
1642 success,
1643 connectionStatus);
1644 }
1645
1646 if (success) {
1647 // if the hostname matches an OnDemand domain
1648 if (session != NULL) {
1649 CFRelease(session);
1650 }
1651 if (connectionStatus == kSCNetworkConnectionConnected) {
1652 // if we are already connected
1653 if (connectionServiceID != NULL) {
1654 CFRelease(connectionServiceID);
1655 }
1656 return FALSE;
1657 }
1658
1659 *serviceID = connectionServiceID;
1660 *userOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1661 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, hostName);
1662 return TRUE;
1663 } else if (!onDemandRetry) {
1664 // if the hostname does not match an OnDemand domain and we have
1665 // not yet issued an initial DNS query (i.e. it's not a query
1666 // being retried after the VPN has been established) than we're
1667 // done
1668 if (session != NULL) {
1669 CFRelease(session);
1670 }
1671 return FALSE;
1672 }
1673 }
1674 }
1675
1676 if (notify_userprefs_token == -1) {
1677 status = notify_register_check(k_NetworkConnect_Notification, &notify_userprefs_token);
1678 if (status != NOTIFY_STATUS_OK) {
1679 SCLog(TRUE, LOG_ERR, CFSTR("notify_register_check() failed, status=%lu"), status);
1680 (void)notify_cancel(notify_userprefs_token);
1681 notify_userprefs_token = -1;
1682 } else {
1683 // clear the "something has changed" state
1684 (void) notify_check(notify_userprefs_token, &prefsChanged);
1685 prefsChanged = 1;
1686 }
1687 }
1688 if (notify_userprefs_token != -1) {
1689 status = notify_check(notify_userprefs_token, &prefsChanged);
1690 if (status != NOTIFY_STATUS_OK) {
1691 SCLog(TRUE, LOG_ERR, CFSTR("notify_check() failed, status=%lu"), status);
1692 (void)notify_cancel(notify_userprefs_token);
1693 notify_userprefs_token = -1;
1694 }
1695 }
1696
1697
1698 *serviceID = NULL;
1699 *userOptions = NULL;
1700
1701 if (session == NULL) {
1702 session = SCDynamicStoreCreate(NULL, CFSTR("SCNetworkConnection"), NULL, NULL);
1703 }
1704 if (session == NULL) {
1705 SCLog(TRUE, LOG_ERR, CFSTR("Error, SCNetworkConnectionCopyUserPreferences, SCDynamicStoreCreate() returned NULL!"));
1706 return FALSE;
1707 }
1708
1709 if (selectionOptions != NULL) {
1710 Boolean catchAllFound = FALSE;
1711 CFIndex catchAllService = 0;
1712 CFIndex catchAllConfig = 0;
1713 CFStringRef hostName = NULL;
1714 CFStringRef priority = NULL;
1715 CFArrayRef serviceNames = NULL;
1716 CFDictionaryRef services = NULL;
1717 CFIndex serviceIndex;
1718 CFIndex servicesCount;
1719
1720 hostName = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName);
1721 if (hostName == NULL) {
1722 hostName = CFDictionaryGetValue(selectionOptions, kSCPropNetPPPOnDemandHostName);
1723 }
1724 hostName = isA_CFString(hostName);
1725 if (hostName == NULL)
1726 goto done_selection; // if no hostname for matching
1727
1728 priority = CFDictionaryGetValue(selectionOptions, kSCPropNetPPPOnDemandPriority);
1729 if (!isA_CFString(priority))
1730 priority = kSCValNetPPPOnDemandPriorityDefault;
1731
1732
1733 if (!isA_CFArray(serviceNames))
1734 goto done_selection;
1735
1736
1737 if (!isA_CFDictionary(services))
1738 goto done_selection;
1739
1740 servicesCount = CFArrayGetCount(serviceNames);
1741 for (serviceIndex = 0; serviceIndex < servicesCount; serviceIndex++) {
1742 CFIndex configIndex;
1743 CFIndex configsCount;
1744 CFArrayRef serviceConfigs;
1745 CFStringRef serviceName;
1746 int val;
1747
1748 serviceName = CFArrayGetValueAtIndex(serviceNames, serviceIndex);
1749 if (!isA_CFString(serviceName))
1750 continue;
1751
1752 serviceConfigs = CFDictionaryGetValue(services, serviceName);
1753 if (!isA_CFArray(serviceConfigs))
1754 continue;
1755
1756 configsCount = CFArrayGetCount(serviceConfigs);
1757 for (configIndex = 0; configIndex < configsCount; configIndex++) {
1758 CFNumberRef autodial;
1759 CFDictionaryRef config;
1760 CFDictionaryRef pppConfig;
1761
1762 config = CFArrayGetValueAtIndex(serviceConfigs, configIndex);
1763 if (!isA_CFDictionary(config))
1764 continue;
1765
1766 pppConfig = CFDictionaryGetValue(config, kSCEntNetPPP);
1767 if (!isA_CFDictionary(pppConfig))
1768 continue;
1769
1770 autodial = CFDictionaryGetValue(pppConfig, kSCPropNetPPPOnDemandEnabled);
1771 if (!isA_CFNumber(autodial))
1772 continue;
1773
1774 CFNumberGetValue(autodial, kCFNumberIntType, &val);
1775 if (val) {
1776 CFArrayRef domains;
1777 CFIndex domainsCount;
1778 CFIndex domainsIndex;
1779
1780 /* we found an conditional connection enabled configuration */
1781
1782 /* check domain */
1783 domains = CFDictionaryGetValue(pppConfig, kSCPropNetPPPOnDemandDomains);
1784 if (!isA_CFArray(domains))
1785 continue;
1786
1787 domainsCount = CFArrayGetCount(domains);
1788 for (domainsIndex = 0; domainsIndex < domainsCount; domainsIndex++) {
1789 CFStringRef domain;
1790
1791 domain = CFArrayGetValueAtIndex(domains, domainsIndex);
1792 if (!isA_CFString(domain))
1793 continue;
1794
1795 if (!catchAllFound &&
1796 (CFStringCompare(domain, CFSTR(""), 0) == kCFCompareEqualTo
1797 || CFStringCompare(domain, CFSTR("."), 0) == kCFCompareEqualTo)) {
1798 // found a catch all
1799 catchAllFound = TRUE;
1800 catchAllService = serviceIndex;
1801 catchAllConfig = configIndex;
1802 }
1803
1804 if (domainEndsWithDomain(hostName, domain)) {
1805 // found matching configuration
1806 *serviceID = serviceName;
1807 CFRetain(*serviceID);
1808 *userOptions = CFDictionaryCreateMutableCopy(NULL, 0, config);
1809 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, hostName);
1810 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCPropNetPPPOnDemandPriority, priority);
1811 addPasswordFromKeychain(session, *serviceID, userOptions);
1812 success = TRUE;
1813 goto done_selection;
1814 }
1815 }
1816 }
1817 }
1818 }
1819
1820 // config not found, do we have a catchall ?
1821 if (catchAllFound) {
1822 CFDictionaryRef config;
1823 CFArrayRef serviceConfigs;
1824 CFStringRef serviceName;
1825
1826 serviceName = CFArrayGetValueAtIndex(serviceNames, catchAllService);
1827 serviceConfigs = CFDictionaryGetValue(services, serviceName);
1828 config = CFArrayGetValueAtIndex(serviceConfigs, catchAllConfig);
1829
1830 *serviceID = serviceName;
1831 CFRetain(*serviceID);
1832 *userOptions = CFDictionaryCreateMutableCopy(NULL, 0, config);
1833 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, hostName);
1834 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCPropNetPPPOnDemandPriority, priority);
1835 addPasswordFromKeychain(session, *serviceID, userOptions);
1836 success = TRUE;
1837 goto done_selection;
1838 }
1839
1840 done_selection:
1841
1842 if (serviceNames)
1843 CFRelease(serviceNames);
1844 if (services)
1845 CFRelease(services);
1846 CFRelease(session);
1847
1848 if (debug > 1) {
1849 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionCopyUserPreferences %@"), success ? CFSTR("succeeded") : CFSTR("failed"));
1850 SCLog(TRUE, LOG_DEBUG, CFSTR("Selection options: %@"), selectionOptions);
1851 }
1852
1853 return success;
1854 }
1855
1856 /* we don't have selection options */
1857
1858 // (1) Figure out which service ID we care about, allocate it into passed "serviceID"
1859 success = SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(session, serviceID);
1860
1861 if (success && (*serviceID != NULL)) {
1862 // (2) Get the list of user data for this service ID
1863 CFPropertyListRef userServices = NULL;
1864
1865
1866 // (3) We are expecting an array if the user has defined records for this service ID or NULL if the user hasn't
1867 if (userServices != NULL) {
1868 if (isA_CFArray(userServices)) {
1869 // (4) Get the default set of user options for this service
1870 success = SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray((CFArrayRef)userServices,
1871 userOptions);
1872 if(success && (userOptions != NULL)) {
1873 addPasswordFromKeychain(session, *serviceID, userOptions);
1874 }
1875 } else {
1876 SCLog(TRUE, LOG_DEBUG, CFSTR("Error, userServices are not of type CFArray!"));
1877 }
1878
1879 CFRelease(userServices); // this is OK because SCNetworkConnectionPrivateISExpectedCFType() checks for NULL
1880 }
1881 }
1882
1883 if (debug > 1) {
1884 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionCopyUserPreferences %@, no selection options"), success ? CFSTR("succeeded") : CFSTR("failed"));
1885 }
1886
1887 CFRelease(session);
1888 return success;
1889 }
1890
1891
1892 //*******************************************************************************************
1893 // SCNetworkConnectionPrivateCopyDefaultServiceIDForDial
1894 // ----------------------------------------------------
1895 // Try to find the service id to connect
1896 // (1) Start by looking at the last service used in Network Pref / Network menu extra
1897 // (2) If Network Pref / Network menu extra has not been used, find the PPP service
1898 // with the highest ordering
1899 //********************************************************************************************
1900 static Boolean
1901 SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(SCDynamicStoreRef session, CFStringRef *serviceID)
1902 {
1903 Boolean foundService = FALSE;
1904 CFPropertyListRef lastServiceSelectedInIC = NULL;
1905
1906
1907
1908 // we found the service the user last had open in IC
1909 if (lastServiceSelectedInIC != NULL) {
1910 // make sure its a PPP service
1911 if (SCNetworkConnectionPrivateIsPPPService(session, lastServiceSelectedInIC, kSCValNetInterfaceSubTypePPPSerial, kSCValNetInterfaceSubTypePPPoE)) {
1912 // make sure the service that we found is valid
1913 CFDictionaryRef dict;
1914 CFStringRef key;
1915
1916 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
1917 kSCDynamicStoreDomainSetup,
1918 lastServiceSelectedInIC,
1919 kSCEntNetInterface);
1920 dict = SCDynamicStoreCopyValue(session, key);
1921 CFRelease(key);
1922 if (dict != NULL) {
1923 CFRelease(dict);
1924 *serviceID = CFRetain(lastServiceSelectedInIC);
1925 foundService = TRUE;
1926 }
1927 }
1928 CFRelease(lastServiceSelectedInIC);
1929 }
1930
1931 if (!foundService) {
1932 foundService = SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(session, serviceID);
1933 }
1934
1935 return foundService;
1936 }
1937
1938 //********************************************************************************
1939 // SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore
1940 // -------------------------------------------------------
1941 // Find the highest ordered PPP service in the dynamic store
1942 //********************************************************************************
1943 static Boolean
1944 SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(SCDynamicStoreRef session, CFStringRef *serviceID)
1945 {
1946 CFDictionaryRef dict = NULL;
1947 CFStringRef key = NULL;
1948 CFArrayRef serviceIDs = NULL;
1949 Boolean success = FALSE;
1950
1951 *serviceID = NULL;
1952
1953 do {
1954 CFIndex count;
1955 CFIndex i;
1956
1957 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainSetup, kSCEntNetIPv4);
1958 if (key == NULL) {
1959 fprintf(stderr, "Error, Setup Key == NULL!\n");
1960 break;
1961 }
1962
1963 dict = SCDynamicStoreCopyValue(session, key);
1964 if (!isA_CFDictionary(dict)) {
1965 fprintf(stderr, "no global IPv4 entity\n");
1966 break;
1967 }
1968
1969 serviceIDs = CFDictionaryGetValue(dict, kSCPropNetServiceOrder); // array of service id's
1970 if (!isA_CFArray(serviceIDs)) {
1971 fprintf(stderr, "service order not specified\n");
1972 break;
1973 }
1974
1975 count = CFArrayGetCount(serviceIDs);
1976 for (i = 0; i < count; i++) {
1977 CFStringRef service = CFArrayGetValueAtIndex(serviceIDs, i);
1978
1979 if (SCNetworkConnectionPrivateIsPPPService(session, service, kSCValNetInterfaceSubTypePPPSerial, kSCValNetInterfaceSubTypePPPoE)) {
1980 *serviceID = CFRetain(service);
1981 success = TRUE;
1982 break;
1983 }
1984 }
1985 } while (FALSE);
1986
1987 if (key != NULL) CFRelease(key);
1988 if (dict != NULL) CFRelease(dict);
1989
1990 return success;
1991 }
1992
1993 //********************************************************************************
1994 // SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray
1995 // ---------------------------------------------------------
1996 // Copy over user preferences for a particular service if they exist
1997 //********************************************************************************
1998 static Boolean
1999 SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray, CFDictionaryRef *userOptions)
2000 {
2001 CFIndex count = CFArrayGetCount(userOptionsArray);
2002 int i;
2003
2004 for (i = 0; i < count; i++) {
2005 // (1) Find the dictionary
2006 CFPropertyListRef propertyList = CFArrayGetValueAtIndex(userOptionsArray, i);
2007
2008 if (isA_CFDictionary(propertyList) != NULL) {
2009 // See if there's a value for dial on demand
2010 CFPropertyListRef value;
2011
2012 value = CFDictionaryGetValue((CFDictionaryRef)propertyList, k_Dial_Default_Key);
2013 if (isA_CFBoolean(value) != NULL) {
2014 if (CFBooleanGetValue(value)) {
2015 // we found the default user options
2016 *userOptions = CFDictionaryCreateCopy(NULL,
2017 (CFDictionaryRef)propertyList);
2018 break;
2019 }
2020 }
2021 }
2022 }
2023
2024 return TRUE;
2025 }
2026
2027 //********************************************************************************
2028 // SCNetworkConnectionPrivateIsServiceType
2029 // --------------------------------------
2030 // Check and see if the service is a PPP service of the given types
2031 //********************************************************************************
2032 static Boolean
2033 SCNetworkConnectionPrivateIsPPPService(SCDynamicStoreRef session, CFStringRef serviceID, CFStringRef subType1, CFStringRef subType2)
2034 {
2035 CFStringRef entityKey;
2036 Boolean isPPPService = FALSE;
2037 Boolean isMatchingSubType = FALSE;
2038 CFDictionaryRef serviceDict;
2039
2040 entityKey = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
2041 kSCDynamicStoreDomainSetup,
2042 serviceID,
2043 kSCEntNetInterface);
2044 if (entityKey == NULL) {
2045 return FALSE;
2046 }
2047
2048 serviceDict = SCDynamicStoreCopyValue(session, entityKey);
2049 if (serviceDict != NULL) {
2050 if (isA_CFDictionary(serviceDict)) {
2051 CFStringRef type;
2052 CFStringRef subtype;
2053
2054 type = CFDictionaryGetValue(serviceDict, kSCPropNetInterfaceType);
2055 if (isA_CFString(type)) {
2056 isPPPService = CFEqual(type, kSCValNetInterfaceTypePPP);
2057 }
2058
2059 subtype = CFDictionaryGetValue(serviceDict, kSCPropNetInterfaceSubType);
2060 if (isA_CFString(subtype)) {
2061 isMatchingSubType = CFEqual(subtype, subType1);
2062 if (!isMatchingSubType && subType2)
2063 isMatchingSubType = CFEqual(subtype, subType2);
2064 }
2065 }
2066 CFRelease(serviceDict);
2067 }
2068 CFRelease(entityKey);
2069
2070 return (isPPPService && isMatchingSubType);
2071 }
2072
2073 //********************************************************************************
2074 // addPasswordFromKeychain
2075 // --------------------------------------
2076 // Get the password and shared secret out of the keychain and add
2077 // them to the PPP and IPSec dictionaries
2078 //********************************************************************************
2079 static void
2080 addPasswordFromKeychain(SCDynamicStoreRef session, CFStringRef serviceID, CFDictionaryRef *userOptions)
2081 {
2082 CFPropertyListRef uniqueID;
2083 CFStringRef password;
2084 CFStringRef sharedsecret = NULL;
2085
2086 /* user options must exist */
2087 if (*userOptions == NULL)
2088 return;
2089
2090 /* first, get the unique identifier used to store passwords in the keychain */
2091 uniqueID = CFDictionaryGetValue(*userOptions, k_Unique_Id_Key);
2092 if (!isA_CFString(uniqueID))
2093 return;
2094
2095 /* first, get the PPP password */
2096 password = copyPasswordFromKeychain(uniqueID);
2097
2098 /* then, if necessary, get the IPSec Shared Secret */
2099 if (SCNetworkConnectionPrivateIsPPPService(session, serviceID, kSCValNetInterfaceSubTypeL2TP, 0)) {
2100 CFMutableStringRef uniqueIDSS;
2101
2102 uniqueIDSS = CFStringCreateMutableCopy(NULL, 0, uniqueID);
2103 CFStringAppend(uniqueIDSS, CFSTR(".SS"));
2104 sharedsecret = copyPasswordFromKeychain(uniqueIDSS);
2105 CFRelease(uniqueIDSS);
2106 }
2107
2108 /* did we find our information in the key chain ? */
2109 if ((password != NULL) || (sharedsecret != NULL)) {
2110 CFMutableDictionaryRef newOptions;
2111
2112 newOptions = CFDictionaryCreateMutableCopy(NULL, 0, *userOptions);
2113
2114 /* PPP password */
2115 if (password != NULL) {
2116 CFDictionaryRef entity;
2117 CFMutableDictionaryRef newEntity;
2118
2119 entity = CFDictionaryGetValue(*userOptions, kSCEntNetPPP);
2120 if (isA_CFDictionary(entity))
2121 newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity);
2122 else
2123 newEntity = CFDictionaryCreateMutable(NULL,
2124 0,
2125 &kCFTypeDictionaryKeyCallBacks,
2126 &kCFTypeDictionaryValueCallBacks);
2127
2128
2129 /* set the PPP password */
2130 CFDictionarySetValue(newEntity, kSCPropNetPPPAuthPassword, uniqueID);
2131 CFDictionarySetValue(newEntity, kSCPropNetPPPAuthPasswordEncryption, kSCValNetPPPAuthPasswordEncryptionKeychain);
2132 CFRelease(password);
2133
2134 /* update the PPP entity */
2135 CFDictionarySetValue(newOptions, kSCEntNetPPP, newEntity);
2136 CFRelease(newEntity);
2137 }
2138
2139 /* IPSec Shared Secret */
2140 if (sharedsecret != NULL) {
2141 CFDictionaryRef entity;
2142 CFMutableDictionaryRef newEntity;
2143
2144 entity = CFDictionaryGetValue(*userOptions, kSCEntNetIPSec);
2145 if (isA_CFDictionary(entity))
2146 newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity);
2147 else
2148 newEntity = CFDictionaryCreateMutable(NULL,
2149 0,
2150 &kCFTypeDictionaryKeyCallBacks,
2151 &kCFTypeDictionaryValueCallBacks);
2152
2153 /* set the IPSec Shared Secret */
2154 CFDictionarySetValue(newEntity, kSCPropNetIPSecSharedSecret, sharedsecret);
2155 CFRelease(sharedsecret);
2156
2157 /* update the IPSec entity */
2158 CFDictionarySetValue(newOptions, kSCEntNetIPSec, newEntity);
2159 CFRelease(newEntity);
2160 }
2161
2162 /* update the userOptions dictionary */
2163 CFRelease(*userOptions);
2164 *userOptions = CFDictionaryCreateCopy(NULL, newOptions);
2165 CFRelease(newOptions);
2166 }
2167
2168 }
2169
2170 #if !TARGET_OS_IPHONE
2171 //********************************************************************************
2172 // copyKeychainEnumerator
2173 // --------------------------------------
2174 // Gather Keychain Enumerator
2175 //********************************************************************************
2176 static CFArrayRef
2177 copyKeychainEnumerator(CFStringRef uniqueIdentifier)
2178 {
2179 char *buf;
2180 CFMutableArrayRef itemArray = NULL;
2181 OSStatus result;
2182 SecKeychainSearchRef search = NULL;
2183
2184 buf = _SC_cfstring_to_cstring(uniqueIdentifier, NULL, 0, kCFStringEncodingUTF8);
2185 if (buf != NULL) {
2186 // search for unique identifier in "svce" attribute
2187 SecKeychainAttribute attributes[] = {{ kSecServiceItemAttr,
2188 CFStringGetLength(uniqueIdentifier),
2189 (void *)buf
2190 }};
2191
2192 SecKeychainAttributeList attrList = { sizeof(attributes) / sizeof(*attributes),
2193 attributes };
2194
2195 result = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attrList, &search);
2196 if (result == noErr) {
2197 itemArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2198
2199 while (result == noErr) {
2200 SecKeychainItemRef itemFound = NULL;
2201
2202 result = SecKeychainSearchCopyNext(search, &itemFound);
2203 if (result != noErr) {
2204 break;
2205 }
2206
2207 if (itemFound) {
2208 CFArrayAppendValue(itemArray, itemFound);
2209 CFRelease(itemFound);
2210 }
2211 }
2212 }
2213 }
2214
2215 if (search) CFRelease(search);
2216 if (buf) CFAllocatorDeallocate(NULL, buf);
2217
2218 return itemArray;
2219 }
2220 #endif // !TARGET_OS_IPHONE
2221
2222 //********************************************************************************
2223 // copyPasswordFromKeychain
2224 // --------------------------------------
2225 // Given a uniqueID, retrieve the password from the keychain
2226 //********************************************************************************
2227 static CFStringRef
2228 copyPasswordFromKeychain(CFStringRef uniqueID)
2229 {
2230 #if !TARGET_OS_IPHONE
2231 CFArrayRef enumerator;
2232 CFIndex n;
2233 CFStringRef password = NULL;
2234
2235 enumerator = copyKeychainEnumerator(uniqueID);
2236 if (enumerator == NULL) {
2237 return NULL; // if no keychain enumerator
2238 }
2239
2240 n = CFArrayGetCount(enumerator);
2241 if (n > 0) {
2242 void *data = NULL;
2243 UInt32 dataLen = 0;
2244 SecKeychainItemRef itemRef;
2245 OSStatus result;
2246
2247 itemRef = (SecKeychainItemRef)CFArrayGetValueAtIndex(enumerator, 0);
2248 result = SecKeychainItemCopyContent(itemRef, // itemRef
2249 NULL, // itemClass
2250 NULL, // attrList
2251 &dataLen, // length
2252 (void *)&data); // outData
2253 if ((result == noErr) && (data != NULL) && (dataLen > 0)) {
2254 password = CFStringCreateWithBytes(NULL, data, dataLen, kCFStringEncodingUTF8, TRUE);
2255 (void) SecKeychainItemFreeContent(NULL, data);
2256 }
2257
2258 }
2259
2260 CFRelease(enumerator);
2261
2262 return password;
2263 #else // !TARGET_OS_IPHONE
2264 return NULL;
2265 #endif // !TARGET_OS_IPHONE
2266 }