Connect to VPN programmatically in iOS 8

后端 未结 2 1098
北恋
北恋 2020-12-04 12:35

Since the release of iOS 8 beta, I found a Network Extension framework in its bundle which is going to let developers configure and connect to VPN servers programmatically a

相关标签:
2条回答
  • 2020-12-04 13:15

    This answer will be helpful for those who are looking for solution using Network Extension framework.

    My requirement was to connect/disconnect VPN server with IKEv2 Protocol (of course you can use this solution for IPSec also by changing vpnManager protocolConfiguration)

    NOTE : If you are looking for L2TP Protocol, Using Network extension its not possible to connect VPN server. Refer : https://forums.developer.apple.com/thread/29909

    Here is my working code snippet :

    Declare VPNManager Object and other useful things

    var vpnManager = NEVPNManager.shared()
    var isConnected = false
    
    @IBOutlet weak var switchConntectionStatus: UISwitch!    
    @IBOutlet weak var labelConntectionStatus: UILabel!
    

    Add observer in viewDidLoad for getting VPN Staus and store vpnPassword in Keychain, you can store sharedSecret too which will you need IPSec protocol.

    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)
    
     }
    

    Now in my app was having UISwitch to connect/disconnect VPN server.

    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
                }
            })
        }
    }
    

    After clicking on switch initiate VPN tunnel using below code.

    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..")
                                }
    
                            }
    
                        })
    
                    }
                })
            }
        }
    }
    

    Once VPN started successfully you can change the status accordingly i.e. by calling 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
        }
    }
    

    I've referred from here :

    https://stackoverflow.com/a/47569982/3931796

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

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

    Thank you :)

    0 讨论(0)
  • 2020-12-04 13:24

    The problem is the error you are getting when saving: Save error: Error Domain=NEVPNErrorDomain Code=4

    If you look in the NEVPNManager.h header file, you will see that error code 4 is "NEVPNErrorConfigurationStale". The configuration is stale and needs to be loaded. You should call loadFromPreferencesWithCompletionHandler: and in the completion handler modify the values you want to modify, and then call saveToPreferencesWithCompletionHandler:. The example in your question is modifying the configuration before the loading is completed, which is why you are getting this error.

    More like this:

    [manager loadFromPreferencesWithCompletionHandler:^(NSError *error) {
         // do config stuff
         [manager saveToPreferencesWithCompletionHandler:^(NSError *error) {
         }];
    }];
    
    0 讨论(0)
提交回复
热议问题