]> git.saurik.com Git - apple/configd.git/blobdiff - SystemConfiguration.fproj/SCNetworkConnection.c
configd-84.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCNetworkConnection.c
diff --git a/SystemConfiguration.fproj/SCNetworkConnection.c b/SystemConfiguration.fproj/SCNetworkConnection.c
new file mode 100644 (file)
index 0000000..b50cda0
--- /dev/null
@@ -0,0 +1,1201 @@
+/*
+ * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*
+ * Modification History
+ *
+ * December 20, 2002           Christophe Allie <callie@apple.com>
+ * - initial revision
+ */
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreFoundation/CFRuntime.h>
+
+#include <Security/Security.h>
+#include "dy_framework.h"
+
+#include <SystemConfiguration/SystemConfiguration.h>
+#include <SystemConfiguration/SCPrivate.h>
+#include <SystemConfiguration/SCValidation.h>
+
+#include <pthread.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#include "ppp.h"
+
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+
+typedef struct {
+
+       /* base CFType information */
+       CFRuntimeBase   cfBase;
+
+       /* service ID */
+       CFStringRef     serviceID;              /* serviceID */
+
+       int             eventRef;               /* ref to PPP controller for event messages */
+       CFSocketRef     eventRefCF;             /* ref to PPP controller for event messages */
+       int             controlRef;             /* ref to PPP controller for control messages */
+       //u_int32_t     status;                 /* current status of the connection */
+       //char          ifname[IFNAMSIZ];       /* ppp interface used for this connection */
+
+       /* run loop source, callout, context, rl scheduling info */
+       CFRunLoopSourceRef              rls;
+       SCNetworkConnectionCallBack     rlsFunction;
+       SCNetworkConnectionContext      rlsContext;
+       CFMutableArrayRef               rlList;
+
+} SCNetworkConnectionPrivate, *SCNetworkConnectionPrivateRef;
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+
+static __inline__ CFTypeRef
+isA_SCNetworkConnection(CFTypeRef obj)
+{
+       return (isA_CFType(obj, SCNetworkConnectionGetTypeID()));
+}
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+
+static CFStringRef
+__SCNetworkConnectionCopyDescription(CFTypeRef cf)
+{
+       CFAllocatorRef                  allocator = CFGetAllocator(cf);
+       CFMutableStringRef              result;
+       SCNetworkConnectionPrivateRef   connectionPrivate = (SCNetworkConnectionPrivateRef)cf;
+
+       result = CFStringCreateMutable(allocator, 0);
+       CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkConnection, %p [%p]> {\n"), cf, allocator);
+       CFStringAppendFormat(result, NULL, CFSTR("   serviceID = %@ \n"), connectionPrivate->serviceID);
+       CFStringAppendFormat(result, NULL, CFSTR("}"));
+
+       return result;
+}
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+static void
+__SCNetworkConnectionDeallocate(CFTypeRef cf)
+{
+       SCNetworkConnectionPrivateRef   connectionPrivate = (SCNetworkConnectionPrivateRef)cf;
+
+       SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCNetworkConnectionDeallocate:"));
+
+       /* release resources */
+       if (connectionPrivate->eventRef != -1) {
+               while (CFArrayGetCount(connectionPrivate->rlList)) {
+                       CFRunLoopRef    runLoop;
+                       CFStringRef             runLoopMode;
+
+                       runLoop = (CFRunLoopRef)CFArrayGetValueAtIndex(connectionPrivate->rlList, 1);
+                       runLoopMode = CFArrayGetValueAtIndex(connectionPrivate->rlList, 2);
+                       CFRunLoopRemoveSource(runLoop, connectionPrivate->rls, runLoopMode);
+
+                       CFArrayRemoveValueAtIndex(connectionPrivate->rlList, 2);
+                       CFArrayRemoveValueAtIndex(connectionPrivate->rlList, 1);
+                       CFArrayRemoveValueAtIndex(connectionPrivate->rlList, 0);
+               }
+               CFRelease(connectionPrivate->rls);
+               CFRelease(connectionPrivate->rlList);
+               //PPPDispose(connectionPrivate->eventRef);
+               CFSocketInvalidate(connectionPrivate->eventRefCF);
+               CFRelease(connectionPrivate->eventRefCF);
+       }
+       if (connectionPrivate->controlRef != -1)
+               PPPDispose(connectionPrivate->controlRef);
+       if (connectionPrivate->rlsContext.release)
+               connectionPrivate->rlsContext.release(connectionPrivate->rlsContext.info);
+       if (connectionPrivate->serviceID)
+               CFRelease(connectionPrivate->serviceID);
+
+       return;
+}
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+
+static pthread_once_t initialized              = PTHREAD_ONCE_INIT;
+
+static CFTypeID __kSCNetworkConnectionTypeID   = _kCFRuntimeNotATypeID;
+
+static const CFRuntimeClass __SCNetworkConnectionClass = {
+       0,                                      // version
+       "SCNetworkConnection",                  // className
+       NULL,                                   // init
+       NULL,                                   // copy
+       __SCNetworkConnectionDeallocate,        // dealloc
+       NULL,                                   // equal
+       NULL,                                   // hash
+       NULL,                                   // copyFormattingDesc
+       __SCNetworkConnectionCopyDescription    // copyDebugDesc
+};
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+
+static void
+__SCNetworkConnectionInitialize(void)
+{
+       __kSCNetworkConnectionTypeID = _CFRuntimeRegisterClass(&__SCNetworkConnectionClass);
+       return;
+}
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+static SCNetworkConnectionPrivateRef
+__SCNetworkConnectionCreatePrivate(CFAllocatorRef allocator, CFStringRef serviceID)
+{
+       SCNetworkConnectionPrivateRef   connectionPrivate = 0;
+       uint32_t                        size;
+       struct ppp_status               *stats = 0;
+       int                             error = kSCStatusFailed;
+
+       /* initialize runtime */
+       pthread_once(&initialized, __SCNetworkConnectionInitialize);
+
+       SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCNetworkConnectionCreatePrivate:"));
+
+       /* allocate NetworkConnection */
+       size = sizeof(SCNetworkConnectionPrivate) - sizeof(CFRuntimeBase);
+       connectionPrivate = (SCNetworkConnectionPrivateRef)_CFRuntimeCreateInstance(allocator, __kSCNetworkConnectionTypeID,size, NULL);
+       if (connectionPrivate == 0)
+               goto fail;
+
+       /* zero the data structure */
+       bzero(((u_char*)connectionPrivate)+sizeof(CFRuntimeBase), size);
+
+       /* save the serviceID */
+       connectionPrivate->serviceID = CFStringCreateCopy(NULL, serviceID);
+
+       connectionPrivate->controlRef = -1;
+       connectionPrivate->eventRef = -1;
+
+       if (PPPInit(&connectionPrivate->controlRef))
+               goto fail;
+
+       if (PPPStatus(connectionPrivate->controlRef, serviceID, 0, &stats)) {
+               error = kSCStatusInvalidArgument;       // XXX can't get status, invalid service id
+               goto fail;
+       }
+
+       CFAllocatorDeallocate(NULL, stats);
+       stats = 0;
+
+       /* success, return the connection reference */
+       return connectionPrivate;
+
+    fail:
+
+       /* failure, clean up and leave */
+       if (connectionPrivate)
+               CFRelease(connectionPrivate);
+       if (stats)
+               CFAllocatorDeallocate(NULL, stats);
+       _SCErrorSet(error);
+       return NULL;
+}
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+
+CFTypeID
+SCNetworkConnectionGetTypeID (void) {
+       pthread_once(&initialized, __SCNetworkConnectionInitialize);    /* initialize runtime */
+       return __kSCNetworkConnectionTypeID;
+}
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+static SCNetworkConnectionStatus
+__SCNetworkConnectionConvertStatus (int state)
+{
+       SCNetworkConnectionStatus       status = kSCNetworkConnectionDisconnected;
+
+       switch (state) {
+               case PPP_INITIALIZE:
+               case PPP_CONNECTLINK:
+               case PPP_ESTABLISH:
+               case PPP_AUTHENTICATE:
+               case PPP_CALLBACK:
+               case PPP_NETWORK:
+               case PPP_WAITONBUSY:
+                       status = kSCNetworkConnectionConnecting;
+                       break;
+               case PPP_TERMINATE:
+               case PPP_DISCONNECTLINK:
+               case PPP_HOLDOFF:
+                       status = kSCNetworkConnectionDisconnecting;
+                       break;
+               case PPP_RUNNING:
+               case PPP_ONHOLD:
+                       status = kSCNetworkConnectionConnected;
+                       break;
+               case PPP_IDLE:
+               case PPP_STATERESERVED:
+               default:
+                       status = kSCNetworkConnectionDisconnected;
+       }
+       return status;
+}
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+
+SCNetworkConnectionRef
+SCNetworkConnectionCreateWithServiceID (CFAllocatorRef                 allocator,
+                                       CFStringRef                     serviceID,
+                                       SCNetworkConnectionCallBack     callout,
+                                       SCNetworkConnectionContext      *context)
+{
+       SCNetworkConnectionPrivateRef   connectionPrivate;
+
+       if (!isA_CFString(serviceID)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
+       connectionPrivate = __SCNetworkConnectionCreatePrivate(allocator, serviceID);
+
+       if (connectionPrivate) {
+               connectionPrivate->rlsFunction = callout;
+               if (context) {
+                       bcopy(context, &connectionPrivate->rlsContext, sizeof(SCNetworkConnectionContext));
+                       if (context->retain) {
+                               connectionPrivate->rlsContext.info = (void *)context->retain(context->info);
+                       }
+               }
+       }
+
+       return (SCNetworkConnectionRef)connectionPrivate;
+}
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+
+CFStringRef
+SCNetworkConnectionCopyServiceID (SCNetworkConnectionRef connection)
+{
+       if (!isA_SCNetworkConnection(connection)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
+       return CFRetain(((SCNetworkConnectionPrivateRef)connection)->serviceID);
+}
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+
+CFDictionaryRef
+SCNetworkConnectionCopyStatistics (SCNetworkConnectionRef connection)
+{
+       SCNetworkConnectionPrivateRef   connectionPrivate       = (SCNetworkConnectionPrivateRef)connection;
+       int                             error                   = kSCStatusFailed;
+       struct ppp_status               *stats                  = 0;
+       CFMutableDictionaryRef          dict                    = 0;
+       CFMutableDictionaryRef          statsdict               = 0;
+
+#define ADDNUMBER(d, k, n)                                     \
+{                                                              \
+       CFNumberRef num;                                        \
+       num = CFNumberCreate(NULL, kCFNumberSInt32Type, n);     \
+       if (num) {                                              \
+               CFDictionaryAddValue(d, k, num);                \
+               CFRelease(num);                                 \
+       }                                                       \
+}
+
+       if (!isA_SCNetworkConnection(connection)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
+       /* get status and check connected state */
+       if (PPPStatus(connectionPrivate->controlRef, connectionPrivate->serviceID, 0, &stats))
+               goto fail;
+
+       if (__SCNetworkConnectionConvertStatus(stats->status) != kSCNetworkConnectionConnected)
+               goto fail;
+
+       /* create dictionaries */
+       if ((statsdict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == 0)
+               goto fail;
+
+       if ((dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == 0)
+               goto fail;
+
+       /* add statistics */
+       ADDNUMBER(dict, kSCNetworkConnectionBytesIn, &stats->s.run.inBytes);
+       ADDNUMBER(dict, kSCNetworkConnectionBytesOut, &stats->s.run.outBytes);
+       ADDNUMBER(dict, kSCNetworkConnectionPacketsIn, &stats->s.run.inPackets);
+       ADDNUMBER(dict, kSCNetworkConnectionPacketsOut, &stats->s.run.outPackets);
+       ADDNUMBER(dict, kSCNetworkConnectionErrorsIn, &stats->s.run.inErrors);
+       ADDNUMBER(dict, kSCNetworkConnectionErrorsOut, &stats->s.run.outErrors);
+
+       /* add the PPP dictionary to the statistics dictionary */
+       CFDictionaryAddValue(statsdict, kSCEntNetPPP, dict);
+       CFRelease(dict);
+
+       /* done */
+       CFAllocatorDeallocate(NULL, stats);
+       return statsdict;
+
+    fail:
+
+       if (stats)
+               CFAllocatorDeallocate(NULL, stats);
+       if (dict)
+               CFRelease(dict);
+       if (statsdict)
+               CFRelease(statsdict);
+       _SCErrorSet(error);
+       return NULL;
+}
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+
+SCNetworkConnectionStatus
+SCNetworkConnectionGetStatus (SCNetworkConnectionRef connection)
+{
+       SCNetworkConnectionPrivateRef   connectionPrivate       = (SCNetworkConnectionPrivateRef)connection;
+       struct ppp_status               *stats                  = 0;
+       SCNetworkConnectionStatus       status;
+
+       if (!isA_SCNetworkConnection(connection)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
+       if (PPPStatus(connectionPrivate->controlRef, connectionPrivate->serviceID, 0, &stats))
+               return kSCNetworkConnectionDisconnected; // XXX
+
+       status = __SCNetworkConnectionConvertStatus(stats->status);
+
+       CFAllocatorDeallocate(NULL, stats);
+       return status;
+}
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+CFDictionaryRef
+SCNetworkConnectionCopyExtendedStatus (SCNetworkConnectionRef connection)
+{
+       SCNetworkConnectionPrivateRef   connectionPrivate       = (SCNetworkConnectionPrivateRef)connection;
+       CFPropertyListRef               status                  = 0;
+       void                            *data                   = 0;
+       u_int32_t                       datalen;
+
+       if (!isA_SCNetworkConnection(connection)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
+       if (PPPExtendedStatus(connectionPrivate->controlRef, connectionPrivate->serviceID, 0, &data, &datalen))
+               goto fail;
+
+       if (!data
+           || !(status = PPPUnserialize(data, datalen))
+           || !isA_CFDictionary(status))
+               goto fail;
+
+       CFAllocatorDeallocate(NULL, data);
+       return status;
+
+    fail:
+
+       _SCErrorSet(kSCStatusFailed);
+       if (status)
+               CFRelease(status);
+       if (data)
+               CFAllocatorDeallocate(NULL, data);
+       return NULL;
+}
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+
+Boolean
+SCNetworkConnectionStart (SCNetworkConnectionRef       connection,
+                         CFDictionaryRef               userOptions,
+                         Boolean                       linger)
+{
+       SCNetworkConnectionPrivateRef   connectionPrivate       = (SCNetworkConnectionPrivateRef)connection;
+       CFDataRef                       dataref                 = 0;
+       void                            *data                   = 0;
+       u_int32_t                       datalen                 = 0;
+
+       if (!isA_SCNetworkConnection(connection)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
+       if (userOptions && !(dataref = PPPSerialize(userOptions, &data, &datalen)))
+               goto fail;
+
+       if (PPPConnect(connectionPrivate->controlRef, connectionPrivate->serviceID, 0, data, datalen, linger))
+               goto fail;
+
+       if (dataref)
+               CFRelease(dataref);
+
+       /* connection is now started */
+       return TRUE;
+
+    fail:
+
+       if (dataref)
+               CFRelease(dataref);
+       _SCErrorSet(kSCStatusFailed);   // XXX
+       return FALSE;
+}
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+
+Boolean
+SCNetworkConnectionStop (SCNetworkConnectionRef        connection,
+                        Boolean                forceDisconnect)
+{
+       SCNetworkConnectionPrivateRef   connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
+
+       if (!isA_SCNetworkConnection(connection)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if (PPPDisconnect(connectionPrivate->controlRef, connectionPrivate->serviceID, 0, forceDisconnect)) {
+               _SCErrorSet(kSCStatusFailed);
+               return FALSE;
+       }
+
+       /* connection is now disconnecting */
+       return TRUE;
+}
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+
+Boolean
+SCNetworkConnectionSuspend (SCNetworkConnectionRef connection)
+{
+       SCNetworkConnectionPrivateRef   connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
+
+       if (!isA_SCNetworkConnection(connection)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
+       if (PPPSuspend(connectionPrivate->controlRef, connectionPrivate->serviceID, 0)) {
+               _SCErrorSet(kSCStatusFailed);
+               return FALSE;
+       }
+
+       /* connection is now suspended */
+       return TRUE;
+}
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+
+Boolean
+SCNetworkConnectionResume (SCNetworkConnectionRef connection)
+{
+       SCNetworkConnectionPrivateRef   connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
+
+       if (!isA_SCNetworkConnection(connection)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
+       if (PPPResume(connectionPrivate->controlRef, connectionPrivate->serviceID, 0)) {
+               _SCErrorSet(kSCStatusFailed);
+               return FALSE;
+       }
+
+       /* connection is now resume */
+       return TRUE;
+}
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+
+CFDictionaryRef
+SCNetworkConnectionCopyUserOptions (SCNetworkConnectionRef connection)
+{
+       SCNetworkConnectionPrivateRef   connectionPrivate       = (SCNetworkConnectionPrivateRef)connection;
+       void                            *data                   = 0;
+       u_int32_t                       datalen;
+       CFPropertyListRef               userOptions             = 0;
+
+       if (!isA_SCNetworkConnection(connection)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
+       if (PPPGetConnectData(connectionPrivate->controlRef, connectionPrivate->serviceID, 0, &data, &datalen))
+               goto fail;
+
+       // no data were used, return an empty dictionary
+       if (data == 0) {
+               CFDictionaryRef dict;
+
+               dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+               if (dict == 0)
+                       _SCErrorSet(kSCStatusFailed); // XXX
+               return dict;
+       }
+
+       userOptions = PPPUnserialize(data, datalen);
+       if (!isA_CFDictionary(userOptions))
+               goto fail;
+
+       CFAllocatorDeallocate(NULL, data);
+       return userOptions;
+
+    fail:
+
+       _SCErrorSet(kSCStatusFailed);
+       if (userOptions)
+               CFRelease(userOptions);
+       if (data)
+               CFAllocatorDeallocate(NULL, data);
+       return NULL;
+}
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+
+static Boolean
+__isScheduled(CFTypeRef obj, CFRunLoopRef runLoop, CFStringRef runLoopMode, CFMutableArrayRef rlList)
+{
+       CFIndex i;
+       CFIndex n       = CFArrayGetCount(rlList);
+
+       for (i = 0; i < n; i += 3) {
+               if (obj         && !CFEqual(obj,         CFArrayGetValueAtIndex(rlList, i))) {
+                       continue;
+               }
+               if (runLoop     && !CFEqual(runLoop,     CFArrayGetValueAtIndex(rlList, i+1))) {
+                       continue;
+               }
+               if (runLoopMode && !CFEqual(runLoopMode, CFArrayGetValueAtIndex(rlList, i+2))) {
+                       continue;
+               }
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+static void
+__schedule(CFTypeRef obj, CFRunLoopRef runLoop, CFStringRef runLoopMode, CFMutableArrayRef rlList)
+{
+       CFArrayAppendValue(rlList, obj);
+       CFArrayAppendValue(rlList, runLoop);
+       CFArrayAppendValue(rlList, runLoopMode);
+
+       return;
+}
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+static Boolean
+__unschedule(CFTypeRef obj, CFRunLoopRef runLoop, CFStringRef runLoopMode, CFMutableArrayRef rlList, Boolean all)
+{
+       CFIndex i       = 0;
+       Boolean found   = FALSE;
+       CFIndex n       = CFArrayGetCount(rlList);
+
+       while (i < n) {
+               if (obj         && !CFEqual(obj,         CFArrayGetValueAtIndex(rlList, i))) {
+                       i += 3;
+                       continue;
+               }
+               if (runLoop     && !CFEqual(runLoop,     CFArrayGetValueAtIndex(rlList, i+1))) {
+                       i += 3;
+                       continue;
+               }
+               if (runLoopMode && !CFEqual(runLoopMode, CFArrayGetValueAtIndex(rlList, i+2))) {
+                       i += 3;
+                       continue;
+               }
+
+               found = TRUE;
+
+               CFArrayRemoveValueAtIndex(rlList, i + 2);
+               CFArrayRemoveValueAtIndex(rlList, i + 1);
+               CFArrayRemoveValueAtIndex(rlList, i);
+
+               if (!all) {
+                       return found;
+               }
+
+               n -= 3;
+       }
+
+       return found;
+}
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+
+static void
+__SCNetworkConnectionCallBack(CFSocketRef              inref,
+                             CFSocketCallBackType      type,
+                             CFDataRef                 address,
+                             const void                *data,
+                             void                      *info)
+{
+       void                            *context_info;
+       void                            (*context_release)(const void *);
+       SCNetworkConnectionRef          connection              = (SCNetworkConnectionRef)info;
+       SCNetworkConnectionPrivateRef   connectionPrivate       = (SCNetworkConnectionPrivateRef)connection;
+       SCNetworkConnectionCallBack     rlsFunction;
+       SCNetworkConnectionStatus       status;
+       int                             pppstatus;
+       int                             err;
+
+       err = PPPReadEvent(connectionPrivate->eventRef, &pppstatus);
+       if (err)
+               return;
+
+       rlsFunction = connectionPrivate->rlsFunction;
+       if (connectionPrivate->rlsContext.retain && connectionPrivate->rlsContext.info) {
+               context_info    = (void *)connectionPrivate->rlsContext.retain(connectionPrivate->rlsContext.info);
+               context_release = connectionPrivate->rlsContext.release;
+       }
+       else {
+               context_info    = connectionPrivate->rlsContext.info;
+               context_release = NULL;
+       }
+
+       status = __SCNetworkConnectionConvertStatus(pppstatus);
+
+       (*rlsFunction)(connection, status, context_info);
+       if (context_release && context_info) {
+               context_release(context_info);
+       }
+}
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+
+Boolean
+SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef  connection,
+                                      CFRunLoopRef             runLoop,
+                                      CFStringRef              runLoopMode)
+{
+       SCNetworkConnectionPrivateRef   connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
+       //CFSocketRef                   ref;
+
+       if (!isA_SCNetworkConnection(connection) || runLoop == NULL || runLoopMode == NULL) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if (connectionPrivate->rlsFunction == 0) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if (connectionPrivate->rlList
+           && __isScheduled(NULL, runLoop, runLoopMode, connectionPrivate->rlList)) {
+               /* already scheduled */
+               _SCErrorSet(kSCStatusFailed);
+               return FALSE;
+       }
+
+       if (connectionPrivate->eventRef == -1) {
+               CFSocketContext context = { 0, (void*)connection, CFRetain, CFRelease, CFCopyDescription };
+
+               if (PPPInit(&connectionPrivate->eventRef)) {
+                       _SCErrorSet(kSCStatusFailed);
+                       return FALSE;
+               }
+
+               PPPEnableEvents(connectionPrivate->eventRef, connectionPrivate->serviceID, 0, 1);
+
+               connectionPrivate->eventRefCF = CFSocketCreateWithNative(NULL, connectionPrivate->eventRef,
+                                                                        kCFSocketReadCallBack, __SCNetworkConnectionCallBack, &context);
+               connectionPrivate->rls = CFSocketCreateRunLoopSource(NULL, connectionPrivate->eventRefCF, 0);
+               connectionPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+
+               //CFRelease(ref);
+       }
+
+       CFRunLoopAddSource(runLoop, connectionPrivate->rls, runLoopMode);
+       __schedule(connectionPrivate, runLoop, runLoopMode, connectionPrivate->rlList);
+
+       return TRUE;
+}
+
+/* -------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------- */
+
+Boolean
+SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef                connection,
+                                        CFRunLoopRef                   runLoop,
+                                        CFStringRef                    runLoopMode)
+{
+       SCNetworkConnectionPrivateRef   connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
+
+       if (!isA_SCNetworkConnection(connection) || runLoop == NULL || runLoopMode == NULL) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if (connectionPrivate->rlList == NULL
+           || !__unschedule(connectionPrivate, runLoop, runLoopMode, connectionPrivate->rlList, FALSE)) {
+               /* if not currently scheduled */
+               _SCErrorSet(kSCStatusFailed);
+               return FALSE;
+       }
+
+       CFRunLoopRemoveSource(runLoop, connectionPrivate->rls, runLoopMode);
+
+       if (CFArrayGetCount(connectionPrivate->rlList) == 0) {
+               CFRelease(connectionPrivate->rls);
+               connectionPrivate->rls = NULL;
+               CFRelease(connectionPrivate->rlList);
+               connectionPrivate->rlList = NULL;
+               //PPPDispose(connectionPrivate->eventRef);
+               CFSocketInvalidate(connectionPrivate->eventRefCF);
+               CFRelease(connectionPrivate->eventRefCF);
+               connectionPrivate->eventRefCF = 0;
+               connectionPrivate->eventRef = -1;
+       }
+
+       return TRUE;
+}
+
+
+//************************* USER LEVEL DIAL API **********************************
+
+
+#define k_NetworkConnect_Pref_File     CFSTR("com.apple.networkConnect")
+#define k_InterentConnect_Pref_File    CFSTR("com.apple.internetconnect")
+
+#define k_Dial_Default_Key             CFSTR("ConnectByDefault") // needs to go into SC
+#define k_Last_Service_Id_Key          CFSTR("ServiceID")
+#define k_Unique_Id_Key                        CFSTR("UniqueIdentifier")
+
+
+/* Private Prototypes */
+static Boolean SCNetworkConnectionPrivateCopyDefaultServiceIDForDial   (SCDynamicStoreRef session, CFStringRef *serviceID);
+static Boolean SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore (SCDynamicStoreRef session, CFStringRef *serviceID);
+static Boolean SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray, CFDictionaryRef *userOptions);
+static Boolean SCNetworkConnectionPrivateIsPPPService                  (SCDynamicStoreRef session, CFStringRef serviceID);
+static void addPasswordFromKeychain(CFDictionaryRef *userOptions);
+static CFArrayRef copyKeychainEnumerator(CFStringRef uniqueIdentifier);
+
+Boolean
+SCNetworkConnectionCopyUserPreferences (CFDictionaryRef        selectionOptions,
+                                       CFStringRef     *serviceID,
+                                       CFDictionaryRef *userOptions)
+{
+       SCDynamicStoreRef       session = SCDynamicStoreCreate(NULL, CFSTR("SCNetworkConnection"), NULL, NULL);
+       Boolean                 success = FALSE;
+
+       // NOTE:  we are currently ignoring selectionOptions
+
+       if (session != NULL) {
+               // (1) Figure out which service ID we care about, allocate it into passed "serviceID"
+               success = SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(session, serviceID);
+
+               if (success && (*serviceID != NULL)) {
+                       // (2) Get the list of user data for this service ID
+                       CFPropertyListRef userServices = CFPreferencesCopyValue(*serviceID,
+                                                                               k_NetworkConnect_Pref_File,
+                                                                               kCFPreferencesCurrentUser,
+                                                                               kCFPreferencesCurrentHost);
+
+                       // (3) We are expecting an array if the user has defined records for this service ID or NULL if the user hasn't
+                       if (userServices != NULL) {
+                               if (isA_CFArray(userServices)) {
+                                       // (4) Get the default set of user options for this service
+                                       success =  SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(
+                                                                         (CFArrayRef)userServices,
+                                                                       userOptions);
+                                       if(success && userOptions != NULL)
+                                       {
+                                           addPasswordFromKeychain(userOptions);
+                                       }
+                               } else {
+                                       fprintf(stderr, "Error, userServices are not of type CFArray!\n");
+                               }
+
+                               CFRelease(userServices); // this is OK because SCNetworkConnectionPrivateISExpectedCFType() checks for NULL
+                       }
+               }
+
+               CFRelease(session);
+       } else {
+               fprintf(stderr, "Error,  SCNetworkConnectionCopyUserPreferences, SCDynamicStoreCreate() returned NULL!\n");
+       }
+
+       return success;
+}
+
+//*******************************************************************************************
+// SCNetworkConnectionPrivateCopyDefaultServiceIDForDial
+// ----------------------------------------------------
+// Try to find the service id to connect
+// (1) Start by looking at the last service used in Internet Connect
+// (2) If Internet Connect has not been used, find the PPP service with the highest ordering
+//********************************************************************************************
+static Boolean
+SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(SCDynamicStoreRef session, CFStringRef *serviceID)
+{
+       Boolean                 foundService            = FALSE;
+       CFPropertyListRef       lastServiceSelectedInIC = NULL;
+
+       // NULL out the pointer
+       *serviceID = NULL;
+
+       // read out the last service from the Internet Connect preference file
+       lastServiceSelectedInIC = CFPreferencesCopyValue(k_Last_Service_Id_Key,
+                                                        k_InterentConnect_Pref_File,
+                                                        kCFPreferencesCurrentUser,
+                                                        kCFPreferencesAnyHost);
+
+       // we found the service the user last had open in IC
+       if (lastServiceSelectedInIC != NULL) {
+               // make sure its a PPP service
+               if (SCNetworkConnectionPrivateIsPPPService(session, lastServiceSelectedInIC)) {
+                       // make sure the service that we found is valid
+                       CFDictionaryRef dict;
+                       CFStringRef     key;
+
+                       key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
+                                                                         kSCDynamicStoreDomainSetup,
+                                                                         lastServiceSelectedInIC,
+                                                                         kSCEntNetInterface);
+                       dict = SCDynamicStoreCopyValue(session, key);
+                       CFRelease(key);
+                       if (dict) {
+                               *serviceID = CFRetain(lastServiceSelectedInIC);
+                               foundService = TRUE;
+                               CFRelease(dict);
+                       }
+               }
+               CFRelease(lastServiceSelectedInIC);
+       }
+
+       if (!foundService) {
+               foundService = SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(session, serviceID);
+       }
+
+       return foundService;
+}
+
+//********************************************************************************
+// SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore
+// -------------------------------------------------------
+// Find the highest ordered PPP service in the dynamic store
+//********************************************************************************
+static Boolean
+SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(SCDynamicStoreRef session, CFStringRef *serviceID)
+{
+       Boolean         success         = FALSE;
+       CFStringRef     key             = NULL;
+       CFDictionaryRef dict            = NULL;
+       CFArrayRef      serviceIDs      = NULL;
+
+       *serviceID = NULL;
+
+       do {
+               CFIndex count;
+               CFIndex i;
+
+               key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainSetup, kSCEntNetIPv4);
+               if (key == NULL) {
+                       fprintf(stderr, "Error, Setup Key == NULL!\n");
+                       break;
+               }
+
+               dict = SCDynamicStoreCopyValue(session, key);
+               if (dict == NULL) {
+                       fprintf(stderr, "Error, Dictionary for setup key == NULL!\n");
+                       break;
+               }
+
+               serviceIDs = CFDictionaryGetValue(dict, kSCPropNetServiceOrder); // array of service id's
+               if (isA_CFArray(serviceIDs) == NULL) {
+                       if (serviceIDs == NULL)
+                               fprintf(stderr, "Error, Array of service IDs == NULL!\n");
+                       else
+                               fprintf(stderr, "Error, serviceIds are not of type CFArray!\n");
+                       break;
+               }
+
+               count = CFArrayGetCount(serviceIDs);
+               for (i = 0; i < count; i++) {
+                       CFStringRef service = CFArrayGetValueAtIndex(serviceIDs, i);
+
+                       if (SCNetworkConnectionPrivateIsPPPService(session, service)) {
+                               *serviceID = CFRetain(service);
+                               success = TRUE;
+                               break;
+                       }
+               }
+       } while (FALSE);
+
+       if (key != NULL)
+               CFRelease(key);
+
+       if (dict != NULL)
+               CFRelease(dict);
+
+       return success;
+}
+
+//********************************************************************************
+// SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray
+// ---------------------------------------------------------
+// Copy over user preferences for a particular service if they exist
+//********************************************************************************
+static Boolean
+SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray, CFDictionaryRef *userOptions)
+{
+       CFIndex count   = CFArrayGetCount(userOptionsArray);
+       int     i;
+
+       *userOptions = NULL;
+
+       for (i = 0; i < count; i++) {
+               // (1) Find the dictionary
+               CFPropertyListRef propertyList = CFArrayGetValueAtIndex(userOptionsArray, i);
+
+               if (isA_CFDictionary(propertyList) != NULL) {
+                       // See if there's a value for dial on demand
+                       CFPropertyListRef value = CFDictionaryGetValue((CFDictionaryRef)propertyList,
+                                                                      k_Dial_Default_Key);
+                       if (isA_CFBoolean(value) != NULL) {
+                               if (CFBooleanGetValue(value)) {
+                                       // we found the default user options
+                                       *userOptions = CFDictionaryCreateCopy(NULL,
+                                                                             (CFDictionaryRef)propertyList);
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       return TRUE;
+}
+
+//********************************************************************************
+// SCNetworkConnectionPrivateIsPPPService
+// --------------------------------------
+// Check and see if the service is a PPP service
+//********************************************************************************
+static Boolean
+SCNetworkConnectionPrivateIsPPPService(SCDynamicStoreRef session, CFStringRef serviceID)
+{
+       CFStringRef     entityKey;
+       Boolean         isPPPService    = FALSE;
+       Boolean         isModemOrPPPoE  = FALSE;
+
+       entityKey = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
+                                                               kSCDynamicStoreDomainSetup,
+                                                               serviceID,
+                                                               kSCEntNetInterface);
+       if (entityKey != NULL) {
+               CFDictionaryRef serviceDict;
+
+               serviceDict = SCDynamicStoreCopyValue(session, entityKey);
+               if (serviceDict != NULL) {
+                       if (isA_CFDictionary(serviceDict)) {
+                               CFStringRef     type;
+                               CFStringRef     subtype;
+
+                               type = CFDictionaryGetValue(serviceDict, kSCPropNetInterfaceType);
+                               if (isA_CFString(type)) {
+                                       isPPPService = CFEqual(type, kSCValNetInterfaceTypePPP);
+                               }
+
+                               subtype = CFDictionaryGetValue(serviceDict, kSCPropNetInterfaceSubType);
+                               if (isA_CFString(subtype)) {
+                                       isModemOrPPPoE = (CFEqual(subtype, kSCValNetInterfaceSubTypePPPSerial) ||
+                                                         CFEqual(subtype, kSCValNetInterfaceSubTypePPPoE));
+                               }
+                       }
+                       CFRelease(serviceDict);
+               }
+               CFRelease(entityKey);
+       }
+
+       return (isPPPService && isModemOrPPPoE);
+}
+
+//********************************************************************************
+// addPasswordFromKeychain
+// --------------------------------------
+// Get the password out of the keychain and add it to the PPP dictionary
+//********************************************************************************
+static void
+addPasswordFromKeychain(CFDictionaryRef *userOptions)
+{
+       CFArrayRef              enumerator;
+       CFIndex                 n;
+       CFDictionaryRef         oldDict;
+       CFPropertyListRef       uniqueID        = NULL;
+
+       oldDict = *userOptions;
+       if(oldDict == NULL) {
+               return;         // if no userOptions
+       }
+
+       uniqueID = CFDictionaryGetValue(oldDict, k_Unique_Id_Key);
+       if(!isA_CFString(uniqueID)) {
+               return;         // if no unique ID
+       }
+
+       enumerator = copyKeychainEnumerator(uniqueID);
+       if(enumerator == NULL) {
+               return;         // if no keychain enumerator
+       }
+
+
+       n = CFArrayGetCount(enumerator);
+       if (n > 0) {
+               void                    *data   = NULL;
+               UInt32                  dataLen = 0;
+               SecKeychainItemRef      itemRef;
+               OSStatus                result;
+
+               itemRef = (SecKeychainItemRef)CFArrayGetValueAtIndex(enumerator, 0);
+               result = SecKeychainItemCopyContent(itemRef,            // itemRef
+                                                   NULL,               // itemClass
+                                                   NULL,               // attrList
+                                                   &dataLen,           // length
+                                                   (void *)&data);     // outData
+               if(result == noErr && data != NULL && dataLen > 0) {
+                       CFStringRef             pass;
+
+                       pass = CFStringCreateWithBytes(NULL, data, dataLen, kCFStringEncodingUTF8, TRUE);
+                       if (pass) {
+                               CFMutableDictionaryRef  newDict;
+                               CFMutableDictionaryRef  newPPP;
+                               CFDictionaryRef         pppDict;
+
+                               newDict = CFDictionaryCreateMutableCopy(NULL, 0, oldDict);
+                               pppDict = CFDictionaryGetValue(newDict, kSCEntNetPPP);
+                               if (isA_CFDictionary(pppDict)) {
+                                       newPPP = CFDictionaryCreateMutableCopy(NULL, 0, pppDict);
+                               } else {
+                                       newPPP = CFDictionaryCreateMutable(NULL,
+                                                                          0,
+                                                                          &kCFTypeDictionaryKeyCallBacks,
+                                                                          &kCFTypeDictionaryValueCallBacks);
+                               }
+
+                               // set the PPP password
+                               CFDictionarySetValue(newPPP, kSCPropNetPPPAuthPassword, pass);
+                               CFRelease(pass);
+
+                               // update the PPP entity
+                               CFDictionarySetValue(newDict, kSCEntNetPPP, newPPP);
+                               CFRelease(newPPP);
+
+                               // update the userOptions dictionary
+                               CFRelease(oldDict);
+                               *userOptions = CFDictionaryCreateCopy(NULL, newDict);
+                               CFRelease(newDict);
+                       }
+               }
+       }
+
+       CFRelease(enumerator);
+       return;
+}
+
+//********************************************************************************
+// copyKeychainEnumerator
+// --------------------------------------
+// Gather Keychain Enumerator
+//********************************************************************************
+static CFArrayRef
+copyKeychainEnumerator(CFStringRef uniqueIdentifier)
+{
+       char                    *buf            = NULL;
+       CFMutableArrayRef       itemArray       = NULL;
+       OSStatus                result;
+       SecKeychainSearchRef    search          = NULL;
+
+       buf = _SC_cfstring_to_cstring(uniqueIdentifier, NULL, 0, kCFStringEncodingUTF8);
+       if (buf != NULL) {
+               // search for unique identifier in "svce" attribute
+               SecKeychainAttribute            attributes[]    = {{ kSecServiceItemAttr,
+                                                                    CFStringGetLength(uniqueIdentifier),
+                                                                    (void *)buf
+                                                                  }};
+
+               SecKeychainAttributeList        attrList        = { sizeof(attributes) / sizeof(*attributes),
+                                                                   attributes };
+
+               result = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attrList, &search);
+               if (result == noErr) {
+                       itemArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+
+                       while (result == noErr) {
+                               SecKeychainItemRef      itemFound       = NULL;
+
+                               result = SecKeychainSearchCopyNext(search, &itemFound);
+                               if (result != noErr) {
+                                       break;
+                               }
+
+                               if (itemFound) {
+                                       CFArrayAppendValue(itemArray, itemFound);
+                                       CFRelease(itemFound);
+                               }
+                       }
+               }
+       }
+
+       if (search)     CFRelease(search);
+       if (buf)        CFAllocatorDeallocate(NULL, buf);
+
+       return itemArray;
+}