Merge pull request #194 from passepartoutvpn/refactor-provider-configuration

Refactor internal provider configuration
This commit is contained in:
Davide De Rosa 2021-01-03 19:47:13 +01:00 committed by GitHub
commit 1bf6c9084a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 174 additions and 442 deletions

View File

@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Changed
- Encoding of internal provider configuration.
## 3.1.0 (2020-12-28) ## 3.1.0 (2020-12-28)
### Added ### Added

View File

@ -1,17 +1,17 @@
PODS: PODS:
- OpenSSL-Apple (1.1.1h.8) - OpenSSL-Apple (1.1.1h.10)
- SwiftyBeaver (1.9.3) - SwiftyBeaver (1.9.3)
- TunnelKit (3.1.0): - TunnelKit (3.2.0):
- TunnelKit/Protocols/OpenVPN (= 3.1.0) - TunnelKit/Protocols/OpenVPN (= 3.2.0)
- TunnelKit/AppExtension (3.1.0): - TunnelKit/AppExtension (3.2.0):
- SwiftyBeaver - SwiftyBeaver
- TunnelKit/Core - TunnelKit/Core
- TunnelKit/Core (3.1.0): - TunnelKit/Core (3.2.0):
- SwiftyBeaver - SwiftyBeaver
- TunnelKit/Manager (3.1.0): - TunnelKit/Manager (3.2.0):
- SwiftyBeaver - SwiftyBeaver
- TunnelKit/Protocols/OpenVPN (3.1.0): - TunnelKit/Protocols/OpenVPN (3.2.0):
- OpenSSL-Apple (~> 1.1.1h.8) - OpenSSL-Apple (~> 1.1.1h.10)
- TunnelKit/AppExtension - TunnelKit/AppExtension
- TunnelKit/Core - TunnelKit/Core
- TunnelKit/Manager - TunnelKit/Manager
@ -30,9 +30,9 @@ EXTERNAL SOURCES:
:path: ".." :path: ".."
SPEC CHECKSUMS: SPEC CHECKSUMS:
OpenSSL-Apple: 70990157548ecf94885310231aff52db698e1077 OpenSSL-Apple: 8a8fcb06fb66f9c2f7aed45ce363668493b8e5f6
SwiftyBeaver: 2e8acd6fc90c6d0a27055867a290794926d57c02 SwiftyBeaver: 2e8acd6fc90c6d0a27055867a290794926d57c02
TunnelKit: 4db9180956f8aaf4ab152fd0d38c6c9c63a46cf8 TunnelKit: b9ea352cbcce641f98687109c2e7d8cb1fa40e19
PODFILE CHECKSUM: 518aaea9a529c96ba3024918bc0850dd6e92ac61 PODFILE CHECKSUM: 518aaea9a529c96ba3024918bc0850dd6e92ac61

View File

