以编程方式在Mac OS X上创建PPTP VPN

时间:2015-01-23 03:05:26

标签: objective-c macos networking vpn cfnetwork

我正在尝试创建一个以编程方式在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);
}

1 个答案:

答案 0 :(得分:1)

您可以在下面链接查看vpn / pptp连接

http://lists.apple.com/archives/macnetworkprog/2011/Jul/msg00001.html

希望这可以解决您的问题。