Add SwiftLint (#318)
This commit is contained in:
parent
c62fc4adaa
commit
0c77062add
|
@ -27,7 +27,7 @@ import Foundation
|
||||||
import TunnelKitOpenVPNAppExtension
|
import TunnelKitOpenVPNAppExtension
|
||||||
|
|
||||||
class PacketTunnelProvider: OpenVPNTunnelProvider {
|
class PacketTunnelProvider: OpenVPNTunnelProvider {
|
||||||
override func startTunnel(options: [String : NSObject]? = nil) async throws {
|
override func startTunnel(options: [String: NSObject]? = nil) async throws {
|
||||||
dataCountInterval = 3
|
dataCountInterval = 3
|
||||||
try await super.startTunnel(options: options)
|
try await super.startTunnel(options: options)
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,38 +31,37 @@ private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
@UIApplicationMain
|
@UIApplicationMain
|
||||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
|
|
||||||
var window: UIWindow?
|
var window: UIWindow?
|
||||||
|
|
||||||
|
|
||||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||||
let logDestination = ConsoleDestination()
|
let logDestination = ConsoleDestination()
|
||||||
logDestination.minLevel = .debug
|
logDestination.minLevel = .debug
|
||||||
logDestination.format = "$DHH:mm:ss$d $L $N.$F:$l - $M"
|
logDestination.format = "$DHH:mm:ss$d $L $N.$F:$l - $M"
|
||||||
log.addDestination(logDestination)
|
log.addDestination(logDestination)
|
||||||
|
|
||||||
// Override point for customization after application launch.
|
// Override point for customization after application launch.
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationWillResignActive(_ application: UIApplication) {
|
func applicationWillResignActive(_ application: UIApplication) {
|
||||||
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
|
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
|
||||||
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
|
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationDidEnterBackground(_ application: UIApplication) {
|
func applicationDidEnterBackground(_ application: UIApplication) {
|
||||||
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
|
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
|
||||||
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
|
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationWillEnterForeground(_ application: UIApplication) {
|
func applicationWillEnterForeground(_ application: UIApplication) {
|
||||||
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
|
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationDidBecomeActive(_ application: UIApplication) {
|
func applicationDidBecomeActive(_ application: UIApplication) {
|
||||||
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
|
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationWillTerminate(_ application: UIApplication) {
|
func applicationWillTerminate(_ application: UIApplication) {
|
||||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,39 +34,39 @@ private let tunnelIdentifier = "com.algoritmico.ios.TunnelKit.Demo.OpenVPN.Tunne
|
||||||
|
|
||||||
class OpenVPNViewController: UIViewController {
|
class OpenVPNViewController: UIViewController {
|
||||||
@IBOutlet var textUsername: UITextField!
|
@IBOutlet var textUsername: UITextField!
|
||||||
|
|
||||||
@IBOutlet var textPassword: UITextField!
|
@IBOutlet var textPassword: UITextField!
|
||||||
|
|
||||||
@IBOutlet var textServer: UITextField!
|
@IBOutlet var textServer: UITextField!
|
||||||
|
|
||||||
@IBOutlet var textDomain: UITextField!
|
@IBOutlet var textDomain: UITextField!
|
||||||
|
|
||||||
@IBOutlet var textPort: UITextField!
|
@IBOutlet var textPort: UITextField!
|
||||||
|
|
||||||
@IBOutlet var switchTCP: UISwitch!
|
@IBOutlet var switchTCP: UISwitch!
|
||||||
|
|
||||||
@IBOutlet var buttonConnection: UIButton!
|
@IBOutlet var buttonConnection: UIButton!
|
||||||
|
|
||||||
@IBOutlet var textLog: UITextView!
|
@IBOutlet var textLog: UITextView!
|
||||||
|
|
||||||
private let vpn = NetworkExtensionVPN()
|
private let vpn = NetworkExtensionVPN()
|
||||||
|
|
||||||
private var vpnStatus: VPNStatus = .disconnected
|
private var vpnStatus: VPNStatus = .disconnected
|
||||||
|
|
||||||
private let keychain = Keychain(group: appGroup)
|
private let keychain = Keychain(group: appGroup)
|
||||||
|
|
||||||
private var cfg: OpenVPN.ProviderConfiguration?
|
private var cfg: OpenVPN.ProviderConfiguration?
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
textServer.text = "nl-free-50"
|
textServer.text = "nl-free-50"
|
||||||
textDomain.text = "protonvpn.net"
|
textDomain.text = "protonvpn.net"
|
||||||
textPort.text = "80"
|
textPort.text = "80"
|
||||||
switchTCP.isOn = false
|
switchTCP.isOn = false
|
||||||
textUsername.text = ""
|
textUsername.text = ""
|
||||||
textPassword.text = ""
|
textPassword.text = ""
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(
|
NotificationCenter.default.addObserver(
|
||||||
self,
|
self,
|
||||||
selector: #selector(VPNStatusDidChange(notification:)),
|
selector: #selector(VPNStatusDidChange(notification:)),
|
||||||
|
@ -86,20 +86,20 @@ class OpenVPNViewController: UIViewController {
|
||||||
|
|
||||||
// testFetchRef()
|
// testFetchRef()
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func connectionClicked(_ sender: Any) {
|
@IBAction func connectionClicked(_ sender: Any) {
|
||||||
switch vpnStatus {
|
switch vpnStatus {
|
||||||
case .disconnected:
|
case .disconnected:
|
||||||
connect()
|
connect()
|
||||||
|
|
||||||
case .connected, .connecting, .disconnecting:
|
case .connected, .connecting, .disconnecting:
|
||||||
disconnect()
|
disconnect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func tcpClicked(_ sender: Any) {
|
@IBAction func tcpClicked(_ sender: Any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func connect() {
|
func connect() {
|
||||||
let server = textServer.text!
|
let server = textServer.text!
|
||||||
let domain = textDomain.text!
|
let domain = textDomain.text!
|
||||||
|
@ -136,7 +136,7 @@ class OpenVPNViewController: UIViewController {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func disconnect() {
|
func disconnect() {
|
||||||
Task {
|
Task {
|
||||||
await vpn.disconnect()
|
await vpn.disconnect()
|
||||||
|
@ -152,20 +152,20 @@ class OpenVPNViewController: UIViewController {
|
||||||
}
|
}
|
||||||
textLog.text = try? String(contentsOf: url)
|
textLog.text = try? String(contentsOf: url)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateButton() {
|
func updateButton() {
|
||||||
switch vpnStatus {
|
switch vpnStatus {
|
||||||
case .connected, .connecting:
|
case .connected, .connecting:
|
||||||
buttonConnection.setTitle("Disconnect", for: .normal)
|
buttonConnection.setTitle("Disconnect", for: .normal)
|
||||||
|
|
||||||
case .disconnected:
|
case .disconnected:
|
||||||
buttonConnection.setTitle("Connect", for: .normal)
|
buttonConnection.setTitle("Connect", for: .normal)
|
||||||
|
|
||||||
case .disconnecting:
|
case .disconnecting:
|
||||||
buttonConnection.setTitle("Disconnecting", for: .normal)
|
buttonConnection.setTitle("Disconnecting", for: .normal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func VPNStatusDidChange(notification: Notification) {
|
@objc private func VPNStatusDidChange(notification: Notification) {
|
||||||
vpnStatus = notification.vpnStatus
|
vpnStatus = notification.vpnStatus
|
||||||
print("VPNStatusDidChange: \(vpnStatus)")
|
print("VPNStatusDidChange: \(vpnStatus)")
|
||||||
|
|
|
@ -33,24 +33,24 @@ private let tunnelIdentifier = "com.algoritmico.ios.TunnelKit.Demo.WireGuard.Tun
|
||||||
|
|
||||||
class WireGuardViewController: UIViewController {
|
class WireGuardViewController: UIViewController {
|
||||||
@IBOutlet var textClientPrivateKey: UITextField!
|
@IBOutlet var textClientPrivateKey: UITextField!
|
||||||
|
|
||||||
@IBOutlet var textAddress: UITextField!
|
@IBOutlet var textAddress: UITextField!
|
||||||
|
|
||||||
@IBOutlet var textServerPublicKey: UITextField!
|
@IBOutlet var textServerPublicKey: UITextField!
|
||||||
|
|
||||||
@IBOutlet var textServerAddress: UITextField!
|
@IBOutlet var textServerAddress: UITextField!
|
||||||
|
|
||||||
@IBOutlet var textServerPort: UITextField!
|
@IBOutlet var textServerPort: UITextField!
|
||||||
|
|
||||||
@IBOutlet var buttonConnection: UIButton!
|
@IBOutlet var buttonConnection: UIButton!
|
||||||
|
|
||||||
private let vpn = NetworkExtensionVPN()
|
private let vpn = NetworkExtensionVPN()
|
||||||
|
|
||||||
private var vpnStatus: VPNStatus = .disconnected
|
private var vpnStatus: VPNStatus = .disconnected
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
textClientPrivateKey.placeholder = "client private key"
|
textClientPrivateKey.placeholder = "client private key"
|
||||||
textAddress.placeholder = "client address"
|
textAddress.placeholder = "client address"
|
||||||
textServerPublicKey.placeholder = "server public key"
|
textServerPublicKey.placeholder = "server public key"
|
||||||
|
@ -81,7 +81,7 @@ class WireGuardViewController: UIViewController {
|
||||||
switch vpnStatus {
|
switch vpnStatus {
|
||||||
case .disconnected:
|
case .disconnected:
|
||||||
connect()
|
connect()
|
||||||
|
|
||||||
case .connected, .connecting, .disconnecting:
|
case .connected, .connecting, .disconnecting:
|
||||||
disconnect()
|
disconnect()
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ class WireGuardViewController: UIViewController {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func disconnect() {
|
func disconnect() {
|
||||||
Task {
|
Task {
|
||||||
await vpn.disconnect()
|
await vpn.disconnect()
|
||||||
|
@ -127,15 +127,15 @@ class WireGuardViewController: UIViewController {
|
||||||
switch vpnStatus {
|
switch vpnStatus {
|
||||||
case .connected, .connecting:
|
case .connected, .connecting:
|
||||||
buttonConnection.setTitle("Disconnect", for: .normal)
|
buttonConnection.setTitle("Disconnect", for: .normal)
|
||||||
|
|
||||||
case .disconnected:
|
case .disconnected:
|
||||||
buttonConnection.setTitle("Connect", for: .normal)
|
buttonConnection.setTitle("Connect", for: .normal)
|
||||||
|
|
||||||
case .disconnecting:
|
case .disconnecting:
|
||||||
buttonConnection.setTitle("Disconnecting", for: .normal)
|
buttonConnection.setTitle("Disconnecting", for: .normal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func VPNStatusDidChange(notification: Notification) {
|
@objc private func VPNStatusDidChange(notification: Notification) {
|
||||||
vpnStatus = notification.vpnStatus
|
vpnStatus = notification.vpnStatus
|
||||||
print("VPNStatusDidChange: \(notification.vpnStatus)")
|
print("VPNStatusDidChange: \(notification.vpnStatus)")
|
||||||
|
|
|
@ -31,8 +31,6 @@ private let log = SwiftyBeaver.self
|
||||||
@NSApplicationMain
|
@NSApplicationMain
|
||||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||||
let logDestination = ConsoleDestination()
|
let logDestination = ConsoleDestination()
|
||||||
logDestination.minLevel = .debug
|
logDestination.minLevel = .debug
|
||||||
|
@ -46,6 +44,4 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
// Insert code here to tear down your application
|
// Insert code here to tear down your application
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,34 +34,34 @@ private let tunnelIdentifier = "com.algoritmico.macos.TunnelKit.Demo.OpenVPN.Tun
|
||||||
|
|
||||||
class OpenVPNViewController: NSViewController {
|
class OpenVPNViewController: NSViewController {
|
||||||
@IBOutlet var textUsername: NSTextField!
|
@IBOutlet var textUsername: NSTextField!
|
||||||
|
|
||||||
@IBOutlet var textPassword: NSTextField!
|
@IBOutlet var textPassword: NSTextField!
|
||||||
|
|
||||||
@IBOutlet var textServer: NSTextField!
|
@IBOutlet var textServer: NSTextField!
|
||||||
|
|
||||||
@IBOutlet var textDomain: NSTextField!
|
@IBOutlet var textDomain: NSTextField!
|
||||||
|
|
||||||
@IBOutlet var textPort: NSTextField!
|
@IBOutlet var textPort: NSTextField!
|
||||||
|
|
||||||
@IBOutlet var buttonConnection: NSButton!
|
@IBOutlet var buttonConnection: NSButton!
|
||||||
|
|
||||||
private let vpn = NetworkExtensionVPN()
|
private let vpn = NetworkExtensionVPN()
|
||||||
|
|
||||||
private var vpnStatus: VPNStatus = .disconnected
|
private var vpnStatus: VPNStatus = .disconnected
|
||||||
|
|
||||||
private let keychain = Keychain(group: appGroup)
|
private let keychain = Keychain(group: appGroup)
|
||||||
|
|
||||||
private var cfg: OpenVPN.ProviderConfiguration?
|
private var cfg: OpenVPN.ProviderConfiguration?
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
textServer.stringValue = "nl-free-50"
|
textServer.stringValue = "nl-free-50"
|
||||||
textDomain.stringValue = "protonvpn.net"
|
textDomain.stringValue = "protonvpn.net"
|
||||||
textPort.stringValue = "80"
|
textPort.stringValue = "80"
|
||||||
textUsername.stringValue = ""
|
textUsername.stringValue = ""
|
||||||
textPassword.stringValue = ""
|
textPassword.stringValue = ""
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(
|
NotificationCenter.default.addObserver(
|
||||||
self,
|
self,
|
||||||
selector: #selector(VPNStatusDidChange(notification:)),
|
selector: #selector(VPNStatusDidChange(notification:)),
|
||||||
|
@ -78,20 +78,20 @@ class OpenVPNViewController: NSViewController {
|
||||||
Task {
|
Task {
|
||||||
await vpn.prepare()
|
await vpn.prepare()
|
||||||
}
|
}
|
||||||
|
|
||||||
// testFetchRef()
|
// testFetchRef()
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func connectionClicked(_ sender: Any) {
|
@IBAction func connectionClicked(_ sender: Any) {
|
||||||
switch vpnStatus {
|
switch vpnStatus {
|
||||||
case .disconnected:
|
case .disconnected:
|
||||||
connect()
|
connect()
|
||||||
|
|
||||||
case .connected, .connecting, .disconnecting:
|
case .connected, .connecting, .disconnecting:
|
||||||
disconnect()
|
disconnect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func connect() {
|
func connect() {
|
||||||
let server = textServer.stringValue
|
let server = textServer.stringValue
|
||||||
let domain = textDomain.stringValue
|
let domain = textDomain.stringValue
|
||||||
|
@ -127,7 +127,7 @@ class OpenVPNViewController: NSViewController {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func disconnect() {
|
func disconnect() {
|
||||||
Task {
|
Task {
|
||||||
await vpn.disconnect()
|
await vpn.disconnect()
|
||||||
|
@ -138,15 +138,15 @@ class OpenVPNViewController: NSViewController {
|
||||||
switch vpnStatus {
|
switch vpnStatus {
|
||||||
case .connected, .connecting:
|
case .connected, .connecting:
|
||||||
buttonConnection.title = "Disconnect"
|
buttonConnection.title = "Disconnect"
|
||||||
|
|
||||||
case .disconnected:
|
case .disconnected:
|
||||||
buttonConnection.title = "Connect"
|
buttonConnection.title = "Connect"
|
||||||
|
|
||||||
case .disconnecting:
|
case .disconnecting:
|
||||||
buttonConnection.title = "Disconnecting"
|
buttonConnection.title = "Disconnecting"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func VPNStatusDidChange(notification: Notification) {
|
@objc private func VPNStatusDidChange(notification: Notification) {
|
||||||
vpnStatus = notification.vpnStatus
|
vpnStatus = notification.vpnStatus
|
||||||
print("VPNStatusDidChange: \(vpnStatus)")
|
print("VPNStatusDidChange: \(vpnStatus)")
|
||||||
|
@ -175,4 +175,3 @@ class OpenVPNViewController: NSViewController {
|
||||||
// print("\(username) -> \(fetchedPassword)")
|
// print("\(username) -> \(fetchedPassword)")
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,24 +33,24 @@ private let tunnelIdentifier = "com.algoritmico.macos.TunnelKit.Demo.WireGuard.T
|
||||||
|
|
||||||
class WireGuardViewController: NSViewController {
|
class WireGuardViewController: NSViewController {
|
||||||
@IBOutlet var textClientPrivateKey: NSTextField!
|
@IBOutlet var textClientPrivateKey: NSTextField!
|
||||||
|
|
||||||
@IBOutlet var textAddress: NSTextField!
|
@IBOutlet var textAddress: NSTextField!
|
||||||
|
|
||||||
@IBOutlet var textServerPublicKey: NSTextField!
|
@IBOutlet var textServerPublicKey: NSTextField!
|
||||||
|
|
||||||
@IBOutlet var textServerAddress: NSTextField!
|
@IBOutlet var textServerAddress: NSTextField!
|
||||||
|
|
||||||
@IBOutlet var textServerPort: NSTextField!
|
@IBOutlet var textServerPort: NSTextField!
|
||||||
|
|
||||||
@IBOutlet var buttonConnection: NSButton!
|
@IBOutlet var buttonConnection: NSButton!
|
||||||
|
|
||||||
private let vpn = NetworkExtensionVPN()
|
private let vpn = NetworkExtensionVPN()
|
||||||
|
|
||||||
private var vpnStatus: VPNStatus = .disconnected
|
private var vpnStatus: VPNStatus = .disconnected
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
textClientPrivateKey.placeholderString = "client private key"
|
textClientPrivateKey.placeholderString = "client private key"
|
||||||
textAddress.placeholderString = "client address"
|
textAddress.placeholderString = "client address"
|
||||||
textServerPublicKey.placeholderString = "server public key"
|
textServerPublicKey.placeholderString = "server public key"
|
||||||
|
@ -81,7 +81,7 @@ class WireGuardViewController: NSViewController {
|
||||||
switch vpnStatus {
|
switch vpnStatus {
|
||||||
case .disconnected:
|
case .disconnected:
|
||||||
connect()
|
connect()
|
||||||
|
|
||||||
case .connected, .connecting, .disconnecting:
|
case .connected, .connecting, .disconnecting:
|
||||||
disconnect()
|
disconnect()
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ class WireGuardViewController: NSViewController {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func disconnect() {
|
func disconnect() {
|
||||||
Task {
|
Task {
|
||||||
await vpn.disconnect()
|
await vpn.disconnect()
|
||||||
|
@ -127,15 +127,15 @@ class WireGuardViewController: NSViewController {
|
||||||
switch vpnStatus {
|
switch vpnStatus {
|
||||||
case .connected, .connecting:
|
case .connected, .connecting:
|
||||||
buttonConnection.title = "Disconnect"
|
buttonConnection.title = "Disconnect"
|
||||||
|
|
||||||
case .disconnected:
|
case .disconnected:
|
||||||
buttonConnection.title = "Connect"
|
buttonConnection.title = "Connect"
|
||||||
|
|
||||||
case .disconnecting:
|
case .disconnecting:
|
||||||
buttonConnection.title = "Disconnecting"
|
buttonConnection.title = "Disconnecting"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func VPNStatusDidChange(notification: Notification) {
|
@objc private func VPNStatusDidChange(notification: Notification) {
|
||||||
vpnStatus = notification.vpnStatus
|
vpnStatus = notification.vpnStatus
|
||||||
print("VPNStatusDidChange: \(vpnStatus)")
|
print("VPNStatusDidChange: \(vpnStatus)")
|
||||||
|
|
|
@ -41,7 +41,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
|
|
||||||
var window: UIWindow?
|
var window: UIWindow?
|
||||||
|
|
||||||
|
|
||||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||||
// Override point for customization after application launch.
|
// Override point for customization after application launch.
|
||||||
return true
|
return true
|
||||||
|
@ -69,6 +68,4 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,4 @@ class ViewController: UIViewController {
|
||||||
// Dispose of any resources that can be recreated.
|
// Dispose of any resources that can be recreated.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -418,6 +418,7 @@
|
||||||
0E05416725A232FD00EFC5FF /* Resources */,
|
0E05416725A232FD00EFC5FF /* Resources */,
|
||||||
0E0541AD25A2343500EFC5FF /* Embed App Extensions */,
|
0E0541AD25A2343500EFC5FF /* Embed App Extensions */,
|
||||||
0E05438525A240E400EFC5FF /* Embed Frameworks */,
|
0E05438525A240E400EFC5FF /* Embed Frameworks */,
|
||||||
|
0EB5A56B29F1C9C8005313B3 /* SwiftLint */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
|
@ -463,6 +464,7 @@
|
||||||
0E05422A25A236EB00EFC5FF /* Resources */,
|
0E05422A25A236EB00EFC5FF /* Resources */,
|
||||||
0E05428425A239C600EFC5FF /* Embed App Extensions */,
|
0E05428425A239C600EFC5FF /* Embed App Extensions */,
|
||||||
0E05438825A240E900EFC5FF /* Embed Frameworks */,
|
0E05438825A240E900EFC5FF /* Embed Frameworks */,
|
||||||
|
0EB5A56A29F1C8FC005313B3 /* SwiftLint */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
|
@ -700,6 +702,45 @@
|
||||||
};
|
};
|
||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
0EB5A56A29F1C8FC005313B3 /* SwiftLint */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = SwiftLint;
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "PATH=\"/opt/homebrew/bin:${PATH}\"\nif which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
|
||||||
|
};
|
||||||
|
0EB5A56B29F1C9C8005313B3 /* SwiftLint */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = SwiftLint;
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "PATH=\"/opt/homebrew/bin:${PATH}\"\nif which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
|
||||||
|
};
|
||||||
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
0E05416525A232FD00EFC5FF /* Sources */ = {
|
0E05416525A232FD00EFC5FF /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
|
|
|
@ -188,6 +188,6 @@ let package = Package(
|
||||||
dependencies: [
|
dependencies: [
|
||||||
"TunnelKitCore",
|
"TunnelKitCore",
|
||||||
"TunnelKitLZO"
|
"TunnelKitLZO"
|
||||||
]),
|
])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
@ -67,10 +67,10 @@ public protocol GenericSocket {
|
||||||
|
|
||||||
/// The address of the remote endpoint.
|
/// The address of the remote endpoint.
|
||||||
var remoteAddress: String? { get }
|
var remoteAddress: String? { get }
|
||||||
|
|
||||||
/// `true` if the socket has a better path.
|
/// `true` if the socket has a better path.
|
||||||
var hasBetterPath: Bool { get }
|
var hasBetterPath: Bool { get }
|
||||||
|
|
||||||
/// `true` if the socket was shut down.
|
/// `true` if the socket was shut down.
|
||||||
var isShutdown: Bool { get }
|
var isShutdown: Bool { get }
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ public protocol GenericSocket {
|
||||||
Shuts down the socket
|
Shuts down the socket
|
||||||
**/
|
**/
|
||||||
func shutdown()
|
func shutdown()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns an upgraded socket if available (e.g. when a better path exists).
|
Returns an upgraded socket if available (e.g. when a better path exists).
|
||||||
|
|
||||||
|
|
|
@ -52,9 +52,9 @@ public class InterfaceObserver: NSObject {
|
||||||
public static let didDetectWifiChange = Notification.Name("InterfaceObserverDidDetectWifiChange")
|
public static let didDetectWifiChange = Notification.Name("InterfaceObserverDidDetectWifiChange")
|
||||||
|
|
||||||
private var queue: DispatchQueue?
|
private var queue: DispatchQueue?
|
||||||
|
|
||||||
private var timer: DispatchSourceTimer?
|
private var timer: DispatchSourceTimer?
|
||||||
|
|
||||||
private var lastWifiName: String?
|
private var lastWifiName: String?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -89,7 +89,7 @@ public class InterfaceObserver: NSObject {
|
||||||
self.fireWifiChange(withSSID: $0)
|
self.fireWifiChange(withSSID: $0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func fireWifiChange(withSSID ssid: String?) {
|
private func fireWifiChange(withSSID ssid: String?) {
|
||||||
if ssid != lastWifiName {
|
if ssid != lastWifiName {
|
||||||
if let current = ssid {
|
if let current = ssid {
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class MemoryDestination: BaseDestination, CustomStringConvertible {
|
||||||
super.init()
|
super.init()
|
||||||
asynchronously = false
|
asynchronously = false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Starts logging. Optionally prepend an array of lines.
|
Starts logging. Optionally prepend an array of lines.
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ public class MemoryDestination: BaseDestination, CustomStringConvertible {
|
||||||
try? content.write(to: url, atomically: true, encoding: .utf8)
|
try? content.write(to: url, atomically: true, encoding: .utf8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: BaseDestination
|
// MARK: BaseDestination
|
||||||
|
|
||||||
// XXX: executed in SwiftyBeaver queue. DO NOT invoke execute* here (sync in sync would crash otherwise)
|
// XXX: executed in SwiftyBeaver queue. DO NOT invoke execute* here (sync in sync would crash otherwise)
|
||||||
|
@ -89,7 +89,7 @@ public class MemoryDestination: BaseDestination, CustomStringConvertible {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: CustomStringConvertible
|
// MARK: CustomStringConvertible
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
return executeSynchronously {
|
return executeSynchronously {
|
||||||
return self.buffer.joined(separator: "\n")
|
return self.buffer.joined(separator: "\n")
|
||||||
|
|
|
@ -44,36 +44,36 @@ private let log = SwiftyBeaver.self
|
||||||
/// TCP implementation of a `GenericSocket` via NetworkExtension.
|
/// TCP implementation of a `GenericSocket` via NetworkExtension.
|
||||||
public class NETCPSocket: NSObject, GenericSocket {
|
public class NETCPSocket: NSObject, GenericSocket {
|
||||||
private static var linkContext = 0
|
private static var linkContext = 0
|
||||||
|
|
||||||
public let impl: NWTCPConnection
|
public let impl: NWTCPConnection
|
||||||
|
|
||||||
public init(impl: NWTCPConnection) {
|
public init(impl: NWTCPConnection) {
|
||||||
self.impl = impl
|
self.impl = impl
|
||||||
isActive = false
|
isActive = false
|
||||||
isShutdown = false
|
isShutdown = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: GenericSocket
|
// MARK: GenericSocket
|
||||||
|
|
||||||
private weak var queue: DispatchQueue?
|
private weak var queue: DispatchQueue?
|
||||||
|
|
||||||
private var isActive: Bool
|
private var isActive: Bool
|
||||||
|
|
||||||
public private(set) var isShutdown: Bool
|
public private(set) var isShutdown: Bool
|
||||||
|
|
||||||
public var remoteAddress: String? {
|
public var remoteAddress: String? {
|
||||||
return (impl.remoteAddress as? NWHostEndpoint)?.hostname
|
return (impl.remoteAddress as? NWHostEndpoint)?.hostname
|
||||||
}
|
}
|
||||||
|
|
||||||
public var hasBetterPath: Bool {
|
public var hasBetterPath: Bool {
|
||||||
return impl.hasBetterPath
|
return impl.hasBetterPath
|
||||||
}
|
}
|
||||||
|
|
||||||
public weak var delegate: GenericSocketDelegate?
|
public weak var delegate: GenericSocketDelegate?
|
||||||
|
|
||||||
public func observe(queue: DispatchQueue, activeTimeout: Int) {
|
public func observe(queue: DispatchQueue, activeTimeout: Int) {
|
||||||
isActive = false
|
isActive = false
|
||||||
|
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
queue.schedule(after: .milliseconds(activeTimeout)) { [weak self] in
|
queue.schedule(after: .milliseconds(activeTimeout)) { [weak self] in
|
||||||
guard let _self = self else {
|
guard let _self = self else {
|
||||||
|
@ -87,28 +87,28 @@ public class NETCPSocket: NSObject, GenericSocket {
|
||||||
impl.addObserver(self, forKeyPath: #keyPath(NWTCPConnection.state), options: [.initial, .new], context: &NETCPSocket.linkContext)
|
impl.addObserver(self, forKeyPath: #keyPath(NWTCPConnection.state), options: [.initial, .new], context: &NETCPSocket.linkContext)
|
||||||
impl.addObserver(self, forKeyPath: #keyPath(NWTCPConnection.hasBetterPath), options: .new, context: &NETCPSocket.linkContext)
|
impl.addObserver(self, forKeyPath: #keyPath(NWTCPConnection.hasBetterPath), options: .new, context: &NETCPSocket.linkContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func unobserve() {
|
public func unobserve() {
|
||||||
impl.removeObserver(self, forKeyPath: #keyPath(NWTCPConnection.state), context: &NETCPSocket.linkContext)
|
impl.removeObserver(self, forKeyPath: #keyPath(NWTCPConnection.state), context: &NETCPSocket.linkContext)
|
||||||
impl.removeObserver(self, forKeyPath: #keyPath(NWTCPConnection.hasBetterPath), context: &NETCPSocket.linkContext)
|
impl.removeObserver(self, forKeyPath: #keyPath(NWTCPConnection.hasBetterPath), context: &NETCPSocket.linkContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func shutdown() {
|
public func shutdown() {
|
||||||
impl.writeClose()
|
impl.writeClose()
|
||||||
impl.cancel()
|
impl.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func upgraded() -> GenericSocket? {
|
public func upgraded() -> GenericSocket? {
|
||||||
guard impl.hasBetterPath else {
|
guard impl.hasBetterPath else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return NETCPSocket(impl: NWTCPConnection(upgradeFor: impl))
|
return NETCPSocket(impl: NWTCPConnection(upgradeFor: impl))
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Connection KVO (any queue)
|
// MARK: Connection KVO (any queue)
|
||||||
|
|
||||||
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
|
||||||
guard (context == &NETCPSocket.linkContext) else {
|
guard context == &NETCPSocket.linkContext else {
|
||||||
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
|
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -119,8 +119,8 @@ public class NETCPSocket: NSObject, GenericSocket {
|
||||||
self.observeValueInTunnelQueue(forKeyPath: keyPath, of: object, change: change, context: context)
|
self.observeValueInTunnelQueue(forKeyPath: keyPath, of: object, change: change, context: context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func observeValueInTunnelQueue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
private func observeValueInTunnelQueue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
|
||||||
// if let keyPath = keyPath {
|
// if let keyPath = keyPath {
|
||||||
// log.debug("KVO change reported (\(anyPointer(object)).\(keyPath))")
|
// log.debug("KVO change reported (\(anyPointer(object)).\(keyPath))")
|
||||||
// }
|
// }
|
||||||
|
@ -138,7 +138,7 @@ public class NETCPSocket: NSObject, GenericSocket {
|
||||||
} else {
|
} else {
|
||||||
log.debug("Socket state is \(impl.state) (endpoint: \(impl.endpoint.maskedDescription) -> in progress)")
|
log.debug("Socket state is \(impl.state) (endpoint: \(impl.endpoint.maskedDescription) -> in progress)")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch impl.state {
|
switch impl.state {
|
||||||
case .connected:
|
case .connected:
|
||||||
guard !isActive else {
|
guard !isActive else {
|
||||||
|
@ -146,26 +146,26 @@ public class NETCPSocket: NSObject, GenericSocket {
|
||||||
}
|
}
|
||||||
isActive = true
|
isActive = true
|
||||||
delegate?.socketDidBecomeActive(self)
|
delegate?.socketDidBecomeActive(self)
|
||||||
|
|
||||||
case .cancelled:
|
case .cancelled:
|
||||||
isShutdown = true
|
isShutdown = true
|
||||||
delegate?.socket(self, didShutdownWithFailure: false)
|
delegate?.socket(self, didShutdownWithFailure: false)
|
||||||
|
|
||||||
case .disconnected:
|
case .disconnected:
|
||||||
isShutdown = true
|
isShutdown = true
|
||||||
delegate?.socket(self, didShutdownWithFailure: true)
|
delegate?.socket(self, didShutdownWithFailure: true)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
case #keyPath(NWTCPConnection.hasBetterPath):
|
case #keyPath(NWTCPConnection.hasBetterPath):
|
||||||
guard impl.hasBetterPath else {
|
guard impl.hasBetterPath else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
log.debug("Socket has a better path")
|
log.debug("Socket has a better path")
|
||||||
delegate?.socketHasBetterPath(self)
|
delegate?.socketHasBetterPath(self)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,40 +44,40 @@ private let log = SwiftyBeaver.self
|
||||||
/// `TunnelInterface` implementation via NetworkExtension.
|
/// `TunnelInterface` implementation via NetworkExtension.
|
||||||
public class NETunnelInterface: TunnelInterface {
|
public class NETunnelInterface: TunnelInterface {
|
||||||
private weak var impl: NEPacketTunnelFlow?
|
private weak var impl: NEPacketTunnelFlow?
|
||||||
|
|
||||||
public init(impl: NEPacketTunnelFlow) {
|
public init(impl: NEPacketTunnelFlow) {
|
||||||
self.impl = impl
|
self.impl = impl
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: TunnelInterface
|
// MARK: TunnelInterface
|
||||||
|
|
||||||
public var isPersistent: Bool {
|
public var isPersistent: Bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: IOInterface
|
// MARK: IOInterface
|
||||||
|
|
||||||
public func setReadHandler(queue: DispatchQueue, _ handler: @escaping ([Data]?, Error?) -> Void) {
|
public func setReadHandler(queue: DispatchQueue, _ handler: @escaping ([Data]?, Error?) -> Void) {
|
||||||
loopReadPackets(queue, handler)
|
loopReadPackets(queue, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loopReadPackets(_ queue: DispatchQueue, _ handler: @escaping ([Data]?, Error?) -> Void) {
|
private func loopReadPackets(_ queue: DispatchQueue, _ handler: @escaping ([Data]?, Error?) -> Void) {
|
||||||
|
|
||||||
// WARNING: runs in NEPacketTunnelFlow queue
|
// WARNING: runs in NEPacketTunnelFlow queue
|
||||||
impl?.readPackets { [weak self] (packets, protocols) in
|
impl?.readPackets { [weak self] (packets, _) in
|
||||||
queue.sync {
|
queue.sync {
|
||||||
self?.loopReadPackets(queue, handler)
|
self?.loopReadPackets(queue, handler)
|
||||||
handler(packets, nil)
|
handler(packets, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func writePacket(_ packet: Data, completionHandler: ((Error?) -> Void)?) {
|
public func writePacket(_ packet: Data, completionHandler: ((Error?) -> Void)?) {
|
||||||
let protocolNumber = IPHeader.protocolNumber(inPacket: packet)
|
let protocolNumber = IPHeader.protocolNumber(inPacket: packet)
|
||||||
impl?.writePackets([packet], withProtocols: [protocolNumber])
|
impl?.writePackets([packet], withProtocols: [protocolNumber])
|
||||||
completionHandler?(nil)
|
completionHandler?(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func writePackets(_ packets: [Data], completionHandler: ((Error?) -> Void)?) {
|
public func writePackets(_ packets: [Data], completionHandler: ((Error?) -> Void)?) {
|
||||||
let protocols = packets.map {
|
let protocols = packets.map {
|
||||||
IPHeader.protocolNumber(inPacket: $0)
|
IPHeader.protocolNumber(inPacket: $0)
|
||||||
|
|
|
@ -44,37 +44,37 @@ private let log = SwiftyBeaver.self
|
||||||
/// UDP implementation of a `GenericSocket` via NetworkExtension.
|
/// UDP implementation of a `GenericSocket` via NetworkExtension.
|
||||||
public class NEUDPSocket: NSObject, GenericSocket {
|
public class NEUDPSocket: NSObject, GenericSocket {
|
||||||
private static var linkContext = 0
|
private static var linkContext = 0
|
||||||
|
|
||||||
public let impl: NWUDPSession
|
public let impl: NWUDPSession
|
||||||
|
|
||||||
public init(impl: NWUDPSession) {
|
public init(impl: NWUDPSession) {
|
||||||
self.impl = impl
|
self.impl = impl
|
||||||
|
|
||||||
isActive = false
|
isActive = false
|
||||||
isShutdown = false
|
isShutdown = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: GenericSocket
|
// MARK: GenericSocket
|
||||||
|
|
||||||
private weak var queue: DispatchQueue?
|
private weak var queue: DispatchQueue?
|
||||||
|
|
||||||
private var isActive: Bool
|
private var isActive: Bool
|
||||||
|
|
||||||
public private(set) var isShutdown: Bool
|
public private(set) var isShutdown: Bool
|
||||||
|
|
||||||
public var remoteAddress: String? {
|
public var remoteAddress: String? {
|
||||||
return (impl.resolvedEndpoint as? NWHostEndpoint)?.hostname
|
return (impl.resolvedEndpoint as? NWHostEndpoint)?.hostname
|
||||||
}
|
}
|
||||||
|
|
||||||
public var hasBetterPath: Bool {
|
public var hasBetterPath: Bool {
|
||||||
return impl.hasBetterPath
|
return impl.hasBetterPath
|
||||||
}
|
}
|
||||||
|
|
||||||
public weak var delegate: GenericSocketDelegate?
|
public weak var delegate: GenericSocketDelegate?
|
||||||
|
|
||||||
public func observe(queue: DispatchQueue, activeTimeout: Int) {
|
public func observe(queue: DispatchQueue, activeTimeout: Int) {
|
||||||
isActive = false
|
isActive = false
|
||||||
|
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
queue.schedule(after: .milliseconds(activeTimeout)) { [weak self] in
|
queue.schedule(after: .milliseconds(activeTimeout)) { [weak self] in
|
||||||
guard let _self = self else {
|
guard let _self = self else {
|
||||||
|
@ -88,27 +88,27 @@ public class NEUDPSocket: NSObject, GenericSocket {
|
||||||
impl.addObserver(self, forKeyPath: #keyPath(NWUDPSession.state), options: [.initial, .new], context: &NEUDPSocket.linkContext)
|
impl.addObserver(self, forKeyPath: #keyPath(NWUDPSession.state), options: [.initial, .new], context: &NEUDPSocket.linkContext)
|
||||||
impl.addObserver(self, forKeyPath: #keyPath(NWUDPSession.hasBetterPath), options: .new, context: &NEUDPSocket.linkContext)
|
impl.addObserver(self, forKeyPath: #keyPath(NWUDPSession.hasBetterPath), options: .new, context: &NEUDPSocket.linkContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func unobserve() {
|
public func unobserve() {
|
||||||
impl.removeObserver(self, forKeyPath: #keyPath(NWUDPSession.state), context: &NEUDPSocket.linkContext)
|
impl.removeObserver(self, forKeyPath: #keyPath(NWUDPSession.state), context: &NEUDPSocket.linkContext)
|
||||||
impl.removeObserver(self, forKeyPath: #keyPath(NWUDPSession.hasBetterPath), context: &NEUDPSocket.linkContext)
|
impl.removeObserver(self, forKeyPath: #keyPath(NWUDPSession.hasBetterPath), context: &NEUDPSocket.linkContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func shutdown() {
|
public func shutdown() {
|
||||||
impl.cancel()
|
impl.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func upgraded() -> GenericSocket? {
|
public func upgraded() -> GenericSocket? {
|
||||||
guard impl.hasBetterPath else {
|
guard impl.hasBetterPath else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return NEUDPSocket(impl: NWUDPSession(upgradeFor: impl))
|
return NEUDPSocket(impl: NWUDPSession(upgradeFor: impl))
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Connection KVO (any queue)
|
// MARK: Connection KVO (any queue)
|
||||||
|
|
||||||
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
|
||||||
guard (context == &NEUDPSocket.linkContext) else {
|
guard context == &NEUDPSocket.linkContext else {
|
||||||
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
|
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -119,8 +119,8 @@ public class NEUDPSocket: NSObject, GenericSocket {
|
||||||
self.observeValueInTunnelQueue(forKeyPath: keyPath, of: object, change: change, context: context)
|
self.observeValueInTunnelQueue(forKeyPath: keyPath, of: object, change: change, context: context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func observeValueInTunnelQueue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
private func observeValueInTunnelQueue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
|
||||||
// if let keyPath = keyPath {
|
// if let keyPath = keyPath {
|
||||||
// log.debug("KVO change reported (\(anyPointer(object)).\(keyPath))")
|
// log.debug("KVO change reported (\(anyPointer(object)).\(keyPath))")
|
||||||
// }
|
// }
|
||||||
|
@ -138,7 +138,7 @@ public class NEUDPSocket: NSObject, GenericSocket {
|
||||||
} else {
|
} else {
|
||||||
log.debug("Socket state is \(impl.state) (endpoint: \(impl.endpoint.maskedDescription) -> in progress)")
|
log.debug("Socket state is \(impl.state) (endpoint: \(impl.endpoint.maskedDescription) -> in progress)")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch impl.state {
|
switch impl.state {
|
||||||
case .ready:
|
case .ready:
|
||||||
guard !isActive else {
|
guard !isActive else {
|
||||||
|
@ -146,29 +146,29 @@ public class NEUDPSocket: NSObject, GenericSocket {
|
||||||
}
|
}
|
||||||
isActive = true
|
isActive = true
|
||||||
delegate?.socketDidBecomeActive(self)
|
delegate?.socketDidBecomeActive(self)
|
||||||
|
|
||||||
case .cancelled:
|
case .cancelled:
|
||||||
isShutdown = true
|
isShutdown = true
|
||||||
delegate?.socket(self, didShutdownWithFailure: false)
|
delegate?.socket(self, didShutdownWithFailure: false)
|
||||||
|
|
||||||
case .failed:
|
case .failed:
|
||||||
isShutdown = true
|
isShutdown = true
|
||||||
// if timedOut {
|
// if timedOut {
|
||||||
// delegate?.socketShouldChangeProtocol(self)
|
// delegate?.socketShouldChangeProtocol(self)
|
||||||
// }
|
// }
|
||||||
delegate?.socket(self, didShutdownWithFailure: true)
|
delegate?.socket(self, didShutdownWithFailure: true)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
case #keyPath(NWUDPSession.hasBetterPath):
|
case #keyPath(NWUDPSession.hasBetterPath):
|
||||||
guard impl.hasBetterPath else {
|
guard impl.hasBetterPath else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
log.debug("Socket has a better path")
|
log.debug("Socket has a better path")
|
||||||
delegate?.socketHasBetterPath(self)
|
delegate?.socketHasBetterPath(self)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,10 +31,10 @@ public struct BidirectionalState<T> {
|
||||||
|
|
||||||
/// The inbound state.
|
/// The inbound state.
|
||||||
public var inbound: T
|
public var inbound: T
|
||||||
|
|
||||||
/// The outbound state.
|
/// The outbound state.
|
||||||
public var outbound: T
|
public var outbound: T
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns current state as a pair.
|
Returns current state as a pair.
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ public struct BidirectionalState<T> {
|
||||||
public var pair: (T, T) {
|
public var pair: (T, T) {
|
||||||
return (inbound, outbound)
|
return (inbound, outbound)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Inits state with a value that will later be reused by `reset()`.
|
Inits state with a value that will later be reused by `reset()`.
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ public struct BidirectionalState<T> {
|
||||||
outbound = value
|
outbound = value
|
||||||
resetValue = value
|
resetValue = value
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Resets state to the value provided with `init(withResetValue:)`.
|
Resets state to the value provided with `init(withResetValue:)`.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -41,7 +41,7 @@ public class CoreConfiguration {
|
||||||
|
|
||||||
/// Unique identifier of the library.
|
/// Unique identifier of the library.
|
||||||
public static let identifier = "com.algoritmico.TunnelKit"
|
public static let identifier = "com.algoritmico.TunnelKit"
|
||||||
|
|
||||||
/// Library version as seen in `Info.plist`.
|
/// Library version as seen in `Info.plist`.
|
||||||
public static let version: String = {
|
public static let version: String = {
|
||||||
let bundle = Bundle(for: CoreConfiguration.self)
|
let bundle = Bundle(for: CoreConfiguration.self)
|
||||||
|
@ -63,7 +63,7 @@ public class CoreConfiguration {
|
||||||
|
|
||||||
/// String representing library version.
|
/// String representing library version.
|
||||||
public static var versionIdentifier: String?
|
public static var versionIdentifier: String?
|
||||||
|
|
||||||
/// Enables logging of sensitive data (hardcoded to false).
|
/// Enables logging of sensitive data (hardcoded to false).
|
||||||
public static let logsSensitiveData = false
|
public static let logsSensitiveData = false
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,10 +33,10 @@ public enum DNSProtocol: String, Codable {
|
||||||
|
|
||||||
/// Standard plaintext DNS (port 53).
|
/// Standard plaintext DNS (port 53).
|
||||||
case plain
|
case plain
|
||||||
|
|
||||||
/// DNS over HTTPS.
|
/// DNS over HTTPS.
|
||||||
case https
|
case https
|
||||||
|
|
||||||
/// DNS over TLS (port 853).
|
/// DNS over TLS (port 853).
|
||||||
case tls
|
case tls
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ public struct DNSRecord {
|
||||||
/// Errors coming from `DNSResolver`.
|
/// Errors coming from `DNSResolver`.
|
||||||
public enum DNSError: Error {
|
public enum DNSError: Error {
|
||||||
case failure
|
case failure
|
||||||
|
|
||||||
case timeout
|
case timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,14 +95,14 @@ public class DNSResolver {
|
||||||
pendingHandler = nil
|
pendingHandler = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func didResolve(host: CFHost, completionHandler: @escaping (Result<[DNSRecord], DNSError>) -> Void) {
|
private static func didResolve(host: CFHost, completionHandler: @escaping (Result<[DNSRecord], DNSError>) -> Void) {
|
||||||
var success: DarwinBoolean = false
|
var success: DarwinBoolean = false
|
||||||
guard let rawAddresses = CFHostGetAddressing(host, &success)?.takeUnretainedValue() as Array? else {
|
guard let rawAddresses = CFHostGetAddressing(host, &success)?.takeUnretainedValue() as Array? else {
|
||||||
completionHandler(.failure(.failure))
|
completionHandler(.failure(.failure))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var records: [DNSRecord] = []
|
var records: [DNSRecord] = []
|
||||||
for case let rawAddress as Data in rawAddresses {
|
for case let rawAddress as Data in rawAddresses {
|
||||||
var ipAddress = [CChar](repeating: 0, count: Int(NI_MAXHOST))
|
var ipAddress = [CChar](repeating: 0, count: Int(NI_MAXHOST))
|
||||||
|
@ -152,7 +152,7 @@ public class DNSResolver {
|
||||||
}
|
}
|
||||||
return groups.map { "\($0)" }.joined(separator: ".")
|
return groups.map { "\($0)" }.joined(separator: ".")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns a numeric representation from an IPv4 address.
|
Returns a numeric representation from an IPv4 address.
|
||||||
|
|
||||||
|
|
|
@ -30,10 +30,10 @@ public struct DataCount: Equatable {
|
||||||
|
|
||||||
/// Received bytes count.
|
/// Received bytes count.
|
||||||
public let received: UInt
|
public let received: UInt
|
||||||
|
|
||||||
/// Sent bytes count.
|
/// Sent bytes count.
|
||||||
public let sent: UInt
|
public let sent: UInt
|
||||||
|
|
||||||
public init(_ received: UInt, _ sent: UInt) {
|
public init(_ received: UInt, _ sent: UInt) {
|
||||||
self.received = received
|
self.received = received
|
||||||
self.sent = sent
|
self.sent = sent
|
||||||
|
|
|
@ -28,29 +28,29 @@ import Foundation
|
||||||
/// Helps expressing integers in bytes, kB, MB, GB.
|
/// Helps expressing integers in bytes, kB, MB, GB.
|
||||||
public enum DataUnit: UInt, CustomStringConvertible {
|
public enum DataUnit: UInt, CustomStringConvertible {
|
||||||
case byte = 1
|
case byte = 1
|
||||||
|
|
||||||
case kilobyte = 1024
|
case kilobyte = 1024
|
||||||
|
|
||||||
case megabyte = 1048576
|
case megabyte = 1048576
|
||||||
|
|
||||||
case gigabyte = 1073741824
|
case gigabyte = 1073741824
|
||||||
|
|
||||||
fileprivate var showsDecimals: Bool {
|
fileprivate var showsDecimals: Bool {
|
||||||
switch self {
|
switch self {
|
||||||
case .byte, .kilobyte:
|
case .byte, .kilobyte:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
case .megabyte, .gigabyte:
|
case .megabyte, .gigabyte:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate var boundary: UInt {
|
fileprivate var boundary: UInt {
|
||||||
return UInt(0.1 * Double(rawValue))
|
return UInt(0.1 * Double(rawValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: CustomStringConvertible
|
// MARK: CustomStringConvertible
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .byte:
|
case .byte:
|
||||||
|
@ -82,7 +82,7 @@ extension UInt: DataUnitRepresentable {
|
||||||
.kilobyte,
|
.kilobyte,
|
||||||
.byte
|
.byte
|
||||||
]
|
]
|
||||||
|
|
||||||
public var descriptionAsDataUnit: String {
|
public var descriptionAsDataUnit: String {
|
||||||
if self == 0 {
|
if self == 0 {
|
||||||
return "0B"
|
return "0B"
|
||||||
|
|
|
@ -33,7 +33,7 @@ public struct Endpoint: RawRepresentable, Codable, Equatable, CustomStringConver
|
||||||
private static let rx = NSRegularExpression("^([^\\s]+):(UDP[46]?|TCP[46]?):(\\d+)$")
|
private static let rx = NSRegularExpression("^([^\\s]+):(UDP[46]?|TCP[46]?):(\\d+)$")
|
||||||
|
|
||||||
public let address: String
|
public let address: String
|
||||||
|
|
||||||
public let proto: EndpointProtocol
|
public let proto: EndpointProtocol
|
||||||
|
|
||||||
public init(_ address: String, _ proto: EndpointProtocol) {
|
public init(_ address: String, _ proto: EndpointProtocol) {
|
||||||
|
@ -60,7 +60,7 @@ public struct Endpoint: RawRepresentable, Codable, Equatable, CustomStringConver
|
||||||
public var isHostname: Bool {
|
public var isHostname: Bool {
|
||||||
!isIPv4 && !isIPv6
|
!isIPv4 && !isIPv6
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withRandomPrefixLength(_ length: Int) throws -> Endpoint {
|
public func withRandomPrefixLength(_ length: Int) throws -> Endpoint {
|
||||||
guard isHostname else {
|
guard isHostname else {
|
||||||
return self
|
return self
|
||||||
|
@ -69,9 +69,9 @@ public struct Endpoint: RawRepresentable, Codable, Equatable, CustomStringConver
|
||||||
let prefixedAddress = "\(prefix.toHex()).\(address)"
|
let prefixedAddress = "\(prefix.toHex()).\(address)"
|
||||||
return Endpoint(prefixedAddress, proto)
|
return Endpoint(prefixedAddress, proto)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: RawRepresentable
|
// MARK: RawRepresentable
|
||||||
|
|
||||||
public init?(rawValue: String) {
|
public init?(rawValue: String) {
|
||||||
let components = Self.rx.groups(in: rawValue)
|
let components = Self.rx.groups(in: rawValue)
|
||||||
guard components.count == 3 else {
|
guard components.count == 3 else {
|
||||||
|
@ -90,9 +90,9 @@ public struct Endpoint: RawRepresentable, Codable, Equatable, CustomStringConver
|
||||||
public var rawValue: String {
|
public var rawValue: String {
|
||||||
"\(address):\(proto.socketType.rawValue):\(proto.port)"
|
"\(address):\(proto.socketType.rawValue):\(proto.port)"
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: CustomStringConvertible
|
// MARK: CustomStringConvertible
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
"\(address.maskedDescription):\(proto.rawValue)"
|
"\(address.maskedDescription):\(proto.rawValue)"
|
||||||
}
|
}
|
||||||
|
@ -100,10 +100,10 @@ public struct Endpoint: RawRepresentable, Codable, Equatable, CustomStringConver
|
||||||
|
|
||||||
/// Defines the communication protocol of an endpoint.
|
/// Defines the communication protocol of an endpoint.
|
||||||
public struct EndpointProtocol: RawRepresentable, Equatable, CustomStringConvertible {
|
public struct EndpointProtocol: RawRepresentable, Equatable, CustomStringConvertible {
|
||||||
|
|
||||||
/// The socket type.
|
/// The socket type.
|
||||||
public let socketType: SocketType
|
public let socketType: SocketType
|
||||||
|
|
||||||
/// The remote port.
|
/// The remote port.
|
||||||
public let port: UInt16
|
public let port: UInt16
|
||||||
|
|
||||||
|
@ -111,9 +111,9 @@ public struct EndpointProtocol: RawRepresentable, Equatable, CustomStringConvert
|
||||||
self.socketType = socketType
|
self.socketType = socketType
|
||||||
self.port = port
|
self.port = port
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: RawRepresentable
|
// MARK: RawRepresentable
|
||||||
|
|
||||||
public init?(rawValue: String) {
|
public init?(rawValue: String) {
|
||||||
let components = rawValue.components(separatedBy: ":")
|
let components = rawValue.components(separatedBy: ":")
|
||||||
guard components.count == 2 else {
|
guard components.count == 2 else {
|
||||||
|
@ -127,13 +127,13 @@ public struct EndpointProtocol: RawRepresentable, Equatable, CustomStringConvert
|
||||||
}
|
}
|
||||||
self.init(socketType, port)
|
self.init(socketType, port)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var rawValue: String {
|
public var rawValue: String {
|
||||||
"\(socketType.rawValue):\(port)"
|
"\(socketType.rawValue):\(port)"
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: CustomStringConvertible
|
// MARK: CustomStringConvertible
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
rawValue
|
rawValue
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,7 @@ extension EndpointProtocol: Codable {
|
||||||
let proto = EndpointProtocol(rawValue: rawValue) ?? EndpointProtocol(.udp, 1198)
|
let proto = EndpointProtocol(rawValue: rawValue) ?? EndpointProtocol(.udp, 1198)
|
||||||
self.init(proto.socketType, proto.port)
|
self.init(proto.socketType, proto.port)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
public func encode(to encoder: Encoder) throws {
|
||||||
var container = encoder.singleValueContainer()
|
var container = encoder.singleValueContainer()
|
||||||
try container.encode(rawValue)
|
try container.encode(rawValue)
|
||||||
|
|
|
@ -46,7 +46,7 @@ public protocol IOInterface: AnyObject {
|
||||||
- Parameter handler: The handler invoked whenever an array of `Data` packets is received, with an optional `Error` in case a network failure occurs.
|
- Parameter handler: The handler invoked whenever an array of `Data` packets is received, with an optional `Error` in case a network failure occurs.
|
||||||
*/
|
*/
|
||||||
func setReadHandler(queue: DispatchQueue, _ handler: @escaping ([Data]?, Error?) -> Void)
|
func setReadHandler(queue: DispatchQueue, _ handler: @escaping ([Data]?, Error?) -> Void)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Writes a packet to the interface.
|
Writes a packet to the interface.
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ public protocol IOInterface: AnyObject {
|
||||||
- Parameter completionHandler: Invoked on write completion, with an optional `Error` in case a network failure occurs.
|
- Parameter completionHandler: Invoked on write completion, with an optional `Error` in case a network failure occurs.
|
||||||
*/
|
*/
|
||||||
func writePacket(_ packet: Data, completionHandler: ((Error?) -> Void)?)
|
func writePacket(_ packet: Data, completionHandler: ((Error?) -> Void)?)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Writes some packets to the interface.
|
Writes some packets to the interface.
|
||||||
|
|
||||||
|
|
|
@ -27,9 +27,9 @@ import Foundation
|
||||||
/// Helper for handling IP headers.
|
/// Helper for handling IP headers.
|
||||||
public struct IPHeader {
|
public struct IPHeader {
|
||||||
private static let ipV4: UInt8 = 4
|
private static let ipV4: UInt8 = 4
|
||||||
|
|
||||||
private static let ipV6: UInt8 = 6
|
private static let ipV6: UInt8 = 6
|
||||||
|
|
||||||
private static let ipV4ProtocolNumber = AF_INET as NSNumber
|
private static let ipV4ProtocolNumber = AF_INET as NSNumber
|
||||||
|
|
||||||
private static let ipV6ProtocolNumber = AF_INET6 as NSNumber
|
private static let ipV6ProtocolNumber = AF_INET6 as NSNumber
|
||||||
|
|
|
@ -27,45 +27,45 @@ import Foundation
|
||||||
|
|
||||||
/// Encapsulates the IPv4 settings for the tunnel.
|
/// Encapsulates the IPv4 settings for the tunnel.
|
||||||
public struct IPv4Settings: Codable, Equatable, CustomStringConvertible {
|
public struct IPv4Settings: Codable, Equatable, CustomStringConvertible {
|
||||||
|
|
||||||
/// Represents an IPv4 route in the routing table.
|
/// Represents an IPv4 route in the routing table.
|
||||||
public struct Route: Codable, Hashable, CustomStringConvertible {
|
public struct Route: Codable, Hashable, CustomStringConvertible {
|
||||||
|
|
||||||
/// The destination host or subnet.
|
/// The destination host or subnet.
|
||||||
public let destination: String
|
public let destination: String
|
||||||
|
|
||||||
/// The address mask.
|
/// The address mask.
|
||||||
public let mask: String
|
public let mask: String
|
||||||
|
|
||||||
/// The address of the gateway (falls back to global gateway).
|
/// The address of the gateway (falls back to global gateway).
|
||||||
public let gateway: String?
|
public let gateway: String?
|
||||||
|
|
||||||
public init(_ destination: String, _ mask: String?, _ gateway: String?) {
|
public init(_ destination: String, _ mask: String?, _ gateway: String?) {
|
||||||
self.destination = destination
|
self.destination = destination
|
||||||
self.mask = mask ?? "255.255.255.255"
|
self.mask = mask ?? "255.255.255.255"
|
||||||
self.gateway = gateway
|
self.gateway = gateway
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: CustomStringConvertible
|
// MARK: CustomStringConvertible
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
"{\(destination)/\(mask) \(gateway?.description ?? "*")}"
|
"{\(destination)/\(mask) \(gateway?.description ?? "*")}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The address.
|
/// The address.
|
||||||
public let address: String
|
public let address: String
|
||||||
|
|
||||||
/// The address mask.
|
/// The address mask.
|
||||||
public let addressMask: String
|
public let addressMask: String
|
||||||
|
|
||||||
/// The address of the default gateway.
|
/// The address of the default gateway.
|
||||||
public let defaultGateway: String
|
public let defaultGateway: String
|
||||||
|
|
||||||
/// The additional routes.
|
/// The additional routes.
|
||||||
@available(*, deprecated, message: "Store routes separately")
|
@available(*, deprecated, message: "Store routes separately")
|
||||||
public let routes: [Route]
|
public let routes: [Route]
|
||||||
|
|
||||||
public init(address: String, addressMask: String, defaultGateway: String) {
|
public init(address: String, addressMask: String, defaultGateway: String) {
|
||||||
self.address = address
|
self.address = address
|
||||||
self.addressMask = addressMask
|
self.addressMask = addressMask
|
||||||
|
@ -80,9 +80,9 @@ public struct IPv4Settings: Codable, Equatable, CustomStringConvertible {
|
||||||
self.defaultGateway = defaultGateway
|
self.defaultGateway = defaultGateway
|
||||||
self.routes = routes
|
self.routes = routes
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: CustomStringConvertible
|
// MARK: CustomStringConvertible
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
"addr \(address) netmask \(addressMask) gw \(defaultGateway)"
|
"addr \(address) netmask \(addressMask) gw \(defaultGateway)"
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,45 +27,45 @@ import Foundation
|
||||||
|
|
||||||
/// Encapsulates the IPv6 settings for the tunnel.
|
/// Encapsulates the IPv6 settings for the tunnel.
|
||||||
public struct IPv6Settings: Codable, Equatable, CustomStringConvertible {
|
public struct IPv6Settings: Codable, Equatable, CustomStringConvertible {
|
||||||
|
|
||||||
/// Represents an IPv6 route in the routing table.
|
/// Represents an IPv6 route in the routing table.
|
||||||
public struct Route: Codable, Hashable, CustomStringConvertible {
|
public struct Route: Codable, Hashable, CustomStringConvertible {
|
||||||
|
|
||||||
/// The destination host or subnet.
|
/// The destination host or subnet.
|
||||||
public let destination: String
|
public let destination: String
|
||||||
|
|
||||||
/// The address prefix length.
|
/// The address prefix length.
|
||||||
public let prefixLength: UInt8
|
public let prefixLength: UInt8
|
||||||
|
|
||||||
/// The address of the gateway (falls back to global gateway).
|
/// The address of the gateway (falls back to global gateway).
|
||||||
public let gateway: String?
|
public let gateway: String?
|
||||||
|
|
||||||
public init(_ destination: String, _ prefixLength: UInt8?, _ gateway: String?) {
|
public init(_ destination: String, _ prefixLength: UInt8?, _ gateway: String?) {
|
||||||
self.destination = destination
|
self.destination = destination
|
||||||
self.prefixLength = prefixLength ?? 3
|
self.prefixLength = prefixLength ?? 3
|
||||||
self.gateway = gateway
|
self.gateway = gateway
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: CustomStringConvertible
|
// MARK: CustomStringConvertible
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
"{\(destination.maskedDescription)/\(prefixLength) \(gateway?.maskedDescription ?? "*")}"
|
"{\(destination.maskedDescription)/\(prefixLength) \(gateway?.maskedDescription ?? "*")}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The address.
|
/// The address.
|
||||||
public let address: String
|
public let address: String
|
||||||
|
|
||||||
/// The address prefix length.
|
/// The address prefix length.
|
||||||
public let addressPrefixLength: UInt8
|
public let addressPrefixLength: UInt8
|
||||||
|
|
||||||
/// The address of the default gateway.
|
/// The address of the default gateway.
|
||||||
public let defaultGateway: String
|
public let defaultGateway: String
|
||||||
|
|
||||||
/// The additional routes.
|
/// The additional routes.
|
||||||
@available(*, deprecated, message: "Store routes separately")
|
@available(*, deprecated, message: "Store routes separately")
|
||||||
public let routes: [Route]
|
public let routes: [Route]
|
||||||
|
|
||||||
public init(address: String, addressPrefixLength: UInt8, defaultGateway: String) {
|
public init(address: String, addressPrefixLength: UInt8, defaultGateway: String) {
|
||||||
self.address = address
|
self.address = address
|
||||||
self.addressPrefixLength = addressPrefixLength
|
self.addressPrefixLength = addressPrefixLength
|
||||||
|
@ -82,7 +82,7 @@ public struct IPv6Settings: Codable, Equatable, CustomStringConvertible {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: CustomStringConvertible
|
// MARK: CustomStringConvertible
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
"addr \(address)/\(addressPrefixLength) gw \(defaultGateway)"
|
"addr \(address)/\(addressPrefixLength) gw \(defaultGateway)"
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ import Foundation
|
||||||
|
|
||||||
/// Represents a specific I/O interface meant to work at the link layer (e.g. TCP/IP).
|
/// Represents a specific I/O interface meant to work at the link layer (e.g. TCP/IP).
|
||||||
public protocol LinkInterface: IOInterface {
|
public protocol LinkInterface: IOInterface {
|
||||||
|
|
||||||
/// When `true`, packets delivery is guaranteed.
|
/// When `true`, packets delivery is guaranteed.
|
||||||
var isReliable: Bool { get }
|
var isReliable: Bool { get }
|
||||||
|
|
||||||
|
|
|
@ -27,24 +27,24 @@ import Foundation
|
||||||
|
|
||||||
/// Encapsulates a proxy setting.
|
/// Encapsulates a proxy setting.
|
||||||
public struct Proxy: Codable, Equatable, RawRepresentable, CustomStringConvertible {
|
public struct Proxy: Codable, Equatable, RawRepresentable, CustomStringConvertible {
|
||||||
|
|
||||||
/// The proxy address.
|
/// The proxy address.
|
||||||
public let address: String
|
public let address: String
|
||||||
|
|
||||||
/// The proxy port.
|
/// The proxy port.
|
||||||
public let port: UInt16
|
public let port: UInt16
|
||||||
|
|
||||||
public init(_ address: String, _ port: UInt16) {
|
public init(_ address: String, _ port: UInt16) {
|
||||||
self.address = address
|
self.address = address
|
||||||
self.port = port
|
self.port = port
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: RawRepresentable
|
// MARK: RawRepresentable
|
||||||
|
|
||||||
public var rawValue: String {
|
public var rawValue: String {
|
||||||
return "\(address):\(port)"
|
return "\(address):\(port)"
|
||||||
}
|
}
|
||||||
|
|
||||||
public init?(rawValue: String) {
|
public init?(rawValue: String) {
|
||||||
let comps = rawValue.components(separatedBy: ":")
|
let comps = rawValue.components(separatedBy: ":")
|
||||||
guard comps.count == 2, let port = UInt16(comps[1]) else {
|
guard comps.count == 2, let port = UInt16(comps[1]) else {
|
||||||
|
@ -52,9 +52,9 @@ public struct Proxy: Codable, Equatable, RawRepresentable, CustomStringConvertib
|
||||||
}
|
}
|
||||||
self.init(comps[0], port)
|
self.init(comps[0], port)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: CustomStringConvertible
|
// MARK: CustomStringConvertible
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
return rawValue
|
return rawValue
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,10 +64,10 @@ public class SecureRandom {
|
||||||
}
|
}
|
||||||
return randomNumber
|
return randomNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func uint32() throws -> UInt32 {
|
public static func uint32() throws -> UInt32 {
|
||||||
var randomNumber: UInt32 = 0
|
var randomNumber: UInt32 = 0
|
||||||
|
|
||||||
try withUnsafeMutablePointer(to: &randomNumber) {
|
try withUnsafeMutablePointer(to: &randomNumber) {
|
||||||
try $0.withMemoryRebound(to: UInt8.self, capacity: 4) { (randomBytes: UnsafeMutablePointer<UInt8>) -> Void in
|
try $0.withMemoryRebound(to: UInt8.self, capacity: 4) { (randomBytes: UnsafeMutablePointer<UInt8>) -> Void in
|
||||||
guard SecRandomCopyBytes(kSecRandomDefault, 4, randomBytes) == 0 else {
|
guard SecRandomCopyBytes(kSecRandomDefault, 4, randomBytes) == 0 else {
|
||||||
|
@ -75,7 +75,7 @@ public class SecureRandom {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return randomNumber
|
return randomNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ public class SecureRandom {
|
||||||
throw SecureRandomError.randomGenerator
|
throw SecureRandomError.randomGenerator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return randomData
|
return randomData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ public class SecureRandom {
|
||||||
bzero(randomBytes, length)
|
bzero(randomBytes, length)
|
||||||
randomBytes.deallocate()
|
randomBytes.deallocate()
|
||||||
}
|
}
|
||||||
|
|
||||||
guard SecRandomCopyBytes(kSecRandomDefault, length, randomBytes) == 0 else {
|
guard SecRandomCopyBytes(kSecRandomDefault, length, randomBytes) == 0 else {
|
||||||
throw SecureRandomError.randomGenerator
|
throw SecureRandomError.randomGenerator
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ import Foundation
|
||||||
|
|
||||||
/// Defines the basics of a VPN session.
|
/// Defines the basics of a VPN session.
|
||||||
public protocol Session {
|
public protocol Session {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Establishes the link interface for this session. The interface must be up and running for sending and receiving packets.
|
Establishes the link interface for this session. The interface must be up and running for sending and receiving packets.
|
||||||
|
|
||||||
|
@ -36,14 +36,14 @@ public protocol Session {
|
||||||
- Parameter link: The `LinkInterface` on which to establish the VPN session.
|
- Parameter link: The `LinkInterface` on which to establish the VPN session.
|
||||||
*/
|
*/
|
||||||
func setLink(_ link: LinkInterface)
|
func setLink(_ link: LinkInterface)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns `true` if the current session can rebind to a new link with `rebindLink(...)`.
|
Returns `true` if the current session can rebind to a new link with `rebindLink(...)`.
|
||||||
|
|
||||||
- Returns: `true` if supports link rebinding.
|
- Returns: `true` if supports link rebinding.
|
||||||
*/
|
*/
|
||||||
func canRebindLink() -> Bool
|
func canRebindLink() -> Bool
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Rebinds the session to a new link if supported.
|
Rebinds the session to a new link if supported.
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ public protocol Session {
|
||||||
- Seealso: `canRebindLink()`
|
- Seealso: `canRebindLink()`
|
||||||
*/
|
*/
|
||||||
func rebindLink(_ link: LinkInterface)
|
func rebindLink(_ link: LinkInterface)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Establishes the tunnel interface for this session. The interface must be up and running for sending and receiving packets.
|
Establishes the tunnel interface for this session. The interface must be up and running for sending and receiving packets.
|
||||||
|
|
||||||
|
@ -62,14 +62,14 @@ public protocol Session {
|
||||||
- Parameter tunnel: The `TunnelInterface` on which to exchange the VPN data traffic.
|
- Parameter tunnel: The `TunnelInterface` on which to exchange the VPN data traffic.
|
||||||
*/
|
*/
|
||||||
func setTunnel(tunnel: TunnelInterface)
|
func setTunnel(tunnel: TunnelInterface)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns the current data bytes count.
|
Returns the current data bytes count.
|
||||||
|
|
||||||
- Returns: The current data bytes count.
|
- Returns: The current data bytes count.
|
||||||
*/
|
*/
|
||||||
func dataCount() -> DataCount?
|
func dataCount() -> DataCount?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns the current server configuration.
|
Returns the current server configuration.
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ public protocol Session {
|
||||||
- Parameter error: An optional `Error` being the reason of the shutdown.
|
- Parameter error: An optional `Error` being the reason of the shutdown.
|
||||||
*/
|
*/
|
||||||
func shutdown(error: Error?)
|
func shutdown(error: Error?)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Shuts down the session with an optional `Error` reason and signals a reconnect flag to `OpenVPNSessionDelegate.sessionDidStop(...)`. Does nothing if the session is already stopped or about to stop.
|
Shuts down the session with an optional `Error` reason and signals a reconnect flag to `OpenVPNSessionDelegate.sessionDidStop(...)`. Does nothing if the session is already stopped or about to stop.
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ public protocol Session {
|
||||||
- Seealso: `OpenVPNSessionDelegate.sessionDidStop(...)`
|
- Seealso: `OpenVPNSessionDelegate.sessionDidStop(...)`
|
||||||
*/
|
*/
|
||||||
func reconnect(error: Error?)
|
func reconnect(error: Error?)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Cleans up the session resources.
|
Cleans up the session resources.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -27,22 +27,22 @@ import Foundation
|
||||||
|
|
||||||
/// A socket type between UDP (recommended) and TCP.
|
/// A socket type between UDP (recommended) and TCP.
|
||||||
public enum SocketType: String {
|
public enum SocketType: String {
|
||||||
|
|
||||||
/// UDP socket type.
|
/// UDP socket type.
|
||||||
case udp = "UDP"
|
case udp = "UDP"
|
||||||
|
|
||||||
/// TCP socket type.
|
/// TCP socket type.
|
||||||
case tcp = "TCP"
|
case tcp = "TCP"
|
||||||
|
|
||||||
/// UDP socket type (IPv4).
|
/// UDP socket type (IPv4).
|
||||||
case udp4 = "UDP4"
|
case udp4 = "UDP4"
|
||||||
|
|
||||||
/// TCP socket type (IPv4).
|
/// TCP socket type (IPv4).
|
||||||
case tcp4 = "TCP4"
|
case tcp4 = "TCP4"
|
||||||
|
|
||||||
/// UDP socket type (IPv6).
|
/// UDP socket type (IPv6).
|
||||||
case udp6 = "UDP6"
|
case udp6 = "UDP6"
|
||||||
|
|
||||||
/// TCP socket type (IPv6).
|
/// TCP socket type (IPv6).
|
||||||
case tcp6 = "TCP6"
|
case tcp6 = "TCP6"
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,9 +61,9 @@ public func Z(_ data: Data) -> ZeroingData {
|
||||||
return ZeroingData(data: data)
|
return ZeroingData(data: data)
|
||||||
}
|
}
|
||||||
|
|
||||||
//public func Z(_ data: Data, _ offset: Int, _ count: Int) -> ZeroingData {
|
// public func Z(_ data: Data, _ offset: Int, _ count: Int) -> ZeroingData {
|
||||||
// return ZeroingData(data: data, offset: offset, count: count)
|
// return ZeroingData(data: data, offset: offset, count: count)
|
||||||
//}
|
// }
|
||||||
|
|
||||||
public func Z(_ string: String, nullTerminated: Bool) -> ZeroingData {
|
public func Z(_ string: String, nullTerminated: Bool) -> ZeroingData {
|
||||||
return ZeroingData(string: string, nullTerminated: nullTerminated)
|
return ZeroingData(string: string, nullTerminated: nullTerminated)
|
||||||
|
|
|
@ -46,13 +46,13 @@ public enum KeychainError: Error {
|
||||||
|
|
||||||
/// Unable to add.
|
/// Unable to add.
|
||||||
case add
|
case add
|
||||||
|
|
||||||
/// Item not found.
|
/// Item not found.
|
||||||
case notFound
|
case notFound
|
||||||
|
|
||||||
/// Operation cancelled or unauthorized.
|
/// Operation cancelled or unauthorized.
|
||||||
case userCancelled
|
case userCancelled
|
||||||
|
|
||||||
// /// Unexpected item type returned.
|
// /// Unexpected item type returned.
|
||||||
// case typeMismatch
|
// case typeMismatch
|
||||||
}
|
}
|
||||||
|
@ -70,9 +70,9 @@ public class Keychain {
|
||||||
public init(group: String?) {
|
public init(group: String?) {
|
||||||
accessGroup = group
|
accessGroup = group
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Password
|
// MARK: Password
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Sets a password.
|
Sets a password.
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ public class Keychain {
|
||||||
}
|
}
|
||||||
return refData
|
return refData
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Removes a password.
|
Removes a password.
|
||||||
|
|
||||||
|
@ -153,12 +153,12 @@ public class Keychain {
|
||||||
query[kSecAttrAccount as String] = username
|
query[kSecAttrAccount as String] = username
|
||||||
query[kSecMatchLimit as String] = kSecMatchLimitOne
|
query[kSecMatchLimit as String] = kSecMatchLimitOne
|
||||||
query[kSecReturnData as String] = true
|
query[kSecReturnData as String] = true
|
||||||
|
|
||||||
var result: AnyObject?
|
var result: AnyObject?
|
||||||
switch SecItemCopyMatching(query as CFDictionary, &result) {
|
switch SecItemCopyMatching(query as CFDictionary, &result) {
|
||||||
case errSecSuccess:
|
case errSecSuccess:
|
||||||
break
|
break
|
||||||
|
|
||||||
case errSecUserCanceled:
|
case errSecUserCanceled:
|
||||||
throw KeychainError.userCancelled
|
throw KeychainError.userCancelled
|
||||||
|
|
||||||
|
@ -190,12 +190,12 @@ public class Keychain {
|
||||||
query[kSecAttrAccount as String] = username
|
query[kSecAttrAccount as String] = username
|
||||||
query[kSecMatchLimit as String] = kSecMatchLimitOne
|
query[kSecMatchLimit as String] = kSecMatchLimitOne
|
||||||
query[kSecReturnPersistentRef as String] = true
|
query[kSecReturnPersistentRef as String] = true
|
||||||
|
|
||||||
var result: AnyObject?
|
var result: AnyObject?
|
||||||
switch SecItemCopyMatching(query as CFDictionary, &result) {
|
switch SecItemCopyMatching(query as CFDictionary, &result) {
|
||||||
case errSecSuccess:
|
case errSecSuccess:
|
||||||
break
|
break
|
||||||
|
|
||||||
case errSecUserCanceled:
|
case errSecUserCanceled:
|
||||||
throw KeychainError.userCancelled
|
throw KeychainError.userCancelled
|
||||||
|
|
||||||
|
@ -207,7 +207,7 @@ public class Keychain {
|
||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Gets a password associated with a password reference.
|
Gets a password associated with a password reference.
|
||||||
|
|
||||||
|
@ -219,12 +219,12 @@ public class Keychain {
|
||||||
var query = [String: Any]()
|
var query = [String: Any]()
|
||||||
query[kSecValuePersistentRef as String] = reference
|
query[kSecValuePersistentRef as String] = reference
|
||||||
query[kSecReturnData as String] = true
|
query[kSecReturnData as String] = true
|
||||||
|
|
||||||
var result: AnyObject?
|
var result: AnyObject?
|
||||||
switch SecItemCopyMatching(query as CFDictionary, &result) {
|
switch SecItemCopyMatching(query as CFDictionary, &result) {
|
||||||
case errSecSuccess:
|
case errSecSuccess:
|
||||||
break
|
break
|
||||||
|
|
||||||
case errSecUserCanceled:
|
case errSecUserCanceled:
|
||||||
throw KeychainError.userCancelled
|
throw KeychainError.userCancelled
|
||||||
|
|
||||||
|
@ -239,11 +239,11 @@ public class Keychain {
|
||||||
}
|
}
|
||||||
return password
|
return password
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Key
|
// MARK: Key
|
||||||
|
|
||||||
// https://forums.developer.apple.com/thread/13748
|
// https://forums.developer.apple.com/thread/13748
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Adds a public key.
|
Adds a public key.
|
||||||
|
|
||||||
|
@ -269,7 +269,7 @@ public class Keychain {
|
||||||
}
|
}
|
||||||
return try publicKey(withIdentifier: identifier)
|
return try publicKey(withIdentifier: identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Gets a public key.
|
Gets a public key.
|
||||||
|
|
||||||
|
@ -292,7 +292,7 @@ public class Keychain {
|
||||||
switch SecItemCopyMatching(query as CFDictionary, &result) {
|
switch SecItemCopyMatching(query as CFDictionary, &result) {
|
||||||
case errSecSuccess:
|
case errSecSuccess:
|
||||||
break
|
break
|
||||||
|
|
||||||
case errSecUserCanceled:
|
case errSecUserCanceled:
|
||||||
throw KeychainError.userCancelled
|
throw KeychainError.userCancelled
|
||||||
|
|
||||||
|
@ -305,7 +305,7 @@ public class Keychain {
|
||||||
// return key
|
// return key
|
||||||
return result as! SecKey
|
return result as! SecKey
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Removes a public key.
|
Removes a public key.
|
||||||
|
|
||||||
|
@ -325,9 +325,9 @@ public class Keychain {
|
||||||
let status = SecItemDelete(query as CFDictionary)
|
let status = SecItemDelete(query as CFDictionary)
|
||||||
return status == errSecSuccess
|
return status == errSecSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Helpers
|
// MARK: Helpers
|
||||||
|
|
||||||
public func setScope(query: inout [String: Any], context: String, userDefined: String?) {
|
public func setScope(query: inout [String: Any], context: String, userDefined: String?) {
|
||||||
if let accessGroup = accessGroup {
|
if let accessGroup = accessGroup {
|
||||||
query[kSecAttrAccessGroup as String] = accessGroup
|
query[kSecAttrAccessGroup as String] = accessGroup
|
||||||
|
|
|
@ -29,32 +29,32 @@ import NetworkExtension
|
||||||
/// Simulates a VPN provider.
|
/// Simulates a VPN provider.
|
||||||
public class MockVPN: VPN {
|
public class MockVPN: VPN {
|
||||||
private var tunnelBundleIdentifier: String?
|
private var tunnelBundleIdentifier: String?
|
||||||
|
|
||||||
private var isEnabled: Bool {
|
private var isEnabled: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
notifyReinstall(isEnabled)
|
notifyReinstall(isEnabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var vpnStatus: VPNStatus {
|
private var vpnStatus: VPNStatus {
|
||||||
didSet {
|
didSet {
|
||||||
notifyStatus(vpnStatus)
|
notifyStatus(vpnStatus)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private let delayNanoseconds: UInt64
|
private let delayNanoseconds: UInt64
|
||||||
|
|
||||||
public init(delay: Int = 1) {
|
public init(delay: Int = 1) {
|
||||||
delayNanoseconds = DispatchTimeInterval.seconds(delay).nanoseconds
|
delayNanoseconds = DispatchTimeInterval.seconds(delay).nanoseconds
|
||||||
isEnabled = false
|
isEnabled = false
|
||||||
vpnStatus = .disconnected
|
vpnStatus = .disconnected
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: VPN
|
// MARK: VPN
|
||||||
|
|
||||||
public func prepare() {
|
public func prepare() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func install(
|
public func install(
|
||||||
_ tunnelBundleIdentifier: String,
|
_ tunnelBundleIdentifier: String,
|
||||||
configuration: NetworkExtensionConfiguration,
|
configuration: NetworkExtensionConfiguration,
|
||||||
|
@ -64,7 +64,7 @@ public class MockVPN: VPN {
|
||||||
isEnabled = true
|
isEnabled = true
|
||||||
vpnStatus = .disconnected
|
vpnStatus = .disconnected
|
||||||
}
|
}
|
||||||
|
|
||||||
public func reconnect(after: DispatchTimeInterval) async throws {
|
public func reconnect(after: DispatchTimeInterval) async throws {
|
||||||
if vpnStatus == .connected {
|
if vpnStatus == .connected {
|
||||||
vpnStatus = .disconnecting
|
vpnStatus = .disconnecting
|
||||||
|
@ -74,7 +74,7 @@ public class MockVPN: VPN {
|
||||||
await delay()
|
await delay()
|
||||||
vpnStatus = .connected
|
vpnStatus = .connected
|
||||||
}
|
}
|
||||||
|
|
||||||
public func reconnect(
|
public func reconnect(
|
||||||
_ tunnelBundleIdentifier: String,
|
_ tunnelBundleIdentifier: String,
|
||||||
configuration: NetworkExtensionConfiguration,
|
configuration: NetworkExtensionConfiguration,
|
||||||
|
@ -91,7 +91,7 @@ public class MockVPN: VPN {
|
||||||
await delay()
|
await delay()
|
||||||
vpnStatus = .connected
|
vpnStatus = .connected
|
||||||
}
|
}
|
||||||
|
|
||||||
public func disconnect() async {
|
public func disconnect() async {
|
||||||
guard vpnStatus != .disconnected else {
|
guard vpnStatus != .disconnected else {
|
||||||
return
|
return
|
||||||
|
@ -101,21 +101,21 @@ public class MockVPN: VPN {
|
||||||
vpnStatus = .disconnected
|
vpnStatus = .disconnected
|
||||||
isEnabled = false
|
isEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
public func uninstall() async {
|
public func uninstall() async {
|
||||||
vpnStatus = .disconnected
|
vpnStatus = .disconnected
|
||||||
isEnabled = false
|
isEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Helpers
|
// MARK: Helpers
|
||||||
|
|
||||||
private func notifyReinstall(_ isEnabled: Bool) {
|
private func notifyReinstall(_ isEnabled: Bool) {
|
||||||
var notification = Notification(name: VPNNotification.didReinstall)
|
var notification = Notification(name: VPNNotification.didReinstall)
|
||||||
notification.vpnBundleIdentifier = tunnelBundleIdentifier
|
notification.vpnBundleIdentifier = tunnelBundleIdentifier
|
||||||
notification.vpnIsEnabled = isEnabled
|
notification.vpnIsEnabled = isEnabled
|
||||||
NotificationCenter.default.post(notification)
|
NotificationCenter.default.post(notification)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func notifyStatus(_ status: VPNStatus) {
|
private func notifyStatus(_ status: VPNStatus) {
|
||||||
var notification = Notification(name: VPNNotification.didChangeStatus)
|
var notification = Notification(name: VPNNotification.didChangeStatus)
|
||||||
notification.vpnBundleIdentifier = tunnelBundleIdentifier
|
notification.vpnBundleIdentifier = tunnelBundleIdentifier
|
||||||
|
|
|
@ -37,10 +37,10 @@ public struct NetworkExtensionExtra {
|
||||||
|
|
||||||
/// Disconnects on sleep if `true`.
|
/// Disconnects on sleep if `true`.
|
||||||
public var disconnectsOnSleep = false
|
public var disconnectsOnSleep = false
|
||||||
|
|
||||||
/// Enables best-effort kill switch.
|
/// Enables best-effort kill switch.
|
||||||
public var killSwitch = false
|
public var killSwitch = false
|
||||||
|
|
||||||
public init() {
|
public init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ public protocol NetworkExtensionConfiguration {
|
||||||
|
|
||||||
/// The profile title in device settings.
|
/// The profile title in device settings.
|
||||||
var title: String { get }
|
var title: String { get }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns a representation for use with tunnel implementations.
|
Returns a representation for use with tunnel implementations.
|
||||||
|
|
||||||
|
|
|
@ -44,13 +44,13 @@ public class NetworkExtensionVPN: VPN {
|
||||||
deinit {
|
deinit {
|
||||||
NotificationCenter.default.removeObserver(self)
|
NotificationCenter.default.removeObserver(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Public
|
// MARK: Public
|
||||||
|
|
||||||
public func prepare() async {
|
public func prepare() async {
|
||||||
_ = try? await NETunnelProviderManager.loadAllFromPreferences()
|
_ = try? await NETunnelProviderManager.loadAllFromPreferences()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func install(
|
public func install(
|
||||||
_ tunnelBundleIdentifier: String,
|
_ tunnelBundleIdentifier: String,
|
||||||
configuration: NetworkExtensionConfiguration,
|
configuration: NetworkExtensionConfiguration,
|
||||||
|
@ -62,7 +62,7 @@ public class NetworkExtensionVPN: VPN {
|
||||||
extra: extra
|
extra: extra
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func reconnect(after: DispatchTimeInterval) async throws {
|
public func reconnect(after: DispatchTimeInterval) async throws {
|
||||||
let managers = try await lookupAll()
|
let managers = try await lookupAll()
|
||||||
guard let manager = managers.first else {
|
guard let manager = managers.first else {
|
||||||
|
@ -97,7 +97,7 @@ public class NetworkExtensionVPN: VPN {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func disconnect() async {
|
public func disconnect() async {
|
||||||
guard let managers = try? await lookupAll() else {
|
guard let managers = try? await lookupAll() else {
|
||||||
return
|
return
|
||||||
|
@ -112,7 +112,7 @@ public class NetworkExtensionVPN: VPN {
|
||||||
try? await m.saveToPreferences()
|
try? await m.saveToPreferences()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func uninstall() async {
|
public func uninstall() async {
|
||||||
guard let managers = try? await lookupAll() else {
|
guard let managers = try? await lookupAll() else {
|
||||||
return
|
return
|
||||||
|
@ -156,10 +156,10 @@ public class NetworkExtensionVPN: VPN {
|
||||||
await retainManagers(managers) {
|
await retainManagers(managers) {
|
||||||
$0.isTunnel(withIdentifier: tunnelBundleIdentifier)
|
$0.isTunnel(withIdentifier: tunnelBundleIdentifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
return targetManager
|
return targetManager
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
private func install(
|
private func install(
|
||||||
_ manager: NETunnelProviderManager,
|
_ manager: NETunnelProviderManager,
|
||||||
|
@ -202,11 +202,11 @@ public class NetworkExtensionVPN: VPN {
|
||||||
try? await o.removeFromPreferences()
|
try? await o.removeFromPreferences()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func lookupAll() async throws -> [NETunnelProviderManager] {
|
private func lookupAll() async throws -> [NETunnelProviderManager] {
|
||||||
try await NETunnelProviderManager.loadAllFromPreferences()
|
try await NETunnelProviderManager.loadAllFromPreferences()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Notifications
|
// MARK: Notifications
|
||||||
|
|
||||||
@objc private func vpnDidUpdate(_ notification: Notification) {
|
@objc private func vpnDidUpdate(_ notification: Notification) {
|
||||||
|
@ -222,7 +222,7 @@ public class NetworkExtensionVPN: VPN {
|
||||||
}
|
}
|
||||||
notifyReinstall(manager)
|
notifyReinstall(manager)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func notifyReinstall(_ manager: NETunnelProviderManager) {
|
private func notifyReinstall(_ manager: NETunnelProviderManager) {
|
||||||
let bundleId = manager.tunnelBundleIdentifier
|
let bundleId = manager.tunnelBundleIdentifier
|
||||||
log.debug("VPN did reinstall (\(bundleId ?? "?")): isEnabled=\(manager.isEnabled)")
|
log.debug("VPN did reinstall (\(bundleId ?? "?")): isEnabled=\(manager.isEnabled)")
|
||||||
|
@ -232,7 +232,7 @@ public class NetworkExtensionVPN: VPN {
|
||||||
notification.vpnIsEnabled = manager.isEnabled
|
notification.vpnIsEnabled = manager.isEnabled
|
||||||
NotificationCenter.default.post(notification)
|
NotificationCenter.default.post(notification)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func notifyStatus(_ connection: NETunnelProviderSession) {
|
private func notifyStatus(_ connection: NETunnelProviderSession) {
|
||||||
guard let _ = connection.manager.localizedDescription else {
|
guard let _ = connection.manager.localizedDescription else {
|
||||||
log.verbose("Ignoring VPN notification from bogus manager")
|
log.verbose("Ignoring VPN notification from bogus manager")
|
||||||
|
@ -247,7 +247,7 @@ public class NetworkExtensionVPN: VPN {
|
||||||
notification.vpnStatus = connection.status.wrappedStatus
|
notification.vpnStatus = connection.status.wrappedStatus
|
||||||
NotificationCenter.default.post(notification)
|
NotificationCenter.default.post(notification)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func notifyInstallError(_ error: Error) {
|
private func notifyInstallError(_ error: Error) {
|
||||||
log.error("VPN installation failed: \(error))")
|
log.error("VPN installation failed: \(error))")
|
||||||
|
|
||||||
|
@ -265,7 +265,7 @@ private extension NEVPNManager {
|
||||||
}
|
}
|
||||||
return proto.providerBundleIdentifier
|
return proto.providerBundleIdentifier
|
||||||
}
|
}
|
||||||
|
|
||||||
func isTunnel(withIdentifier bundleIdentifier: String) -> Bool {
|
func isTunnel(withIdentifier bundleIdentifier: String) -> Bool {
|
||||||
return tunnelBundleIdentifier == bundleIdentifier
|
return tunnelBundleIdentifier == bundleIdentifier
|
||||||
}
|
}
|
||||||
|
@ -276,13 +276,13 @@ private extension NEVPNStatus {
|
||||||
switch self {
|
switch self {
|
||||||
case .connected:
|
case .connected:
|
||||||
return .connected
|
return .connected
|
||||||
|
|
||||||
case .connecting, .reasserting:
|
case .connecting, .reasserting:
|
||||||
return .connecting
|
return .connecting
|
||||||
|
|
||||||
case .disconnecting:
|
case .disconnecting:
|
||||||
return .disconnecting
|
return .disconnecting
|
||||||
|
|
||||||
case .disconnected, .invalid:
|
case .disconnected, .invalid:
|
||||||
return .disconnected
|
return .disconnected
|
||||||
|
|
||||||
|
|
|
@ -28,14 +28,14 @@ import Foundation
|
||||||
/// Helps controlling a VPN without messing with underlying implementations.
|
/// Helps controlling a VPN without messing with underlying implementations.
|
||||||
public protocol VPN {
|
public protocol VPN {
|
||||||
associatedtype Configuration
|
associatedtype Configuration
|
||||||
|
|
||||||
associatedtype Extra
|
associatedtype Extra
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Synchronizes with the current VPN state.
|
Synchronizes with the current VPN state.
|
||||||
*/
|
*/
|
||||||
func prepare() async
|
func prepare() async
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Installs the VPN profile.
|
Installs the VPN profile.
|
||||||
|
|
||||||
|
@ -72,12 +72,12 @@ public protocol VPN {
|
||||||
extra: Extra?,
|
extra: Extra?,
|
||||||
after: DispatchTimeInterval
|
after: DispatchTimeInterval
|
||||||
) async throws
|
) async throws
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Disconnects from the VPN.
|
Disconnects from the VPN.
|
||||||
*/
|
*/
|
||||||
func disconnect() async
|
func disconnect() async
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Uninstalls the VPN profile.
|
Uninstalls the VPN profile.
|
||||||
*/
|
*/
|
||||||
|
@ -91,19 +91,19 @@ extension DispatchTimeInterval {
|
||||||
switch self {
|
switch self {
|
||||||
case .seconds(let sec):
|
case .seconds(let sec):
|
||||||
return UInt64(sec) * NSEC_PER_SEC
|
return UInt64(sec) * NSEC_PER_SEC
|
||||||
|
|
||||||
case .milliseconds(let msec):
|
case .milliseconds(let msec):
|
||||||
return UInt64(msec) * NSEC_PER_MSEC
|
return UInt64(msec) * NSEC_PER_MSEC
|
||||||
|
|
||||||
case .microseconds(let usec):
|
case .microseconds(let usec):
|
||||||
return UInt64(usec) * NSEC_PER_USEC
|
return UInt64(usec) * NSEC_PER_USEC
|
||||||
|
|
||||||
case .nanoseconds(let nsec):
|
case .nanoseconds(let nsec):
|
||||||
return UInt64(nsec)
|
return UInt64(nsec)
|
||||||
|
|
||||||
case .never:
|
case .never:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@unknown default:
|
@unknown default:
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,13 +30,13 @@ public enum VPNStatus: String {
|
||||||
|
|
||||||
/// VPN is connected.
|
/// VPN is connected.
|
||||||
case connected
|
case connected
|
||||||
|
|
||||||
/// VPN is attempting a connection.
|
/// VPN is attempting a connection.
|
||||||
case connecting
|
case connecting
|
||||||
|
|
||||||
/// VPN is disconnected.
|
/// VPN is disconnected.
|
||||||
case disconnected
|
case disconnected
|
||||||
|
|
||||||
/// VPN is completing a disconnection.
|
/// VPN is completing a disconnection.
|
||||||
case disconnecting
|
case disconnecting
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ class ConnectionStrategy {
|
||||||
private var remotes: [ResolvedRemote]
|
private var remotes: [ResolvedRemote]
|
||||||
|
|
||||||
private var currentRemoteIndex: Int
|
private var currentRemoteIndex: Int
|
||||||
|
|
||||||
var currentRemote: ResolvedRemote? {
|
var currentRemote: ResolvedRemote? {
|
||||||
guard currentRemoteIndex < remotes.count else {
|
guard currentRemoteIndex < remotes.count else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -89,13 +89,12 @@ class ConnectionStrategy {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSocket(
|
func createSocket(
|
||||||
from provider: NEProvider,
|
from provider: NEProvider,
|
||||||
timeout: Int,
|
timeout: Int,
|
||||||
queue: DispatchQueue,
|
queue: DispatchQueue,
|
||||||
completionHandler: @escaping (Result<GenericSocket, OpenVPNProviderError>) -> Void)
|
completionHandler: @escaping (Result<GenericSocket, OpenVPNProviderError>) -> Void) {
|
||||||
{
|
|
||||||
guard let remote = currentRemote else {
|
guard let remote = currentRemote else {
|
||||||
completionHandler(.failure(.exhaustedEndpoints))
|
completionHandler(.failure(.exhaustedEndpoints))
|
||||||
return
|
return
|
||||||
|
@ -130,7 +129,7 @@ private extension NEProvider {
|
||||||
case .udp, .udp4, .udp6:
|
case .udp, .udp4, .udp6:
|
||||||
let impl = createUDPSession(to: ep, from: nil)
|
let impl = createUDPSession(to: ep, from: nil)
|
||||||
return NEUDPSocket(impl: impl)
|
return NEUDPSocket(impl: impl)
|
||||||
|
|
||||||
case .tcp, .tcp4, .tcp6:
|
case .tcp, .tcp4, .tcp6:
|
||||||
let impl = createTCPConnection(to: ep, enableTLS: false, tlsParameters: nil, delegate: nil)
|
let impl = createTCPConnection(to: ep, enableTLS: false, tlsParameters: nil, delegate: nil)
|
||||||
return NETCPSocket(impl: impl)
|
return NETCPSocket(impl: impl)
|
||||||
|
|
|
@ -32,7 +32,7 @@ import CTunnelKitOpenVPNProtocol
|
||||||
|
|
||||||
class NETCPLink: LinkInterface {
|
class NETCPLink: LinkInterface {
|
||||||
private let impl: NWTCPConnection
|
private let impl: NWTCPConnection
|
||||||
|
|
||||||
private let maxPacketSize: Int
|
private let maxPacketSize: Int
|
||||||
|
|
||||||
private let xorMethod: OpenVPN.XORMethod?
|
private let xorMethod: OpenVPN.XORMethod?
|
||||||
|
@ -45,15 +45,15 @@ class NETCPLink: LinkInterface {
|
||||||
self.xorMethod = xorMethod
|
self.xorMethod = xorMethod
|
||||||
xorMask = xorMethod?.mask
|
xorMask = xorMethod?.mask
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: LinkInterface
|
// MARK: LinkInterface
|
||||||
|
|
||||||
let isReliable: Bool = true
|
let isReliable: Bool = true
|
||||||
|
|
||||||
var remoteAddress: String? {
|
var remoteAddress: String? {
|
||||||
(impl.remoteAddress as? NWHostEndpoint)?.hostname
|
(impl.remoteAddress as? NWHostEndpoint)?.hostname
|
||||||
}
|
}
|
||||||
|
|
||||||
var remoteProtocol: String? {
|
var remoteProtocol: String? {
|
||||||
guard let remote = impl.remoteAddress as? NWHostEndpoint else {
|
guard let remote = impl.remoteAddress as? NWHostEndpoint else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -64,13 +64,13 @@ class NETCPLink: LinkInterface {
|
||||||
var packetBufferSize: Int {
|
var packetBufferSize: Int {
|
||||||
return maxPacketSize
|
return maxPacketSize
|
||||||
}
|
}
|
||||||
|
|
||||||
func setReadHandler(queue: DispatchQueue, _ handler: @escaping ([Data]?, Error?) -> Void) {
|
func setReadHandler(queue: DispatchQueue, _ handler: @escaping ([Data]?, Error?) -> Void) {
|
||||||
loopReadPackets(queue, Data(), handler)
|
loopReadPackets(queue, Data(), handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loopReadPackets(_ queue: DispatchQueue, _ buffer: Data, _ handler: @escaping ([Data]?, Error?) -> Void) {
|
private func loopReadPackets(_ queue: DispatchQueue, _ buffer: Data, _ handler: @escaping ([Data]?, Error?) -> Void) {
|
||||||
|
|
||||||
// WARNING: runs in Network.framework queue
|
// WARNING: runs in Network.framework queue
|
||||||
impl.readMinimumLength(2, maximumLength: packetBufferSize) { [weak self] (data, error) in
|
impl.readMinimumLength(2, maximumLength: packetBufferSize) { [weak self] (data, error) in
|
||||||
guard let self = self else {
|
guard let self = self else {
|
||||||
|
@ -81,7 +81,7 @@ class NETCPLink: LinkInterface {
|
||||||
handler(nil, error)
|
handler(nil, error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var newBuffer = buffer
|
var newBuffer = buffer
|
||||||
newBuffer.append(contentsOf: data)
|
newBuffer.append(contentsOf: data)
|
||||||
var until = 0
|
var until = 0
|
||||||
|
@ -93,12 +93,12 @@ class NETCPLink: LinkInterface {
|
||||||
)
|
)
|
||||||
newBuffer = newBuffer.subdata(in: until..<newBuffer.count)
|
newBuffer = newBuffer.subdata(in: until..<newBuffer.count)
|
||||||
self.loopReadPackets(queue, newBuffer, handler)
|
self.loopReadPackets(queue, newBuffer, handler)
|
||||||
|
|
||||||
handler(packets, nil)
|
handler(packets, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func writePacket(_ packet: Data, completionHandler: ((Error?) -> Void)?) {
|
func writePacket(_ packet: Data, completionHandler: ((Error?) -> Void)?) {
|
||||||
let stream = PacketStream.outboundStream(
|
let stream = PacketStream.outboundStream(
|
||||||
fromPacket: packet,
|
fromPacket: packet,
|
||||||
|
@ -109,7 +109,7 @@ class NETCPLink: LinkInterface {
|
||||||
completionHandler?(error)
|
completionHandler?(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func writePackets(_ packets: [Data], completionHandler: ((Error?) -> Void)?) {
|
func writePackets(_ packets: [Data], completionHandler: ((Error?) -> Void)?) {
|
||||||
let stream = PacketStream.outboundStream(
|
let stream = PacketStream.outboundStream(
|
||||||
fromPackets: packets,
|
fromPackets: packets,
|
||||||
|
|
|
@ -32,9 +32,9 @@ import TunnelKitOpenVPNProtocol
|
||||||
|
|
||||||
class NEUDPLink: LinkInterface {
|
class NEUDPLink: LinkInterface {
|
||||||
private let impl: NWUDPSession
|
private let impl: NWUDPSession
|
||||||
|
|
||||||
private let maxDatagrams: Int
|
private let maxDatagrams: Int
|
||||||
|
|
||||||
private let xor: XORProcessor
|
private let xor: XORProcessor
|
||||||
|
|
||||||
init(impl: NWUDPSession, maxDatagrams: Int? = nil, xorMethod: OpenVPN.XORMethod?) {
|
init(impl: NWUDPSession, maxDatagrams: Int? = nil, xorMethod: OpenVPN.XORMethod?) {
|
||||||
|
@ -42,15 +42,15 @@ class NEUDPLink: LinkInterface {
|
||||||
self.maxDatagrams = maxDatagrams ?? 200
|
self.maxDatagrams = maxDatagrams ?? 200
|
||||||
xor = XORProcessor(method: xorMethod)
|
xor = XORProcessor(method: xorMethod)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: LinkInterface
|
// MARK: LinkInterface
|
||||||
|
|
||||||
let isReliable: Bool = false
|
let isReliable: Bool = false
|
||||||
|
|
||||||
var remoteAddress: String? {
|
var remoteAddress: String? {
|
||||||
(impl.resolvedEndpoint as? NWHostEndpoint)?.hostname
|
(impl.resolvedEndpoint as? NWHostEndpoint)?.hostname
|
||||||
}
|
}
|
||||||
|
|
||||||
var remoteProtocol: String? {
|
var remoteProtocol: String? {
|
||||||
guard let remote = impl.resolvedEndpoint as? NWHostEndpoint else {
|
guard let remote = impl.resolvedEndpoint as? NWHostEndpoint else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -61,9 +61,9 @@ class NEUDPLink: LinkInterface {
|
||||||
var packetBufferSize: Int {
|
var packetBufferSize: Int {
|
||||||
return maxDatagrams
|
return maxDatagrams
|
||||||
}
|
}
|
||||||
|
|
||||||
func setReadHandler(queue: DispatchQueue, _ handler: @escaping ([Data]?, Error?) -> Void) {
|
func setReadHandler(queue: DispatchQueue, _ handler: @escaping ([Data]?, Error?) -> Void) {
|
||||||
|
|
||||||
// WARNING: runs in Network.framework queue
|
// WARNING: runs in Network.framework queue
|
||||||
impl.setReadHandler({ [weak self] packets, error in
|
impl.setReadHandler({ [weak self] packets, error in
|
||||||
guard let self = self else {
|
guard let self = self else {
|
||||||
|
@ -78,14 +78,14 @@ class NEUDPLink: LinkInterface {
|
||||||
}
|
}
|
||||||
}, maxDatagrams: maxDatagrams)
|
}, maxDatagrams: maxDatagrams)
|
||||||
}
|
}
|
||||||
|
|
||||||
func writePacket(_ packet: Data, completionHandler: ((Error?) -> Void)?) {
|
func writePacket(_ packet: Data, completionHandler: ((Error?) -> Void)?) {
|
||||||
let dataToUse = xor.processPacket(packet, outbound: true)
|
let dataToUse = xor.processPacket(packet, outbound: true)
|
||||||
impl.writeDatagram(dataToUse) { error in
|
impl.writeDatagram(dataToUse) { error in
|
||||||
completionHandler?(error)
|
completionHandler?(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func writePackets(_ packets: [Data], completionHandler: ((Error?) -> Void)?) {
|
func writePackets(_ packets: [Data], completionHandler: ((Error?) -> Void)?) {
|
||||||
let packetsToUse = xor.processPackets(packets, outbound: true)
|
let packetsToUse = xor.processPackets(packets, outbound: true)
|
||||||
impl.writeMultipleDatagrams(packetsToUse) { error in
|
impl.writeMultipleDatagrams(packetsToUse) { error in
|
||||||
|
|
|
@ -33,17 +33,17 @@ private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
struct NetworkSettingsBuilder {
|
struct NetworkSettingsBuilder {
|
||||||
let remoteAddress: String
|
let remoteAddress: String
|
||||||
|
|
||||||
let localOptions: OpenVPN.Configuration
|
let localOptions: OpenVPN.Configuration
|
||||||
|
|
||||||
let remoteOptions: OpenVPN.Configuration
|
let remoteOptions: OpenVPN.Configuration
|
||||||
|
|
||||||
init(remoteAddress: String, localOptions: OpenVPN.Configuration, remoteOptions: OpenVPN.Configuration) {
|
init(remoteAddress: String, localOptions: OpenVPN.Configuration, remoteOptions: OpenVPN.Configuration) {
|
||||||
self.remoteAddress = remoteAddress
|
self.remoteAddress = remoteAddress
|
||||||
self.localOptions = localOptions
|
self.localOptions = localOptions
|
||||||
self.remoteOptions = remoteOptions
|
self.remoteOptions = remoteOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
func build() -> NEPacketTunnelNetworkSettings {
|
func build() -> NEPacketTunnelNetworkSettings {
|
||||||
let ipv4Settings = computedIPv4Settings
|
let ipv4Settings = computedIPv4Settings
|
||||||
let ipv6Settings = computedIPv6Settings
|
let ipv6Settings = computedIPv6Settings
|
||||||
|
@ -91,19 +91,19 @@ extension NetworkSettingsBuilder {
|
||||||
var isGateway: Bool {
|
var isGateway: Bool {
|
||||||
isIPv4Gateway || isIPv6Gateway
|
isIPv4Gateway || isIPv6Gateway
|
||||||
}
|
}
|
||||||
|
|
||||||
private var routingPolicies: [OpenVPN.RoutingPolicy]? {
|
private var routingPolicies: [OpenVPN.RoutingPolicy]? {
|
||||||
pullRoutes ? (remoteOptions.routingPolicies ?? localOptions.routingPolicies) : localOptions.routingPolicies
|
pullRoutes ? (remoteOptions.routingPolicies ?? localOptions.routingPolicies) : localOptions.routingPolicies
|
||||||
}
|
}
|
||||||
|
|
||||||
private var isIPv4Gateway: Bool {
|
private var isIPv4Gateway: Bool {
|
||||||
routingPolicies?.contains(.IPv4) ?? false
|
routingPolicies?.contains(.IPv4) ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
private var isIPv6Gateway: Bool {
|
private var isIPv6Gateway: Bool {
|
||||||
routingPolicies?.contains(.IPv6) ?? false
|
routingPolicies?.contains(.IPv6) ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
private var allRoutes4: [IPv4Settings.Route] {
|
private var allRoutes4: [IPv4Settings.Route] {
|
||||||
var routes = localOptions.routes4 ?? []
|
var routes = localOptions.routes4 ?? []
|
||||||
if pullRoutes, let remoteRoutes = remoteOptions.routes4 {
|
if pullRoutes, let remoteRoutes = remoteOptions.routes4 {
|
||||||
|
@ -111,7 +111,7 @@ extension NetworkSettingsBuilder {
|
||||||
}
|
}
|
||||||
return routes
|
return routes
|
||||||
}
|
}
|
||||||
|
|
||||||
private var allRoutes6: [IPv6Settings.Route] {
|
private var allRoutes6: [IPv6Settings.Route] {
|
||||||
var routes = localOptions.routes6 ?? []
|
var routes = localOptions.routes6 ?? []
|
||||||
if pullRoutes, let remoteRoutes = remoteOptions.routes6 {
|
if pullRoutes, let remoteRoutes = remoteOptions.routes6 {
|
||||||
|
@ -119,7 +119,7 @@ extension NetworkSettingsBuilder {
|
||||||
}
|
}
|
||||||
return routes
|
return routes
|
||||||
}
|
}
|
||||||
|
|
||||||
private var allDNSServers: [String] {
|
private var allDNSServers: [String] {
|
||||||
var servers = localOptions.dnsServers ?? []
|
var servers = localOptions.dnsServers ?? []
|
||||||
if pullDNS, let remoteServers = remoteOptions.dnsServers {
|
if pullDNS, let remoteServers = remoteOptions.dnsServers {
|
||||||
|
@ -127,7 +127,7 @@ extension NetworkSettingsBuilder {
|
||||||
}
|
}
|
||||||
return servers
|
return servers
|
||||||
}
|
}
|
||||||
|
|
||||||
private var dnsDomain: String? {
|
private var dnsDomain: String? {
|
||||||
var domain = localOptions.dnsDomain
|
var domain = localOptions.dnsDomain
|
||||||
if pullDNS, let remoteDomain = remoteOptions.dnsDomain {
|
if pullDNS, let remoteDomain = remoteOptions.dnsDomain {
|
||||||
|
@ -143,7 +143,7 @@ extension NetworkSettingsBuilder {
|
||||||
}
|
}
|
||||||
return searchDomains
|
return searchDomains
|
||||||
}
|
}
|
||||||
|
|
||||||
private var allProxyBypassDomains: [String] {
|
private var allProxyBypassDomains: [String] {
|
||||||
var bypass = localOptions.proxyBypassDomains ?? []
|
var bypass = localOptions.proxyBypassDomains ?? []
|
||||||
if pullProxy, let remoteBypass = remoteOptions.proxyBypassDomains {
|
if pullProxy, let remoteBypass = remoteOptions.proxyBypassDomains {
|
||||||
|
@ -164,7 +164,7 @@ extension NetworkSettingsBuilder {
|
||||||
}
|
}
|
||||||
let ipv4Settings = NEIPv4Settings(addresses: [ipv4.address], subnetMasks: [ipv4.addressMask])
|
let ipv4Settings = NEIPv4Settings(addresses: [ipv4.address], subnetMasks: [ipv4.addressMask])
|
||||||
var neRoutes: [NEIPv4Route] = []
|
var neRoutes: [NEIPv4Route] = []
|
||||||
|
|
||||||
// route all traffic to VPN?
|
// route all traffic to VPN?
|
||||||
if isIPv4Gateway {
|
if isIPv4Gateway {
|
||||||
let defaultRoute = NEIPv4Route.default()
|
let defaultRoute = NEIPv4Route.default()
|
||||||
|
@ -172,7 +172,7 @@ extension NetworkSettingsBuilder {
|
||||||
neRoutes.append(defaultRoute)
|
neRoutes.append(defaultRoute)
|
||||||
log.info("Routing.IPv4: Setting default gateway to \(ipv4.defaultGateway)")
|
log.info("Routing.IPv4: Setting default gateway to \(ipv4.defaultGateway)")
|
||||||
}
|
}
|
||||||
|
|
||||||
for r in allRoutes4 {
|
for r in allRoutes4 {
|
||||||
let ipv4Route = NEIPv4Route(destinationAddress: r.destination, subnetMask: r.mask)
|
let ipv4Route = NEIPv4Route(destinationAddress: r.destination, subnetMask: r.mask)
|
||||||
let gw = r.gateway ?? ipv4.defaultGateway
|
let gw = r.gateway ?? ipv4.defaultGateway
|
||||||
|
@ -185,14 +185,14 @@ extension NetworkSettingsBuilder {
|
||||||
ipv4Settings.excludedRoutes = []
|
ipv4Settings.excludedRoutes = []
|
||||||
return ipv4Settings
|
return ipv4Settings
|
||||||
}
|
}
|
||||||
|
|
||||||
private var computedIPv6Settings: NEIPv6Settings? {
|
private var computedIPv6Settings: NEIPv6Settings? {
|
||||||
guard let ipv6 = remoteOptions.ipv6 else {
|
guard let ipv6 = remoteOptions.ipv6 else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
let ipv6Settings = NEIPv6Settings(addresses: [ipv6.address], networkPrefixLengths: [ipv6.addressPrefixLength as NSNumber])
|
let ipv6Settings = NEIPv6Settings(addresses: [ipv6.address], networkPrefixLengths: [ipv6.addressPrefixLength as NSNumber])
|
||||||
var neRoutes: [NEIPv6Route] = []
|
var neRoutes: [NEIPv6Route] = []
|
||||||
|
|
||||||
// route all traffic to VPN?
|
// route all traffic to VPN?
|
||||||
if isIPv6Gateway {
|
if isIPv6Gateway {
|
||||||
let defaultRoute = NEIPv6Route.default()
|
let defaultRoute = NEIPv6Route.default()
|
||||||
|
@ -200,7 +200,7 @@ extension NetworkSettingsBuilder {
|
||||||
neRoutes.append(defaultRoute)
|
neRoutes.append(defaultRoute)
|
||||||
log.info("Routing.IPv6: Setting default gateway to \(ipv6.defaultGateway)")
|
log.info("Routing.IPv6: Setting default gateway to \(ipv6.defaultGateway)")
|
||||||
}
|
}
|
||||||
|
|
||||||
for r in allRoutes6 {
|
for r in allRoutes6 {
|
||||||
let ipv6Route = NEIPv6Route(destinationAddress: r.destination, networkPrefixLength: r.prefixLength as NSNumber)
|
let ipv6Route = NEIPv6Route(destinationAddress: r.destination, networkPrefixLength: r.prefixLength as NSNumber)
|
||||||
let gw = r.gateway ?? ipv6.defaultGateway
|
let gw = r.gateway ?? ipv6.defaultGateway
|
||||||
|
@ -213,7 +213,7 @@ extension NetworkSettingsBuilder {
|
||||||
ipv6Settings.excludedRoutes = []
|
ipv6Settings.excludedRoutes = []
|
||||||
return ipv6Settings
|
return ipv6Settings
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasGateway: Bool {
|
var hasGateway: Bool {
|
||||||
var hasGateway = false
|
var hasGateway = false
|
||||||
if isIPv4Gateway && remoteOptions.ipv4 != nil {
|
if isIPv4Gateway && remoteOptions.ipv4 != nil {
|
||||||
|
@ -258,7 +258,7 @@ extension NetworkSettingsBuilder {
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// fall back
|
// fall back
|
||||||
if dnsSettings == nil {
|
if dnsSettings == nil {
|
||||||
let dnsServers = allDNSServers
|
let dnsServers = allDNSServers
|
||||||
|
@ -275,7 +275,7 @@ extension NetworkSettingsBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// "hack" for split DNS (i.e. use VPN only for DNS)
|
// "hack" for split DNS (i.e. use VPN only for DNS)
|
||||||
if !isGateway {
|
if !isGateway {
|
||||||
dnsSettings?.matchDomains = [""]
|
dnsSettings?.matchDomains = [""]
|
||||||
|
@ -294,7 +294,7 @@ extension NetworkSettingsBuilder {
|
||||||
dnsSettings?.matchDomains = dnsSettings?.searchDomains
|
dnsSettings?.matchDomains = dnsSettings?.searchDomains
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dnsSettings
|
return dnsSettings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -327,7 +327,7 @@ extension NetworkSettingsBuilder {
|
||||||
proxySettings?.autoProxyConfigurationEnabled = true
|
proxySettings?.autoProxyConfigurationEnabled = true
|
||||||
log.info("Routing: Setting PAC \(pacURL)")
|
log.info("Routing: Setting PAC \(pacURL)")
|
||||||
}
|
}
|
||||||
|
|
||||||
// only set if there is a proxy (proxySettings set to non-nil above)
|
// only set if there is a proxy (proxySettings set to non-nil above)
|
||||||
if proxySettings != nil {
|
if proxySettings != nil {
|
||||||
let bypass = allProxyBypassDomains
|
let bypass = allProxyBypassDomains
|
||||||
|
|
|
@ -57,39 +57,39 @@ private let log = SwiftyBeaver.self
|
||||||
Packet Tunnel Provider extension both on iOS and macOS.
|
Packet Tunnel Provider extension both on iOS and macOS.
|
||||||
*/
|
*/
|
||||||
open class OpenVPNTunnelProvider: NEPacketTunnelProvider {
|
open class OpenVPNTunnelProvider: NEPacketTunnelProvider {
|
||||||
|
|
||||||
// MARK: Tweaks
|
// MARK: Tweaks
|
||||||
|
|
||||||
/// An optional string describing host app version on tunnel start.
|
/// An optional string describing host app version on tunnel start.
|
||||||
public var appVersion: String?
|
public var appVersion: String?
|
||||||
|
|
||||||
/// The log separator between sessions.
|
/// The log separator between sessions.
|
||||||
public var logSeparator = "--- EOF ---"
|
public var logSeparator = "--- EOF ---"
|
||||||
|
|
||||||
/// The maximum size of the log.
|
/// The maximum size of the log.
|
||||||
public var maxLogSize = 20000
|
public var maxLogSize = 20000
|
||||||
|
|
||||||
/// The log level when `OpenVPNTunnelProvider.Configuration.shouldDebug` is enabled.
|
/// The log level when `OpenVPNTunnelProvider.Configuration.shouldDebug` is enabled.
|
||||||
public var debugLogLevel: SwiftyBeaver.Level = .debug
|
public var debugLogLevel: SwiftyBeaver.Level = .debug
|
||||||
|
|
||||||
/// The number of milliseconds after which a DNS resolution fails.
|
/// The number of milliseconds after which a DNS resolution fails.
|
||||||
public var dnsTimeout = 3000
|
public var dnsTimeout = 3000
|
||||||
|
|
||||||
/// The number of milliseconds after which the tunnel gives up on a connection attempt.
|
/// The number of milliseconds after which the tunnel gives up on a connection attempt.
|
||||||
public var socketTimeout = 5000
|
public var socketTimeout = 5000
|
||||||
|
|
||||||
/// The number of milliseconds after which the tunnel is shut down forcibly.
|
/// The number of milliseconds after which the tunnel is shut down forcibly.
|
||||||
public var shutdownTimeout = 2000
|
public var shutdownTimeout = 2000
|
||||||
|
|
||||||
/// The number of milliseconds after which a reconnection attempt is issued.
|
/// The number of milliseconds after which a reconnection attempt is issued.
|
||||||
public var reconnectionDelay = 1000
|
public var reconnectionDelay = 1000
|
||||||
|
|
||||||
/// The number of link failures after which the tunnel is expected to die.
|
/// The number of link failures after which the tunnel is expected to die.
|
||||||
public var maxLinkFailures = 3
|
public var maxLinkFailures = 3
|
||||||
|
|
||||||
/// The number of milliseconds between data count updates. Set to 0 to disable updates (default).
|
/// The number of milliseconds between data count updates. Set to 0 to disable updates (default).
|
||||||
public var dataCountInterval = 0
|
public var dataCountInterval = 0
|
||||||
|
|
||||||
/// A list of public DNS servers to use as fallback when none are provided (defaults to CloudFlare).
|
/// A list of public DNS servers to use as fallback when none are provided (defaults to CloudFlare).
|
||||||
public var fallbackDNSServers = [
|
public var fallbackDNSServers = [
|
||||||
"1.1.1.1",
|
"1.1.1.1",
|
||||||
|
@ -97,13 +97,13 @@ open class OpenVPNTunnelProvider: NEPacketTunnelProvider {
|
||||||
"2606:4700:4700::1111",
|
"2606:4700:4700::1111",
|
||||||
"2606:4700:4700::1001"
|
"2606:4700:4700::1001"
|
||||||
]
|
]
|
||||||
|
|
||||||
// MARK: Constants
|
// MARK: Constants
|
||||||
|
|
||||||
private let tunnelQueue = DispatchQueue(label: OpenVPNTunnelProvider.description(), qos: .utility)
|
private let tunnelQueue = DispatchQueue(label: OpenVPNTunnelProvider.description(), qos: .utility)
|
||||||
|
|
||||||
private let prngSeedLength = 64
|
private let prngSeedLength = 64
|
||||||
|
|
||||||
private var cachesURL: URL {
|
private var cachesURL: URL {
|
||||||
let appGroup = cfg.appGroup
|
let appGroup = cfg.appGroup
|
||||||
guard let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup) else {
|
guard let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup) else {
|
||||||
|
@ -115,32 +115,32 @@ open class OpenVPNTunnelProvider: NEPacketTunnelProvider {
|
||||||
// MARK: Tunnel configuration
|
// MARK: Tunnel configuration
|
||||||
|
|
||||||
private var cfg: OpenVPN.ProviderConfiguration!
|
private var cfg: OpenVPN.ProviderConfiguration!
|
||||||
|
|
||||||
private var strategy: ConnectionStrategy!
|
private var strategy: ConnectionStrategy!
|
||||||
|
|
||||||
// MARK: Internal state
|
// MARK: Internal state
|
||||||
|
|
||||||
private var session: OpenVPNSession?
|
private var session: OpenVPNSession?
|
||||||
|
|
||||||
private var socket: GenericSocket?
|
private var socket: GenericSocket?
|
||||||
|
|
||||||
private var pendingStartHandler: ((Error?) -> Void)?
|
private var pendingStartHandler: ((Error?) -> Void)?
|
||||||
|
|
||||||
private var pendingStopHandler: (() -> Void)?
|
private var pendingStopHandler: (() -> Void)?
|
||||||
|
|
||||||
private var isCountingData = false
|
private var isCountingData = false
|
||||||
|
|
||||||
private var shouldReconnect = false
|
private var shouldReconnect = false
|
||||||
|
|
||||||
// MARK: NEPacketTunnelProvider (XPC queue)
|
// MARK: NEPacketTunnelProvider (XPC queue)
|
||||||
|
|
||||||
open override var reasserting: Bool {
|
open override var reasserting: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
log.debug("Reasserting flag \(reasserting ? "set" : "cleared")")
|
log.debug("Reasserting flag \(reasserting ? "set" : "cleared")")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func startTunnel(options: [String : NSObject]? = nil, completionHandler: @escaping (Error?) -> Void) {
|
open override func startTunnel(options: [String: NSObject]? = nil, completionHandler: @escaping (Error?) -> Void) {
|
||||||
|
|
||||||
// required configuration
|
// required configuration
|
||||||
do {
|
do {
|
||||||
|
@ -160,7 +160,7 @@ open class OpenVPNTunnelProvider: NEPacketTunnelProvider {
|
||||||
switch te {
|
switch te {
|
||||||
case .parameter(let name):
|
case .parameter(let name):
|
||||||
message = "Tunnel configuration incomplete: \(name)"
|
message = "Tunnel configuration incomplete: \(name)"
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -198,7 +198,7 @@ open class OpenVPNTunnelProvider: NEPacketTunnelProvider {
|
||||||
|
|
||||||
log.info("Starting tunnel...")
|
log.info("Starting tunnel...")
|
||||||
cfg._appexSetLastError(nil)
|
cfg._appexSetLastError(nil)
|
||||||
|
|
||||||
guard OpenVPN.prepareRandomNumberGenerator(seedLength: prngSeedLength) else {
|
guard OpenVPN.prepareRandomNumberGenerator(seedLength: prngSeedLength) else {
|
||||||
completionHandler(OpenVPNProviderConfigurationError.prngInitialization)
|
completionHandler(OpenVPNProviderConfigurationError.prngInitialization)
|
||||||
return
|
return
|
||||||
|
@ -231,7 +231,7 @@ open class OpenVPNTunnelProvider: NEPacketTunnelProvider {
|
||||||
self.connectTunnel()
|
self.connectTunnel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
open override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
||||||
pendingStartHandler = nil
|
pendingStartHandler = nil
|
||||||
log.info("Stopping tunnel...")
|
log.info("Stopping tunnel...")
|
||||||
|
@ -261,35 +261,35 @@ open class OpenVPNTunnelProvider: NEPacketTunnelProvider {
|
||||||
session.shutdown(error: nil)
|
session.shutdown(error: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Wake/Sleep (debugging placeholders)
|
// MARK: Wake/Sleep (debugging placeholders)
|
||||||
|
|
||||||
open override func wake() {
|
open override func wake() {
|
||||||
log.verbose("Wake signal received")
|
log.verbose("Wake signal received")
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func sleep(completionHandler: @escaping () -> Void) {
|
open override func sleep(completionHandler: @escaping () -> Void) {
|
||||||
log.verbose("Sleep signal received")
|
log.verbose("Sleep signal received")
|
||||||
completionHandler()
|
completionHandler()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Connection (tunnel queue)
|
// MARK: Connection (tunnel queue)
|
||||||
|
|
||||||
private func connectTunnel(upgradedSocket: GenericSocket? = nil) {
|
private func connectTunnel(upgradedSocket: GenericSocket? = nil) {
|
||||||
log.info("Creating link session")
|
log.info("Creating link session")
|
||||||
|
|
||||||
// reuse upgraded socket
|
// reuse upgraded socket
|
||||||
if let upgradedSocket = upgradedSocket, !upgradedSocket.isShutdown {
|
if let upgradedSocket = upgradedSocket, !upgradedSocket.isShutdown {
|
||||||
log.debug("Socket follows a path upgrade")
|
log.debug("Socket follows a path upgrade")
|
||||||
connectTunnel(via: upgradedSocket)
|
connectTunnel(via: upgradedSocket)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
strategy.createSocket(from: self, timeout: dnsTimeout, queue: tunnelQueue) {
|
strategy.createSocket(from: self, timeout: dnsTimeout, queue: tunnelQueue) {
|
||||||
switch $0 {
|
switch $0 {
|
||||||
case .success(let socket):
|
case .success(let socket):
|
||||||
self.connectTunnel(via: socket)
|
self.connectTunnel(via: socket)
|
||||||
|
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
if case .dnsFailure = error {
|
if case .dnsFailure = error {
|
||||||
self.tunnelQueue.async {
|
self.tunnelQueue.async {
|
||||||
|
@ -302,7 +302,7 @@ open class OpenVPNTunnelProvider: NEPacketTunnelProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func connectTunnel(via socket: GenericSocket) {
|
private func connectTunnel(via socket: GenericSocket) {
|
||||||
log.info("Will connect to \(socket)")
|
log.info("Will connect to \(socket)")
|
||||||
cfg._appexSetLastError(nil)
|
cfg._appexSetLastError(nil)
|
||||||
|
@ -312,16 +312,16 @@ open class OpenVPNTunnelProvider: NEPacketTunnelProvider {
|
||||||
self.socket?.delegate = self
|
self.socket?.delegate = self
|
||||||
self.socket?.observe(queue: tunnelQueue, activeTimeout: socketTimeout)
|
self.socket?.observe(queue: tunnelQueue, activeTimeout: socketTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func finishTunnelDisconnection(error: Error?) {
|
private func finishTunnelDisconnection(error: Error?) {
|
||||||
if let session = session, !(shouldReconnect && session.canRebindLink()) {
|
if let session = session, !(shouldReconnect && session.canRebindLink()) {
|
||||||
session.cleanup()
|
session.cleanup()
|
||||||
}
|
}
|
||||||
|
|
||||||
socket?.delegate = nil
|
socket?.delegate = nil
|
||||||
socket?.unobserve()
|
socket?.unobserve()
|
||||||
socket = nil
|
socket = nil
|
||||||
|
|
||||||
if let error = error {
|
if let error = error {
|
||||||
log.error("Tunnel did stop (error: \(error))")
|
log.error("Tunnel did stop (error: \(error))")
|
||||||
setErrorStatus(with: error)
|
setErrorStatus(with: error)
|
||||||
|
@ -329,7 +329,7 @@ open class OpenVPNTunnelProvider: NEPacketTunnelProvider {
|
||||||
log.info("Tunnel did stop on request")
|
log.info("Tunnel did stop on request")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func disposeTunnel(error: Error?) {
|
private func disposeTunnel(error: Error?) {
|
||||||
log.info("Dispose tunnel in \(reconnectionDelay) milliseconds...")
|
log.info("Dispose tunnel in \(reconnectionDelay) milliseconds...")
|
||||||
tunnelQueue.asyncAfter(deadline: .now() + .milliseconds(reconnectionDelay)) { [weak self] in
|
tunnelQueue.asyncAfter(deadline: .now() + .milliseconds(reconnectionDelay)) { [weak self] in
|
||||||
|
@ -342,7 +342,7 @@ open class OpenVPNTunnelProvider: NEPacketTunnelProvider {
|
||||||
|
|
||||||
// failed to start
|
// failed to start
|
||||||
if pendingStartHandler != nil {
|
if pendingStartHandler != nil {
|
||||||
|
|
||||||
//
|
//
|
||||||
// CAUTION
|
// CAUTION
|
||||||
//
|
//
|
||||||
|
@ -374,7 +374,7 @@ open class OpenVPNTunnelProvider: NEPacketTunnelProvider {
|
||||||
forceExitOnMac()
|
forceExitOnMac()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Data counter (tunnel queue)
|
// MARK: Data counter (tunnel queue)
|
||||||
|
|
||||||
private func refreshDataCount() {
|
private func refreshDataCount() {
|
||||||
|
@ -393,9 +393,9 @@ open class OpenVPNTunnelProvider: NEPacketTunnelProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension OpenVPNTunnelProvider: GenericSocketDelegate {
|
extension OpenVPNTunnelProvider: GenericSocketDelegate {
|
||||||
|
|
||||||
// MARK: GenericSocketDelegate (tunnel queue)
|
// MARK: GenericSocketDelegate (tunnel queue)
|
||||||
|
|
||||||
public func socketDidTimeout(_ socket: GenericSocket) {
|
public func socketDidTimeout(_ socket: GenericSocket) {
|
||||||
log.debug("Socket timed out waiting for activity, cancelling...")
|
log.debug("Socket timed out waiting for activity, cancelling...")
|
||||||
shouldReconnect = true
|
shouldReconnect = true
|
||||||
|
@ -409,7 +409,7 @@ extension OpenVPNTunnelProvider: GenericSocketDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func socketDidBecomeActive(_ socket: GenericSocket) {
|
public func socketDidBecomeActive(_ socket: GenericSocket) {
|
||||||
guard let session = session, let producer = socket as? LinkProducer else {
|
guard let session = session, let producer = socket as? LinkProducer else {
|
||||||
return
|
return
|
||||||
|
@ -421,12 +421,12 @@ extension OpenVPNTunnelProvider: GenericSocketDelegate {
|
||||||
session.setLink(producer.link(userObject: cfg.configuration.xorMethod))
|
session.setLink(producer.link(userObject: cfg.configuration.xorMethod))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func socket(_ socket: GenericSocket, didShutdownWithFailure failure: Bool) {
|
public func socket(_ socket: GenericSocket, didShutdownWithFailure failure: Bool) {
|
||||||
guard let session = session else {
|
guard let session = session else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var shutdownError: Error?
|
var shutdownError: Error?
|
||||||
let didTimeoutNegotiation: Bool
|
let didTimeoutNegotiation: Bool
|
||||||
var upgradedSocket: GenericSocket?
|
var upgradedSocket: GenericSocket?
|
||||||
|
@ -437,7 +437,7 @@ extension OpenVPNTunnelProvider: GenericSocketDelegate {
|
||||||
shutdownError = OpenVPNProviderError.linkError
|
shutdownError = OpenVPNProviderError.linkError
|
||||||
}
|
}
|
||||||
didTimeoutNegotiation = (shutdownError as? OpenVPNError == .negotiationTimeout)
|
didTimeoutNegotiation = (shutdownError as? OpenVPNError == .negotiationTimeout)
|
||||||
|
|
||||||
// only try upgrade on network errors
|
// only try upgrade on network errors
|
||||||
if shutdownError as? OpenVPNError == nil {
|
if shutdownError as? OpenVPNError == nil {
|
||||||
upgradedSocket = socket.upgraded()
|
upgradedSocket = socket.upgraded()
|
||||||
|
@ -475,7 +475,7 @@ extension OpenVPNTunnelProvider: GenericSocketDelegate {
|
||||||
// shut down
|
// shut down
|
||||||
disposeTunnel(error: shutdownError)
|
disposeTunnel(error: shutdownError)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func socketHasBetterPath(_ socket: GenericSocket) {
|
public func socketHasBetterPath(_ socket: GenericSocket) {
|
||||||
log.debug("Stopping tunnel due to a new better path")
|
log.debug("Stopping tunnel due to a new better path")
|
||||||
logCurrentSSID()
|
logCurrentSSID()
|
||||||
|
@ -484,9 +484,9 @@ extension OpenVPNTunnelProvider: GenericSocketDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension OpenVPNTunnelProvider: OpenVPNSessionDelegate {
|
extension OpenVPNTunnelProvider: OpenVPNSessionDelegate {
|
||||||
|
|
||||||
// MARK: OpenVPNSessionDelegate (tunnel queue)
|
// MARK: OpenVPNSessionDelegate (tunnel queue)
|
||||||
|
|
||||||
public func sessionDidStart(_ session: OpenVPNSession, remoteAddress: String, remoteProtocol: String?, options: OpenVPN.Configuration) {
|
public func sessionDidStart(_ session: OpenVPNSession, remoteAddress: String, remoteProtocol: String?, options: OpenVPN.Configuration) {
|
||||||
log.info("Session did start")
|
log.info("Session did start")
|
||||||
log.info("\tAddress: \(remoteAddress.maskedDescription)")
|
log.info("\tAddress: \(remoteAddress.maskedDescription)")
|
||||||
|
@ -504,18 +504,18 @@ extension OpenVPNTunnelProvider: OpenVPNSessionDelegate {
|
||||||
bringNetworkUp(remoteAddress: remoteAddress, localOptions: session.configuration, remoteOptions: options) { (error) in
|
bringNetworkUp(remoteAddress: remoteAddress, localOptions: session.configuration, remoteOptions: options) { (error) in
|
||||||
|
|
||||||
// FIXME: XPC queue
|
// FIXME: XPC queue
|
||||||
|
|
||||||
self.reasserting = false
|
self.reasserting = false
|
||||||
|
|
||||||
if let error = error {
|
if let error = error {
|
||||||
log.error("Failed to configure tunnel: \(error)")
|
log.error("Failed to configure tunnel: \(error)")
|
||||||
self.pendingStartHandler?(error)
|
self.pendingStartHandler?(error)
|
||||||
self.pendingStartHandler = nil
|
self.pendingStartHandler = nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Tunnel interface is now UP")
|
log.info("Tunnel interface is now UP")
|
||||||
|
|
||||||
session.setTunnel(tunnel: NETunnelInterface(impl: self.packetFlow))
|
session.setTunnel(tunnel: NETunnelInterface(impl: self.packetFlow))
|
||||||
|
|
||||||
self.pendingStartHandler?(nil)
|
self.pendingStartHandler?(nil)
|
||||||
|
@ -525,7 +525,7 @@ extension OpenVPNTunnelProvider: OpenVPNSessionDelegate {
|
||||||
isCountingData = true
|
isCountingData = true
|
||||||
refreshDataCount()
|
refreshDataCount()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func sessionDidStop(_: OpenVPNSession, withError error: Error?, shouldReconnect: Bool) {
|
public func sessionDidStop(_: OpenVPNSession, withError error: Error?, shouldReconnect: Bool) {
|
||||||
cfg._appexSetServerConfiguration(nil)
|
cfg._appexSetServerConfiguration(nil)
|
||||||
|
|
||||||
|
@ -541,7 +541,7 @@ extension OpenVPNTunnelProvider: OpenVPNSessionDelegate {
|
||||||
self.shouldReconnect = shouldReconnect
|
self.shouldReconnect = shouldReconnect
|
||||||
socket?.shutdown()
|
socket?.shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func bringNetworkUp(remoteAddress: String, localOptions: OpenVPN.Configuration, remoteOptions: OpenVPN.Configuration, completionHandler: @escaping (Error?) -> Void) {
|
private func bringNetworkUp(remoteAddress: String, localOptions: OpenVPN.Configuration, remoteOptions: OpenVPN.Configuration, completionHandler: @escaping (Error?) -> Void) {
|
||||||
let newSettings = NetworkSettingsBuilder(remoteAddress: remoteAddress, localOptions: localOptions, remoteOptions: remoteOptions)
|
let newSettings = NetworkSettingsBuilder(remoteAddress: remoteAddress, localOptions: localOptions, remoteOptions: remoteOptions)
|
||||||
|
|
||||||
|
@ -599,13 +599,13 @@ extension OpenVPNTunnelProvider {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Logging
|
// MARK: Logging
|
||||||
|
|
||||||
private func configureLogging() {
|
private func configureLogging() {
|
||||||
let logLevel: SwiftyBeaver.Level = (cfg.shouldDebug ? debugLogLevel : .info)
|
let logLevel: SwiftyBeaver.Level = (cfg.shouldDebug ? debugLogLevel : .info)
|
||||||
let logFormat = cfg.debugLogFormat ?? "$Dyyyy-MM-dd HH:mm:ss.SSS$d $L $N.$F:$l - $M"
|
let logFormat = cfg.debugLogFormat ?? "$Dyyyy-MM-dd HH:mm:ss.SSS$d $L $N.$F:$l - $M"
|
||||||
|
|
||||||
if cfg.shouldDebug {
|
if cfg.shouldDebug {
|
||||||
let console = ConsoleDestination()
|
let console = ConsoleDestination()
|
||||||
console.useNSLog = true
|
console.useNSLog = true
|
||||||
|
@ -623,7 +623,7 @@ extension OpenVPNTunnelProvider {
|
||||||
// store path for clients
|
// store path for clients
|
||||||
cfg._appexSetDebugLogPath()
|
cfg._appexSetDebugLogPath()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func flushLog() {
|
private func flushLog() {
|
||||||
log.debug("Flushing log...")
|
log.debug("Flushing log...")
|
||||||
|
|
||||||
|
@ -649,37 +649,37 @@ extension OpenVPNTunnelProvider {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// MARK: Errors
|
// MARK: Errors
|
||||||
|
|
||||||
private func setErrorStatus(with error: Error) {
|
private func setErrorStatus(with error: Error) {
|
||||||
cfg._appexSetLastError(unifiedError(from: error))
|
cfg._appexSetLastError(unifiedError(from: error))
|
||||||
}
|
}
|
||||||
|
|
||||||
private func unifiedError(from error: Error) -> OpenVPNProviderError {
|
private func unifiedError(from error: Error) -> OpenVPNProviderError {
|
||||||
if let te = error.openVPNErrorCode() {
|
if let te = error.openVPNErrorCode() {
|
||||||
switch te {
|
switch te {
|
||||||
case .cryptoRandomGenerator, .cryptoAlgorithm:
|
case .cryptoRandomGenerator, .cryptoAlgorithm:
|
||||||
return .encryptionInitialization
|
return .encryptionInitialization
|
||||||
|
|
||||||
case .cryptoEncryption, .cryptoHMAC:
|
case .cryptoEncryption, .cryptoHMAC:
|
||||||
return .encryptionData
|
return .encryptionData
|
||||||
|
|
||||||
case .tlscaRead, .tlscaUse, .tlscaPeerVerification,
|
case .tlscaRead, .tlscaUse, .tlscaPeerVerification,
|
||||||
.tlsClientCertificateRead, .tlsClientCertificateUse,
|
.tlsClientCertificateRead, .tlsClientCertificateUse,
|
||||||
.tlsClientKeyRead, .tlsClientKeyUse:
|
.tlsClientKeyRead, .tlsClientKeyUse:
|
||||||
return .tlsInitialization
|
return .tlsInitialization
|
||||||
|
|
||||||
case .tlsServerCertificate, .tlsServerEKU, .tlsServerHost:
|
case .tlsServerCertificate, .tlsServerEKU, .tlsServerHost:
|
||||||
return .tlsServerVerification
|
return .tlsServerVerification
|
||||||
|
|
||||||
case .tlsHandshake:
|
case .tlsHandshake:
|
||||||
return .tlsHandshake
|
return .tlsHandshake
|
||||||
|
|
||||||
case .dataPathOverflow, .dataPathPeerIdMismatch:
|
case .dataPathOverflow, .dataPathPeerIdMismatch:
|
||||||
return .unexpectedReply
|
return .unexpectedReply
|
||||||
|
|
||||||
case .dataPathCompression:
|
case .dataPathCompression:
|
||||||
return .serverCompression
|
return .serverCompression
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -687,19 +687,19 @@ extension OpenVPNTunnelProvider {
|
||||||
switch se {
|
switch se {
|
||||||
case .negotiationTimeout, .pingTimeout, .staleSession:
|
case .negotiationTimeout, .pingTimeout, .staleSession:
|
||||||
return .timeout
|
return .timeout
|
||||||
|
|
||||||
case .badCredentials:
|
case .badCredentials:
|
||||||
return .authentication
|
return .authentication
|
||||||
|
|
||||||
case .serverCompression:
|
case .serverCompression:
|
||||||
return .serverCompression
|
return .serverCompression
|
||||||
|
|
||||||
case .failedLinkWrite:
|
case .failedLinkWrite:
|
||||||
return .linkError
|
return .linkError
|
||||||
|
|
||||||
case .noRouting:
|
case .noRouting:
|
||||||
return .routing
|
return .routing
|
||||||
|
|
||||||
case .serverShutdown:
|
case .serverShutdown:
|
||||||
return .serverShutdown
|
return .serverShutdown
|
||||||
|
|
||||||
|
|
|
@ -31,27 +31,27 @@ private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
class ResolvedRemote: CustomStringConvertible {
|
class ResolvedRemote: CustomStringConvertible {
|
||||||
let originalEndpoint: Endpoint
|
let originalEndpoint: Endpoint
|
||||||
|
|
||||||
private(set) var isResolved: Bool
|
private(set) var isResolved: Bool
|
||||||
|
|
||||||
private(set) var resolvedEndpoints: [Endpoint]
|
private(set) var resolvedEndpoints: [Endpoint]
|
||||||
|
|
||||||
private var currentEndpointIndex: Int
|
private var currentEndpointIndex: Int
|
||||||
|
|
||||||
var currentEndpoint: Endpoint? {
|
var currentEndpoint: Endpoint? {
|
||||||
guard currentEndpointIndex < resolvedEndpoints.count else {
|
guard currentEndpointIndex < resolvedEndpoints.count else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return resolvedEndpoints[currentEndpointIndex]
|
return resolvedEndpoints[currentEndpointIndex]
|
||||||
}
|
}
|
||||||
|
|
||||||
init(_ originalEndpoint: Endpoint) {
|
init(_ originalEndpoint: Endpoint) {
|
||||||
self.originalEndpoint = originalEndpoint
|
self.originalEndpoint = originalEndpoint
|
||||||
isResolved = false
|
isResolved = false
|
||||||
resolvedEndpoints = []
|
resolvedEndpoints = []
|
||||||
currentEndpointIndex = 0
|
currentEndpointIndex = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func nextEndpoint() -> Bool {
|
func nextEndpoint() -> Bool {
|
||||||
currentEndpointIndex += 1
|
currentEndpointIndex += 1
|
||||||
return currentEndpointIndex < resolvedEndpoints.count
|
return currentEndpointIndex < resolvedEndpoints.count
|
||||||
|
@ -63,7 +63,7 @@ class ResolvedRemote: CustomStringConvertible {
|
||||||
completionHandler()
|
completionHandler()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handleResult(_ result: Result<[DNSRecord], DNSError>) {
|
private func handleResult(_ result: Result<[DNSRecord], DNSError>) {
|
||||||
switch result {
|
switch result {
|
||||||
case .success(let records):
|
case .success(let records):
|
||||||
|
@ -87,9 +87,9 @@ class ResolvedRemote: CustomStringConvertible {
|
||||||
log.debug("Unrolled endpoints: \(endpoints.maskedDescription)")
|
log.debug("Unrolled endpoints: \(endpoints.maskedDescription)")
|
||||||
return endpoints
|
return endpoints
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: CustomStringConvertible
|
// MARK: CustomStringConvertible
|
||||||
|
|
||||||
var description: String {
|
var description: String {
|
||||||
"{\(originalEndpoint.maskedDescription), resolved: \(resolvedEndpoints.maskedDescription)}"
|
"{\(originalEndpoint.maskedDescription), resolved: \(resolvedEndpoints.maskedDescription)}"
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,36 +27,36 @@ import Foundation
|
||||||
import CTunnelKitOpenVPNCore
|
import CTunnelKitOpenVPNCore
|
||||||
|
|
||||||
extension OpenVPN {
|
extension OpenVPN {
|
||||||
|
|
||||||
/// Defines the type of compression algorithm.
|
/// Defines the type of compression algorithm.
|
||||||
public enum CompressionAlgorithm: Int, Codable, CustomStringConvertible {
|
public enum CompressionAlgorithm: Int, Codable, CustomStringConvertible {
|
||||||
|
|
||||||
/// No compression.
|
/// No compression.
|
||||||
case disabled
|
case disabled
|
||||||
|
|
||||||
/// LZO compression.
|
/// LZO compression.
|
||||||
case LZO
|
case LZO
|
||||||
|
|
||||||
/// Any other compression algorithm (unsupported).
|
/// Any other compression algorithm (unsupported).
|
||||||
case other
|
case other
|
||||||
|
|
||||||
public var native: CompressionAlgorithmNative {
|
public var native: CompressionAlgorithmNative {
|
||||||
guard let val = CompressionAlgorithmNative(rawValue: rawValue) else {
|
guard let val = CompressionAlgorithmNative(rawValue: rawValue) else {
|
||||||
fatalError("Unhandled CompressionAlgorithm bridging")
|
fatalError("Unhandled CompressionAlgorithm bridging")
|
||||||
}
|
}
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: CustomStringConvertible
|
// MARK: CustomStringConvertible
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .disabled:
|
case .disabled:
|
||||||
return "disabled"
|
return "disabled"
|
||||||
|
|
||||||
case .LZO:
|
case .LZO:
|
||||||
return "lzo"
|
return "lzo"
|
||||||
|
|
||||||
case .other:
|
case .other:
|
||||||
return "other"
|
return "other"
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ extension OpenVPN {
|
||||||
|
|
||||||
/// No compression framing.
|
/// No compression framing.
|
||||||
case disabled
|
case disabled
|
||||||
|
|
||||||
/// Framing compatible with `comp-lzo` (deprecated in 2.4).
|
/// Framing compatible with `comp-lzo` (deprecated in 2.4).
|
||||||
case compLZO
|
case compLZO
|
||||||
|
|
||||||
|
@ -42,27 +42,27 @@ extension OpenVPN {
|
||||||
|
|
||||||
/// Framing compatible with 2.4 `compress` (version 2, e.g. stub-v2).
|
/// Framing compatible with 2.4 `compress` (version 2, e.g. stub-v2).
|
||||||
case compressV2
|
case compressV2
|
||||||
|
|
||||||
public var native: CompressionFramingNative {
|
public var native: CompressionFramingNative {
|
||||||
guard let val = CompressionFramingNative(rawValue: rawValue) else {
|
guard let val = CompressionFramingNative(rawValue: rawValue) else {
|
||||||
fatalError("Unhandled CompressionFraming bridging")
|
fatalError("Unhandled CompressionFraming bridging")
|
||||||
}
|
}
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: CustomStringConvertible
|
// MARK: CustomStringConvertible
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .disabled:
|
case .disabled:
|
||||||
return "disabled"
|
return "disabled"
|
||||||
|
|
||||||
case .compress:
|
case .compress:
|
||||||
return "compress"
|
return "compress"
|
||||||
|
|
||||||
case .compressV2:
|
case .compressV2:
|
||||||
return "compress"
|
return "compress"
|
||||||
|
|
||||||
case .compLZO:
|
case .compLZO:
|
||||||
return "comp-lzo"
|
return "comp-lzo"
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,16 +41,16 @@ import TunnelKitCore
|
||||||
private let log = SwiftyBeaver.self
|
private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
extension OpenVPN {
|
extension OpenVPN {
|
||||||
|
|
||||||
/// A pair of credentials for authentication.
|
/// A pair of credentials for authentication.
|
||||||
public struct Credentials: Codable, Equatable {
|
public struct Credentials: Codable, Equatable {
|
||||||
|
|
||||||
/// The username.
|
/// The username.
|
||||||
public let username: String
|
public let username: String
|
||||||
|
|
||||||
/// The password.
|
/// The password.
|
||||||
public let password: String
|
public let password: String
|
||||||
|
|
||||||
public init(_ username: String, _ password: String) {
|
public init(_ username: String, _ password: String) {
|
||||||
self.username = username
|
self.username = username
|
||||||
self.password = password
|
self.password = password
|
||||||
|
@ -59,64 +59,64 @@ extension OpenVPN {
|
||||||
|
|
||||||
/// Encryption algorithm.
|
/// Encryption algorithm.
|
||||||
public enum Cipher: String, Codable, CustomStringConvertible {
|
public enum Cipher: String, Codable, CustomStringConvertible {
|
||||||
|
|
||||||
// WARNING: must match OpenSSL algorithm names
|
// WARNING: must match OpenSSL algorithm names
|
||||||
|
|
||||||
/// AES encryption with 128-bit key size and CBC.
|
/// AES encryption with 128-bit key size and CBC.
|
||||||
case aes128cbc = "AES-128-CBC"
|
case aes128cbc = "AES-128-CBC"
|
||||||
|
|
||||||
/// AES encryption with 192-bit key size and CBC.
|
/// AES encryption with 192-bit key size and CBC.
|
||||||
case aes192cbc = "AES-192-CBC"
|
case aes192cbc = "AES-192-CBC"
|
||||||
|
|
||||||
/// AES encryption with 256-bit key size and CBC.
|
/// AES encryption with 256-bit key size and CBC.
|
||||||
case aes256cbc = "AES-256-CBC"
|
case aes256cbc = "AES-256-CBC"
|
||||||
|
|
||||||
/// AES encryption with 128-bit key size and GCM.
|
/// AES encryption with 128-bit key size and GCM.
|
||||||
case aes128gcm = "AES-128-GCM"
|
case aes128gcm = "AES-128-GCM"
|
||||||
|
|
||||||
/// AES encryption with 192-bit key size and GCM.
|
/// AES encryption with 192-bit key size and GCM.
|
||||||
case aes192gcm = "AES-192-GCM"
|
case aes192gcm = "AES-192-GCM"
|
||||||
|
|
||||||
/// AES encryption with 256-bit key size and GCM.
|
/// AES encryption with 256-bit key size and GCM.
|
||||||
case aes256gcm = "AES-256-GCM"
|
case aes256gcm = "AES-256-GCM"
|
||||||
|
|
||||||
/// Returns the key size for this cipher.
|
/// Returns the key size for this cipher.
|
||||||
public var keySize: Int {
|
public var keySize: Int {
|
||||||
switch self {
|
switch self {
|
||||||
case .aes128cbc, .aes128gcm:
|
case .aes128cbc, .aes128gcm:
|
||||||
return 128
|
return 128
|
||||||
|
|
||||||
case .aes192cbc, .aes192gcm:
|
case .aes192cbc, .aes192gcm:
|
||||||
return 192
|
return 192
|
||||||
|
|
||||||
case .aes256cbc, .aes256gcm:
|
case .aes256cbc, .aes256gcm:
|
||||||
return 256
|
return 256
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Digest should be ignored when this is `true`.
|
/// Digest should be ignored when this is `true`.
|
||||||
public var embedsDigest: Bool {
|
public var embedsDigest: Bool {
|
||||||
return rawValue.hasSuffix("-GCM")
|
return rawValue.hasSuffix("-GCM")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a generic name for this cipher.
|
/// Returns a generic name for this cipher.
|
||||||
public var genericName: String {
|
public var genericName: String {
|
||||||
return rawValue.hasSuffix("-GCM") ? "AES-GCM" : "AES-CBC"
|
return rawValue.hasSuffix("-GCM") ? "AES-GCM" : "AES-CBC"
|
||||||
}
|
}
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
return rawValue
|
return rawValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Message digest algorithm.
|
/// Message digest algorithm.
|
||||||
public enum Digest: String, Codable, CustomStringConvertible {
|
public enum Digest: String, Codable, CustomStringConvertible {
|
||||||
|
|
||||||
// WARNING: must match OpenSSL algorithm names
|
// WARNING: must match OpenSSL algorithm names
|
||||||
|
|
||||||
/// SHA1 message digest.
|
/// SHA1 message digest.
|
||||||
case sha1 = "SHA1"
|
case sha1 = "SHA1"
|
||||||
|
|
||||||
/// SHA224 message digest.
|
/// SHA224 message digest.
|
||||||
case sha224 = "SHA224"
|
case sha224 = "SHA224"
|
||||||
|
|
||||||
|
@ -128,17 +128,17 @@ extension OpenVPN {
|
||||||
|
|
||||||
/// SHA256 message digest.
|
/// SHA256 message digest.
|
||||||
case sha512 = "SHA512"
|
case sha512 = "SHA512"
|
||||||
|
|
||||||
/// Returns a generic name for this digest.
|
/// Returns a generic name for this digest.
|
||||||
public var genericName: String {
|
public var genericName: String {
|
||||||
return "HMAC"
|
return "HMAC"
|
||||||
}
|
}
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
return "\(genericName)-\(rawValue)"
|
return "\(genericName)-\(rawValue)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Routing policy.
|
/// Routing policy.
|
||||||
public enum RoutingPolicy: String, Codable {
|
public enum RoutingPolicy: String, Codable {
|
||||||
|
|
||||||
|
@ -147,20 +147,20 @@ extension OpenVPN {
|
||||||
|
|
||||||
/// All IPv6 traffic goes through the VPN.
|
/// All IPv6 traffic goes through the VPN.
|
||||||
case IPv6
|
case IPv6
|
||||||
|
|
||||||
/// Block LAN while connected.
|
/// Block LAN while connected.
|
||||||
case blockLocal
|
case blockLocal
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Settings that can be pulled from server.
|
/// Settings that can be pulled from server.
|
||||||
public enum PullMask: String, Codable, CaseIterable {
|
public enum PullMask: String, Codable, CaseIterable {
|
||||||
|
|
||||||
/// Routes and gateways.
|
/// Routes and gateways.
|
||||||
case routes
|
case routes
|
||||||
|
|
||||||
/// DNS settings.
|
/// DNS settings.
|
||||||
case dns
|
case dns
|
||||||
|
|
||||||
/// Proxy settings.
|
/// Proxy settings.
|
||||||
case proxy
|
case proxy
|
||||||
}
|
}
|
||||||
|
@ -169,91 +169,91 @@ extension OpenVPN {
|
||||||
public struct ConfigurationBuilder {
|
public struct ConfigurationBuilder {
|
||||||
|
|
||||||
// MARK: General
|
// MARK: General
|
||||||
|
|
||||||
/// The cipher algorithm for data encryption.
|
/// The cipher algorithm for data encryption.
|
||||||
public var cipher: Cipher?
|
public var cipher: Cipher?
|
||||||
|
|
||||||
/// The set of supported cipher algorithms for data encryption (2.5.).
|
/// The set of supported cipher algorithms for data encryption (2.5.).
|
||||||
public var dataCiphers: [Cipher]?
|
public var dataCiphers: [Cipher]?
|
||||||
|
|
||||||
/// The digest algorithm for HMAC.
|
/// The digest algorithm for HMAC.
|
||||||
public var digest: Digest?
|
public var digest: Digest?
|
||||||
|
|
||||||
/// Compression framing, disabled by default.
|
/// Compression framing, disabled by default.
|
||||||
public var compressionFraming: CompressionFraming?
|
public var compressionFraming: CompressionFraming?
|
||||||
|
|
||||||
/// Compression algorithm, disabled by default.
|
/// Compression algorithm, disabled by default.
|
||||||
public var compressionAlgorithm: CompressionAlgorithm?
|
public var compressionAlgorithm: CompressionAlgorithm?
|
||||||
|
|
||||||
/// The CA for TLS negotiation (PEM format).
|
/// The CA for TLS negotiation (PEM format).
|
||||||
public var ca: CryptoContainer?
|
public var ca: CryptoContainer?
|
||||||
|
|
||||||
/// The optional client certificate for TLS negotiation (PEM format).
|
/// The optional client certificate for TLS negotiation (PEM format).
|
||||||
public var clientCertificate: CryptoContainer?
|
public var clientCertificate: CryptoContainer?
|
||||||
|
|
||||||
/// The private key for the certificate in `clientCertificate` (PEM format).
|
/// The private key for the certificate in `clientCertificate` (PEM format).
|
||||||
public var clientKey: CryptoContainer?
|
public var clientKey: CryptoContainer?
|
||||||
|
|
||||||
/// The optional TLS wrapping.
|
/// The optional TLS wrapping.
|
||||||
public var tlsWrap: TLSWrap?
|
public var tlsWrap: TLSWrap?
|
||||||
|
|
||||||
/// If set, overrides TLS security level (0 = lowest).
|
/// If set, overrides TLS security level (0 = lowest).
|
||||||
public var tlsSecurityLevel: Int?
|
public var tlsSecurityLevel: Int?
|
||||||
|
|
||||||
/// Sends periodical keep-alive packets if set.
|
/// Sends periodical keep-alive packets if set.
|
||||||
public var keepAliveInterval: TimeInterval?
|
public var keepAliveInterval: TimeInterval?
|
||||||
|
|
||||||
/// Disconnects after no keep-alive packets are received within timeout interval if set.
|
/// Disconnects after no keep-alive packets are received within timeout interval if set.
|
||||||
public var keepAliveTimeout: TimeInterval?
|
public var keepAliveTimeout: TimeInterval?
|
||||||
|
|
||||||
/// The number of seconds after which a renegotiation should be initiated. If `nil`, the client will never initiate a renegotiation.
|
/// The number of seconds after which a renegotiation should be initiated. If `nil`, the client will never initiate a renegotiation.
|
||||||
public var renegotiatesAfter: TimeInterval?
|
public var renegotiatesAfter: TimeInterval?
|
||||||
|
|
||||||
// MARK: Client
|
// MARK: Client
|
||||||
|
|
||||||
/// The list of server endpoints.
|
/// The list of server endpoints.
|
||||||
public var remotes: [Endpoint]?
|
public var remotes: [Endpoint]?
|
||||||
|
|
||||||
/// If true, checks EKU of server certificate.
|
/// If true, checks EKU of server certificate.
|
||||||
public var checksEKU: Bool?
|
public var checksEKU: Bool?
|
||||||
|
|
||||||
/// If true, checks if hostname (sanHost) is present in certificates SAN.
|
/// If true, checks if hostname (sanHost) is present in certificates SAN.
|
||||||
public var checksSANHost: Bool?
|
public var checksSANHost: Bool?
|
||||||
|
|
||||||
/// The server hostname used for checking certificate SAN.
|
/// The server hostname used for checking certificate SAN.
|
||||||
public var sanHost: String?
|
public var sanHost: String?
|
||||||
|
|
||||||
/// Picks endpoint from `remotes` randomly.
|
/// Picks endpoint from `remotes` randomly.
|
||||||
public var randomizeEndpoint: Bool?
|
public var randomizeEndpoint: Bool?
|
||||||
|
|
||||||
/// Prepend hostnames with a number of random bytes defined in `Configuration.randomHostnamePrefixLength`.
|
/// Prepend hostnames with a number of random bytes defined in `Configuration.randomHostnamePrefixLength`.
|
||||||
public var randomizeHostnames: Bool?
|
public var randomizeHostnames: Bool?
|
||||||
|
|
||||||
/// Server is patched for the PIA VPN provider.
|
/// Server is patched for the PIA VPN provider.
|
||||||
public var usesPIAPatches: Bool?
|
public var usesPIAPatches: Bool?
|
||||||
|
|
||||||
/// The tunnel MTU.
|
/// The tunnel MTU.
|
||||||
public var mtu: Int?
|
public var mtu: Int?
|
||||||
|
|
||||||
/// Requires username authentication.
|
/// Requires username authentication.
|
||||||
public var authUserPass: Bool?
|
public var authUserPass: Bool?
|
||||||
|
|
||||||
// MARK: Server
|
// MARK: Server
|
||||||
|
|
||||||
/// The auth-token returned by the server.
|
/// The auth-token returned by the server.
|
||||||
public var authToken: String?
|
public var authToken: String?
|
||||||
|
|
||||||
/// The peer-id returned by the server.
|
/// The peer-id returned by the server.
|
||||||
public var peerId: UInt32?
|
public var peerId: UInt32?
|
||||||
|
|
||||||
// MARK: Routing
|
// MARK: Routing
|
||||||
|
|
||||||
/// The settings for IPv4. `OpenVPNSession` only evaluates this server-side.
|
/// The settings for IPv4. `OpenVPNSession` only evaluates this server-side.
|
||||||
public var ipv4: IPv4Settings?
|
public var ipv4: IPv4Settings?
|
||||||
|
|
||||||
/// The settings for IPv6. `OpenVPNSession` only evaluates this server-side.
|
/// The settings for IPv6. `OpenVPNSession` only evaluates this server-side.
|
||||||
public var ipv6: IPv6Settings?
|
public var ipv6: IPv6Settings?
|
||||||
|
|
||||||
/// The IPv4 routes if `ipv4` is nil.
|
/// The IPv4 routes if `ipv4` is nil.
|
||||||
public var routes4: [IPv4Settings.Route]?
|
public var routes4: [IPv4Settings.Route]?
|
||||||
|
|
||||||
|
@ -262,13 +262,13 @@ extension OpenVPN {
|
||||||
|
|
||||||
/// Set false to ignore DNS settings, even when pushed.
|
/// Set false to ignore DNS settings, even when pushed.
|
||||||
public var isDNSEnabled: Bool?
|
public var isDNSEnabled: Bool?
|
||||||
|
|
||||||
/// The DNS protocol, defaults to `.plain` (iOS 14+ / macOS 11+).
|
/// The DNS protocol, defaults to `.plain` (iOS 14+ / macOS 11+).
|
||||||
public var dnsProtocol: DNSProtocol?
|
public var dnsProtocol: DNSProtocol?
|
||||||
|
|
||||||
/// The DNS servers if `dnsProtocol = .plain` or nil.
|
/// The DNS servers if `dnsProtocol = .plain` or nil.
|
||||||
public var dnsServers: [String]?
|
public var dnsServers: [String]?
|
||||||
|
|
||||||
/// The server URL if `dnsProtocol = .https`.
|
/// The server URL if `dnsProtocol = .https`.
|
||||||
public var dnsHTTPSURL: URL?
|
public var dnsHTTPSURL: URL?
|
||||||
|
|
||||||
|
@ -295,30 +295,30 @@ extension OpenVPN {
|
||||||
|
|
||||||
/// The Proxy Auto-Configuration (PAC) url.
|
/// The Proxy Auto-Configuration (PAC) url.
|
||||||
public var proxyAutoConfigurationURL: URL?
|
public var proxyAutoConfigurationURL: URL?
|
||||||
|
|
||||||
/// Set false to ignore proxy settings, even when pushed.
|
/// Set false to ignore proxy settings, even when pushed.
|
||||||
public var isProxyEnabled: Bool?
|
public var isProxyEnabled: Bool?
|
||||||
|
|
||||||
/// The HTTP proxy.
|
/// The HTTP proxy.
|
||||||
public var httpProxy: Proxy?
|
public var httpProxy: Proxy?
|
||||||
|
|
||||||
/// The HTTPS proxy.
|
/// The HTTPS proxy.
|
||||||
public var httpsProxy: Proxy?
|
public var httpsProxy: Proxy?
|
||||||
|
|
||||||
/// The list of domains not passing through the proxy.
|
/// The list of domains not passing through the proxy.
|
||||||
public var proxyBypassDomains: [String]?
|
public var proxyBypassDomains: [String]?
|
||||||
|
|
||||||
/// Policies for redirecting traffic through the VPN gateway.
|
/// Policies for redirecting traffic through the VPN gateway.
|
||||||
public var routingPolicies: [RoutingPolicy]?
|
public var routingPolicies: [RoutingPolicy]?
|
||||||
|
|
||||||
/// Server settings that must not be pulled.
|
/// Server settings that must not be pulled.
|
||||||
public var noPullMask: [PullMask]?
|
public var noPullMask: [PullMask]?
|
||||||
|
|
||||||
// MARK: Extra
|
// MARK: Extra
|
||||||
|
|
||||||
/// The method to follow in regards to the XOR patch.
|
/// The method to follow in regards to the XOR patch.
|
||||||
public var xorMethod: XORMethod?
|
public var xorMethod: XORMethod?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Creates a `ConfigurationBuilder`.
|
Creates a `ConfigurationBuilder`.
|
||||||
|
|
||||||
|
@ -332,7 +332,7 @@ extension OpenVPN {
|
||||||
compressionAlgorithm = Configuration.Fallback.compressionAlgorithm
|
compressionAlgorithm = Configuration.Fallback.compressionAlgorithm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Builds a `Configuration` object.
|
Builds a `Configuration` object.
|
||||||
|
|
||||||
|
@ -386,45 +386,45 @@ extension OpenVPN {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The immutable configuration for `OpenVPNSession`.
|
/// The immutable configuration for `OpenVPNSession`.
|
||||||
public struct Configuration: Codable, Equatable {
|
public struct Configuration: Codable, Equatable {
|
||||||
struct Fallback {
|
struct Fallback {
|
||||||
static let cipher: Cipher = .aes128cbc
|
static let cipher: Cipher = .aes128cbc
|
||||||
|
|
||||||
static let digest: Digest = .sha1
|
static let digest: Digest = .sha1
|
||||||
|
|
||||||
static let compressionFraming: CompressionFraming = .disabled
|
static let compressionFraming: CompressionFraming = .disabled
|
||||||
|
|
||||||
static let compressionAlgorithm: CompressionAlgorithm = .disabled
|
static let compressionAlgorithm: CompressionAlgorithm = .disabled
|
||||||
}
|
}
|
||||||
|
|
||||||
private static let randomHostnamePrefixLength = 6
|
private static let randomHostnamePrefixLength = 6
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.cipher`
|
/// - Seealso: `ConfigurationBuilder.cipher`
|
||||||
public let cipher: Cipher?
|
public let cipher: Cipher?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.dataCiphers`
|
/// - Seealso: `ConfigurationBuilder.dataCiphers`
|
||||||
public let dataCiphers: [Cipher]?
|
public let dataCiphers: [Cipher]?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.digest`
|
/// - Seealso: `ConfigurationBuilder.digest`
|
||||||
public let digest: Digest?
|
public let digest: Digest?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.compressionFraming`
|
/// - Seealso: `ConfigurationBuilder.compressionFraming`
|
||||||
public let compressionFraming: CompressionFraming?
|
public let compressionFraming: CompressionFraming?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.compressionAlgorithm`
|
/// - Seealso: `ConfigurationBuilder.compressionAlgorithm`
|
||||||
public let compressionAlgorithm: CompressionAlgorithm?
|
public let compressionAlgorithm: CompressionAlgorithm?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.ca`
|
/// - Seealso: `ConfigurationBuilder.ca`
|
||||||
public let ca: CryptoContainer?
|
public let ca: CryptoContainer?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.clientCertificate`
|
/// - Seealso: `ConfigurationBuilder.clientCertificate`
|
||||||
public let clientCertificate: CryptoContainer?
|
public let clientCertificate: CryptoContainer?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.clientKey`
|
/// - Seealso: `ConfigurationBuilder.clientKey`
|
||||||
public let clientKey: CryptoContainer?
|
public let clientKey: CryptoContainer?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.tlsWrap`
|
/// - Seealso: `ConfigurationBuilder.tlsWrap`
|
||||||
public let tlsWrap: TLSWrap?
|
public let tlsWrap: TLSWrap?
|
||||||
|
|
||||||
|
@ -433,7 +433,7 @@ extension OpenVPN {
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.keepAliveInterval`
|
/// - Seealso: `ConfigurationBuilder.keepAliveInterval`
|
||||||
public let keepAliveInterval: TimeInterval?
|
public let keepAliveInterval: TimeInterval?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.keepAliveTimeout`
|
/// - Seealso: `ConfigurationBuilder.keepAliveTimeout`
|
||||||
public let keepAliveTimeout: TimeInterval?
|
public let keepAliveTimeout: TimeInterval?
|
||||||
|
|
||||||
|
@ -445,22 +445,22 @@ extension OpenVPN {
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.checksEKU`
|
/// - Seealso: `ConfigurationBuilder.checksEKU`
|
||||||
public let checksEKU: Bool?
|
public let checksEKU: Bool?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.checksSANHost`
|
/// - Seealso: `ConfigurationBuilder.checksSANHost`
|
||||||
public let checksSANHost: Bool?
|
public let checksSANHost: Bool?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.sanHost`
|
/// - Seealso: `ConfigurationBuilder.sanHost`
|
||||||
public let sanHost: String?
|
public let sanHost: String?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.randomizeEndpoint`
|
/// - Seealso: `ConfigurationBuilder.randomizeEndpoint`
|
||||||
public let randomizeEndpoint: Bool?
|
public let randomizeEndpoint: Bool?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.randomizeHostnames`
|
/// - Seealso: `ConfigurationBuilder.randomizeHostnames`
|
||||||
public var randomizeHostnames: Bool?
|
public var randomizeHostnames: Bool?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.usesPIAPatches`
|
/// - Seealso: `ConfigurationBuilder.usesPIAPatches`
|
||||||
public let usesPIAPatches: Bool?
|
public let usesPIAPatches: Bool?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.mtu`
|
/// - Seealso: `ConfigurationBuilder.mtu`
|
||||||
public let mtu: Int?
|
public let mtu: Int?
|
||||||
|
|
||||||
|
@ -469,10 +469,10 @@ extension OpenVPN {
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.authToken`
|
/// - Seealso: `ConfigurationBuilder.authToken`
|
||||||
public let authToken: String?
|
public let authToken: String?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.peerId`
|
/// - Seealso: `ConfigurationBuilder.peerId`
|
||||||
public let peerId: UInt32?
|
public let peerId: UInt32?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.ipv4`
|
/// - Seealso: `ConfigurationBuilder.ipv4`
|
||||||
public let ipv4: IPv4Settings?
|
public let ipv4: IPv4Settings?
|
||||||
|
|
||||||
|
@ -490,16 +490,16 @@ extension OpenVPN {
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.dnsProtocol`
|
/// - Seealso: `ConfigurationBuilder.dnsProtocol`
|
||||||
public let dnsProtocol: DNSProtocol?
|
public let dnsProtocol: DNSProtocol?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.dnsServers`
|
/// - Seealso: `ConfigurationBuilder.dnsServers`
|
||||||
public let dnsServers: [String]?
|
public let dnsServers: [String]?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.dnsHTTPSURL`
|
/// - Seealso: `ConfigurationBuilder.dnsHTTPSURL`
|
||||||
public let dnsHTTPSURL: URL?
|
public let dnsHTTPSURL: URL?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.dnsTLSServerName`
|
/// - Seealso: `ConfigurationBuilder.dnsTLSServerName`
|
||||||
public let dnsTLSServerName: String?
|
public let dnsTLSServerName: String?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.dnsDomain`
|
/// - Seealso: `ConfigurationBuilder.dnsDomain`
|
||||||
public let dnsDomain: String?
|
public let dnsDomain: String?
|
||||||
|
|
||||||
|
@ -514,24 +514,24 @@ extension OpenVPN {
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.httpsProxy`
|
/// - Seealso: `ConfigurationBuilder.httpsProxy`
|
||||||
public let httpsProxy: Proxy?
|
public let httpsProxy: Proxy?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.proxyAutoConfigurationURL`
|
/// - Seealso: `ConfigurationBuilder.proxyAutoConfigurationURL`
|
||||||
public let proxyAutoConfigurationURL: URL?
|
public let proxyAutoConfigurationURL: URL?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.proxyBypassDomains`
|
/// - Seealso: `ConfigurationBuilder.proxyBypassDomains`
|
||||||
public let proxyBypassDomains: [String]?
|
public let proxyBypassDomains: [String]?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.routingPolicies`
|
/// - Seealso: `ConfigurationBuilder.routingPolicies`
|
||||||
public let routingPolicies: [RoutingPolicy]?
|
public let routingPolicies: [RoutingPolicy]?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.noPullMask`
|
/// - Seealso: `ConfigurationBuilder.noPullMask`
|
||||||
public let noPullMask: [PullMask]?
|
public let noPullMask: [PullMask]?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.xorMethod`
|
/// - Seealso: `ConfigurationBuilder.xorMethod`
|
||||||
public let xorMethod: XORMethod?
|
public let xorMethod: XORMethod?
|
||||||
|
|
||||||
// MARK: Shortcuts
|
// MARK: Shortcuts
|
||||||
|
|
||||||
public var fallbackCipher: Cipher {
|
public var fallbackCipher: Cipher {
|
||||||
return cipher ?? Fallback.cipher
|
return cipher ?? Fallback.cipher
|
||||||
}
|
}
|
||||||
|
@ -556,9 +556,9 @@ extension OpenVPN {
|
||||||
let pulled = Array(Set(all).subtracting(notPulled))
|
let pulled = Array(Set(all).subtracting(notPulled))
|
||||||
return !pulled.isEmpty ? pulled : nil
|
return !pulled.isEmpty ? pulled : nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Computed
|
// MARK: Computed
|
||||||
|
|
||||||
public var processedRemotes: [Endpoint]? {
|
public var processedRemotes: [Endpoint]? {
|
||||||
guard var processedRemotes = remotes else {
|
guard var processedRemotes = remotes else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -584,7 +584,7 @@ extension OpenVPN {
|
||||||
// MARK: Modification
|
// MARK: Modification
|
||||||
|
|
||||||
extension OpenVPN.Configuration {
|
extension OpenVPN.Configuration {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns a `ConfigurationBuilder` to use this configuration as a starting point for a new one.
|
Returns a `ConfigurationBuilder` to use this configuration as a starting point for a new one.
|
||||||
|
|
||||||
|
@ -701,7 +701,7 @@ extension OpenVPN.Configuration {
|
||||||
log.info("\tTLS security level: default")
|
log.info("\tTLS security level: default")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let keepAliveSeconds = keepAliveInterval, keepAliveSeconds > 0 {
|
if let keepAliveSeconds = keepAliveInterval, keepAliveSeconds > 0 {
|
||||||
log.info("\tKeep-alive interval: \(keepAliveSeconds.asTimeString)")
|
log.info("\tKeep-alive interval: \(keepAliveSeconds.asTimeString)")
|
||||||
} else if isLocal {
|
} else if isLocal {
|
||||||
|
|
|
@ -29,19 +29,19 @@ extension OpenVPN {
|
||||||
|
|
||||||
/// Error raised by the configuration parser, with details about the line that triggered it.
|
/// Error raised by the configuration parser, with details about the line that triggered it.
|
||||||
public enum ConfigurationError: Error {
|
public enum ConfigurationError: Error {
|
||||||
|
|
||||||
/// Option syntax is incorrect.
|
/// Option syntax is incorrect.
|
||||||
case malformed(option: String)
|
case malformed(option: String)
|
||||||
|
|
||||||
/// A required option is missing.
|
/// A required option is missing.
|
||||||
case missingConfiguration(option: String)
|
case missingConfiguration(option: String)
|
||||||
|
|
||||||
/// An option is unsupported.
|
/// An option is unsupported.
|
||||||
case unsupportedConfiguration(option: String)
|
case unsupportedConfiguration(option: String)
|
||||||
|
|
||||||
/// Passphrase required to decrypt private keys.
|
/// Passphrase required to decrypt private keys.
|
||||||
case encryptionPassphrase
|
case encryptionPassphrase
|
||||||
|
|
||||||
/// Encryption passphrase is incorrect or key is corrupt.
|
/// Encryption passphrase is incorrect or key is corrupt.
|
||||||
case unableToDecrypt(error: Error)
|
case unableToDecrypt(error: Error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,136 +37,136 @@ extension OpenVPN {
|
||||||
public class ConfigurationParser {
|
public class ConfigurationParser {
|
||||||
|
|
||||||
// XXX: parsing is very optimistic
|
// XXX: parsing is very optimistic
|
||||||
|
|
||||||
/// Regexes used to parse OpenVPN options.
|
/// Regexes used to parse OpenVPN options.
|
||||||
public struct Regex {
|
public struct Regex {
|
||||||
|
|
||||||
// MARK: General
|
// MARK: General
|
||||||
|
|
||||||
static let cipher = NSRegularExpression("^cipher +[^,\\s]+")
|
static let cipher = NSRegularExpression("^cipher +[^,\\s]+")
|
||||||
|
|
||||||
static let dataCiphers = NSRegularExpression("^(data-ciphers|ncp-ciphers) +[^,\\s]+(:[^,\\s]+)*")
|
static let dataCiphers = NSRegularExpression("^(data-ciphers|ncp-ciphers) +[^,\\s]+(:[^,\\s]+)*")
|
||||||
|
|
||||||
static let dataCiphersFallback = NSRegularExpression("^data-ciphers-fallback +[^,\\s]+")
|
static let dataCiphersFallback = NSRegularExpression("^data-ciphers-fallback +[^,\\s]+")
|
||||||
|
|
||||||
static let auth = NSRegularExpression("^auth +[\\w\\-]+")
|
static let auth = NSRegularExpression("^auth +[\\w\\-]+")
|
||||||
|
|
||||||
static let compLZO = NSRegularExpression("^comp-lzo.*")
|
static let compLZO = NSRegularExpression("^comp-lzo.*")
|
||||||
|
|
||||||
static let compress = NSRegularExpression("^compress.*")
|
static let compress = NSRegularExpression("^compress.*")
|
||||||
|
|
||||||
static let keyDirection = NSRegularExpression("^key-direction +\\d")
|
static let keyDirection = NSRegularExpression("^key-direction +\\d")
|
||||||
|
|
||||||
static let ping = NSRegularExpression("^ping +\\d+")
|
static let ping = NSRegularExpression("^ping +\\d+")
|
||||||
|
|
||||||
static let pingRestart = NSRegularExpression("^ping-restart +\\d+")
|
static let pingRestart = NSRegularExpression("^ping-restart +\\d+")
|
||||||
|
|
||||||
static let keepAlive = NSRegularExpression("^keepalive +\\d+ ++\\d+")
|
static let keepAlive = NSRegularExpression("^keepalive +\\d+ ++\\d+")
|
||||||
|
|
||||||
static let renegSec = NSRegularExpression("^reneg-sec +\\d+")
|
static let renegSec = NSRegularExpression("^reneg-sec +\\d+")
|
||||||
|
|
||||||
static let blockBegin = NSRegularExpression("^<[\\w\\-]+>")
|
static let blockBegin = NSRegularExpression("^<[\\w\\-]+>")
|
||||||
|
|
||||||
static let blockEnd = NSRegularExpression("^<\\/[\\w\\-]+>")
|
static let blockEnd = NSRegularExpression("^<\\/[\\w\\-]+>")
|
||||||
|
|
||||||
// MARK: Client
|
// MARK: Client
|
||||||
|
|
||||||
static let proto = NSRegularExpression("^proto +(udp[46]?|tcp[46]?)")
|
static let proto = NSRegularExpression("^proto +(udp[46]?|tcp[46]?)")
|
||||||
|
|
||||||
static let port = NSRegularExpression("^port +\\d+")
|
static let port = NSRegularExpression("^port +\\d+")
|
||||||
|
|
||||||
static let remote = NSRegularExpression("^remote +[^ ]+( +\\d+)?( +(udp[46]?|tcp[46]?))?")
|
static let remote = NSRegularExpression("^remote +[^ ]+( +\\d+)?( +(udp[46]?|tcp[46]?))?")
|
||||||
|
|
||||||
static let authUserPass = NSRegularExpression("^auth-user-pass")
|
static let authUserPass = NSRegularExpression("^auth-user-pass")
|
||||||
|
|
||||||
static let eku = NSRegularExpression("^remote-cert-tls +server")
|
static let eku = NSRegularExpression("^remote-cert-tls +server")
|
||||||
|
|
||||||
static let remoteRandom = NSRegularExpression("^remote-random")
|
static let remoteRandom = NSRegularExpression("^remote-random")
|
||||||
|
|
||||||
static let remoteRandomHostname = NSRegularExpression("^remote-random-hostname")
|
static let remoteRandomHostname = NSRegularExpression("^remote-random-hostname")
|
||||||
|
|
||||||
static let mtu = NSRegularExpression("^tun-mtu +\\d+")
|
static let mtu = NSRegularExpression("^tun-mtu +\\d+")
|
||||||
|
|
||||||
// MARK: Server
|
// MARK: Server
|
||||||
|
|
||||||
public static let authToken = NSRegularExpression("^auth-token +[a-zA-Z0-9/=+]+")
|
public static let authToken = NSRegularExpression("^auth-token +[a-zA-Z0-9/=+]+")
|
||||||
|
|
||||||
static let peerId = NSRegularExpression("^peer-id +[0-9]+")
|
static let peerId = NSRegularExpression("^peer-id +[0-9]+")
|
||||||
|
|
||||||
// MARK: Routing
|
// MARK: Routing
|
||||||
|
|
||||||
static let topology = NSRegularExpression("^topology +(net30|p2p|subnet)")
|
static let topology = NSRegularExpression("^topology +(net30|p2p|subnet)")
|
||||||
|
|
||||||
static let ifconfig = NSRegularExpression("^ifconfig +[\\d\\.]+ [\\d\\.]+")
|
static let ifconfig = NSRegularExpression("^ifconfig +[\\d\\.]+ [\\d\\.]+")
|
||||||
|
|
||||||
static let ifconfig6 = NSRegularExpression("^ifconfig-ipv6 +[\\da-fA-F:]+/\\d+ [\\da-fA-F:]+")
|
static let ifconfig6 = NSRegularExpression("^ifconfig-ipv6 +[\\da-fA-F:]+/\\d+ [\\da-fA-F:]+")
|
||||||
|
|
||||||
static let route = NSRegularExpression("^route +[\\d\\.]+( +[\\d\\.]+){0,2}")
|
static let route = NSRegularExpression("^route +[\\d\\.]+( +[\\d\\.]+){0,2}")
|
||||||
|
|
||||||
static let route6 = NSRegularExpression("^route-ipv6 +[\\da-fA-F:]+/\\d+( +[\\da-fA-F:]+){0,2}")
|
static let route6 = NSRegularExpression("^route-ipv6 +[\\da-fA-F:]+/\\d+( +[\\da-fA-F:]+){0,2}")
|
||||||
|
|
||||||
static let gateway = NSRegularExpression("^route-gateway +[\\d\\.]+")
|
static let gateway = NSRegularExpression("^route-gateway +[\\d\\.]+")
|
||||||
|
|
||||||
static let dns = NSRegularExpression("^dhcp-option +DNS6? +[\\d\\.a-fA-F:]+")
|
static let dns = NSRegularExpression("^dhcp-option +DNS6? +[\\d\\.a-fA-F:]+")
|
||||||
|
|
||||||
static let domain = NSRegularExpression("^dhcp-option +DOMAIN +[^ ]+")
|
static let domain = NSRegularExpression("^dhcp-option +DOMAIN +[^ ]+")
|
||||||
|
|
||||||
static let domainSearch = NSRegularExpression("^dhcp-option +DOMAIN-SEARCH +[^ ]+")
|
static let domainSearch = NSRegularExpression("^dhcp-option +DOMAIN-SEARCH +[^ ]+")
|
||||||
|
|
||||||
static let proxy = NSRegularExpression("^dhcp-option +PROXY_(HTTPS? +[^ ]+ +\\d+|AUTO_CONFIG_URL +[^ ]+)")
|
static let proxy = NSRegularExpression("^dhcp-option +PROXY_(HTTPS? +[^ ]+ +\\d+|AUTO_CONFIG_URL +[^ ]+)")
|
||||||
|
|
||||||
static let proxyBypass = NSRegularExpression("^dhcp-option +PROXY_BYPASS +.+")
|
static let proxyBypass = NSRegularExpression("^dhcp-option +PROXY_BYPASS +.+")
|
||||||
|
|
||||||
static let redirectGateway = NSRegularExpression("^redirect-gateway.*")
|
static let redirectGateway = NSRegularExpression("^redirect-gateway.*")
|
||||||
|
|
||||||
static let routeNoPull = NSRegularExpression("^route-nopull")
|
static let routeNoPull = NSRegularExpression("^route-nopull")
|
||||||
|
|
||||||
// MARK: Extra
|
// MARK: Extra
|
||||||
|
|
||||||
static let xorInfo = NSRegularExpression("^scramble +(xormask|xorptrpos|reverse|obfuscate)[\\s]?([^\\s]+)?")
|
static let xorInfo = NSRegularExpression("^scramble +(xormask|xorptrpos|reverse|obfuscate)[\\s]?([^\\s]+)?")
|
||||||
|
|
||||||
// MARK: Unsupported
|
// MARK: Unsupported
|
||||||
|
|
||||||
// static let fragment = NSRegularExpression("^fragment +\\d+")
|
// static let fragment = NSRegularExpression("^fragment +\\d+")
|
||||||
static let fragment = NSRegularExpression("^fragment")
|
static let fragment = NSRegularExpression("^fragment")
|
||||||
|
|
||||||
static let connectionProxy = NSRegularExpression("^\\w+-proxy")
|
static let connectionProxy = NSRegularExpression("^\\w+-proxy")
|
||||||
|
|
||||||
static let externalFiles = NSRegularExpression("^(ca|cert|key|tls-auth|tls-crypt) ")
|
static let externalFiles = NSRegularExpression("^(ca|cert|key|tls-auth|tls-crypt) ")
|
||||||
|
|
||||||
static let connection = NSRegularExpression("^<connection>")
|
static let connection = NSRegularExpression("^<connection>")
|
||||||
|
|
||||||
// MARK: Continuation
|
// MARK: Continuation
|
||||||
|
|
||||||
static let continuation = NSRegularExpression("^push-continuation [12]")
|
static let continuation = NSRegularExpression("^push-continuation [12]")
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum Topology: String {
|
private enum Topology: String {
|
||||||
case net30
|
case net30
|
||||||
|
|
||||||
case p2p
|
case p2p
|
||||||
|
|
||||||
case subnet
|
case subnet
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum RedirectGateway: String {
|
private enum RedirectGateway: String {
|
||||||
case def1 // default
|
case def1 // default
|
||||||
|
|
||||||
case noIPv4 = "!ipv4"
|
case noIPv4 = "!ipv4"
|
||||||
|
|
||||||
case ipv6
|
case ipv6
|
||||||
|
|
||||||
case local
|
case local
|
||||||
|
|
||||||
case autolocal
|
case autolocal
|
||||||
|
|
||||||
case blockLocal = "block-local"
|
case blockLocal = "block-local"
|
||||||
|
|
||||||
case bypassDHCP = "bypass-dhcp"
|
case bypassDHCP = "bypass-dhcp"
|
||||||
|
|
||||||
case bypassDNS = "bypass-dns"
|
case bypassDNS = "bypass-dns"
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Result of the parser.
|
/// Result of the parser.
|
||||||
public struct Result {
|
public struct Result {
|
||||||
|
|
||||||
|
@ -181,11 +181,11 @@ extension OpenVPN {
|
||||||
///
|
///
|
||||||
/// - Seealso: `ConfigurationParser.parsed(...)`
|
/// - Seealso: `ConfigurationParser.parsed(...)`
|
||||||
public let strippedLines: [String]?
|
public let strippedLines: [String]?
|
||||||
|
|
||||||
/// Holds an optional `ConfigurationError` that didn't block the parser, but it would be worth taking care of.
|
/// Holds an optional `ConfigurationError` that didn't block the parser, but it would be worth taking care of.
|
||||||
public let warning: ConfigurationError?
|
public let warning: ConfigurationError?
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Parses a configuration from a .ovpn file.
|
Parses a configuration from a .ovpn file.
|
||||||
|
|
||||||
|
@ -254,7 +254,7 @@ extension OpenVPN {
|
||||||
var unsupportedError: ConfigurationError?
|
var unsupportedError: ConfigurationError?
|
||||||
var currentBlockName: String?
|
var currentBlockName: String?
|
||||||
var currentBlock: [String] = []
|
var currentBlock: [String] = []
|
||||||
|
|
||||||
var optDataCiphers: [Cipher]?
|
var optDataCiphers: [Cipher]?
|
||||||
var optDataCiphersFallback: Cipher?
|
var optDataCiphersFallback: Cipher?
|
||||||
var optCipher: Cipher?
|
var optCipher: Cipher?
|
||||||
|
@ -304,7 +304,7 @@ extension OpenVPN {
|
||||||
log.verbose("Configuration file:")
|
log.verbose("Configuration file:")
|
||||||
for line in lines {
|
for line in lines {
|
||||||
log.verbose(line)
|
log.verbose(line)
|
||||||
|
|
||||||
var isHandled = false
|
var isHandled = false
|
||||||
var strippedLine = line
|
var strippedLine = line
|
||||||
defer {
|
defer {
|
||||||
|
@ -312,9 +312,9 @@ extension OpenVPN {
|
||||||
optStrippedLines?.append(strippedLine)
|
optStrippedLines?.append(strippedLine)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Unsupported
|
// MARK: Unsupported
|
||||||
|
|
||||||
// check blocks first
|
// check blocks first
|
||||||
Regex.connection.enumerateSpacedComponents(in: line) { (_) in
|
Regex.connection.enumerateSpacedComponents(in: line) { (_) in
|
||||||
unsupportedError = ConfigurationError.unsupportedConfiguration(option: "<connection> blocks")
|
unsupportedError = ConfigurationError.unsupportedConfiguration(option: "<connection> blocks")
|
||||||
|
@ -331,7 +331,7 @@ extension OpenVPN {
|
||||||
if line.contains("mtu") || line.contains("mssfix") {
|
if line.contains("mtu") || line.contains("mssfix") {
|
||||||
isHandled = true
|
isHandled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Continuation
|
// MARK: Continuation
|
||||||
|
|
||||||
var isContinuation = false
|
var isContinuation = false
|
||||||
|
@ -343,7 +343,7 @@ extension OpenVPN {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Inline content
|
// MARK: Inline content
|
||||||
|
|
||||||
if unsupportedError == nil {
|
if unsupportedError == nil {
|
||||||
if currentBlockName == nil {
|
if currentBlockName == nil {
|
||||||
Regex.blockBegin.enumerateSpacedComponents(in: line) {
|
Regex.blockBegin.enumerateSpacedComponents(in: line) {
|
||||||
|
@ -351,7 +351,7 @@ extension OpenVPN {
|
||||||
let tag = $0.first!
|
let tag = $0.first!
|
||||||
let from = tag.index(after: tag.startIndex)
|
let from = tag.index(after: tag.startIndex)
|
||||||
let to = tag.index(before: tag.endIndex)
|
let to = tag.index(before: tag.endIndex)
|
||||||
|
|
||||||
currentBlockName = String(tag[from..<to])
|
currentBlockName = String(tag[from..<to])
|
||||||
currentBlock = []
|
currentBlock = []
|
||||||
}
|
}
|
||||||
|
@ -361,33 +361,33 @@ extension OpenVPN {
|
||||||
let tag = $0.first!
|
let tag = $0.first!
|
||||||
let from = tag.index(tag.startIndex, offsetBy: 2)
|
let from = tag.index(tag.startIndex, offsetBy: 2)
|
||||||
let to = tag.index(before: tag.endIndex)
|
let to = tag.index(before: tag.endIndex)
|
||||||
|
|
||||||
let blockName = String(tag[from..<to])
|
let blockName = String(tag[from..<to])
|
||||||
guard blockName == currentBlockName else {
|
guard blockName == currentBlockName else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// first is opening tag
|
// first is opening tag
|
||||||
currentBlock.removeFirst()
|
currentBlock.removeFirst()
|
||||||
switch blockName {
|
switch blockName {
|
||||||
case "ca":
|
case "ca":
|
||||||
optCA = CryptoContainer(pem: currentBlock.joined(separator: "\n"))
|
optCA = CryptoContainer(pem: currentBlock.joined(separator: "\n"))
|
||||||
|
|
||||||
case "cert":
|
case "cert":
|
||||||
optClientCertificate = CryptoContainer(pem: currentBlock.joined(separator: "\n"))
|
optClientCertificate = CryptoContainer(pem: currentBlock.joined(separator: "\n"))
|
||||||
|
|
||||||
case "key":
|
case "key":
|
||||||
ConfigurationParser.normalizeEncryptedPEMBlock(block: ¤tBlock)
|
ConfigurationParser.normalizeEncryptedPEMBlock(block: ¤tBlock)
|
||||||
optClientKey = CryptoContainer(pem: currentBlock.joined(separator: "\n"))
|
optClientKey = CryptoContainer(pem: currentBlock.joined(separator: "\n"))
|
||||||
|
|
||||||
case "tls-auth":
|
case "tls-auth":
|
||||||
optTLSKeyLines = currentBlock.map(Substring.init(_:))
|
optTLSKeyLines = currentBlock.map(Substring.init(_:))
|
||||||
optTLSStrategy = .auth
|
optTLSStrategy = .auth
|
||||||
|
|
||||||
case "tls-crypt":
|
case "tls-crypt":
|
||||||
optTLSKeyLines = currentBlock.map(Substring.init(_:))
|
optTLSKeyLines = currentBlock.map(Substring.init(_:))
|
||||||
optTLSStrategy = .crypt
|
optTLSStrategy = .crypt
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -399,9 +399,9 @@ extension OpenVPN {
|
||||||
currentBlock.append(line)
|
currentBlock.append(line)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: General
|
// MARK: General
|
||||||
|
|
||||||
Regex.cipher.enumerateSpacedArguments(in: line) {
|
Regex.cipher.enumerateSpacedArguments(in: line) {
|
||||||
isHandled = true
|
isHandled = true
|
||||||
guard let rawValue = $0.first else {
|
guard let rawValue = $0.first else {
|
||||||
|
@ -443,7 +443,7 @@ extension OpenVPN {
|
||||||
Regex.compLZO.enumerateSpacedArguments(in: line) {
|
Regex.compLZO.enumerateSpacedArguments(in: line) {
|
||||||
isHandled = true
|
isHandled = true
|
||||||
optCompressionFraming = .compLZO
|
optCompressionFraming = .compLZO
|
||||||
|
|
||||||
if !LZOFactory.isSupported() {
|
if !LZOFactory.isSupported() {
|
||||||
guard let arg = $0.first else {
|
guard let arg = $0.first else {
|
||||||
optWarning = optWarning ?? .unsupportedConfiguration(option: line)
|
optWarning = optWarning ?? .unsupportedConfiguration(option: line)
|
||||||
|
@ -461,7 +461,7 @@ extension OpenVPN {
|
||||||
Regex.compress.enumerateSpacedArguments(in: line) {
|
Regex.compress.enumerateSpacedArguments(in: line) {
|
||||||
isHandled = true
|
isHandled = true
|
||||||
optCompressionFraming = .compress
|
optCompressionFraming = .compress
|
||||||
|
|
||||||
if !LZOFactory.isSupported() {
|
if !LZOFactory.isSupported() {
|
||||||
guard $0.isEmpty else {
|
guard $0.isEmpty else {
|
||||||
unsupportedError = .unsupportedConfiguration(option: line)
|
unsupportedError = .unsupportedConfiguration(option: line)
|
||||||
|
@ -472,10 +472,10 @@ extension OpenVPN {
|
||||||
switch arg {
|
switch arg {
|
||||||
case "lzo":
|
case "lzo":
|
||||||
optCompressionAlgorithm = .LZO
|
optCompressionAlgorithm = .LZO
|
||||||
|
|
||||||
case "stub":
|
case "stub":
|
||||||
optCompressionAlgorithm = .disabled
|
optCompressionAlgorithm = .disabled
|
||||||
|
|
||||||
case "stub-v2":
|
case "stub-v2":
|
||||||
optCompressionFraming = .compressV2
|
optCompressionFraming = .compressV2
|
||||||
optCompressionAlgorithm = .disabled
|
optCompressionAlgorithm = .disabled
|
||||||
|
@ -524,9 +524,9 @@ extension OpenVPN {
|
||||||
}
|
}
|
||||||
optRenegotiateAfterSeconds = TimeInterval(arg)
|
optRenegotiateAfterSeconds = TimeInterval(arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Client
|
// MARK: Client
|
||||||
|
|
||||||
Regex.proto.enumerateSpacedArguments(in: line) {
|
Regex.proto.enumerateSpacedArguments(in: line) {
|
||||||
isHandled = true
|
isHandled = true
|
||||||
guard let str = $0.first else {
|
guard let str = $0.first else {
|
||||||
|
@ -561,7 +561,7 @@ extension OpenVPN {
|
||||||
strippedComponents.append($0[2])
|
strippedComponents.append($0[2])
|
||||||
}
|
}
|
||||||
optRemotes.append((hostname, port, proto))
|
optRemotes.append((hostname, port, proto))
|
||||||
|
|
||||||
// replace private data
|
// replace private data
|
||||||
strippedLine = strippedComponents.joined(separator: " ")
|
strippedLine = strippedComponents.joined(separator: " ")
|
||||||
}
|
}
|
||||||
|
@ -588,18 +588,18 @@ extension OpenVPN {
|
||||||
isHandled = true
|
isHandled = true
|
||||||
authUserPass = true
|
authUserPass = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Server
|
// MARK: Server
|
||||||
|
|
||||||
Regex.authToken.enumerateSpacedArguments(in: line) {
|
Regex.authToken.enumerateSpacedArguments(in: line) {
|
||||||
optAuthToken = $0[0]
|
optAuthToken = $0[0]
|
||||||
}
|
}
|
||||||
Regex.peerId.enumerateSpacedArguments(in: line) {
|
Regex.peerId.enumerateSpacedArguments(in: line) {
|
||||||
optPeerId = UInt32($0[0])
|
optPeerId = UInt32($0[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Routing
|
// MARK: Routing
|
||||||
|
|
||||||
Regex.topology.enumerateSpacedArguments(in: line) {
|
Regex.topology.enumerateSpacedArguments(in: line) {
|
||||||
optTopology = $0.first
|
optTopology = $0.first
|
||||||
}
|
}
|
||||||
|
@ -611,7 +611,7 @@ extension OpenVPN {
|
||||||
}
|
}
|
||||||
Regex.route.enumerateSpacedArguments(in: line) {
|
Regex.route.enumerateSpacedArguments(in: line) {
|
||||||
let routeEntryArguments = $0
|
let routeEntryArguments = $0
|
||||||
|
|
||||||
let address = routeEntryArguments[0]
|
let address = routeEntryArguments[0]
|
||||||
let mask = (routeEntryArguments.count > 1) ? routeEntryArguments[1] : "255.255.255.255"
|
let mask = (routeEntryArguments.count > 1) ? routeEntryArguments[1] : "255.255.255.255"
|
||||||
var gateway = (routeEntryArguments.count > 2) ? routeEntryArguments[2] : nil // defaultGateway4
|
var gateway = (routeEntryArguments.count > 2) ? routeEntryArguments[2] : nil // defaultGateway4
|
||||||
|
@ -625,7 +625,7 @@ extension OpenVPN {
|
||||||
}
|
}
|
||||||
Regex.route6.enumerateSpacedArguments(in: line) {
|
Regex.route6.enumerateSpacedArguments(in: line) {
|
||||||
let routeEntryArguments = $0
|
let routeEntryArguments = $0
|
||||||
|
|
||||||
let destinationComponents = routeEntryArguments[0].components(separatedBy: "/")
|
let destinationComponents = routeEntryArguments[0].components(separatedBy: "/")
|
||||||
guard destinationComponents.count == 2 else {
|
guard destinationComponents.count == 2 else {
|
||||||
return
|
return
|
||||||
|
@ -633,7 +633,7 @@ extension OpenVPN {
|
||||||
guard let prefix = UInt8(destinationComponents[1]) else {
|
guard let prefix = UInt8(destinationComponents[1]) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let destination = destinationComponents[0]
|
let destination = destinationComponents[0]
|
||||||
var gateway = (routeEntryArguments.count > 1) ? routeEntryArguments[1] : nil // defaultGateway6
|
var gateway = (routeEntryArguments.count > 1) ? routeEntryArguments[1] : nil // defaultGateway6
|
||||||
if gateway == "vpn_gateway" {
|
if gateway == "vpn_gateway" {
|
||||||
|
@ -687,7 +687,7 @@ extension OpenVPN {
|
||||||
switch $0[0] {
|
switch $0[0] {
|
||||||
case "PROXY_HTTPS":
|
case "PROXY_HTTPS":
|
||||||
optHTTPSProxy = Proxy($0[1], port)
|
optHTTPSProxy = Proxy($0[1], port)
|
||||||
|
|
||||||
case "PROXY_HTTP":
|
case "PROXY_HTTP":
|
||||||
optHTTPProxy = Proxy($0[1], port)
|
optHTTPProxy = Proxy($0[1], port)
|
||||||
|
|
||||||
|
@ -717,7 +717,7 @@ extension OpenVPN {
|
||||||
Regex.routeNoPull.enumerateSpacedComponents(in: line) { _ in
|
Regex.routeNoPull.enumerateSpacedComponents(in: line) { _ in
|
||||||
optRouteNoPull = true
|
optRouteNoPull = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Extra
|
// MARK: Extra
|
||||||
|
|
||||||
Regex.xorInfo.enumerateSpacedArguments(in: line) {
|
Regex.xorInfo.enumerateSpacedArguments(in: line) {
|
||||||
|
@ -747,14 +747,14 @@ extension OpenVPN {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
if let error = unsupportedError {
|
if let error = unsupportedError {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isClient {
|
if isClient {
|
||||||
guard let _ = optCA else {
|
guard let _ = optCA else {
|
||||||
throw ConfigurationError.missingConfiguration(option: "ca")
|
throw ConfigurationError.missingConfiguration(option: "ca")
|
||||||
|
@ -763,9 +763,9 @@ extension OpenVPN {
|
||||||
throw ConfigurationError.missingConfiguration(option: "cipher or data-ciphers")
|
throw ConfigurationError.missingConfiguration(option: "cipher or data-ciphers")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Post-processing
|
// MARK: Post-processing
|
||||||
|
|
||||||
// ensure that non-nil network settings also imply non-empty
|
// ensure that non-nil network settings also imply non-empty
|
||||||
if let array = optRoutes4 {
|
if let array = optRoutes4 {
|
||||||
assert(!array.isEmpty)
|
assert(!array.isEmpty)
|
||||||
|
@ -784,11 +784,11 @@ extension OpenVPN {
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
var sessionBuilder = ConfigurationBuilder()
|
var sessionBuilder = ConfigurationBuilder()
|
||||||
|
|
||||||
// MARK: General
|
// MARK: General
|
||||||
|
|
||||||
sessionBuilder.cipher = optDataCiphersFallback ?? optCipher
|
sessionBuilder.cipher = optDataCiphersFallback ?? optCipher
|
||||||
sessionBuilder.dataCiphers = optDataCiphers
|
sessionBuilder.dataCiphers = optDataCiphers
|
||||||
sessionBuilder.digest = optDigest
|
sessionBuilder.digest = optDigest
|
||||||
|
@ -797,7 +797,7 @@ extension OpenVPN {
|
||||||
sessionBuilder.ca = optCA
|
sessionBuilder.ca = optCA
|
||||||
sessionBuilder.clientCertificate = optClientCertificate
|
sessionBuilder.clientCertificate = optClientCertificate
|
||||||
sessionBuilder.authUserPass = authUserPass
|
sessionBuilder.authUserPass = authUserPass
|
||||||
|
|
||||||
if let clientKey = optClientKey, clientKey.isEncrypted {
|
if let clientKey = optClientKey, clientKey.isEncrypted {
|
||||||
// FIXME: remove dependency on TLSBox
|
// FIXME: remove dependency on TLSBox
|
||||||
guard let passphrase = passphrase, !passphrase.isEmpty else {
|
guard let passphrase = passphrase, !passphrase.isEmpty else {
|
||||||
|
@ -811,13 +811,13 @@ extension OpenVPN {
|
||||||
} else {
|
} else {
|
||||||
sessionBuilder.clientKey = optClientKey
|
sessionBuilder.clientKey = optClientKey
|
||||||
}
|
}
|
||||||
|
|
||||||
if let keyLines = optTLSKeyLines, let strategy = optTLSStrategy {
|
if let keyLines = optTLSKeyLines, let strategy = optTLSStrategy {
|
||||||
let optKey: StaticKey?
|
let optKey: StaticKey?
|
||||||
switch strategy {
|
switch strategy {
|
||||||
case .auth:
|
case .auth:
|
||||||
optKey = StaticKey(lines: keyLines, direction: optKeyDirection)
|
optKey = StaticKey(lines: keyLines, direction: optKeyDirection)
|
||||||
|
|
||||||
case .crypt:
|
case .crypt:
|
||||||
optKey = StaticKey(lines: keyLines, direction: .client)
|
optKey = StaticKey(lines: keyLines, direction: .client)
|
||||||
}
|
}
|
||||||
|
@ -825,13 +825,13 @@ extension OpenVPN {
|
||||||
sessionBuilder.tlsWrap = TLSWrap(strategy: strategy, key: key)
|
sessionBuilder.tlsWrap = TLSWrap(strategy: strategy, key: key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionBuilder.keepAliveInterval = optKeepAliveSeconds
|
sessionBuilder.keepAliveInterval = optKeepAliveSeconds
|
||||||
sessionBuilder.keepAliveTimeout = optKeepAliveTimeoutSeconds
|
sessionBuilder.keepAliveTimeout = optKeepAliveTimeoutSeconds
|
||||||
sessionBuilder.renegotiatesAfter = optRenegotiateAfterSeconds
|
sessionBuilder.renegotiatesAfter = optRenegotiateAfterSeconds
|
||||||
|
|
||||||
// MARK: Client
|
// MARK: Client
|
||||||
|
|
||||||
optDefaultProto = optDefaultProto ?? .udp
|
optDefaultProto = optDefaultProto ?? .udp
|
||||||
optDefaultPort = optDefaultPort ?? 1194
|
optDefaultPort = optDefaultPort ?? 1194
|
||||||
if !optRemotes.isEmpty {
|
if !optRemotes.isEmpty {
|
||||||
|
@ -850,20 +850,20 @@ extension OpenVPN {
|
||||||
Endpoint($0.0, .init($0.2, $0.1))
|
Endpoint($0.0, .init($0.2, $0.1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionBuilder.authUserPass = authUserPass
|
sessionBuilder.authUserPass = authUserPass
|
||||||
sessionBuilder.checksEKU = optChecksEKU
|
sessionBuilder.checksEKU = optChecksEKU
|
||||||
sessionBuilder.randomizeEndpoint = optRandomizeEndpoint
|
sessionBuilder.randomizeEndpoint = optRandomizeEndpoint
|
||||||
sessionBuilder.randomizeHostnames = optRandomizeHostnames
|
sessionBuilder.randomizeHostnames = optRandomizeHostnames
|
||||||
sessionBuilder.mtu = optMTU
|
sessionBuilder.mtu = optMTU
|
||||||
|
|
||||||
// MARK: Server
|
// MARK: Server
|
||||||
|
|
||||||
sessionBuilder.authToken = optAuthToken
|
sessionBuilder.authToken = optAuthToken
|
||||||
sessionBuilder.peerId = optPeerId
|
sessionBuilder.peerId = optPeerId
|
||||||
|
|
||||||
// MARK: Routing
|
// MARK: Routing
|
||||||
|
|
||||||
//
|
//
|
||||||
// excerpts from OpenVPN manpage
|
// excerpts from OpenVPN manpage
|
||||||
//
|
//
|
||||||
|
@ -881,15 +881,15 @@ extension OpenVPN {
|
||||||
guard ifconfig4Arguments.count == 2 else {
|
guard ifconfig4Arguments.count == 2 else {
|
||||||
throw ConfigurationError.malformed(option: "ifconfig takes 2 arguments")
|
throw ConfigurationError.malformed(option: "ifconfig takes 2 arguments")
|
||||||
}
|
}
|
||||||
|
|
||||||
let address4: String
|
let address4: String
|
||||||
let addressMask4: String
|
let addressMask4: String
|
||||||
let defaultGateway4: String
|
let defaultGateway4: String
|
||||||
|
|
||||||
let topology = Topology(rawValue: optTopology ?? "") ?? .net30
|
let topology = Topology(rawValue: optTopology ?? "") ?? .net30
|
||||||
switch topology {
|
switch topology {
|
||||||
case .subnet:
|
case .subnet:
|
||||||
|
|
||||||
// default gateway required when topology is subnet
|
// default gateway required when topology is subnet
|
||||||
guard let gateway4Arguments = optGateway4Arguments, gateway4Arguments.count == 1 else {
|
guard let gateway4Arguments = optGateway4Arguments, gateway4Arguments.count == 1 else {
|
||||||
throw ConfigurationError.malformed(option: "route-gateway takes 1 argument")
|
throw ConfigurationError.malformed(option: "route-gateway takes 1 argument")
|
||||||
|
@ -897,7 +897,7 @@ extension OpenVPN {
|
||||||
address4 = ifconfig4Arguments[0]
|
address4 = ifconfig4Arguments[0]
|
||||||
addressMask4 = ifconfig4Arguments[1]
|
addressMask4 = ifconfig4Arguments[1]
|
||||||
defaultGateway4 = gateway4Arguments[0]
|
defaultGateway4 = gateway4Arguments[0]
|
||||||
|
|
||||||
default:
|
default:
|
||||||
address4 = ifconfig4Arguments[0]
|
address4 = ifconfig4Arguments[0]
|
||||||
addressMask4 = "255.255.255.255"
|
addressMask4 = "255.255.255.255"
|
||||||
|
@ -913,7 +913,7 @@ extension OpenVPN {
|
||||||
sessionBuilder.routes4 = optRoutes4?.map {
|
sessionBuilder.routes4 = optRoutes4?.map {
|
||||||
IPv4Settings.Route($0.0, $0.1, $0.2)
|
IPv4Settings.Route($0.0, $0.1, $0.2)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let ifconfig6Arguments = optIfconfig6Arguments {
|
if let ifconfig6Arguments = optIfconfig6Arguments {
|
||||||
guard ifconfig6Arguments.count == 2 else {
|
guard ifconfig6Arguments.count == 2 else {
|
||||||
throw ConfigurationError.malformed(option: "ifconfig-ipv6 takes 2 arguments")
|
throw ConfigurationError.malformed(option: "ifconfig-ipv6 takes 2 arguments")
|
||||||
|
@ -925,10 +925,10 @@ extension OpenVPN {
|
||||||
guard let addressPrefix6 = UInt8(address6Components[1]) else {
|
guard let addressPrefix6 = UInt8(address6Components[1]) else {
|
||||||
throw ConfigurationError.malformed(option: "ifconfig-ipv6 address prefix must be a 8-bit number")
|
throw ConfigurationError.malformed(option: "ifconfig-ipv6 address prefix must be a 8-bit number")
|
||||||
}
|
}
|
||||||
|
|
||||||
let address6 = address6Components[0]
|
let address6 = address6Components[0]
|
||||||
let defaultGateway6 = ifconfig6Arguments[1]
|
let defaultGateway6 = ifconfig6Arguments[1]
|
||||||
|
|
||||||
sessionBuilder.ipv6 = IPv6Settings(
|
sessionBuilder.ipv6 = IPv6Settings(
|
||||||
address: address6,
|
address: address6,
|
||||||
addressPrefixLength: addressPrefix6,
|
addressPrefixLength: addressPrefix6,
|
||||||
|
@ -938,7 +938,7 @@ extension OpenVPN {
|
||||||
sessionBuilder.routes6 = optRoutes6?.map {
|
sessionBuilder.routes6 = optRoutes6?.map {
|
||||||
IPv6Settings.Route($0.0, $0.1, $0.2)
|
IPv6Settings.Route($0.0, $0.1, $0.2)
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionBuilder.dnsServers = optDNSServers
|
sessionBuilder.dnsServers = optDNSServers
|
||||||
sessionBuilder.dnsDomain = optDomain
|
sessionBuilder.dnsDomain = optDomain
|
||||||
sessionBuilder.searchDomains = optSearchDomains
|
sessionBuilder.searchDomains = optSearchDomains
|
||||||
|
@ -956,10 +956,10 @@ extension OpenVPN {
|
||||||
switch opt {
|
switch opt {
|
||||||
case .def1:
|
case .def1:
|
||||||
policies.insert(.IPv4)
|
policies.insert(.IPv4)
|
||||||
|
|
||||||
case .ipv6:
|
case .ipv6:
|
||||||
policies.insert(.IPv6)
|
policies.insert(.IPv6)
|
||||||
|
|
||||||
case .blockLocal:
|
case .blockLocal:
|
||||||
policies.insert(.blockLocal)
|
policies.insert(.blockLocal)
|
||||||
|
|
||||||
|
@ -973,13 +973,13 @@ extension OpenVPN {
|
||||||
}
|
}
|
||||||
sessionBuilder.routingPolicies = [RoutingPolicy](policies)
|
sessionBuilder.routingPolicies = [RoutingPolicy](policies)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Extra
|
// MARK: Extra
|
||||||
|
|
||||||
sessionBuilder.xorMethod = optXorMethod
|
sessionBuilder.xorMethod = optXorMethod
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
return Result(
|
return Result(
|
||||||
url: originalURL,
|
url: originalURL,
|
||||||
configuration: sessionBuilder.build(),
|
configuration: sessionBuilder.build(),
|
||||||
|
@ -992,7 +992,7 @@ extension OpenVPN {
|
||||||
// if block.count >= 1 && block[0].contains("ENCRYPTED") {
|
// if block.count >= 1 && block[0].contains("ENCRYPTED") {
|
||||||
// return true
|
// return true
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// XXX: restore blank line after encryption header (easier than tweaking trimmedLines)
|
// XXX: restore blank line after encryption header (easier than tweaking trimmedLines)
|
||||||
if block.count >= 3 && block[1].contains("Proc-Type") {
|
if block.count >= 3 && block[1].contains("Proc-Type") {
|
||||||
block.insert("", at: 3)
|
block.insert("", at: 3)
|
||||||
|
|
|
@ -46,14 +46,14 @@ extension OpenVPN {
|
||||||
private static let begin = "-----BEGIN "
|
private static let begin = "-----BEGIN "
|
||||||
|
|
||||||
private static let end = "-----END "
|
private static let end = "-----END "
|
||||||
|
|
||||||
/// The content in PEM format (ASCII).
|
/// The content in PEM format (ASCII).
|
||||||
public let pem: String
|
public let pem: String
|
||||||
|
|
||||||
var isEncrypted: Bool {
|
var isEncrypted: Bool {
|
||||||
return pem.contains("ENCRYPTED")
|
return pem.contains("ENCRYPTED")
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(pem: String) {
|
public init(pem: String) {
|
||||||
guard let beginRange = pem.range(of: CryptoContainer.begin) else {
|
guard let beginRange = pem.range(of: CryptoContainer.begin) else {
|
||||||
self.pem = ""
|
self.pem = ""
|
||||||
|
@ -61,7 +61,7 @@ extension OpenVPN {
|
||||||
}
|
}
|
||||||
self.pem = String(pem[beginRange.lowerBound...])
|
self.pem = String(pem[beginRange.lowerBound...])
|
||||||
}
|
}
|
||||||
|
|
||||||
func write(to url: URL) throws {
|
func write(to url: URL) throws {
|
||||||
try pem.write(to: url, atomically: true, encoding: .ascii)
|
try pem.write(to: url, atomically: true, encoding: .ascii)
|
||||||
}
|
}
|
||||||
|
@ -73,13 +73,13 @@ extension OpenVPN {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Codable
|
// MARK: Codable
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
let container = try decoder.singleValueContainer()
|
let container = try decoder.singleValueContainer()
|
||||||
let pem = try container.decode(String.self)
|
let pem = try container.decode(String.self)
|
||||||
self.init(pem: pem)
|
self.init(pem: pem)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
public func encode(to encoder: Encoder) throws {
|
||||||
var container = encoder.singleValueContainer()
|
var container = encoder.singleValueContainer()
|
||||||
try container.encode(pem)
|
try container.encode(pem)
|
||||||
|
|
|
@ -31,7 +31,7 @@ extension Error {
|
||||||
let te = self as NSError
|
let te = self as NSError
|
||||||
return te.domain == OpenVPNErrorDomain
|
return te.domain == OpenVPNErrorDomain
|
||||||
}
|
}
|
||||||
|
|
||||||
public func openVPNErrorCode() -> OpenVPNErrorCode? {
|
public func openVPNErrorCode() -> OpenVPNErrorCode? {
|
||||||
let te = self as NSError
|
let te = self as NSError
|
||||||
guard te.domain == OpenVPNErrorDomain else {
|
guard te.domain == OpenVPNErrorDomain else {
|
||||||
|
|
|
@ -38,37 +38,37 @@ import Foundation
|
||||||
|
|
||||||
/// The possible errors raised/thrown during `OpenVPNSession` operation.
|
/// The possible errors raised/thrown during `OpenVPNSession` operation.
|
||||||
public enum OpenVPNError: String, Error {
|
public enum OpenVPNError: String, Error {
|
||||||
|
|
||||||
/// The negotiation timed out.
|
/// The negotiation timed out.
|
||||||
case negotiationTimeout
|
case negotiationTimeout
|
||||||
|
|
||||||
/// The VPN session id is missing.
|
/// The VPN session id is missing.
|
||||||
case missingSessionId
|
case missingSessionId
|
||||||
|
|
||||||
/// The VPN session id doesn't match.
|
/// The VPN session id doesn't match.
|
||||||
case sessionMismatch
|
case sessionMismatch
|
||||||
|
|
||||||
/// The connection key is wrong or wasn't expected.
|
/// The connection key is wrong or wasn't expected.
|
||||||
case badKey
|
case badKey
|
||||||
|
|
||||||
/// The control packet has an incorrect prefix payload.
|
/// The control packet has an incorrect prefix payload.
|
||||||
case wrongControlDataPrefix
|
case wrongControlDataPrefix
|
||||||
|
|
||||||
/// The provided credentials failed authentication.
|
/// The provided credentials failed authentication.
|
||||||
case badCredentials
|
case badCredentials
|
||||||
|
|
||||||
/// The PUSH_REPLY is multipart.
|
/// The PUSH_REPLY is multipart.
|
||||||
case continuationPushReply
|
case continuationPushReply
|
||||||
|
|
||||||
/// The reply to PUSH_REQUEST is malformed.
|
/// The reply to PUSH_REQUEST is malformed.
|
||||||
case malformedPushReply
|
case malformedPushReply
|
||||||
|
|
||||||
/// A write operation failed at the link layer (e.g. network unreachable).
|
/// A write operation failed at the link layer (e.g. network unreachable).
|
||||||
case failedLinkWrite
|
case failedLinkWrite
|
||||||
|
|
||||||
/// The server couldn't ping back before timeout.
|
/// The server couldn't ping back before timeout.
|
||||||
case pingTimeout
|
case pingTimeout
|
||||||
|
|
||||||
/// The session reached a stale state and can't be recovered.
|
/// The session reached a stale state and can't be recovered.
|
||||||
case staleSession
|
case staleSession
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ extension OpenVPN {
|
||||||
public struct StaticKey: Codable, Equatable {
|
public struct StaticKey: Codable, Equatable {
|
||||||
enum CodingKeys: CodingKey {
|
enum CodingKeys: CodingKey {
|
||||||
case data
|
case data
|
||||||
|
|
||||||
case dir
|
case dir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,27 +42,27 @@ extension OpenVPN {
|
||||||
|
|
||||||
/// Conventional server direction (implicit for tls-crypt).
|
/// Conventional server direction (implicit for tls-crypt).
|
||||||
case server = 0
|
case server = 0
|
||||||
|
|
||||||
/// Conventional client direction (implicit for tls-crypt).
|
/// Conventional client direction (implicit for tls-crypt).
|
||||||
case client = 1
|
case client = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
private static let contentLength = 256 // 2048-bit
|
private static let contentLength = 256 // 2048-bit
|
||||||
|
|
||||||
private static let keyCount = 4
|
private static let keyCount = 4
|
||||||
|
|
||||||
private static let keyLength = StaticKey.contentLength / StaticKey.keyCount
|
private static let keyLength = StaticKey.contentLength / StaticKey.keyCount
|
||||||
|
|
||||||
private static let fileHead = "-----BEGIN OpenVPN Static key V1-----"
|
private static let fileHead = "-----BEGIN OpenVPN Static key V1-----"
|
||||||
|
|
||||||
private static let fileFoot = "-----END OpenVPN Static key V1-----"
|
private static let fileFoot = "-----END OpenVPN Static key V1-----"
|
||||||
|
|
||||||
private static let nonHexCharset = CharacterSet(charactersIn: "0123456789abcdefABCDEF").inverted
|
private static let nonHexCharset = CharacterSet(charactersIn: "0123456789abcdefABCDEF").inverted
|
||||||
|
|
||||||
private let secureData: ZeroingData
|
private let secureData: ZeroingData
|
||||||
|
|
||||||
public let direction: Direction?
|
public let direction: Direction?
|
||||||
|
|
||||||
/// Returns the encryption key.
|
/// Returns the encryption key.
|
||||||
///
|
///
|
||||||
/// - Precondition: `direction` must be non-nil.
|
/// - Precondition: `direction` must be non-nil.
|
||||||
|
@ -74,7 +74,7 @@ extension OpenVPN {
|
||||||
switch direction {
|
switch direction {
|
||||||
case .server:
|
case .server:
|
||||||
return key(at: 0)
|
return key(at: 0)
|
||||||
|
|
||||||
case .client:
|
case .client:
|
||||||
return key(at: 2)
|
return key(at: 2)
|
||||||
}
|
}
|
||||||
|
@ -91,12 +91,12 @@ extension OpenVPN {
|
||||||
switch direction {
|
switch direction {
|
||||||
case .server:
|
case .server:
|
||||||
return key(at: 2)
|
return key(at: 2)
|
||||||
|
|
||||||
case .client:
|
case .client:
|
||||||
return key(at: 0)
|
return key(at: 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the HMAC sending key.
|
/// Returns the HMAC sending key.
|
||||||
///
|
///
|
||||||
/// - Seealso: `ConfigurationBuilder.tlsWrap`
|
/// - Seealso: `ConfigurationBuilder.tlsWrap`
|
||||||
|
@ -107,12 +107,12 @@ extension OpenVPN {
|
||||||
switch direction {
|
switch direction {
|
||||||
case .server:
|
case .server:
|
||||||
return key(at: 1)
|
return key(at: 1)
|
||||||
|
|
||||||
case .client:
|
case .client:
|
||||||
return key(at: 3)
|
return key(at: 3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the HMAC receiving key.
|
/// Returns the HMAC receiving key.
|
||||||
///
|
///
|
||||||
/// - Seealso: `ConfigurationBuilder.tlsWrap`
|
/// - Seealso: `ConfigurationBuilder.tlsWrap`
|
||||||
|
@ -123,12 +123,12 @@ extension OpenVPN {
|
||||||
switch direction {
|
switch direction {
|
||||||
case .server:
|
case .server:
|
||||||
return key(at: 3)
|
return key(at: 3)
|
||||||
|
|
||||||
case .client:
|
case .client:
|
||||||
return key(at: 1)
|
return key(at: 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Initializes with data and direction.
|
Initializes with data and direction.
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ extension OpenVPN {
|
||||||
secureData = Z(data)
|
secureData = Z(data)
|
||||||
self.direction = direction
|
self.direction = direction
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Initializes with file content and direction.
|
Initializes with file content and direction.
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ extension OpenVPN {
|
||||||
let lines = file.split(separator: "\n")
|
let lines = file.split(separator: "\n")
|
||||||
self.init(lines: lines, direction: direction)
|
self.init(lines: lines, direction: direction)
|
||||||
}
|
}
|
||||||
|
|
||||||
public init?(lines: [Substring], direction: Direction?) {
|
public init?(lines: [Substring], direction: Direction?) {
|
||||||
var isHead = true
|
var isHead = true
|
||||||
var hexLines: [Substring] = []
|
var hexLines: [Substring] = []
|
||||||
|
@ -187,10 +187,10 @@ extension OpenVPN {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
let data = Data(hex: hex)
|
let data = Data(hex: hex)
|
||||||
|
|
||||||
self.init(data: data, direction: direction)
|
self.init(data: data, direction: direction)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Initializes as bidirectional.
|
Initializes as bidirectional.
|
||||||
|
|
||||||
|
@ -199,41 +199,41 @@ extension OpenVPN {
|
||||||
public init(biData data: Data) {
|
public init(biData data: Data) {
|
||||||
self.init(data: data, direction: nil)
|
self.init(data: data, direction: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func key(at: Int) -> ZeroingData {
|
private func key(at: Int) -> ZeroingData {
|
||||||
let size = secureData.count / StaticKey.keyCount // 64 bytes each
|
let size = secureData.count / StaticKey.keyCount // 64 bytes each
|
||||||
assert(size == StaticKey.keyLength)
|
assert(size == StaticKey.keyLength)
|
||||||
return secureData.withOffset(at * size, count: size)
|
return secureData.withOffset(at * size, count: size)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func deserialized(_ data: Data) throws -> StaticKey {
|
public static func deserialized(_ data: Data) throws -> StaticKey {
|
||||||
return try JSONDecoder().decode(StaticKey.self, from: data)
|
return try JSONDecoder().decode(StaticKey.self, from: data)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func serialized() -> Data? {
|
public func serialized() -> Data? {
|
||||||
return try? JSONEncoder().encode(self)
|
return try? JSONEncoder().encode(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Equatable
|
// MARK: Equatable
|
||||||
|
|
||||||
public static func ==(lhs: Self, rhs: Self) -> Bool {
|
public static func ==(lhs: Self, rhs: Self) -> Bool {
|
||||||
return lhs.secureData.toData() == rhs.secureData.toData()
|
return lhs.secureData.toData() == rhs.secureData.toData()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Codable
|
// MARK: Codable
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
secureData = Z(try container.decode(Data.self, forKey: .data))
|
secureData = Z(try container.decode(Data.self, forKey: .data))
|
||||||
direction = try container.decodeIfPresent(Direction.self, forKey: .dir)
|
direction = try container.decodeIfPresent(Direction.self, forKey: .dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
public func encode(to encoder: Encoder) throws {
|
||||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||||
try container.encode(secureData.toData(), forKey: .data)
|
try container.encode(secureData.toData(), forKey: .data)
|
||||||
try container.encodeIfPresent(direction, forKey: .dir)
|
try container.encodeIfPresent(direction, forKey: .dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var hexString: String {
|
public var hexString: String {
|
||||||
return secureData.toHex()
|
return secureData.toHex()
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ extension OpenVPN {
|
||||||
|
|
||||||
/// The wrapping strategy.
|
/// The wrapping strategy.
|
||||||
public enum Strategy: String, Codable, Equatable {
|
public enum Strategy: String, Codable, Equatable {
|
||||||
|
|
||||||
/// Authenticates payload (--tls-auth).
|
/// Authenticates payload (--tls-auth).
|
||||||
case auth
|
case auth
|
||||||
|
|
||||||
|
@ -42,10 +42,10 @@ extension OpenVPN {
|
||||||
|
|
||||||
/// The wrapping strategy.
|
/// The wrapping strategy.
|
||||||
public let strategy: Strategy
|
public let strategy: Strategy
|
||||||
|
|
||||||
/// The static encryption key.
|
/// The static encryption key.
|
||||||
public let key: StaticKey
|
public let key: StaticKey
|
||||||
|
|
||||||
public init(strategy: Strategy, key: StaticKey) {
|
public init(strategy: Strategy, key: StaticKey) {
|
||||||
self.strategy = strategy
|
self.strategy = strategy
|
||||||
self.key = key
|
self.key = key
|
||||||
|
@ -54,7 +54,7 @@ extension OpenVPN {
|
||||||
public static func deserialized(_ data: Data) throws -> TLSWrap {
|
public static func deserialized(_ data: Data) throws -> TLSWrap {
|
||||||
return try JSONDecoder().decode(TLSWrap.self, from: data)
|
return try JSONDecoder().decode(TLSWrap.self, from: data)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func serialized() -> Data? {
|
public func serialized() -> Data? {
|
||||||
return try? JSONEncoder().encode(self)
|
return try? JSONEncoder().encode(self)
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,19 +27,19 @@ import Foundation
|
||||||
import CTunnelKitOpenVPNCore
|
import CTunnelKitOpenVPNCore
|
||||||
|
|
||||||
extension OpenVPN {
|
extension OpenVPN {
|
||||||
|
|
||||||
/// The obfuscation method.
|
/// The obfuscation method.
|
||||||
public enum XORMethod: Codable, Equatable {
|
public enum XORMethod: Codable, Equatable {
|
||||||
|
|
||||||
/// XORs the bytes in each buffer with the given mask.
|
/// XORs the bytes in each buffer with the given mask.
|
||||||
case xormask(mask: Data)
|
case xormask(mask: Data)
|
||||||
|
|
||||||
/// XORs each byte with its position in the packet.
|
/// XORs each byte with its position in the packet.
|
||||||
case xorptrpos
|
case xorptrpos
|
||||||
|
|
||||||
/// Reverses the order of bytes in each buffer except for the first (abcde becomes aedcb).
|
/// Reverses the order of bytes in each buffer except for the first (abcde becomes aedcb).
|
||||||
case reverse
|
case reverse
|
||||||
|
|
||||||
/// Performs several of the above steps (xormask -> xorptrpos -> reverse -> xorptrpos).
|
/// Performs several of the above steps (xormask -> xorptrpos -> reverse -> xorptrpos).
|
||||||
case obfuscate(mask: Data)
|
case obfuscate(mask: Data)
|
||||||
|
|
||||||
|
@ -48,13 +48,13 @@ extension OpenVPN {
|
||||||
switch self {
|
switch self {
|
||||||
case .xormask:
|
case .xormask:
|
||||||
return .mask
|
return .mask
|
||||||
|
|
||||||
case .xorptrpos:
|
case .xorptrpos:
|
||||||
return .ptrPos
|
return .ptrPos
|
||||||
|
|
||||||
case .reverse:
|
case .reverse:
|
||||||
return .reverse
|
return .reverse
|
||||||
|
|
||||||
case .obfuscate:
|
case .obfuscate:
|
||||||
return .obfuscate
|
return .obfuscate
|
||||||
}
|
}
|
||||||
|
@ -65,10 +65,10 @@ extension OpenVPN {
|
||||||
switch self {
|
switch self {
|
||||||
case .xormask(let mask):
|
case .xormask(let mask):
|
||||||
return mask
|
return mask
|
||||||
|
|
||||||
case .obfuscate(let mask):
|
case .obfuscate(let mask):
|
||||||
return mask
|
return mask
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,34 +46,34 @@ extension OpenVPN {
|
||||||
|
|
||||||
case lastError = "OpenVPN.LastError"
|
case lastError = "OpenVPN.LastError"
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Optional version identifier about the client pushed to server in peer-info as `IV_UI_VER`.
|
/// Optional version identifier about the client pushed to server in peer-info as `IV_UI_VER`.
|
||||||
public var versionIdentifier: String?
|
public var versionIdentifier: String?
|
||||||
|
|
||||||
/// The configuration title.
|
/// The configuration title.
|
||||||
public let title: String
|
public let title: String
|
||||||
|
|
||||||
/// The access group for shared data.
|
/// The access group for shared data.
|
||||||
public let appGroup: String
|
public let appGroup: String
|
||||||
|
|
||||||
/// The client configuration.
|
/// The client configuration.
|
||||||
public let configuration: OpenVPN.Configuration
|
public let configuration: OpenVPN.Configuration
|
||||||
|
|
||||||
/// The optional username.
|
/// The optional username.
|
||||||
public var username: String?
|
public var username: String?
|
||||||
|
|
||||||
/// Enables debugging.
|
/// Enables debugging.
|
||||||
public var shouldDebug = false
|
public var shouldDebug = false
|
||||||
|
|
||||||
/// Debug log path.
|
/// Debug log path.
|
||||||
public var debugLogPath: String? = nil
|
public var debugLogPath: String?
|
||||||
|
|
||||||
/// Optional debug log format (SwiftyBeaver format).
|
/// Optional debug log format (SwiftyBeaver format).
|
||||||
public var debugLogFormat: String? = nil
|
public var debugLogFormat: String?
|
||||||
|
|
||||||
/// Mask private data in debug log (default is `true`).
|
/// Mask private data in debug log (default is `true`).
|
||||||
public var masksPrivateData = true
|
public var masksPrivateData = true
|
||||||
|
|
||||||
public init(_ title: String, appGroup: String, configuration: OpenVPN.Configuration) {
|
public init(_ title: String, appGroup: String, configuration: OpenVPN.Configuration) {
|
||||||
self.title = title
|
self.title = title
|
||||||
self.appGroup = appGroup
|
self.appGroup = appGroup
|
||||||
|
@ -142,7 +142,7 @@ extension OpenVPN.ProviderConfiguration {
|
||||||
public var lastError: OpenVPNProviderError? {
|
public var lastError: OpenVPNProviderError? {
|
||||||
return defaults?.openVPNLastError
|
return defaults?.openVPNLastError
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The URL of the latest debug log.
|
The URL of the latest debug log.
|
||||||
*/
|
*/
|
||||||
|
@ -208,7 +208,7 @@ extension UserDefaults {
|
||||||
openVPNDataCountArray = [newValue.received, newValue.sent]
|
openVPNDataCountArray = [newValue.received, newValue.sent]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private var openVPNDataCountArray: [UInt]? {
|
@objc private var openVPNDataCountArray: [UInt]? {
|
||||||
get {
|
get {
|
||||||
return array(forKey: OpenVPN.ProviderConfiguration.Keys.dataCount.rawValue) as? [UInt]
|
return array(forKey: OpenVPN.ProviderConfiguration.Keys.dataCount.rawValue) as? [UInt]
|
||||||
|
@ -217,7 +217,7 @@ extension UserDefaults {
|
||||||
set(newValue, forKey: OpenVPN.ProviderConfiguration.Keys.dataCount.rawValue)
|
set(newValue, forKey: OpenVPN.ProviderConfiguration.Keys.dataCount.rawValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func openVPNRemoveDataCountArray() {
|
private func openVPNRemoveDataCountArray() {
|
||||||
removeObject(forKey: OpenVPN.ProviderConfiguration.Keys.dataCount.rawValue)
|
removeObject(forKey: OpenVPN.ProviderConfiguration.Keys.dataCount.rawValue)
|
||||||
}
|
}
|
||||||
|
@ -249,7 +249,7 @@ extension UserDefaults {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fileprivate(set) var openVPNLastError: OpenVPNProviderError? {
|
public fileprivate(set) var openVPNLastError: OpenVPNProviderError? {
|
||||||
get {
|
get {
|
||||||
guard let rawValue = string(forKey: OpenVPN.ProviderConfiguration.Keys.lastError.rawValue) else {
|
guard let rawValue = string(forKey: OpenVPN.ProviderConfiguration.Keys.lastError.rawValue) else {
|
||||||
|
|
|
@ -38,35 +38,35 @@ import Foundation
|
||||||
|
|
||||||
/// Mostly programming errors by host app.
|
/// Mostly programming errors by host app.
|
||||||
public enum OpenVPNProviderConfigurationError: Error {
|
public enum OpenVPNProviderConfigurationError: Error {
|
||||||
|
|
||||||
/// A field in the `OpenVPNProvider.Configuration` provided is incorrect or incomplete.
|
/// A field in the `OpenVPNProvider.Configuration` provided is incorrect or incomplete.
|
||||||
case parameter(name: String)
|
case parameter(name: String)
|
||||||
|
|
||||||
/// Credentials are missing or inaccessible.
|
/// Credentials are missing or inaccessible.
|
||||||
case credentials(details: String)
|
case credentials(details: String)
|
||||||
|
|
||||||
/// The pseudo-random number generator could not be initialized.
|
/// The pseudo-random number generator could not be initialized.
|
||||||
case prngInitialization
|
case prngInitialization
|
||||||
|
|
||||||
/// The TLS certificate could not be serialized.
|
/// The TLS certificate could not be serialized.
|
||||||
case certificateSerialization
|
case certificateSerialization
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The errors causing a tunnel disconnection.
|
/// The errors causing a tunnel disconnection.
|
||||||
public enum OpenVPNProviderError: String, Error {
|
public enum OpenVPNProviderError: String, Error {
|
||||||
|
|
||||||
/// Socket endpoint could not be resolved.
|
/// Socket endpoint could not be resolved.
|
||||||
case dnsFailure
|
case dnsFailure
|
||||||
|
|
||||||
/// No more endpoints available to try.
|
/// No more endpoints available to try.
|
||||||
case exhaustedEndpoints
|
case exhaustedEndpoints
|
||||||
|
|
||||||
/// Socket failed to reach active state.
|
/// Socket failed to reach active state.
|
||||||
case socketActivity
|
case socketActivity
|
||||||
|
|
||||||
/// Credentials authentication failed.
|
/// Credentials authentication failed.
|
||||||
case authentication
|
case authentication
|
||||||
|
|
||||||
/// TLS could not be initialized (e.g. malformed CA or client PEMs).
|
/// TLS could not be initialized (e.g. malformed CA or client PEMs).
|
||||||
case tlsInitialization
|
case tlsInitialization
|
||||||
|
|
||||||
|
@ -75,37 +75,37 @@ public enum OpenVPNProviderError: String, Error {
|
||||||
|
|
||||||
/// TLS handshake failed.
|
/// TLS handshake failed.
|
||||||
case tlsHandshake
|
case tlsHandshake
|
||||||
|
|
||||||
/// The encryption logic could not be initialized (e.g. PRNG, algorithms).
|
/// The encryption logic could not be initialized (e.g. PRNG, algorithms).
|
||||||
case encryptionInitialization
|
case encryptionInitialization
|
||||||
|
|
||||||
/// Data encryption/decryption failed.
|
/// Data encryption/decryption failed.
|
||||||
case encryptionData
|
case encryptionData
|
||||||
|
|
||||||
/// The LZO engine failed.
|
/// The LZO engine failed.
|
||||||
case lzo
|
case lzo
|
||||||
|
|
||||||
/// Server uses an unsupported compression algorithm.
|
/// Server uses an unsupported compression algorithm.
|
||||||
case serverCompression
|
case serverCompression
|
||||||
|
|
||||||
/// Tunnel timed out.
|
/// Tunnel timed out.
|
||||||
case timeout
|
case timeout
|
||||||
|
|
||||||
/// An error occurred at the link level.
|
/// An error occurred at the link level.
|
||||||
case linkError
|
case linkError
|
||||||
|
|
||||||
/// Network routing information is missing or incomplete.
|
/// Network routing information is missing or incomplete.
|
||||||
case routing
|
case routing
|
||||||
|
|
||||||
/// The current network changed (e.g. switched from WiFi to data connection).
|
/// The current network changed (e.g. switched from WiFi to data connection).
|
||||||
case networkChanged
|
case networkChanged
|
||||||
|
|
||||||
/// Default gateway could not be attained.
|
/// Default gateway could not be attained.
|
||||||
case gatewayUnattainable
|
case gatewayUnattainable
|
||||||
|
|
||||||
/// Remove server has shut down.
|
/// Remove server has shut down.
|
||||||
case serverShutdown
|
case serverShutdown
|
||||||
|
|
||||||
/// The server replied in an unexpected way.
|
/// The server replied in an unexpected way.
|
||||||
case unexpectedReply
|
case unexpectedReply
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,28 +53,28 @@ fileprivate extension ZeroingData {
|
||||||
extension OpenVPN {
|
extension OpenVPN {
|
||||||
class Authenticator {
|
class Authenticator {
|
||||||
private var controlBuffer: ZeroingData
|
private var controlBuffer: ZeroingData
|
||||||
|
|
||||||
private(set) var preMaster: ZeroingData
|
private(set) var preMaster: ZeroingData
|
||||||
|
|
||||||
private(set) var random1: ZeroingData
|
private(set) var random1: ZeroingData
|
||||||
|
|
||||||
private(set) var random2: ZeroingData
|
private(set) var random2: ZeroingData
|
||||||
|
|
||||||
private(set) var serverRandom1: ZeroingData?
|
private(set) var serverRandom1: ZeroingData?
|
||||||
|
|
||||||
private(set) var serverRandom2: ZeroingData?
|
private(set) var serverRandom2: ZeroingData?
|
||||||
|
|
||||||
private(set) var username: ZeroingData?
|
private(set) var username: ZeroingData?
|
||||||
|
|
||||||
private(set) var password: ZeroingData?
|
private(set) var password: ZeroingData?
|
||||||
|
|
||||||
var withLocalOptions: Bool
|
var withLocalOptions: Bool
|
||||||
|
|
||||||
init(_ username: String?, _ password: String?) throws {
|
init(_ username: String?, _ password: String?) throws {
|
||||||
preMaster = try SecureRandom.safeData(length: CoreConfiguration.OpenVPN.preMasterLength)
|
preMaster = try SecureRandom.safeData(length: CoreConfiguration.OpenVPN.preMasterLength)
|
||||||
random1 = try SecureRandom.safeData(length: CoreConfiguration.OpenVPN.randomLength)
|
random1 = try SecureRandom.safeData(length: CoreConfiguration.OpenVPN.randomLength)
|
||||||
random2 = try SecureRandom.safeData(length: CoreConfiguration.OpenVPN.randomLength)
|
random2 = try SecureRandom.safeData(length: CoreConfiguration.OpenVPN.randomLength)
|
||||||
|
|
||||||
// XXX: not 100% secure, can't erase input username/password
|
// XXX: not 100% secure, can't erase input username/password
|
||||||
if let username = username, let password = password {
|
if let username = username, let password = password {
|
||||||
self.username = Z(username, nullTerminated: true)
|
self.username = Z(username, nullTerminated: true)
|
||||||
|
@ -83,12 +83,12 @@ extension OpenVPN {
|
||||||
self.username = nil
|
self.username = nil
|
||||||
self.password = nil
|
self.password = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
withLocalOptions = true
|
withLocalOptions = true
|
||||||
|
|
||||||
controlBuffer = Z()
|
controlBuffer = Z()
|
||||||
}
|
}
|
||||||
|
|
||||||
func reset() {
|
func reset() {
|
||||||
controlBuffer.zero()
|
controlBuffer.zero()
|
||||||
preMaster.zero()
|
preMaster.zero()
|
||||||
|
@ -99,18 +99,18 @@ extension OpenVPN {
|
||||||
username = nil
|
username = nil
|
||||||
password = nil
|
password = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Authentication request
|
// MARK: Authentication request
|
||||||
|
|
||||||
// Ruby: on_tls_connect
|
// Ruby: on_tls_connect
|
||||||
func putAuth(into: TLSBox, options: Configuration) throws {
|
func putAuth(into: TLSBox, options: Configuration) throws {
|
||||||
let raw = Z(ProtocolMacros.tlsPrefix)
|
let raw = Z(ProtocolMacros.tlsPrefix)
|
||||||
|
|
||||||
// local keys
|
// local keys
|
||||||
raw.append(preMaster)
|
raw.append(preMaster)
|
||||||
raw.append(random1)
|
raw.append(random1)
|
||||||
raw.append(random2)
|
raw.append(random2)
|
||||||
|
|
||||||
// options string
|
// options string
|
||||||
let optsString: String
|
let optsString: String
|
||||||
if withLocalOptions {
|
if withLocalOptions {
|
||||||
|
@ -122,10 +122,10 @@ extension OpenVPN {
|
||||||
switch comp {
|
switch comp {
|
||||||
case .compLZO:
|
case .compLZO:
|
||||||
opts.append("comp-lzo")
|
opts.append("comp-lzo")
|
||||||
|
|
||||||
case .compress:
|
case .compress:
|
||||||
opts.append("compress")
|
opts.append("compress")
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ extension OpenVPN {
|
||||||
}
|
}
|
||||||
log.debug("TLS.auth: Local options: \(optsString)")
|
log.debug("TLS.auth: Local options: \(optsString)")
|
||||||
raw.appendSized(Z(optsString, nullTerminated: true))
|
raw.appendSized(Z(optsString, nullTerminated: true))
|
||||||
|
|
||||||
// credentials
|
// credentials
|
||||||
if let username = username, let password = password {
|
if let username = username, let password = password {
|
||||||
raw.appendSized(username)
|
raw.appendSized(username)
|
||||||
|
@ -169,40 +169,40 @@ extension OpenVPN {
|
||||||
} else {
|
} else {
|
||||||
log.debug("TLS.auth: Put plaintext (\(raw.count) bytes)")
|
log.debug("TLS.auth: Put plaintext (\(raw.count) bytes)")
|
||||||
}
|
}
|
||||||
|
|
||||||
try into.putRawPlainText(raw.bytes, length: raw.count)
|
try into.putRawPlainText(raw.bytes, length: raw.count)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Server replies
|
// MARK: Server replies
|
||||||
|
|
||||||
func appendControlData(_ data: ZeroingData) {
|
func appendControlData(_ data: ZeroingData) {
|
||||||
controlBuffer.append(data)
|
controlBuffer.append(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseAuthReply() throws -> Bool {
|
func parseAuthReply() throws -> Bool {
|
||||||
let prefixLength = ProtocolMacros.tlsPrefix.count
|
let prefixLength = ProtocolMacros.tlsPrefix.count
|
||||||
|
|
||||||
// TLS prefix + random (x2) + opts length [+ opts]
|
// TLS prefix + random (x2) + opts length [+ opts]
|
||||||
guard (controlBuffer.count >= prefixLength + 2 * CoreConfiguration.OpenVPN.randomLength + 2) else {
|
guard controlBuffer.count >= prefixLength + 2 * CoreConfiguration.OpenVPN.randomLength + 2 else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
let prefix = controlBuffer.withOffset(0, count: prefixLength)
|
let prefix = controlBuffer.withOffset(0, count: prefixLength)
|
||||||
guard prefix.isEqual(to: ProtocolMacros.tlsPrefix) else {
|
guard prefix.isEqual(to: ProtocolMacros.tlsPrefix) else {
|
||||||
throw OpenVPNError.wrongControlDataPrefix
|
throw OpenVPNError.wrongControlDataPrefix
|
||||||
}
|
}
|
||||||
|
|
||||||
var offset = ProtocolMacros.tlsPrefix.count
|
var offset = ProtocolMacros.tlsPrefix.count
|
||||||
|
|
||||||
let serverRandom1 = controlBuffer.withOffset(offset, count: CoreConfiguration.OpenVPN.randomLength)
|
let serverRandom1 = controlBuffer.withOffset(offset, count: CoreConfiguration.OpenVPN.randomLength)
|
||||||
offset += CoreConfiguration.OpenVPN.randomLength
|
offset += CoreConfiguration.OpenVPN.randomLength
|
||||||
|
|
||||||
let serverRandom2 = controlBuffer.withOffset(offset, count: CoreConfiguration.OpenVPN.randomLength)
|
let serverRandom2 = controlBuffer.withOffset(offset, count: CoreConfiguration.OpenVPN.randomLength)
|
||||||
offset += CoreConfiguration.OpenVPN.randomLength
|
offset += CoreConfiguration.OpenVPN.randomLength
|
||||||
|
|
||||||
let serverOptsLength = Int(controlBuffer.networkUInt16Value(fromOffset: offset))
|
let serverOptsLength = Int(controlBuffer.networkUInt16Value(fromOffset: offset))
|
||||||
offset += 2
|
offset += 2
|
||||||
|
|
||||||
guard controlBuffer.count >= offset + serverOptsLength else {
|
guard controlBuffer.count >= offset + serverOptsLength else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -214,22 +214,22 @@ extension OpenVPN {
|
||||||
} else {
|
} else {
|
||||||
log.debug("TLS.auth: Parsed server random")
|
log.debug("TLS.auth: Parsed server random")
|
||||||
}
|
}
|
||||||
|
|
||||||
if let serverOptsString = serverOpts.nullTerminatedString(fromOffset: 0) {
|
if let serverOptsString = serverOpts.nullTerminatedString(fromOffset: 0) {
|
||||||
log.debug("TLS.auth: Parsed server options: \"\(serverOptsString)\"")
|
log.debug("TLS.auth: Parsed server options: \"\(serverOptsString)\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
self.serverRandom1 = serverRandom1
|
self.serverRandom1 = serverRandom1
|
||||||
self.serverRandom2 = serverRandom2
|
self.serverRandom2 = serverRandom2
|
||||||
controlBuffer.remove(untilOffset: offset)
|
controlBuffer.remove(untilOffset: offset)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseMessages() -> [String] {
|
func parseMessages() -> [String] {
|
||||||
var messages = [String]()
|
var messages = [String]()
|
||||||
var offset = 0
|
var offset = 0
|
||||||
|
|
||||||
while true {
|
while true {
|
||||||
guard let msg = controlBuffer.nullTerminatedString(fromOffset: offset) else {
|
guard let msg = controlBuffer.nullTerminatedString(fromOffset: offset) else {
|
||||||
break
|
break
|
||||||
|
|
|
@ -35,7 +35,7 @@ private let log = SwiftyBeaver.self
|
||||||
extension OpenVPN {
|
extension OpenVPN {
|
||||||
class ControlChannelError: Error, CustomStringConvertible {
|
class ControlChannelError: Error, CustomStringConvertible {
|
||||||
let description: String
|
let description: String
|
||||||
|
|
||||||
init(_ message: String) {
|
init(_ message: String) {
|
||||||
description = "\(String(describing: ControlChannelError.self))(\(message))"
|
description = "\(String(describing: ControlChannelError.self))(\(message))"
|
||||||
}
|
}
|
||||||
|
@ -43,9 +43,9 @@ extension OpenVPN {
|
||||||
|
|
||||||
class ControlChannel {
|
class ControlChannel {
|
||||||
private let serializer: ControlChannelSerializer
|
private let serializer: ControlChannelSerializer
|
||||||
|
|
||||||
private(set) var sessionId: Data?
|
private(set) var sessionId: Data?
|
||||||
|
|
||||||
var remoteSessionId: Data? {
|
var remoteSessionId: Data? {
|
||||||
didSet {
|
didSet {
|
||||||
if let id = remoteSessionId {
|
if let id = remoteSessionId {
|
||||||
|
@ -63,11 +63,11 @@ extension OpenVPN {
|
||||||
private var plainBuffer: ZeroingData
|
private var plainBuffer: ZeroingData
|
||||||
|
|
||||||
private var dataCount: BidirectionalState<Int>
|
private var dataCount: BidirectionalState<Int>
|
||||||
|
|
||||||
convenience init() {
|
convenience init() {
|
||||||
self.init(serializer: PlainSerializer())
|
self.init(serializer: PlainSerializer())
|
||||||
}
|
}
|
||||||
|
|
||||||
convenience init(withAuthKey key: StaticKey, digest: Digest) throws {
|
convenience init(withAuthKey key: StaticKey, digest: Digest) throws {
|
||||||
self.init(serializer: try AuthSerializer(withKey: key, digest: digest))
|
self.init(serializer: try AuthSerializer(withKey: key, digest: digest))
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ extension OpenVPN {
|
||||||
convenience init(withCryptKey key: StaticKey) throws {
|
convenience init(withCryptKey key: StaticKey) throws {
|
||||||
self.init(serializer: try CryptSerializer(withKey: key))
|
self.init(serializer: try CryptSerializer(withKey: key))
|
||||||
}
|
}
|
||||||
|
|
||||||
private init(serializer: ControlChannelSerializer) {
|
private init(serializer: ControlChannelSerializer) {
|
||||||
self.serializer = serializer
|
self.serializer = serializer
|
||||||
sessionId = nil
|
sessionId = nil
|
||||||
|
@ -86,7 +86,7 @@ extension OpenVPN {
|
||||||
plainBuffer = Z(count: TLSBoxMaxBufferLength)
|
plainBuffer = Z(count: TLSBoxMaxBufferLength)
|
||||||
dataCount = BidirectionalState(withResetValue: 0)
|
dataCount = BidirectionalState(withResetValue: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func reset(forNewSession: Bool) throws {
|
func reset(forNewSession: Bool) throws {
|
||||||
if forNewSession {
|
if forNewSession {
|
||||||
try sessionId = SecureRandom.data(length: PacketSessionIdLength)
|
try sessionId = SecureRandom.data(length: PacketSessionIdLength)
|
||||||
|
@ -112,7 +112,7 @@ extension OpenVPN {
|
||||||
func enqueueInboundPacket(packet: ControlPacket) -> [ControlPacket] {
|
func enqueueInboundPacket(packet: ControlPacket) -> [ControlPacket] {
|
||||||
queue.inbound.append(packet)
|
queue.inbound.append(packet)
|
||||||
queue.inbound.sort { $0.packetId < $1.packetId }
|
queue.inbound.sort { $0.packetId < $1.packetId }
|
||||||
|
|
||||||
var toHandle: [ControlPacket] = []
|
var toHandle: [ControlPacket] = []
|
||||||
for queuedPacket in queue.inbound {
|
for queuedPacket in queue.inbound {
|
||||||
if queuedPacket.packetId < currentPacketId.inbound {
|
if queuedPacket.packetId < currentPacketId.inbound {
|
||||||
|
@ -122,15 +122,15 @@ extension OpenVPN {
|
||||||
if queuedPacket.packetId != currentPacketId.inbound {
|
if queuedPacket.packetId != currentPacketId.inbound {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
toHandle.append(queuedPacket)
|
toHandle.append(queuedPacket)
|
||||||
|
|
||||||
currentPacketId.inbound += 1
|
currentPacketId.inbound += 1
|
||||||
queue.inbound.removeFirst()
|
queue.inbound.removeFirst()
|
||||||
}
|
}
|
||||||
return toHandle
|
return toHandle
|
||||||
}
|
}
|
||||||
|
|
||||||
func enqueueOutboundPackets(withCode code: PacketCode, key: UInt8, payload: Data, maxPacketSize: Int) {
|
func enqueueOutboundPackets(withCode code: PacketCode, key: UInt8, payload: Data, maxPacketSize: Int) {
|
||||||
guard let sessionId = sessionId else {
|
guard let sessionId = sessionId else {
|
||||||
fatalError("Missing sessionId, do reset(forNewSession: true) first")
|
fatalError("Missing sessionId, do reset(forNewSession: true) first")
|
||||||
|
@ -139,40 +139,40 @@ extension OpenVPN {
|
||||||
let oldIdOut = currentPacketId.outbound
|
let oldIdOut = currentPacketId.outbound
|
||||||
var queuedCount = 0
|
var queuedCount = 0
|
||||||
var offset = 0
|
var offset = 0
|
||||||
|
|
||||||
repeat {
|
repeat {
|
||||||
let subPayloadLength = min(maxPacketSize, payload.count - offset)
|
let subPayloadLength = min(maxPacketSize, payload.count - offset)
|
||||||
let subPayloadData = payload.subdata(offset: offset, count: subPayloadLength)
|
let subPayloadData = payload.subdata(offset: offset, count: subPayloadLength)
|
||||||
let packet = ControlPacket(code: code, key: key, sessionId: sessionId, packetId: currentPacketId.outbound, payload: subPayloadData)
|
let packet = ControlPacket(code: code, key: key, sessionId: sessionId, packetId: currentPacketId.outbound, payload: subPayloadData)
|
||||||
|
|
||||||
queue.outbound.append(packet)
|
queue.outbound.append(packet)
|
||||||
currentPacketId.outbound += 1
|
currentPacketId.outbound += 1
|
||||||
offset += maxPacketSize
|
offset += maxPacketSize
|
||||||
queuedCount += subPayloadLength
|
queuedCount += subPayloadLength
|
||||||
} while (offset < payload.count)
|
} while (offset < payload.count)
|
||||||
|
|
||||||
assert(queuedCount == payload.count)
|
assert(queuedCount == payload.count)
|
||||||
|
|
||||||
// packet count
|
// packet count
|
||||||
let packetCount = currentPacketId.outbound - oldIdOut
|
let packetCount = currentPacketId.outbound - oldIdOut
|
||||||
if (packetCount > 1) {
|
if packetCount > 1 {
|
||||||
log.debug("Control: Enqueued \(packetCount) packets [\(oldIdOut)-\(currentPacketId.outbound - 1)]")
|
log.debug("Control: Enqueued \(packetCount) packets [\(oldIdOut)-\(currentPacketId.outbound - 1)]")
|
||||||
} else {
|
} else {
|
||||||
log.debug("Control: Enqueued 1 packet [\(oldIdOut)]")
|
log.debug("Control: Enqueued 1 packet [\(oldIdOut)]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeOutboundPackets() throws -> [Data] {
|
func writeOutboundPackets() throws -> [Data] {
|
||||||
var rawList: [Data] = []
|
var rawList: [Data] = []
|
||||||
for packet in queue.outbound {
|
for packet in queue.outbound {
|
||||||
if let sentDate = packet.sentDate {
|
if let sentDate = packet.sentDate {
|
||||||
let timeAgo = -sentDate.timeIntervalSinceNow
|
let timeAgo = -sentDate.timeIntervalSinceNow
|
||||||
guard (timeAgo >= CoreConfiguration.OpenVPN.retransmissionLimit) else {
|
guard timeAgo >= CoreConfiguration.OpenVPN.retransmissionLimit else {
|
||||||
log.debug("Control: Skip writing packet with packetId \(packet.packetId) (sent on \(sentDate), \(timeAgo) seconds ago)")
|
log.debug("Control: Skip writing packet with packetId \(packet.packetId) (sent on \(sentDate), \(timeAgo) seconds ago)")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("Control: Write control packet \(packet)")
|
log.debug("Control: Write control packet \(packet)")
|
||||||
|
|
||||||
let raw = try serializer.serialize(packet: packet)
|
let raw = try serializer.serialize(packet: packet)
|
||||||
|
@ -185,11 +185,11 @@ extension OpenVPN {
|
||||||
// log.verbose("Packets now pending ack: \(pendingAcks)")
|
// log.verbose("Packets now pending ack: \(pendingAcks)")
|
||||||
return rawList
|
return rawList
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasPendingAcks() -> Bool {
|
func hasPendingAcks() -> Bool {
|
||||||
return !pendingAcks.isEmpty
|
return !pendingAcks.isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: handle_acks
|
// Ruby: handle_acks
|
||||||
private func readAcks(_ packetIds: [UInt32], acksRemoteSessionId: Data) throws {
|
private func readAcks(_ packetIds: [UInt32], acksRemoteSessionId: Data) throws {
|
||||||
guard let sessionId = sessionId else {
|
guard let sessionId = sessionId else {
|
||||||
|
@ -199,18 +199,18 @@ extension OpenVPN {
|
||||||
log.error("Control: Ack session mismatch (\(acksRemoteSessionId.toHex()) != \(sessionId.toHex()))")
|
log.error("Control: Ack session mismatch (\(acksRemoteSessionId.toHex()) != \(sessionId.toHex()))")
|
||||||
throw OpenVPNError.sessionMismatch
|
throw OpenVPNError.sessionMismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
// drop queued out packets if ack-ed
|
// drop queued out packets if ack-ed
|
||||||
queue.outbound.removeAll {
|
queue.outbound.removeAll {
|
||||||
return packetIds.contains($0.packetId)
|
return packetIds.contains($0.packetId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove ack-ed packets from pending
|
// remove ack-ed packets from pending
|
||||||
pendingAcks.subtract(packetIds)
|
pendingAcks.subtract(packetIds)
|
||||||
|
|
||||||
// log.verbose("Packets still pending ack: \(pendingAcks)")
|
// log.verbose("Packets still pending ack: \(pendingAcks)")
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeAcks(withKey key: UInt8, ackPacketIds: [UInt32], ackRemoteSessionId: Data) throws -> Data {
|
func writeAcks(withKey key: UInt8, ackPacketIds: [UInt32], ackRemoteSessionId: Data) throws -> Data {
|
||||||
guard let sessionId = sessionId else {
|
guard let sessionId = sessionId else {
|
||||||
throw OpenVPNError.missingSessionId
|
throw OpenVPNError.missingSessionId
|
||||||
|
@ -219,13 +219,13 @@ extension OpenVPN {
|
||||||
log.debug("Control: Write ack packet \(packet)")
|
log.debug("Control: Write ack packet \(packet)")
|
||||||
return try serializer.serialize(packet: packet)
|
return try serializer.serialize(packet: packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
func currentControlData(withTLS tls: TLSBox) throws -> ZeroingData {
|
func currentControlData(withTLS tls: TLSBox) throws -> ZeroingData {
|
||||||
var length = 0
|
var length = 0
|
||||||
try tls.pullRawPlainText(plainBuffer.mutableBytes, length: &length)
|
try tls.pullRawPlainText(plainBuffer.mutableBytes, length: &length)
|
||||||
return plainBuffer.withOffset(0, count: length)
|
return plainBuffer.withOffset(0, count: length)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addReceivedDataCount(_ count: Int) {
|
func addReceivedDataCount(_ count: Int) {
|
||||||
dataCount.inbound += count
|
dataCount.inbound += count
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,7 @@ extension OpenVPN {
|
||||||
func addSentDataCount(_ count: Int) {
|
func addSentDataCount(_ count: Int) {
|
||||||
dataCount.outbound += count
|
dataCount.outbound += count
|
||||||
}
|
}
|
||||||
|
|
||||||
func currentDataCount() -> DataCount {
|
func currentDataCount() -> DataCount {
|
||||||
return DataCount(UInt(dataCount.inbound), UInt(dataCount.outbound))
|
return DataCount(UInt(dataCount.inbound), UInt(dataCount.outbound))
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
protocol ControlChannelSerializer {
|
protocol ControlChannelSerializer {
|
||||||
func reset()
|
func reset()
|
||||||
|
|
||||||
func serialize(packet: ControlPacket) throws -> Data
|
func serialize(packet: ControlPacket) throws -> Data
|
||||||
|
|
||||||
func deserialize(data: Data, start: Int, end: Int?) throws -> ControlPacket
|
func deserialize(data: Data, start: Int, end: Int?) throws -> ControlPacket
|
||||||
|
@ -44,15 +44,15 @@ extension OpenVPN.ControlChannel {
|
||||||
class PlainSerializer: ControlChannelSerializer {
|
class PlainSerializer: ControlChannelSerializer {
|
||||||
func reset() {
|
func reset() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func serialize(packet: ControlPacket) throws -> Data {
|
func serialize(packet: ControlPacket) throws -> Data {
|
||||||
return packet.serialized()
|
return packet.serialized()
|
||||||
}
|
}
|
||||||
|
|
||||||
func deserialize(data packet: Data, start: Int, end: Int?) throws -> ControlPacket {
|
func deserialize(data packet: Data, start: Int, end: Int?) throws -> ControlPacket {
|
||||||
var offset = start
|
var offset = start
|
||||||
let end = end ?? packet.count
|
let end = end ?? packet.count
|
||||||
|
|
||||||
guard end >= offset + PacketOpcodeLength else {
|
guard end >= offset + PacketOpcodeLength else {
|
||||||
throw OpenVPN.ControlChannelError("Missing opcode")
|
throw OpenVPN.ControlChannelError("Missing opcode")
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ extension OpenVPN.ControlChannel {
|
||||||
offset += PacketOpcodeLength
|
offset += PacketOpcodeLength
|
||||||
|
|
||||||
log.debug("Control: Try read packet with code \(code) and key \(key)")
|
log.debug("Control: Try read packet with code \(code) and key \(key)")
|
||||||
|
|
||||||
guard end >= offset + PacketSessionIdLength else {
|
guard end >= offset + PacketSessionIdLength else {
|
||||||
throw OpenVPN.ControlChannelError("Missing sessionId")
|
throw OpenVPN.ControlChannelError("Missing sessionId")
|
||||||
}
|
}
|
||||||
|
@ -134,23 +134,23 @@ extension OpenVPN.ControlChannel {
|
||||||
extension OpenVPN.ControlChannel {
|
extension OpenVPN.ControlChannel {
|
||||||
class AuthSerializer: ControlChannelSerializer {
|
class AuthSerializer: ControlChannelSerializer {
|
||||||
private let encrypter: Encrypter
|
private let encrypter: Encrypter
|
||||||
|
|
||||||
private let decrypter: Decrypter
|
private let decrypter: Decrypter
|
||||||
|
|
||||||
private let prefixLength: Int
|
private let prefixLength: Int
|
||||||
|
|
||||||
private let hmacLength: Int
|
private let hmacLength: Int
|
||||||
|
|
||||||
private let authLength: Int
|
private let authLength: Int
|
||||||
|
|
||||||
private let preambleLength: Int
|
private let preambleLength: Int
|
||||||
|
|
||||||
private var currentReplayId: BidirectionalState<UInt32>
|
private var currentReplayId: BidirectionalState<UInt32>
|
||||||
|
|
||||||
private let timestamp: UInt32
|
private let timestamp: UInt32
|
||||||
|
|
||||||
private let plain: PlainSerializer
|
private let plain: PlainSerializer
|
||||||
|
|
||||||
init(withKey key: OpenVPN.StaticKey, digest: OpenVPN.Digest) throws {
|
init(withKey key: OpenVPN.StaticKey, digest: OpenVPN.Digest) throws {
|
||||||
let crypto = CryptoBox(cipherAlgorithm: nil, digestAlgorithm: digest.rawValue)
|
let crypto = CryptoBox(cipherAlgorithm: nil, digestAlgorithm: digest.rawValue)
|
||||||
try crypto.configure(
|
try crypto.configure(
|
||||||
|
@ -161,40 +161,40 @@ extension OpenVPN.ControlChannel {
|
||||||
)
|
)
|
||||||
encrypter = crypto.encrypter()
|
encrypter = crypto.encrypter()
|
||||||
decrypter = crypto.decrypter()
|
decrypter = crypto.decrypter()
|
||||||
|
|
||||||
prefixLength = PacketOpcodeLength + PacketSessionIdLength
|
prefixLength = PacketOpcodeLength + PacketSessionIdLength
|
||||||
hmacLength = crypto.digestLength()
|
hmacLength = crypto.digestLength()
|
||||||
authLength = hmacLength + PacketReplayIdLength + PacketReplayTimestampLength
|
authLength = hmacLength + PacketReplayIdLength + PacketReplayTimestampLength
|
||||||
preambleLength = prefixLength + authLength
|
preambleLength = prefixLength + authLength
|
||||||
|
|
||||||
currentReplayId = BidirectionalState(withResetValue: 1)
|
currentReplayId = BidirectionalState(withResetValue: 1)
|
||||||
timestamp = UInt32(Date().timeIntervalSince1970)
|
timestamp = UInt32(Date().timeIntervalSince1970)
|
||||||
plain = PlainSerializer()
|
plain = PlainSerializer()
|
||||||
}
|
}
|
||||||
|
|
||||||
func reset() {
|
func reset() {
|
||||||
currentReplayId.reset()
|
currentReplayId.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
func serialize(packet: ControlPacket) throws -> Data {
|
func serialize(packet: ControlPacket) throws -> Data {
|
||||||
return try serialize(packet: packet, timestamp: timestamp)
|
return try serialize(packet: packet, timestamp: timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func serialize(packet: ControlPacket, timestamp: UInt32) throws -> Data {
|
func serialize(packet: ControlPacket, timestamp: UInt32) throws -> Data {
|
||||||
let data = try packet.serialized(withAuthenticator: encrypter, replayId: currentReplayId.outbound, timestamp: timestamp)
|
let data = try packet.serialized(withAuthenticator: encrypter, replayId: currentReplayId.outbound, timestamp: timestamp)
|
||||||
currentReplayId.outbound += 1
|
currentReplayId.outbound += 1
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: start/end are ignored, parses whole packet
|
// XXX: start/end are ignored, parses whole packet
|
||||||
func deserialize(data packet: Data, start: Int, end: Int?) throws -> ControlPacket {
|
func deserialize(data packet: Data, start: Int, end: Int?) throws -> ControlPacket {
|
||||||
let end = packet.count
|
let end = packet.count
|
||||||
|
|
||||||
// data starts with (prefix=(header + sessionId) + auth=(hmac + replayId))
|
// data starts with (prefix=(header + sessionId) + auth=(hmac + replayId))
|
||||||
guard end >= preambleLength else {
|
guard end >= preambleLength else {
|
||||||
throw OpenVPN.ControlChannelError("Missing HMAC")
|
throw OpenVPN.ControlChannelError("Missing HMAC")
|
||||||
}
|
}
|
||||||
|
|
||||||
// needs a copy for swapping
|
// needs a copy for swapping
|
||||||
var authPacket = packet
|
var authPacket = packet
|
||||||
let authCount = authPacket.count
|
let authCount = authPacket.count
|
||||||
|
@ -203,9 +203,9 @@ extension OpenVPN.ControlChannel {
|
||||||
PacketSwapCopy(ptr, packet, prefixLength, authLength)
|
PacketSwapCopy(ptr, packet, prefixLength, authLength)
|
||||||
try decrypter.verifyBytes(ptr, length: authCount, flags: nil)
|
try decrypter.verifyBytes(ptr, length: authCount, flags: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: validate replay packet id
|
// TODO: validate replay packet id
|
||||||
|
|
||||||
return try plain.deserialize(data: authPacket, start: authLength, end: nil)
|
return try plain.deserialize(data: authPacket, start: authLength, end: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,19 +214,19 @@ extension OpenVPN.ControlChannel {
|
||||||
extension OpenVPN.ControlChannel {
|
extension OpenVPN.ControlChannel {
|
||||||
class CryptSerializer: ControlChannelSerializer {
|
class CryptSerializer: ControlChannelSerializer {
|
||||||
private let encrypter: Encrypter
|
private let encrypter: Encrypter
|
||||||
|
|
||||||
private let decrypter: Decrypter
|
private let decrypter: Decrypter
|
||||||
|
|
||||||
private let headerLength: Int
|
private let headerLength: Int
|
||||||
|
|
||||||
private var adLength: Int
|
private var adLength: Int
|
||||||
|
|
||||||
private let tagLength: Int
|
private let tagLength: Int
|
||||||
|
|
||||||
private var currentReplayId: BidirectionalState<UInt32>
|
private var currentReplayId: BidirectionalState<UInt32>
|
||||||
|
|
||||||
private let timestamp: UInt32
|
private let timestamp: UInt32
|
||||||
|
|
||||||
private let plain: PlainSerializer
|
private let plain: PlainSerializer
|
||||||
|
|
||||||
init(withKey key: OpenVPN.StaticKey) throws {
|
init(withKey key: OpenVPN.StaticKey) throws {
|
||||||
|
@ -239,7 +239,7 @@ extension OpenVPN.ControlChannel {
|
||||||
)
|
)
|
||||||
encrypter = crypto.encrypter()
|
encrypter = crypto.encrypter()
|
||||||
decrypter = crypto.decrypter()
|
decrypter = crypto.decrypter()
|
||||||
|
|
||||||
headerLength = PacketOpcodeLength + PacketSessionIdLength
|
headerLength = PacketOpcodeLength + PacketSessionIdLength
|
||||||
adLength = headerLength + PacketReplayIdLength + PacketReplayTimestampLength
|
adLength = headerLength + PacketReplayIdLength + PacketReplayTimestampLength
|
||||||
tagLength = crypto.tagLength()
|
tagLength = crypto.tagLength()
|
||||||
|
@ -248,30 +248,30 @@ extension OpenVPN.ControlChannel {
|
||||||
timestamp = UInt32(Date().timeIntervalSince1970)
|
timestamp = UInt32(Date().timeIntervalSince1970)
|
||||||
plain = PlainSerializer()
|
plain = PlainSerializer()
|
||||||
}
|
}
|
||||||
|
|
||||||
func reset() {
|
func reset() {
|
||||||
currentReplayId.reset()
|
currentReplayId.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
func serialize(packet: ControlPacket) throws -> Data {
|
func serialize(packet: ControlPacket) throws -> Data {
|
||||||
return try serialize(packet: packet, timestamp: timestamp)
|
return try serialize(packet: packet, timestamp: timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func serialize(packet: ControlPacket, timestamp: UInt32) throws -> Data {
|
func serialize(packet: ControlPacket, timestamp: UInt32) throws -> Data {
|
||||||
let data = try packet.serialized(with: encrypter, replayId: currentReplayId.outbound, timestamp: timestamp, adLength: adLength)
|
let data = try packet.serialized(with: encrypter, replayId: currentReplayId.outbound, timestamp: timestamp, adLength: adLength)
|
||||||
currentReplayId.outbound += 1
|
currentReplayId.outbound += 1
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: start/end are ignored, parses whole packet
|
// XXX: start/end are ignored, parses whole packet
|
||||||
func deserialize(data packet: Data, start: Int, end: Int?) throws -> ControlPacket {
|
func deserialize(data packet: Data, start: Int, end: Int?) throws -> ControlPacket {
|
||||||
let end = end ?? packet.count
|
let end = end ?? packet.count
|
||||||
|
|
||||||
// data starts with (ad=(header + sessionId + replayId) + tag)
|
// data starts with (ad=(header + sessionId + replayId) + tag)
|
||||||
guard end >= start + adLength + tagLength else {
|
guard end >= start + adLength + tagLength else {
|
||||||
throw OpenVPN.ControlChannelError("Missing AD+TAG")
|
throw OpenVPN.ControlChannelError("Missing AD+TAG")
|
||||||
}
|
}
|
||||||
|
|
||||||
let encryptedCount = packet.count - adLength
|
let encryptedCount = packet.count - adLength
|
||||||
var decryptedPacket = Data(count: decrypter.encryptionCapacity(withLength: encryptedCount))
|
var decryptedPacket = Data(count: decrypter.encryptionCapacity(withLength: encryptedCount))
|
||||||
var decryptedCount = 0
|
var decryptedCount = 0
|
||||||
|
@ -285,9 +285,9 @@ extension OpenVPN.ControlChannel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
decryptedPacket.count = headerLength + decryptedCount
|
decryptedPacket.count = headerLength + decryptedCount
|
||||||
|
|
||||||
// TODO: validate replay packet id
|
// TODO: validate replay packet id
|
||||||
|
|
||||||
return try plain.deserialize(data: decryptedPacket, start: 0, end: nil)
|
return try plain.deserialize(data: decryptedPacket, start: 0, end: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,29 +41,29 @@ import CTunnelKitOpenVPNProtocol
|
||||||
|
|
||||||
extension CoreConfiguration {
|
extension CoreConfiguration {
|
||||||
struct OpenVPN {
|
struct OpenVPN {
|
||||||
|
|
||||||
// MARK: Session
|
// MARK: Session
|
||||||
|
|
||||||
static let usesReplayProtection = true
|
static let usesReplayProtection = true
|
||||||
|
|
||||||
static let negotiationTimeout = 30.0
|
static let negotiationTimeout = 30.0
|
||||||
|
|
||||||
static let hardResetTimeout = 10.0
|
static let hardResetTimeout = 10.0
|
||||||
|
|
||||||
static let tickInterval = 0.2
|
static let tickInterval = 0.2
|
||||||
|
|
||||||
static let pushRequestInterval = 2.0
|
static let pushRequestInterval = 2.0
|
||||||
|
|
||||||
static let pingTimeoutCheckInterval = 10.0
|
static let pingTimeoutCheckInterval = 10.0
|
||||||
|
|
||||||
static let pingTimeout = 120.0
|
static let pingTimeout = 120.0
|
||||||
|
|
||||||
static let retransmissionLimit = 0.1
|
static let retransmissionLimit = 0.1
|
||||||
|
|
||||||
static let softNegotiationTimeout = 120.0
|
static let softNegotiationTimeout = 120.0
|
||||||
|
|
||||||
// MARK: Authentication
|
// MARK: Authentication
|
||||||
|
|
||||||
static func peerInfo(extra: [String: String]? = nil) -> String {
|
static func peerInfo(extra: [String: String]? = nil) -> String {
|
||||||
let platform: String
|
let platform: String
|
||||||
let platformVersion = ProcessInfo.processInfo.operatingSystemVersion
|
let platformVersion = ProcessInfo.processInfo.operatingSystemVersion
|
||||||
|
@ -79,7 +79,7 @@ extension CoreConfiguration {
|
||||||
"IV_UI_VER=\(uiVersion)",
|
"IV_UI_VER=\(uiVersion)",
|
||||||
"IV_PROTO=2",
|
"IV_PROTO=2",
|
||||||
"IV_NCP=2",
|
"IV_NCP=2",
|
||||||
"IV_LZO_STUB=1",
|
"IV_LZO_STUB=1"
|
||||||
]
|
]
|
||||||
if LZOFactory.isSupported() {
|
if LZOFactory.isSupported() {
|
||||||
info.append("IV_LZO=1")
|
info.append("IV_LZO=1")
|
||||||
|
@ -97,19 +97,19 @@ extension CoreConfiguration {
|
||||||
info.append("")
|
info.append("")
|
||||||
return info.joined(separator: "\n")
|
return info.joined(separator: "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
static let randomLength = 32
|
static let randomLength = 32
|
||||||
|
|
||||||
// MARK: Keys
|
// MARK: Keys
|
||||||
|
|
||||||
static let label1 = "OpenVPN master secret"
|
static let label1 = "OpenVPN master secret"
|
||||||
|
|
||||||
static let label2 = "OpenVPN key expansion"
|
static let label2 = "OpenVPN key expansion"
|
||||||
|
|
||||||
static let preMasterLength = 48
|
static let preMasterLength = 48
|
||||||
|
|
||||||
static let keyLength = 64
|
static let keyLength = 64
|
||||||
|
|
||||||
static let keysCount = 4
|
static let keysCount = 4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,9 +43,9 @@ import CTunnelKitOpenVPNProtocol
|
||||||
extension OpenVPN {
|
extension OpenVPN {
|
||||||
class EncryptionBridge {
|
class EncryptionBridge {
|
||||||
private static let maxHmacLength = 100
|
private static let maxHmacLength = 100
|
||||||
|
|
||||||
private let box: CryptoBox
|
private let box: CryptoBox
|
||||||
|
|
||||||
// Ruby: keys_prf
|
// Ruby: keys_prf
|
||||||
private static func keysPRF(
|
private static func keysPRF(
|
||||||
_ label: String,
|
_ label: String,
|
||||||
|
@ -55,7 +55,7 @@ extension OpenVPN {
|
||||||
_ clientSessionId: Data?,
|
_ clientSessionId: Data?,
|
||||||
_ serverSessionId: Data?,
|
_ serverSessionId: Data?,
|
||||||
_ size: Int) throws -> ZeroingData {
|
_ size: Int) throws -> ZeroingData {
|
||||||
|
|
||||||
let seed = Z(label, nullTerminated: false)
|
let seed = Z(label, nullTerminated: false)
|
||||||
seed.append(clientSeed)
|
seed.append(clientSeed)
|
||||||
seed.append(serverSeed)
|
seed.append(serverSeed)
|
||||||
|
@ -69,36 +69,36 @@ extension OpenVPN {
|
||||||
let lenx = len + (secret.count & 1)
|
let lenx = len + (secret.count & 1)
|
||||||
let secret1 = secret.withOffset(0, count: lenx)
|
let secret1 = secret.withOffset(0, count: lenx)
|
||||||
let secret2 = secret.withOffset(len, count: lenx)
|
let secret2 = secret.withOffset(len, count: lenx)
|
||||||
|
|
||||||
let hash1 = try keysHash("md5", secret1, seed, size)
|
let hash1 = try keysHash("md5", secret1, seed, size)
|
||||||
let hash2 = try keysHash("sha1", secret2, seed, size)
|
let hash2 = try keysHash("sha1", secret2, seed, size)
|
||||||
|
|
||||||
let prf = Z()
|
let prf = Z()
|
||||||
for i in 0..<hash1.count {
|
for i in 0..<hash1.count {
|
||||||
let h1 = hash1.bytes[i]
|
let h1 = hash1.bytes[i]
|
||||||
let h2 = hash2.bytes[i]
|
let h2 = hash2.bytes[i]
|
||||||
|
|
||||||
prf.append(Z(h1 ^ h2))
|
prf.append(Z(h1 ^ h2))
|
||||||
}
|
}
|
||||||
return prf
|
return prf
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: keys_hash
|
// Ruby: keys_hash
|
||||||
private static func keysHash(_ digestName: String, _ secret: ZeroingData, _ seed: ZeroingData, _ size: Int) throws -> ZeroingData {
|
private static func keysHash(_ digestName: String, _ secret: ZeroingData, _ seed: ZeroingData, _ size: Int) throws -> ZeroingData {
|
||||||
let out = Z()
|
let out = Z()
|
||||||
let buffer = Z(count: EncryptionBridge.maxHmacLength)
|
let buffer = Z(count: EncryptionBridge.maxHmacLength)
|
||||||
var chain = try EncryptionBridge.hmac(buffer, digestName, secret, seed)
|
var chain = try EncryptionBridge.hmac(buffer, digestName, secret, seed)
|
||||||
while (out.count < size) {
|
while out.count < size {
|
||||||
out.append(try EncryptionBridge.hmac(buffer, digestName, secret, chain.appending(seed)))
|
out.append(try EncryptionBridge.hmac(buffer, digestName, secret, chain.appending(seed)))
|
||||||
chain = try EncryptionBridge.hmac(buffer, digestName, secret, chain)
|
chain = try EncryptionBridge.hmac(buffer, digestName, secret, chain)
|
||||||
}
|
}
|
||||||
return out.withOffset(0, count: size)
|
return out.withOffset(0, count: size)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: hmac
|
// Ruby: hmac
|
||||||
private static func hmac(_ buffer: ZeroingData, _ digestName: String, _ secret: ZeroingData, _ data: ZeroingData) throws -> ZeroingData {
|
private static func hmac(_ buffer: ZeroingData, _ digestName: String, _ secret: ZeroingData, _ data: ZeroingData) throws -> ZeroingData {
|
||||||
var length = 0
|
var length = 0
|
||||||
|
|
||||||
try CryptoBox.hmac(
|
try CryptoBox.hmac(
|
||||||
withDigestName: digestName,
|
withDigestName: digestName,
|
||||||
secret: secret.bytes,
|
secret: secret.bytes,
|
||||||
|
@ -108,44 +108,44 @@ extension OpenVPN {
|
||||||
hmac: buffer.mutableBytes,
|
hmac: buffer.mutableBytes,
|
||||||
hmacLength: &length
|
hmacLength: &length
|
||||||
)
|
)
|
||||||
|
|
||||||
return buffer.withOffset(0, count: length)
|
return buffer.withOffset(0, count: length)
|
||||||
}
|
}
|
||||||
|
|
||||||
convenience init(_ cipher: Cipher, _ digest: Digest, _ auth: Authenticator,
|
convenience init(_ cipher: Cipher, _ digest: Digest, _ auth: Authenticator,
|
||||||
_ sessionId: Data, _ remoteSessionId: Data) throws {
|
_ sessionId: Data, _ remoteSessionId: Data) throws {
|
||||||
|
|
||||||
guard let serverRandom1 = auth.serverRandom1, let serverRandom2 = auth.serverRandom2 else {
|
guard let serverRandom1 = auth.serverRandom1, let serverRandom2 = auth.serverRandom2 else {
|
||||||
fatalError("Configuring encryption without server randoms")
|
fatalError("Configuring encryption without server randoms")
|
||||||
}
|
}
|
||||||
|
|
||||||
let masterData = try EncryptionBridge.keysPRF(
|
let masterData = try EncryptionBridge.keysPRF(
|
||||||
CoreConfiguration.OpenVPN.label1, auth.preMaster, auth.random1,
|
CoreConfiguration.OpenVPN.label1, auth.preMaster, auth.random1,
|
||||||
serverRandom1, nil, nil,
|
serverRandom1, nil, nil,
|
||||||
CoreConfiguration.OpenVPN.preMasterLength
|
CoreConfiguration.OpenVPN.preMasterLength
|
||||||
)
|
)
|
||||||
|
|
||||||
let keysData = try EncryptionBridge.keysPRF(
|
let keysData = try EncryptionBridge.keysPRF(
|
||||||
CoreConfiguration.OpenVPN.label2, masterData, auth.random2,
|
CoreConfiguration.OpenVPN.label2, masterData, auth.random2,
|
||||||
serverRandom2, sessionId, remoteSessionId,
|
serverRandom2, sessionId, remoteSessionId,
|
||||||
CoreConfiguration.OpenVPN.keysCount * CoreConfiguration.OpenVPN.keyLength
|
CoreConfiguration.OpenVPN.keysCount * CoreConfiguration.OpenVPN.keyLength
|
||||||
)
|
)
|
||||||
|
|
||||||
var keysArray = [ZeroingData]()
|
var keysArray = [ZeroingData]()
|
||||||
for i in 0..<CoreConfiguration.OpenVPN.keysCount {
|
for i in 0..<CoreConfiguration.OpenVPN.keysCount {
|
||||||
let offset = i * CoreConfiguration.OpenVPN.keyLength
|
let offset = i * CoreConfiguration.OpenVPN.keyLength
|
||||||
let zbuf = keysData.withOffset(offset, count: CoreConfiguration.OpenVPN.keyLength)
|
let zbuf = keysData.withOffset(offset, count: CoreConfiguration.OpenVPN.keyLength)
|
||||||
keysArray.append(zbuf)
|
keysArray.append(zbuf)
|
||||||
}
|
}
|
||||||
|
|
||||||
let cipherEncKey = keysArray[0]
|
let cipherEncKey = keysArray[0]
|
||||||
let hmacEncKey = keysArray[1]
|
let hmacEncKey = keysArray[1]
|
||||||
let cipherDecKey = keysArray[2]
|
let cipherDecKey = keysArray[2]
|
||||||
let hmacDecKey = keysArray[3]
|
let hmacDecKey = keysArray[3]
|
||||||
|
|
||||||
try self.init(cipher, digest, cipherEncKey, cipherDecKey, hmacEncKey, hmacDecKey)
|
try self.init(cipher, digest, cipherEncKey, cipherDecKey, hmacEncKey, hmacDecKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
init(_ cipher: Cipher, _ digest: Digest, _ cipherEncKey: ZeroingData, _ cipherDecKey: ZeroingData, _ hmacEncKey: ZeroingData, _ hmacDecKey: ZeroingData) throws {
|
init(_ cipher: Cipher, _ digest: Digest, _ cipherEncKey: ZeroingData, _ cipherDecKey: ZeroingData, _ hmacEncKey: ZeroingData, _ hmacDecKey: ZeroingData) throws {
|
||||||
box = CryptoBox(cipherAlgorithm: cipher.rawValue, digestAlgorithm: digest.rawValue)
|
box = CryptoBox(cipherAlgorithm: cipher.rawValue, digestAlgorithm: digest.rawValue)
|
||||||
try box.configure(
|
try box.configure(
|
||||||
|
@ -155,7 +155,7 @@ extension OpenVPN {
|
||||||
hmacDecKey: hmacDecKey
|
hmacDecKey: hmacDecKey
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func encrypter() -> DataPathEncrypter {
|
func encrypter() -> DataPathEncrypter {
|
||||||
return box.encrypter().dataPathEncrypter()
|
return box.encrypter().dataPathEncrypter()
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,35 +41,35 @@ import TunnelKitOpenVPNCore
|
||||||
extension OpenVPNSession {
|
extension OpenVPNSession {
|
||||||
struct PIAHardReset {
|
struct PIAHardReset {
|
||||||
private static let obfuscationKeyLength = 3
|
private static let obfuscationKeyLength = 3
|
||||||
|
|
||||||
private static let magic = "53eo0rk92gxic98p1asgl5auh59r1vp4lmry1e3chzi100qntd"
|
private static let magic = "53eo0rk92gxic98p1asgl5auh59r1vp4lmry1e3chzi100qntd"
|
||||||
|
|
||||||
private static let encodedFormat = "\(magic)crypto\t%@|%@\tca\t%@"
|
private static let encodedFormat = "\(magic)crypto\t%@|%@\tca\t%@"
|
||||||
|
|
||||||
private let caMd5Digest: String
|
private let caMd5Digest: String
|
||||||
|
|
||||||
private let cipherName: String
|
private let cipherName: String
|
||||||
|
|
||||||
private let digestName: String
|
private let digestName: String
|
||||||
|
|
||||||
init(caMd5Digest: String, cipher: OpenVPN.Cipher, digest: OpenVPN.Digest) {
|
init(caMd5Digest: String, cipher: OpenVPN.Cipher, digest: OpenVPN.Digest) {
|
||||||
self.caMd5Digest = caMd5Digest
|
self.caMd5Digest = caMd5Digest
|
||||||
cipherName = cipher.rawValue.lowercased()
|
cipherName = cipher.rawValue.lowercased()
|
||||||
digestName = digest.rawValue.lowercased()
|
digestName = digest.rawValue.lowercased()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: pia_settings
|
// Ruby: pia_settings
|
||||||
func encodedData() throws -> Data {
|
func encodedData() throws -> Data {
|
||||||
guard let plainData = String(format: PIAHardReset.encodedFormat, cipherName, digestName, caMd5Digest).data(using: .ascii) else {
|
guard let plainData = String(format: PIAHardReset.encodedFormat, cipherName, digestName, caMd5Digest).data(using: .ascii) else {
|
||||||
fatalError("Unable to encode string to ASCII")
|
fatalError("Unable to encode string to ASCII")
|
||||||
}
|
}
|
||||||
let keyBytes = try SecureRandom.data(length: PIAHardReset.obfuscationKeyLength)
|
let keyBytes = try SecureRandom.data(length: PIAHardReset.obfuscationKeyLength)
|
||||||
|
|
||||||
var encodedData = Data(keyBytes)
|
var encodedData = Data(keyBytes)
|
||||||
for (i, b) in plainData.enumerated() {
|
for (i, b) in plainData.enumerated() {
|
||||||
let keyChar = keyBytes[i % keyBytes.count]
|
let keyChar = keyBytes[i % keyBytes.count]
|
||||||
let xorredB = b ^ keyChar
|
let xorredB = b ^ keyChar
|
||||||
|
|
||||||
encodedData.append(xorredB)
|
encodedData.append(xorredB)
|
||||||
}
|
}
|
||||||
return encodedData
|
return encodedData
|
||||||
|
|
|
@ -45,7 +45,7 @@ private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
/// Observes major events notified by a `OpenVPNSession`.
|
/// Observes major events notified by a `OpenVPNSession`.
|
||||||
public protocol OpenVPNSessionDelegate: AnyObject {
|
public protocol OpenVPNSessionDelegate: AnyObject {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Called after starting a session.
|
Called after starting a session.
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ public protocol OpenVPNSessionDelegate: AnyObject {
|
||||||
- Parameter options: The pulled tunnel settings.
|
- Parameter options: The pulled tunnel settings.
|
||||||
*/
|
*/
|
||||||
func sessionDidStart(_: OpenVPNSession, remoteAddress: String, remoteProtocol: String?, options: OpenVPN.Configuration)
|
func sessionDidStart(_: OpenVPNSession, remoteAddress: String, remoteProtocol: String?, options: OpenVPN.Configuration)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Called after stopping a session.
|
Called after stopping a session.
|
||||||
|
|
||||||
|
@ -69,22 +69,22 @@ public protocol OpenVPNSessionDelegate: AnyObject {
|
||||||
public class OpenVPNSession: Session {
|
public class OpenVPNSession: Session {
|
||||||
private enum StopMethod {
|
private enum StopMethod {
|
||||||
case shutdown
|
case shutdown
|
||||||
|
|
||||||
case reconnect
|
case reconnect
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct Caches {
|
private struct Caches {
|
||||||
static let ca = "ca.pem"
|
static let ca = "ca.pem"
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Configuration
|
// MARK: Configuration
|
||||||
|
|
||||||
/// The session base configuration.
|
/// The session base configuration.
|
||||||
public let configuration: OpenVPN.Configuration
|
public let configuration: OpenVPN.Configuration
|
||||||
|
|
||||||
/// The optional credentials.
|
/// The optional credentials.
|
||||||
public var credentials: OpenVPN.Credentials?
|
public var credentials: OpenVPN.Credentials?
|
||||||
|
|
||||||
private var keepAliveInterval: TimeInterval? {
|
private var keepAliveInterval: TimeInterval? {
|
||||||
let interval: TimeInterval?
|
let interval: TimeInterval?
|
||||||
if let negInterval = pushReply?.options.keepAliveInterval, negInterval > 0.0 {
|
if let negInterval = pushReply?.options.keepAliveInterval, negInterval > 0.0 {
|
||||||
|
@ -96,7 +96,7 @@ public class OpenVPNSession: Session {
|
||||||
}
|
}
|
||||||
return interval
|
return interval
|
||||||
}
|
}
|
||||||
|
|
||||||
private var keepAliveTimeout: TimeInterval {
|
private var keepAliveTimeout: TimeInterval {
|
||||||
if let negTimeout = pushReply?.options.keepAliveTimeout, negTimeout > 0.0 {
|
if let negTimeout = pushReply?.options.keepAliveTimeout, negTimeout > 0.0 {
|
||||||
return negTimeout
|
return negTimeout
|
||||||
|
@ -106,16 +106,16 @@ public class OpenVPNSession: Session {
|
||||||
return CoreConfiguration.OpenVPN.pingTimeout
|
return CoreConfiguration.OpenVPN.pingTimeout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An optional `OpenVPNSessionDelegate` for receiving session events.
|
/// An optional `OpenVPNSessionDelegate` for receiving session events.
|
||||||
public weak var delegate: OpenVPNSessionDelegate?
|
public weak var delegate: OpenVPNSessionDelegate?
|
||||||
|
|
||||||
// MARK: State
|
// MARK: State
|
||||||
|
|
||||||
private let queue: DispatchQueue
|
private let queue: DispatchQueue
|
||||||
|
|
||||||
private var tlsObserver: NSObjectProtocol?
|
private var tlsObserver: NSObjectProtocol?
|
||||||
|
|
||||||
private var withLocalOptions: Bool
|
private var withLocalOptions: Bool
|
||||||
|
|
||||||
private var keys: [UInt8: OpenVPN.SessionKey]
|
private var keys: [UInt8: OpenVPN.SessionKey]
|
||||||
|
@ -123,29 +123,29 @@ public class OpenVPNSession: Session {
|
||||||
private var oldKeys: [OpenVPN.SessionKey]
|
private var oldKeys: [OpenVPN.SessionKey]
|
||||||
|
|
||||||
private var negotiationKeyIdx: UInt8
|
private var negotiationKeyIdx: UInt8
|
||||||
|
|
||||||
private var currentKeyIdx: UInt8?
|
private var currentKeyIdx: UInt8?
|
||||||
|
|
||||||
private var isRenegotiating: Bool
|
private var isRenegotiating: Bool
|
||||||
|
|
||||||
private var negotiationKey: OpenVPN.SessionKey {
|
private var negotiationKey: OpenVPN.SessionKey {
|
||||||
guard let key = keys[negotiationKeyIdx] else {
|
guard let key = keys[negotiationKeyIdx] else {
|
||||||
fatalError("Keys are empty or index \(negotiationKeyIdx) not found in \(keys.keys)")
|
fatalError("Keys are empty or index \(negotiationKeyIdx) not found in \(keys.keys)")
|
||||||
}
|
}
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
||||||
private var currentKey: OpenVPN.SessionKey? {
|
private var currentKey: OpenVPN.SessionKey? {
|
||||||
guard let i = currentKeyIdx else {
|
guard let i = currentKeyIdx else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return keys[i]
|
return keys[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
private var link: LinkInterface?
|
private var link: LinkInterface?
|
||||||
|
|
||||||
private var tunnel: TunnelInterface?
|
private var tunnel: TunnelInterface?
|
||||||
|
|
||||||
private var isReliableLink: Bool {
|
private var isReliableLink: Bool {
|
||||||
return link?.isReliable ?? false
|
return link?.isReliable ?? false
|
||||||
}
|
}
|
||||||
|
@ -153,24 +153,24 @@ public class OpenVPNSession: Session {
|
||||||
private var continuatedPushReplyMessage: String?
|
private var continuatedPushReplyMessage: String?
|
||||||
|
|
||||||
private var pushReply: OpenVPN.PushReply?
|
private var pushReply: OpenVPN.PushReply?
|
||||||
|
|
||||||
private var nextPushRequestDate: Date?
|
private var nextPushRequestDate: Date?
|
||||||
|
|
||||||
private var connectedDate: Date?
|
private var connectedDate: Date?
|
||||||
|
|
||||||
private var lastPing: BidirectionalState<Date>
|
private var lastPing: BidirectionalState<Date>
|
||||||
|
|
||||||
private(set) var isStopping: Bool
|
private(set) var isStopping: Bool
|
||||||
|
|
||||||
/// The optional reason why the session stopped.
|
/// The optional reason why the session stopped.
|
||||||
public private(set) var stopError: Error?
|
public private(set) var stopError: Error?
|
||||||
|
|
||||||
// MARK: Control
|
// MARK: Control
|
||||||
|
|
||||||
private var controlChannel: OpenVPN.ControlChannel
|
private var controlChannel: OpenVPN.ControlChannel
|
||||||
|
|
||||||
private var authenticator: OpenVPN.Authenticator?
|
private var authenticator: OpenVPN.Authenticator?
|
||||||
|
|
||||||
// MARK: Caching
|
// MARK: Caching
|
||||||
|
|
||||||
private let cachesURL: URL
|
private let cachesURL: URL
|
||||||
|
@ -191,7 +191,7 @@ public class OpenVPNSession: Session {
|
||||||
guard let ca = configuration.ca else {
|
guard let ca = configuration.ca else {
|
||||||
throw OpenVPN.ConfigurationError.missingConfiguration(option: "ca")
|
throw OpenVPN.ConfigurationError.missingConfiguration(option: "ca")
|
||||||
}
|
}
|
||||||
|
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
self.configuration = configuration
|
self.configuration = configuration
|
||||||
self.cachesURL = cachesURL
|
self.cachesURL = cachesURL
|
||||||
|
@ -203,7 +203,7 @@ public class OpenVPNSession: Session {
|
||||||
isRenegotiating = false
|
isRenegotiating = false
|
||||||
lastPing = BidirectionalState(withResetValue: Date.distantPast)
|
lastPing = BidirectionalState(withResetValue: Date.distantPast)
|
||||||
isStopping = false
|
isStopping = false
|
||||||
|
|
||||||
if let tlsWrap = configuration.tlsWrap {
|
if let tlsWrap = configuration.tlsWrap {
|
||||||
switch tlsWrap.strategy {
|
switch tlsWrap.strategy {
|
||||||
case .auth:
|
case .auth:
|
||||||
|
@ -219,22 +219,22 @@ public class OpenVPNSession: Session {
|
||||||
// cache CA locally (mandatory for OpenSSL)
|
// cache CA locally (mandatory for OpenSSL)
|
||||||
try ca.pem.write(to: caURL, atomically: true, encoding: .ascii)
|
try ca.pem.write(to: caURL, atomically: true, encoding: .ascii)
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
cleanup()
|
cleanup()
|
||||||
cleanupCache()
|
cleanupCache()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Session
|
// MARK: Session
|
||||||
|
|
||||||
public func setLink(_ link: LinkInterface) {
|
public func setLink(_ link: LinkInterface) {
|
||||||
guard (self.link == nil) else {
|
guard self.link == nil else {
|
||||||
log.warning("Link interface already set!")
|
log.warning("Link interface already set!")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("Starting VPN session")
|
log.debug("Starting VPN session")
|
||||||
|
|
||||||
// WARNING: runs in notification source queue (we know it's "queue", but better be safe than sorry)
|
// WARNING: runs in notification source queue (we know it's "queue", but better be safe than sorry)
|
||||||
tlsObserver = NotificationCenter.default.addObserver(forName: .TLSBoxPeerVerificationError, object: nil, queue: nil) { (notification) in
|
tlsObserver = NotificationCenter.default.addObserver(forName: .TLSBoxPeerVerificationError, object: nil, queue: nil) { (notification) in
|
||||||
let error = notification.userInfo?[OpenVPNErrorKey] as? Error
|
let error = notification.userInfo?[OpenVPNErrorKey] as? Error
|
||||||
|
@ -242,18 +242,18 @@ public class OpenVPNSession: Session {
|
||||||
self.deferStop(.shutdown, error)
|
self.deferStop(.shutdown, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.link = link
|
self.link = link
|
||||||
start()
|
start()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func canRebindLink() -> Bool {
|
public func canRebindLink() -> Bool {
|
||||||
// return (pushReply?.peerId != nil)
|
// return (pushReply?.peerId != nil)
|
||||||
|
|
||||||
// FIXME: floating is currently unreliable
|
// FIXME: floating is currently unreliable
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
public func rebindLink(_ link: LinkInterface) {
|
public func rebindLink(_ link: LinkInterface) {
|
||||||
guard let _ = pushReply?.options.peerId else {
|
guard let _ = pushReply?.options.peerId else {
|
||||||
log.warning("Session doesn't support link rebinding!")
|
log.warning("Session doesn't support link rebinding!")
|
||||||
|
@ -269,7 +269,7 @@ public class OpenVPNSession: Session {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func setTunnel(tunnel: TunnelInterface) {
|
public func setTunnel(tunnel: TunnelInterface) {
|
||||||
guard (self.tunnel == nil) else {
|
guard self.tunnel == nil else {
|
||||||
log.warning("Tunnel interface already set!")
|
log.warning("Tunnel interface already set!")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -283,11 +283,11 @@ public class OpenVPNSession: Session {
|
||||||
}
|
}
|
||||||
return controlChannel.currentDataCount()
|
return controlChannel.currentDataCount()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func serverConfiguration() -> Any? {
|
public func serverConfiguration() -> Any? {
|
||||||
return pushReply?.options
|
return pushReply?.options
|
||||||
}
|
}
|
||||||
|
|
||||||
public func shutdown(error: Error?) {
|
public func shutdown(error: Error?) {
|
||||||
guard !isStopping else {
|
guard !isStopping else {
|
||||||
log.warning("Ignore stop request, already stopping!")
|
log.warning("Ignore stop request, already stopping!")
|
||||||
|
@ -295,7 +295,7 @@ public class OpenVPNSession: Session {
|
||||||
}
|
}
|
||||||
deferStop(.shutdown, error)
|
deferStop(.shutdown, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func reconnect(error: Error?) {
|
public func reconnect(error: Error?) {
|
||||||
guard !isStopping else {
|
guard !isStopping else {
|
||||||
log.warning("Ignore stop request, already stopping!")
|
log.warning("Ignore stop request, already stopping!")
|
||||||
|
@ -303,7 +303,7 @@ public class OpenVPNSession: Session {
|
||||||
}
|
}
|
||||||
deferStop(.reconnect, error)
|
deferStop(.reconnect, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: cleanup
|
// Ruby: cleanup
|
||||||
public func cleanup() {
|
public func cleanup() {
|
||||||
log.info("Cleaning up...")
|
log.info("Cleaning up...")
|
||||||
|
@ -312,13 +312,13 @@ public class OpenVPNSession: Session {
|
||||||
NotificationCenter.default.removeObserver(observer)
|
NotificationCenter.default.removeObserver(observer)
|
||||||
tlsObserver = nil
|
tlsObserver = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
keys.removeAll()
|
keys.removeAll()
|
||||||
oldKeys.removeAll()
|
oldKeys.removeAll()
|
||||||
negotiationKeyIdx = 0
|
negotiationKeyIdx = 0
|
||||||
currentKeyIdx = nil
|
currentKeyIdx = nil
|
||||||
isRenegotiating = false
|
isRenegotiating = false
|
||||||
|
|
||||||
nextPushRequestDate = nil
|
nextPushRequestDate = nil
|
||||||
connectedDate = nil
|
connectedDate = nil
|
||||||
authenticator = nil
|
authenticator = nil
|
||||||
|
@ -328,7 +328,7 @@ public class OpenVPNSession: Session {
|
||||||
if !(tunnel?.isPersistent ?? false) {
|
if !(tunnel?.isPersistent ?? false) {
|
||||||
tunnel = nil
|
tunnel = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
isStopping = false
|
isStopping = false
|
||||||
stopError = nil
|
stopError = nil
|
||||||
}
|
}
|
||||||
|
@ -347,7 +347,7 @@ public class OpenVPNSession: Session {
|
||||||
loopLink()
|
loopLink()
|
||||||
hardReset()
|
hardReset()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loopNegotiation() {
|
private func loopNegotiation() {
|
||||||
guard let link = link else {
|
guard let link = link else {
|
||||||
return
|
return
|
||||||
|
@ -364,12 +364,12 @@ public class OpenVPNSession: Session {
|
||||||
doShutdown(error: OpenVPNError.negotiationTimeout)
|
doShutdown(error: OpenVPNError.negotiationTimeout)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pushRequest()
|
pushRequest()
|
||||||
if !isReliableLink {
|
if !isReliableLink {
|
||||||
flushControlQueue()
|
flushControlQueue()
|
||||||
}
|
}
|
||||||
|
|
||||||
guard negotiationKey.controlState == .connected else {
|
guard negotiationKey.controlState == .connected else {
|
||||||
queue.asyncAfter(deadline: .now() + CoreConfiguration.OpenVPN.tickInterval) { [weak self] in
|
queue.asyncAfter(deadline: .now() + CoreConfiguration.OpenVPN.tickInterval) { [weak self] in
|
||||||
self?.loopNegotiation()
|
self?.loopNegotiation()
|
||||||
|
@ -390,11 +390,11 @@ public class OpenVPNSession: Session {
|
||||||
}
|
}
|
||||||
if let error = error {
|
if let error = error {
|
||||||
log.error("Failed LINK read: \(error)")
|
log.error("Failed LINK read: \(error)")
|
||||||
|
|
||||||
// XXX: why isn't the tunnel shutting down at this point?
|
// XXX: why isn't the tunnel shutting down at this point?
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let packets = newPackets, !packets.isEmpty {
|
if let packets = newPackets, !packets.isEmpty {
|
||||||
self?.maybeRenegotiate()
|
self?.maybeRenegotiate()
|
||||||
|
|
||||||
|
@ -425,11 +425,11 @@ public class OpenVPNSession: Session {
|
||||||
log.warning("Discarding \(packets.count) LINK packets (should not handle)")
|
log.warning("Discarding \(packets.count) LINK packets (should not handle)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
lastPing.inbound = Date()
|
lastPing.inbound = Date()
|
||||||
|
|
||||||
var dataPacketsByKey = [UInt8: [Data]]()
|
var dataPacketsByKey = [UInt8: [Data]]()
|
||||||
|
|
||||||
for packet in packets {
|
for packet in packets {
|
||||||
// log.verbose("Received data from LINK (\(packet.count) bytes): \(packet.toHex())")
|
// log.verbose("Received data from LINK (\(packet.count) bytes): \(packet.toHex())")
|
||||||
|
|
||||||
|
@ -445,7 +445,7 @@ public class OpenVPNSession: Session {
|
||||||
// log.verbose("Parsed packet with code \(code)")
|
// log.verbose("Parsed packet with code \(code)")
|
||||||
|
|
||||||
var offset = 1
|
var offset = 1
|
||||||
if (code == .dataV2) {
|
if code == .dataV2 {
|
||||||
guard packet.count >= offset + PacketPeerIdLength else {
|
guard packet.count >= offset + PacketPeerIdLength else {
|
||||||
log.warning("Dropped malformed packet (missing peerId)")
|
log.warning("Dropped malformed packet (missing peerId)")
|
||||||
continue
|
continue
|
||||||
|
@ -491,7 +491,7 @@ public class OpenVPNSession: Session {
|
||||||
deferStop(.shutdown, OpenVPNError.staleSession)
|
deferStop(.shutdown, OpenVPNError.staleSession)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
case .softResetV1:
|
case .softResetV1:
|
||||||
if !isRenegotiating {
|
if !isRenegotiating {
|
||||||
softReset(isServerInitiated: true)
|
softReset(isServerInitiated: true)
|
||||||
|
@ -518,7 +518,7 @@ public class OpenVPNSession: Session {
|
||||||
handleDataPackets(dataPackets, key: sessionKey)
|
handleDataPackets(dataPackets, key: sessionKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: recv_tun
|
// Ruby: recv_tun
|
||||||
private func receiveTunnel(packets: [Data]) {
|
private func receiveTunnel(packets: [Data]) {
|
||||||
guard shouldHandlePackets() else {
|
guard shouldHandlePackets() else {
|
||||||
|
@ -527,13 +527,13 @@ public class OpenVPNSession: Session {
|
||||||
}
|
}
|
||||||
sendDataPackets(packets)
|
sendDataPackets(packets)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: ping
|
// Ruby: ping
|
||||||
private func ping() {
|
private func ping() {
|
||||||
guard currentKey?.controlState == .connected else {
|
guard currentKey?.controlState == .connected else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let now = Date()
|
let now = Date()
|
||||||
guard now.timeIntervalSince(lastPing.inbound) <= keepAliveTimeout else {
|
guard now.timeIntervalSince(lastPing.inbound) <= keepAliveTimeout else {
|
||||||
deferStop(.shutdown, OpenVPNError.pingTimeout)
|
deferStop(.shutdown, OpenVPNError.pingTimeout)
|
||||||
|
@ -550,7 +550,7 @@ public class OpenVPNSession: Session {
|
||||||
// schedule even just to check for ping timeout
|
// schedule even just to check for ping timeout
|
||||||
scheduleNextPing()
|
scheduleNextPing()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func scheduleNextPing() {
|
private func scheduleNextPing() {
|
||||||
let interval: TimeInterval
|
let interval: TimeInterval
|
||||||
if let keepAliveInterval = keepAliveInterval {
|
if let keepAliveInterval = keepAliveInterval {
|
||||||
|
@ -565,9 +565,9 @@ public class OpenVPNSession: Session {
|
||||||
self?.ping()
|
self?.ping()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Handshake
|
// MARK: Handshake
|
||||||
|
|
||||||
// Ruby: reset_ctrl
|
// Ruby: reset_ctrl
|
||||||
private func resetControlChannel(forNewSession: Bool) {
|
private func resetControlChannel(forNewSession: Bool) {
|
||||||
authenticator = nil
|
authenticator = nil
|
||||||
|
@ -577,7 +577,7 @@ public class OpenVPNSession: Session {
|
||||||
deferStop(.shutdown, e)
|
deferStop(.shutdown, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: hard_reset
|
// Ruby: hard_reset
|
||||||
private func hardReset() {
|
private func hardReset() {
|
||||||
log.debug("Send hard reset")
|
log.debug("Send hard reset")
|
||||||
|
@ -598,7 +598,7 @@ public class OpenVPNSession: Session {
|
||||||
loopNegotiation()
|
loopNegotiation()
|
||||||
enqueueControlPackets(code: .hardResetClientV2, key: UInt8(negotiationKeyIdx), payload: payload)
|
enqueueControlPackets(code: .hardResetClientV2, key: UInt8(negotiationKeyIdx), payload: payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func hardResetPayload() -> Data? {
|
private func hardResetPayload() -> Data? {
|
||||||
guard !(configuration.usesPIAPatches ?? false) else {
|
guard !(configuration.usesPIAPatches ?? false) else {
|
||||||
guard let _ = configuration.ca else {
|
guard let _ = configuration.ca else {
|
||||||
|
@ -621,7 +621,7 @@ public class OpenVPNSession: Session {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: soft_reset
|
// Ruby: soft_reset
|
||||||
private func softReset(isServerInitiated: Bool) {
|
private func softReset(isServerInitiated: Bool) {
|
||||||
guard !isRenegotiating else {
|
guard !isRenegotiating else {
|
||||||
|
@ -633,7 +633,7 @@ public class OpenVPNSession: Session {
|
||||||
} else {
|
} else {
|
||||||
log.debug("Send soft reset")
|
log.debug("Send soft reset")
|
||||||
}
|
}
|
||||||
|
|
||||||
resetControlChannel(forNewSession: false)
|
resetControlChannel(forNewSession: false)
|
||||||
negotiationKeyIdx = max(1, (negotiationKeyIdx + 1) % OpenVPN.ProtocolMacros.numberOfKeys)
|
negotiationKeyIdx = max(1, (negotiationKeyIdx + 1) % OpenVPN.ProtocolMacros.numberOfKeys)
|
||||||
let newKey = OpenVPN.SessionKey(id: UInt8(negotiationKeyIdx), timeout: CoreConfiguration.OpenVPN.softNegotiationTimeout)
|
let newKey = OpenVPN.SessionKey(id: UInt8(negotiationKeyIdx), timeout: CoreConfiguration.OpenVPN.softNegotiationTimeout)
|
||||||
|
@ -647,13 +647,13 @@ public class OpenVPNSession: Session {
|
||||||
enqueueControlPackets(code: .softResetV1, key: UInt8(negotiationKeyIdx), payload: Data())
|
enqueueControlPackets(code: .softResetV1, key: UInt8(negotiationKeyIdx), payload: Data())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: on_tls_connect
|
// Ruby: on_tls_connect
|
||||||
private func onTLSConnect() {
|
private func onTLSConnect() {
|
||||||
log.debug("TLS.connect: Handshake is complete")
|
log.debug("TLS.connect: Handshake is complete")
|
||||||
|
|
||||||
negotiationKey.controlState = .preAuth
|
negotiationKey.controlState = .preAuth
|
||||||
|
|
||||||
do {
|
do {
|
||||||
authenticator = try OpenVPN.Authenticator(credentials?.username, pushReply?.options.authToken ?? credentials?.password)
|
authenticator = try OpenVPN.Authenticator(credentials?.username, pushReply?.options.authToken ?? credentials?.password)
|
||||||
authenticator?.withLocalOptions = withLocalOptions
|
authenticator?.withLocalOptions = withLocalOptions
|
||||||
|
@ -679,7 +679,7 @@ public class OpenVPNSession: Session {
|
||||||
log.debug("TLS.auth: Pulled ciphertext (\(cipherTextOut.count) bytes)")
|
log.debug("TLS.auth: Pulled ciphertext (\(cipherTextOut.count) bytes)")
|
||||||
enqueueControlPackets(code: .controlV1, key: negotiationKey.id, payload: cipherTextOut)
|
enqueueControlPackets(code: .controlV1, key: negotiationKey.id, payload: cipherTextOut)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: push_request
|
// Ruby: push_request
|
||||||
private func pushRequest() {
|
private func pushRequest() {
|
||||||
guard negotiationKey.controlState == .preIfConfig else {
|
guard negotiationKey.controlState == .preIfConfig else {
|
||||||
|
@ -688,10 +688,10 @@ public class OpenVPNSession: Session {
|
||||||
guard let targetDate = nextPushRequestDate, Date() > targetDate else {
|
guard let targetDate = nextPushRequestDate, Date() > targetDate else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("TLS.ifconfig: Put plaintext (PUSH_REQUEST)")
|
log.debug("TLS.ifconfig: Put plaintext (PUSH_REQUEST)")
|
||||||
try? negotiationKey.tls.putPlainText("PUSH_REQUEST\0")
|
try? negotiationKey.tls.putPlainText("PUSH_REQUEST\0")
|
||||||
|
|
||||||
let cipherTextOut: Data
|
let cipherTextOut: Data
|
||||||
do {
|
do {
|
||||||
cipherTextOut = try negotiationKey.tls.pullCipherText()
|
cipherTextOut = try negotiationKey.tls.pullCipherText()
|
||||||
|
@ -704,32 +704,32 @@ public class OpenVPNSession: Session {
|
||||||
log.verbose("TLS.ifconfig: Still can't pull ciphertext")
|
log.verbose("TLS.ifconfig: Still can't pull ciphertext")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("TLS.ifconfig: Send pulled ciphertext (\(cipherTextOut.count) bytes)")
|
log.debug("TLS.ifconfig: Send pulled ciphertext (\(cipherTextOut.count) bytes)")
|
||||||
enqueueControlPackets(code: .controlV1, key: negotiationKey.id, payload: cipherTextOut)
|
enqueueControlPackets(code: .controlV1, key: negotiationKey.id, payload: cipherTextOut)
|
||||||
|
|
||||||
if isRenegotiating {
|
if isRenegotiating {
|
||||||
completeConnection()
|
completeConnection()
|
||||||
isRenegotiating = false
|
isRenegotiating = false
|
||||||
}
|
}
|
||||||
nextPushRequestDate = Date().addingTimeInterval(CoreConfiguration.OpenVPN.pushRequestInterval)
|
nextPushRequestDate = Date().addingTimeInterval(CoreConfiguration.OpenVPN.pushRequestInterval)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func maybeRenegotiate() {
|
private func maybeRenegotiate() {
|
||||||
guard let renegotiatesAfter = configuration.renegotiatesAfter, renegotiatesAfter > 0 else {
|
guard let renegotiatesAfter = configuration.renegotiatesAfter, renegotiatesAfter > 0 else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard (negotiationKeyIdx == currentKeyIdx) else {
|
guard negotiationKeyIdx == currentKeyIdx else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let elapsed = -negotiationKey.startTime.timeIntervalSinceNow
|
let elapsed = -negotiationKey.startTime.timeIntervalSinceNow
|
||||||
if (elapsed > renegotiatesAfter) {
|
if elapsed > renegotiatesAfter {
|
||||||
log.debug("Renegotiating after \(elapsed.asTimeString)")
|
log.debug("Renegotiating after \(elapsed.asTimeString)")
|
||||||
softReset(isServerInitiated: false)
|
softReset(isServerInitiated: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func completeConnection() {
|
private func completeConnection() {
|
||||||
setupEncryption()
|
setupEncryption()
|
||||||
authenticator?.reset()
|
authenticator?.reset()
|
||||||
|
@ -737,7 +737,7 @@ public class OpenVPNSession: Session {
|
||||||
connectedDate = Date()
|
connectedDate = Date()
|
||||||
transitionKeys()
|
transitionKeys()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Control
|
// MARK: Control
|
||||||
|
|
||||||
// Ruby: handle_ctrl_pkt
|
// Ruby: handle_ctrl_pkt
|
||||||
|
@ -747,16 +747,16 @@ public class OpenVPNSession: Session {
|
||||||
// deferStop(.shutdown, OpenVPNError.badKey)
|
// deferStop(.shutdown, OpenVPNError.badKey)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let _ = configuration.ca else {
|
guard let _ = configuration.ca else {
|
||||||
log.error("Configuration doesn't have a CA")
|
log.error("Configuration doesn't have a CA")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// start new TLS handshake
|
// start new TLS handshake
|
||||||
if ((packet.code == .hardResetServerV2) && (negotiationKey.state == .hardReset)) ||
|
if ((packet.code == .hardResetServerV2) && (negotiationKey.state == .hardReset)) ||
|
||||||
((packet.code == .softResetV1) && (negotiationKey.state == .softReset)) {
|
((packet.code == .softResetV1) && (negotiationKey.state == .softReset)) {
|
||||||
|
|
||||||
if negotiationKey.state == .hardReset {
|
if negotiationKey.state == .hardReset {
|
||||||
controlChannel.remoteSessionId = packet.sessionId
|
controlChannel.remoteSessionId = packet.sessionId
|
||||||
}
|
}
|
||||||
|
@ -811,7 +811,7 @@ public class OpenVPNSession: Session {
|
||||||
enqueueControlPackets(code: .controlV1, key: negotiationKey.id, payload: cipherTextOut)
|
enqueueControlPackets(code: .controlV1, key: negotiationKey.id, payload: cipherTextOut)
|
||||||
}
|
}
|
||||||
// exchange TLS ciphertext
|
// exchange TLS ciphertext
|
||||||
else if ((packet.code == .controlV1) && (negotiationKey.state == .tls)) {
|
else if (packet.code == .controlV1) && (negotiationKey.state == .tls) {
|
||||||
guard let remoteSessionId = controlChannel.remoteSessionId else {
|
guard let remoteSessionId = controlChannel.remoteSessionId else {
|
||||||
log.error("No remote sessionId found in packet (control packets before server HARD_RESET)")
|
log.error("No remote sessionId found in packet (control packets before server HARD_RESET)")
|
||||||
deferStop(.shutdown, OpenVPNError.missingSessionId)
|
deferStop(.shutdown, OpenVPNError.missingSessionId)
|
||||||
|
@ -822,7 +822,7 @@ public class OpenVPNSession: Session {
|
||||||
deferStop(.shutdown, OpenVPNError.sessionMismatch)
|
deferStop(.shutdown, OpenVPNError.sessionMismatch)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let cipherTextIn = packet.payload else {
|
guard let cipherTextIn = packet.payload else {
|
||||||
log.warning("TLS.connect: Control packet with empty payload?")
|
log.warning("TLS.connect: Control packet with empty payload?")
|
||||||
return
|
return
|
||||||
|
@ -844,7 +844,7 @@ public class OpenVPNSession: Session {
|
||||||
}
|
}
|
||||||
log.verbose("TLS.connect: No available ciphertext to pull")
|
log.verbose("TLS.connect: No available ciphertext to pull")
|
||||||
}
|
}
|
||||||
|
|
||||||
if negotiationKey.shouldOnTLSConnect() {
|
if negotiationKey.shouldOnTLSConnect() {
|
||||||
onTLSConnect()
|
onTLSConnect()
|
||||||
}
|
}
|
||||||
|
@ -873,7 +873,7 @@ public class OpenVPNSession: Session {
|
||||||
|
|
||||||
auth.appendControlData(data)
|
auth.appendControlData(data)
|
||||||
|
|
||||||
if (negotiationKey.controlState == .preAuth) {
|
if negotiationKey.controlState == .preAuth {
|
||||||
do {
|
do {
|
||||||
guard try auth.parseAuthReply() else {
|
guard try auth.parseAuthReply() else {
|
||||||
return
|
return
|
||||||
|
@ -882,13 +882,13 @@ public class OpenVPNSession: Session {
|
||||||
deferStop(.shutdown, e)
|
deferStop(.shutdown, e)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
negotiationKey.controlState = .preIfConfig
|
negotiationKey.controlState = .preIfConfig
|
||||||
nextPushRequestDate = Date()
|
nextPushRequestDate = Date()
|
||||||
pushRequest()
|
pushRequest()
|
||||||
nextPushRequestDate?.addTimeInterval(isRenegotiating ? CoreConfiguration.OpenVPN.pushRequestInterval : CoreConfiguration.OpenVPN.retransmissionLimit)
|
nextPushRequestDate?.addTimeInterval(isRenegotiating ? CoreConfiguration.OpenVPN.pushRequestInterval : CoreConfiguration.OpenVPN.retransmissionLimit)
|
||||||
}
|
}
|
||||||
|
|
||||||
for message in auth.parseMessages() {
|
for message in auth.parseMessages() {
|
||||||
if CoreConfiguration.logsSensitiveData {
|
if CoreConfiguration.logsSensitiveData {
|
||||||
log.debug("Parsed control message (\(message.count) bytes): \"\(message)\"")
|
log.debug("Parsed control message (\(message.count) bytes): \"\(message)\"")
|
||||||
|
@ -919,14 +919,14 @@ public class OpenVPNSession: Session {
|
||||||
deferStop(.shutdown, OpenVPNError.badCredentials)
|
deferStop(.shutdown, OpenVPNError.badCredentials)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// disconnect on remote server restart (--explicit-exit-notify)
|
// disconnect on remote server restart (--explicit-exit-notify)
|
||||||
guard !message.hasPrefix("RESTART") else {
|
guard !message.hasPrefix("RESTART") else {
|
||||||
log.debug("Disconnecting due to server shutdown")
|
log.debug("Disconnecting due to server shutdown")
|
||||||
deferStop(.shutdown, OpenVPNError.serverShutdown)
|
deferStop(.shutdown, OpenVPNError.serverShutdown)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle authentication from now on
|
// handle authentication from now on
|
||||||
guard negotiationKey.controlState == .preIfConfig else {
|
guard negotiationKey.controlState == .preIfConfig else {
|
||||||
return
|
return
|
||||||
|
@ -945,7 +945,7 @@ public class OpenVPNSession: Session {
|
||||||
}
|
}
|
||||||
reply = optionalReply
|
reply = optionalReply
|
||||||
log.debug("Received PUSH_REPLY: \"\(reply)\"")
|
log.debug("Received PUSH_REPLY: \"\(reply)\"")
|
||||||
|
|
||||||
if let framing = reply.options.compressionFraming, let compression = reply.options.compressionAlgorithm {
|
if let framing = reply.options.compressionFraming, let compression = reply.options.compressionAlgorithm {
|
||||||
switch compression {
|
switch compression {
|
||||||
case .disabled:
|
case .disabled:
|
||||||
|
@ -970,13 +970,13 @@ public class OpenVPNSession: Session {
|
||||||
deferStop(.shutdown, e)
|
deferStop(.shutdown, e)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pushReply = reply
|
pushReply = reply
|
||||||
guard reply.options.ipv4 != nil || reply.options.ipv6 != nil else {
|
guard reply.options.ipv4 != nil || reply.options.ipv6 != nil else {
|
||||||
deferStop(.shutdown, OpenVPNError.noRouting)
|
deferStop(.shutdown, OpenVPNError.noRouting)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
completeConnection()
|
completeConnection()
|
||||||
|
|
||||||
guard let remoteAddress = link?.remoteAddress else {
|
guard let remoteAddress = link?.remoteAddress else {
|
||||||
|
@ -991,7 +991,7 @@ public class OpenVPNSession: Session {
|
||||||
|
|
||||||
scheduleNextPing()
|
scheduleNextPing()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: transition_keys
|
// Ruby: transition_keys
|
||||||
private func transitionKeys() {
|
private func transitionKeys() {
|
||||||
if let key = currentKey {
|
if let key = currentKey {
|
||||||
|
@ -1000,15 +1000,15 @@ public class OpenVPNSession: Session {
|
||||||
currentKeyIdx = negotiationKeyIdx
|
currentKeyIdx = negotiationKeyIdx
|
||||||
cleanKeys()
|
cleanKeys()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: clean_keys
|
// Ruby: clean_keys
|
||||||
private func cleanKeys() {
|
private func cleanKeys() {
|
||||||
while (oldKeys.count > 1) {
|
while oldKeys.count > 1 {
|
||||||
let key = oldKeys.removeFirst()
|
let key = oldKeys.removeFirst()
|
||||||
keys.removeValue(forKey: key.id)
|
keys.removeValue(forKey: key.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: q_ctrl
|
// Ruby: q_ctrl
|
||||||
private func enqueueControlPackets(code: PacketCode, key: UInt8, payload: Data) {
|
private func enqueueControlPackets(code: PacketCode, key: UInt8, payload: Data) {
|
||||||
guard let _ = link else {
|
guard let _ = link else {
|
||||||
|
@ -1019,7 +1019,7 @@ public class OpenVPNSession: Session {
|
||||||
controlChannel.enqueueOutboundPackets(withCode: code, key: key, payload: payload, maxPacketSize: 1000)
|
controlChannel.enqueueOutboundPackets(withCode: code, key: key, payload: payload, maxPacketSize: 1000)
|
||||||
flushControlQueue()
|
flushControlQueue()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: flush_ctrl_q_out
|
// Ruby: flush_ctrl_q_out
|
||||||
private func flushControlQueue() {
|
private func flushControlQueue() {
|
||||||
let rawList: [Data]
|
let rawList: [Data]
|
||||||
|
@ -1033,7 +1033,7 @@ public class OpenVPNSession: Session {
|
||||||
for raw in rawList {
|
for raw in rawList {
|
||||||
log.debug("Send control packet (\(raw.count) bytes): \(raw.toHex())")
|
log.debug("Send control packet (\(raw.count) bytes): \(raw.toHex())")
|
||||||
}
|
}
|
||||||
|
|
||||||
// WARNING: runs in Network.framework queue
|
// WARNING: runs in Network.framework queue
|
||||||
let writeLink = link
|
let writeLink = link
|
||||||
link?.writePackets(rawList) { [weak self] (error) in
|
link?.writePackets(rawList) { [weak self] (error) in
|
||||||
|
@ -1050,7 +1050,7 @@ public class OpenVPNSession: Session {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: setup_keys
|
// Ruby: setup_keys
|
||||||
private func setupEncryption() {
|
private func setupEncryption() {
|
||||||
guard let auth = authenticator else {
|
guard let auth = authenticator else {
|
||||||
|
@ -1081,7 +1081,7 @@ public class OpenVPNSession: Session {
|
||||||
} else {
|
} else {
|
||||||
log.debug("Set up encryption")
|
log.debug("Set up encryption")
|
||||||
}
|
}
|
||||||
|
|
||||||
let pushedCipher = pushReply.options.cipher
|
let pushedCipher = pushReply.options.cipher
|
||||||
if let negCipher = pushedCipher {
|
if let negCipher = pushedCipher {
|
||||||
log.info("\tNegotiated cipher: \(negCipher.rawValue)")
|
log.info("\tNegotiated cipher: \(negCipher.rawValue)")
|
||||||
|
@ -1125,7 +1125,7 @@ public class OpenVPNSession: Session {
|
||||||
usesReplayProtection: CoreConfiguration.OpenVPN.usesReplayProtection
|
usesReplayProtection: CoreConfiguration.OpenVPN.usesReplayProtection
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Data
|
// MARK: Data
|
||||||
|
|
||||||
// Ruby: handle_data_pkt
|
// Ruby: handle_data_pkt
|
||||||
|
@ -1149,7 +1149,7 @@ public class OpenVPNSession: Session {
|
||||||
deferStop(.reconnect, e)
|
deferStop(.reconnect, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: send_data_pkt
|
// Ruby: send_data_pkt
|
||||||
private func sendDataPackets(_ packets: [Data]) {
|
private func sendDataPackets(_ packets: [Data]) {
|
||||||
guard let key = currentKey else {
|
guard let key = currentKey else {
|
||||||
|
@ -1163,7 +1163,7 @@ public class OpenVPNSession: Session {
|
||||||
guard !encryptedPackets.isEmpty else {
|
guard !encryptedPackets.isEmpty else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// WARNING: runs in Network.framework queue
|
// WARNING: runs in Network.framework queue
|
||||||
controlChannel.addSentDataCount(encryptedPackets.flatCount)
|
controlChannel.addSentDataCount(encryptedPackets.flatCount)
|
||||||
let writeLink = link
|
let writeLink = link
|
||||||
|
@ -1189,12 +1189,12 @@ public class OpenVPNSession: Session {
|
||||||
deferStop(.reconnect, e)
|
deferStop(.reconnect, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Acks
|
// MARK: Acks
|
||||||
|
|
||||||
private func handleAcks() {
|
private func handleAcks() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: send_ack
|
// Ruby: send_ack
|
||||||
private func sendAck(for controlPacket: ControlPacket) {
|
private func sendAck(for controlPacket: ControlPacket) {
|
||||||
log.debug("Send ack for received packetId \(controlPacket.packetId)")
|
log.debug("Send ack for received packetId \(controlPacket.packetId)")
|
||||||
|
@ -1210,7 +1210,7 @@ public class OpenVPNSession: Session {
|
||||||
deferStop(.shutdown, e)
|
deferStop(.shutdown, e)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// WARNING: runs in Network.framework queue
|
// WARNING: runs in Network.framework queue
|
||||||
let writeLink = link
|
let writeLink = link
|
||||||
link?.writePacket(raw) { [weak self] (error) in
|
link?.writePacket(raw) { [weak self] (error) in
|
||||||
|
@ -1228,13 +1228,13 @@ public class OpenVPNSession: Session {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Stop
|
// MARK: Stop
|
||||||
|
|
||||||
private func shouldHandlePackets() -> Bool {
|
private func shouldHandlePackets() -> Bool {
|
||||||
return !isStopping && !keys.isEmpty
|
return !isStopping && !keys.isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
private func deferStop(_ method: StopMethod, _ error: Error?) {
|
private func deferStop(_ method: StopMethod, _ error: Error?) {
|
||||||
guard !isStopping else {
|
guard !isStopping else {
|
||||||
return
|
return
|
||||||
|
@ -1246,7 +1246,7 @@ public class OpenVPNSession: Session {
|
||||||
case .shutdown:
|
case .shutdown:
|
||||||
self?.doShutdown(error: error)
|
self?.doShutdown(error: error)
|
||||||
self?.cleanupCache()
|
self?.cleanupCache()
|
||||||
|
|
||||||
case .reconnect:
|
case .reconnect:
|
||||||
self?.doReconnect(error: error)
|
self?.doReconnect(error: error)
|
||||||
}
|
}
|
||||||
|
@ -1259,7 +1259,7 @@ public class OpenVPNSession: Session {
|
||||||
completion()
|
completion()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
link.writePackets(packets) { [weak self] (error) in
|
link.writePackets(packets) { [weak self] (_) in
|
||||||
self?.queue.sync {
|
self?.queue.sync {
|
||||||
completion()
|
completion()
|
||||||
}
|
}
|
||||||
|
@ -1271,7 +1271,7 @@ public class OpenVPNSession: Session {
|
||||||
completion()
|
completion()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func doShutdown(error: Error?) {
|
private func doShutdown(error: Error?) {
|
||||||
if let error = error {
|
if let error = error {
|
||||||
log.error("Trigger shutdown (error: \(error))")
|
log.error("Trigger shutdown (error: \(error))")
|
||||||
|
@ -1281,7 +1281,7 @@ public class OpenVPNSession: Session {
|
||||||
stopError = error
|
stopError = error
|
||||||
delegate?.sessionDidStop(self, withError: error, shouldReconnect: false)
|
delegate?.sessionDidStop(self, withError: error, shouldReconnect: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func doReconnect(error: Error?) {
|
private func doReconnect(error: Error?) {
|
||||||
if let error = error {
|
if let error = error {
|
||||||
log.error("Trigger reconnection (error: \(error))")
|
log.error("Trigger reconnection (error: \(error))")
|
||||||
|
|
|
@ -69,7 +69,7 @@ extension OpenVPN {
|
||||||
|
|
||||||
enum OCCPacket: UInt8 {
|
enum OCCPacket: UInt8 {
|
||||||
case exit = 0x06
|
case exit = 0x06
|
||||||
|
|
||||||
private static let magicString = Data(hex: "287f346bd4ef7a812d56b8d3afc5459c")
|
private static let magicString = Data(hex: "287f346bd4ef7a812d56b8d3afc5459c")
|
||||||
|
|
||||||
func serialized(_ info: Any? = nil) -> Data {
|
func serialized(_ info: Any? = nil) -> Data {
|
||||||
|
|
|
@ -39,7 +39,7 @@ import TunnelKitOpenVPNCore
|
||||||
|
|
||||||
extension OpenVPN {
|
extension OpenVPN {
|
||||||
class ProtocolMacros {
|
class ProtocolMacros {
|
||||||
|
|
||||||
// UInt32(0) + UInt8(KeyMethod = 2)
|
// UInt32(0) + UInt8(KeyMethod = 2)
|
||||||
static let tlsPrefix = Data(hex: "0000000002")
|
static let tlsPrefix = Data(hex: "0000000002")
|
||||||
|
|
||||||
|
|
|
@ -40,11 +40,11 @@ import TunnelKitOpenVPNCore
|
||||||
extension OpenVPN {
|
extension OpenVPN {
|
||||||
struct PushReply: CustomStringConvertible {
|
struct PushReply: CustomStringConvertible {
|
||||||
private static let prefix = "PUSH_REPLY,"
|
private static let prefix = "PUSH_REPLY,"
|
||||||
|
|
||||||
private let original: String
|
private let original: String
|
||||||
|
|
||||||
let options: Configuration
|
let options: Configuration
|
||||||
|
|
||||||
init?(message: String) throws {
|
init?(message: String) throws {
|
||||||
guard message.hasPrefix(PushReply.prefix) else {
|
guard message.hasPrefix(PushReply.prefix) else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -57,15 +57,15 @@ extension OpenVPN {
|
||||||
let lines = original.components(separatedBy: ",")
|
let lines = original.components(separatedBy: ",")
|
||||||
options = try ConfigurationParser.parsed(fromLines: lines).configuration
|
options = try ConfigurationParser.parsed(fromLines: lines).configuration
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: CustomStringConvertible
|
// MARK: CustomStringConvertible
|
||||||
|
|
||||||
var description: String {
|
var description: String {
|
||||||
let stripped = NSMutableString(string: original)
|
let stripped = NSMutableString(string: original)
|
||||||
ConfigurationParser.Regex.authToken.replaceMatches(
|
ConfigurationParser.Regex.authToken.replaceMatches(
|
||||||
in: stripped,
|
in: stripped,
|
||||||
options: [],
|
options: [],
|
||||||
range: NSMakeRange(0, stripped.length),
|
range: NSRange(location: 0, length: stripped.length),
|
||||||
withTemplate: "auth-token"
|
withTemplate: "auth-token"
|
||||||
)
|
)
|
||||||
return stripped as String
|
return stripped as String
|
||||||
|
|
|
@ -48,21 +48,21 @@ extension OpenVPN {
|
||||||
enum State {
|
enum State {
|
||||||
case invalid, hardReset, softReset, tls
|
case invalid, hardReset, softReset, tls
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ControlState {
|
enum ControlState {
|
||||||
case preAuth, preIfConfig, connected
|
case preAuth, preIfConfig, connected
|
||||||
}
|
}
|
||||||
|
|
||||||
let id: UInt8 // 3-bit
|
let id: UInt8 // 3-bit
|
||||||
|
|
||||||
let timeout: TimeInterval
|
let timeout: TimeInterval
|
||||||
|
|
||||||
let startTime: Date
|
let startTime: Date
|
||||||
|
|
||||||
var state = State.invalid
|
var state = State.invalid
|
||||||
|
|
||||||
var controlState: ControlState?
|
var controlState: ControlState?
|
||||||
|
|
||||||
var tlsOptional: TLSBox?
|
var tlsOptional: TLSBox?
|
||||||
|
|
||||||
var tls: TLSBox {
|
var tls: TLSBox {
|
||||||
|
@ -71,11 +71,11 @@ extension OpenVPN {
|
||||||
}
|
}
|
||||||
return tls
|
return tls
|
||||||
}
|
}
|
||||||
|
|
||||||
var dataPath: DataPath?
|
var dataPath: DataPath?
|
||||||
|
|
||||||
private var isTLSConnected: Bool
|
private var isTLSConnected: Bool
|
||||||
|
|
||||||
init(id: UInt8, timeout: TimeInterval) {
|
init(id: UInt8, timeout: TimeInterval) {
|
||||||
self.id = id
|
self.id = id
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
|
@ -89,12 +89,12 @@ extension OpenVPN {
|
||||||
func didHardResetTimeOut(link: LinkInterface) -> Bool {
|
func didHardResetTimeOut(link: LinkInterface) -> Bool {
|
||||||
return ((state == .hardReset) && (-startTime.timeIntervalSinceNow > CoreConfiguration.OpenVPN.hardResetTimeout))
|
return ((state == .hardReset) && (-startTime.timeIntervalSinceNow > CoreConfiguration.OpenVPN.hardResetTimeout))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: Key.negotiate_timeout
|
// Ruby: Key.negotiate_timeout
|
||||||
func didNegotiationTimeOut(link: LinkInterface) -> Bool {
|
func didNegotiationTimeOut(link: LinkInterface) -> Bool {
|
||||||
return ((controlState != .connected) && (-startTime.timeIntervalSinceNow > timeout))
|
return ((controlState != .connected) && (-startTime.timeIntervalSinceNow > timeout))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: Key.on_tls_connect
|
// Ruby: Key.on_tls_connect
|
||||||
func shouldOnTLSConnect() -> Bool {
|
func shouldOnTLSConnect() -> Bool {
|
||||||
guard !isTLSConnected else {
|
guard !isTLSConnected else {
|
||||||
|
@ -105,7 +105,7 @@ extension OpenVPN {
|
||||||
}
|
}
|
||||||
return isTLSConnected
|
return isTLSConnected
|
||||||
}
|
}
|
||||||
|
|
||||||
func encrypt(packets: [Data]) throws -> [Data]? {
|
func encrypt(packets: [Data]) throws -> [Data]? {
|
||||||
guard let dataPath = dataPath else {
|
guard let dataPath = dataPath else {
|
||||||
log.warning("Data: Set dataPath first")
|
log.warning("Data: Set dataPath first")
|
||||||
|
@ -113,7 +113,7 @@ extension OpenVPN {
|
||||||
}
|
}
|
||||||
return try dataPath.encryptPackets(packets, key: id)
|
return try dataPath.encryptPackets(packets, key: id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func decrypt(packets: [Data]) throws -> [Data]? {
|
func decrypt(packets: [Data]) throws -> [Data]? {
|
||||||
guard let dataPath = dataPath else {
|
guard let dataPath = dataPath else {
|
||||||
log.warning("Data: Set dataPath first")
|
log.warning("Data: Set dataPath first")
|
||||||
|
|
|
@ -29,11 +29,11 @@ import TunnelKitOpenVPNCore
|
||||||
/// Processes data packets according to a XOR method.
|
/// Processes data packets according to a XOR method.
|
||||||
public struct XORProcessor {
|
public struct XORProcessor {
|
||||||
private let method: OpenVPN.XORMethod?
|
private let method: OpenVPN.XORMethod?
|
||||||
|
|
||||||
public init(method: OpenVPN.XORMethod?) {
|
public init(method: OpenVPN.XORMethod?) {
|
||||||
self.method = method
|
self.method = method
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns an array of data packets processed according to XOR method.
|
Returns an array of data packets processed according to XOR method.
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ public struct XORProcessor {
|
||||||
processPacket($0, outbound: outbound)
|
processPacket($0, outbound: outbound)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns a data packet processed according to XOR method.
|
Returns a data packet processed according to XOR method.
|
||||||
|
|
||||||
|
@ -64,13 +64,13 @@ public struct XORProcessor {
|
||||||
switch method {
|
switch method {
|
||||||
case .xormask(let mask):
|
case .xormask(let mask):
|
||||||
return Self.xormask(packet: packet, mask: mask)
|
return Self.xormask(packet: packet, mask: mask)
|
||||||
|
|
||||||
case .xorptrpos:
|
case .xorptrpos:
|
||||||
return Self.xorptrpos(packet: packet)
|
return Self.xorptrpos(packet: packet)
|
||||||
|
|
||||||
case .reverse:
|
case .reverse:
|
||||||
return Self.reverse(packet: packet)
|
return Self.reverse(packet: packet)
|
||||||
|
|
||||||
case .obfuscate(let mask):
|
case .obfuscate(let mask):
|
||||||
if outbound {
|
if outbound {
|
||||||
return Self.xormask(packet: Self.xorptrpos(packet: Self.reverse(packet: Self.xorptrpos(packet: packet))), mask: mask)
|
return Self.xormask(packet: Self.xorptrpos(packet: Self.reverse(packet: Self.xorptrpos(packet: packet))), mask: mask)
|
||||||
|
@ -87,13 +87,13 @@ extension XORProcessor {
|
||||||
byte ^ [UInt8](mask)[index % mask.count]
|
byte ^ [UInt8](mask)[index % mask.count]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func xorptrpos(packet: Data) -> Data {
|
private static func xorptrpos(packet: Data) -> Data {
|
||||||
Data(packet.enumerated().map { (index, byte) in
|
Data(packet.enumerated().map { (index, byte) in
|
||||||
byte ^ UInt8(truncatingIfNeeded: index &+ 1)
|
byte ^ UInt8(truncatingIfNeeded: index &+ 1)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func reverse(packet: Data) -> Data {
|
private static func reverse(packet: Data) -> Data {
|
||||||
Data(([UInt8](packet))[0..<1] + ([UInt8](packet)[1...]).reversed())
|
Data(([UInt8](packet))[0..<1] + ([UInt8](packet)[1...]).reversed())
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ open class WireGuardTunnelProvider: NEPacketTunnelProvider {
|
||||||
open override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
open override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
||||||
|
|
||||||
// BEGIN: TunnelKit
|
// BEGIN: TunnelKit
|
||||||
|
|
||||||
guard let tunnelProviderProtocol = protocolConfiguration as? NETunnelProviderProtocol else {
|
guard let tunnelProviderProtocol = protocolConfiguration as? NETunnelProviderProtocol else {
|
||||||
fatalError("Not a NETunnelProviderProtocol")
|
fatalError("Not a NETunnelProviderProtocol")
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ open class WireGuardTunnelProvider: NEPacketTunnelProvider {
|
||||||
completionHandler(WireGuardProviderError.savedProtocolConfigurationIsInvalid)
|
completionHandler(WireGuardProviderError.savedProtocolConfigurationIsInvalid)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
configureLogging()
|
configureLogging()
|
||||||
|
|
||||||
// END: TunnelKit
|
// END: TunnelKit
|
||||||
|
@ -128,7 +128,7 @@ extension WireGuardTunnelProvider {
|
||||||
private func configureLogging() {
|
private func configureLogging() {
|
||||||
let logLevel: SwiftyBeaver.Level = (cfg.shouldDebug ? .debug : .info)
|
let logLevel: SwiftyBeaver.Level = (cfg.shouldDebug ? .debug : .info)
|
||||||
let logFormat = cfg.debugLogFormat ?? "$Dyyyy-MM-dd HH:mm:ss.SSS$d $L $N.$F:$l - $M"
|
let logFormat = cfg.debugLogFormat ?? "$Dyyyy-MM-dd HH:mm:ss.SSS$d $L $N.$F:$l - $M"
|
||||||
|
|
||||||
if cfg.shouldDebug {
|
if cfg.shouldDebug {
|
||||||
let console = ConsoleDestination()
|
let console = ConsoleDestination()
|
||||||
console.useNSLog = true
|
console.useNSLog = true
|
||||||
|
|
|
@ -29,15 +29,15 @@ import NetworkExtension
|
||||||
|
|
||||||
public protocol WireGuardConfigurationProviding {
|
public protocol WireGuardConfigurationProviding {
|
||||||
var interface: InterfaceConfiguration { get }
|
var interface: InterfaceConfiguration { get }
|
||||||
|
|
||||||
var peers: [PeerConfiguration] { get }
|
var peers: [PeerConfiguration] { get }
|
||||||
|
|
||||||
var privateKey: String { get }
|
var privateKey: String { get }
|
||||||
|
|
||||||
var publicKey: String { get }
|
var publicKey: String { get }
|
||||||
|
|
||||||
var addresses: [String] { get }
|
var addresses: [String] { get }
|
||||||
|
|
||||||
var dnsServers: [String] { get }
|
var dnsServers: [String] { get }
|
||||||
|
|
||||||
var dnsSearchDomains: [String] { get }
|
var dnsSearchDomains: [String] { get }
|
||||||
|
@ -64,13 +64,13 @@ public protocol WireGuardConfigurationProviding {
|
||||||
extension WireGuard {
|
extension WireGuard {
|
||||||
public struct ConfigurationBuilder: WireGuardConfigurationProviding {
|
public struct ConfigurationBuilder: WireGuardConfigurationProviding {
|
||||||
private static let defaultGateway4 = IPAddressRange(from: "0.0.0.0/0")!
|
private static let defaultGateway4 = IPAddressRange(from: "0.0.0.0/0")!
|
||||||
|
|
||||||
private static let defaultGateway6 = IPAddressRange(from: "::/0")!
|
private static let defaultGateway6 = IPAddressRange(from: "::/0")!
|
||||||
|
|
||||||
public private(set) var interface: InterfaceConfiguration
|
public private(set) var interface: InterfaceConfiguration
|
||||||
|
|
||||||
public private(set) var peers: [PeerConfiguration]
|
public private(set) var peers: [PeerConfiguration]
|
||||||
|
|
||||||
public init() {
|
public init() {
|
||||||
self.init(PrivateKey())
|
self.init(PrivateKey())
|
||||||
}
|
}
|
||||||
|
@ -81,19 +81,19 @@ extension WireGuard {
|
||||||
}
|
}
|
||||||
self.init(privateKey)
|
self.init(privateKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
private init(_ privateKey: PrivateKey) {
|
private init(_ privateKey: PrivateKey) {
|
||||||
interface = InterfaceConfiguration(privateKey: privateKey)
|
interface = InterfaceConfiguration(privateKey: privateKey)
|
||||||
peers = []
|
peers = []
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(_ tunnelConfiguration: TunnelConfiguration) {
|
public init(_ tunnelConfiguration: TunnelConfiguration) {
|
||||||
interface = tunnelConfiguration.interface
|
interface = tunnelConfiguration.interface
|
||||||
peers = tunnelConfiguration.peers
|
peers = tunnelConfiguration.peers
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: WireGuardConfigurationProviding
|
// MARK: WireGuardConfigurationProviding
|
||||||
|
|
||||||
public var privateKey: String {
|
public var privateKey: String {
|
||||||
get {
|
get {
|
||||||
interface.privateKey.base64Key
|
interface.privateKey.base64Key
|
||||||
|
@ -114,7 +114,7 @@ extension WireGuard {
|
||||||
interface.addresses = newValue.compactMap(IPAddressRange.init)
|
interface.addresses = newValue.compactMap(IPAddressRange.init)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var dnsServers: [String] {
|
public var dnsServers: [String] {
|
||||||
get {
|
get {
|
||||||
interface.dns.map(\.stringRepresentation)
|
interface.dns.map(\.stringRepresentation)
|
||||||
|
@ -159,7 +159,7 @@ extension WireGuard {
|
||||||
interface.mtu = newValue
|
interface.mtu = newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Modification
|
// MARK: Modification
|
||||||
|
|
||||||
public mutating func addPeer(_ base64PublicKey: String, endpoint: String, allowedIPs: [String] = []) throws {
|
public mutating func addPeer(_ base64PublicKey: String, endpoint: String, allowedIPs: [String] = []) throws {
|
||||||
|
@ -198,7 +198,7 @@ extension WireGuard {
|
||||||
$0 == Self.defaultGateway6
|
$0 == Self.defaultGateway6
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public mutating func removeDefaultGateways(fromPeer peerIndex: Int) {
|
public mutating func removeDefaultGateways(fromPeer peerIndex: Int) {
|
||||||
peers[peerIndex].allowedIPs.removeAll {
|
peers[peerIndex].allowedIPs.removeAll {
|
||||||
$0 == Self.defaultGateway4 || $0 == Self.defaultGateway6
|
$0 == Self.defaultGateway4 || $0 == Self.defaultGateway6
|
||||||
|
@ -230,7 +230,7 @@ extension WireGuard {
|
||||||
public mutating func setKeepAlive(_ keepAlive: UInt16, forPeer peerIndex: Int) {
|
public mutating func setKeepAlive(_ keepAlive: UInt16, forPeer peerIndex: Int) {
|
||||||
peers[peerIndex].persistentKeepAlive = keepAlive
|
peers[peerIndex].persistentKeepAlive = keepAlive
|
||||||
}
|
}
|
||||||
|
|
||||||
public func build() -> Configuration {
|
public func build() -> Configuration {
|
||||||
let tunnelConfiguration = TunnelConfiguration(name: nil, interface: interface, peers: peers)
|
let tunnelConfiguration = TunnelConfiguration(name: nil, interface: interface, peers: peers)
|
||||||
return Configuration(tunnelConfiguration: tunnelConfiguration)
|
return Configuration(tunnelConfiguration: tunnelConfiguration)
|
||||||
|
@ -239,25 +239,25 @@ extension WireGuard {
|
||||||
|
|
||||||
public struct Configuration: Codable, Equatable, WireGuardConfigurationProviding {
|
public struct Configuration: Codable, Equatable, WireGuardConfigurationProviding {
|
||||||
public let tunnelConfiguration: TunnelConfiguration
|
public let tunnelConfiguration: TunnelConfiguration
|
||||||
|
|
||||||
public var interface: InterfaceConfiguration {
|
public var interface: InterfaceConfiguration {
|
||||||
tunnelConfiguration.interface
|
tunnelConfiguration.interface
|
||||||
}
|
}
|
||||||
|
|
||||||
public var peers: [PeerConfiguration] {
|
public var peers: [PeerConfiguration] {
|
||||||
tunnelConfiguration.peers
|
tunnelConfiguration.peers
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(tunnelConfiguration: TunnelConfiguration) {
|
public init(tunnelConfiguration: TunnelConfiguration) {
|
||||||
self.tunnelConfiguration = tunnelConfiguration
|
self.tunnelConfiguration = tunnelConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
public func builder() -> WireGuard.ConfigurationBuilder {
|
public func builder() -> WireGuard.ConfigurationBuilder {
|
||||||
WireGuard.ConfigurationBuilder(tunnelConfiguration)
|
WireGuard.ConfigurationBuilder(tunnelConfiguration)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: WireGuardConfigurationProviding
|
// MARK: WireGuardConfigurationProviding
|
||||||
|
|
||||||
public var privateKey: String {
|
public var privateKey: String {
|
||||||
interface.privateKey.base64Key
|
interface.privateKey.base64Key
|
||||||
}
|
}
|
||||||
|
@ -269,7 +269,7 @@ extension WireGuard {
|
||||||
public var addresses: [String] {
|
public var addresses: [String] {
|
||||||
interface.addresses.map(\.stringRepresentation)
|
interface.addresses.map(\.stringRepresentation)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var dnsServers: [String] {
|
public var dnsServers: [String] {
|
||||||
interface.dns.map(\.stringRepresentation)
|
interface.dns.map(\.stringRepresentation)
|
||||||
}
|
}
|
||||||
|
@ -291,14 +291,14 @@ extension WireGuard {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Codable
|
// MARK: Codable
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
let container = try decoder.singleValueContainer()
|
let container = try decoder.singleValueContainer()
|
||||||
let wg = try container.decode(String.self)
|
let wg = try container.decode(String.self)
|
||||||
let cfg = try TunnelConfiguration(fromWgQuickConfig: wg, called: nil)
|
let cfg = try TunnelConfiguration(fromWgQuickConfig: wg, called: nil)
|
||||||
self.init(tunnelConfiguration: cfg)
|
self.init(tunnelConfiguration: cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
public func encode(to encoder: Encoder) throws {
|
||||||
let wg = tunnelConfiguration.asWgQuickConfig()
|
let wg = tunnelConfiguration.asWgQuickConfig()
|
||||||
var container = encoder.singleValueContainer()
|
var container = encoder.singleValueContainer()
|
||||||
|
@ -315,7 +315,7 @@ extension WireGuardConfigurationProviding {
|
||||||
public var peersCount: Int {
|
public var peersCount: Int {
|
||||||
peers.count
|
peers.count
|
||||||
}
|
}
|
||||||
|
|
||||||
public func publicKey(ofPeer peerIndex: Int) -> String {
|
public func publicKey(ofPeer peerIndex: Int) -> String {
|
||||||
peers[peerIndex].publicKey.base64Key
|
peers[peerIndex].publicKey.base64Key
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,10 @@ extension OSLogType {
|
||||||
switch self {
|
switch self {
|
||||||
case .debug:
|
case .debug:
|
||||||
return .debug
|
return .debug
|
||||||
|
|
||||||
case .info:
|
case .info:
|
||||||
return .info
|
return .info
|
||||||
|
|
||||||
case .error, .fault:
|
case .error, .fault:
|
||||||
return .error
|
return .error
|
||||||
|
|
||||||
|
|
|
@ -42,18 +42,18 @@ extension WireGuard {
|
||||||
|
|
||||||
case lastError = "WireGuard.LastError"
|
case lastError = "WireGuard.LastError"
|
||||||
}
|
}
|
||||||
|
|
||||||
public let title: String
|
public let title: String
|
||||||
|
|
||||||
public let appGroup: String
|
public let appGroup: String
|
||||||
|
|
||||||
public let configuration: WireGuard.Configuration
|
public let configuration: WireGuard.Configuration
|
||||||
|
|
||||||
public var shouldDebug = false
|
public var shouldDebug = false
|
||||||
|
|
||||||
public var debugLogPath: String? = nil
|
public var debugLogPath: String?
|
||||||
|
|
||||||
public var debugLogFormat: String? = nil
|
public var debugLogFormat: String?
|
||||||
|
|
||||||
public init(_ title: String, appGroup: String, configuration: WireGuard.Configuration) {
|
public init(_ title: String, appGroup: String, configuration: WireGuard.Configuration) {
|
||||||
self.title = title
|
self.title = title
|
||||||
|
@ -68,7 +68,7 @@ extension WireGuard {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: NetworkExtensionConfiguration
|
// MARK: NetworkExtensionConfiguration
|
||||||
|
|
||||||
extension WireGuard.ProviderConfiguration: NetworkExtensionConfiguration {
|
extension WireGuard.ProviderConfiguration: NetworkExtensionConfiguration {
|
||||||
|
@ -94,7 +94,6 @@ extension WireGuard.ProviderConfiguration {
|
||||||
public var lastError: WireGuardProviderError? {
|
public var lastError: WireGuardProviderError? {
|
||||||
return defaults?.wireGuardLastError
|
return defaults?.wireGuardLastError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public var urlForDebugLog: URL? {
|
public var urlForDebugLog: URL? {
|
||||||
return defaults?.wireGuardURLForDebugLog(appGroup: appGroup)
|
return defaults?.wireGuardURLForDebugLog(appGroup: appGroup)
|
||||||
|
|
|
@ -44,11 +44,9 @@ extension UnicodeScalar {
|
||||||
let value = self.value
|
let value = self.value
|
||||||
if 48 <= value && value <= 57 {
|
if 48 <= value && value <= 57 {
|
||||||
return UInt8(value - 48)
|
return UInt8(value - 48)
|
||||||
}
|
} else if 65 <= value && value <= 70 {
|
||||||
else if 65 <= value && value <= 70 {
|
|
||||||
return UInt8(value - 55)
|
return UInt8(value - 55)
|
||||||
}
|
} else if 97 <= value && value <= 102 {
|
||||||
else if 97 <= value && value <= 102 {
|
|
||||||
return UInt8(value - 87)
|
return UInt8(value - 87)
|
||||||
}
|
}
|
||||||
fatalError("\(self) not a legal hex nibble")
|
fatalError("\(self) not a legal hex nibble")
|
||||||
|
@ -58,7 +56,7 @@ extension UnicodeScalar {
|
||||||
extension Data {
|
extension Data {
|
||||||
public init(hex: String) {
|
public init(hex: String) {
|
||||||
let scalars = hex.unicodeScalars
|
let scalars = hex.unicodeScalars
|
||||||
var bytes = Array<UInt8>(repeating: 0, count: (scalars.count + 1) >> 1)
|
var bytes = [UInt8](repeating: 0, count: (scalars.count + 1) >> 1)
|
||||||
for (index, scalar) in scalars.enumerated() {
|
for (index, scalar) in scalars.enumerated() {
|
||||||
var nibble = scalar.hexNibble
|
var nibble = scalar.hexNibble
|
||||||
if index & 1 == 0 {
|
if index & 1 == 0 {
|
||||||
|
@ -72,7 +70,7 @@ extension Data {
|
||||||
public func toHex() -> String {
|
public func toHex() -> String {
|
||||||
return map { String(format: "%02hhx", $0) }.joined()
|
return map { String(format: "%02hhx", $0) }.joined()
|
||||||
}
|
}
|
||||||
|
|
||||||
public mutating func zero() {
|
public mutating func zero() {
|
||||||
resetBytes(in: 0..<count)
|
resetBytes(in: 0..<count)
|
||||||
}
|
}
|
||||||
|
@ -90,7 +88,7 @@ extension Data {
|
||||||
}
|
}
|
||||||
append(buffer)
|
append(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
public mutating func append(_ value: UInt32) {
|
public mutating func append(_ value: UInt32) {
|
||||||
var localValue = value
|
var localValue = value
|
||||||
let buffer = withUnsafePointer(to: &localValue) {
|
let buffer = withUnsafePointer(to: &localValue) {
|
||||||
|
@ -98,7 +96,7 @@ extension Data {
|
||||||
}
|
}
|
||||||
append(buffer)
|
append(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
public mutating func append(_ value: UInt64) {
|
public mutating func append(_ value: UInt64) {
|
||||||
var localValue = value
|
var localValue = value
|
||||||
let buffer = withUnsafePointer(to: &localValue) {
|
let buffer = withUnsafePointer(to: &localValue) {
|
||||||
|
@ -106,7 +104,7 @@ extension Data {
|
||||||
}
|
}
|
||||||
append(buffer)
|
append(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
public mutating func append(nullTerminatedString: String) {
|
public mutating func append(nullTerminatedString: String) {
|
||||||
append(nullTerminatedString.data(using: .ascii)!)
|
append(nullTerminatedString.data(using: .ascii)!)
|
||||||
append(UInt8(0))
|
append(UInt8(0))
|
||||||
|
@ -115,7 +113,7 @@ extension Data {
|
||||||
public func nullTerminatedString(from: Int) -> String? {
|
public func nullTerminatedString(from: Int) -> String? {
|
||||||
var nullOffset: Int?
|
var nullOffset: Int?
|
||||||
for i in from..<count {
|
for i in from..<count {
|
||||||
if (self[i] == 0) {
|
if self[i] == 0 {
|
||||||
nullOffset = i
|
nullOffset = i
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -137,7 +135,7 @@ extension Data {
|
||||||
// print("value: \(String(format: "%x", value))")
|
// print("value: \(String(format: "%x", value))")
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(*, deprecated)
|
@available(*, deprecated)
|
||||||
func UInt16ValueFromPointers(from: Int) -> UInt16 {
|
func UInt16ValueFromPointers(from: Int) -> UInt16 {
|
||||||
return subdata(in: from..<(from + 2)).withUnsafeBytes { $0.pointee }
|
return subdata(in: from..<(from + 2)).withUnsafeBytes { $0.pointee }
|
||||||
|
@ -155,7 +153,7 @@ extension Data {
|
||||||
// print("value: \(String(format: "%x", value))")
|
// print("value: \(String(format: "%x", value))")
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(*, deprecated)
|
@available(*, deprecated)
|
||||||
func UInt32ValueFromBuffer(from: Int) -> UInt32 {
|
func UInt32ValueFromBuffer(from: Int) -> UInt32 {
|
||||||
var value: UInt32 = 0
|
var value: UInt32 = 0
|
||||||
|
@ -167,7 +165,7 @@ extension Data {
|
||||||
// print("value: \(String(format: "%x", value))")
|
// print("value: \(String(format: "%x", value))")
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
// best
|
// best
|
||||||
public func UInt32Value(from: Int) -> UInt32 {
|
public func UInt32Value(from: Int) -> UInt32 {
|
||||||
return subdata(in: from..<(from + 4)).withUnsafeBytes {
|
return subdata(in: from..<(from + 4)).withUnsafeBytes {
|
||||||
|
|
|
@ -29,10 +29,10 @@ extension NSRegularExpression {
|
||||||
public convenience init(_ pattern: String) {
|
public convenience init(_ pattern: String) {
|
||||||
try! self.init(pattern: pattern, options: [])
|
try! self.init(pattern: pattern, options: [])
|
||||||
}
|
}
|
||||||
|
|
||||||
public func groups(in string: String) -> [String] {
|
public func groups(in string: String) -> [String] {
|
||||||
var results: [String] = []
|
var results: [String] = []
|
||||||
enumerateMatches(in: string, options: [], range: NSMakeRange(0, string.count)) { result, flags, stop in
|
enumerateMatches(in: string, options: [], range: NSRange(location: 0, length: string.count)) { result, _, _ in
|
||||||
guard let result = result else {
|
guard let result = result else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ extension NSRegularExpression {
|
||||||
|
|
||||||
extension NSRegularExpression {
|
extension NSRegularExpression {
|
||||||
public func enumerateSpacedComponents(in string: String, using block: ([String]) -> Void) {
|
public func enumerateSpacedComponents(in string: String, using block: ([String]) -> Void) {
|
||||||
enumerateMatches(in: string, options: [], range: NSMakeRange(0, string.count)) { result, flags, stop in
|
enumerateMatches(in: string, options: [], range: NSRange(location: 0, length: string.count)) { result, _, _ in
|
||||||
guard let range = result?.range else {
|
guard let range = result?.range else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ extension NSRegularExpression {
|
||||||
block(tokens)
|
block(tokens)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func enumerateSpacedArguments(in string: String, using block: ([String]) -> Void) {
|
public func enumerateSpacedArguments(in string: String, using block: ([String]) -> Void) {
|
||||||
enumerateSpacedComponents(in: string) { (tokens) in
|
enumerateSpacedComponents(in: string) { (tokens) in
|
||||||
var args = tokens
|
var args = tokens
|
||||||
|
|
|
@ -49,30 +49,30 @@ class DataManipulationTests: XCTestCase {
|
||||||
|
|
||||||
func testUInt() {
|
func testUInt() {
|
||||||
let data = Data([0x22, 0xff, 0xaa, 0xbb, 0x55, 0x66])
|
let data = Data([0x22, 0xff, 0xaa, 0xbb, 0x55, 0x66])
|
||||||
|
|
||||||
XCTAssertEqual(data.UInt16Value(from: 3), 0x55bb)
|
XCTAssertEqual(data.UInt16Value(from: 3), 0x55bb)
|
||||||
XCTAssertEqual(data.UInt32Value(from: 2), 0x6655bbaa)
|
XCTAssertEqual(data.UInt32Value(from: 2), 0x6655bbaa)
|
||||||
XCTAssertEqual(data.UInt16Value(from: 4), 0x6655)
|
XCTAssertEqual(data.UInt16Value(from: 4), 0x6655)
|
||||||
XCTAssertEqual(data.UInt32Value(from: 0), 0xbbaaff22)
|
XCTAssertEqual(data.UInt32Value(from: 0), 0xbbaaff22)
|
||||||
|
|
||||||
// XCTAssertEqual(data.UInt16Value(from: 3), data.UInt16ValueFromPointers(from: 3))
|
// XCTAssertEqual(data.UInt16Value(from: 3), data.UInt16ValueFromPointers(from: 3))
|
||||||
// XCTAssertEqual(data.UInt32Value(from: 2), data.UInt32ValueFromBuffer(from: 2))
|
// XCTAssertEqual(data.UInt32Value(from: 2), data.UInt32ValueFromBuffer(from: 2))
|
||||||
// XCTAssertEqual(data.UInt16Value(from: 4), data.UInt16ValueFromPointers(from: 4))
|
// XCTAssertEqual(data.UInt16Value(from: 4), data.UInt16ValueFromPointers(from: 4))
|
||||||
// XCTAssertEqual(data.UInt32Value(from: 0), data.UInt32ValueFromBuffer(from: 0))
|
// XCTAssertEqual(data.UInt32Value(from: 0), data.UInt32ValueFromBuffer(from: 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testZeroingData() {
|
func testZeroingData() {
|
||||||
let z1 = Z()
|
let z1 = Z()
|
||||||
z1.append(Z(Data(hex: "12345678")))
|
z1.append(Z(Data(hex: "12345678")))
|
||||||
z1.append(Z(Data(hex: "abcdef")))
|
z1.append(Z(Data(hex: "abcdef")))
|
||||||
let z2 = z1.withOffset(2, count: 3) // 5678ab
|
let z2 = z1.withOffset(2, count: 3) // 5678ab
|
||||||
let z3 = z2.appending(Z(Data(hex: "aaddcc"))) // 5678abaaddcc
|
let z3 = z2.appending(Z(Data(hex: "aaddcc"))) // 5678abaaddcc
|
||||||
|
|
||||||
XCTAssertEqual(z1.toData(), Data(hex: "12345678abcdef"))
|
XCTAssertEqual(z1.toData(), Data(hex: "12345678abcdef"))
|
||||||
XCTAssertEqual(z2.toData(), Data(hex: "5678ab"))
|
XCTAssertEqual(z2.toData(), Data(hex: "5678ab"))
|
||||||
XCTAssertEqual(z3.toData(), Data(hex: "5678abaaddcc"))
|
XCTAssertEqual(z3.toData(), Data(hex: "5678abaaddcc"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testFlatCount() {
|
func testFlatCount() {
|
||||||
var v: [Data] = []
|
var v: [Data] = []
|
||||||
v.append(Data(hex: "11223344"))
|
v.append(Data(hex: "11223344"))
|
||||||
|
|
|
@ -40,26 +40,26 @@ import XCTest
|
||||||
@testable import TunnelKitCore
|
@testable import TunnelKitCore
|
||||||
|
|
||||||
class RawPerformanceTests: XCTestCase {
|
class RawPerformanceTests: XCTestCase {
|
||||||
|
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tearDown() {
|
override func tearDown() {
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0.434s
|
// 0.434s
|
||||||
func testUInt16FromBuffer() {
|
func testUInt16FromBuffer() {
|
||||||
let data = Data([0x22, 0xff, 0xaa, 0xbb, 0x55, 0x66])
|
let data = Data([0x22, 0xff, 0xaa, 0xbb, 0x55, 0x66])
|
||||||
|
|
||||||
measure {
|
measure {
|
||||||
for _ in 0..<1000000 {
|
for _ in 0..<1000000 {
|
||||||
let _ = data.UInt16Value(from: 3)
|
_ = data.UInt16Value(from: 3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// // 0.463s
|
// // 0.463s
|
||||||
// func testUInt16FromPointers() {
|
// func testUInt16FromPointers() {
|
||||||
// let data = Data([0x22, 0xff, 0xaa, 0xbb, 0x55, 0x66])
|
// let data = Data([0x22, 0xff, 0xaa, 0xbb, 0x55, 0x66])
|
||||||
|
@ -81,18 +81,18 @@ class RawPerformanceTests: XCTestCase {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// 0.469s
|
// 0.469s
|
||||||
func testUInt32FromPointers() {
|
func testUInt32FromPointers() {
|
||||||
let data = Data([0x22, 0xff, 0xaa, 0xbb, 0x55, 0x66])
|
let data = Data([0x22, 0xff, 0xaa, 0xbb, 0x55, 0x66])
|
||||||
|
|
||||||
measure {
|
measure {
|
||||||
for _ in 0..<1000000 {
|
for _ in 0..<1000000 {
|
||||||
let _ = data.UInt32Value(from: 1)
|
_ = data.UInt32Value(from: 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// // 0.071s
|
// // 0.071s
|
||||||
// func testRandomUInt32FromBuffer() {
|
// func testRandomUInt32FromBuffer() {
|
||||||
// measure {
|
// measure {
|
||||||
|
@ -101,12 +101,12 @@ class RawPerformanceTests: XCTestCase {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// 0.063s
|
// 0.063s
|
||||||
func testRandomUInt32FromPointers() {
|
func testRandomUInt32FromPointers() {
|
||||||
measure {
|
measure {
|
||||||
for _ in 0..<10000 {
|
for _ in 0..<10000 {
|
||||||
let _ = try! SecureRandom.uint32()
|
_ = try! SecureRandom.uint32()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,7 +127,7 @@ class RawPerformanceTests: XCTestCase {
|
||||||
measure {
|
measure {
|
||||||
for data in suite {
|
for data in suite {
|
||||||
// let _ = UInt32(bigEndian: data.subdata(in: 0..<4).withUnsafeBytes { $0.pointee })
|
// let _ = UInt32(bigEndian: data.subdata(in: 0..<4).withUnsafeBytes { $0.pointee })
|
||||||
let _ = data.networkUInt32Value(from: 0)
|
_ = data.networkUInt32Value(from: 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ class RawPerformanceTests: XCTestCase {
|
||||||
let suite = TestUtils.generateDataSuite(1000, 100000)
|
let suite = TestUtils.generateDataSuite(1000, 100000)
|
||||||
measure {
|
measure {
|
||||||
for data in suite {
|
for data in suite {
|
||||||
let _ = data.subdata(in: 5..<data.count)
|
_ = data.subdata(in: 5..<data.count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ class RoutingTests: XCTestCase {
|
||||||
override func tearDown() {
|
override func tearDown() {
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEntryMatch4() {
|
func testEntryMatch4() {
|
||||||
let entry24 = RoutingTableEntry(iPv4Network: "192.168.1.0/24", gateway: nil, networkInterface: "en0")
|
let entry24 = RoutingTableEntry(iPv4Network: "192.168.1.0/24", gateway: nil, networkInterface: "en0")
|
||||||
print(entry24.networkMask()!)
|
print(entry24.networkMask()!)
|
||||||
|
@ -65,10 +65,10 @@ class RoutingTests: XCTestCase {
|
||||||
XCTAssertFalse(entry24.matchesDestination("abcd:efef:1233::\(i)"))
|
XCTAssertFalse(entry24.matchesDestination("abcd:efef:1233::\(i)"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testFindGatewayLAN4() {
|
func testFindGatewayLAN4() {
|
||||||
let table = RoutingTable()
|
let table = RoutingTable()
|
||||||
|
|
||||||
for entry in table.ipv4() {
|
for entry in table.ipv4() {
|
||||||
print(entry)
|
print(entry)
|
||||||
}
|
}
|
||||||
|
@ -83,11 +83,11 @@ class RoutingTests: XCTestCase {
|
||||||
|
|
||||||
func testFindGatewayLAN6() {
|
func testFindGatewayLAN6() {
|
||||||
let table = RoutingTable()
|
let table = RoutingTable()
|
||||||
|
|
||||||
for entry in table.ipv6() {
|
for entry in table.ipv6() {
|
||||||
print(entry)
|
print(entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let defaultGateway = table.defaultGateway6()?.gateway() {
|
if let defaultGateway = table.defaultGateway6()?.gateway() {
|
||||||
print("Default gateway: \(defaultGateway)")
|
print("Default gateway: \(defaultGateway)")
|
||||||
if let lan = table.broadestRoute6(matchingDestination: defaultGateway) {
|
if let lan = table.broadestRoute6(matchingDestination: defaultGateway) {
|
||||||
|
@ -95,13 +95,13 @@ class RoutingTests: XCTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPartitioning() {
|
func testPartitioning() {
|
||||||
let v4 = RoutingTableEntry(iPv4Network: "192.168.1.0/24", gateway: nil, networkInterface: "en0")
|
let v4 = RoutingTableEntry(iPv4Network: "192.168.1.0/24", gateway: nil, networkInterface: "en0")
|
||||||
let v4Boundary = RoutingTableEntry(iPv4Network: "192.168.1.0/31", gateway: nil, networkInterface: "en0")
|
let v4Boundary = RoutingTableEntry(iPv4Network: "192.168.1.0/31", gateway: nil, networkInterface: "en0")
|
||||||
let v6 = RoutingTableEntry(iPv6Network: "abcd:efef:120::/46", gateway: nil, networkInterface: "en0")
|
let v6 = RoutingTableEntry(iPv6Network: "abcd:efef:120::/46", gateway: nil, networkInterface: "en0")
|
||||||
let v6Boundary = RoutingTableEntry(iPv6Network: "abcd:efef:120::/127", gateway: nil, networkInterface: "en0")
|
let v6Boundary = RoutingTableEntry(iPv6Network: "abcd:efef:120::/127", gateway: nil, networkInterface: "en0")
|
||||||
|
|
||||||
guard let v4parts = v4.partitioned() else {
|
guard let v4parts = v4.partitioned() else {
|
||||||
fatalError()
|
fatalError()
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ public class TestUtils {
|
||||||
}
|
}
|
||||||
return suite
|
return suite
|
||||||
}
|
}
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ class CompressionTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSymmetric() {
|
func testSymmetric() {
|
||||||
XCTAssertTrue(LZOFactory.isSupported());
|
XCTAssertTrue(LZOFactory.isSupported())
|
||||||
let lzo = LZOFactory.create()
|
let lzo = LZOFactory.create()
|
||||||
let src = Data([UInt8](repeating: 6, count: 100))
|
let src = Data([UInt8](repeating: 6, count: 100))
|
||||||
guard let dst = try? lzo.compressedData(with: src) else {
|
guard let dst = try? lzo.compressedData(with: src) else {
|
||||||
|
|
|
@ -44,12 +44,12 @@ import TunnelKitManager
|
||||||
import TunnelKitOpenVPNManager
|
import TunnelKitOpenVPNManager
|
||||||
|
|
||||||
class AppExtensionTests: XCTestCase {
|
class AppExtensionTests: XCTestCase {
|
||||||
|
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
super.setUp()
|
super.setUp()
|
||||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tearDown() {
|
override func tearDown() {
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||||
super.tearDown()
|
super.tearDown()
|
||||||
|
@ -80,7 +80,7 @@ class AppExtensionTests: XCTestCase {
|
||||||
XCTFail(error.localizedDescription)
|
XCTFail(error.localizedDescription)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
XCTAssertEqual(proto.providerBundleIdentifier, bundleIdentifier)
|
XCTAssertEqual(proto.providerBundleIdentifier, bundleIdentifier)
|
||||||
XCTAssertEqual(proto.serverAddress, serverAddress)
|
XCTAssertEqual(proto.serverAddress, serverAddress)
|
||||||
XCTAssertEqual(proto.username, credentials.username)
|
XCTAssertEqual(proto.username, credentials.username)
|
||||||
|
@ -99,7 +99,7 @@ class AppExtensionTests: XCTestCase {
|
||||||
XCTAssertEqual(ovpn?["mtu"] as? Int, cfg.configuration.mtu)
|
XCTAssertEqual(ovpn?["mtu"] as? Int, cfg.configuration.mtu)
|
||||||
XCTAssertEqual(ovpn?["renegotiatesAfter"] as? TimeInterval, cfg.configuration.renegotiatesAfter)
|
XCTAssertEqual(ovpn?["renegotiatesAfter"] as? TimeInterval, cfg.configuration.renegotiatesAfter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDNSResolver() {
|
func testDNSResolver() {
|
||||||
let exp = expectation(description: "DNS")
|
let exp = expectation(description: "DNS")
|
||||||
DNSResolver.resolve("www.google.com", timeout: 1000, queue: .main) {
|
DNSResolver.resolve("www.google.com", timeout: 1000, queue: .main) {
|
||||||
|
@ -116,7 +116,7 @@ class AppExtensionTests: XCTestCase {
|
||||||
}
|
}
|
||||||
waitForExpectations(timeout: 5.0, handler: nil)
|
waitForExpectations(timeout: 5.0, handler: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDNSAddressConversion() {
|
func testDNSAddressConversion() {
|
||||||
let testStrings = [
|
let testStrings = [
|
||||||
"0.0.0.0",
|
"0.0.0.0",
|
||||||
|
@ -148,7 +148,7 @@ class AppExtensionTests: XCTestCase {
|
||||||
.init(hostname, .init(.udp4, 3333))
|
.init(hostname, .init(.udp4, 3333))
|
||||||
]
|
]
|
||||||
let strategy = ConnectionStrategy(configuration: builder.build())
|
let strategy = ConnectionStrategy(configuration: builder.build())
|
||||||
|
|
||||||
let expected = [
|
let expected = [
|
||||||
"italy.privateinternetaccess.com:TCP6:2222",
|
"italy.privateinternetaccess.com:TCP6:2222",
|
||||||
"italy.privateinternetaccess.com:UDP:1111",
|
"italy.privateinternetaccess.com:UDP:1111",
|
||||||
|
|
|
@ -32,24 +32,24 @@ class ConfigurationParserTests: XCTestCase {
|
||||||
super.setUp()
|
super.setUp()
|
||||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tearDown() {
|
override func tearDown() {
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||||
super.tearDown()
|
super.tearDown()
|
||||||
}
|
}
|
||||||
|
|
||||||
// from lines
|
// from lines
|
||||||
|
|
||||||
func testCompression() throws {
|
func testCompression() throws {
|
||||||
XCTAssertNil(try OpenVPN.ConfigurationParser.parsed(fromLines: ["comp-lzo"]).warning)
|
XCTAssertNil(try OpenVPN.ConfigurationParser.parsed(fromLines: ["comp-lzo"]).warning)
|
||||||
XCTAssertNoThrow(try OpenVPN.ConfigurationParser.parsed(fromLines: ["comp-lzo no"]))
|
XCTAssertNoThrow(try OpenVPN.ConfigurationParser.parsed(fromLines: ["comp-lzo no"]))
|
||||||
XCTAssertNoThrow(try OpenVPN.ConfigurationParser.parsed(fromLines: ["comp-lzo yes"]))
|
XCTAssertNoThrow(try OpenVPN.ConfigurationParser.parsed(fromLines: ["comp-lzo yes"]))
|
||||||
// XCTAssertThrowsError(try OpenVPN.ConfigurationParser.parsed(fromLines: ["comp-lzo yes"]))
|
// XCTAssertThrowsError(try OpenVPN.ConfigurationParser.parsed(fromLines: ["comp-lzo yes"]))
|
||||||
|
|
||||||
XCTAssertNoThrow(try OpenVPN.ConfigurationParser.parsed(fromLines: ["compress"]))
|
XCTAssertNoThrow(try OpenVPN.ConfigurationParser.parsed(fromLines: ["compress"]))
|
||||||
XCTAssertNoThrow(try OpenVPN.ConfigurationParser.parsed(fromLines: ["compress lzo"]))
|
XCTAssertNoThrow(try OpenVPN.ConfigurationParser.parsed(fromLines: ["compress lzo"]))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testKeepAlive() throws {
|
func testKeepAlive() throws {
|
||||||
let cfg1 = try OpenVPN.ConfigurationParser.parsed(fromLines: ["ping 10", "ping-restart 60"])
|
let cfg1 = try OpenVPN.ConfigurationParser.parsed(fromLines: ["ping 10", "ping-restart 60"])
|
||||||
let cfg2 = try OpenVPN.ConfigurationParser.parsed(fromLines: ["keepalive 10 60"])
|
let cfg2 = try OpenVPN.ConfigurationParser.parsed(fromLines: ["keepalive 10 60"])
|
||||||
|
@ -59,7 +59,7 @@ class ConfigurationParserTests: XCTestCase {
|
||||||
XCTAssertNotEqual(cfg1.configuration.keepAliveInterval, cfg3.configuration.keepAliveInterval)
|
XCTAssertNotEqual(cfg1.configuration.keepAliveInterval, cfg3.configuration.keepAliveInterval)
|
||||||
XCTAssertNotEqual(cfg1.configuration.keepAliveTimeout, cfg3.configuration.keepAliveTimeout)
|
XCTAssertNotEqual(cfg1.configuration.keepAliveTimeout, cfg3.configuration.keepAliveTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDHCPOption() throws {
|
func testDHCPOption() throws {
|
||||||
let lines = [
|
let lines = [
|
||||||
"dhcp-option DNS 8.8.8.8",
|
"dhcp-option DNS 8.8.8.8",
|
||||||
|
@ -76,7 +76,7 @@ class ConfigurationParserTests: XCTestCase {
|
||||||
"dhcp-option PROXY_BYPASS foo.com bar.org net.chat"
|
"dhcp-option PROXY_BYPASS foo.com bar.org net.chat"
|
||||||
]
|
]
|
||||||
XCTAssertNoThrow(try OpenVPN.ConfigurationParser.parsed(fromLines: lines))
|
XCTAssertNoThrow(try OpenVPN.ConfigurationParser.parsed(fromLines: lines))
|
||||||
|
|
||||||
let parsed = try! OpenVPN.ConfigurationParser.parsed(fromLines: lines).configuration
|
let parsed = try! OpenVPN.ConfigurationParser.parsed(fromLines: lines).configuration
|
||||||
XCTAssertEqual(parsed.dnsServers, ["8.8.8.8", "ffff::1"])
|
XCTAssertEqual(parsed.dnsServers, ["8.8.8.8", "ffff::1"])
|
||||||
XCTAssertEqual(parsed.dnsDomain, "second-domain.org")
|
XCTAssertEqual(parsed.dnsDomain, "second-domain.org")
|
||||||
|
@ -88,7 +88,7 @@ class ConfigurationParserTests: XCTestCase {
|
||||||
XCTAssertEqual(parsed.proxyAutoConfigurationURL?.absoluteString, "https://pac/")
|
XCTAssertEqual(parsed.proxyAutoConfigurationURL?.absoluteString, "https://pac/")
|
||||||
XCTAssertEqual(parsed.proxyBypassDomains, ["foo.com", "bar.org", "net.chat"])
|
XCTAssertEqual(parsed.proxyBypassDomains, ["foo.com", "bar.org", "net.chat"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRedirectGateway() throws {
|
func testRedirectGateway() throws {
|
||||||
var parsed: OpenVPN.Configuration
|
var parsed: OpenVPN.Configuration
|
||||||
|
|
||||||
|
@ -105,12 +105,12 @@ class ConfigurationParserTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
// from file
|
// from file
|
||||||
|
|
||||||
func testPIA() throws {
|
func testPIA() throws {
|
||||||
let file = try OpenVPN.ConfigurationParser.parsed(fromURL: url(withName: "pia-hungary"))
|
let file = try OpenVPN.ConfigurationParser.parsed(fromURL: url(withName: "pia-hungary"))
|
||||||
XCTAssertEqual(file.configuration.remotes, [
|
XCTAssertEqual(file.configuration.remotes, [
|
||||||
.init("hungary.privateinternetaccess.com", .init(.udp, 1198)),
|
.init("hungary.privateinternetaccess.com", .init(.udp, 1198)),
|
||||||
.init("hungary.privateinternetaccess.com", .init(.tcp, 502)),
|
.init("hungary.privateinternetaccess.com", .init(.tcp, 502))
|
||||||
])
|
])
|
||||||
XCTAssertEqual(file.configuration.cipher, .aes128cbc)
|
XCTAssertEqual(file.configuration.cipher, .aes128cbc)
|
||||||
XCTAssertEqual(file.configuration.digest, .sha1)
|
XCTAssertEqual(file.configuration.digest, .sha1)
|
||||||
|
@ -121,38 +121,38 @@ class ConfigurationParserTests: XCTestCase {
|
||||||
let stripped = lines.joined(separator: "\n")
|
let stripped = lines.joined(separator: "\n")
|
||||||
print(stripped)
|
print(stripped)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEncryptedCertificateKey() throws {
|
func testEncryptedCertificateKey() throws {
|
||||||
try privateTestEncryptedCertificateKey(pkcs: "1")
|
try privateTestEncryptedCertificateKey(pkcs: "1")
|
||||||
try privateTestEncryptedCertificateKey(pkcs: "8")
|
try privateTestEncryptedCertificateKey(pkcs: "8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testXOR() throws {
|
func testXOR() throws {
|
||||||
let cfg = try OpenVPN.ConfigurationParser.parsed(fromLines: ["scramble xormask F"])
|
let cfg = try OpenVPN.ConfigurationParser.parsed(fromLines: ["scramble xormask F"])
|
||||||
XCTAssertNil(cfg.warning)
|
XCTAssertNil(cfg.warning)
|
||||||
XCTAssertEqual(cfg.configuration.xorMethod, OpenVPN.XORMethod.xormask(mask: Data(repeating: Character("F").asciiValue!, count:1)))
|
XCTAssertEqual(cfg.configuration.xorMethod, OpenVPN.XORMethod.xormask(mask: Data(repeating: Character("F").asciiValue!, count: 1)))
|
||||||
|
|
||||||
let cfg2 = try OpenVPN.ConfigurationParser.parsed(fromLines: ["scramble reverse"])
|
let cfg2 = try OpenVPN.ConfigurationParser.parsed(fromLines: ["scramble reverse"])
|
||||||
XCTAssertNil(cfg.warning)
|
XCTAssertNil(cfg.warning)
|
||||||
XCTAssertEqual(cfg2.configuration.xorMethod, OpenVPN.XORMethod.reverse)
|
XCTAssertEqual(cfg2.configuration.xorMethod, OpenVPN.XORMethod.reverse)
|
||||||
|
|
||||||
let cfg3 = try OpenVPN.ConfigurationParser.parsed(fromLines: ["scramble xorptrpos"])
|
let cfg3 = try OpenVPN.ConfigurationParser.parsed(fromLines: ["scramble xorptrpos"])
|
||||||
XCTAssertNil(cfg.warning)
|
XCTAssertNil(cfg.warning)
|
||||||
XCTAssertEqual(cfg3.configuration.xorMethod, OpenVPN.XORMethod.xorptrpos)
|
XCTAssertEqual(cfg3.configuration.xorMethod, OpenVPN.XORMethod.xorptrpos)
|
||||||
|
|
||||||
let cfg4 = try OpenVPN.ConfigurationParser.parsed(fromLines: ["scramble obfuscate FFFF"])
|
let cfg4 = try OpenVPN.ConfigurationParser.parsed(fromLines: ["scramble obfuscate FFFF"])
|
||||||
XCTAssertNil(cfg.warning)
|
XCTAssertNil(cfg.warning)
|
||||||
XCTAssertEqual(cfg4.configuration.xorMethod, OpenVPN.XORMethod.obfuscate(mask: Data(repeating: Character("F").asciiValue!, count:4)))
|
XCTAssertEqual(cfg4.configuration.xorMethod, OpenVPN.XORMethod.obfuscate(mask: Data(repeating: Character("F").asciiValue!, count: 4)))
|
||||||
}
|
}
|
||||||
|
|
||||||
private func privateTestEncryptedCertificateKey(pkcs: String) throws {
|
private func privateTestEncryptedCertificateKey(pkcs: String) throws {
|
||||||
let cfgURL = url(withName: "tunnelbear.enc.\(pkcs)")
|
let cfgURL = url(withName: "tunnelbear.enc.\(pkcs)")
|
||||||
XCTAssertThrowsError(try OpenVPN.ConfigurationParser.parsed(fromURL: cfgURL))
|
XCTAssertThrowsError(try OpenVPN.ConfigurationParser.parsed(fromURL: cfgURL))
|
||||||
XCTAssertNoThrow(try OpenVPN.ConfigurationParser.parsed(fromURL: cfgURL, passphrase: "foobar"))
|
XCTAssertNoThrow(try OpenVPN.ConfigurationParser.parsed(fromURL: cfgURL, passphrase: "foobar"))
|
||||||
}
|
}
|
||||||
|
|
||||||
private func url(withName name: String) -> URL {
|
private func url(withName name: String) -> URL {
|
||||||
return Bundle.module.url(forResource: name, withExtension: "ovpn")!
|
return Bundle.module.url(forResource: name, withExtension: "ovpn")!
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,15 +30,15 @@ import TunnelKitOpenVPNCore
|
||||||
class ConfigurationTests: XCTestCase {
|
class ConfigurationTests: XCTestCase {
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
super.setUp()
|
super.setUp()
|
||||||
|
|
||||||
CoreConfiguration.masksPrivateData = false
|
CoreConfiguration.masksPrivateData = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tearDown() {
|
override func tearDown() {
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||||
super.tearDown()
|
super.tearDown()
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRandomizeHostnames() {
|
func testRandomizeHostnames() {
|
||||||
var builder = OpenVPN.ConfigurationBuilder()
|
var builder = OpenVPN.ConfigurationBuilder()
|
||||||
let hostname = "my.host.name"
|
let hostname = "my.host.name"
|
||||||
|
@ -49,7 +49,7 @@ class ConfigurationTests: XCTestCase {
|
||||||
]
|
]
|
||||||
builder.randomizeHostnames = true
|
builder.randomizeHostnames = true
|
||||||
let cfg = builder.build()
|
let cfg = builder.build()
|
||||||
|
|
||||||
cfg.processedRemotes?.forEach {
|
cfg.processedRemotes?.forEach {
|
||||||
let comps = $0.address.components(separatedBy: ".")
|
let comps = $0.address.components(separatedBy: ".")
|
||||||
guard let first = comps.first else {
|
guard let first = comps.first else {
|
||||||
|
|
|
@ -39,7 +39,7 @@ class ControlChannelTests: XCTestCase {
|
||||||
super.setUp()
|
super.setUp()
|
||||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tearDown() {
|
override func tearDown() {
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||||
super.tearDown()
|
super.tearDown()
|
||||||
|
@ -55,16 +55,16 @@ class ControlChannelTests: XCTestCase {
|
||||||
let key = OpenVPN.StaticKey(biData: Data(hex: hex))
|
let key = OpenVPN.StaticKey(biData: Data(hex: hex))
|
||||||
let server = CryptoBox(cipherAlgorithm: nil, digestAlgorithm: OpenVPN.Digest.sha1.rawValue)
|
let server = CryptoBox(cipherAlgorithm: nil, digestAlgorithm: OpenVPN.Digest.sha1.rawValue)
|
||||||
XCTAssertNoThrow(try server.configure(withCipherEncKey: nil, cipherDecKey: nil, hmacEncKey: key.hmacReceiveKey, hmacDecKey: key.hmacSendKey))
|
XCTAssertNoThrow(try server.configure(withCipherEncKey: nil, cipherDecKey: nil, hmacEncKey: key.hmacReceiveKey, hmacDecKey: key.hmacSendKey))
|
||||||
|
|
||||||
// let original = Data(hex: "38858fe14742fdae40e67c9137933a412a711c0d0514aca6db6476d17d000000015b96c9470000000000")
|
// let original = Data(hex: "38858fe14742fdae40e67c9137933a412a711c0d0514aca6db6476d17d000000015b96c9470000000000")
|
||||||
let hmac = Data(hex: "e67c9137933a412a711c0d0514aca6db6476d17d")
|
let hmac = Data(hex: "e67c9137933a412a711c0d0514aca6db6476d17d")
|
||||||
let subject = Data(hex: "000000015b96c94738858fe14742fdae400000000000")
|
let subject = Data(hex: "000000015b96c94738858fe14742fdae400000000000")
|
||||||
let data = hmac + subject
|
let data = hmac + subject
|
||||||
print(data.toHex())
|
print(data.toHex())
|
||||||
|
|
||||||
XCTAssertNoThrow(try server.decrypter().verifyData(data, flags: nil))
|
XCTAssertNoThrow(try server.decrypter().verifyData(data, flags: nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 38 // HARD_RESET
|
// 38 // HARD_RESET
|
||||||
// bccfd171ce22e085 // session_id
|
// bccfd171ce22e085 // session_id
|
||||||
// e01a3454c354f3c3093b00fc8d6228a8b69ef503d56f6a572ebd26a800711b4cd4df2b9daf06cb90f82379e7815e39fb73be4ac5461752db4f35120474af82b2 // hmac
|
// e01a3454c354f3c3093b00fc8d6228a8b69ef503d56f6a572ebd26a800711b4cd4df2b9daf06cb90f82379e7815e39fb73be4ac5461752db4f35120474af82b2 // hmac
|
||||||
|
@ -74,11 +74,11 @@ class ControlChannelTests: XCTestCase {
|
||||||
func testAuth() {
|
func testAuth() {
|
||||||
let client = try! OpenVPN.ControlChannel.AuthSerializer(withKey: OpenVPN.StaticKey(data: Data(hex: hex), direction: .client), digest: .sha512)
|
let client = try! OpenVPN.ControlChannel.AuthSerializer(withKey: OpenVPN.StaticKey(data: Data(hex: hex), direction: .client), digest: .sha512)
|
||||||
let server = try! OpenVPN.ControlChannel.AuthSerializer(withKey: OpenVPN.StaticKey(data: Data(hex: hex), direction: .server), digest: .sha512)
|
let server = try! OpenVPN.ControlChannel.AuthSerializer(withKey: OpenVPN.StaticKey(data: Data(hex: hex), direction: .server), digest: .sha512)
|
||||||
|
|
||||||
// let original = Data(hex: "38bccfd1")
|
// let original = Data(hex: "38bccfd1")
|
||||||
let original = Data(hex: "38bccfd171ce22e085e01a3454c354f3c3093b00fc8d6228a8b69ef503d56f6a572ebd26a800711b4cd4df2b9daf06cb90f82379e7815e39fb73be4ac5461752db4f35120474af82b2000000015b93b65d0000000000")
|
let original = Data(hex: "38bccfd171ce22e085e01a3454c354f3c3093b00fc8d6228a8b69ef503d56f6a572ebd26a800711b4cd4df2b9daf06cb90f82379e7815e39fb73be4ac5461752db4f35120474af82b2000000015b93b65d0000000000")
|
||||||
let timestamp = UInt32(0x5b93b65d)
|
let timestamp = UInt32(0x5b93b65d)
|
||||||
|
|
||||||
let packet: ControlPacket
|
let packet: ControlPacket
|
||||||
do {
|
do {
|
||||||
packet = try client.deserialize(data: original, start: 0, end: nil)
|
packet = try client.deserialize(data: original, start: 0, end: nil)
|
||||||
|
@ -90,7 +90,7 @@ class ControlChannelTests: XCTestCase {
|
||||||
XCTAssertEqual(packet.sessionId, Data(hex: "bccfd171ce22e085"))
|
XCTAssertEqual(packet.sessionId, Data(hex: "bccfd171ce22e085"))
|
||||||
XCTAssertNil(packet.ackIds)
|
XCTAssertNil(packet.ackIds)
|
||||||
XCTAssertEqual(packet.packetId, 0)
|
XCTAssertEqual(packet.packetId, 0)
|
||||||
|
|
||||||
let raw: Data
|
let raw: Data
|
||||||
do {
|
do {
|
||||||
raw = try server.serialize(packet: packet, timestamp: timestamp)
|
raw = try server.serialize(packet: packet, timestamp: timestamp)
|
||||||
|
@ -109,7 +109,7 @@ class ControlChannelTests: XCTestCase {
|
||||||
|
|
||||||
let original = Data(hex: "407bf3d6a260e6476d000000015ba4155887940856ddb70e01693980c5c955cb5506ecf9fd3e0bcee0c802ec269427d43bf1cda1837ffbf30c83cacff852cd0b7f4c")
|
let original = Data(hex: "407bf3d6a260e6476d000000015ba4155887940856ddb70e01693980c5c955cb5506ecf9fd3e0bcee0c802ec269427d43bf1cda1837ffbf30c83cacff852cd0b7f4c")
|
||||||
let timestamp = UInt32(0x5ba41558)
|
let timestamp = UInt32(0x5ba41558)
|
||||||
|
|
||||||
let packet: ControlPacket
|
let packet: ControlPacket
|
||||||
do {
|
do {
|
||||||
packet = try client.deserialize(data: original, start: 0, end: nil)
|
packet = try client.deserialize(data: original, start: 0, end: nil)
|
||||||
|
|
|
@ -45,28 +45,28 @@ class DataPathEncryptionTests: XCTestCase {
|
||||||
private let hmacKey = try! SecureRandom.safeData(length: 32)
|
private let hmacKey = try! SecureRandom.safeData(length: 32)
|
||||||
|
|
||||||
private var enc: DataPathEncrypter!
|
private var enc: DataPathEncrypter!
|
||||||
|
|
||||||
private var dec: DataPathDecrypter!
|
private var dec: DataPathDecrypter!
|
||||||
|
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tearDown() {
|
override func tearDown() {
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCBC() {
|
func testCBC() {
|
||||||
prepareBox(cipher: "aes-128-cbc", digest: "sha256")
|
prepareBox(cipher: "aes-128-cbc", digest: "sha256")
|
||||||
privateTestDataPathHigh(peerId: nil)
|
privateTestDataPathHigh(peerId: nil)
|
||||||
privateTestDataPathLow(peerId: nil)
|
privateTestDataPathLow(peerId: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testFloatingCBC() {
|
func testFloatingCBC() {
|
||||||
prepareBox(cipher: "aes-128-cbc", digest: "sha256")
|
prepareBox(cipher: "aes-128-cbc", digest: "sha256")
|
||||||
privateTestDataPathHigh(peerId: 0x64385837)
|
privateTestDataPathHigh(peerId: 0x64385837)
|
||||||
privateTestDataPathLow(peerId: 0x64385837)
|
privateTestDataPathLow(peerId: 0x64385837)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testGCM() {
|
func testGCM() {
|
||||||
prepareBox(cipher: "aes-256-gcm", digest: nil)
|
prepareBox(cipher: "aes-256-gcm", digest: nil)
|
||||||
privateTestDataPathHigh(peerId: nil)
|
privateTestDataPathHigh(peerId: nil)
|
||||||
|
@ -78,14 +78,14 @@ class DataPathEncryptionTests: XCTestCase {
|
||||||
privateTestDataPathHigh(peerId: 0x64385837)
|
privateTestDataPathHigh(peerId: 0x64385837)
|
||||||
privateTestDataPathLow(peerId: 0x64385837)
|
privateTestDataPathLow(peerId: 0x64385837)
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareBox(cipher: String, digest: String?) {
|
func prepareBox(cipher: String, digest: String?) {
|
||||||
let box = CryptoBox(cipherAlgorithm: cipher, digestAlgorithm: digest)
|
let box = CryptoBox(cipherAlgorithm: cipher, digestAlgorithm: digest)
|
||||||
try! box.configure(withCipherEncKey: cipherKey, cipherDecKey: cipherKey, hmacEncKey: hmacKey, hmacDecKey: hmacKey)
|
try! box.configure(withCipherEncKey: cipherKey, cipherDecKey: cipherKey, hmacEncKey: hmacKey, hmacDecKey: hmacKey)
|
||||||
enc = box.encrypter().dataPathEncrypter()
|
enc = box.encrypter().dataPathEncrypter()
|
||||||
dec = box.decrypter().dataPathDecrypter()
|
dec = box.decrypter().dataPathDecrypter()
|
||||||
}
|
}
|
||||||
|
|
||||||
func privateTestDataPathHigh(peerId: UInt32?) {
|
func privateTestDataPathHigh(peerId: UInt32?) {
|
||||||
let path = DataPath(
|
let path = DataPath(
|
||||||
encrypter: enc,
|
encrypter: enc,
|
||||||
|
|
|
@ -47,15 +47,15 @@ class DataPathPerformanceTests: XCTestCase {
|
||||||
private var encrypter: DataPathEncrypter!
|
private var encrypter: DataPathEncrypter!
|
||||||
|
|
||||||
private var decrypter: DataPathDecrypter!
|
private var decrypter: DataPathDecrypter!
|
||||||
|
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
let ck = try! SecureRandom.safeData(length: 32)
|
let ck = try! SecureRandom.safeData(length: 32)
|
||||||
let hk = try! SecureRandom.safeData(length: 32)
|
let hk = try! SecureRandom.safeData(length: 32)
|
||||||
|
|
||||||
let crypto = try! OpenVPN.EncryptionBridge(.aes128cbc, .sha1, ck, ck, hk, hk)
|
let crypto = try! OpenVPN.EncryptionBridge(.aes128cbc, .sha1, ck, ck, hk, hk)
|
||||||
encrypter = crypto.encrypter()
|
encrypter = crypto.encrypter()
|
||||||
decrypter = crypto.decrypter()
|
decrypter = crypto.decrypter()
|
||||||
|
|
||||||
dataPath = DataPath(
|
dataPath = DataPath(
|
||||||
encrypter: encrypter,
|
encrypter: encrypter,
|
||||||
decrypter: decrypter,
|
decrypter: decrypter,
|
||||||
|
@ -85,18 +85,18 @@ class DataPathPerformanceTests: XCTestCase {
|
||||||
//// print(">>> \(packets?.count) packets")
|
//// print(">>> \(packets?.count) packets")
|
||||||
// XCTAssertEqual(decryptedPackets, packets)
|
// XCTAssertEqual(decryptedPackets, packets)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// 16ms
|
// 16ms
|
||||||
func testPointerBased() {
|
func testPointerBased() {
|
||||||
let packets = TestUtils.generateDataSuite(1200, 1000)
|
let packets = TestUtils.generateDataSuite(1200, 1000)
|
||||||
var encryptedPackets: [Data]!
|
var encryptedPackets: [Data]!
|
||||||
var decryptedPackets: [Data]!
|
var decryptedPackets: [Data]!
|
||||||
|
|
||||||
measure {
|
measure {
|
||||||
encryptedPackets = try! self.dataPath.encryptPackets(packets, key: 0)
|
encryptedPackets = try! self.dataPath.encryptPackets(packets, key: 0)
|
||||||
decryptedPackets = try! self.dataPath.decryptPackets(encryptedPackets, keepAlive: nil)
|
decryptedPackets = try! self.dataPath.decryptPackets(encryptedPackets, keepAlive: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// print(">>> \(packets?.count) packets")
|
// print(">>> \(packets?.count) packets")
|
||||||
XCTAssertEqual(decryptedPackets, packets)
|
XCTAssertEqual(decryptedPackets, packets)
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,17 +41,17 @@ import CTunnelKitOpenVPNProtocol
|
||||||
|
|
||||||
class EncryptionPerformanceTests: XCTestCase {
|
class EncryptionPerformanceTests: XCTestCase {
|
||||||
private var cbcEncrypter: Encrypter!
|
private var cbcEncrypter: Encrypter!
|
||||||
|
|
||||||
private var cbcDecrypter: Decrypter!
|
private var cbcDecrypter: Decrypter!
|
||||||
|
|
||||||
private var gcmEncrypter: Encrypter!
|
private var gcmEncrypter: Encrypter!
|
||||||
|
|
||||||
private var gcmDecrypter: Decrypter!
|
private var gcmDecrypter: Decrypter!
|
||||||
|
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
let cipherKey = try! SecureRandom.safeData(length: 32)
|
let cipherKey = try! SecureRandom.safeData(length: 32)
|
||||||
let hmacKey = try! SecureRandom.safeData(length: 32)
|
let hmacKey = try! SecureRandom.safeData(length: 32)
|
||||||
|
|
||||||
let cbc = CryptoBox(cipherAlgorithm: "aes-128-cbc", digestAlgorithm: "sha1")
|
let cbc = CryptoBox(cipherAlgorithm: "aes-128-cbc", digestAlgorithm: "sha1")
|
||||||
try! cbc.configure(withCipherEncKey: cipherKey, cipherDecKey: cipherKey, hmacEncKey: hmacKey, hmacDecKey: hmacKey)
|
try! cbc.configure(withCipherEncKey: cipherKey, cipherDecKey: cipherKey, hmacEncKey: hmacKey, hmacDecKey: hmacKey)
|
||||||
cbcEncrypter = cbc.encrypter()
|
cbcEncrypter = cbc.encrypter()
|
||||||
|
@ -72,7 +72,7 @@ class EncryptionPerformanceTests: XCTestCase {
|
||||||
let suite = TestUtils.generateDataSuite(1000, 100000)
|
let suite = TestUtils.generateDataSuite(1000, 100000)
|
||||||
measure {
|
measure {
|
||||||
for data in suite {
|
for data in suite {
|
||||||
let _ = try! self.cbcEncrypter.encryptData(data, flags: nil)
|
_ = try! self.cbcEncrypter.encryptData(data, flags: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ class EncryptionPerformanceTests: XCTestCase {
|
||||||
}
|
}
|
||||||
measure {
|
measure {
|
||||||
for data in suite {
|
for data in suite {
|
||||||
let _ = try! self.gcmEncrypter.encryptData(data, flags: &flags)
|
_ = try! self.gcmEncrypter.encryptData(data, flags: &flags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,11 +44,11 @@ class EncryptionTests: XCTestCase {
|
||||||
private var cipherEncKey: ZeroingData!
|
private var cipherEncKey: ZeroingData!
|
||||||
|
|
||||||
private var cipherDecKey: ZeroingData!
|
private var cipherDecKey: ZeroingData!
|
||||||
|
|
||||||
private var hmacEncKey: ZeroingData!
|
private var hmacEncKey: ZeroingData!
|
||||||
|
|
||||||
private var hmacDecKey: ZeroingData!
|
private var hmacDecKey: ZeroingData!
|
||||||
|
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
cipherEncKey = try! SecureRandom.safeData(length: 32)
|
cipherEncKey = try! SecureRandom.safeData(length: 32)
|
||||||
cipherDecKey = try! SecureRandom.safeData(length: 32)
|
cipherDecKey = try! SecureRandom.safeData(length: 32)
|
||||||
|
@ -76,10 +76,10 @@ class EncryptionTests: XCTestCase {
|
||||||
let encrypted = try! client.encrypter().encryptData(plain, flags: nil)
|
let encrypted = try! client.encrypter().encryptData(plain, flags: nil)
|
||||||
XCTAssertNoThrow(try server.decrypter().verifyData(encrypted, flags: nil))
|
XCTAssertNoThrow(try server.decrypter().verifyData(encrypted, flags: nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testGCM() {
|
func testGCM() {
|
||||||
let (client, server) = clientServer("aes-256-gcm", nil)
|
let (client, server) = clientServer("aes-256-gcm", nil)
|
||||||
|
|
||||||
let packetId: [UInt8] = [0x56, 0x34, 0x12, 0x00]
|
let packetId: [UInt8] = [0x56, 0x34, 0x12, 0x00]
|
||||||
let ad: [UInt8] = [0x00, 0x12, 0x34, 0x56]
|
let ad: [UInt8] = [0x00, 0x12, 0x34, 0x56]
|
||||||
var flags = packetId.withUnsafeBufferPointer { (iv) in
|
var flags = packetId.withUnsafeBufferPointer { (iv) in
|
||||||
|
@ -92,7 +92,7 @@ class EncryptionTests: XCTestCase {
|
||||||
let decrypted = try! server.decrypter().decryptData(encrypted, flags: &flags)
|
let decrypted = try! server.decrypter().decryptData(encrypted, flags: &flags)
|
||||||
XCTAssertEqual(plain, decrypted)
|
XCTAssertEqual(plain, decrypted)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCTR() {
|
func testCTR() {
|
||||||
let (client, server) = clientServer("aes-256-ctr", "sha256")
|
let (client, server) = clientServer("aes-256-ctr", "sha256")
|
||||||
|
|
||||||
|
@ -119,17 +119,17 @@ class EncryptionTests: XCTestCase {
|
||||||
print(md5)
|
print(md5)
|
||||||
XCTAssertEqual(md5, exp)
|
XCTAssertEqual(md5, exp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPrivateKeyDecryption() {
|
func testPrivateKeyDecryption() {
|
||||||
privateTestPrivateKeyDecryption(pkcs: "1")
|
privateTestPrivateKeyDecryption(pkcs: "1")
|
||||||
privateTestPrivateKeyDecryption(pkcs: "8")
|
privateTestPrivateKeyDecryption(pkcs: "8")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func privateTestPrivateKeyDecryption(pkcs: String) {
|
private func privateTestPrivateKeyDecryption(pkcs: String) {
|
||||||
let bundle = Bundle.module
|
let bundle = Bundle.module
|
||||||
let encryptedPath = bundle.path(forResource: "tunnelbear", ofType: "enc.\(pkcs).key")!
|
let encryptedPath = bundle.path(forResource: "tunnelbear", ofType: "enc.\(pkcs).key")!
|
||||||
let decryptedPath = bundle.path(forResource: "tunnelbear", ofType: "key")!
|
let decryptedPath = bundle.path(forResource: "tunnelbear", ofType: "key")!
|
||||||
|
|
||||||
XCTAssertThrowsError(try TLSBox.decryptedPrivateKey(fromPath: encryptedPath, passphrase: "wrongone"))
|
XCTAssertThrowsError(try TLSBox.decryptedPrivateKey(fromPath: encryptedPath, passphrase: "wrongone"))
|
||||||
let decryptedViaPath = try! TLSBox.decryptedPrivateKey(fromPath: encryptedPath, passphrase: "foobar")
|
let decryptedViaPath = try! TLSBox.decryptedPrivateKey(fromPath: encryptedPath, passphrase: "foobar")
|
||||||
print(decryptedViaPath)
|
print(decryptedViaPath)
|
||||||
|
@ -137,17 +137,17 @@ class EncryptionTests: XCTestCase {
|
||||||
let decryptedViaString = try! TLSBox.decryptedPrivateKey(fromPEM: encryptedPEM, passphrase: "foobar")
|
let decryptedViaString = try! TLSBox.decryptedPrivateKey(fromPEM: encryptedPEM, passphrase: "foobar")
|
||||||
print(decryptedViaString)
|
print(decryptedViaString)
|
||||||
XCTAssertEqual(decryptedViaPath, decryptedViaString)
|
XCTAssertEqual(decryptedViaPath, decryptedViaString)
|
||||||
|
|
||||||
let expDecrypted = try! String(contentsOfFile: decryptedPath)
|
let expDecrypted = try! String(contentsOfFile: decryptedPath)
|
||||||
XCTAssertEqual(decryptedViaPath, expDecrypted)
|
XCTAssertEqual(decryptedViaPath, expDecrypted)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCertificatePreamble() {
|
func testCertificatePreamble() {
|
||||||
let url = Bundle.module.url(forResource: "tunnelbear", withExtension: "crt")!
|
let url = Bundle.module.url(forResource: "tunnelbear", withExtension: "crt")!
|
||||||
let cert = OpenVPN.CryptoContainer(pem: try! String(contentsOf: url))
|
let cert = OpenVPN.CryptoContainer(pem: try! String(contentsOf: url))
|
||||||
XCTAssert(cert.pem.hasPrefix("-----BEGIN"))
|
XCTAssert(cert.pem.hasPrefix("-----BEGIN"))
|
||||||
}
|
}
|
||||||
|
|
||||||
private func clientServer(_ c: String?, _ d: String?) -> (CryptoBox, CryptoBox) {
|
private func clientServer(_ c: String?, _ d: String?) -> (CryptoBox, CryptoBox) {
|
||||||
let client = CryptoBox(cipherAlgorithm: c, digestAlgorithm: d)
|
let client = CryptoBox(cipherAlgorithm: c, digestAlgorithm: d)
|
||||||
let server = CryptoBox(cipherAlgorithm: c, digestAlgorithm: d)
|
let server = CryptoBox(cipherAlgorithm: c, digestAlgorithm: d)
|
||||||
|
|
|
@ -49,19 +49,19 @@ class LinkTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UDP
|
// UDP
|
||||||
|
|
||||||
func testUnreliableControlQueue() {
|
func testUnreliableControlQueue() {
|
||||||
let seq1 = [0, 5, 2, 1, 4, 3]
|
let seq1 = [0, 5, 2, 1, 4, 3]
|
||||||
let seq2 = [5, 2, 1, 9, 4, 3, 0, 8, 7, 10, 4, 3, 5, 6]
|
let seq2 = [5, 2, 1, 9, 4, 3, 0, 8, 7, 10, 4, 3, 5, 6]
|
||||||
let seq3 = [5, 2, 11, 1, 2, 9, 4, 5, 5, 3, 8, 0, 6, 8, 2, 7, 10, 4, 3, 5, 6]
|
let seq3 = [5, 2, 11, 1, 2, 9, 4, 5, 5, 3, 8, 0, 6, 8, 2, 7, 10, 4, 3, 5, 6]
|
||||||
|
|
||||||
for seq in [seq1, seq2, seq3] {
|
for seq in [seq1, seq2, seq3] {
|
||||||
XCTAssertEqual(TestUtils.uniqArray(seq.sorted()), handleControlSequence(seq))
|
XCTAssertEqual(TestUtils.uniqArray(seq.sorted()), handleControlSequence(seq))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TCP
|
// TCP
|
||||||
|
|
||||||
// private func testPacketStream() {
|
// private func testPacketStream() {
|
||||||
// var bytes: [UInt8] = []
|
// var bytes: [UInt8] = []
|
||||||
// var until: Int
|
// var until: Int
|
||||||
|
@ -156,25 +156,25 @@ class LinkTests: XCTestCase {
|
||||||
}
|
}
|
||||||
return hdl
|
return hdl
|
||||||
}
|
}
|
||||||
|
|
||||||
private func enqueueControl(_ q: inout [Int], _ id: inout Int, _ p: Int, _ h: (Int) -> Void) {
|
private func enqueueControl(_ q: inout [Int], _ id: inout Int, _ p: Int, _ h: (Int) -> Void) {
|
||||||
q.append(p)
|
q.append(p)
|
||||||
q.sort { (p1, p2) -> Bool in
|
q.sort { (p1, p2) -> Bool in
|
||||||
return (p1 < p2)
|
return (p1 < p2)
|
||||||
}
|
}
|
||||||
|
|
||||||
print("q = \(q)")
|
print("q = \(q)")
|
||||||
print("id = \(id)")
|
print("id = \(id)")
|
||||||
for p in q {
|
for p in q {
|
||||||
print("test(\(p))")
|
print("test(\(p))")
|
||||||
if (p < id) {
|
if p < id {
|
||||||
q.removeFirst()
|
q.removeFirst()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (p != id) {
|
if p != id {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
h(p)
|
h(p)
|
||||||
print("handle(\(p))")
|
print("handle(\(p))")
|
||||||
id += 1
|
id += 1
|
||||||
|
|
|
@ -28,17 +28,17 @@ import XCTest
|
||||||
import CTunnelKitOpenVPNProtocol
|
import CTunnelKitOpenVPNProtocol
|
||||||
|
|
||||||
class PacketTests: XCTestCase {
|
class PacketTests: XCTestCase {
|
||||||
|
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
super.setUp()
|
super.setUp()
|
||||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tearDown() {
|
override func tearDown() {
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||||
super.tearDown()
|
super.tearDown()
|
||||||
}
|
}
|
||||||
|
|
||||||
func testControlPacket() {
|
func testControlPacket() {
|
||||||
let id: UInt32 = 0x1456
|
let id: UInt32 = 0x1456
|
||||||
let code: PacketCode = .controlV1
|
let code: PacketCode = .controlV1
|
||||||
|
@ -64,7 +64,7 @@ class PacketTests: XCTestCase {
|
||||||
let expected = Data(hex: "2b112233445566778805000000aa000000bb000000cc000000dd000000eea639328cbf03490e")
|
let expected = Data(hex: "2b112233445566778805000000aa000000bb000000cc000000dd000000eea639328cbf03490e")
|
||||||
print("Serialized: \(serialized.toHex())")
|
print("Serialized: \(serialized.toHex())")
|
||||||
print("Expected : \(expected.toHex())")
|
print("Expected : \(expected.toHex())")
|
||||||
|
|
||||||
XCTAssertEqual(serialized, expected)
|
XCTAssertEqual(serialized, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,15 +40,15 @@ private extension OpenVPN.PushReply {
|
||||||
}
|
}
|
||||||
|
|
||||||
class PushTests: XCTestCase {
|
class PushTests: XCTestCase {
|
||||||
|
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tearDown() {
|
override func tearDown() {
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||||
}
|
}
|
||||||
|
|
||||||
func testNet30() {
|
func testNet30() {
|
||||||
let msg = "PUSH_REPLY,redirect-gateway def1,dhcp-option DNS 209.222.18.222,dhcp-option DNS 209.222.18.218,ping 10,comp-lzo no,route 10.5.10.1,topology net30,ifconfig 10.5.10.6 10.5.10.5,auth-token AUkQf/b3nj3L+CH4RJPP0Vuq8/gpntr7uPqzjQhncig="
|
let msg = "PUSH_REPLY,redirect-gateway def1,dhcp-option DNS 209.222.18.222,dhcp-option DNS 209.222.18.218,ping 10,comp-lzo no,route 10.5.10.1,topology net30,ifconfig 10.5.10.6 10.5.10.5,auth-token AUkQf/b3nj3L+CH4RJPP0Vuq8/gpntr7uPqzjQhncig="
|
||||||
let reply = try! OpenVPN.PushReply(message: msg)!
|
let reply = try! OpenVPN.PushReply(message: msg)!
|
||||||
|
@ -59,25 +59,25 @@ class PushTests: XCTestCase {
|
||||||
XCTAssertEqual(reply.options.ipv4?.defaultGateway, "10.5.10.5")
|
XCTAssertEqual(reply.options.ipv4?.defaultGateway, "10.5.10.5")
|
||||||
XCTAssertEqual(reply.options.dnsServers, ["209.222.18.222", "209.222.18.218"])
|
XCTAssertEqual(reply.options.dnsServers, ["209.222.18.222", "209.222.18.218"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSubnet() {
|
func testSubnet() {
|
||||||
let msg = "PUSH_REPLY,dhcp-option DNS 8.8.8.8,dhcp-option DNS 4.4.4.4,route-gateway 10.8.0.1,topology subnet,ping 10,ping-restart 120,ifconfig 10.8.0.2 255.255.255.0,peer-id 0"
|
let msg = "PUSH_REPLY,dhcp-option DNS 8.8.8.8,dhcp-option DNS 4.4.4.4,route-gateway 10.8.0.1,topology subnet,ping 10,ping-restart 120,ifconfig 10.8.0.2 255.255.255.0,peer-id 0"
|
||||||
let reply = try! OpenVPN.PushReply(message: msg)!
|
let reply = try! OpenVPN.PushReply(message: msg)!
|
||||||
reply.debug()
|
reply.debug()
|
||||||
|
|
||||||
XCTAssertEqual(reply.options.ipv4?.address, "10.8.0.2")
|
XCTAssertEqual(reply.options.ipv4?.address, "10.8.0.2")
|
||||||
XCTAssertEqual(reply.options.ipv4?.addressMask, "255.255.255.0")
|
XCTAssertEqual(reply.options.ipv4?.addressMask, "255.255.255.0")
|
||||||
XCTAssertEqual(reply.options.ipv4?.defaultGateway, "10.8.0.1")
|
XCTAssertEqual(reply.options.ipv4?.defaultGateway, "10.8.0.1")
|
||||||
XCTAssertEqual(reply.options.dnsServers, ["8.8.8.8", "4.4.4.4"])
|
XCTAssertEqual(reply.options.dnsServers, ["8.8.8.8", "4.4.4.4"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRoute() {
|
func testRoute() {
|
||||||
let msg = "PUSH_REPLY,dhcp-option DNS 8.8.8.8,dhcp-option DNS 4.4.4.4,route-gateway 10.8.0.1,route 192.168.0.0 255.255.255.0 10.8.0.12,topology subnet,ping 10,ping-restart 120,ifconfig 10.8.0.2 255.255.255.0,peer-id 0"
|
let msg = "PUSH_REPLY,dhcp-option DNS 8.8.8.8,dhcp-option DNS 4.4.4.4,route-gateway 10.8.0.1,route 192.168.0.0 255.255.255.0 10.8.0.12,topology subnet,ping 10,ping-restart 120,ifconfig 10.8.0.2 255.255.255.0,peer-id 0"
|
||||||
let reply = try! OpenVPN.PushReply(message: msg)!
|
let reply = try! OpenVPN.PushReply(message: msg)!
|
||||||
reply.debug()
|
reply.debug()
|
||||||
|
|
||||||
let route = reply.options.routes4!.first!
|
let route = reply.options.routes4!.first!
|
||||||
|
|
||||||
XCTAssertEqual(route.destination, "192.168.0.0")
|
XCTAssertEqual(route.destination, "192.168.0.0")
|
||||||
XCTAssertEqual(route.mask, "255.255.255.0")
|
XCTAssertEqual(route.mask, "255.255.255.0")
|
||||||
XCTAssertEqual(route.gateway, "10.8.0.12")
|
XCTAssertEqual(route.gateway, "10.8.0.12")
|
||||||
|
@ -87,7 +87,7 @@ class PushTests: XCTestCase {
|
||||||
let msg = "PUSH_REPLY,dhcp-option DNS6 2001:4860:4860::8888,dhcp-option DNS6 2001:4860:4860::8844,tun-ipv6,route-gateway 10.8.0.1,topology subnet,ping 10,ping-restart 120,ifconfig-ipv6 fe80::601:30ff:feb7:ec01/64 fe80::601:30ff:feb7:dc02,ifconfig 10.8.0.2 255.255.255.0,peer-id 0"
|
let msg = "PUSH_REPLY,dhcp-option DNS6 2001:4860:4860::8888,dhcp-option DNS6 2001:4860:4860::8844,tun-ipv6,route-gateway 10.8.0.1,topology subnet,ping 10,ping-restart 120,ifconfig-ipv6 fe80::601:30ff:feb7:ec01/64 fe80::601:30ff:feb7:dc02,ifconfig 10.8.0.2 255.255.255.0,peer-id 0"
|
||||||
let reply = try! OpenVPN.PushReply(message: msg)!
|
let reply = try! OpenVPN.PushReply(message: msg)!
|
||||||
reply.debug()
|
reply.debug()
|
||||||
|
|
||||||
XCTAssertEqual(reply.options.ipv4?.address, "10.8.0.2")
|
XCTAssertEqual(reply.options.ipv4?.address, "10.8.0.2")
|
||||||
XCTAssertEqual(reply.options.ipv4?.addressMask, "255.255.255.0")
|
XCTAssertEqual(reply.options.ipv4?.addressMask, "255.255.255.0")
|
||||||
XCTAssertEqual(reply.options.ipv4?.defaultGateway, "10.8.0.1")
|
XCTAssertEqual(reply.options.ipv4?.defaultGateway, "10.8.0.1")
|
||||||
|
@ -96,19 +96,19 @@ class PushTests: XCTestCase {
|
||||||
XCTAssertEqual(reply.options.ipv6?.defaultGateway, "fe80::601:30ff:feb7:dc02")
|
XCTAssertEqual(reply.options.ipv6?.defaultGateway, "fe80::601:30ff:feb7:dc02")
|
||||||
XCTAssertEqual(reply.options.dnsServers, ["2001:4860:4860::8888", "2001:4860:4860::8844"])
|
XCTAssertEqual(reply.options.dnsServers, ["2001:4860:4860::8888", "2001:4860:4860::8844"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCompressionFraming() {
|
func testCompressionFraming() {
|
||||||
let msg = "PUSH_REPLY,dhcp-option DNS 8.8.8.8,dhcp-option DNS 4.4.4.4,comp-lzo no,route 10.8.0.1,topology net30,ping 10,ping-restart 120,ifconfig 10.8.0.6 10.8.0.5,peer-id 0,cipher AES-256-CBC"
|
let msg = "PUSH_REPLY,dhcp-option DNS 8.8.8.8,dhcp-option DNS 4.4.4.4,comp-lzo no,route 10.8.0.1,topology net30,ping 10,ping-restart 120,ifconfig 10.8.0.6 10.8.0.5,peer-id 0,cipher AES-256-CBC"
|
||||||
let reply = try! OpenVPN.PushReply(message: msg)!
|
let reply = try! OpenVPN.PushReply(message: msg)!
|
||||||
reply.debug()
|
reply.debug()
|
||||||
|
|
||||||
XCTAssertEqual(reply.options.compressionFraming, .compLZO)
|
XCTAssertEqual(reply.options.compressionFraming, .compLZO)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCompression() {
|
func testCompression() {
|
||||||
let msg = "PUSH_REPLY,dhcp-option DNS 8.8.8.8,dhcp-option DNS 4.4.4.4,route 10.8.0.1,topology net30,ping 10,ping-restart 120,ifconfig 10.8.0.6 10.8.0.5,peer-id 0,cipher AES-256-CBC"
|
let msg = "PUSH_REPLY,dhcp-option DNS 8.8.8.8,dhcp-option DNS 4.4.4.4,route 10.8.0.1,topology net30,ping 10,ping-restart 120,ifconfig 10.8.0.6 10.8.0.5,peer-id 0,cipher AES-256-CBC"
|
||||||
var reply: OpenVPN.PushReply
|
var reply: OpenVPN.PushReply
|
||||||
|
|
||||||
reply = try! OpenVPN.PushReply(message: msg.appending(",comp-lzo no"))!
|
reply = try! OpenVPN.PushReply(message: msg.appending(",comp-lzo no"))!
|
||||||
reply.debug()
|
reply.debug()
|
||||||
XCTAssertEqual(reply.options.compressionFraming, .compLZO)
|
XCTAssertEqual(reply.options.compressionFraming, .compLZO)
|
||||||
|
@ -134,7 +134,7 @@ class PushTests: XCTestCase {
|
||||||
XCTAssertEqual(reply.options.compressionFraming, .compress)
|
XCTAssertEqual(reply.options.compressionFraming, .compress)
|
||||||
XCTAssertEqual(reply.options.compressionAlgorithm, .other)
|
XCTAssertEqual(reply.options.compressionAlgorithm, .other)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testNCP() {
|
func testNCP() {
|
||||||
let msg = "PUSH_REPLY,dhcp-option DNS 8.8.8.8,dhcp-option DNS 4.4.4.4,comp-lzo no,route 10.8.0.1,topology net30,ping 10,ping-restart 120,ifconfig 10.8.0.6 10.8.0.5,peer-id 0,cipher AES-256-GCM"
|
let msg = "PUSH_REPLY,dhcp-option DNS 8.8.8.8,dhcp-option DNS 4.4.4.4,comp-lzo no,route 10.8.0.1,topology net30,ping 10,ping-restart 120,ifconfig 10.8.0.6 10.8.0.5,peer-id 0,cipher AES-256-GCM"
|
||||||
let reply = try! OpenVPN.PushReply(message: msg)!
|
let reply = try! OpenVPN.PushReply(message: msg)!
|
||||||
|
@ -147,32 +147,32 @@ class PushTests: XCTestCase {
|
||||||
let msg = "PUSH_REPLY,dhcp-option DNS 8.8.8.8,dhcp-option DNS 4.4.4.4,comp-lzo no,route 10.8.0.1,topology net30,ping 10,ping-restart 120,ifconfig 10.8.0.18 10.8.0.17,peer-id 3,cipher AES-256-GCM,auth-token"
|
let msg = "PUSH_REPLY,dhcp-option DNS 8.8.8.8,dhcp-option DNS 4.4.4.4,comp-lzo no,route 10.8.0.1,topology net30,ping 10,ping-restart 120,ifconfig 10.8.0.18 10.8.0.17,peer-id 3,cipher AES-256-GCM,auth-token"
|
||||||
let reply = try! OpenVPN.PushReply(message: msg)!
|
let reply = try! OpenVPN.PushReply(message: msg)!
|
||||||
reply.debug()
|
reply.debug()
|
||||||
|
|
||||||
XCTAssertEqual(reply.options.cipher, .aes256gcm)
|
XCTAssertEqual(reply.options.cipher, .aes256gcm)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPing() {
|
func testPing() {
|
||||||
let msg = "PUSH_REPLY,route 192.168.1.0 255.255.255.0,route 10.0.2.0 255.255.255.0,dhcp-option DNS 192.168.1.99,dhcp-option DNS 176.103.130.130,route 10.0.2.1,topology net30,ping 10,ping-restart 60,ifconfig 10.0.2.14 10.0.2.13"
|
let msg = "PUSH_REPLY,route 192.168.1.0 255.255.255.0,route 10.0.2.0 255.255.255.0,dhcp-option DNS 192.168.1.99,dhcp-option DNS 176.103.130.130,route 10.0.2.1,topology net30,ping 10,ping-restart 60,ifconfig 10.0.2.14 10.0.2.13"
|
||||||
let reply = try! OpenVPN.PushReply(message: msg)!
|
let reply = try! OpenVPN.PushReply(message: msg)!
|
||||||
reply.debug()
|
reply.debug()
|
||||||
|
|
||||||
XCTAssertEqual(reply.options.keepAliveInterval, 10)
|
XCTAssertEqual(reply.options.keepAliveInterval, 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPingRestart() {
|
func testPingRestart() {
|
||||||
let msg = "PUSH_REPLY,route 192.168.1.0 255.255.255.0,route 10.0.2.0 255.255.255.0,dhcp-option DNS 192.168.1.99,dhcp-option DNS 176.103.130.130,route 10.0.2.1,topology net30,ping 10,ping-restart 60,ifconfig 10.0.2.14 10.0.2.13"
|
let msg = "PUSH_REPLY,route 192.168.1.0 255.255.255.0,route 10.0.2.0 255.255.255.0,dhcp-option DNS 192.168.1.99,dhcp-option DNS 176.103.130.130,route 10.0.2.1,topology net30,ping 10,ping-restart 60,ifconfig 10.0.2.14 10.0.2.13"
|
||||||
let reply = try! OpenVPN.PushReply(message: msg)!
|
let reply = try! OpenVPN.PushReply(message: msg)!
|
||||||
reply.debug()
|
reply.debug()
|
||||||
|
|
||||||
XCTAssertEqual(reply.options.keepAliveTimeout, 60)
|
XCTAssertEqual(reply.options.keepAliveTimeout, 60)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testProvost() {
|
func testProvost() {
|
||||||
let msg = "PUSH_REPLY,route 87.233.192.218,route 87.233.192.219,route 87.233.192.220,route 87.248.186.252,route 92.241.171.245,route 103.246.200.0 255.255.252.0,route 109.239.140.0 255.255.255.0,route 128.199.0.0 255.255.0.0,route 13.125.0.0 255.255.0.0,route 13.230.0.0 255.254.0.0,route 13.56.0.0 255.252.0.0,route 149.154.160.0 255.255.252.0,route 149.154.164.0 255.255.252.0,route 149.154.168.0 255.255.252.0,route 149.154.172.0 255.255.252.0,route 159.122.128.0 255.255.192.0,route 159.203.0.0 255.255.0.0,route 159.65.0.0 255.255.0.0,route 159.89.0.0 255.255.0.0,route 165.227.0.0 255.255.0.0,route 167.99.0.0 255.255.0.0,route 174.138.0.0 255.255.128.0,route 176.67.169.0 255.255.255.0,route 178.239.88.0 255.255.248.0,route 178.63.0.0 255.255.0.0,route 18.130.0.0 255.255.0.0,route 18.144.0.0 255.255.0.0,route 18.184.0.0 255.254.0.0,route 18.194.0.0 255.254.0.0,route 18.196.0.0 255.254.0.0,route 18.204.0.0 255.252.0.0,push-continuation 2"
|
let msg = "PUSH_REPLY,route 87.233.192.218,route 87.233.192.219,route 87.233.192.220,route 87.248.186.252,route 92.241.171.245,route 103.246.200.0 255.255.252.0,route 109.239.140.0 255.255.255.0,route 128.199.0.0 255.255.0.0,route 13.125.0.0 255.255.0.0,route 13.230.0.0 255.254.0.0,route 13.56.0.0 255.252.0.0,route 149.154.160.0 255.255.252.0,route 149.154.164.0 255.255.252.0,route 149.154.168.0 255.255.252.0,route 149.154.172.0 255.255.252.0,route 159.122.128.0 255.255.192.0,route 159.203.0.0 255.255.0.0,route 159.65.0.0 255.255.0.0,route 159.89.0.0 255.255.0.0,route 165.227.0.0 255.255.0.0,route 167.99.0.0 255.255.0.0,route 174.138.0.0 255.255.128.0,route 176.67.169.0 255.255.255.0,route 178.239.88.0 255.255.248.0,route 178.63.0.0 255.255.0.0,route 18.130.0.0 255.255.0.0,route 18.144.0.0 255.255.0.0,route 18.184.0.0 255.254.0.0,route 18.194.0.0 255.254.0.0,route 18.196.0.0 255.254.0.0,route 18.204.0.0 255.252.0.0,push-continuation 2"
|
||||||
let reply = try? OpenVPN.PushReply(message: msg)!
|
let reply = try? OpenVPN.PushReply(message: msg)!
|
||||||
reply?.debug()
|
reply?.debug()
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPeerInfo() {
|
func testPeerInfo() {
|
||||||
let peerInfo = CoreConfiguration.OpenVPN.peerInfo()
|
let peerInfo = CoreConfiguration.OpenVPN.peerInfo()
|
||||||
print(peerInfo)
|
print(peerInfo)
|
||||||
|
|
|
@ -51,22 +51,22 @@ dccdb953cdf32bea03f365760b0ed800
|
||||||
7aed27125592a7148d25c87fdbe0a3f6
|
7aed27125592a7148d25c87fdbe0a3f6
|
||||||
-----END OpenVPN Static key V1-----
|
-----END OpenVPN Static key V1-----
|
||||||
"""
|
"""
|
||||||
|
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
super.setUp()
|
super.setUp()
|
||||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tearDown() {
|
override func tearDown() {
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||||
super.tearDown()
|
super.tearDown()
|
||||||
}
|
}
|
||||||
|
|
||||||
func testFileBidirectional() {
|
func testFileBidirectional() {
|
||||||
let expected = Data(hex: "cf55d863fcbe314df5f0b45dbe974d9bde33ef5b4803c3985531c6c23ca6906d6cd028efc8585d1b9e71003566bd7891b9cc9212bcba510109922eed87f5c8e6")
|
let expected = Data(hex: "cf55d863fcbe314df5f0b45dbe974d9bde33ef5b4803c3985531c6c23ca6906d6cd028efc8585d1b9e71003566bd7891b9cc9212bcba510109922eed87f5c8e6")
|
||||||
let key = OpenVPN.StaticKey(file: content, direction: nil)
|
let key = OpenVPN.StaticKey(file: content, direction: nil)
|
||||||
XCTAssertNotNil(key)
|
XCTAssertNotNil(key)
|
||||||
|
|
||||||
XCTAssertEqual(key?.hmacSendKey.toData(), expected)
|
XCTAssertEqual(key?.hmacSendKey.toData(), expected)
|
||||||
XCTAssertEqual(key?.hmacReceiveKey.toData(), expected)
|
XCTAssertEqual(key?.hmacReceiveKey.toData(), expected)
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ dccdb953cdf32bea03f365760b0ed800
|
||||||
let receive = Data(hex: "cf55d863fcbe314df5f0b45dbe974d9bde33ef5b4803c3985531c6c23ca6906d6cd028efc8585d1b9e71003566bd7891b9cc9212bcba510109922eed87f5c8e6")
|
let receive = Data(hex: "cf55d863fcbe314df5f0b45dbe974d9bde33ef5b4803c3985531c6c23ca6906d6cd028efc8585d1b9e71003566bd7891b9cc9212bcba510109922eed87f5c8e6")
|
||||||
let key = OpenVPN.StaticKey(file: content, direction: .client)
|
let key = OpenVPN.StaticKey(file: content, direction: .client)
|
||||||
XCTAssertNotNil(key)
|
XCTAssertNotNil(key)
|
||||||
|
|
||||||
XCTAssertEqual(key?.hmacSendKey.toData(), send)
|
XCTAssertEqual(key?.hmacSendKey.toData(), send)
|
||||||
XCTAssertEqual(key?.hmacReceiveKey.toData(), receive)
|
XCTAssertEqual(key?.hmacReceiveKey.toData(), receive)
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,9 +41,9 @@ import CTunnelKitOpenVPNProtocol
|
||||||
|
|
||||||
public class TestUtils {
|
public class TestUtils {
|
||||||
public static func uniqArray(_ v: [Int]) -> [Int] {
|
public static func uniqArray(_ v: [Int]) -> [Int] {
|
||||||
return v.reduce([]){ $0.contains($1) ? $0 : $0 + [$1] }
|
return v.reduce([]) { $0.contains($1) ? $0 : $0 + [$1] }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func generateDataSuite(_ size: Int, _ count: Int) -> [Data] {
|
public static func generateDataSuite(_ size: Int, _ count: Int) -> [Data] {
|
||||||
var suite = [Data]()
|
var suite = [Data]()
|
||||||
for _ in 0..<count {
|
for _ in 0..<count {
|
||||||
|
@ -51,7 +51,7 @@ public class TestUtils {
|
||||||
}
|
}
|
||||||
return suite
|
return suite
|
||||||
}
|
}
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ extension Decrypter {
|
||||||
dest.removeSubrange(destLength..<dest.count)
|
dest.removeSubrange(destLength..<dest.count)
|
||||||
return Data(dest)
|
return Data(dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyData(_ data: Data, flags: UnsafePointer<CryptoFlags>?) throws {
|
func verifyData(_ data: Data, flags: UnsafePointer<CryptoFlags>?) throws {
|
||||||
let srcLength = data.count
|
let srcLength = data.count
|
||||||
try data.withUnsafeBytes {
|
try data.withUnsafeBytes {
|
||||||
|
|
|
@ -30,7 +30,7 @@ import CTunnelKitOpenVPNProtocol
|
||||||
|
|
||||||
final class XORTests: XCTestCase {
|
final class XORTests: XCTestCase {
|
||||||
private let mask = Data(hex: "f76dab30")
|
private let mask = Data(hex: "f76dab30")
|
||||||
|
|
||||||
override func setUpWithError() throws {
|
override func setUpWithError() throws {
|
||||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue