Newer
Older
XinYang_IOS / PacketTunnelProvider / PacketTunnelProvider.swift
@zhangfeng zhangfeng on 7 Dec 7 KB 1.8.0
//
//  PacketTunnelProvider.swift
//  PacketTunnelProvider
//
//  Created by Civet on 2021/9/9.
//

import NetworkExtension
import OpenVPNAdapter
extension NEPacketTunnelFlow: OpenVPNAdapterPacketFlow {}


class PacketTunnelProvider: NEPacketTunnelProvider {

    lazy var vpnAdapter: OpenVPNAdapter = {
            let adapter = OpenVPNAdapter()
            adapter.delegate = self

            return adapter
        }()

        let vpnReachability = OpenVPNReachability()

        var startHandler: ((Error?) -> Void)?
        var stopHandler: (() -> Void)?

        override func startTunnel(
            
            
            options: [String : NSObject]?,
            completionHandler: @escaping (Error?) -> Void
        ) {
            
           
            let protocolConfiguration2  = self.protocolConfiguration as! NETunnelProviderProtocol
            let providerConfiguration2 = protocolConfiguration2.providerConfiguration
            

            guard let ovpnFileContent: Data = providerConfiguration2!["ovpn"] as? Data else {
                fatalError()
            }

            let configuration = OpenVPNConfiguration()
            configuration.fileContent = ovpnFileContent
            configuration.autologinSessions = false
            
            configuration.settings = [
                "username" : "chenj",
                "password" : "xyzhsw@123"
            ]

            // Uncomment this line if you want to keep TUN interface active during pauses or reconnections
            // configuration.tunPersist = true

            // Apply OpenVPN configuration
            let evaluation: OpenVPNConfigurationEvaluation
            do {
                evaluation = try vpnAdapter.apply(configuration: configuration)
            } catch {
                completionHandler(error)
                return
            }
             
            // Provide credentials if needed
            if !evaluation.autologin {
                // If your VPN configuration requires user credentials you can provide them by
                // `protocolConfiguration.username` and `protocolConfiguration.passwordReference`
                // properties. It is recommended to use persistent keychain reference to a keychain
                // item containing the password.

                guard let username: String = protocolConfiguration2.username else {
                    fatalError()
                }

//                // Retrieve a password from the keychain
//                guard let password: String = ... {
//                    fatalError()
//                }
//                protocolConfiguration2.identityDataPassword = "xyzhsw@123"
                let credentials = OpenVPNCredentials()
                
                
                HTKeychainManager.save(server: "xinyangshuiwu", account: "chenj", password: "xyzhsw@123")
                credentials.password = HTKeychainManager.getUserPassword(server: "xinyangshuiwu", account: "chenj")
                credentials.username = username
//                credentials.password = "xyzhsw@123"

                do {
                    try vpnAdapter.provide(credentials: credentials)
                } catch {
                    completionHandler(error)
                    return
                }
            }

            // Checking reachability. In some cases after switching from cellular to
            // WiFi the adapter still uses cellular data. Changing reachability forces
            // reconnection so the adapter will use actual connection.
            vpnReachability.startTracking { [weak self] status in
                guard status == .reachableViaWiFi else { return }
                self?.vpnAdapter.reconnect(afterTimeInterval: 5)
            }

            // Establish connection and wait for .connected event
            startHandler = completionHandler
            vpnAdapter.connect(using: packetFlow)
        }

        override func stopTunnel(
            with reason: NEProviderStopReason,
            completionHandler: @escaping () -> Void
        ) {
            stopHandler = completionHandler

            if vpnReachability.isTracking {
                vpnReachability.stopTracking()
            }

            vpnAdapter.disconnect()
        }

    }

    extension PacketTunnelProvider: OpenVPNAdapterDelegate {

        // OpenVPNAdapter calls this delegate method to configure a VPN tunnel.
        // `completionHandler` callback requires an object conforming to `OpenVPNAdapterPacketFlow`
        // protocol if the tunnel is configured without errors. Otherwise send nil.
        // `OpenVPNAdapterPacketFlow` method signatures are similar to `NEPacketTunnelFlow` so
        // you can just extend that class to adopt `OpenVPNAdapterPacketFlow` protocol and
        // send `self.packetFlow` to `completionHandler` callback.
        func openVPNAdapter(
            _ openVPNAdapter: OpenVPNAdapter,
            configureTunnelWithNetworkSettings networkSettings: NEPacketTunnelNetworkSettings?,
            completionHandler: @escaping (Error?) -> Void
        ) {
            // In order to direct all DNS queries first to the VPN DNS servers before the primary DNS servers
            // send empty string to NEDNSSettings.matchDomains
//            networkSettings?.dnsSettings?.matchDomains = ["36.133.215.233"]

            // Set the network settings for the current tunneling session.
            setTunnelNetworkSettings(networkSettings, completionHandler: completionHandler)
        }

        // Process events returned by the OpenVPN library
        func openVPNAdapter(
            _ openVPNAdapter: OpenVPNAdapter,
            handleEvent event:
            OpenVPNAdapterEvent, message: String?
        ) {
            switch event {
            case .connected:
                if reasserting {
                    reasserting = false
                }

                guard let startHandler = startHandler else { return }

                startHandler(nil)
                self.startHandler = nil

            case .disconnected:
                guard let stopHandler = stopHandler else { return }

                if vpnReachability.isTracking {
                    vpnReachability.stopTracking()
                }

                stopHandler()
                self.stopHandler = nil

            case .reconnecting:
                reasserting = true

            default:
                break
            }
        }

        // Handle errors thrown by the OpenVPN library
        func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleError error: Error) {
            // Handle only fatal errors
            guard let fatal = (error as NSError).userInfo[OpenVPNAdapterErrorFatalKey] as? Bool,
                  fatal == true else { return }

            if vpnReachability.isTracking {
                vpnReachability.stopTracking()
            }

            if let startHandler = startHandler {
                startHandler(error)
                self.startHandler = nil
            } else {
                cancelTunnelWithError(error)
            }
        }

        // Use this method to process any log message returned by OpenVPN library.
        func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleLogMessage logMessage: String) {
            // Handle log messages
        }
    
    override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
        // Add code here to handle the message.
        if let handler = completionHandler {
            handler(messageData)
        }
    }
    
    override func sleep(completionHandler: @escaping () -> Void) {
        // Add code here to get ready to sleep.
        completionHandler()
    }
    
    override func wake() {
        // Add code here to wake up.
    }
}