Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

jjin

macrumors newbie
Original poster
Jul 4, 2011
2
0
I am trying to create vpn/pptp connection using SystemConfiguration framework. After reading Apple's SC programming guide and some codes I could find on Internet, I came up with the follow code.

The code doesn't work (of course). SCNetworkConnectionStart(...) return error code 1001 which is a non-specific error (completely useless).

Then I looked at Network panel in System Preference, the connection is there but shown as "Not Configured". all entries are empty. It seems like that the options I set in the program didn't make it into data store.

Can someone tell me where I did wrong?

Apple's SC programming guide (http://developer.apple.com/library/.../SC_UnderstandSchema/SC_UnderstandSchema.html)

mentioned a sample code MORESCF. But I could not find it in Sample code library. Does anyone have a copy of it and send it to me?

Thanks.

Code:
 +(int) createPptpConnection: (CFStringRef) username
				withPassword: (CFStringRef) password
				remoteServer: (CFStringRef) address
{
	// PPP options
	CFStringRef keysPPP[4]  = { NULL, NULL, NULL, NULL };
	CFStringRef valsPPP[4]  = { NULL, NULL, NULL, NULL };
	CFIndex numkeys         = 0;
	CFStringRef kVendorName = CFSTR("myvpn.com");
	CFStringRef kAppName = CFSTR("myvpn");
    OSStatus        authErr = noErr;
	CFDictionaryRef serviceOptions;
	SCNetworkConnectionRef connection;
	
	keysPPP[numkeys]    = kSCPropNetPPPAuthName;
	valsPPP[numkeys++]  = username;
	keysPPP[numkeys]    = kSCPropNetPPPAuthPassword;
	valsPPP[numkeys++]  = password;
	keysPPP[numkeys]    = kSCPropNetPPPCommRemoteAddress;
	valsPPP[numkeys++]  = address;
	keysPPP[numkeys]    = kSCPropUserDefinedName;
	valsPPP[numkeys++]  = kAppName;
    int err = 0;

	CFDictionaryRef pppOptionsForDial   = CFDictionaryCreate (NULL,
											  (const void **)&keysPPP,
											  (const void **)&valsPPP,
											  numkeys,  
											  &kCFTypeDictionaryKeyCallBacks,
											  &kCFTypeDictionaryValueCallBacks);
    // Release strings after including them in dictionary
    CFRelease(username);
    CFRelease(password);
    CFRelease(address);
	
	// Interface options
	CFStringRef keysInt[]   = { kSCPropNetInterfaceType, kSCPropNetInterfaceSubType };
	CFStringRef valsInt[]   = { kSCValNetInterfaceTypePPP, kSCValNetInterfaceSubTypePPTP };
	
	CFDictionaryRef interfaceOptionsForDial = CFDictionaryCreate (NULL,
												  (const void **)&keysInt,
												  (const void **)&valsInt,
												  2,  
												  &kCFTypeDictionaryKeyCallBacks,
												  &kCFTypeDictionaryValueCallBacks);
	
	// IPv4 options
	CFStringRef keysIPv4[]  = { kSCPropNetIPv4ConfigMethod, kSCPropNetOverridePrimary };
	CFStringRef valsIPv4[]  = { kSCValNetIPv4ConfigMethodPPP, CFSTR("1") };
	
	CFDictionaryRef ipv4OptionsForDial  = CFDictionaryCreate (NULL, 
															  (const void **)&keysIPv4,
															  (const void **)&valsIPv4,
															  2,  
															  &kCFTypeDictionaryKeyCallBacks,
															  &kCFTypeDictionaryValueCallBacks);
	
	// IPv6 options
	CFStringRef keysIPv6[]  = { kSCPropNetIPv6ConfigMethod };
	CFStringRef valsIPv6[]  = { kSCValNetIPv6ConfigMethodAutomatic };
	
	CFDictionaryRef ipv6OptionsForDial  = CFDictionaryCreate (NULL,
															  (const void **)&keysIPv6,
															  (const void **)&valsIPv6,
															  1,  
															  &kCFTypeDictionaryKeyCallBacks,
															  &kCFTypeDictionaryValueCallBacks);
	
	CFStringRef keys[] = { kSCEntNetInterface, kSCEntNetIPv4, kSCEntNetIPv6, kSCEntNetPPP };
	CFDictionaryRef vals[] = { interfaceOptionsForDial, ipv4OptionsForDial, ipv6OptionsForDial, pppOptionsForDial };    
	
	
	CFDictionaryRef optionsForDial = CFDictionaryCreate (NULL,
										(const void **) &keys,
										(const void **) &vals,
										4,
										&kCFTypeDictionaryKeyCallBacks, 
										&kCFTypeDictionaryValueCallBacks);
	
	CFRelease( interfaceOptionsForDial);
	CFRelease( ipv4OptionsForDial );
	CFRelease( ipv6OptionsForDial );
	CFRelease( pppOptionsForDial);
	
	interfaceOptionsForDial = NULL;
	ipv4OptionsForDial = NULL;
	ipv6OptionsForDial = NULL;
	pppOptionsForDial = NULL;

	// Authority
	AuthorizationFlags rootFlags = 
		kAuthorizationFlagDefaults              |
		kAuthorizationFlagExtendRights          |
		kAuthorizationFlagInteractionAllowed    |
		kAuthorizationFlagPreAuthorize;
	
	AuthorizationRef auth;
	
	authErr = AuthorizationCreate(NULL,
						kAuthorizationEmptyEnvironment,
						rootFlags,
						&auth);
	
	// Create preferences for interface.
	SCPreferencesRef prefs;
	
    if (authErr == noErr) {
        prefs = SCPreferencesCreateWithAuthorization(NULL, kVendorName, NULL, auth);
    } else {
        prefs = SCPreferencesCreate(NULL, kVendorName, NULL);
    }
	
    if (prefs == NULL) {
        NSLog(@"Could not create preferences\n");
        err = SCError();
    }
	
    CFIndex					i, arraySize;
	CFStringRef				serviceToDial;
	Boolean					serviceFound = FALSE;
	SCNetworkInterfaceRef	pptpItfc;
	SCNetworkInterfaceRef	iface;
	CallbackParams          params;
	SCNetworkServiceRef		service;
	CFArrayRef              servicesArray;
	Boolean					ok;
	
    if (err == 0) {
        servicesArray = SCNetworkServiceCopyAll(prefs);
        if (servicesArray == NULL) {
            NSLog(@"No network services");
            return -1;
        }
		arraySize = CFArrayGetCount(servicesArray);
        for (i = 0; i < arraySize; ++i) {
            service = (SCNetworkServiceRef) CFArrayGetValueAtIndex(servicesArray, i);
            CFStringRef serviceName = SCNetworkServiceGetName(service);
            if (CFStringCompare(serviceName, kAppName, 0) == kCFCompareEqualTo) {
                serviceFound = TRUE;
                serviceToDial = SCNetworkServiceGetServiceID(service);
                break;
            }
        }
    }
	
    if (!SCPreferencesLock(prefs, TRUE)){
		NSLog(@"Failed to call SCPreferencesLock");
        err = SCError();
	}
	
    if( (err == 0) && !serviceFound) {	
		pptpItfc = SCNetworkInterfaceCreateWithInterface(kSCNetworkInterfaceIPv4, 
														kSCNetworkInterfaceTypePPTP);
	
		iface = SCNetworkInterfaceCreateWithInterface(pptpItfc, kSCNetworkInterfaceTypePPP);
		if (prefs && iface) {
			service = SCNetworkServiceCreate(prefs, iface);
			SCNetworkServiceSetName(service, kAppName);
			iface = SCNetworkServiceGetInterface(service);
			if (! SCNetworkInterfaceSetConfiguration(iface, optionsForDial)){
				NSLog(@"Failed to set interface configuration");
				err = SCError();
			}
		
			SCNetworkServiceEstablishDefaultConfiguration(service);
			SCNetworkSetRef set = SCNetworkSetCopyCurrent(prefs);
			if (set && SCNetworkSetAddService(set, service)) {
				if (SCPreferencesCommitChanges(prefs)) {
					if (SCPreferencesApplyChanges(prefs)){
						SCPreferencesSynchronize(prefs);
						serviceToDial = SCNetworkServiceGetServiceID(service);
					}
				}
			}
		}
	} else {

		if (SCNetworkServiceGetEnabled(service)) {
			iface = SCNetworkServiceGetInterface(service);
		
			if (! (iface && SCNetworkInterfaceSetConfiguration(iface, optionsForDial)) ){
				NSLog(@"Failed to set interface configuration");
				err = SCError();
			}
		}
        NSLog(@"Service was found!");
        SCNetworkConnectionCopyUserPreferences(NULL, &serviceToDial, &serviceOptions);
    }
	SCPreferencesUnlock(prefs);
	if (servicesArray != NULL) {
		CFRelease(servicesArray);
	}
	
    // Create a SCNetworkConnectionRef for it.
    if (err == 0) {
        SCNetworkConnectionContext context;

        // Set up the parameters to our callback function.
        params.magic            = kCallbackParamsMagic;
        params.forcePrintStatus = true;
        params.lastMinorStatus  = kSCNetworkConnectionDisconnected;
        params.lastMinorStatus  = kSCNetworkConnectionPPPDisconnected;
		
        // Set up the context to reference those parameters.
        context.version         = 0;
        context.info            = (void *) &params;
        context.retain          = NULL;
        context.release         = NULL;
        context.copyDescription = NULL;
		
        connection = SCNetworkConnectionCreateWithServiceID(NULL,
                                                            serviceToDial,
                                                            MyNetworkConnectionCallBack,
                                                            &context);
		
        if (connection == NULL) {
            err = SCError();
        }
    }
    // Schedule our callback with the runloop.
    if (err == 0) {
        ok = SCNetworkConnectionScheduleWithRunLoop(connection,
															CFRunLoopGetCurrent(),
															kCFRunLoopDefaultMode);
        if (!ok) {
            err = SCError();
        }
    }
	
    // Check the status.  If we're already connected tell the user.
    // If we're not connected, initiate the connection.
    if (err == 0) {
        // Most cases involve us bailing out, set the error here
        err = ECANCELED;
		
        switch (SCNetworkConnectionGetStatus(connection)) {
            case kSCNetworkConnectionDisconnected:
                err = 0;
                break;
            case kSCNetworkConnectionConnecting:
                NSLog(@"Service is already connecting.\n");
                break;
            case kSCNetworkConnectionDisconnecting:
                NSLog(@"Service is disconnecting.\n");
                break;
            case kSCNetworkConnectionConnected:
                NSLog(@"Service is already connected.\n");
                break;
            case kSCNetworkConnectionInvalid:
                NSLog(@"Service is invalid. Weird.\n");
                break;
            default:
                NSLog(@"Unexpected status.\n");
                break;
        }
    }
	
    // Initiate the connection.
    if (err == 0) {
        NSLog(@"Connecting...\n");
        ok = SCNetworkConnectionStart(connection, optionsForDial, false);
        if (!ok) {
            err = SCError();
			NSLog(@"SCNetworkConnectionStart failed: %d", err);
        }
    }
	
    // Run the runloop and wait for our connection attempt to be resolved.
    // Once that happens, print the result.
    CFDictionaryRef failedstatus;
    if (err == 0) {
        CFRunLoopRun();
		
        switch (params.lastMinorStatus) {
            case kSCNetworkConnectionPPPConnected:
                NSLog(@"Connection succeeded\n");
                break;
            case kSCNetworkConnectionPPPDisconnected:
                NSLog(@"Connection failed\n");
                failedstatus = SCNetworkConnectionCopyExtendedStatus(connection);
                CFShow(failedstatus);
                err = ECANCELED;
                break;
            default:
                NSLog(@"Bad params.lastMinorStatus (%ld)\n",
                        (long) params.lastMinorStatus);
                err = EINVAL;
        }
    }
	
    // Run this loop indefinitely until a SIGINT signal is received
    if (err == 0) {
        CFRunLoopRun();
    }
	
    // Clean up.
    if (serviceToDial != NULL) {
        CFRelease(serviceToDial);
        NSLog(@"Cleaning up serviceToDial\n");
    }
	
    if (serviceOptions != NULL) {
        NSLog(@"Cleaning up serviceOptions\n");
    }
    if (optionsForDial != NULL) {
        CFRelease(optionsForDial);
        NSLog(@"Cleaning up optionsForDial\n");
    }
	
    if (pppOptionsForDial != NULL) {
        CFRelease(pppOptionsForDial);
        NSLog(@"Cleaning up pppOptionsForDial\n");
    }
	
    if (connection != NULL) {
        (void) SCNetworkConnectionUnscheduleFromRunLoop(connection,
                                                        CFRunLoopGetCurrent(),
                                                        kCFRunLoopDefaultMode);
        CFRelease(connection);
        NSLog(@"Cleaning up connection\n");
    }
	
    if (err == 0) {
        return EXIT_SUCCESS;
    } else {
        if (err != ECANCELED) {
            NSLog(@"Failed with error %d\n.", err);
        }
        return EXIT_FAILURE;
    }
		
}
 
Last edited by a moderator:
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.