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

Posted

技术标签:

【中文标题】以编程方式在 Mac OS X 上创建 PPTP ***【英文标题】:Creating PPTP *** on Mac OS X Programatically 【发布时间】:2015-03-22 01:09:01 【问题描述】:

我正在尝试创建一个以编程方式在 Mac 上设置 *** 的应用。不幸的是,网络领域的 Apple 文档对设置和连接 *** 不是很具体。

所以我在网上找到了一些用于设置 L2TP 和 IPsec 的示例代码,但我找不到 PPTP 的任何内容。下面是我正在使用的通用代码,但 PPTP 不适用于我。当我运行它时,它肯定会崩溃。

所以下面是一些我无法弄清楚的问题。

-我在设置 PPTP *** 时缺少什么?

-如何连接到设置 ***?

-如何找到已经添加的***并删除它们?

- (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)setup***:(***Config *)***Config 
    // Obtaining permission to modify network settings
    AuthorizationRef auth = [self getAuth];
    SCPreferencesRef prefs = SCPreferencesCreateWithAuthorization(NULL, CFSTR("mac***"), 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 *** interfaces.");
        return;   
    
    // If everything will work out fin
    [self createService:***Config usingPreferencesRef:prefs];

    // We're done, other processes may modify the system configuration again
    SCPreferencesUnlock(prefs);
    return;    


#pragma mark - Create *** Service
// This method creates one *** interface according to the desired configuration
- (void) createService:(***Config*)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 *** 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 *** Service %@ because it already exists.", config.humanType);
            return;
         else 
            NSLog(@"Error: Could not add new *** 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

    [***Keychain createPasswordKeyChainItem:config.name forService:serviceID withAccount:config.username andPassword:config.password];

    [***Keychain 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 %@ *** %@ with ID %@", config.humanType, config.name, serviceID);
    return;

这是我的 ***Config 文件:

// ***Config.h
@interface ***Config : 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



// ***Config.m

typedef NS_ENUM(NSInteger, ***ServiceType) 

    ***ServicePPTP ,

    ***ServiceL2TPOverIPSec,

    ***ServiceCiscoIPSec

;


@implementation ***Config

@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 ***ServicePPTP          : return @"PPTP"; break;

        case ***ServiceL2TPOverIPSec : return @"L2TP over IPSec"; break;

        case ***ServiceCiscoIPSec    : 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_***_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);

【问题讨论】:

你还遇到这个问题吗? @MalavSoni 在我的情况下服务器地址和帐户名称在界面中显示为空,我正在使用 macos***-0.1.0 请帮助我,我将非常感激 @soheil B.marvasti 你有解决方案吗?我也想PPTP。请帮助我,我将非常感激 @MalavSoni 当我将 EditScheme->Run->Info->Debug Process 设置为 (Me) 时,此代码不会将密码数据存储在系统钥匙串中。它只能以(root)身份工作。我已经安装了 Helpertool 仍然是同样的问题 【参考方案1】:

您可以查看以下链接以了解 ***/pptp 连接

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

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

【讨论】:

以上是关于以编程方式在 Mac OS X 上创建 PPTP ***的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式在 iOS 上创建 PPTP ***

如何以编程方式在 macOS Sierra/High Sierra 上创建 PPTP *** 连接?

以编程方式配置 Mac OS X MIDI

在 Mac OS X 上以编程方式为 Matplotlib 选择正确的后端

在 Mac OS X 上以编程方式获取睡眠时间(和其他节能设置)

以编程方式录制声音发送到内置输出,Mac OS X