Merge branch 'openvpn-appextension'
This commit is contained in:
commit
f3edd6ec59
19
.jazzy.yaml
19
.jazzy.yaml
|
@ -16,6 +16,7 @@ custom_categories:
|
||||||
children:
|
children:
|
||||||
- BidirectionalState
|
- BidirectionalState
|
||||||
- ConfigurationError
|
- ConfigurationError
|
||||||
|
- DNSResolver
|
||||||
- EndpointProtocol
|
- EndpointProtocol
|
||||||
- IOInterface
|
- IOInterface
|
||||||
- IPv4Settings
|
- IPv4Settings
|
||||||
|
@ -25,12 +26,22 @@ custom_categories:
|
||||||
- Session
|
- Session
|
||||||
- SocketType
|
- SocketType
|
||||||
- TunnelInterface
|
- TunnelInterface
|
||||||
- name: OpenVPN
|
- name: AppExtension
|
||||||
|
children:
|
||||||
|
- GenericSocket
|
||||||
|
- GenericSocketDelegate
|
||||||
|
- InterfaceObserver
|
||||||
|
- Keychain
|
||||||
|
- KeychainError
|
||||||
|
- LinkProducer
|
||||||
|
- MemoryDestination
|
||||||
|
- NETCPSocket
|
||||||
|
- NETunnelInterface
|
||||||
|
- NEUDPSocket
|
||||||
|
- name: Protocols/OpenVPN
|
||||||
children:
|
children:
|
||||||
- OpenVPN
|
- OpenVPN
|
||||||
- OpenVPNError
|
- OpenVPNError
|
||||||
- OpenVPNSession
|
- OpenVPNSession
|
||||||
- OpenVPNSessionDelegate
|
- OpenVPNSessionDelegate
|
||||||
- name: AppExtension
|
- OpenVPNTunnelProvider
|
||||||
children:
|
|
||||||
- TunnelKitProvider
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ import UIKit
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
import TunnelKit
|
import TunnelKit
|
||||||
|
|
||||||
private let ca = CryptoContainer(pem: """
|
private let ca = OpenVPN.CryptoContainer(pem: """
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIFqzCCBJOgAwIBAgIJAKZ7D5Yv87qDMA0GCSqGSIb3DQEBDQUAMIHoMQswCQYD
|
MIIFqzCCBJOgAwIBAgIJAKZ7D5Yv87qDMA0GCSqGSIb3DQEBDQUAMIHoMQswCQYD
|
||||||
VQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNV
|
VQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNV
|
||||||
|
@ -86,9 +86,9 @@ extension ViewController {
|
||||||
|
|
||||||
let hostname = ((domain == "") ? server : [server, domain].joined(separator: "."))
|
let hostname = ((domain == "") ? server : [server, domain].joined(separator: "."))
|
||||||
let port = UInt16(textPort.text!)!
|
let port = UInt16(textPort.text!)!
|
||||||
let credentials = SessionProxy.Credentials(textUsername.text!, textPassword.text!)
|
let credentials = OpenVPN.Credentials(textUsername.text!, textPassword.text!)
|
||||||
|
|
||||||
var sessionBuilder = SessionProxy.ConfigurationBuilder()
|
var sessionBuilder = OpenVPN.ConfigurationBuilder()
|
||||||
sessionBuilder.ca = ca
|
sessionBuilder.ca = ca
|
||||||
sessionBuilder.cipher = .aes128cbc
|
sessionBuilder.cipher = .aes128cbc
|
||||||
sessionBuilder.digest = .sha1
|
sessionBuilder.digest = .sha1
|
||||||
|
@ -98,7 +98,7 @@ extension ViewController {
|
||||||
let socketType: SocketType = switchTCP.isOn ? .tcp : .udp
|
let socketType: SocketType = switchTCP.isOn ? .tcp : .udp
|
||||||
sessionBuilder.endpointProtocols = [EndpointProtocol(socketType, port)]
|
sessionBuilder.endpointProtocols = [EndpointProtocol(socketType, port)]
|
||||||
sessionBuilder.usesPIAPatches = true
|
sessionBuilder.usesPIAPatches = true
|
||||||
var builder = TunnelKitProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build())
|
var builder = OpenVPNTunnelProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build())
|
||||||
builder.mtu = 1350
|
builder.mtu = 1350
|
||||||
builder.shouldDebug = true
|
builder.shouldDebug = true
|
||||||
builder.masksPrivateData = false
|
builder.masksPrivateData = false
|
||||||
|
@ -218,7 +218,7 @@ class ViewController: UIViewController, URLSessionDataDelegate {
|
||||||
guard let vpn = currentManager?.connection as? NETunnelProviderSession else {
|
guard let vpn = currentManager?.connection as? NETunnelProviderSession else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try? vpn.sendProviderMessage(TunnelKitProvider.Message.requestLog.data) { (data) in
|
try? vpn.sendProviderMessage(OpenVPNTunnelProvider.Message.requestLog.data) { (data) in
|
||||||
guard let data = data, let log = String(data: data, encoding: .utf8) else {
|
guard let data = data, let log = String(data: data, encoding: .utf8) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ import Cocoa
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
import TunnelKit
|
import TunnelKit
|
||||||
|
|
||||||
private let ca = CryptoContainer(pem: """
|
private let ca = OpenVPN.CryptoContainer(pem: """
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIFqzCCBJOgAwIBAgIJAKZ7D5Yv87qDMA0GCSqGSIb3DQEBDQUAMIHoMQswCQYD
|
MIIFqzCCBJOgAwIBAgIJAKZ7D5Yv87qDMA0GCSqGSIb3DQEBDQUAMIHoMQswCQYD
|
||||||
VQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNV
|
VQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNV
|
||||||
|
@ -86,9 +86,9 @@ extension ViewController {
|
||||||
|
|
||||||
let hostname = ((domain == "") ? server : [server, domain].joined(separator: "."))
|
let hostname = ((domain == "") ? server : [server, domain].joined(separator: "."))
|
||||||
let port = UInt16(textPort.stringValue)!
|
let port = UInt16(textPort.stringValue)!
|
||||||
let credentials = SessionProxy.Credentials(textUsername.stringValue, textPassword.stringValue)
|
let credentials = OpenVPN.Credentials(textUsername.stringValue, textPassword.stringValue)
|
||||||
|
|
||||||
var sessionBuilder = SessionProxy.ConfigurationBuilder()
|
var sessionBuilder = OpenVPN.ConfigurationBuilder()
|
||||||
sessionBuilder.ca = ca
|
sessionBuilder.ca = ca
|
||||||
sessionBuilder.cipher = .aes128cbc
|
sessionBuilder.cipher = .aes128cbc
|
||||||
sessionBuilder.digest = .sha1
|
sessionBuilder.digest = .sha1
|
||||||
|
@ -99,7 +99,7 @@ extension ViewController {
|
||||||
let socketType: SocketType = .udp
|
let socketType: SocketType = .udp
|
||||||
sessionBuilder.endpointProtocols = [EndpointProtocol(socketType, port)]
|
sessionBuilder.endpointProtocols = [EndpointProtocol(socketType, port)]
|
||||||
sessionBuilder.usesPIAPatches = true
|
sessionBuilder.usesPIAPatches = true
|
||||||
var builder = TunnelKitProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build())
|
var builder = OpenVPNTunnelProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build())
|
||||||
builder.mtu = 1350
|
builder.mtu = 1350
|
||||||
builder.shouldDebug = true
|
builder.shouldDebug = true
|
||||||
builder.masksPrivateData = false
|
builder.masksPrivateData = false
|
||||||
|
|
|
@ -37,5 +37,5 @@
|
||||||
|
|
||||||
import TunnelKit
|
import TunnelKit
|
||||||
|
|
||||||
class PacketTunnelProvider: TunnelKitProvider {
|
class PacketTunnelProvider: OpenVPNTunnelProvider {
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,5 +37,5 @@
|
||||||
|
|
||||||
import TunnelKit
|
import TunnelKit
|
||||||
|
|
||||||
class PacketTunnelProvider: TunnelKitProvider {
|
class PacketTunnelProvider: OpenVPNTunnelProvider {
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
PODS:
|
PODS:
|
||||||
- OpenSSL-Apple (1.1.0j.2)
|
- OpenSSL-Apple (1.1.0j.2)
|
||||||
- SwiftyBeaver (1.7.0)
|
- SwiftyBeaver (1.7.0)
|
||||||
- TunnelKit (1.6.3):
|
- TunnelKit (2.0.0):
|
||||||
- TunnelKit/AppExtension (= 1.6.3)
|
- TunnelKit/Protocols/OpenVPN (= 2.0.0)
|
||||||
- TunnelKit/Core (= 1.6.3)
|
- TunnelKit/AppExtension (2.0.0):
|
||||||
- TunnelKit/AppExtension (1.6.3):
|
|
||||||
- SwiftyBeaver
|
- SwiftyBeaver
|
||||||
- TunnelKit/Core
|
- TunnelKit/Core
|
||||||
- TunnelKit/Core (1.6.3):
|
- TunnelKit/Core (2.0.0):
|
||||||
- OpenSSL-Apple (~> 1.1.0j.2)
|
- OpenSSL-Apple (~> 1.1.0j.2)
|
||||||
- SwiftyBeaver
|
- SwiftyBeaver
|
||||||
|
- TunnelKit/Protocols/OpenVPN (2.0.0):
|
||||||
|
- TunnelKit/AppExtension
|
||||||
|
- TunnelKit/Core
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- TunnelKit (from `..`)
|
- TunnelKit (from `..`)
|
||||||
|
@ -26,7 +28,7 @@ EXTERNAL SOURCES:
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
OpenSSL-Apple: e88e1eb314acb4a05e2348069790c4aa49f6d319
|
OpenSSL-Apple: e88e1eb314acb4a05e2348069790c4aa49f6d319
|
||||||
SwiftyBeaver: 4cc0080d2e23f980652e28978db11a5c9da39165
|
SwiftyBeaver: 4cc0080d2e23f980652e28978db11a5c9da39165
|
||||||
TunnelKit: ac8dad986e36e513303ed5d258c7e4aedb45c84b
|
TunnelKit: 821c15bb87aafae69eb8c63e4cc46d883fff8797
|
||||||
|
|
||||||
PODFILE CHECKSUM: f66dfaaa92a8d04ab2743f3caeab0ac9f9f25859
|
PODFILE CHECKSUM: f66dfaaa92a8d04ab2743f3caeab0ac9f9f25859
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ Pod::Spec.new do |s|
|
||||||
s.ios.deployment_target = "11.0"
|
s.ios.deployment_target = "11.0"
|
||||||
s.osx.deployment_target = "10.11"
|
s.osx.deployment_target = "10.11"
|
||||||
|
|
||||||
s.default_subspecs = "Core", "OpenVPN", "AppExtension"
|
s.default_subspecs = "Protocols/OpenVPN"
|
||||||
|
|
||||||
s.subspec "Core" do |p|
|
s.subspec "Core" do |p|
|
||||||
p.source_files = "TunnelKit/Sources/Core/**/*.{h,m,swift}"
|
p.source_files = "TunnelKit/Sources/Core/**/*.{h,m,swift}"
|
||||||
|
@ -25,32 +25,38 @@ Pod::Spec.new do |s|
|
||||||
p.libraries = "resolv"
|
p.libraries = "resolv"
|
||||||
end
|
end
|
||||||
|
|
||||||
s.subspec "OpenVPN" do |p|
|
|
||||||
p.source_files = "TunnelKit/Sources/OpenVPN/**/*.{h,m,swift}"
|
|
||||||
p.private_header_files = "TunnelKit/Sources/OpenVPN/**/*.h"
|
|
||||||
p.preserve_paths = "TunnelKit/Sources/OpenVPN/*.modulemap"
|
|
||||||
p.pod_target_xcconfig = { "OTHER_LDFLAGS" => "-framework openssl",
|
|
||||||
"SWIFT_INCLUDE_PATHS" => "${PODS_TARGET_SRCROOT}/TunnelKit/Sources/OpenVPN",
|
|
||||||
"APPLICATION_EXTENSION_API_ONLY" => "YES" }
|
|
||||||
|
|
||||||
p.dependency "TunnelKit/Core"
|
|
||||||
end
|
|
||||||
|
|
||||||
s.subspec "AppExtension" do |p|
|
s.subspec "AppExtension" do |p|
|
||||||
p.source_files = "TunnelKit/Sources/AppExtension/**/*.swift"
|
p.source_files = "TunnelKit/Sources/AppExtension/**/*.swift"
|
||||||
p.frameworks = "NetworkExtension"
|
p.frameworks = "NetworkExtension"
|
||||||
p.pod_target_xcconfig = { "APPLICATION_EXTENSION_API_ONLY" => "YES" }
|
p.pod_target_xcconfig = { "APPLICATION_EXTENSION_API_ONLY" => "YES" }
|
||||||
|
|
||||||
p.dependency "TunnelKit/OpenVPN"
|
p.dependency "SwiftyBeaver"
|
||||||
|
p.dependency "TunnelKit/Core"
|
||||||
end
|
end
|
||||||
|
|
||||||
s.subspec "LZO" do |p|
|
s.subspec "Protocols" do |t|
|
||||||
p.source_files = "TunnelKit/Sources/Core/LZO.h",
|
t.subspec "OpenVPN" do |p|
|
||||||
"TunnelKit/Sources/Core/Errors.{h,m}",
|
p.source_files = "TunnelKit/Sources/Protocols/OpenVPN/**/*.{h,m,swift}"
|
||||||
"TunnelKit/Sources/LZO/lib/*lzo*.{h,m,c}"
|
p.private_header_files = "TunnelKit/Sources/Protocols/OpenVPN/**/*.h"
|
||||||
p.private_header_files = "TunnelKit/Sources/Core/LZO.h",
|
p.preserve_paths = "TunnelKit/Sources/Protocols/OpenVPN/*.modulemap"
|
||||||
"TunnelKit/Sources/Core/Errors.h",
|
p.pod_target_xcconfig = { "OTHER_LDFLAGS" => "-framework openssl",
|
||||||
"TunnelKit/Sources/LZO/lib/*lzo*.h"
|
"SWIFT_INCLUDE_PATHS" => "${PODS_TARGET_SRCROOT}/TunnelKit/Sources/Protocols/OpenVPN",
|
||||||
p.pod_target_xcconfig = { "APPLICATION_EXTENSION_API_ONLY" => "YES" }
|
"APPLICATION_EXTENSION_API_ONLY" => "YES" }
|
||||||
|
|
||||||
|
p.dependency "TunnelKit/Core"
|
||||||
|
p.dependency "TunnelKit/AppExtension"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
s.subspec "Extra" do |t|
|
||||||
|
t.subspec "LZO" do |p|
|
||||||
|
p.source_files = "TunnelKit/Sources/Core/LZO.h",
|
||||||
|
"TunnelKit/Sources/Core/Errors.{h,m}",
|
||||||
|
"TunnelKit/Sources/Extra/LZO/lib/*lzo*.{h,m,c}"
|
||||||
|
p.private_header_files = "TunnelKit/Sources/Core/LZO.h",
|
||||||
|
"TunnelKit/Sources/Core/Errors.h",
|
||||||
|
"TunnelKit/Sources/Extra/LZO/lib/*lzo*.h"
|
||||||
|
p.pod_target_xcconfig = { "APPLICATION_EXTENSION_API_ONLY" => "YES" }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -36,34 +36,69 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
protocol LinkProducer {
|
/// Receives events from a `GenericSocket`.
|
||||||
func link(withMTU mtu: Int) -> LinkInterface
|
public protocol GenericSocketDelegate: class {
|
||||||
}
|
|
||||||
|
|
||||||
protocol GenericSocketDelegate: class {
|
/**
|
||||||
|
The socket timed out.
|
||||||
|
**/
|
||||||
func socketDidTimeout(_ socket: GenericSocket)
|
func socketDidTimeout(_ socket: GenericSocket)
|
||||||
|
|
||||||
|
/**
|
||||||
|
The socket became active.
|
||||||
|
**/
|
||||||
func socketDidBecomeActive(_ socket: GenericSocket)
|
func socketDidBecomeActive(_ socket: GenericSocket)
|
||||||
|
|
||||||
|
/**
|
||||||
|
The socket shut down.
|
||||||
|
|
||||||
|
- Parameter failure: `true` if the shutdown was caused by a failure.
|
||||||
|
**/
|
||||||
func socket(_ socket: GenericSocket, didShutdownWithFailure failure: Bool)
|
func socket(_ socket: GenericSocket, didShutdownWithFailure failure: Bool)
|
||||||
|
|
||||||
|
/**
|
||||||
|
The socket has a better path.
|
||||||
|
**/
|
||||||
func socketHasBetterPath(_ socket: GenericSocket)
|
func socketHasBetterPath(_ socket: GenericSocket)
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol GenericSocket: LinkProducer {
|
/// An opaque socket implementation.
|
||||||
|
public protocol GenericSocket {
|
||||||
|
|
||||||
|
/// The address of the remote endpoint.
|
||||||
var remoteAddress: String? { get }
|
var remoteAddress: String? { get }
|
||||||
|
|
||||||
|
/// `true` if the socket has a better path.
|
||||||
var hasBetterPath: Bool { get }
|
var hasBetterPath: Bool { get }
|
||||||
|
|
||||||
|
/// `true` if the socket was shut down.
|
||||||
var isShutdown: Bool { get }
|
var isShutdown: Bool { get }
|
||||||
|
|
||||||
|
/// The optional delegate for events.
|
||||||
var delegate: GenericSocketDelegate? { get set }
|
var delegate: GenericSocketDelegate? { get set }
|
||||||
|
|
||||||
|
/**
|
||||||
|
Observes socket events.
|
||||||
|
|
||||||
|
- Parameter queue: The queue to observe events in.
|
||||||
|
- Parameter activeTimeout: The timeout in milliseconds for socket activity.
|
||||||
|
**/
|
||||||
func observe(queue: DispatchQueue, activeTimeout: Int)
|
func observe(queue: DispatchQueue, activeTimeout: Int)
|
||||||
|
|
||||||
|
/**
|
||||||
|
Stops observing socket events.
|
||||||
|
**/
|
||||||
func unobserve()
|
func unobserve()
|
||||||
|
|
||||||
|
/**
|
||||||
|
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 any.
|
||||||
|
**/
|
||||||
func upgraded() -> GenericSocket?
|
func upgraded() -> GenericSocket?
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,17 +41,25 @@ import SwiftyBeaver
|
||||||
private let log = SwiftyBeaver.self
|
private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
extension NSNotification.Name {
|
extension NSNotification.Name {
|
||||||
static let __InterfaceObserverDidDetectWifiChange = NSNotification.Name("__InterfaceObserverDidDetectWifiChange")
|
|
||||||
|
/// A change in Wi-Fi state occurred.
|
||||||
|
public static let InterfaceObserverDidDetectWifiChange = NSNotification.Name("InterfaceObserverDidDetectWifiChange")
|
||||||
}
|
}
|
||||||
|
|
||||||
class InterfaceObserver: NSObject {
|
/// Observes changes in the current Wi-Fi network.
|
||||||
|
public class InterfaceObserver: NSObject {
|
||||||
private var queue: DispatchQueue?
|
private var queue: DispatchQueue?
|
||||||
|
|
||||||
private var timer: DispatchSourceTimer?
|
private var timer: DispatchSourceTimer?
|
||||||
|
|
||||||
private var lastWifiName: String?
|
private var lastWifiName: String?
|
||||||
|
|
||||||
func start(queue: DispatchQueue) {
|
/**
|
||||||
|
Starts observing Wi-Fi updates.
|
||||||
|
|
||||||
|
- Parameter queue: The `DispatchQueue` to deliver notifications to.
|
||||||
|
**/
|
||||||
|
public func start(queue: DispatchQueue) {
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
|
|
||||||
let timer = DispatchSource.makeTimerSource(flags: DispatchSource.TimerFlags(rawValue: UInt(0)), queue: queue)
|
let timer = DispatchSource.makeTimerSource(flags: DispatchSource.TimerFlags(rawValue: UInt(0)), queue: queue)
|
||||||
|
@ -63,8 +71,11 @@ class InterfaceObserver: NSObject {
|
||||||
|
|
||||||
self.timer = timer
|
self.timer = timer
|
||||||
}
|
}
|
||||||
|
|
||||||
func stop() {
|
/**
|
||||||
|
Stops observing Wi-Fi updates.
|
||||||
|
**/
|
||||||
|
public func stop() {
|
||||||
timer?.cancel()
|
timer?.cancel()
|
||||||
timer = nil
|
timer = nil
|
||||||
queue = nil
|
queue = nil
|
||||||
|
@ -77,7 +88,7 @@ class InterfaceObserver: NSObject {
|
||||||
log.debug("SSID is now '\(current.maskedDescription)'")
|
log.debug("SSID is now '\(current.maskedDescription)'")
|
||||||
if let last = lastWifiName, (current != last) {
|
if let last = lastWifiName, (current != last) {
|
||||||
queue?.async {
|
queue?.async {
|
||||||
NotificationCenter.default.post(name: .__InterfaceObserverDidDetectWifiChange, object: nil)
|
NotificationCenter.default.post(name: .InterfaceObserverDidDetectWifiChange, object: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -87,7 +98,12 @@ class InterfaceObserver: NSObject {
|
||||||
lastWifiName = currentWifiName
|
lastWifiName = currentWifiName
|
||||||
}
|
}
|
||||||
|
|
||||||
func currentWifiNetworkName() -> String? {
|
/**
|
||||||
|
Returns the current Wi-Fi SSID if any.
|
||||||
|
|
||||||
|
- Returns: The current Wi-Fi SSID if any.
|
||||||
|
**/
|
||||||
|
public func currentWifiNetworkName() -> String? {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
guard let interfaceNames = CNCopySupportedInterfaces() as? [CFString] else {
|
guard let interfaceNames = CNCopySupportedInterfaces() as? [CFString] else {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -36,31 +36,49 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// :nodoc:
|
/// Error raised by `Keychain` methods.
|
||||||
public enum KeychainError: Error {
|
public enum KeychainError: Error {
|
||||||
|
|
||||||
|
/// Unable to add.
|
||||||
case add
|
case add
|
||||||
|
|
||||||
|
/// Item not found.
|
||||||
case notFound
|
case notFound
|
||||||
|
|
||||||
case typeMismatch
|
// /// Unexpected item type returned.
|
||||||
|
// case typeMismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
/// :nodoc:
|
/// Wrapper for easy keychain access and modification.
|
||||||
public class Keychain {
|
public class Keychain {
|
||||||
private let service: String?
|
private let service: String?
|
||||||
|
|
||||||
private let accessGroup: String?
|
private let accessGroup: String?
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public init() {
|
public init() {
|
||||||
service = Bundle.main.bundleIdentifier
|
service = Bundle.main.bundleIdentifier
|
||||||
accessGroup = nil
|
accessGroup = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Creates a keychain in an App Group.
|
||||||
|
|
||||||
|
- Parameter group: The App Group.
|
||||||
|
- Precondition: Proper App Group entitlements.
|
||||||
|
**/
|
||||||
public init(group: String) {
|
public init(group: String) {
|
||||||
service = nil
|
service = nil
|
||||||
accessGroup = group
|
accessGroup = group
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Creates a keychain in an App Group and a Team ID prefix.
|
||||||
|
|
||||||
|
- Parameter team: The Team ID prefix.
|
||||||
|
- Parameter group: The App Group.
|
||||||
|
- Precondition: Proper App Group entitlements.
|
||||||
|
**/
|
||||||
public init(team: String, group: String) {
|
public init(team: String, group: String) {
|
||||||
service = nil
|
service = nil
|
||||||
accessGroup = "\(team).\(group)"
|
accessGroup = "\(team).\(group)"
|
||||||
|
@ -68,6 +86,14 @@ public class Keychain {
|
||||||
|
|
||||||
// MARK: Password
|
// MARK: Password
|
||||||
|
|
||||||
|
/**
|
||||||
|
Sets a password.
|
||||||
|
|
||||||
|
- Parameter password: The password to set.
|
||||||
|
- Parameter username: The username to set the password for.
|
||||||
|
- Parameter label: An optional label.
|
||||||
|
- Throws: `KeychainError.add` if unable to add the password to the keychain.
|
||||||
|
**/
|
||||||
public func set(password: String, for username: String, label: String? = nil) throws {
|
public func set(password: String, for username: String, label: String? = nil) throws {
|
||||||
do {
|
do {
|
||||||
let currentPassword = try self.password(for: username)
|
let currentPassword = try self.password(for: username)
|
||||||
|
@ -96,6 +122,12 @@ public class Keychain {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Removes a password.
|
||||||
|
|
||||||
|
- Parameter username: The username to remove the password for.
|
||||||
|
- Returns: `true` if the password was successfully removed.
|
||||||
|
**/
|
||||||
@discardableResult public func removePassword(for username: String) -> Bool {
|
@discardableResult public func removePassword(for username: String) -> Bool {
|
||||||
var query = [String: Any]()
|
var query = [String: Any]()
|
||||||
setScope(query: &query)
|
setScope(query: &query)
|
||||||
|
@ -106,6 +138,13 @@ public class Keychain {
|
||||||
return (status == errSecSuccess)
|
return (status == errSecSuccess)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Gets a password.
|
||||||
|
|
||||||
|
- Parameter username: The username to get the password for.
|
||||||
|
- Returns: The password for the input username.
|
||||||
|
- Throws: `KeychainError.notFound` if unable to find the password in the keychain.
|
||||||
|
**/
|
||||||
public func password(for username: String) throws -> String {
|
public func password(for username: String) throws -> String {
|
||||||
var query = [String: Any]()
|
var query = [String: Any]()
|
||||||
setScope(query: &query)
|
setScope(query: &query)
|
||||||
|
@ -128,6 +167,13 @@ public class Keychain {
|
||||||
return password
|
return password
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Gets a password reference.
|
||||||
|
|
||||||
|
- Parameter username: The username to get the password for.
|
||||||
|
- Returns: The password reference for the input username.
|
||||||
|
- Throws: `KeychainError.notFound` if unable to find the password in the keychain.
|
||||||
|
**/
|
||||||
public func passwordReference(for username: String) throws -> Data {
|
public func passwordReference(for username: String) throws -> Data {
|
||||||
var query = [String: Any]()
|
var query = [String: Any]()
|
||||||
setScope(query: &query)
|
setScope(query: &query)
|
||||||
|
@ -147,6 +193,14 @@ public class Keychain {
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Gets a password associated with a password reference.
|
||||||
|
|
||||||
|
- Parameter username: The username to get the password for.
|
||||||
|
- Parameter reference: The password reference.
|
||||||
|
- Returns: The password for the input username and reference.
|
||||||
|
- Throws: `KeychainError.notFound` if unable to find the password in the keychain.
|
||||||
|
**/
|
||||||
public static func password(for username: String, reference: Data) throws -> String {
|
public static func password(for username: String, reference: Data) throws -> String {
|
||||||
var query = [String: Any]()
|
var query = [String: Any]()
|
||||||
query[kSecClass as String] = kSecClassGenericPassword
|
query[kSecClass as String] = kSecClassGenericPassword
|
||||||
|
@ -172,6 +226,14 @@ public class Keychain {
|
||||||
|
|
||||||
// https://forums.developer.apple.com/thread/13748
|
// https://forums.developer.apple.com/thread/13748
|
||||||
|
|
||||||
|
/**
|
||||||
|
Adds a public key.
|
||||||
|
|
||||||
|
- Parameter identifier: The unique identifier.
|
||||||
|
- Parameter data: The public key data.
|
||||||
|
- Returns: The `SecKey` object representing the public key.
|
||||||
|
- Throws: `KeychainError.add` if unable to add the public key to the keychain.
|
||||||
|
**/
|
||||||
public func add(publicKeyWithIdentifier identifier: String, data: Data) throws -> SecKey {
|
public func add(publicKeyWithIdentifier identifier: String, data: Data) throws -> SecKey {
|
||||||
var query = [String: Any]()
|
var query = [String: Any]()
|
||||||
query[kSecClass as String] = kSecClassKey
|
query[kSecClass as String] = kSecClassKey
|
||||||
|
@ -190,6 +252,13 @@ public class Keychain {
|
||||||
return try publicKey(withIdentifier: identifier)
|
return try publicKey(withIdentifier: identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Gets a public key.
|
||||||
|
|
||||||
|
- Parameter identifier: The unique identifier.
|
||||||
|
- Returns: The `SecKey` object representing the public key.
|
||||||
|
- Throws: `KeychainError.notFound` if unable to find the public key in the keychain.
|
||||||
|
**/
|
||||||
public func publicKey(withIdentifier identifier: String) throws -> SecKey {
|
public func publicKey(withIdentifier identifier: String) throws -> SecKey {
|
||||||
var query = [String: Any]()
|
var query = [String: Any]()
|
||||||
query[kSecClass as String] = kSecClassKey
|
query[kSecClass as String] = kSecClassKey
|
||||||
|
@ -213,6 +282,12 @@ public class Keychain {
|
||||||
return result as! SecKey
|
return result as! SecKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Removes a public key.
|
||||||
|
|
||||||
|
- Parameter identifier: The unique identifier.
|
||||||
|
- Returns: `true` if the public key was successfully removed.
|
||||||
|
**/
|
||||||
@discardableResult public func remove(publicKeyWithIdentifier identifier: String) -> Bool {
|
@discardableResult public func remove(publicKeyWithIdentifier identifier: String) -> Bool {
|
||||||
var query = [String: Any]()
|
var query = [String: Any]()
|
||||||
query[kSecClass as String] = kSecClassKey
|
query[kSecClass as String] = kSecClassKey
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
//
|
||||||
|
// LinkProducer.swift
|
||||||
|
// TunnelKit
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 5/23/19.
|
||||||
|
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
|
||||||
|
//
|
||||||
|
// https://github.com/passepartoutvpn
|
||||||
|
//
|
||||||
|
// This file is part of TunnelKit.
|
||||||
|
//
|
||||||
|
// TunnelKit is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// TunnelKit is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Entity able to produce a `LinkInterface`.
|
||||||
|
public protocol LinkProducer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns a `LinkInterface`.
|
||||||
|
|
||||||
|
- Parameter mtu: The MTU value.
|
||||||
|
**/
|
||||||
|
func link(withMTU mtu: Int) -> LinkInterface
|
||||||
|
}
|
|
@ -37,39 +37,47 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import SwiftyBeaver
|
import SwiftyBeaver
|
||||||
|
|
||||||
class MemoryDestination: BaseDestination, CustomStringConvertible {
|
/// Implements a `SwiftyBeaver.BaseDestination` logging to a memory buffer.
|
||||||
|
public class MemoryDestination: BaseDestination, CustomStringConvertible {
|
||||||
private var buffer: [String] = []
|
private var buffer: [String] = []
|
||||||
|
|
||||||
var maxLines: Int?
|
/// Max number of retained lines.
|
||||||
|
public var maxLines: Int?
|
||||||
override init() {
|
|
||||||
|
/// :nodoc:
|
||||||
|
public override init() {
|
||||||
super.init()
|
super.init()
|
||||||
asynchronously = false
|
asynchronously = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func start(with existing: [String]) {
|
/**
|
||||||
|
Starts logging. Optionally prepend an array of lines.
|
||||||
|
|
||||||
|
- Parameter existing: The optional lines to prepend (none by default).
|
||||||
|
**/
|
||||||
|
public func start(with existing: [String] = []) {
|
||||||
execute(synchronously: true) {
|
execute(synchronously: true) {
|
||||||
self.buffer = existing
|
self.buffer = existing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func flush(to url: URL) {
|
/**
|
||||||
|
Flushes the log content to an URL.
|
||||||
|
|
||||||
|
- Parameter url: The URL to write the log content to.
|
||||||
|
**/
|
||||||
|
public func flush(to url: URL) {
|
||||||
execute(synchronously: true) {
|
execute(synchronously: true) {
|
||||||
let content = self.buffer.joined(separator: "\n")
|
let content = self.buffer.joined(separator: "\n")
|
||||||
try? content.write(to: url, atomically: true, encoding: .utf8)
|
try? content.write(to: url, atomically: true, encoding: .utf8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var description: String {
|
|
||||||
return executeSynchronously {
|
|
||||||
return self.buffer.joined(separator: "\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
||||||
override func send(_ level: SwiftyBeaver.Level, msg: String, thread: String, file: String, function: String, line: Int, context: Any?) -> String? {
|
/// :nodoc:
|
||||||
|
public override func send(_ level: SwiftyBeaver.Level, msg: String, thread: String, file: String, function: String, line: Int, context: Any?) -> String? {
|
||||||
guard let message = super.send(level, msg: msg, thread: thread, file: file, function: function, line: line) else {
|
guard let message = super.send(level, msg: msg, thread: thread, file: file, function: function, line: line) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -81,4 +89,13 @@ class MemoryDestination: BaseDestination, CustomStringConvertible {
|
||||||
}
|
}
|
||||||
return message
|
return message
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: CustomStringConvertible
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
|
public var description: String {
|
||||||
|
return executeSynchronously {
|
||||||
|
return self.buffer.joined(separator: "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,16 +37,18 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
import SwiftyBeaver
|
import SwiftyBeaver
|
||||||
import __TunnelKitOpenVPN
|
|
||||||
|
|
||||||
private let log = SwiftyBeaver.self
|
private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
class NETCPSocket: NSObject, GenericSocket {
|
/// TCP implementation of a `GenericSocket` via NetworkExtension.
|
||||||
|
public class NETCPSocket: NSObject, GenericSocket {
|
||||||
private static var linkContext = 0
|
private static var linkContext = 0
|
||||||
|
|
||||||
private let impl: NWTCPConnection
|
/// :nodoc:
|
||||||
|
public let impl: NWTCPConnection
|
||||||
|
|
||||||
init(impl: NWTCPConnection) {
|
/// :nodoc:
|
||||||
|
public init(impl: NWTCPConnection) {
|
||||||
self.impl = impl
|
self.impl = impl
|
||||||
isActive = false
|
isActive = false
|
||||||
isShutdown = false
|
isShutdown = false
|
||||||
|
@ -58,19 +60,24 @@ class NETCPSocket: NSObject, GenericSocket {
|
||||||
|
|
||||||
private var isActive: Bool
|
private var isActive: Bool
|
||||||
|
|
||||||
private(set) var isShutdown: Bool
|
/// :nodoc:
|
||||||
|
public private(set) var isShutdown: Bool
|
||||||
|
|
||||||
var remoteAddress: String? {
|
/// :nodoc:
|
||||||
|
public var remoteAddress: String? {
|
||||||
return (impl.remoteAddress as? NWHostEndpoint)?.hostname
|
return (impl.remoteAddress as? NWHostEndpoint)?.hostname
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasBetterPath: Bool {
|
/// :nodoc:
|
||||||
|
public var hasBetterPath: Bool {
|
||||||
return impl.hasBetterPath
|
return impl.hasBetterPath
|
||||||
}
|
}
|
||||||
|
|
||||||
weak var delegate: GenericSocketDelegate?
|
/// :nodoc:
|
||||||
|
public weak var delegate: GenericSocketDelegate?
|
||||||
|
|
||||||
func observe(queue: DispatchQueue, activeTimeout: Int) {
|
/// :nodoc:
|
||||||
|
public func observe(queue: DispatchQueue, activeTimeout: Int) {
|
||||||
isActive = false
|
isActive = false
|
||||||
|
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
|
@ -87,30 +94,30 @@ class NETCPSocket: NSObject, GenericSocket {
|
||||||
impl.addObserver(self, forKeyPath: #keyPath(NWTCPConnection.hasBetterPath), options: .new, context: &NETCPSocket.linkContext)
|
impl.addObserver(self, forKeyPath: #keyPath(NWTCPConnection.hasBetterPath), options: .new, context: &NETCPSocket.linkContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
func unobserve() {
|
/// :nodoc:
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
func shutdown() {
|
/// :nodoc:
|
||||||
|
public func shutdown() {
|
||||||
impl.writeClose()
|
impl.writeClose()
|
||||||
impl.cancel()
|
impl.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
func upgraded() -> GenericSocket? {
|
/// :nodoc:
|
||||||
|
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))
|
||||||
}
|
}
|
||||||
|
|
||||||
func link(withMTU mtu: Int) -> LinkInterface {
|
|
||||||
return NETCPLink(impl: impl, mtu: mtu)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Connection KVO (any queue)
|
// MARK: Connection KVO (any queue)
|
||||||
|
|
||||||
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
/// :nodoc:
|
||||||
|
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
|
||||||
|
@ -175,77 +182,9 @@ class NETCPSocket: NSObject, GenericSocket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NETCPLink: LinkInterface {
|
/// :nodoc:
|
||||||
private let impl: NWTCPConnection
|
|
||||||
|
|
||||||
private let maxPacketSize: Int
|
|
||||||
|
|
||||||
init(impl: NWTCPConnection, mtu: Int, maxPacketSize: Int? = nil) {
|
|
||||||
self.impl = impl
|
|
||||||
self.mtu = mtu
|
|
||||||
self.maxPacketSize = maxPacketSize ?? (512 * 1024)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: LinkInterface
|
|
||||||
|
|
||||||
let isReliable: Bool = true
|
|
||||||
|
|
||||||
var remoteAddress: String? {
|
|
||||||
return (impl.remoteAddress as? NWHostEndpoint)?.hostname
|
|
||||||
}
|
|
||||||
|
|
||||||
let mtu: Int
|
|
||||||
|
|
||||||
var packetBufferSize: Int {
|
|
||||||
return maxPacketSize
|
|
||||||
}
|
|
||||||
|
|
||||||
func setReadHandler(queue: DispatchQueue, _ handler: @escaping ([Data]?, Error?) -> Void) {
|
|
||||||
loopReadPackets(queue, Data(), handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func loopReadPackets(_ queue: DispatchQueue, _ buffer: Data, _ handler: @escaping ([Data]?, Error?) -> Void) {
|
|
||||||
|
|
||||||
// WARNING: runs in Network.framework queue
|
|
||||||
impl.readMinimumLength(2, maximumLength: packetBufferSize) { [weak self] (data, error) in
|
|
||||||
guard let _ = self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
queue.sync {
|
|
||||||
guard (error == nil), let data = data else {
|
|
||||||
handler(nil, error)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var newBuffer = buffer
|
|
||||||
newBuffer.append(contentsOf: data)
|
|
||||||
var until = 0
|
|
||||||
let packets = PacketStream.packets(fromStream: newBuffer, until: &until)
|
|
||||||
newBuffer = newBuffer.subdata(in: until..<newBuffer.count)
|
|
||||||
self?.loopReadPackets(queue, newBuffer, handler)
|
|
||||||
|
|
||||||
handler(packets, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func writePacket(_ packet: Data, completionHandler: ((Error?) -> Void)?) {
|
|
||||||
let stream = PacketStream.stream(fromPacket: packet)
|
|
||||||
impl.write(stream) { (error) in
|
|
||||||
completionHandler?(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func writePackets(_ packets: [Data], completionHandler: ((Error?) -> Void)?) {
|
|
||||||
let stream = PacketStream.stream(fromPackets: packets)
|
|
||||||
impl.write(stream) { (error) in
|
|
||||||
completionHandler?(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension NETCPSocket {
|
extension NETCPSocket {
|
||||||
override var description: String {
|
public override var description: String {
|
||||||
guard let hostEndpoint = impl.endpoint as? NWHostEndpoint else {
|
guard let hostEndpoint = impl.endpoint as? NWHostEndpoint else {
|
||||||
return impl.endpoint.maskedDescription
|
return impl.endpoint.maskedDescription
|
||||||
}
|
}
|
|
@ -37,25 +37,29 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
|
|
||||||
class NETunnelInterface: TunnelInterface {
|
/// `TunnelInterface` implementation via NetworkExtension.
|
||||||
|
public class NETunnelInterface: TunnelInterface {
|
||||||
private weak var impl: NEPacketTunnelFlow?
|
private weak var impl: NEPacketTunnelFlow?
|
||||||
|
|
||||||
private let protocolNumber: NSNumber
|
private let protocolNumber: NSNumber
|
||||||
|
|
||||||
init(impl: NEPacketTunnelFlow, isIPv6: Bool) {
|
/// :nodoc:
|
||||||
|
public init(impl: NEPacketTunnelFlow, isIPv6: Bool) {
|
||||||
self.impl = impl
|
self.impl = impl
|
||||||
protocolNumber = (isIPv6 ? AF_INET6 : AF_INET) as NSNumber
|
protocolNumber = (isIPv6 ? AF_INET6 : AF_INET) as NSNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: TunnelInterface
|
// MARK: TunnelInterface
|
||||||
|
|
||||||
var isPersistent: Bool {
|
/// :nodoc:
|
||||||
|
public var isPersistent: Bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: IOInterface
|
// MARK: IOInterface
|
||||||
|
|
||||||
func setReadHandler(queue: DispatchQueue, _ handler: @escaping ([Data]?, Error?) -> Void) {
|
/// :nodoc:
|
||||||
|
public func setReadHandler(queue: DispatchQueue, _ handler: @escaping ([Data]?, Error?) -> Void) {
|
||||||
loopReadPackets(queue, handler)
|
loopReadPackets(queue, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,12 +74,14 @@ class NETunnelInterface: TunnelInterface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func writePacket(_ packet: Data, completionHandler: ((Error?) -> Void)?) {
|
/// :nodoc:
|
||||||
|
public func writePacket(_ packet: Data, completionHandler: ((Error?) -> Void)?) {
|
||||||
impl?.writePackets([packet], withProtocols: [protocolNumber])
|
impl?.writePackets([packet], withProtocols: [protocolNumber])
|
||||||
completionHandler?(nil)
|
completionHandler?(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func writePackets(_ packets: [Data], completionHandler: ((Error?) -> Void)?) {
|
/// :nodoc:
|
||||||
|
public func writePackets(_ packets: [Data], completionHandler: ((Error?) -> Void)?) {
|
||||||
let protocols = [NSNumber](repeating: protocolNumber, count: packets.count)
|
let protocols = [NSNumber](repeating: protocolNumber, count: packets.count)
|
||||||
impl?.writePackets(packets, withProtocols: protocols)
|
impl?.writePackets(packets, withProtocols: protocols)
|
||||||
completionHandler?(nil)
|
completionHandler?(nil)
|
||||||
|
|
|
@ -40,12 +40,15 @@ import SwiftyBeaver
|
||||||
|
|
||||||
private let log = SwiftyBeaver.self
|
private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
class NEUDPSocket: NSObject, GenericSocket {
|
/// UDP implementation of a `GenericSocket` via NetworkExtension.
|
||||||
|
public class NEUDPSocket: NSObject, GenericSocket {
|
||||||
private static var linkContext = 0
|
private static var linkContext = 0
|
||||||
|
|
||||||
private let impl: NWUDPSession
|
/// :nodoc:
|
||||||
|
public let impl: NWUDPSession
|
||||||
|
|
||||||
init(impl: NWUDPSession) {
|
/// :nodoc:
|
||||||
|
public init(impl: NWUDPSession) {
|
||||||
self.impl = impl
|
self.impl = impl
|
||||||
|
|
||||||
isActive = false
|
isActive = false
|
||||||
|
@ -58,19 +61,24 @@ class NEUDPSocket: NSObject, GenericSocket {
|
||||||
|
|
||||||
private var isActive: Bool
|
private var isActive: Bool
|
||||||
|
|
||||||
private(set) var isShutdown: Bool
|
/// :nodoc:
|
||||||
|
public private(set) var isShutdown: Bool
|
||||||
|
|
||||||
var remoteAddress: String? {
|
/// :nodoc:
|
||||||
|
public var remoteAddress: String? {
|
||||||
return (impl.resolvedEndpoint as? NWHostEndpoint)?.hostname
|
return (impl.resolvedEndpoint as? NWHostEndpoint)?.hostname
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasBetterPath: Bool {
|
/// :nodoc:
|
||||||
|
public var hasBetterPath: Bool {
|
||||||
return impl.hasBetterPath
|
return impl.hasBetterPath
|
||||||
}
|
}
|
||||||
|
|
||||||
weak var delegate: GenericSocketDelegate?
|
/// :nodoc:
|
||||||
|
public weak var delegate: GenericSocketDelegate?
|
||||||
|
|
||||||
func observe(queue: DispatchQueue, activeTimeout: Int) {
|
/// :nodoc:
|
||||||
|
public func observe(queue: DispatchQueue, activeTimeout: Int) {
|
||||||
isActive = false
|
isActive = false
|
||||||
|
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
|
@ -87,29 +95,29 @@ class NEUDPSocket: NSObject, GenericSocket {
|
||||||
impl.addObserver(self, forKeyPath: #keyPath(NWUDPSession.hasBetterPath), options: .new, context: &NEUDPSocket.linkContext)
|
impl.addObserver(self, forKeyPath: #keyPath(NWUDPSession.hasBetterPath), options: .new, context: &NEUDPSocket.linkContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
func unobserve() {
|
/// :nodoc:
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
func shutdown() {
|
/// :nodoc:
|
||||||
|
public func shutdown() {
|
||||||
impl.cancel()
|
impl.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
func upgraded() -> GenericSocket? {
|
/// :nodoc:
|
||||||
|
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))
|
||||||
}
|
}
|
||||||
|
|
||||||
func link(withMTU mtu: Int) -> LinkInterface {
|
|
||||||
return NEUDPLink(impl: impl, mtu: mtu)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Connection KVO (any queue)
|
// MARK: Connection KVO (any queue)
|
||||||
|
|
||||||
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
/// :nodoc:
|
||||||
|
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
|
||||||
|
@ -177,59 +185,9 @@ class NEUDPSocket: NSObject, GenericSocket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NEUDPLink: LinkInterface {
|
/// :nodoc:
|
||||||
private let impl: NWUDPSession
|
|
||||||
|
|
||||||
private let maxDatagrams: Int
|
|
||||||
|
|
||||||
init(impl: NWUDPSession, mtu: Int, maxDatagrams: Int? = nil) {
|
|
||||||
self.impl = impl
|
|
||||||
self.mtu = mtu
|
|
||||||
self.maxDatagrams = maxDatagrams ?? 200
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: LinkInterface
|
|
||||||
|
|
||||||
let isReliable: Bool = false
|
|
||||||
|
|
||||||
var remoteAddress: String? {
|
|
||||||
return (impl.resolvedEndpoint as? NWHostEndpoint)?.hostname
|
|
||||||
}
|
|
||||||
|
|
||||||
let mtu: Int
|
|
||||||
|
|
||||||
var packetBufferSize: Int {
|
|
||||||
return maxDatagrams
|
|
||||||
}
|
|
||||||
|
|
||||||
func setReadHandler(queue: DispatchQueue, _ handler: @escaping ([Data]?, Error?) -> Void) {
|
|
||||||
|
|
||||||
// WARNING: runs in Network.framework queue
|
|
||||||
impl.setReadHandler({ [weak self] (packets, error) in
|
|
||||||
guard let _ = self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
queue.sync {
|
|
||||||
handler(packets, error)
|
|
||||||
}
|
|
||||||
}, maxDatagrams: maxDatagrams)
|
|
||||||
}
|
|
||||||
|
|
||||||
func writePacket(_ packet: Data, completionHandler: ((Error?) -> Void)?) {
|
|
||||||
impl.writeDatagram(packet) { (error) in
|
|
||||||
completionHandler?(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func writePackets(_ packets: [Data], completionHandler: ((Error?) -> Void)?) {
|
|
||||||
impl.writeMultipleDatagrams(packets) { (error) in
|
|
||||||
completionHandler?(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension NEUDPSocket {
|
extension NEUDPSocket {
|
||||||
override var description: String {
|
public override var description: String {
|
||||||
guard let hostEndpoint = impl.endpoint as? NWHostEndpoint else {
|
guard let hostEndpoint = impl.endpoint as? NWHostEndpoint else {
|
||||||
return impl.endpoint.maskedDescription
|
return impl.endpoint.maskedDescription
|
||||||
}
|
}
|
|
@ -36,10 +36,18 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// :nodoc:
|
/// Convenient methods for DNS resolution.
|
||||||
public class DNSResolver {
|
public class DNSResolver {
|
||||||
private static let queue = DispatchQueue(label: "DNSResolver")
|
private static let queue = DispatchQueue(label: "DNSResolver")
|
||||||
|
|
||||||
|
/**
|
||||||
|
Resolves a hostname asynchronously.
|
||||||
|
|
||||||
|
- Parameter hostname: The hostname to resolve.
|
||||||
|
- Parameter timeout: The timeout in milliseconds.
|
||||||
|
- Parameter queue: The queue to execute the `completionHandler` in.
|
||||||
|
- Parameter completionHandler: The completion handler with the resolved addresses and an optional error.
|
||||||
|
*/
|
||||||
public static func resolve(_ hostname: String, timeout: Int, queue: DispatchQueue, completionHandler: @escaping ([String]?, Error?) -> Void) {
|
public static func resolve(_ hostname: String, timeout: Int, queue: DispatchQueue, completionHandler: @escaping ([String]?, Error?) -> Void) {
|
||||||
var pendingHandler: (([String]?, Error?) -> Void)? = completionHandler
|
var pendingHandler: (([String]?, Error?) -> Void)? = completionHandler
|
||||||
let host = CFHostCreateWithName(nil, hostname as CFString).takeRetainedValue()
|
let host = CFHostCreateWithName(nil, hostname as CFString).takeRetainedValue()
|
||||||
|
@ -95,6 +103,12 @@ public class DNSResolver {
|
||||||
completionHandler(ipAddresses, nil)
|
completionHandler(ipAddresses, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns a `String` representation from a numeric IPv4 address.
|
||||||
|
|
||||||
|
- Parameter ipv4: The IPv4 address as a 32-bit number.
|
||||||
|
- Returns: The string representation of `ipv4`.
|
||||||
|
*/
|
||||||
public static func string(fromIPv4 ipv4: UInt32) -> String {
|
public static func string(fromIPv4 ipv4: UInt32) -> String {
|
||||||
var addr = in_addr(s_addr: CFSwapInt32HostToBig(ipv4))
|
var addr = in_addr(s_addr: CFSwapInt32HostToBig(ipv4))
|
||||||
var buf = Data(count: Int(INET_ADDRSTRLEN))
|
var buf = Data(count: Int(INET_ADDRSTRLEN))
|
||||||
|
@ -111,6 +125,12 @@ public class DNSResolver {
|
||||||
return String(cString: result)
|
return String(cString: result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns a numeric representation from an IPv4 address.
|
||||||
|
|
||||||
|
- Parameter string: The IPv4 address as a string.
|
||||||
|
- Returns: The numeric representation of `string`.
|
||||||
|
*/
|
||||||
public static func ipv4(fromString string: String) -> UInt32? {
|
public static func ipv4(fromString string: String) -> UInt32? {
|
||||||
var addr = in_addr()
|
var addr = in_addr()
|
||||||
let result = string.withCString {
|
let result = string.withCString {
|
|
@ -2,7 +2,7 @@
|
||||||
// Errors.swift
|
// Errors.swift
|
||||||
// TunnelKit
|
// TunnelKit
|
||||||
//
|
//
|
||||||
// Created by Davide De Rosa on 05/19/19.
|
// Created by Davide De Rosa on 5/19/19.
|
||||||
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
|
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
|
||||||
//
|
//
|
||||||
// https://github.com/passepartoutvpn
|
// https://github.com/passepartoutvpn
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// Session.swift
|
// Session.swift
|
||||||
// TunnelKit
|
// TunnelKit
|
||||||
//
|
//
|
||||||
// Created by Davide De Rosa on 05/19/19.
|
// Created by Davide De Rosa on 5/19/19.
|
||||||
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
|
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
|
||||||
//
|
//
|
||||||
// https://github.com/passepartoutvpn
|
// https://github.com/passepartoutvpn
|
||||||
|
|
|
@ -36,7 +36,8 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
extension DispatchQueue {
|
/// :nodoc:
|
||||||
|
public extension DispatchQueue {
|
||||||
func schedule(after: DispatchTimeInterval, block: @escaping () -> Void) {
|
func schedule(after: DispatchTimeInterval, block: @escaping () -> Void) {
|
||||||
asyncAfter(deadline: .now() + after, execute: block)
|
asyncAfter(deadline: .now() + after, execute: block)
|
||||||
}
|
}
|
|
@ -51,7 +51,7 @@ class ConnectionStrategy {
|
||||||
|
|
||||||
private var currentProtocolIndex = 0
|
private var currentProtocolIndex = 0
|
||||||
|
|
||||||
init(configuration: TunnelKitProvider.Configuration) {
|
init(configuration: OpenVPNTunnelProvider.Configuration) {
|
||||||
hostname = configuration.sessionConfiguration.hostname
|
hostname = configuration.sessionConfiguration.hostname
|
||||||
prefersResolvedAddresses = (hostname == nil) || configuration.prefersResolvedAddresses
|
prefersResolvedAddresses = (hostname == nil) || configuration.prefersResolvedAddresses
|
||||||
resolvedAddresses = configuration.resolvedAddresses
|
resolvedAddresses = configuration.resolvedAddresses
|
||||||
|
@ -95,7 +95,7 @@ class ConnectionStrategy {
|
||||||
// fall back to DNS
|
// fall back to DNS
|
||||||
guard let hostname = hostname else {
|
guard let hostname = hostname else {
|
||||||
log.error("DNS resolution unavailable: no hostname provided!")
|
log.error("DNS resolution unavailable: no hostname provided!")
|
||||||
completionHandler(nil, TunnelKitProvider.ProviderError.dnsFailure)
|
completionHandler(nil, OpenVPNTunnelProvider.ProviderError.dnsFailure)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.debug("DNS resolve hostname: \(hostname.maskedDescription)")
|
log.debug("DNS resolve hostname: \(hostname.maskedDescription)")
|
||||||
|
@ -112,7 +112,7 @@ class ConnectionStrategy {
|
||||||
|
|
||||||
guard let targetAddress = self.resolvedAddress(from: addresses) else {
|
guard let targetAddress = self.resolvedAddress(from: addresses) else {
|
||||||
log.error("No resolved or fallback address available")
|
log.error("No resolved or fallback address available")
|
||||||
completionHandler(nil, TunnelKitProvider.ProviderError.dnsFailure)
|
completionHandler(nil, OpenVPNTunnelProvider.ProviderError.dnsFailure)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
//
|
||||||
|
// NETCPLink.swift
|
||||||
|
// TunnelKit
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 5/23/19.
|
||||||
|
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
|
||||||
|
//
|
||||||
|
// https://github.com/passepartoutvpn
|
||||||
|
//
|
||||||
|
// This file is part of TunnelKit.
|
||||||
|
//
|
||||||
|
// TunnelKit is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// TunnelKit is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import NetworkExtension
|
||||||
|
import __TunnelKitOpenVPN
|
||||||
|
|
||||||
|
class NETCPLink: LinkInterface {
|
||||||
|
private let impl: NWTCPConnection
|
||||||
|
|
||||||
|
private let maxPacketSize: Int
|
||||||
|
|
||||||
|
init(impl: NWTCPConnection, mtu: Int, maxPacketSize: Int? = nil) {
|
||||||
|
self.impl = impl
|
||||||
|
self.mtu = mtu
|
||||||
|
self.maxPacketSize = maxPacketSize ?? (512 * 1024)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: LinkInterface
|
||||||
|
|
||||||
|
let isReliable: Bool = true
|
||||||
|
|
||||||
|
var remoteAddress: String? {
|
||||||
|
return (impl.remoteAddress as? NWHostEndpoint)?.hostname
|
||||||
|
}
|
||||||
|
|
||||||
|
let mtu: Int
|
||||||
|
|
||||||
|
var packetBufferSize: Int {
|
||||||
|
return maxPacketSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func setReadHandler(queue: DispatchQueue, _ handler: @escaping ([Data]?, Error?) -> Void) {
|
||||||
|
loopReadPackets(queue, Data(), handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func loopReadPackets(_ queue: DispatchQueue, _ buffer: Data, _ handler: @escaping ([Data]?, Error?) -> Void) {
|
||||||
|
|
||||||
|
// WARNING: runs in Network.framework queue
|
||||||
|
impl.readMinimumLength(2, maximumLength: packetBufferSize) { [weak self] (data, error) in
|
||||||
|
guard let _ = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
queue.sync {
|
||||||
|
guard (error == nil), let data = data else {
|
||||||
|
handler(nil, error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var newBuffer = buffer
|
||||||
|
newBuffer.append(contentsOf: data)
|
||||||
|
var until = 0
|
||||||
|
let packets = PacketStream.packets(fromStream: newBuffer, until: &until)
|
||||||
|
newBuffer = newBuffer.subdata(in: until..<newBuffer.count)
|
||||||
|
self?.loopReadPackets(queue, newBuffer, handler)
|
||||||
|
|
||||||
|
handler(packets, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writePacket(_ packet: Data, completionHandler: ((Error?) -> Void)?) {
|
||||||
|
let stream = PacketStream.stream(fromPacket: packet)
|
||||||
|
impl.write(stream) { (error) in
|
||||||
|
completionHandler?(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writePackets(_ packets: [Data], completionHandler: ((Error?) -> Void)?) {
|
||||||
|
let stream = PacketStream.stream(fromPackets: packets)
|
||||||
|
impl.write(stream) { (error) in
|
||||||
|
completionHandler?(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
|
extension NETCPSocket: LinkProducer {
|
||||||
|
public func link(withMTU mtu: Int) -> LinkInterface {
|
||||||
|
return NETCPLink(impl: impl, mtu: mtu)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
//
|
||||||
|
// NEUDPLink.swift
|
||||||
|
// TunnelKit
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 5/23/19.
|
||||||
|
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
|
||||||
|
//
|
||||||
|
// https://github.com/passepartoutvpn
|
||||||
|
//
|
||||||
|
// This file is part of TunnelKit.
|
||||||
|
//
|
||||||
|
// TunnelKit is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// TunnelKit is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import NetworkExtension
|
||||||
|
|
||||||
|
class NEUDPLink: LinkInterface {
|
||||||
|
private let impl: NWUDPSession
|
||||||
|
|
||||||
|
private let maxDatagrams: Int
|
||||||
|
|
||||||
|
init(impl: NWUDPSession, mtu: Int, maxDatagrams: Int? = nil) {
|
||||||
|
self.impl = impl
|
||||||
|
self.mtu = mtu
|
||||||
|
self.maxDatagrams = maxDatagrams ?? 200
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: LinkInterface
|
||||||
|
|
||||||
|
let isReliable: Bool = false
|
||||||
|
|
||||||
|
var remoteAddress: String? {
|
||||||
|
return (impl.resolvedEndpoint as? NWHostEndpoint)?.hostname
|
||||||
|
}
|
||||||
|
|
||||||
|
let mtu: Int
|
||||||
|
|
||||||
|
var packetBufferSize: Int {
|
||||||
|
return maxDatagrams
|
||||||
|
}
|
||||||
|
|
||||||
|
func setReadHandler(queue: DispatchQueue, _ handler: @escaping ([Data]?, Error?) -> Void) {
|
||||||
|
|
||||||
|
// WARNING: runs in Network.framework queue
|
||||||
|
impl.setReadHandler({ [weak self] (packets, error) in
|
||||||
|
guard let _ = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
queue.sync {
|
||||||
|
handler(packets, error)
|
||||||
|
}
|
||||||
|
}, maxDatagrams: maxDatagrams)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writePacket(_ packet: Data, completionHandler: ((Error?) -> Void)?) {
|
||||||
|
impl.writeDatagram(packet) { (error) in
|
||||||
|
completionHandler?(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writePackets(_ packets: [Data], completionHandler: ((Error?) -> Void)?) {
|
||||||
|
impl.writeMultipleDatagrams(packets) { (error) in
|
||||||
|
completionHandler?(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
|
extension NEUDPSocket: LinkProducer {
|
||||||
|
public func link(withMTU mtu: Int) -> LinkInterface {
|
||||||
|
return NEUDPLink(impl: impl, mtu: mtu)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// TunnelKitProvider+Configuration.swift
|
// OpenVPNTunnelProvider+Configuration.swift
|
||||||
// TunnelKit
|
// TunnelKit
|
||||||
//
|
//
|
||||||
// Created by Davide De Rosa on 10/23/17.
|
// Created by Davide De Rosa on 10/23/17.
|
||||||
|
@ -40,25 +40,27 @@ import SwiftyBeaver
|
||||||
|
|
||||||
private let log = SwiftyBeaver.self
|
private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
extension TunnelKitProvider {
|
extension OpenVPNTunnelProvider {
|
||||||
|
|
||||||
// MARK: Configuration
|
// MARK: Configuration
|
||||||
|
|
||||||
/// The way to create a `TunnelKitProvider.Configuration` object for the tunnel profile.
|
/// The way to create a `OpenVPNTunnelProvider.Configuration` object for the tunnel profile.
|
||||||
public struct ConfigurationBuilder {
|
public struct ConfigurationBuilder {
|
||||||
|
|
||||||
/// :nodoc:
|
/// :nodoc:
|
||||||
public static let defaults = Configuration(
|
public static let defaults = Configuration(
|
||||||
|
sessionConfiguration: OpenVPN.ConfigurationBuilder().build(),
|
||||||
prefersResolvedAddresses: false,
|
prefersResolvedAddresses: false,
|
||||||
resolvedAddresses: nil,
|
resolvedAddresses: nil,
|
||||||
endpointProtocols: nil,
|
|
||||||
mtu: 1250,
|
mtu: 1250,
|
||||||
sessionConfiguration: OpenVPN.ConfigurationBuilder().build(),
|
|
||||||
shouldDebug: false,
|
shouldDebug: false,
|
||||||
debugLogFormat: nil,
|
debugLogFormat: nil,
|
||||||
masksPrivateData: true
|
masksPrivateData: true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/// The session configuration.
|
||||||
|
public var sessionConfiguration: OpenVPN.Configuration
|
||||||
|
|
||||||
/// Prefers resolved addresses over DNS resolution. `resolvedAddresses` must be set and non-empty. Default is `false`.
|
/// Prefers resolved addresses over DNS resolution. `resolvedAddresses` must be set and non-empty. Default is `false`.
|
||||||
///
|
///
|
||||||
/// - Seealso: `fallbackServerAddresses`
|
/// - Seealso: `fallbackServerAddresses`
|
||||||
|
@ -70,9 +72,6 @@ extension TunnelKitProvider {
|
||||||
/// The MTU of the link.
|
/// The MTU of the link.
|
||||||
public var mtu: Int
|
public var mtu: Int
|
||||||
|
|
||||||
/// The session configuration.
|
|
||||||
public var sessionConfiguration: OpenVPN.Configuration
|
|
||||||
|
|
||||||
// MARK: Debugging
|
// MARK: Debugging
|
||||||
|
|
||||||
/// Enables debugging.
|
/// Enables debugging.
|
||||||
|
@ -92,10 +91,10 @@ extension TunnelKitProvider {
|
||||||
- Parameter ca: The CA certificate.
|
- Parameter ca: The CA certificate.
|
||||||
*/
|
*/
|
||||||
public init(sessionConfiguration: OpenVPN.Configuration) {
|
public init(sessionConfiguration: OpenVPN.Configuration) {
|
||||||
|
self.sessionConfiguration = sessionConfiguration
|
||||||
prefersResolvedAddresses = ConfigurationBuilder.defaults.prefersResolvedAddresses
|
prefersResolvedAddresses = ConfigurationBuilder.defaults.prefersResolvedAddresses
|
||||||
resolvedAddresses = nil
|
resolvedAddresses = nil
|
||||||
mtu = ConfigurationBuilder.defaults.mtu
|
mtu = ConfigurationBuilder.defaults.mtu
|
||||||
self.sessionConfiguration = sessionConfiguration
|
|
||||||
shouldDebug = ConfigurationBuilder.defaults.shouldDebug
|
shouldDebug = ConfigurationBuilder.defaults.shouldDebug
|
||||||
debugLogFormat = ConfigurationBuilder.defaults.debugLogFormat
|
debugLogFormat = ConfigurationBuilder.defaults.debugLogFormat
|
||||||
masksPrivateData = ConfigurationBuilder.defaults.masksPrivateData
|
masksPrivateData = ConfigurationBuilder.defaults.masksPrivateData
|
||||||
|
@ -104,88 +103,10 @@ extension TunnelKitProvider {
|
||||||
fileprivate init(providerConfiguration: [String: Any]) throws {
|
fileprivate init(providerConfiguration: [String: Any]) throws {
|
||||||
let S = Configuration.Keys.self
|
let S = Configuration.Keys.self
|
||||||
|
|
||||||
|
sessionConfiguration = try OpenVPN.Configuration.with(providerConfiguration: providerConfiguration)
|
||||||
prefersResolvedAddresses = providerConfiguration[S.prefersResolvedAddresses] as? Bool ?? ConfigurationBuilder.defaults.prefersResolvedAddresses
|
prefersResolvedAddresses = providerConfiguration[S.prefersResolvedAddresses] as? Bool ?? ConfigurationBuilder.defaults.prefersResolvedAddresses
|
||||||
resolvedAddresses = providerConfiguration[S.resolvedAddresses] as? [String]
|
resolvedAddresses = providerConfiguration[S.resolvedAddresses] as? [String]
|
||||||
mtu = providerConfiguration[S.mtu] as? Int ?? ConfigurationBuilder.defaults.mtu
|
mtu = providerConfiguration[S.mtu] as? Int ?? ConfigurationBuilder.defaults.mtu
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
guard let caPEM = providerConfiguration[S.ca] as? String else {
|
|
||||||
throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[\(S.ca)]")
|
|
||||||
}
|
|
||||||
|
|
||||||
var sessionConfigurationBuilder = OpenVPN.ConfigurationBuilder()
|
|
||||||
if let cipherAlgorithm = providerConfiguration[S.cipherAlgorithm] as? String {
|
|
||||||
sessionConfigurationBuilder.cipher = OpenVPN.Cipher(rawValue: cipherAlgorithm)
|
|
||||||
}
|
|
||||||
if let digestAlgorithm = providerConfiguration[S.digestAlgorithm] as? String {
|
|
||||||
sessionConfigurationBuilder.digest = OpenVPN.Digest(rawValue: digestAlgorithm)
|
|
||||||
}
|
|
||||||
if let compressionFramingValue = providerConfiguration[S.compressionFraming] as? Int, let compressionFraming = OpenVPN.CompressionFraming(rawValue: compressionFramingValue) {
|
|
||||||
sessionConfigurationBuilder.compressionFraming = compressionFraming
|
|
||||||
} else {
|
|
||||||
sessionConfigurationBuilder.compressionFraming = ConfigurationBuilder.defaults.sessionConfiguration.compressionFraming
|
|
||||||
}
|
|
||||||
if let compressionAlgorithmValue = providerConfiguration[S.compressionAlgorithm] as? Int, let compressionAlgorithm = OpenVPN.CompressionAlgorithm(rawValue: compressionAlgorithmValue) {
|
|
||||||
sessionConfigurationBuilder.compressionAlgorithm = compressionAlgorithm
|
|
||||||
} else {
|
|
||||||
sessionConfigurationBuilder.compressionAlgorithm = ConfigurationBuilder.defaults.sessionConfiguration.compressionAlgorithm
|
|
||||||
}
|
|
||||||
sessionConfigurationBuilder.ca = OpenVPN.CryptoContainer(pem: caPEM)
|
|
||||||
if let clientPEM = providerConfiguration[S.clientCertificate] as? String {
|
|
||||||
guard let keyPEM = providerConfiguration[S.clientKey] as? String else {
|
|
||||||
throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[\(S.clientKey)]")
|
|
||||||
}
|
|
||||||
sessionConfigurationBuilder.clientCertificate = OpenVPN.CryptoContainer(pem: clientPEM)
|
|
||||||
sessionConfigurationBuilder.clientKey = OpenVPN.CryptoContainer(pem: keyPEM)
|
|
||||||
}
|
|
||||||
if let tlsWrapData = providerConfiguration[S.tlsWrap] as? Data {
|
|
||||||
do {
|
|
||||||
sessionConfigurationBuilder.tlsWrap = try OpenVPN.TLSWrap.deserialized(tlsWrapData)
|
|
||||||
} catch {
|
|
||||||
throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[\(S.tlsWrap)]")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sessionConfigurationBuilder.tlsSecurityLevel = providerConfiguration[S.tlsSecurityLevel] as? Int ?? ConfigurationBuilder.defaults.sessionConfiguration.tlsSecurityLevel
|
|
||||||
sessionConfigurationBuilder.keepAliveInterval = providerConfiguration[S.keepAlive] as? TimeInterval ?? ConfigurationBuilder.defaults.sessionConfiguration.keepAliveInterval
|
|
||||||
sessionConfigurationBuilder.renegotiatesAfter = providerConfiguration[S.renegotiatesAfter] as? TimeInterval ?? ConfigurationBuilder.defaults.sessionConfiguration.renegotiatesAfter
|
|
||||||
guard let endpointProtocolsStrings = providerConfiguration[S.endpointProtocols] as? [String], !endpointProtocolsStrings.isEmpty else {
|
|
||||||
throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[\(S.endpointProtocols)] is nil or empty")
|
|
||||||
}
|
|
||||||
sessionConfigurationBuilder.endpointProtocols = try endpointProtocolsStrings.map {
|
|
||||||
guard let ep = EndpointProtocol(rawValue: $0) else {
|
|
||||||
throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[\(S.endpointProtocols)] has a badly formed element")
|
|
||||||
}
|
|
||||||
return ep
|
|
||||||
}
|
|
||||||
sessionConfigurationBuilder.checksEKU = providerConfiguration[S.checksEKU] as? Bool ?? ConfigurationBuilder.defaults.sessionConfiguration.checksEKU
|
|
||||||
sessionConfigurationBuilder.randomizeEndpoint = providerConfiguration[S.randomizeEndpoint] as? Bool ?? ConfigurationBuilder.defaults.sessionConfiguration.randomizeEndpoint
|
|
||||||
sessionConfigurationBuilder.usesPIAPatches = providerConfiguration[S.usesPIAPatches] as? Bool ?? ConfigurationBuilder.defaults.sessionConfiguration.usesPIAPatches
|
|
||||||
sessionConfigurationBuilder.dnsServers = providerConfiguration[S.dnsServers] as? [String]
|
|
||||||
sessionConfigurationBuilder.searchDomain = providerConfiguration[S.searchDomain] as? String
|
|
||||||
if let proxyString = providerConfiguration[S.httpProxy] as? String {
|
|
||||||
guard let proxy = Proxy(rawValue: proxyString) else {
|
|
||||||
throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[\(S.httpProxy)] has a badly formed element")
|
|
||||||
}
|
|
||||||
sessionConfigurationBuilder.httpProxy = proxy
|
|
||||||
}
|
|
||||||
if let proxyString = providerConfiguration[S.httpsProxy] as? String {
|
|
||||||
guard let proxy = Proxy(rawValue: proxyString) else {
|
|
||||||
throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[\(S.httpsProxy)] has a badly formed element")
|
|
||||||
}
|
|
||||||
sessionConfigurationBuilder.httpsProxy = proxy
|
|
||||||
}
|
|
||||||
sessionConfigurationBuilder.proxyBypassDomains = providerConfiguration[S.proxyBypassDomains] as? [String]
|
|
||||||
if let routingPoliciesStrings = providerConfiguration[S.routingPolicies] as? [String] {
|
|
||||||
sessionConfigurationBuilder.routingPolicies = try routingPoliciesStrings.map {
|
|
||||||
guard let policy = OpenVPN.RoutingPolicy(rawValue: $0) else {
|
|
||||||
throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[\(S.routingPolicies)] has a badly formed element")
|
|
||||||
}
|
|
||||||
return policy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sessionConfiguration = sessionConfigurationBuilder.build()
|
|
||||||
|
|
||||||
shouldDebug = providerConfiguration[S.debug] as? Bool ?? ConfigurationBuilder.defaults.shouldDebug
|
shouldDebug = providerConfiguration[S.debug] as? Bool ?? ConfigurationBuilder.defaults.shouldDebug
|
||||||
if shouldDebug {
|
if shouldDebug {
|
||||||
debugLogFormat = providerConfiguration[S.debugLogFormat] as? String
|
debugLogFormat = providerConfiguration[S.debugLogFormat] as? String
|
||||||
|
@ -198,17 +119,16 @@ extension TunnelKitProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Builds a `TunnelKitProvider.Configuration` object that will connect to the provided endpoint.
|
Builds a `OpenVPNTunnelProvider.Configuration` object that will connect to the provided endpoint.
|
||||||
|
|
||||||
- Returns: A `TunnelKitProvider.Configuration` object with this builder and the additional method parameters.
|
- Returns: A `OpenVPNTunnelProvider.Configuration` object with this builder and the additional method parameters.
|
||||||
*/
|
*/
|
||||||
public func build() -> Configuration {
|
public func build() -> Configuration {
|
||||||
return Configuration(
|
return Configuration(
|
||||||
|
sessionConfiguration: sessionConfiguration,
|
||||||
prefersResolvedAddresses: prefersResolvedAddresses,
|
prefersResolvedAddresses: prefersResolvedAddresses,
|
||||||
resolvedAddresses: resolvedAddresses,
|
resolvedAddresses: resolvedAddresses,
|
||||||
endpointProtocols: nil,
|
|
||||||
mtu: mtu,
|
mtu: mtu,
|
||||||
sessionConfiguration: sessionConfiguration,
|
|
||||||
shouldDebug: shouldDebug,
|
shouldDebug: shouldDebug,
|
||||||
debugLogFormat: shouldDebug ? debugLogFormat : nil,
|
debugLogFormat: shouldDebug ? debugLogFormat : nil,
|
||||||
masksPrivateData: masksPrivateData
|
masksPrivateData: masksPrivateData
|
||||||
|
@ -216,17 +136,11 @@ extension TunnelKitProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Offers a bridge between the abstract `TunnelKitProvider.ConfigurationBuilder` and a concrete `NETunnelProviderProtocol` profile.
|
/// Offers a bridge between the abstract `OpenVPNTunnelProvider.ConfigurationBuilder` and a concrete `NETunnelProviderProtocol` profile.
|
||||||
public struct Configuration: Codable {
|
public struct Configuration: Codable {
|
||||||
struct Keys {
|
struct Keys {
|
||||||
static let appGroup = "AppGroup"
|
static let appGroup = "AppGroup"
|
||||||
|
|
||||||
static let prefersResolvedAddresses = "PrefersResolvedAddresses"
|
|
||||||
|
|
||||||
static let resolvedAddresses = "ResolvedAddresses"
|
|
||||||
|
|
||||||
static let mtu = "MTU"
|
|
||||||
|
|
||||||
// MARK: SessionConfiguration
|
// MARK: SessionConfiguration
|
||||||
|
|
||||||
static let cipherAlgorithm = "CipherAlgorithm"
|
static let cipherAlgorithm = "CipherAlgorithm"
|
||||||
|
@ -271,6 +185,14 @@ extension TunnelKitProvider {
|
||||||
|
|
||||||
static let routingPolicies = "RoutingPolicies"
|
static let routingPolicies = "RoutingPolicies"
|
||||||
|
|
||||||
|
// MARK: Customization
|
||||||
|
|
||||||
|
static let prefersResolvedAddresses = "PrefersResolvedAddresses"
|
||||||
|
|
||||||
|
static let resolvedAddresses = "ResolvedAddresses"
|
||||||
|
|
||||||
|
static let mtu = "MTU"
|
||||||
|
|
||||||
// MARK: Debugging
|
// MARK: Debugging
|
||||||
|
|
||||||
static let debug = "Debug"
|
static let debug = "Debug"
|
||||||
|
@ -280,29 +202,25 @@ extension TunnelKitProvider {
|
||||||
static let masksPrivateData = "MasksPrivateData"
|
static let masksPrivateData = "MasksPrivateData"
|
||||||
}
|
}
|
||||||
|
|
||||||
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.prefersResolvedAddresses`
|
/// - Seealso: `OpenVPNTunnelProvider.ConfigurationBuilder.sessionConfiguration`
|
||||||
public let prefersResolvedAddresses: Bool
|
|
||||||
|
|
||||||
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.resolvedAddresses`
|
|
||||||
public let resolvedAddresses: [String]?
|
|
||||||
|
|
||||||
/// - Seealso: `OpenVPN.Configuration.endpointProtocols`
|
|
||||||
@available(*, deprecated)
|
|
||||||
public var endpointProtocols: [EndpointProtocol]?
|
|
||||||
|
|
||||||
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.mtu`
|
|
||||||
public let mtu: Int
|
|
||||||
|
|
||||||
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.sessionConfiguration`
|
|
||||||
public let sessionConfiguration: OpenVPN.Configuration
|
public let sessionConfiguration: OpenVPN.Configuration
|
||||||
|
|
||||||
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.shouldDebug`
|
/// - Seealso: `OpenVPNTunnelProvider.ConfigurationBuilder.prefersResolvedAddresses`
|
||||||
|
public let prefersResolvedAddresses: Bool
|
||||||
|
|
||||||
|
/// - Seealso: `OpenVPNTunnelProvider.ConfigurationBuilder.resolvedAddresses`
|
||||||
|
public let resolvedAddresses: [String]?
|
||||||
|
|
||||||
|
/// - Seealso: `OpenVPNTunnelProvider.ConfigurationBuilder.mtu`
|
||||||
|
public let mtu: Int
|
||||||
|
|
||||||
|
/// - Seealso: `OpenVPNTunnelProvider.ConfigurationBuilder.shouldDebug`
|
||||||
public let shouldDebug: Bool
|
public let shouldDebug: Bool
|
||||||
|
|
||||||
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.debugLogFormat`
|
/// - Seealso: `OpenVPNTunnelProvider.ConfigurationBuilder.debugLogFormat`
|
||||||
public let debugLogFormat: String?
|
public let debugLogFormat: String?
|
||||||
|
|
||||||
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.masksPrivateData`
|
/// - Seealso: `OpenVPNTunnelProvider.ConfigurationBuilder.masksPrivateData`
|
||||||
public let masksPrivateData: Bool?
|
public let masksPrivateData: Bool?
|
||||||
|
|
||||||
// MARK: Shortcuts
|
// MARK: Shortcuts
|
||||||
|
@ -397,10 +315,10 @@ extension TunnelKitProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Parses a new `TunnelKitProvider.Configuration` object from a provider configuration map.
|
Parses a new `OpenVPNTunnelProvider.Configuration` object from a provider configuration map.
|
||||||
|
|
||||||
- Parameter from: The map to parse.
|
- Parameter from: The map to parse.
|
||||||
- Returns: The parsed `TunnelKitProvider.Configuration` object.
|
- Returns: The parsed `OpenVPNTunnelProvider.Configuration` object.
|
||||||
- Throws: `ProviderError.configuration` if `providerConfiguration` is incomplete.
|
- Throws: `ProviderError.configuration` if `providerConfiguration` is incomplete.
|
||||||
*/
|
*/
|
||||||
public static func parsed(from providerConfiguration: [String: Any]) throws -> Configuration {
|
public static func parsed(from providerConfiguration: [String: Any]) throws -> Configuration {
|
||||||
|
@ -432,64 +350,7 @@ extension TunnelKitProvider {
|
||||||
S.mtu: mtu,
|
S.mtu: mtu,
|
||||||
S.debug: shouldDebug
|
S.debug: shouldDebug
|
||||||
]
|
]
|
||||||
if let cipher = sessionConfiguration.cipher {
|
sessionConfiguration.store(to: &dict)
|
||||||
dict[S.cipherAlgorithm] = cipher.rawValue
|
|
||||||
}
|
|
||||||
if let digest = sessionConfiguration.digest {
|
|
||||||
dict[S.digestAlgorithm] = digest.rawValue
|
|
||||||
}
|
|
||||||
if let compressionFraming = sessionConfiguration.compressionFraming {
|
|
||||||
dict[S.compressionFraming] = compressionFraming.rawValue
|
|
||||||
}
|
|
||||||
if let compressionAlgorithm = sessionConfiguration.compressionAlgorithm {
|
|
||||||
dict[S.compressionAlgorithm] = compressionAlgorithm.rawValue
|
|
||||||
}
|
|
||||||
if let clientCertificate = sessionConfiguration.clientCertificate {
|
|
||||||
dict[S.clientCertificate] = clientCertificate.pem
|
|
||||||
}
|
|
||||||
if let clientKey = sessionConfiguration.clientKey {
|
|
||||||
dict[S.clientKey] = clientKey.pem
|
|
||||||
}
|
|
||||||
if let tlsWrapData = sessionConfiguration.tlsWrap?.serialized() {
|
|
||||||
dict[S.tlsWrap] = tlsWrapData
|
|
||||||
}
|
|
||||||
if let tlsSecurityLevel = sessionConfiguration.tlsSecurityLevel {
|
|
||||||
dict[S.tlsSecurityLevel] = tlsSecurityLevel
|
|
||||||
}
|
|
||||||
if let keepAliveSeconds = sessionConfiguration.keepAliveInterval {
|
|
||||||
dict[S.keepAlive] = keepAliveSeconds
|
|
||||||
}
|
|
||||||
if let renegotiatesAfterSeconds = sessionConfiguration.renegotiatesAfter {
|
|
||||||
dict[S.renegotiatesAfter] = renegotiatesAfterSeconds
|
|
||||||
}
|
|
||||||
if let checksEKU = sessionConfiguration.checksEKU {
|
|
||||||
dict[S.checksEKU] = checksEKU
|
|
||||||
}
|
|
||||||
if let randomizeEndpoint = sessionConfiguration.randomizeEndpoint {
|
|
||||||
dict[S.randomizeEndpoint] = randomizeEndpoint
|
|
||||||
}
|
|
||||||
if let usesPIAPatches = sessionConfiguration.usesPIAPatches {
|
|
||||||
dict[S.usesPIAPatches] = usesPIAPatches
|
|
||||||
}
|
|
||||||
if let dnsServers = sessionConfiguration.dnsServers {
|
|
||||||
dict[S.dnsServers] = dnsServers
|
|
||||||
}
|
|
||||||
if let searchDomain = sessionConfiguration.searchDomain {
|
|
||||||
dict[S.searchDomain] = searchDomain
|
|
||||||
}
|
|
||||||
if let httpProxy = sessionConfiguration.httpProxy {
|
|
||||||
dict[S.httpProxy] = httpProxy.rawValue
|
|
||||||
}
|
|
||||||
if let httpsProxy = sessionConfiguration.httpsProxy {
|
|
||||||
dict[S.httpsProxy] = httpsProxy.rawValue
|
|
||||||
}
|
|
||||||
if let proxyBypassDomains = sessionConfiguration.proxyBypassDomains {
|
|
||||||
dict[S.proxyBypassDomains] = proxyBypassDomains
|
|
||||||
}
|
|
||||||
if let routingPolicies = sessionConfiguration.routingPolicies {
|
|
||||||
dict[S.routingPolicies] = routingPolicies.map { $0.rawValue }
|
|
||||||
}
|
|
||||||
//
|
|
||||||
if let resolvedAddresses = resolvedAddresses {
|
if let resolvedAddresses = resolvedAddresses {
|
||||||
dict[S.resolvedAddresses] = resolvedAddresses
|
dict[S.resolvedAddresses] = resolvedAddresses
|
||||||
}
|
}
|
||||||
|
@ -532,78 +393,10 @@ extension TunnelKitProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
func print(appVersion: String?) {
|
func print(appVersion: String?) {
|
||||||
guard let endpointProtocols = sessionConfiguration.endpointProtocols else {
|
|
||||||
fatalError("No sessionConfiguration.endpointProtocols set")
|
|
||||||
}
|
|
||||||
|
|
||||||
if let appVersion = appVersion {
|
if let appVersion = appVersion {
|
||||||
log.info("App version: \(appVersion)")
|
log.info("App version: \(appVersion)")
|
||||||
}
|
}
|
||||||
|
sessionConfiguration.print()
|
||||||
log.info("\tProtocols: \(endpointProtocols)")
|
|
||||||
log.info("\tCipher: \(sessionConfiguration.fallbackCipher)")
|
|
||||||
log.info("\tDigest: \(sessionConfiguration.fallbackDigest)")
|
|
||||||
log.info("\tCompression framing: \(sessionConfiguration.fallbackCompressionFraming)")
|
|
||||||
if let compressionAlgorithm = sessionConfiguration.compressionAlgorithm, compressionAlgorithm != .disabled {
|
|
||||||
log.info("\tCompression algorithm: \(compressionAlgorithm)")
|
|
||||||
} else {
|
|
||||||
log.info("\tCompression algorithm: disabled")
|
|
||||||
}
|
|
||||||
if let _ = sessionConfiguration.clientCertificate {
|
|
||||||
log.info("\tClient verification: enabled")
|
|
||||||
} else {
|
|
||||||
log.info("\tClient verification: disabled")
|
|
||||||
}
|
|
||||||
if let tlsWrap = sessionConfiguration.tlsWrap {
|
|
||||||
log.info("\tTLS wrapping: \(tlsWrap.strategy)")
|
|
||||||
} else {
|
|
||||||
log.info("\tTLS wrapping: disabled")
|
|
||||||
}
|
|
||||||
if let tlsSecurityLevel = sessionConfiguration.tlsSecurityLevel {
|
|
||||||
log.info("\tTLS security level: \(tlsSecurityLevel)")
|
|
||||||
} else {
|
|
||||||
log.info("\tTLS security level: default")
|
|
||||||
}
|
|
||||||
if let keepAliveSeconds = sessionConfiguration.keepAliveInterval, keepAliveSeconds > 0 {
|
|
||||||
log.info("\tKeep-alive: \(keepAliveSeconds) seconds")
|
|
||||||
} else {
|
|
||||||
log.info("\tKeep-alive: never")
|
|
||||||
}
|
|
||||||
if let renegotiatesAfterSeconds = sessionConfiguration.renegotiatesAfter, renegotiatesAfterSeconds > 0 {
|
|
||||||
log.info("\tRenegotiation: \(renegotiatesAfterSeconds) seconds")
|
|
||||||
} else {
|
|
||||||
log.info("\tRenegotiation: never")
|
|
||||||
}
|
|
||||||
if sessionConfiguration.checksEKU ?? false {
|
|
||||||
log.info("\tServer EKU verification: enabled")
|
|
||||||
} else {
|
|
||||||
log.info("\tServer EKU verification: disabled")
|
|
||||||
}
|
|
||||||
if sessionConfiguration.randomizeEndpoint ?? false {
|
|
||||||
log.info("\tRandomize endpoint: true")
|
|
||||||
}
|
|
||||||
if let routingPolicies = sessionConfiguration.routingPolicies {
|
|
||||||
log.info("\tGateway: \(routingPolicies.map { $0.rawValue })")
|
|
||||||
} else {
|
|
||||||
log.info("\tGateway: not configured")
|
|
||||||
}
|
|
||||||
if let dnsServers = sessionConfiguration.dnsServers, !dnsServers.isEmpty {
|
|
||||||
log.info("\tDNS: \(dnsServers.maskedDescription)")
|
|
||||||
} else {
|
|
||||||
log.info("\tDNS: not configured")
|
|
||||||
}
|
|
||||||
if let searchDomain = sessionConfiguration.searchDomain, !searchDomain.isEmpty {
|
|
||||||
log.info("\tSearch domain: \(searchDomain.maskedDescription)")
|
|
||||||
}
|
|
||||||
if let httpProxy = sessionConfiguration.httpProxy {
|
|
||||||
log.info("\tHTTP proxy: \(httpProxy.maskedDescription)")
|
|
||||||
}
|
|
||||||
if let httpsProxy = sessionConfiguration.httpsProxy {
|
|
||||||
log.info("\tHTTPS proxy: \(httpsProxy.maskedDescription)")
|
|
||||||
}
|
|
||||||
if let proxyBypassDomains = sessionConfiguration.proxyBypassDomains {
|
|
||||||
log.info("\tProxy bypass domains: \(proxyBypassDomains.maskedDescription)")
|
|
||||||
}
|
|
||||||
log.info("\tMTU: \(mtu)")
|
log.info("\tMTU: \(mtu)")
|
||||||
log.info("\tDebug: \(shouldDebug)")
|
log.info("\tDebug: \(shouldDebug)")
|
||||||
log.info("\tMasks private data: \(masksPrivateData ?? true)")
|
log.info("\tMasks private data: \(masksPrivateData ?? true)")
|
||||||
|
@ -613,15 +406,15 @@ extension TunnelKitProvider {
|
||||||
|
|
||||||
// MARK: Modification
|
// MARK: Modification
|
||||||
|
|
||||||
extension TunnelKitProvider.Configuration {
|
extension OpenVPNTunnelProvider.Configuration {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns a `TunnelKitProvider.ConfigurationBuilder` to use this configuration as a starting point for a new one.
|
Returns a `OpenVPNTunnelProvider.ConfigurationBuilder` to use this configuration as a starting point for a new one.
|
||||||
|
|
||||||
- Returns: An editable `TunnelKitProvider.ConfigurationBuilder` initialized with this configuration.
|
- Returns: An editable `OpenVPNTunnelProvider.ConfigurationBuilder` initialized with this configuration.
|
||||||
*/
|
*/
|
||||||
public func builder() -> TunnelKitProvider.ConfigurationBuilder {
|
public func builder() -> OpenVPNTunnelProvider.ConfigurationBuilder {
|
||||||
var builder = TunnelKitProvider.ConfigurationBuilder(sessionConfiguration: sessionConfiguration)
|
var builder = OpenVPNTunnelProvider.ConfigurationBuilder(sessionConfiguration: sessionConfiguration)
|
||||||
builder.prefersResolvedAddresses = prefersResolvedAddresses
|
builder.prefersResolvedAddresses = prefersResolvedAddresses
|
||||||
builder.resolvedAddresses = resolvedAddresses
|
builder.resolvedAddresses = resolvedAddresses
|
||||||
builder.mtu = mtu
|
builder.mtu = mtu
|
||||||
|
@ -636,14 +429,247 @@ extension TunnelKitProvider.Configuration {
|
||||||
public extension UserDefaults {
|
public extension UserDefaults {
|
||||||
@objc var dataCountArray: [Int]? {
|
@objc var dataCountArray: [Int]? {
|
||||||
get {
|
get {
|
||||||
return array(forKey: TunnelKitProvider.Configuration.dataCountKey) as? [Int]
|
return array(forKey: OpenVPNTunnelProvider.Configuration.dataCountKey) as? [Int]
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
set(newValue, forKey: TunnelKitProvider.Configuration.dataCountKey)
|
set(newValue, forKey: OpenVPNTunnelProvider.Configuration.dataCountKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeDataCountArray() {
|
func removeDataCountArray() {
|
||||||
removeObject(forKey: TunnelKitProvider.Configuration.dataCountKey)
|
removeObject(forKey: OpenVPNTunnelProvider.Configuration.dataCountKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: OpenVPN configuration
|
||||||
|
|
||||||
|
private extension OpenVPN.Configuration {
|
||||||
|
static func with(providerConfiguration: [String: Any]) throws -> OpenVPN.Configuration {
|
||||||
|
let S = OpenVPNTunnelProvider.Configuration.Keys.self
|
||||||
|
let E = OpenVPNTunnelProvider.ProviderConfigurationError.self
|
||||||
|
|
||||||
|
guard let caPEM = providerConfiguration[S.ca] as? String else {
|
||||||
|
throw E.parameter(name: "protocolConfiguration.providerConfiguration[\(S.ca)]")
|
||||||
|
}
|
||||||
|
guard let endpointProtocolsStrings = providerConfiguration[S.endpointProtocols] as? [String], !endpointProtocolsStrings.isEmpty else {
|
||||||
|
throw E.parameter(name: "protocolConfiguration.providerConfiguration[\(S.endpointProtocols)] is nil or empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = OpenVPNTunnelProvider.ConfigurationBuilder.defaults.sessionConfiguration.builder()
|
||||||
|
|
||||||
|
builder.ca = OpenVPN.CryptoContainer(pem: caPEM)
|
||||||
|
builder.endpointProtocols = try endpointProtocolsStrings.map {
|
||||||
|
guard let ep = EndpointProtocol(rawValue: $0) else {
|
||||||
|
throw E.parameter(name: "protocolConfiguration.providerConfiguration[\(S.endpointProtocols)] has a badly formed element")
|
||||||
|
}
|
||||||
|
return ep
|
||||||
|
}
|
||||||
|
|
||||||
|
if let cipherAlgorithm = providerConfiguration[S.cipherAlgorithm] as? String {
|
||||||
|
builder.cipher = OpenVPN.Cipher(rawValue: cipherAlgorithm)
|
||||||
|
}
|
||||||
|
if let digestAlgorithm = providerConfiguration[S.digestAlgorithm] as? String {
|
||||||
|
builder.digest = OpenVPN.Digest(rawValue: digestAlgorithm)
|
||||||
|
}
|
||||||
|
if let compressionFramingValue = providerConfiguration[S.compressionFraming] as? Int, let compressionFraming = OpenVPN.CompressionFraming(rawValue: compressionFramingValue) {
|
||||||
|
builder.compressionFraming = compressionFraming
|
||||||
|
}
|
||||||
|
if let compressionAlgorithmValue = providerConfiguration[S.compressionAlgorithm] as? Int, let compressionAlgorithm = OpenVPN.CompressionAlgorithm(rawValue: compressionAlgorithmValue) {
|
||||||
|
builder.compressionAlgorithm = compressionAlgorithm
|
||||||
|
}
|
||||||
|
if let clientPEM = providerConfiguration[S.clientCertificate] as? String {
|
||||||
|
guard let keyPEM = providerConfiguration[S.clientKey] as? String else {
|
||||||
|
throw E.parameter(name: "protocolConfiguration.providerConfiguration[\(S.clientKey)]")
|
||||||
|
}
|
||||||
|
builder.clientCertificate = OpenVPN.CryptoContainer(pem: clientPEM)
|
||||||
|
builder.clientKey = OpenVPN.CryptoContainer(pem: keyPEM)
|
||||||
|
}
|
||||||
|
if let tlsWrapData = providerConfiguration[S.tlsWrap] as? Data {
|
||||||
|
do {
|
||||||
|
builder.tlsWrap = try OpenVPN.TLSWrap.deserialized(tlsWrapData)
|
||||||
|
} catch {
|
||||||
|
throw E.parameter(name: "protocolConfiguration.providerConfiguration[\(S.tlsWrap)]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let tlsSecurityLevel = providerConfiguration[S.tlsSecurityLevel] as? Int {
|
||||||
|
builder.tlsSecurityLevel = tlsSecurityLevel
|
||||||
|
}
|
||||||
|
if let keepAliveInterval = providerConfiguration[S.keepAlive] as? TimeInterval {
|
||||||
|
builder.keepAliveInterval = keepAliveInterval
|
||||||
|
}
|
||||||
|
if let renegotiatesAfter = providerConfiguration[S.renegotiatesAfter] as? TimeInterval {
|
||||||
|
builder.renegotiatesAfter = renegotiatesAfter
|
||||||
|
}
|
||||||
|
if let checksEKU = providerConfiguration[S.checksEKU] as? Bool {
|
||||||
|
builder.checksEKU = checksEKU
|
||||||
|
}
|
||||||
|
if let randomizeEndpoint = providerConfiguration[S.randomizeEndpoint] as? Bool {
|
||||||
|
builder.randomizeEndpoint = randomizeEndpoint
|
||||||
|
}
|
||||||
|
if let usesPIAPatches = providerConfiguration[S.usesPIAPatches] as? Bool {
|
||||||
|
builder.usesPIAPatches = usesPIAPatches
|
||||||
|
}
|
||||||
|
if let dnsServers = providerConfiguration[S.dnsServers] as? [String] {
|
||||||
|
builder.dnsServers = dnsServers
|
||||||
|
}
|
||||||
|
if let searchDomain = providerConfiguration[S.searchDomain] as? String {
|
||||||
|
builder.searchDomain = searchDomain
|
||||||
|
}
|
||||||
|
if let proxyString = providerConfiguration[S.httpProxy] as? String {
|
||||||
|
guard let proxy = Proxy(rawValue: proxyString) else {
|
||||||
|
throw E.parameter(name: "protocolConfiguration.providerConfiguration[\(S.httpProxy)] has a badly formed element")
|
||||||
|
}
|
||||||
|
builder.httpProxy = proxy
|
||||||
|
}
|
||||||
|
if let proxyString = providerConfiguration[S.httpsProxy] as? String {
|
||||||
|
guard let proxy = Proxy(rawValue: proxyString) else {
|
||||||
|
throw E.parameter(name: "protocolConfiguration.providerConfiguration[\(S.httpsProxy)] has a badly formed element")
|
||||||
|
}
|
||||||
|
builder.httpsProxy = proxy
|
||||||
|
}
|
||||||
|
if let proxyBypassDomains = providerConfiguration[S.proxyBypassDomains] as? [String] {
|
||||||
|
builder.proxyBypassDomains = proxyBypassDomains
|
||||||
|
}
|
||||||
|
if let routingPoliciesStrings = providerConfiguration[S.routingPolicies] as? [String] {
|
||||||
|
builder.routingPolicies = try routingPoliciesStrings.map {
|
||||||
|
guard let policy = OpenVPN.RoutingPolicy(rawValue: $0) else {
|
||||||
|
throw E.parameter(name: "protocolConfiguration.providerConfiguration[\(S.routingPolicies)] has a badly formed element")
|
||||||
|
}
|
||||||
|
return policy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
func store(to dict: inout [String: Any]) {
|
||||||
|
let S = OpenVPNTunnelProvider.Configuration.Keys.self
|
||||||
|
|
||||||
|
if let cipher = cipher {
|
||||||
|
dict[S.cipherAlgorithm] = cipher.rawValue
|
||||||
|
}
|
||||||
|
if let digest = digest {
|
||||||
|
dict[S.digestAlgorithm] = digest.rawValue
|
||||||
|
}
|
||||||
|
if let compressionFraming = compressionFraming {
|
||||||
|
dict[S.compressionFraming] = compressionFraming.rawValue
|
||||||
|
}
|
||||||
|
if let compressionAlgorithm = compressionAlgorithm {
|
||||||
|
dict[S.compressionAlgorithm] = compressionAlgorithm.rawValue
|
||||||
|
}
|
||||||
|
if let clientCertificate = clientCertificate {
|
||||||
|
dict[S.clientCertificate] = clientCertificate.pem
|
||||||
|
}
|
||||||
|
if let clientKey = clientKey {
|
||||||
|
dict[S.clientKey] = clientKey.pem
|
||||||
|
}
|
||||||
|
if let tlsWrapData = tlsWrap?.serialized() {
|
||||||
|
dict[S.tlsWrap] = tlsWrapData
|
||||||
|
}
|
||||||
|
if let tlsSecurityLevel = tlsSecurityLevel {
|
||||||
|
dict[S.tlsSecurityLevel] = tlsSecurityLevel
|
||||||
|
}
|
||||||
|
if let keepAliveSeconds = keepAliveInterval {
|
||||||
|
dict[S.keepAlive] = keepAliveSeconds
|
||||||
|
}
|
||||||
|
if let renegotiatesAfterSeconds = renegotiatesAfter {
|
||||||
|
dict[S.renegotiatesAfter] = renegotiatesAfterSeconds
|
||||||
|
}
|
||||||
|
if let checksEKU = checksEKU {
|
||||||
|
dict[S.checksEKU] = checksEKU
|
||||||
|
}
|
||||||
|
if let randomizeEndpoint = randomizeEndpoint {
|
||||||
|
dict[S.randomizeEndpoint] = randomizeEndpoint
|
||||||
|
}
|
||||||
|
if let usesPIAPatches = usesPIAPatches {
|
||||||
|
dict[S.usesPIAPatches] = usesPIAPatches
|
||||||
|
}
|
||||||
|
if let dnsServers = dnsServers {
|
||||||
|
dict[S.dnsServers] = dnsServers
|
||||||
|
}
|
||||||
|
if let searchDomain = searchDomain {
|
||||||
|
dict[S.searchDomain] = searchDomain
|
||||||
|
}
|
||||||
|
if let httpProxy = httpProxy {
|
||||||
|
dict[S.httpProxy] = httpProxy.rawValue
|
||||||
|
}
|
||||||
|
if let httpsProxy = httpsProxy {
|
||||||
|
dict[S.httpsProxy] = httpsProxy.rawValue
|
||||||
|
}
|
||||||
|
if let proxyBypassDomains = proxyBypassDomains {
|
||||||
|
dict[S.proxyBypassDomains] = proxyBypassDomains
|
||||||
|
}
|
||||||
|
if let routingPolicies = routingPolicies {
|
||||||
|
dict[S.routingPolicies] = routingPolicies.map { $0.rawValue }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func print() {
|
||||||
|
guard let endpointProtocols = endpointProtocols else {
|
||||||
|
fatalError("No sessionConfiguration.endpointProtocols set")
|
||||||
|
}
|
||||||
|
log.info("\tProtocols: \(endpointProtocols)")
|
||||||
|
log.info("\tCipher: \(fallbackCipher)")
|
||||||
|
log.info("\tDigest: \(fallbackDigest)")
|
||||||
|
log.info("\tCompression framing: \(fallbackCompressionFraming)")
|
||||||
|
if let compressionAlgorithm = compressionAlgorithm, compressionAlgorithm != .disabled {
|
||||||
|
log.info("\tCompression algorithm: \(compressionAlgorithm)")
|
||||||
|
} else {
|
||||||
|
log.info("\tCompression algorithm: disabled")
|
||||||
|
}
|
||||||
|
if let _ = clientCertificate {
|
||||||
|
log.info("\tClient verification: enabled")
|
||||||
|
} else {
|
||||||
|
log.info("\tClient verification: disabled")
|
||||||
|
}
|
||||||
|
if let tlsWrap = tlsWrap {
|
||||||
|
log.info("\tTLS wrapping: \(tlsWrap.strategy)")
|
||||||
|
} else {
|
||||||
|
log.info("\tTLS wrapping: disabled")
|
||||||
|
}
|
||||||
|
if let tlsSecurityLevel = tlsSecurityLevel {
|
||||||
|
log.info("\tTLS security level: \(tlsSecurityLevel)")
|
||||||
|
} else {
|
||||||
|
log.info("\tTLS security level: default")
|
||||||
|
}
|
||||||
|
if let keepAliveSeconds = keepAliveInterval, keepAliveSeconds > 0 {
|
||||||
|
log.info("\tKeep-alive: \(keepAliveSeconds) seconds")
|
||||||
|
} else {
|
||||||
|
log.info("\tKeep-alive: never")
|
||||||
|
}
|
||||||
|
if let renegotiatesAfterSeconds = renegotiatesAfter, renegotiatesAfterSeconds > 0 {
|
||||||
|
log.info("\tRenegotiation: \(renegotiatesAfterSeconds) seconds")
|
||||||
|
} else {
|
||||||
|
log.info("\tRenegotiation: never")
|
||||||
|
}
|
||||||
|
if checksEKU ?? false {
|
||||||
|
log.info("\tServer EKU verification: enabled")
|
||||||
|
} else {
|
||||||
|
log.info("\tServer EKU verification: disabled")
|
||||||
|
}
|
||||||
|
if randomizeEndpoint ?? false {
|
||||||
|
log.info("\tRandomize endpoint: true")
|
||||||
|
}
|
||||||
|
if let routingPolicies = routingPolicies {
|
||||||
|
log.info("\tGateway: \(routingPolicies.map { $0.rawValue })")
|
||||||
|
} else {
|
||||||
|
log.info("\tGateway: not configured")
|
||||||
|
}
|
||||||
|
if let dnsServers = dnsServers, !dnsServers.isEmpty {
|
||||||
|
log.info("\tDNS: \(dnsServers.maskedDescription)")
|
||||||
|
} else {
|
||||||
|
log.info("\tDNS: not configured")
|
||||||
|
}
|
||||||
|
if let searchDomain = searchDomain, !searchDomain.isEmpty {
|
||||||
|
log.info("\tSearch domain: \(searchDomain.maskedDescription)")
|
||||||
|
}
|
||||||
|
if let httpProxy = httpProxy {
|
||||||
|
log.info("\tHTTP proxy: \(httpProxy.maskedDescription)")
|
||||||
|
}
|
||||||
|
if let httpsProxy = httpsProxy {
|
||||||
|
log.info("\tHTTPS proxy: \(httpsProxy.maskedDescription)")
|
||||||
|
}
|
||||||
|
if let proxyBypassDomains = proxyBypassDomains {
|
||||||
|
log.info("\tProxy bypass domains: \(proxyBypassDomains.maskedDescription)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// TunnelKitProvider+Interaction.swift
|
// OpenVPNTunnelProvider+Interaction.swift
|
||||||
// TunnelKit
|
// TunnelKit
|
||||||
//
|
//
|
||||||
// Created by Davide De Rosa on 9/24/17.
|
// Created by Davide De Rosa on 9/24/17.
|
||||||
|
@ -36,11 +36,11 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
extension TunnelKitProvider {
|
extension OpenVPNTunnelProvider {
|
||||||
|
|
||||||
// MARK: Interaction
|
// MARK: Interaction
|
||||||
|
|
||||||
/// The messages accepted by `TunnelKitProvider`.
|
/// The messages accepted by `OpenVPNTunnelProvider`.
|
||||||
public class Message: Equatable {
|
public class Message: Equatable {
|
||||||
|
|
||||||
/// Requests a snapshot of the latest debug log. Returns the log data decoded from UTF-8.
|
/// Requests a snapshot of the latest debug log. Returns the log data decoded from UTF-8.
|
||||||
|
@ -73,7 +73,7 @@ extension TunnelKitProvider {
|
||||||
// mostly programming errors by host app
|
// mostly programming errors by host app
|
||||||
enum ProviderConfigurationError: Error {
|
enum ProviderConfigurationError: Error {
|
||||||
|
|
||||||
/// A field in the `TunnelKitProvider.Configuration` provided is incorrect or incomplete.
|
/// A field in the `OpenVPNTunnelProvider.Configuration` provided is incorrect or incomplete.
|
||||||
case parameter(name: String)
|
case parameter(name: String)
|
||||||
|
|
||||||
/// Credentials are missing or inaccessible.
|
/// Credentials are missing or inaccessible.
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// TunnelKitProvider.swift
|
// OpenVPNTunnelProvider.swift
|
||||||
// TunnelKit
|
// TunnelKit
|
||||||
//
|
//
|
||||||
// Created by Davide De Rosa on 2/1/17.
|
// Created by Davide De Rosa on 2/1/17.
|
||||||
|
@ -44,7 +44,7 @@ private let log = SwiftyBeaver.self
|
||||||
Provides an all-in-one `NEPacketTunnelProvider` implementation for use in a
|
Provides an all-in-one `NEPacketTunnelProvider` implementation for use in a
|
||||||
Packet Tunnel Provider extension both on iOS and macOS.
|
Packet Tunnel Provider extension both on iOS and macOS.
|
||||||
*/
|
*/
|
||||||
open class TunnelKitProvider: NEPacketTunnelProvider {
|
open class OpenVPNTunnelProvider: NEPacketTunnelProvider {
|
||||||
|
|
||||||
// MARK: Tweaks
|
// MARK: Tweaks
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
|
||||||
|
|
||||||
private let observer = InterfaceObserver()
|
private let observer = InterfaceObserver()
|
||||||
|
|
||||||
private let tunnelQueue = DispatchQueue(label: TunnelKitProvider.description())
|
private let tunnelQueue = DispatchQueue(label: OpenVPNTunnelProvider.description())
|
||||||
|
|
||||||
private let prngSeedLength = 64
|
private let prngSeedLength = 64
|
||||||
|
|
||||||
|
@ -382,11 +382,12 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension TunnelKitProvider: GenericSocketDelegate {
|
extension OpenVPNTunnelProvider: GenericSocketDelegate {
|
||||||
|
|
||||||
// MARK: GenericSocketDelegate (tunnel queue)
|
// MARK: GenericSocketDelegate (tunnel queue)
|
||||||
|
|
||||||
func socketDidTimeout(_ socket: GenericSocket) {
|
/// :nodoc:
|
||||||
|
public func socketDidTimeout(_ socket: GenericSocket) {
|
||||||
log.debug("Socket timed out waiting for activity, cancelling...")
|
log.debug("Socket timed out waiting for activity, cancelling...")
|
||||||
reasserting = true
|
reasserting = true
|
||||||
socket.shutdown()
|
socket.shutdown()
|
||||||
|
@ -400,19 +401,21 @@ extension TunnelKitProvider: GenericSocketDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func socketDidBecomeActive(_ socket: GenericSocket) {
|
/// :nodoc:
|
||||||
guard let session = session else {
|
public func socketDidBecomeActive(_ socket: GenericSocket) {
|
||||||
|
guard let session = session, let producer = socket as? LinkProducer else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if session.canRebindLink() {
|
if session.canRebindLink() {
|
||||||
session.rebindLink(socket.link(withMTU: cfg.mtu))
|
session.rebindLink(producer.link(withMTU: cfg.mtu))
|
||||||
reasserting = false
|
reasserting = false
|
||||||
} else {
|
} else {
|
||||||
session.setLink(socket.link(withMTU: cfg.mtu))
|
session.setLink(producer.link(withMTU: cfg.mtu))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func socket(_ socket: GenericSocket, didShutdownWithFailure failure: Bool) {
|
/// :nodoc:
|
||||||
|
public func socket(_ socket: GenericSocket, didShutdownWithFailure failure: Bool) {
|
||||||
guard let session = session else {
|
guard let session = session else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -463,14 +466,15 @@ extension TunnelKitProvider: GenericSocketDelegate {
|
||||||
disposeTunnel(error: shutdownError)
|
disposeTunnel(error: shutdownError)
|
||||||
}
|
}
|
||||||
|
|
||||||
func socketHasBetterPath(_ socket: GenericSocket) {
|
/// :nodoc:
|
||||||
|
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()
|
||||||
session?.reconnect(error: ProviderError.networkChanged)
|
session?.reconnect(error: ProviderError.networkChanged)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension TunnelKitProvider: OpenVPNSessionDelegate {
|
extension OpenVPNTunnelProvider: OpenVPNSessionDelegate {
|
||||||
|
|
||||||
// MARK: OpenVPNSessionDelegate (tunnel queue)
|
// MARK: OpenVPNSessionDelegate (tunnel queue)
|
||||||
|
|
||||||
|
@ -721,7 +725,7 @@ extension TunnelKitProvider: OpenVPNSessionDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension TunnelKitProvider {
|
extension OpenVPNTunnelProvider {
|
||||||
private func tryNextProtocol() -> Bool {
|
private func tryNextProtocol() -> Bool {
|
||||||
guard strategy.tryNextProtocol() else {
|
guard strategy.tryNextProtocol() else {
|
||||||
disposeTunnel(error: ProviderError.exhaustedProtocols)
|
disposeTunnel(error: ProviderError.exhaustedProtocols)
|
|
@ -0,0 +1,55 @@
|
||||||
|
//
|
||||||
|
// TestUtils+Core.swift
|
||||||
|
// TunnelKitTests
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 7/7/18.
|
||||||
|
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
|
||||||
|
//
|
||||||
|
// https://github.com/passepartoutvpn
|
||||||
|
//
|
||||||
|
// This file is part of TunnelKit.
|
||||||
|
//
|
||||||
|
// TunnelKit is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// TunnelKit is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
// This file incorporates work covered by the following copyright and
|
||||||
|
// permission notice:
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018-Present Private Internet Access
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
@testable import TunnelKit
|
||||||
|
|
||||||
|
class TestUtils {
|
||||||
|
static func uniqArray(_ v: [Int]) -> [Int] {
|
||||||
|
return v.reduce([]){ $0.contains($1) ? $0 : $0 + [$1] }
|
||||||
|
}
|
||||||
|
|
||||||
|
static func generateDataSuite(_ size: Int, _ count: Int) -> [Data] {
|
||||||
|
var suite = [Data]()
|
||||||
|
for _ in 0..<count {
|
||||||
|
suite.append(try! SecureRandom.data(length: size))
|
||||||
|
}
|
||||||
|
return suite
|
||||||
|
}
|
||||||
|
|
||||||
|
private init() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -51,8 +51,8 @@ class AppExtensionTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testConfiguration() {
|
func testConfiguration() {
|
||||||
var builder: TunnelKitProvider.ConfigurationBuilder!
|
var builder: OpenVPNTunnelProvider.ConfigurationBuilder!
|
||||||
var cfg: TunnelKitProvider.Configuration!
|
var cfg: OpenVPNTunnelProvider.Configuration!
|
||||||
|
|
||||||
let identifier = "com.example.Provider"
|
let identifier = "com.example.Provider"
|
||||||
let appGroup = "group.com.algoritmico.TunnelKit"
|
let appGroup = "group.com.algoritmico.TunnelKit"
|
||||||
|
@ -65,7 +65,7 @@ class AppExtensionTests: XCTestCase {
|
||||||
sessionBuilder.digest = .sha256
|
sessionBuilder.digest = .sha256
|
||||||
sessionBuilder.hostname = hostname
|
sessionBuilder.hostname = hostname
|
||||||
sessionBuilder.endpointProtocols = []
|
sessionBuilder.endpointProtocols = []
|
||||||
builder = TunnelKitProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build())
|
builder = OpenVPNTunnelProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build())
|
||||||
XCTAssertNotNil(builder)
|
XCTAssertNotNil(builder)
|
||||||
|
|
||||||
cfg = builder.build()
|
cfg = builder.build()
|
||||||
|
@ -82,7 +82,7 @@ class AppExtensionTests: XCTestCase {
|
||||||
print("\(pc)")
|
print("\(pc)")
|
||||||
}
|
}
|
||||||
|
|
||||||
let K = TunnelKitProvider.Configuration.Keys.self
|
let K = OpenVPNTunnelProvider.Configuration.Keys.self
|
||||||
XCTAssertEqual(proto?.providerConfiguration?[K.appGroup] as? String, appGroup)
|
XCTAssertEqual(proto?.providerConfiguration?[K.appGroup] as? String, appGroup)
|
||||||
XCTAssertEqual(proto?.providerConfiguration?[K.cipherAlgorithm] as? String, cfg.sessionConfiguration.cipher?.rawValue)
|
XCTAssertEqual(proto?.providerConfiguration?[K.cipherAlgorithm] as? String, cfg.sessionConfiguration.cipher?.rawValue)
|
||||||
XCTAssertEqual(proto?.providerConfiguration?[K.digestAlgorithm] as? String, cfg.sessionConfiguration.digest?.rawValue)
|
XCTAssertEqual(proto?.providerConfiguration?[K.digestAlgorithm] as? String, cfg.sessionConfiguration.digest?.rawValue)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue