Подтвердить что ты не робот

Подключение к VPN программно в iOS 8

С момента выпуска бета-версии iOS 8 я нашел в своем комплекте инфраструктуру расширения сети, которая позволяет разработчикам настраивать и подключаться к VPN-серверам программно и без какой-либо установки профиля.

Структура содержит основной класс NEVPNManager. Этот класс также имеет 3 основных метода, которые позволяют мне сохранять, загружать или удалять настройки VPN. Ive написал кусок кода в методе viewDidLoad следующим образом:

NEVPNManager *manager = [NEVPNManager sharedManager];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(vpnConnectionStatusChanged) name:NEVPNStatusDidChangeNotification object:nil];
[manager loadFromPreferencesWithCompletionHandler:^(NSError *error) {
    if(error) {
        NSLog(@"Load error: %@", error);
    }}];
NEVPNProtocolIPSec *p = [[NEVPNProtocolIPSec alloc] init];
p.username = @"[My username]";
p.passwordReference = [KeyChainAccess loadDataForServiceNamed:@"VIT"];
p.serverAddress = @"[My Server Address]";
p.authenticationMethod = NEVPNIKEAuthenticationMethodCertificate;
p.localIdentifier = @"[My Local identifier]";
p.remoteIdentifier = @"[My Remote identifier]";
p.useExtendedAuthentication = NO;
p.identityData = [My VPN certification private key];
p.disconnectOnSleep = NO;
[manager setProtocol:p];
[manager setOnDemandEnabled:NO];
[manager setLocalizedDescription:@"VIT VPN"];
NSArray *array = [NSArray new];
[manager setOnDemandRules: array];
NSLog(@"Connection desciption: %@", manager.localizedDescription);
NSLog(@"VPN status:  %i", manager.connection.status);
[manager saveToPreferencesWithCompletionHandler:^(NSError *error) {
   if(error) {
      NSLog(@"Save error: %@", error);
   }
}];

Я также поместил кнопку на свой взгляд и применил действие TouchUpInside к следующему методу:

- (IBAction)buttonPressed:(id)sender {
   NSError *startError;
   [[NEVPNManager sharedManager].connection startVPNTunnelAndReturnError:&startError];
   if(startError) {
      NSLog(@"Start error: %@", startError.localizedDescription);
   }
}

Здесь есть две проблемы:

1) Когда я попытаюсь сохранить настройки, будет выведена следующая ошибка: Сохранить ошибку: Ошибка домена = NEVPNErrorDomain Code = 4 "Операция не может быть завершена (ошибка NEVPNErrorDomain 4.)" Что это за ошибка? Как я могу решить эту проблему?

2) [[NEVPNManager sharedManager]. connection startVPNTunnelAndReturnError: & startError]; метод не возвращает никаких ошибок, когда я его вызываю, но статус соединения изменяется с отключенного на соединение в течение всего лишь минуты, а затем возвращается в состояние Disconnected.

Любая помощь будет оценена:)

4b9b3361

Ответ 1

Проблема заключается в ошибке, которую вы получаете при сохранении: Save error: Error Domain=NEVPNErrorDomain Code=4

Если вы посмотрите в заголовочном файле NEVPNManager.h, вы увидите, что код ошибки 4 "NEVPNErrorConfigurationStale". Конфигурация устарела и должна быть загружена. Вы должны вызвать loadFromPreferencesWithCompletionHandler: и в обработчике завершения изменить значения, которые вы хотите изменить, а затем вызвать saveToPreferencesWithCompletionHandler:. Пример вашего вопроса заключается в изменении конфигурации до завершения загрузки, поэтому вы получаете эту ошибку.

Более того:

[manager loadFromPreferencesWithCompletionHandler:^(NSError *error) {
     // do config stuff
     [manager saveToPreferencesWithCompletionHandler:^(NSError *error) {
     }];
}];

Ответ 2

Я опубликовал сообщение в блоге относительно этой публикации. Это полный учебник по управлению подключением VPN в iOS 8, который можно найти здесь

Ответ 3

Этот ответ будет полезен тем, кто ищет решение, используя инфраструктуру Network Extension.

Мое требование заключалось в подключении/отключении VPN-сервера с протоколом IKEv2 (конечно, вы можете использовать это решение для IPSec также путем изменения конфигурации протокола vpnManager)

ПРИМЕЧАНИЕ. Если вы ищете протокол L2TP, используя расширение сети, невозможно подключить VPN-сервер. См. https://forums.developer.apple.com/thread/29909

Вот мой фрагмент рабочего кода:

Объявить объект VPNManager и другие полезные вещи

var vpnManager = NEVPNManager.shared()
var isConnected = false

@IBOutlet weak var switchConntectionStatus: UISwitch!    
@IBOutlet weak var labelConntectionStatus: UILabel!

Добавить наблюдателя в viewDidLoad для получения VPN Staus и сохранить vpnPassword в Keychain, вы также можете сохранить sharedSecret, который вам нужен протокол IPSec.

override func viewDidLoad() {

    super.viewDidLoad()

    let keychain = KeychainSwift()
    keychain.set("*****", forKey: "vpnPassword")

    NotificationCenter.default.addObserver(self, selector: #selector(ViewController.VPNStatusDidChange(_:)), name: NSNotification.Name.NEVPNStatusDidChange, object: nil)

 }

Теперь в моем приложении было UISwitch для подключения/отключения VPN-сервера.

func switchClicked() {

    switchConntectionStatus.isOn = false

    if !isConnected {
        initVPNTunnelProviderManager()
    }
    else{
        vpnManager.removeFromPreferences(completionHandler: { (error) in

            if((error) != nil) {
                print("VPN Remove Preferences error: 1")
            }
            else {
                self.vpnManager.connection.stopVPNTunnel()
                self.labelConntectionStatus.text = "Disconnected"
                self.switchConntectionStatus.isOn = false
                self.isConnected = false
            }
        })
    }
}

После нажатия на переключатель инициировать туннель VPN, используя код ниже.

func initVPNTunnelProviderManager(){

    self.vpnManager.loadFromPreferences { (error) -> Void in

        if((error) != nil) {
            print("VPN Preferences error: 1")
        }
        else {

            let p = NEVPNProtocolIKEv2()
// You can change Protocol and credentials as per your protocol i.e IPSec or IKEv2

            p.username = "*****"
            p.remoteIdentifier = "*****"
            p.serverAddress = "*****"

            let keychain = KeychainSwift()
            let data = keychain.getData("vpnPassword")

            p.passwordReference = data
            p.authenticationMethod = NEVPNIKEAuthenticationMethod.none

//          p.sharedSecretReference = KeychainAccess.getData("sharedSecret")! 
// Useful for when you have IPSec Protocol

            p.useExtendedAuthentication = true
            p.disconnectOnSleep = false

            self.vpnManager.protocolConfiguration = p
            self.vpnManager.isEnabled = true

            self.vpnManager.saveToPreferences(completionHandler: { (error) -> Void in
                if((error) != nil) {
                    print("VPN Preferences error: 2")
                }
                else {


                    self.vpnManager.loadFromPreferences(completionHandler: { (error) in

                        if((error) != nil) {

                            print("VPN Preferences error: 2")
                        }
                        else {

                            var startError: NSError?

                            do {
                                try self.vpnManager.connection.startVPNTunnel()
                            }
                            catch let error as NSError {
                                startError = error
                                print(startError)
                            }
                            catch {
                                print("Fatal Error")
                                fatalError()
                            }
                            if((startError) != nil) {
                                print("VPN Preferences error: 3")
                                let alertController = UIAlertController(title: "Oops..", message:
                                    "Something went wrong while connecting to the VPN. Please try again.", preferredStyle: UIAlertControllerStyle.alert)
                                alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.default,handler: nil))

                                self.present(alertController, animated: true, completion: nil)
                                print(startError)
                            }
                            else {
                                self.VPNStatusDidChange(nil)
                                print("VPN started successfully..")
                            }

                        }

                    })

                }
            })
        }
    }
}

После успешного запуска VPN вы можете изменить статус соответственно, то есть вызовом VPNStatusDidChange

func VPNStatusDidChange(_ notification: Notification?) {

    print("VPN Status changed:")
    let status = self.vpnManager.connection.status
    switch status {
    case .connecting:
        print("Connecting...")
        self.labelConntectionStatus.text = "Connecting..."
        self.switchConntectionStatus.isOn = false
        self.isConnected = false

        break
    case .connected:
        print("Connected")
        self.labelConntectionStatus.text = "Connected"
        self.switchConntectionStatus.isOn = true
        self.isConnected = true
        break
    case .disconnecting:
        print("Disconnecting...")
        self.labelConntectionStatus.text = "Disconnecting..."
        self.switchConntectionStatus.isOn = false
        self.isConnected = false

        break
    case .disconnected:
        print("Disconnected")
        self.labelConntectionStatus.text = "Disconnected..."
        self.switchConntectionStatus.isOn = false
        self.isConnected = false

        break
    case .invalid:
        print("Invalid")
        self.labelConntectionStatus.text = "Invalid Connection"
        self.switchConntectionStatus.isOn = false
        self.isConnected = false

        break
    case .reasserting:
        print("Reasserting...")
        self.labelConntectionStatus.text = "Reasserting Connection"
        self.switchConntectionStatus.isOn = false
        self.isConnected = false

        break
    }
}

Я сказал здесь:

fooobar.com/info/239208/...

https://forums.developer.apple.com/thread/25928

http://blog.moatazthenervous.com/create-a-vpn-connection-with-apple-swift/

Спасибо:)