diff --git a/CHANGELOG.md b/CHANGELOG.md index 3987c71..c53f20d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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/), 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) ### Added diff --git a/Demo/Podfile.lock b/Demo/Podfile.lock index 83316e9..1c857f6 100644 --- a/Demo/Podfile.lock +++ b/Demo/Podfile.lock @@ -1,17 +1,17 @@ PODS: - - OpenSSL-Apple (1.1.1h.8) + - OpenSSL-Apple (1.1.1h.10) - SwiftyBeaver (1.9.3) - - TunnelKit (3.1.0): - - TunnelKit/Protocols/OpenVPN (= 3.1.0) - - TunnelKit/AppExtension (3.1.0): + - TunnelKit (3.2.0): + - TunnelKit/Protocols/OpenVPN (= 3.2.0) + - TunnelKit/AppExtension (3.2.0): - SwiftyBeaver - TunnelKit/Core - - TunnelKit/Core (3.1.0): + - TunnelKit/Core (3.2.0): - SwiftyBeaver - - TunnelKit/Manager (3.1.0): + - TunnelKit/Manager (3.2.0): - SwiftyBeaver - - TunnelKit/Protocols/OpenVPN (3.1.0): - - OpenSSL-Apple (~> 1.1.1h.8) + - TunnelKit/Protocols/OpenVPN (3.2.0): + - OpenSSL-Apple (~> 1.1.1h.10) - TunnelKit/AppExtension - TunnelKit/Core - TunnelKit/Manager @@ -30,9 +30,9 @@ EXTERNAL SOURCES: :path: ".." SPEC CHECKSUMS: - OpenSSL-Apple: 70990157548ecf94885310231aff52db698e1077 + OpenSSL-Apple: 8a8fcb06fb66f9c2f7aed45ce363668493b8e5f6 SwiftyBeaver: 2e8acd6fc90c6d0a27055867a290794926d57c02 - TunnelKit: 4db9180956f8aaf4ab152fd0d38c6c9c63a46cf8 + TunnelKit: b9ea352cbcce641f98687109c2e7d8cb1fa40e19 PODFILE CHECKSUM: 518aaea9a529c96ba3024918bc0850dd6e92ac61 diff --git a/README.md b/README.md index 0d87878..f85aad1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # TunnelKit ![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) [![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) @@ -52,8 +53,8 @@ Unsupported: Ignored: -- MTU overrides - - `--*-mtu` and variants +- Some MTU overrides + - `--link-mtu` and variants - `--mssfix` - Multiple `--remote` with different `host` values (first wins) - Static client-side routes diff --git a/TunnelKit.podspec b/TunnelKit.podspec index 07e6d6d..24c3c92 100644 --- a/TunnelKit.podspec +++ b/TunnelKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "TunnelKit" - s.version = "3.1.0" + s.version = "3.2.0" s.summary = "Non-official OpenVPN client for Apple platforms." s.homepage = "https://github.com/passepartoutvpn/tunnelkit" diff --git a/TunnelKit.xcodeproj/project.pbxproj b/TunnelKit.xcodeproj/project.pbxproj index 6706f84..c38662c 100644 --- a/TunnelKit.xcodeproj/project.pbxproj +++ b/TunnelKit.xcodeproj/project.pbxproj @@ -750,12 +750,12 @@ 0E23B3F722982AF800304C30 /* AppExtension */ = { isa = PBXGroup; children = ( - 0E23B3F822982AF800304C30 /* OpenVPNTunnelProvider+Interaction.swift */, - 0E23B3F922982AF800304C30 /* NEUDPLink.swift */, 0E23B3FA22982AF800304C30 /* ConnectionStrategy.swift */, - 0E23B3FB22982AF800304C30 /* OpenVPNTunnelProvider+Configuration.swift */, 0E23B3FC22982AF800304C30 /* NETCPLink.swift */, + 0E23B3F922982AF800304C30 /* NEUDPLink.swift */, 0E23B3FD22982AF800304C30 /* OpenVPNTunnelProvider.swift */, + 0E23B3FB22982AF800304C30 /* OpenVPNTunnelProvider+Configuration.swift */, + 0E23B3F822982AF800304C30 /* OpenVPNTunnelProvider+Interaction.swift */, ); path = AppExtension; sourceTree = ""; @@ -1781,7 +1781,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 766; + CURRENT_PROJECT_VERSION = 825; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -1846,7 +1846,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 766; + CURRENT_PROJECT_VERSION = 825; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -1879,7 +1879,7 @@ DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 766; + DYLIB_CURRENT_VERSION = 825; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = "$(SRCROOT)/TunnelKit-iOS/Info.plist"; @@ -1904,7 +1904,7 @@ DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 766; + DYLIB_CURRENT_VERSION = 825; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = "$(SRCROOT)/TunnelKit-iOS/Info.plist"; @@ -1929,7 +1929,7 @@ COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 766; + DYLIB_CURRENT_VERSION = 825; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_VERSION = A; INFOPLIST_FILE = "$(SRCROOT)/TunnelKit-macOS/Info.plist"; @@ -1953,7 +1953,7 @@ COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 766; + DYLIB_CURRENT_VERSION = 825; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_VERSION = A; INFOPLIST_FILE = "$(SRCROOT)/TunnelKit-macOS/Info.plist"; diff --git a/TunnelKit/Sources/Core/Utils.swift b/TunnelKit/Sources/Core/Utils.swift index 2250425..49d4b7a 100644 --- a/TunnelKit/Sources/Core/Utils.swift +++ b/TunnelKit/Sources/Core/Utils.swift @@ -42,3 +42,20 @@ public extension DispatchQueue { asyncAfter(deadline: .now() + after, execute: block) } } + +/// :nodoc: +func fromDictionary(_ 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 + } +} diff --git a/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider+Configuration.swift b/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider+Configuration.swift index ceb7130..29acc8b 100644 --- a/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider+Configuration.swift +++ b/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider+Configuration.swift @@ -41,6 +41,9 @@ import SwiftyBeaver private let log = SwiftyBeaver.self extension OpenVPNTunnelProvider { + private struct ExtraKeys { + static let appGroup = "appGroup" + } // MARK: Configuration @@ -100,24 +103,6 @@ extension OpenVPNTunnelProvider { 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. @@ -138,79 +123,6 @@ extension OpenVPNTunnelProvider { /// Offers a bridge between the abstract `OpenVPNTunnelProvider.ConfigurationBuilder` and a concrete `NETunnelProviderProtocol` profile. 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` public let sessionConfiguration: OpenVPN.Configuration @@ -318,8 +230,8 @@ extension OpenVPNTunnelProvider { - Throws: `ProviderError.configuration` if `providerConfiguration` does not contain an app group. */ public static func appGroup(from providerConfiguration: [String: Any]) throws -> String { - guard let appGroup = providerConfiguration[Keys.appGroup] as? String else { - throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[\(Keys.appGroup)]") + guard let appGroup = providerConfiguration[ExtraKeys.appGroup] as? String else { + throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[\(ExtraKeys.appGroup)]") } return appGroup } @@ -332,10 +244,13 @@ extension OpenVPNTunnelProvider { - Throws: `ProviderError.configuration` if `providerConfiguration` is incomplete. */ public static func parsed(from providerConfiguration: [String: Any]) throws -> Configuration { - let builder = try ConfigurationBuilder(providerConfiguration: providerConfiguration) - return builder.build() + let cfg = try fromDictionary(OpenVPNTunnelProvider.Configuration.self, providerConfiguration) + guard !cfg.prefersResolvedAddresses || !(cfg.resolvedAddresses?.isEmpty ?? true) else { + throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[prefersResolvedAddresses] is true but no [resolvedAddresses]") + } + return cfg } - + /** Returns a dictionary representation of this configuration for use with `NETunnelProviderProtocol.providerConfiguration`. @@ -343,36 +258,14 @@ extension OpenVPNTunnelProvider { - Returns: The dictionary representation of `self`. */ public func generatedProviderConfiguration(appGroup: String) -> [String: Any] { - let S = Keys.self - - guard let ca = sessionConfiguration.ca else { - fatalError("No sessionConfiguration.ca set") + do { + var dict = try asDictionary() + dict[ExtraKeys.appGroup] = appGroup + return dict + } catch let e { + log.error("Unable to encode OpenVPN.Configuration: \(e)") } - 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 [:] } /** @@ -451,284 +344,3 @@ public extension UserDefaults { 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") - } - } -} diff --git a/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift b/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift index 43de035..3b94188 100644 --- a/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift +++ b/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift @@ -35,6 +35,9 @@ // import Foundation +import SwiftyBeaver + +private let log = SwiftyBeaver.self extension OpenVPN { @@ -506,3 +509,95 @@ extension OpenVPN.Configuration { 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") + } + } +} diff --git a/TunnelKitHost/Info.plist b/TunnelKitHost/Info.plist index f062a9a..fb8a027 100644 --- a/TunnelKitHost/Info.plist +++ b/TunnelKitHost/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 3.1.0 + 3.2.0 CFBundleVersion 1 LSRequiresIPhoneOS diff --git a/TunnelKitTests-iOS/Info.plist b/TunnelKitTests-iOS/Info.plist index 43dc03c..d9fff86 100644 --- a/TunnelKitTests-iOS/Info.plist +++ b/TunnelKitTests-iOS/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 3.1.0 + 3.2.0 CFBundleVersion 1 diff --git a/TunnelKitTests-macOS/Info.plist b/TunnelKitTests-macOS/Info.plist index 43dc03c..d9fff86 100644 --- a/TunnelKitTests-macOS/Info.plist +++ b/TunnelKitTests-macOS/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 3.1.0 + 3.2.0 CFBundleVersion 1 diff --git a/TunnelKitTests/OpenVPN/AppExtensionTests.swift b/TunnelKitTests/OpenVPN/AppExtensionTests.swift index 861c6e9..a6e6f95 100644 --- a/TunnelKitTests/OpenVPN/AppExtensionTests.swift +++ b/TunnelKitTests/OpenVPN/AppExtensionTests.swift @@ -79,18 +79,19 @@ class AppExtensionTests: XCTestCase { XCTAssertEqual(proto?.username, credentials.username) XCTAssertEqual(proto?.passwordReference, try? Keychain(group: appGroup).passwordReference(for: credentials.username)) - if let pc = proto?.providerConfiguration { - print("\(pc)") + guard let pc = proto?.providerConfiguration else { + return } - - let K = OpenVPNTunnelProvider.Configuration.Keys.self - XCTAssertEqual(proto?.providerConfiguration?[K.appGroup] as? String, appGroup) - 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.ca] as? String, cfg.sessionConfiguration.ca?.pem) - XCTAssertEqual(proto?.providerConfiguration?[K.mtu] as? Int, cfg.sessionConfiguration.mtu) - XCTAssertEqual(proto?.providerConfiguration?[K.renegotiatesAfter] as? TimeInterval, cfg.sessionConfiguration.renegotiatesAfter) - XCTAssertEqual(proto?.providerConfiguration?[K.debug] as? Bool, cfg.shouldDebug) + print("\(pc)") + + let pcSession = pc["sessionConfiguration"] as? [String: Any] + XCTAssertEqual(pc["appGroup"] as? String, appGroup) + XCTAssertEqual(pc["shouldDebug"] as? Bool, cfg.shouldDebug) + XCTAssertEqual(pcSession?["cipher"] as? String, cfg.sessionConfiguration.cipher?.rawValue) + XCTAssertEqual(pcSession?["digest"] as? String, cfg.sessionConfiguration.digest?.rawValue) + XCTAssertEqual(pcSession?["ca"] as? String, cfg.sessionConfiguration.ca?.pem) + XCTAssertEqual(pcSession?["mtu"] as? Int, cfg.sessionConfiguration.mtu) + XCTAssertEqual(pcSession?["renegotiatesAfter"] as? TimeInterval, cfg.sessionConfiguration.renegotiatesAfter) } func testDNSResolver() {