/* * 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 * - initial revision */ /* ------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------- */ #include #include #include #include "dy_framework.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #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(" {\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; }