PacketTunnel/PacketTunnelProvider.swift
/* |
Copyright (C) 2016 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
This file contains the PacketTunnelProvider class. The PacketTunnelProvider class is a sub-class of NEPacketTunnelProvider, and is the integration point between the Network Extension framework and the SimpleTunnel tunneling protocol. |
*/ |
import NetworkExtension |
import SimpleTunnelServices |
/// A packet tunnel provider object. |
class PacketTunnelProvider: NEPacketTunnelProvider, TunnelDelegate, ClientTunnelConnectionDelegate { |
// MARK: Properties |
/// A reference to the tunnel object. |
var tunnel: ClientTunnel? |
/// The single logical flow of packets through the tunnel. |
var tunnelConnection: ClientTunnelConnection? |
/// The completion handler to call when the tunnel is fully established. |
var pendingStartCompletion: ((Error?) -> Void)? |
/// The completion handler to call when the tunnel is fully disconnected. |
var pendingStopCompletion: ((Void) -> Void)? |
// MARK: NEPacketTunnelProvider |
/// Begin the process of establishing the tunnel. |
override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) { |
let newTunnel = ClientTunnel() |
newTunnel.delegate = self |
if let error = newTunnel.startTunnel(self) { |
completionHandler(error as NSError) |
} |
else { |
// Save the completion handler for when the tunnel is fully established. |
pendingStartCompletion = completionHandler |
tunnel = newTunnel |
} |
} |
/// Begin the process of stopping the tunnel. |
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { |
// Clear out any pending start completion handler. |
pendingStartCompletion = nil |
// Save the completion handler for when the tunnel is fully disconnected. |
pendingStopCompletion = completionHandler |
tunnel?.closeTunnel() |
} |
/// Handle IPC messages from the app. |
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) { |
guard let messageString = NSString(data: messageData, encoding: String.Encoding.utf8.rawValue) else { |
completionHandler?(nil) |
return |
} |
simpleTunnelLog("Got a message from the app: \(messageString)") |
let responseData = "Hello app".data(using: String.Encoding.utf8) |
completionHandler?(responseData) |
} |
// MARK: TunnelDelegate |
/// Handle the event of the tunnel connection being established. |
func tunnelDidOpen(_ targetTunnel: Tunnel) { |
// Open the logical flow of packets through the tunnel. |
let newConnection = ClientTunnelConnection(tunnel: tunnel!, clientPacketFlow: packetFlow, connectionDelegate: self) |
newConnection.open() |
tunnelConnection = newConnection |
} |
/// Handle the event of the tunnel connection being closed. |
func tunnelDidClose(_ targetTunnel: Tunnel) { |
if pendingStartCompletion != nil { |
// Closed while starting, call the start completion handler with the appropriate error. |
pendingStartCompletion?(tunnel?.lastError) |
pendingStartCompletion = nil |
} |
else if pendingStopCompletion != nil { |
// Closed as the result of a call to stopTunnelWithReason, call the stop completion handler. |
pendingStopCompletion?() |
pendingStopCompletion = nil |
} |
else { |
// Closed as the result of an error on the tunnel connection, cancel the tunnel. |
cancelTunnelWithError(tunnel?.lastError) |
} |
tunnel = nil |
} |
/// Handle the server sending a configuration. |
func tunnelDidSendConfiguration(_ targetTunnel: Tunnel, configuration: [String : AnyObject]) { |
} |
// MARK: ClientTunnelConnectionDelegate |
/// Handle the event of the logical flow of packets being established through the tunnel. |
func tunnelConnectionDidOpen(_ connection: ClientTunnelConnection, configuration: [NSObject: AnyObject]) { |
// Create the virtual interface settings. |
guard let settings = createTunnelSettingsFromConfiguration(configuration) else { |
pendingStartCompletion?(SimpleTunnelError.internalError as NSError) |
pendingStartCompletion = nil |
return |
} |
// Set the virtual interface settings. |
setTunnelNetworkSettings(settings) { error in |
var startError: NSError? |
if let error = error { |
simpleTunnelLog("Failed to set the tunnel network settings: \(error)") |
startError = SimpleTunnelError.badConfiguration as NSError |
} |
else { |
// Now we can start reading and writing packets to/from the virtual interface. |
self.tunnelConnection?.startHandlingPackets() |
} |
// Now the tunnel is fully established, call the start completion handler. |
self.pendingStartCompletion?(startError) |
self.pendingStartCompletion = nil |
} |
} |
/// Handle the event of the logical flow of packets being torn down. |
func tunnelConnectionDidClose(_ connection: ClientTunnelConnection, error: NSError?) { |
tunnelConnection = nil |
tunnel?.closeTunnelWithError(error) |
} |
/// Create the tunnel network settings to be applied to the virtual interface. |
func createTunnelSettingsFromConfiguration(_ configuration: [NSObject: AnyObject]) -> NEPacketTunnelNetworkSettings? { |
guard let tunnelAddress = tunnel?.remoteHost, |
let address = getValueFromPlist(configuration, keyArray: [.IPv4, .Address]) as? String, |
let netmask = getValueFromPlist(configuration, keyArray: [.IPv4, .Netmask]) as? String |
else { return nil } |
let newSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: tunnelAddress) |
var fullTunnel = true |
newSettings.iPv4Settings = NEIPv4Settings(addresses: [address], subnetMasks: [netmask]) |
if let routes = getValueFromPlist(configuration, keyArray: [.IPv4, .Routes]) as? [[String: AnyObject]] { |
var includedRoutes = [NEIPv4Route]() |
for route in routes { |
if let netAddress = route[SettingsKey.Address.rawValue] as? String, |
let netMask = route[SettingsKey.Netmask.rawValue] as? String |
{ |
includedRoutes.append(NEIPv4Route(destinationAddress: netAddress, subnetMask: netMask)) |
} |
} |
newSettings.iPv4Settings?.includedRoutes = includedRoutes |
fullTunnel = false |
} |
else { |
// No routes specified, use the default route. |
newSettings.iPv4Settings?.includedRoutes = [NEIPv4Route.default()] |
} |
if let DNSDictionary = configuration[SettingsKey.DNS.rawValue as NSString] as? [String: AnyObject], |
let DNSServers = DNSDictionary[SettingsKey.Servers.rawValue] as? [String] |
{ |
newSettings.dnsSettings = NEDNSSettings(servers: DNSServers) |
if let DNSSearchDomains = DNSDictionary[SettingsKey.SearchDomains.rawValue] as? [String] { |
newSettings.dnsSettings?.searchDomains = DNSSearchDomains |
if !fullTunnel { |
newSettings.dnsSettings?.matchDomains = DNSSearchDomains |
} |
} |
} |
newSettings.tunnelOverheadBytes = 150 |
return newSettings |
} |
} |
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-10-04