create vpn / pptp programmatically

Discussion in 'Mac Programming' started by jjin, Jul 4, 2011.

  1. jjin, Jul 4, 2011
    Last edited by a moderator: Jul 4, 2011

    macrumors newbie

    Joined:
    Jul 4, 2011
    #1
    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;
        }
    		
    }
     
  2. macrumors newbie

    Joined:
    Jul 28, 2014

Share This Page