@ -1,6 +1,7 @@
# TunnelKit # TunnelKit
![iOS 12+](https://img.shields.io/badge/ios-12+-green.svg) ![iOS 12+](https://img.shields.io/badge/ios-12+-green.svg)
![macOS 10.15+](https://img.shields.io/badge/macos-10.15+-green.svg)
[![OpenSSL 1.1.1h](https://img.shields.io/badge/openssl-1.1.1h-d69c68.svg)](https://www.openssl.org/news/openssl-1.1.1-notes.html) [![OpenSSL 1.1.1h](https://img.shields.io/badge/openssl-1.1.1h-d69c68.svg)](https://www.openssl.org/news/openssl-1.1.1-notes.html)
[![License GPLv3](https://img.shields.io/badge/license-GPLv3-lightgray.svg)](LICENSE) [![License GPLv3](https://img.shields.io/badge/license-GPLv3-lightgray.svg)](LICENSE)
[![Travis-CI](https://api.travis-ci.org/passepartoutvpn/tunnelkit.svg?branch=master)](https://travis-ci.org/passepartoutvpn/tunnelkit) [![Travis-CI](https://api.travis-ci.org/passepartoutvpn/tunnelkit.svg?branch=master)](https://travis-ci.org/passepartoutvpn/tunnelkit)
@ -52,8 +53,8 @@ Unsupported:
Ignored: Ignored:
- MTU overrides - Some MTU overrides
- `--*-mtu` and variants - `--link-mtu` and variants
- `--mssfix` - `--mssfix`
- Multiple `--remote` with different `host` values (first wins) - Multiple `--remote` with different `host` values (first wins)
- Static client-side routes - Static client-side routes

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = "TunnelKit" s.name = "TunnelKit"
s.version = "3.1.0" s.version = "3.2.0"
s.summary = "Non-official OpenVPN client for Apple platforms." s.summary = "Non-official OpenVPN client for Apple platforms."
s.homepage = "https://github.com/passepartoutvpn/tunnelkit" s.homepage = "https://github.com/passepartoutvpn/tunnelkit"

View File

@ -750,12 +750,12 @@
0E23B3F722982AF800304C30 /* AppExtension */ = { 0E23B3F722982AF800304C30 /* AppExtension */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
0E23B3F822982AF800304C30 /* OpenVPNTunnelProvider+Interaction.swift */,
0E23B3F922982AF800304C30 /* NEUDPLink.swift */,
0E23B3FA22982AF800304C30 /* ConnectionStrategy.swift */, 0E23B3FA22982AF800304C30 /* ConnectionStrategy.swift */,
0E23B3FB22982AF800304C30 /* OpenVPNTunnelProvider+Configuration.swift */,
0E23B3FC22982AF800304C30 /* NETCPLink.swift */, 0E23B3FC22982AF800304C30 /* NETCPLink.swift */,
0E23B3F922982AF800304C30 /* NEUDPLink.swift */,
0E23B3FD22982AF800304C30 /* OpenVPNTunnelProvider.swift */, 0E23B3FD22982AF800304C30 /* OpenVPNTunnelProvider.swift */,
0E23B3FB22982AF800304C30 /* OpenVPNTunnelProvider+Configuration.swift */,
0E23B3F822982AF800304C30 /* OpenVPNTunnelProvider+Interaction.swift */,
); );
path = AppExtension; path = AppExtension;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1781,7 +1781,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 766; CURRENT_PROJECT_VERSION = 825;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES; ENABLE_TESTABILITY = YES;
@ -1846,7 +1846,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 766; CURRENT_PROJECT_VERSION = 825;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
@ -1879,7 +1879,7 @@
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 766; DYLIB_CURRENT_VERSION = 825;
DYLIB_INSTALL_NAME_BASE = "@rpath"; DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = "$(inherited)"; FRAMEWORK_SEARCH_PATHS = "$(inherited)";
INFOPLIST_FILE = "$(SRCROOT)/TunnelKit-iOS/Info.plist"; INFOPLIST_FILE = "$(SRCROOT)/TunnelKit-iOS/Info.plist";
@ -1904,7 +1904,7 @@
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 766; DYLIB_CURRENT_VERSION = 825;
DYLIB_INSTALL_NAME_BASE = "@rpath"; DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = "$(inherited)"; FRAMEWORK_SEARCH_PATHS = "$(inherited)";
INFOPLIST_FILE = "$(SRCROOT)/TunnelKit-iOS/Info.plist"; INFOPLIST_FILE = "$(SRCROOT)/TunnelKit-iOS/Info.plist";
@ -1929,7 +1929,7 @@
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 766; DYLIB_CURRENT_VERSION = 825;
DYLIB_INSTALL_NAME_BASE = "@rpath"; DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_VERSION = A; FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "$(SRCROOT)/TunnelKit-macOS/Info.plist"; INFOPLIST_FILE = "$(SRCROOT)/TunnelKit-macOS/Info.plist";
@ -1953,7 +1953,7 @@
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 766; DYLIB_CURRENT_VERSION = 825;
DYLIB_INSTALL_NAME_BASE = "@rpath"; DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_VERSION = A; FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "$(SRCROOT)/TunnelKit-macOS/Info.plist"; INFOPLIST_FILE = "$(SRCROOT)/TunnelKit-macOS/Info.plist";

View File

@ -42,3 +42,20 @@ public extension DispatchQueue {
asyncAfter(deadline: .now() + after, execute: block) asyncAfter(deadline: .now() + after, execute: block)
} }
} }
/// :nodoc:
func fromDictionary<T: Decodable>(_ type: T.Type, _ dictionary: [String: Any]) throws -> T {
let data = try JSONSerialization.data(withJSONObject: dictionary, options: .fragmentsAllowed)
return try JSONDecoder().decode(T.self, from: data)
}
/// :nodoc:
public extension Encodable {
func asDictionary() throws -> [String: Any] {
let data = try JSONEncoder().encode(self)
guard let dictionary = try JSONSerialization.jsonObject(with: data, options: .fragmentsAllowed) as? [String: Any] else {
fatalError("JSONSerialization failed to encode")
}
return dictionary
}
}

View File

@ -41,6 +41,9 @@ import SwiftyBeaver
private let log = SwiftyBeaver.self private let log = SwiftyBeaver.self
extension OpenVPNTunnelProvider { extension OpenVPNTunnelProvider {
private struct ExtraKeys {
static let appGroup = "appGroup"
}
// MARK: Configuration // MARK: Configuration
@ -100,24 +103,6 @@ extension OpenVPNTunnelProvider {
versionIdentifier = ConfigurationBuilder.defaults.versionIdentifier versionIdentifier = ConfigurationBuilder.defaults.versionIdentifier
} }
fileprivate init(providerConfiguration: [String: Any]) throws {
let S = Configuration.Keys.self
sessionConfiguration = try OpenVPN.Configuration.with(providerConfiguration: providerConfiguration)
prefersResolvedAddresses = providerConfiguration[S.prefersResolvedAddresses] as? Bool ?? ConfigurationBuilder.defaults.prefersResolvedAddresses
resolvedAddresses = providerConfiguration[S.resolvedAddresses] as? [String]
shouldDebug = providerConfiguration[S.debug] as? Bool ?? ConfigurationBuilder.defaults.shouldDebug
if shouldDebug {
debugLogFormat = providerConfiguration[S.debugLogFormat] as? String
}
masksPrivateData = providerConfiguration[S.masksPrivateData] as? Bool ?? ConfigurationBuilder.defaults.masksPrivateData
versionIdentifier = providerConfiguration[S.versionIdentifier] as? String ?? ConfigurationBuilder.defaults.versionIdentifier
guard !prefersResolvedAddresses || !(resolvedAddresses?.isEmpty ?? true) else {
throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[\(S.prefersResolvedAddresses)] is true but no [\(S.resolvedAddresses)]")
}
}
/** /**
Builds a `OpenVPNTunnelProvider.Configuration` object that will connect to the provided endpoint. Builds a `OpenVPNTunnelProvider.Configuration` object that will connect to the provided endpoint.
@ -138,79 +123,6 @@ extension OpenVPNTunnelProvider {
/// Offers a bridge between the abstract `OpenVPNTunnelProvider.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 {
static let appGroup = "AppGroup"
static let versionIdentifier = "VersionIdentifier"
// MARK: SessionConfiguration
static let cipherAlgorithm = "CipherAlgorithm"
static let digestAlgorithm = "DigestAlgorithm"
static let compressionFraming = "CompressionFraming"
static let compressionAlgorithm = "CompressionAlgorithm"
static let ca = "CA"
static let clientCertificate = "ClientCertificate"
static let clientKey = "ClientKey"
static let tlsWrap = "TLSWrap"
static let tlsSecurityLevel = "TLSSecurityLevel"
static let keepAlive = "KeepAlive"
static let keepAliveTimeout = "KeepAliveTimeout"
static let endpointProtocols = "EndpointProtocols"
static let renegotiatesAfter = "RenegotiatesAfter"
static let checksEKU = "ChecksEKU"
static let checksSANHost = "checksSANHost"
static let sanHost = "sanHost"
static let randomizeEndpoint = "RandomizeEndpoint"
static let usesPIAPatches = "UsesPIAPatches"
static let mtu = "MTU"
static let dnsServers = "DNSServers"
static let searchDomains = "SearchDomains"
static let httpProxy = "HTTPProxy"
static let httpsProxy = "HTTPSProxy"
static let proxyAutoConfigurationURL = "ProxyAutoConfigurationURL"
static let proxyBypassDomains = "ProxyBypassDomains"
static let routingPolicies = "RoutingPolicies"
// MARK: Customization
static let prefersResolvedAddresses = "PrefersResolvedAddresses"
static let resolvedAddresses = "ResolvedAddresses"
// MARK: Debugging
static let debug = "Debug"
static let debugLogFormat = "DebugLogFormat"
static let masksPrivateData = "MasksPrivateData"
}
/// - Seealso: `OpenVPNTunnelProvider.ConfigurationBuilder.sessionConfiguration` /// - Seealso: `OpenVPNTunnelProvider.ConfigurationBuilder.sessionConfiguration`
public let sessionConfiguration: OpenVPN.Configuration public let sessionConfiguration: OpenVPN.Configuration
@ -318,8 +230,8 @@ extension OpenVPNTunnelProvider {
- Throws: `ProviderError.configuration` if `providerConfiguration` does not contain an app group. - Throws: `ProviderError.configuration` if `providerConfiguration` does not contain an app group.
*/ */
public static func appGroup(from providerConfiguration: [String: Any]) throws -> String { public static func appGroup(from providerConfiguration: [String: Any]) throws -> String {
guard let appGroup = providerConfiguration[Keys.appGroup] as? String else { guard let appGroup = providerConfiguration[ExtraKeys.appGroup] as? String else {
throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[\(Keys.appGroup)]") throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[\(ExtraKeys.appGroup)]")
} }
return appGroup return appGroup
} }
@ -332,8 +244,11 @@ extension OpenVPNTunnelProvider {
- 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 {
let builder = try ConfigurationBuilder(providerConfiguration: providerConfiguration) let cfg = try fromDictionary(OpenVPNTunnelProvider.Configuration.self, providerConfiguration)
return builder.build() guard !cfg.prefersResolvedAddresses || !(cfg.resolvedAddresses?.isEmpty ?? true) else {
throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[prefersResolvedAddresses] is true but no [resolvedAddresses]")
}
return cfg
} }
/** /**
@ -343,36 +258,14 @@ extension OpenVPNTunnelProvider {
- Returns: The dictionary representation of `self`. - Returns: The dictionary representation of `self`.
*/ */
public func generatedProviderConfiguration(appGroup: String) -> [String: Any] { public func generatedProviderConfiguration(appGroup: String) -> [String: Any] {
let S = Keys.self do {
var dict = try asDictionary()
guard let ca = sessionConfiguration.ca else { dict[ExtraKeys.appGroup] = appGroup
fatalError("No sessionConfiguration.ca set")
}
guard let endpointProtocols = sessionConfiguration.endpointProtocols else {
fatalError("No sessionConfiguration.endpointProtocols set")
}
var dict: [String: Any] = [
S.appGroup: appGroup,
S.prefersResolvedAddresses: prefersResolvedAddresses,
S.ca: ca.pem,
S.endpointProtocols: endpointProtocols.map { $0.rawValue },
S.debug: shouldDebug
]
sessionConfiguration.store(to: &dict)
if let resolvedAddresses = resolvedAddresses {
dict[S.resolvedAddresses] = resolvedAddresses
}
if let debugLogFormat = debugLogFormat {
dict[S.debugLogFormat] = debugLogFormat
}
if let masksPrivateData = masksPrivateData {
dict[S.masksPrivateData] = masksPrivateData
}
if let versionIdentifier = versionIdentifier {
dict[S.versionIdentifier] = versionIdentifier
}
return dict return dict
} catch let e {
log.error("Unable to encode OpenVPN.Configuration: \(e)")
}
return [:]
} }
/** /**
@ -451,284 +344,3 @@ public extension UserDefaults {
removeObject(forKey: OpenVPNTunnelProvider.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 keepAliveTimeout = providerConfiguration[S.keepAliveTimeout] as? TimeInterval {
builder.keepAliveTimeout = keepAliveTimeout
}
if let renegotiatesAfter = providerConfiguration[S.renegotiatesAfter] as? TimeInterval {
builder.renegotiatesAfter = renegotiatesAfter
}
if let checksEKU = providerConfiguration[S.checksEKU] as? Bool {
builder.checksEKU = checksEKU
}
if let checksSANHost = providerConfiguration[S.checksSANHost] as? Bool {
builder.checksSANHost = checksSANHost
}
if let sanHost = providerConfiguration[S.sanHost] as? String {
builder.sanHost = sanHost
}
if let randomizeEndpoint = providerConfiguration[S.randomizeEndpoint] as? Bool {
builder.randomizeEndpoint = randomizeEndpoint
}
if let usesPIAPatches = providerConfiguration[S.usesPIAPatches] as? Bool {
builder.usesPIAPatches = usesPIAPatches
}
if let mtu = providerConfiguration[S.mtu] as? Int {
builder.mtu = mtu
}
if let dnsServers = providerConfiguration[S.dnsServers] as? [String] {
builder.dnsServers = dnsServers
}
if let searchDomains = providerConfiguration[S.searchDomains] as? [String] {
builder.searchDomains = searchDomains
}
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 proxyAutoConfigurationURLString = providerConfiguration[S.proxyAutoConfigurationURL] as? String, let proxyAutoConfigurationURL = URL(string: proxyAutoConfigurationURLString) {
builder.proxyAutoConfigurationURL = proxyAutoConfigurationURL
}
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 keepAliveTimeoutSeconds = keepAliveTimeout {
dict[S.keepAliveTimeout] = keepAliveTimeoutSeconds
}
if let renegotiatesAfterSeconds = renegotiatesAfter {
dict[S.renegotiatesAfter] = renegotiatesAfterSeconds
}
if let checksEKU = checksEKU {
dict[S.checksEKU] = checksEKU
}
if let checksSANHost = checksSANHost {
dict[S.checksSANHost] = checksSANHost
}
if let sanHost = sanHost {
dict[S.sanHost] = sanHost
}
if let randomizeEndpoint = randomizeEndpoint {
dict[S.randomizeEndpoint] = randomizeEndpoint
}
if let usesPIAPatches = usesPIAPatches {
dict[S.usesPIAPatches] = usesPIAPatches
}
if let mtu = mtu {
dict[S.mtu] = mtu
}
if let dnsServers = dnsServers {
dict[S.dnsServers] = dnsServers
}
if let searchDomains = searchDomains {
dict[S.searchDomains] = searchDomains
}
if let httpProxy = httpProxy {
dict[S.httpProxy] = httpProxy.rawValue
}
if let httpsProxy = httpsProxy {
dict[S.httpsProxy] = httpsProxy.rawValue
}
if let proxyAutoConfigurationURL = proxyAutoConfigurationURL {
dict[S.proxyAutoConfigurationURL] = proxyAutoConfigurationURL.absoluteString
}
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 interval: \(keepAliveSeconds) seconds")
} else {
log.info("\tKeep-alive interval: never")
}
if let keepAliveTimeoutSeconds = keepAliveTimeout, keepAliveTimeoutSeconds > 0 {
log.info("\tKeep-alive timeout: \(keepAliveTimeoutSeconds) seconds")
} else {
log.info("\tKeep-alive timeout: 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 checksSANHost ?? false {
log.info("\tHost SAN verification: enabled (\(sanHost ?? "-"))")
} else {
log.info("\tHost SAN 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 searchDomains = searchDomains, !searchDomains.isEmpty {
log.info("\tSearch domains: \(searchDomains.maskedDescription)")
}
if let httpProxy = httpProxy {
log.info("\tHTTP proxy: \(httpProxy.maskedDescription)")
}
if let httpsProxy = httpsProxy {
log.info("\tHTTPS proxy: \(httpsProxy.maskedDescription)")
}
if let proxyAutoConfigurationURL = proxyAutoConfigurationURL {
log.info("\tPAC: \(proxyAutoConfigurationURL)")
}
if let proxyBypassDomains = proxyBypassDomains {
log.info("\tProxy bypass domains: \(proxyBypassDomains.maskedDescription)")
}
if let mtu = mtu {
log.info("\tMTU: \(mtu)")
} else {
log.info("\tMTU: default")
}
}
}

View File

@ -35,6 +35,9 @@
// //
import Foundation import Foundation
import SwiftyBeaver
private let log = SwiftyBeaver.self
extension OpenVPN { extension OpenVPN {
@ -506,3 +509,95 @@ extension OpenVPN.Configuration {
return builder return builder
} }
} }
// MARK: Encoding
extension OpenVPN.Configuration {
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 interval: \(keepAliveSeconds) seconds")
} else {
log.info("\tKeep-alive interval: never")
}
if let keepAliveTimeoutSeconds = keepAliveTimeout, keepAliveTimeoutSeconds > 0 {
log.info("\tKeep-alive timeout: \(keepAliveTimeoutSeconds) seconds")
} else {
log.info("\tKeep-alive timeout: 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 checksSANHost ?? false {
log.info("\tHost SAN verification: enabled (\(sanHost ?? "-"))")
} else {
log.info("\tHost SAN 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 searchDomains = searchDomains, !searchDomains.isEmpty {
log.info("\tSearch domains: \(searchDomains.maskedDescription)")
}
if let httpProxy = httpProxy {
log.info("\tHTTP proxy: \(httpProxy.maskedDescription)")
}
if let httpsProxy = httpsProxy {
log.info("\tHTTPS proxy: \(httpsProxy.maskedDescription)")
}
if let proxyAutoConfigurationURL = proxyAutoConfigurationURL {
log.info("\tPAC: \(proxyAutoConfigurationURL)")
}
if let proxyBypassDomains = proxyBypassDomains {
log.info("\tProxy bypass domains: \(proxyBypassDomains.maskedDescription)")
}
if let mtu = mtu {
log.info("\tMTU: \(mtu)")
} else {
log.info("\tMTU: default")
}
}
}

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>3.1.0</string> <string>3.2.0</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>1</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>BNDL</string> <string>BNDL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>3.1.0</string> <string>3.2.0</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>1</string>
</dict> </dict>

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>BNDL</string> <string>BNDL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>3.1.0</string> <string>3.2.0</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>1</string>
</dict> </dict>

View File

@ -79,18 +79,19 @@ class AppExtensionTests: XCTestCase {
XCTAssertEqual(proto?.username, credentials.username) XCTAssertEqual(proto?.username, credentials.username)
XCTAssertEqual(proto?.passwordReference, try? Keychain(group: appGroup).passwordReference(for: credentials.username)) XCTAssertEqual(proto?.passwordReference, try? Keychain(group: appGroup).passwordReference(for: credentials.username))
if let pc = proto?.providerConfiguration { guard let pc = proto?.providerConfiguration else {
print("\(pc)") return
} }
print("\(pc)")
let K = OpenVPNTunnelProvider.Configuration.Keys.self let pcSession = pc["sessionConfiguration"] as? [String: Any]
XCTAssertEqual(proto?.providerConfiguration?[K.appGroup] as? String, appGroup) XCTAssertEqual(pc["appGroup"] as? String, appGroup)
XCTAssertEqual(proto?.providerConfiguration?[K.cipherAlgorithm] as? String, cfg.sessionConfiguration.cipher?.rawValue) XCTAssertEqual(pc["shouldDebug"] as? Bool, cfg.shouldDebug)
XCTAssertEqual(proto?.providerConfiguration?[K.digestAlgorithm] as? String, cfg.sessionConfiguration.digest?.rawValue) XCTAssertEqual(pcSession?["cipher"] as? String, cfg.sessionConfiguration.cipher?.rawValue)
XCTAssertEqual(proto?.providerConfiguration?[K.ca] as? String, cfg.sessionConfiguration.ca?.pem) XCTAssertEqual(pcSession?["digest"] as? String, cfg.sessionConfiguration.digest?.rawValue)
XCTAssertEqual(proto?.providerConfiguration?[K.mtu] as? Int, cfg.sessionConfiguration.mtu) XCTAssertEqual(pcSession?["ca"] as? String, cfg.sessionConfiguration.ca?.pem)
XCTAssertEqual(proto?.providerConfiguration?[K.renegotiatesAfter] as? TimeInterval, cfg.sessionConfiguration.renegotiatesAfter) XCTAssertEqual(pcSession?["mtu"] as? Int, cfg.sessionConfiguration.mtu)
XCTAssertEqual(proto?.providerConfiguration?[K.debug] as? Bool, cfg.shouldDebug) XCTAssertEqual(pcSession?["renegotiatesAfter"] as? TimeInterval, cfg.sessionConfiguration.renegotiatesAfter)
} }
func testDNSResolver() { func testDNSResolver() {