我正在尝试创建一个以编程方式在Mac上设置 VPN 的应用。不幸的是,网络领域的Apple doc在设置和连接VPN方面并不是非常具体。
所以我在网上找到了一些设置L2TP和IPsec的示例代码,但我找不到 PPTP 的任何内容。下面是我正在使用的一般代码,但PPTP对我不起作用。当我运行它时,肯定会崩溃。
以下是我无法解决的一些问题。
- 我在设置PPTP VPN时缺少什么?
- 如何连接到设置VPN?
- 如何找到已添加的VPN并将其删除?
- (AuthorizationRef) getAuth {
AuthorizationRef auth = NULL;
OSStatus status;
status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, [self flags], &auth);
if (status == errAuthorizationSuccess) {
//NSLog(@"Successfully obtained Authorization reference");
} else {
NSLog(@"Could not obtain Authorization reference");
exit(101);
}
return auth;
}
- (AuthorizationFlags) flags {
return kAuthorizationFlagDefaults |
kAuthorizationFlagExtendRights |
kAuthorizationFlagInteractionAllowed |
kAuthorizationFlagPreAuthorize;
}
- (void)setupVPN:(VPNConfig *)vpnConfig {
// Obtaining permission to modify network settings
AuthorizationRef auth = [self getAuth];
SCPreferencesRef prefs = SCPreferencesCreateWithAuthorization(NULL, CFSTR("macVPN"), NULL, auth);
// Making sure other process cannot make configuration modifications
// by obtaining a system-wide lock over the system preferences.
if (SCPreferencesLock(prefs, TRUE)) {
NSLog(@"Gained superhuman rights.");
} else {
NSLog(@"Sorry, without superuser privileges I won't be able to add any VPN interfaces.");
return;
}
// If everything will work out fin
[self createService:vpnConfig usingPreferencesRef:prefs];
// We're done, other processes may modify the system configuration again
SCPreferencesUnlock(prefs);
return;
}
#pragma mark - Create VPN Service
// This method creates one VPN interface according to the desired configuration
- (void) createService:(VPNConfig*)config usingPreferencesRef:(SCPreferencesRef)prefs {
NSLog(@"Creating new %@ Service using %@", config.humanType, config);
// These variables will hold references to our new interfaces
SCNetworkInterfaceRef bottomInterface = SCNetworkInterfaceCreateWithInterface(kSCNetworkInterfaceIPv4,kSCNetworkInterfaceTypePPTP);
SCNetworkInterfaceRef topInterface = SCNetworkInterfaceCreateWithInterface(bottomInterface, kSCNetworkInterfaceTypePPP);
// Creating a new, fresh VPN service in memory using the interface we already created
SCNetworkServiceRef service = SCNetworkServiceCreate(prefs, topInterface);
// That service is to have a name
SCNetworkServiceSetName(service, (__bridge CFStringRef)config.name);
// And we also woould like to know the internal ID of this service
NSString *serviceID = (__bridge NSString *)(SCNetworkServiceGetServiceID(service));
// It will be used to find the correct passwords in the system keychain
config.serviceID = serviceID;
// Interestingly enough, the interface variables in itself are now worthless
// We used them to create the service and that's it, we cannot modify them any more.
CFRelease(topInterface);
CFRelease(bottomInterface);
topInterface = NULL;
bottomInterface = NULL;
topInterface = SCNetworkServiceGetInterface(service);
// Let's apply all configuration to the PPTP interface
// Specifically, the servername, account username and password
if (SCNetworkInterfaceSetConfiguration(topInterface, config.PPTPConfig)) {
NSLog(@"Successfully configured PPP interface of service %@", config.name);
} else {
NSLog(@"Error: Could not configure PPP interface for service %@", config.name);
return;
}
if (SCNetworkInterfaceSetExtendedConfiguration(topInterface, NULL, config.PPTPConfig)) {
NSLog(@"Successfully configured IPSec on PPP interface for service %@", config.name);
} else {
NSLog(@"Error: Could not configure PPTP on PPP interface for service %@. %s (Code %i)", config.name, SCErrorString(SCError()), SCError());
return;
}
NSLog(@"Adding default protocols (DNS, etc.) to service %@...", config.name);
if (!SCNetworkServiceEstablishDefaultConfiguration(service)) {
NSLog(@"Error: Could not establish a default service configuration for %@. %s (Code %i)", config.name, SCErrorString(SCError()), SCError());
return;
}
NSLog(@"Fetching set of all available network services...");
SCNetworkSetRef networkSet = SCNetworkSetCopyCurrent(prefs);
if (!networkSet) {
NSLog(@"Error: Could not fetch current network set when creating %@. %s (Code %i)", config.name, SCErrorString(SCError()), SCError());
return;
}
if (!SCNetworkSetAddService (networkSet, service)) {
if (SCError() == 1005) {
NSLog(@"Skipping VPN Service %@ because it already exists.", config.humanType);
return;
} else {
NSLog(@"Error: Could not add new VPN service %@ to current network set. %s (Code %i)", config.name, SCErrorString(SCError()), SCError());
return;
}
}
NSLog(@"Fetching IPv4 protocol of service %@...", config.name);
SCNetworkProtocolRef protocol = SCNetworkServiceCopyProtocol(service, kSCNetworkProtocolTypeIPv4);
if (!protocol) {
NSLog(@"Error: Could not fetch IPv4 protocol of %@. %s (Code %i)", config.name, SCErrorString(SCError()), SCError());
return;
}
NSLog(@"Configuring IPv4 protocol of service %@...", config.name);
if (!SCNetworkProtocolSetConfiguration(protocol, config.L2TPIPv4Config)) {
NSLog(@"Error: Could not configure IPv4 protocol of %@. %s (Code %i)", config.name, SCErrorString(SCError()), SCError());
return;
}
NSLog(@"Commiting all changes including service %@...", config.name);
if (!SCPreferencesCommitChanges(prefs)) {
NSLog(@"Error: Could not commit preferences with service %@. %s (Code %i)", config.name, SCErrorString(SCError()), SCError());
return;
}
NSLog(@"Preparing to add Keychain items for service %@...", config.name);
// The password and the shared secret are not stored directly in the System Preferences .plist file
// Instead we put them into the KeyChain. I know we're creating new items each time you run this application
// But this actually is the same behaviour you get using the official System Preferences Network Pane
[VPNKeychain createPasswordKeyChainItem:config.name forService:serviceID withAccount:config.username andPassword:config.password];
[VPNKeychain createSharedSecretKeyChainItem:config.name forService:serviceID withPassword:config.sharedSecret];
if (!SCPreferencesApplyChanges(prefs)) {
NSLog(@"Error: Could not apply changes with service %@. %s (Code %i)", config.name, SCErrorString(SCError()), SCError());
return;
}
NSLog(@"Successfully created %@ VPN %@ with ID %@", config.humanType, config.name, serviceID);
return;
}
这是我的VPNConfig文件:
// VPNConfig.h
@interface VPNConfig : NSObject
@property (atomic) NSUInteger type;
@property (strong) NSString *serviceID;
@property (strong) NSString *name;
@property (strong) NSString *endpointPrefix;
@property (nonatomic) NSString *endpoint;
@property (strong) NSString *endpointSuffix;
@property (strong) NSString *username;
@property (strong) NSString *password;
@property (strong) NSString *sharedSecret;
@property (readonly) NSString *humanType;
@property (readonly) CFDictionaryRef PPTPConfig;
@property (readonly) CFDictionaryRef L2TPPPPConfig;
@property (readonly) CFDictionaryRef L2TPIPSecConfig;
@property (readonly) CFDictionaryRef L2TPIPv4Config;
@end
// VPNConfig.m
typedef NS_ENUM(NSInteger, VPNServiceType) {
VPNServicePPTP ,
VPNServiceL2TPOverIPSec,
VPNServiceCiscoIPSec
};
@implementation VPNConfig
@synthesize type, name, endpointPrefix, endpointSuffix, username, password, sharedSecret;
@synthesize endpoint = _endpoint;
- (void) setEndpoint:(NSString *)newEndpoint {
_endpoint = newEndpoint;
}
- (NSString*) endpoint {
if (_endpoint) return _endpoint;
if ((!endpointPrefix && !endpointSuffix) || ([endpointPrefix isEqualToString:@""] && [endpointSuffix isEqualToString:@""])) return NULL;
return [NSString stringWithFormat:@"%@%@", endpointPrefix, endpointSuffix];
}
- (BOOL) is:(NSUInteger)aType {
return self.type == aType;
}
- (NSString*) humanType {
switch(self.type) {
case VPNServicePPTP : return @"PPTP"; break;
case VPNServiceL2TPOverIPSec : return @"L2TP over IPSec"; break;
case VPNServiceCiscoIPSec : return @"Cisco IPSec"; break;
default : return @"Unknown"; break;
}
}
- (NSString*) description {
return [NSString stringWithFormat:@"<[%@] name=%@ endpointPrefix=%@ endpoint=%@ endpointSuffix=%@ username=%@ password=%@ sharedSecret=%@>", self.humanType, self.name, self.endpointPrefix, self.endpoint, self.endpointSuffix, self.username, self.password, self.sharedSecret];
}
- (CFDictionaryRef) PPTPConfig {
CFStringRef keys[4] = { NULL, NULL, NULL, NULL };
CFStringRef vals[4] = { NULL, NULL, NULL, NULL };
CFIndex count = 0;
keys[count] = kSCPropNetPPPAuthName;
vals[count++] = (__bridge CFStringRef)self.username;
keys[count] = kSCPropNetPPPAuthPassword;
vals[count++] = (__bridge CFStringRef)self.serviceID;
keys[count] = kSCPropNetPPPCommRemoteAddress;
vals[count++] = (__bridge CFStringRef)self.endpoint;
keys[count] = kSCPropUserDefinedName;
vals[count++] = (__bridge CFStringRef)C_VPN_NAME;
return CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&vals, count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
- (CFDictionaryRef) L2TPIPSecConfig {
CFStringRef keys[3] = { NULL, NULL, NULL };
CFStringRef vals[3] = { NULL, NULL, NULL };
CFIndex count = 0;
keys[count] = kSCPropNetIPSecAuthenticationMethod;
vals[count++] = kSCValNetIPSecAuthenticationMethodSharedSecret;
keys[count] = kSCPropNetIPSecSharedSecretEncryption;
vals[count++] = kSCValNetIPSecSharedSecretEncryptionKeychain;
keys[count] = kSCPropNetIPSecSharedSecret;
vals[count++] = (__bridge CFStringRef)[NSString stringWithFormat:@"%@.SS", self.serviceID];
return CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&vals, count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
- (CFDictionaryRef) L2TPIPv4Config {
CFStringRef keys[5] = { NULL, NULL, NULL, NULL, NULL };
CFStringRef vals[5] = { NULL, NULL, NULL, NULL, NULL };
CFIndex count = 0;
keys[count] = kSCPropNetIPv4ConfigMethod;
vals[count++] = kSCValNetIPv4ConfigMethodPPP;
int one = 1;
keys[count] = kSCPropNetOverridePrimary;
vals[count++] = CFNumberCreate(NULL, kCFNumberIntType, &one);
return CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&vals, count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
答案 0 :(得分:1)
您可以在下面链接查看vpn / pptp连接
http://lists.apple.com/archives/macnetworkprog/2011/Jul/msg00001.html
希望这可以解决您的问题。