PDA

View Full Version : create vpn / pptp programmatically




jjin
Jul 4, 2011, 07:41 PM
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/mac/#documentation/Networking/Conceptual/SystemConfigFrameworks/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.

+(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;
}

}



pinker
Jul 28, 2014, 07:06 AM
If it helps you any, the code to create an L2TP is very similar and can be found here: https://github.com/halo/macosvpn/blob/master/macosvpn/Classes/VPNController.m