This paper briefly introduces the simple App applications such as G advanced search and Y video in providing security science (double S) in iOS Network Extension, and the third-party library NEKit is used to provide routing rule support.
Demo code has been adapted to Swift5, click the GitHub link to view.
Demo requires a developer account to run. Change the Bundle ID and register with your own developer account.
We need to install before creating an NEProviderTargetTemplates. PKG, after xcode10.12 apple to delete the file in xcode, why? Probably for the same reason as those VXNS that were taken down in China. Fortunately, we can also extract this file from an older version of Xcode, click here to download the link, extract code: 18EK, keep a low profile, and restart Xcode after installation.
Create a project
Next, we will create the Project. First, we will create a Project just like we would create a normal App
Once created, we create a Target in the current Project
Select Network Extension, Next
The language is swift, because NEKit is written by SWIFT and does not support OC very well, so swift is used here.
Once created, a new folder will appear under the directory
About the code of the agent in PacketTunnelProvider. Swift in writing.
After the project is created, enable the Network Extensions and Personal VXN functions in Target’s Capabilities. Ensure that both the main Target and PacketTunnel of the project are enabled.
Ok, this project is created.
NEKit integration
Because NEKit, the third-party library used in the project, only supports Carthage for integration management, the integration tool used in demo is also Carthage. If you have not used it, you can Google it by yourself. It is not difficult to install and use, and you will know it at a sight.
However, note that the third party library needs to use NEKit’s compilation method when compiling, and the direct Carthage Update project will not run.
carthage update –no-use-binaries –platform mac,ios
Framework files will be generated in Build/iOS under the Carthage directory after the third-party library is compiled. We need to add these framework files to the project. The following two locations need to be added.
So far the relevant environment configuration has been done, let’s start to look at the code how to establish VXN link
Create VXN Manager
First you need to create a NETunnelProviderManager
fileprivate func createProviderManager(a) -> NETunnelProviderManager {
let manager = NETunnelProviderManager(a)let conf = NETunnelProviderProtocol()
conf.serverAddress = "BearFree"
manager.protocolConfiguration = conf
manager.localizedDescription = "BearFree"
return manager
}
Copy the code
Save the manager to the system
manager.saveToPreferences{
error in
iferror ! =nil{print(error);return; }//Todo
}
Copy the code
After the save method is executed, the VXN menu is displayed to add the CREATED VPN
In this case, if the save method is invoked for several times, VPN 1, VPN 2 and other description files will appear. Therefore, Apple also requires that the current Managers be read before creation
NETunnelProviderManager.loadAllFromPreferencesWithCompletionHandler{
(managers, error) in
guard let managers = managers else{return}
let manager: NETunnelProviderManager
if managers.count > 0 {
manager = managers[0]}else{
manager = self.createProviderManager()
}
// Todo
// manager.saveToPreferences.......
}
Copy the code
Configure the Network Extension
Open the template file in Extension and modify the syntax for the Swift version and parent class. The following two functions are required to control the VPN status
// Called when the VPN is started
func startTunnel(options: [String : NSObject]? , completionHandler: @escaping(Error?) -> Void)
// Called when the VPN is stopped
func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping (a) -> Void)
Copy the code
At this point, we just need to test whether the VPN connection can be established normally. So we just need to call setTunnelNetworkSettings in startTunnelWithOptions. When completionHandler() executes, the VPN connection is displayed on the phone.
override func startTunnel(options: [String : NSObject]? , completionHandler: @escaping(Error?) -> Void) {
let ipv4Settings = NEIPv4Settings(addresses: ["192.169.89.1"], subnetMasks: ["255.255.255.0"])
let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "8.8.8.8")
networkSettings.mtu = 1500
networkSettings.iPv4Settings = ipv4Settings
setTunnelNetworkSettings(networkSettings) {
error in
guard error == nil else {
completionHandler(error)
return
}
completionHandler(nil)}}Copy the code
Open VXN
Call the startVPNTunnel method on the newly created Manager
try manager.connection.startVPNTunnel(options: [:])
Copy the code
If startVPNTunnel is executed immediately after VXN saveToPreferences is created, Domin=”null” may be displayed, indicating that the system is not ready. So the startup code to loadAndCreatePrividerManager methods return in the block
func connect(a){
self.loadAndCreatePrividerManager { (manager) in
guard let manager = manager else{return}
do{
try manager.connection.startVPNTunnel(options: [:])
}catch let err{
print(err)
}
}
}
Copy the code
For the implementation logic, see the VPNManager. swift file in Demo.
After the success of the connection manager. The connection. The status will change, so we need to monitor the status value, thus learned that the current state of the VPN connection, used to update the UI display.
func addVPNStatusObserver(a) {
guard! observerAddedelse{
return
}
loadProviderManager { [unowned self] (manager) -> Void in
if let manager = manager {
self.observerAdded = true
NotificationCenter.default.addObserver(forName: NSNotification.Name.NEVPNStatusDidChange, object: manager.connection, queue: OperationQueue.main, using: { [unowned self] (notification) -> Void in
self.updateVPNStatus(manager)
})
}
}
}
Copy the code
VXN configuration (SUCH as IP address and port)
The Manager transmits configuration information to Network Extension through the protocolConfiguration property. The following configuration method is implemented in vpnManager and invoked before starting the VPN
fileprivate func setRulerConfig(_ manager:NETunnelProviderManager){
var conf = [String:AnyObject]()
conf["ss_address"] = ip_address as AnyObject?
conf["ss_port"] = port as AnyObject?
conf["ss_method"] = algorithm as AnyObject? // A fatal Error is raised
conf["ss_password"] = password as AnyObject?
conf["ymal_conf"] = getRuleConf() as AnyObject?
let orignConf = manager.protocolConfiguration as! NETunnelProviderProtocol
orignConf.providerConfiguration = conf
manager.protocolConfiguration = orignConf
print(ip_address,port,algorithm,password)
}
Copy the code
In the Extension
public var protocolConfiguration: NEVPNProtocol { get }
Copy the code
Save the configuration information written above, we can read directly.
About network status changes
4G and wifi switch, here is really a bit of a pit, in the inside around for a long time, at the beginning of the debugging found that VXN is through, but over a period of time can not be used, deleted reinstallation is not good, inadvertently changed an EXTENSION IP and good, changed the network environment and can not be used, puzzled, debugging for a long time did not find the problem, Here is how to monitor network environment changes:
Public var defaultPath: NWPath? {get} indicates the network request path of the current system. We can listen for defaultPath changes through KVO. If defaultPath changes from the previous state and defaultPath.status ==.Satisfied, we can assume that the system network has been switched. In this case, you need to restart the VPN and proxy server.
Reconnecting the VPN method is as simple as calling the StartVPN function again.
override func observeValue(forKeyPath keyPath: String? , of object: Any? , change: [NSKeyValueChangeKey : Any]? , context: UnsafeMutableRawPointer?) {
if keyPath == "defaultPath" {
if self.defaultPath?.status == .satisfied && self.defaultPath ! = lastPath{if(lastPath == nil){
lastPath = self.defaultPath
}else{
NSLog("received network change notifcation")
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {[weak self] in
// Delay for 1s to ensure the system is ready
guard let strongSelf = self else { return }
strongSelf.startTunnel(options: nil) {_ in}}}}else{
lastPath = defaultPath
}
}
}
Copy the code
How do YOU debug in this Network Extension? We tried To add breakpoints To the current file in normal debug mode, but found no response, so Tunnel debugging has a separate debugging mode, first we need To run the main program on the device, and then through Xcode-> debug ->Attach To Process (there is a list loading Process, Wait a moment.) Select your Tunnel name and perform debugging. If you do not manually disable the debugging mode of a Tunnel during debugging, the VXN link will break in seconds. Here again, I was how to solve the above problems when switching network, find our App in the system Settings, adjust the network access for WLAN and cellular mobile, at this point before switching network, there is no problem, in order to when just start the App can get Internet access, I added a useless network request, You can get access on your own terms.
demoUI
For the UI layer, I directly use Eureka, the third-party framework, which is the Swift version of XLForm and is also integrated through Carthage. If you want to know more, you can also see the demo.
The End
Ok, THE problems I encountered should be clear, you can communicate with me if there is any problem, hope to correct, 3Q!
Reference Documents:
NEKit
A preliminary look at iOS Network Extension
IOS development practice -NetworkExtension