X-Git-Url: https://git.saurik.com/apple/configd.git/blobdiff_plain/32d96e3d77d900203b7faba2d7937f8b3472f4d7..009ee3c6fe2929a4c90ae5c9eb1925573e17956b:/SystemConfiguration.fproj/SCNetworkConnection.c?ds=inline diff --git a/SystemConfiguration.fproj/SCNetworkConnection.c b/SystemConfiguration.fproj/SCNetworkConnection.c new file mode 100644 index 0000000..b50cda0 --- /dev/null +++ b/SystemConfiguration.fproj/SCNetworkConnection.c @@ -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 + * - 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; +}