Updated NETunnelProvider save format

Signed-off-by: Eric Kuck <eric@bluelinelabs.com>
This commit is contained in:
Eric Kuck
2018-12-20 22:52:45 -06:00
committed by Jason A. Donenfeld
parent 38445114e0
commit 8553723e04
23 changed files with 664 additions and 224 deletions
@@ -3,24 +3,53 @@
import NetworkExtension
let tunnelConfigurationVersion = 2
extension NETunnelProviderProtocol {
enum Keys: String {
case tunnelConfiguration = "TunnelConfiguration"
case tunnelConfigurationVersion = "TunnelConfigurationVersion"
case isActivateOnDemandEnabled = "IsActivateOnDemandEnabled"
}
var tunnelConfiguration: TunnelConfiguration? {
migrateConfigurationIfNeeded()
let tunnelConfigurationData: Data?
if let configurationDictionary = providerConfiguration?[Keys.tunnelConfiguration.rawValue] {
tunnelConfigurationData = try? JSONSerialization.data(withJSONObject: configurationDictionary, options: [])
} else {
tunnelConfigurationData = nil
}
guard tunnelConfigurationData != nil else { return nil }
return try? JSONDecoder().decode(TunnelConfiguration.self, from: tunnelConfigurationData!)
}
var isActivateOnDemandEnabled: Bool {
return providerConfiguration?[Keys.isActivateOnDemandEnabled.rawValue] as? Bool ?? false
}
convenience init?(tunnelConfiguration: TunnelConfiguration, isActivateOnDemandEnabled: Bool) {
assert(!tunnelConfiguration.interface.name.isEmpty)
guard let serializedTunnelConfiguration = try? JSONEncoder().encode(tunnelConfiguration) else { return nil }
guard let tunnelConfigData = try? JSONEncoder().encode(tunnelConfiguration) else { return nil }
guard let tunnelConfigDictionary = try? JSONSerialization.jsonObject(with: tunnelConfigData, options: .allowFragments) else { return nil }
self.init()
let appId = Bundle.main.bundleIdentifier!
providerBundleIdentifier = "\(appId).network-extension"
providerConfiguration = [
"tunnelConfiguration": serializedTunnelConfiguration,
"tunnelConfigurationVersion": 1,
"isActivateOnDemandEnabled": isActivateOnDemandEnabled
Keys.tunnelConfiguration.rawValue: tunnelConfigDictionary,
Keys.tunnelConfigurationVersion.rawValue: tunnelConfigurationVersion,
Keys.isActivateOnDemandEnabled.rawValue: isActivateOnDemandEnabled
]
let endpoints = tunnelConfiguration.peers.compactMap {$0.endpoint}
let endpoints = tunnelConfiguration.peers.compactMap { $0.endpoint }
if endpoints.count == 1 {
serverAddress = endpoints.first!.stringRepresentation()
serverAddress = endpoints[0].stringRepresentation
} else if endpoints.isEmpty {
serverAddress = "Unspecified"
} else {
@@ -29,18 +58,42 @@ extension NETunnelProviderProtocol {
username = tunnelConfiguration.interface.name
}
func tunnelConfiguration() -> TunnelConfiguration? {
guard let serializedTunnelConfiguration = providerConfiguration?["tunnelConfiguration"] as? Data else { return nil }
return try? JSONDecoder().decode(TunnelConfiguration.self, from: serializedTunnelConfiguration)
}
var isActivateOnDemandEnabled: Bool {
return (providerConfiguration?["isActivateOnDemandEnabled"] as? Bool) ?? false
}
func hasTunnelConfiguration(tunnelConfiguration otherTunnelConfiguration: TunnelConfiguration) -> Bool {
guard let serializedThisTunnelConfiguration = providerConfiguration?["tunnelConfiguration"] as? Data else { return false }
guard let serializedThisTunnelConfiguration = try? JSONEncoder().encode(tunnelConfiguration) else { return false }
guard let serializedOtherTunnelConfiguration = try? JSONEncoder().encode(otherTunnelConfiguration) else { return false }
return serializedThisTunnelConfiguration == serializedOtherTunnelConfiguration
}
@discardableResult
func migrateConfigurationIfNeeded() -> Bool {
guard let providerConfiguration = providerConfiguration else { return false }
guard let configurationVersion = providerConfiguration[Keys.tunnelConfigurationVersion.rawValue] as? Int ?? providerConfiguration["tunnelConfigurationVersion"] as? Int else { return false }
if configurationVersion < tunnelConfigurationVersion {
switch configurationVersion {
case 1:
migrateFromConfigurationV1()
default:
fatalError("No migration from configuration version \(configurationVersion) exists.")
}
return true
}
return false
}
private func migrateFromConfigurationV1() {
guard let serializedTunnelConfiguration = providerConfiguration?["tunnelConfiguration"] as? Data else { return }
guard let configuration = try? JSONDecoder().decode(LegacyTunnelConfiguration.self, from: serializedTunnelConfiguration) else { return }
guard let isActivateOnDemandEnabled = providerConfiguration?["isActivateOnDemandEnabled"] as? Bool else { return }
guard let tunnelConfigData = try? JSONEncoder().encode(configuration.migrated) else { return }
guard let tunnelConfigDictionary = try? JSONSerialization.jsonObject(with: tunnelConfigData, options: .allowFragments) else { return }
providerConfiguration = [
Keys.tunnelConfiguration.rawValue: tunnelConfigDictionary,
Keys.tunnelConfigurationVersion.rawValue: tunnelConfigurationVersion,
Keys.isActivateOnDemandEnabled.rawValue: isActivateOnDemandEnabled
]
}
}