Updated NETunnelProvider save format

This commit is contained in:
Eric Kuck 2018-12-20 22:52:45 -06:00 committed by Jason A. Donenfeld
parent cab80f8fc0
commit f39893685f
23 changed files with 664 additions and 224 deletions

View File

@ -1,68 +0,0 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
import Foundation
@available(OSX 10.14, iOS 12.0, *)
final class TunnelConfiguration: Codable {
var interface: InterfaceConfiguration
let peers: [PeerConfiguration]
static let keyLength: Int = 32
init(interface: InterfaceConfiguration, peers: [PeerConfiguration]) {
self.interface = interface
self.peers = peers
let peerPublicKeysArray = peers.map { $0.publicKey }
let peerPublicKeysSet = Set<Data>(peerPublicKeysArray)
if peerPublicKeysArray.count != peerPublicKeysSet.count {
fatalError("Two or more peers cannot have the same public key")
}
}
}
@available(OSX 10.14, iOS 12.0, *)
struct InterfaceConfiguration: Codable {
var name: String
var privateKey: Data
var addresses = [IPAddressRange]()
var listenPort: UInt16?
var mtu: UInt16?
var dns = [DNSServer]()
init(name: String, privateKey: Data) {
self.name = name
self.privateKey = privateKey
if name.isEmpty {
fatalError("Empty name")
}
if privateKey.count != TunnelConfiguration.keyLength {
fatalError("Invalid private key")
}
}
}
@available(OSX 10.14, iOS 12.0, *)
struct PeerConfiguration: Codable {
var publicKey: Data
var preSharedKey: Data? {
didSet(value) {
if let value = value {
if value.count != TunnelConfiguration.keyLength {
fatalError("Invalid preshared key")
}
}
}
}
var allowedIPs = [IPAddressRange]()
var endpoint: Endpoint?
var persistentKeepAlive: UInt16?
init(publicKey: Data) {
self.publicKey = publicKey
if publicKey.count != TunnelConfiguration.keyLength {
fatalError("Invalid public key")
}
}
}

View File

@ -4,14 +4,43 @@
import Foundation
import Network
@available(OSX 10.14, iOS 12.0, *)
struct DNSServer {
let address: IPAddress
init(address: IPAddress) {
self.address = address
}
}
// MARK: Converting to and from String
extension DNSServer: Codable {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(stringRepresentation)
}
public init(from decoder: Decoder) throws {
let values = try decoder.singleValueContainer()
let addressString = try values.decode(String.self)
if let address = IPv4Address(addressString) {
self.address = address
} else if let address = IPv6Address(addressString) {
self.address = address
} else {
throw DecodingError.invalidData
}
}
enum DecodingError: Error {
case invalidData
}
}
extension DNSServer {
var stringRepresentation: String {
return "\(address)"
}
init?(from addressString: String) {
if let addr = IPv4Address(addressString) {
address = addr
@ -21,35 +50,4 @@ extension DNSServer {
return nil
}
}
func stringRepresentation() -> String {
return "\(address)"
}
}
// MARK: Codable
@available(OSX 10.14, iOS 12.0, *)
extension DNSServer: Codable {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(address.rawValue)
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
var data = try container.decode(Data.self)
let ipAddressFromData: IPAddress? = {
switch data.count {
case 4: return IPv4Address(data)
case 16: return IPv6Address(data)
default: return nil
}
}()
guard let ipAddress = ipAddressFromData else {
throw DecodingError.invalidData
}
address = ipAddress
}
enum DecodingError: Error {
case invalidData
}
}

View File

@ -4,15 +4,48 @@
import Foundation
import Network
@available(OSX 10.14, iOS 12.0, *)
struct Endpoint {
let host: NWEndpoint.Host
let port: NWEndpoint.Port
init(host: NWEndpoint.Host, port: NWEndpoint.Port) {
self.host = host
self.port = port
}
}
// MARK: Converting to and from String
extension Endpoint: Codable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let endpointString = try container.decode(String.self)
guard let endpoint = Endpoint(from: endpointString) else {
throw DecodingError.invalidData
}
self = endpoint
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(stringRepresentation)
}
enum DecodingError: Error {
case invalidData
}
}
extension Endpoint {
var stringRepresentation: String {
switch host {
case .name(let hostname, _):
return "\(hostname):\(port)"
case .ipv4(let address):
return "\(address):\(port)"
case .ipv6(let address):
return "[\(address)]:\(port)"
}
}
init?(from string: String) {
// Separation of host and port is based on 'parse_endpoint' function in
// https://git.zx2c4.com/WireGuard/tree/src/tools/config.c
@ -41,37 +74,6 @@ extension Endpoint {
host = NWEndpoint.Host(hostString)
port = endpointPort
}
func stringRepresentation() -> String {
switch host {
case .name(let hostname, _):
return "\(hostname):\(port)"
case .ipv4(let address):
return "\(address):\(port)"
case .ipv6(let address):
return "[\(address)]:\(port)"
}
}
}
// MARK: Codable
@available(OSX 10.14, iOS 12.0, *)
extension Endpoint: Codable {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(stringRepresentation())
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let endpointString = try container.decode(String.self)
guard let endpoint = Endpoint(from: endpointString) else {
throw DecodingError.invalidData
}
self = endpoint
}
enum DecodingError: Error {
case invalidData
}
}
extension Endpoint {

View File

@ -4,16 +4,28 @@
import Foundation
import Network
@available(OSX 10.14, iOS 12.0, *)
struct IPAddressRange {
let address: IPAddress
var networkPrefixLength: UInt8
init(address: IPAddress, networkPrefixLength: UInt8) {
self.address = address
self.networkPrefixLength = networkPrefixLength
}
}
// MARK: Converting to and from String
extension IPAddressRange {
var stringRepresentation: String {
return "\(address)/\(networkPrefixLength)"
}
init?(from string: String) {
guard let parsed = IPAddressRange.parseAddressString(string) else { return nil }
address = parsed.0
networkPrefixLength = parsed.1
}
private static func parseAddressString(_ string: String) -> (IPAddress, UInt8)? {
let endOfIPAddress = string.lastIndex(of: "/") ?? string.endIndex
let addressString = String(string[string.startIndex ..< endOfIPAddress])
let address: IPAddress
@ -24,7 +36,8 @@ extension IPAddressRange {
} else {
return nil
}
let maxNetworkPrefixLength: UInt8 = (address is IPv4Address) ? 32 : 128
let maxNetworkPrefixLength: UInt8 = address is IPv4Address ? 32 : 128
var networkPrefixLength: UInt8
if endOfIPAddress < string.endIndex { // "/" was located
let indexOfNetworkPrefixLength = string.index(after: endOfIPAddress)
@ -35,47 +48,25 @@ extension IPAddressRange {
} else {
networkPrefixLength = maxNetworkPrefixLength
}
self.address = address
self.networkPrefixLength = networkPrefixLength
}
func stringRepresentation() -> String {
return "\(address)/\(networkPrefixLength)"
return (address, networkPrefixLength)
}
}
// MARK: Codable
@available(OSX 10.14, iOS 12.0, *)
extension IPAddressRange: Codable {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
let addressDataLength: Int
if address is IPv4Address {
addressDataLength = 4
} else if address is IPv6Address {
addressDataLength = 16
} else {
fatalError()
}
var data = Data(capacity: addressDataLength + 1)
data.append(address.rawValue)
data.append(networkPrefixLength)
try container.encode(data)
try container.encode(stringRepresentation)
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
var data = try container.decode(Data.self)
networkPrefixLength = data.removeLast()
let ipAddressFromData: IPAddress? = {
switch data.count {
case 4: return IPv4Address(data)
case 16: return IPv6Address(data)
default: return nil
}
}()
guard let ipAddress = ipAddressFromData else { throw DecodingError.invalidData }
address = ipAddress
let values = try decoder.singleValueContainer()
let addressString = try values.decode(String.self)
guard let parsed = IPAddressRange.parseAddressString(addressString) else { throw DecodingError.invalidData }
address = parsed.0
networkPrefixLength = parsed.1
}
enum DecodingError: Error {
case invalidData
}

View File

@ -0,0 +1,59 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
import Foundation
struct InterfaceConfiguration {
var name: String
var privateKey: Data
var addresses = [IPAddressRange]()
var listenPort: UInt16?
var mtu: UInt16?
var dns = [DNSServer]()
init(name: String, privateKey: Data) {
self.name = name
self.privateKey = privateKey
if name.isEmpty {
fatalError("Empty name")
}
if privateKey.count != TunnelConfiguration.keyLength {
fatalError("Invalid private key")
}
}
}
extension InterfaceConfiguration: Codable {
enum CodingKeys: String, CodingKey {
case name = "Name"
case privateKey = "PrivateKey"
case addresses = "Address"
case listenPort = "ListenPort"
case mtu = "MTU"
case dns = "DNS"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
name = try values.decode(String.self, forKey: .name)
privateKey = try Data(base64Encoded: values.decode(String.self, forKey: .privateKey))!
addresses = try values.decode([IPAddressRange].self, forKey: .addresses)
listenPort = try? values.decode(UInt16.self, forKey: .listenPort)
mtu = try? values.decode(UInt16.self, forKey: .mtu)
dns = try values.decode([DNSServer].self, forKey: .dns)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
try container.encode(privateKey.base64EncodedString(), forKey: .privateKey)
try container.encode(addresses, forKey: .addresses)
if let listenPort = listenPort {
try container.encode(listenPort, forKey: .listenPort)
}
if let mtu = mtu {
try container.encode(mtu, forKey: .mtu)
}
try container.encode(dns, forKey: .dns)
}
}

View File

@ -0,0 +1,46 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
import Foundation
import Network
struct LegacyDNSServer: Codable {
let address: IPAddress
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
var data = try container.decode(Data.self)
let ipAddressFromData: IPAddress? = {
switch data.count {
case 4: return IPv4Address(data)
case 16: return IPv6Address(data)
default: return nil
}
}()
guard let ipAddress = ipAddressFromData else {
throw DecodingError.invalidData
}
address = ipAddress
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(address.rawValue)
}
enum DecodingError: Error {
case invalidData
}
}
extension LegacyDNSServer {
var migrated: DNSServer {
return DNSServer(address: address)
}
}
extension Array where Element == LegacyDNSServer {
var migrated: [DNSServer] {
return map { $0.migrated }
}
}

View File

@ -0,0 +1,64 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
import Foundation
import Network
struct LegacyEndpoint: Codable {
let host: NWEndpoint.Host
let port: NWEndpoint.Port
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let endpointString = try container.decode(String.self)
guard !endpointString.isEmpty else { throw DecodingError.invalidData }
let startOfPort: String.Index
let hostString: String
if endpointString.first! == "[" {
// Look for IPv6-style endpoint, like [::1]:80
let startOfHost = endpointString.index(after: endpointString.startIndex)
guard let endOfHost = endpointString.dropFirst().firstIndex(of: "]") else { throw DecodingError.invalidData }
let afterEndOfHost = endpointString.index(after: endOfHost)
guard endpointString[afterEndOfHost] == ":" else { throw DecodingError.invalidData }
startOfPort = endpointString.index(after: afterEndOfHost)
hostString = String(endpointString[startOfHost ..< endOfHost])
} else {
// Look for an IPv4-style endpoint, like 127.0.0.1:80
guard let endOfHost = endpointString.firstIndex(of: ":") else { throw DecodingError.invalidData }
startOfPort = endpointString.index(after: endOfHost)
hostString = String(endpointString[endpointString.startIndex ..< endOfHost])
}
guard let endpointPort = NWEndpoint.Port(String(endpointString[startOfPort ..< endpointString.endIndex])) else { throw DecodingError.invalidData }
let invalidCharacterIndex = hostString.unicodeScalars.firstIndex { char in
return !CharacterSet.urlHostAllowed.contains(char)
}
guard invalidCharacterIndex == nil else { throw DecodingError.invalidData }
host = NWEndpoint.Host(hostString)
port = endpointPort
}
public func encode(to encoder: Encoder) throws {
let stringRepresentation: String
switch host {
case .name(let hostname, _):
stringRepresentation = "\(hostname):\(port)"
case .ipv4(let address):
stringRepresentation = "\(address):\(port)"
case .ipv6(let address):
stringRepresentation = "[\(address)]:\(port)"
}
var container = encoder.singleValueContainer()
try container.encode(stringRepresentation)
}
enum DecodingError: Error {
case invalidData
}
}
extension LegacyEndpoint {
var migrated: Endpoint {
return Endpoint(host: host, port: port)
}
}

View File

@ -0,0 +1,57 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
import Foundation
import Network
struct LegacyIPAddressRange: Codable {
let address: IPAddress
let networkPrefixLength: UInt8
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
var data = try container.decode(Data.self)
networkPrefixLength = data.removeLast()
let ipAddressFromData: IPAddress? = {
switch data.count {
case 4: return IPv4Address(data)
case 16: return IPv6Address(data)
default: return nil
}
}()
guard let ipAddress = ipAddressFromData else { throw DecodingError.invalidData }
address = ipAddress
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
let addressDataLength: Int
if address is IPv4Address {
addressDataLength = 4
} else if address is IPv6Address {
addressDataLength = 16
} else {
fatalError()
}
var data = Data(capacity: addressDataLength + 1)
data.append(address.rawValue)
data.append(networkPrefixLength)
try container.encode(data)
}
enum DecodingError: Error {
case invalidData
}
}
extension LegacyIPAddressRange {
var migrated: IPAddressRange {
return IPAddressRange(address: address, networkPrefixLength: networkPrefixLength)
}
}
extension Array where Element == LegacyIPAddressRange {
var migrated: [IPAddressRange] {
return map { $0.migrated }
}
}

View File

@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
import Foundation
struct LegacyInterfaceConfiguration: Codable {
let name: String
let privateKey: Data
let addresses: [LegacyIPAddressRange]
let listenPort: UInt16?
let mtu: UInt16?
let dns: [LegacyDNSServer]
}
extension LegacyInterfaceConfiguration {
var migrated: InterfaceConfiguration {
var interface = InterfaceConfiguration(name: name, privateKey: privateKey)
interface.addresses = addresses.migrated
interface.listenPort = listenPort
interface.mtu = mtu
interface.dns = dns.migrated
return interface
}
}

View File

@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
import Foundation
struct LegacyPeerConfiguration: Codable {
let publicKey: Data
let preSharedKey: Data?
let allowedIPs: [LegacyIPAddressRange]
let endpoint: LegacyEndpoint?
let persistentKeepAlive: UInt16?
}
extension LegacyPeerConfiguration {
var migrated: PeerConfiguration {
var configuration = PeerConfiguration(publicKey: publicKey)
configuration.preSharedKey = preSharedKey
configuration.allowedIPs = allowedIPs.migrated
configuration.endpoint = endpoint?.migrated
configuration.persistentKeepAlive = persistentKeepAlive
return configuration
}
}
extension Array where Element == LegacyPeerConfiguration {
var migrated: [PeerConfiguration] {
return map { $0.migrated }
}
}

View File

@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
import Foundation
final class LegacyTunnelConfiguration: Codable {
let interface: LegacyInterfaceConfiguration
let peers: [LegacyPeerConfiguration]
}
extension LegacyTunnelConfiguration {
var migrated: TunnelConfiguration {
return TunnelConfiguration(interface: interface.migrated, peers: peers.migrated)
}
}

View File

@ -0,0 +1,66 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
import Foundation
struct PeerConfiguration {
var publicKey: Data
var preSharedKey: Data? {
didSet(value) {
if let value = value {
if value.count != TunnelConfiguration.keyLength {
fatalError("Invalid preshared key")
}
}
}
}
var allowedIPs = [IPAddressRange]()
var endpoint: Endpoint?
var persistentKeepAlive: UInt16?
init(publicKey: Data) {
self.publicKey = publicKey
if publicKey.count != TunnelConfiguration.keyLength {
fatalError("Invalid public key")
}
}
}
extension PeerConfiguration: Codable {
enum CodingKeys: String, CodingKey {
case publicKey = "PublicKey"
case preSharedKey = "PreSharedKey"
case allowedIPs = "AllowedIPs"
case endpoint = "Endpoint"
case persistentKeepAlive = "PersistentKeepAlive"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
publicKey = try Data(base64Encoded: values.decode(String.self, forKey: .publicKey))!
if let base64PreSharedKey = try? values.decode(Data.self, forKey: .preSharedKey) {
preSharedKey = Data(base64Encoded: base64PreSharedKey)
} else {
preSharedKey = nil
}
allowedIPs = try values.decode([IPAddressRange].self, forKey: .allowedIPs)
endpoint = try? values.decode(Endpoint.self, forKey: .endpoint)
persistentKeepAlive = try? values.decode(UInt16.self, forKey: .persistentKeepAlive)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(publicKey.base64EncodedString(), forKey: .publicKey)
if let preSharedKey = preSharedKey {
try container.encode(preSharedKey.base64EncodedString(), forKey: .preSharedKey)
}
try container.encode(allowedIPs, forKey: .allowedIPs)
if let endpoint = endpoint {
try container.encode(endpoint, forKey: .endpoint)
}
if let persistentKeepAlive = persistentKeepAlive {
try container.encode(persistentKeepAlive, forKey: .persistentKeepAlive)
}
}
}

View File

@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
import Foundation
final class TunnelConfiguration {
var interface: InterfaceConfiguration
let peers: [PeerConfiguration]
static let keyLength = 32
init(interface: InterfaceConfiguration, peers: [PeerConfiguration]) {
self.interface = interface
self.peers = peers
let peerPublicKeysArray = peers.map { $0.publicKey }
let peerPublicKeysSet = Set<Data>(peerPublicKeysArray)
if peerPublicKeysArray.count != peerPublicKeysSet.count {
fatalError("Two or more peers cannot have the same public key")
}
}
}
extension TunnelConfiguration: Codable {
enum CodingKeys: String, CodingKey {
case interface = "Interface"
case peers = "Peer"
}
convenience init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
let interface = try values.decode(InterfaceConfiguration.self, forKey: .interface)
let peers = try values.decode([PeerConfiguration].self, forKey: .peers)
self.init(interface: interface, peers: peers)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(interface, forKey: .interface)
try container.encode(peers, forKey: .peers)
}
}

View File

@ -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
]
}
}

View File

@ -18,6 +18,22 @@
5F4541A921C451D100994C13 /* TunnelStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F4541A821C451D100994C13 /* TunnelStatus.swift */; };
5F4541AE21C7704300994C13 /* NEVPNStatus+CustomStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F4541AD21C7704300994C13 /* NEVPNStatus+CustomStringConvertible.swift */; };
5F4541B221CBFAEE00994C13 /* String+ArrayConversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F4541B121CBFAEE00994C13 /* String+ArrayConversion.swift */; };
5FF7B96221CC95DE00A7DD74 /* InterfaceConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FF7B96121CC95DE00A7DD74 /* InterfaceConfiguration.swift */; };
5FF7B96321CC95DE00A7DD74 /* InterfaceConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FF7B96121CC95DE00A7DD74 /* InterfaceConfiguration.swift */; };
5FF7B96521CC95FA00A7DD74 /* PeerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FF7B96421CC95FA00A7DD74 /* PeerConfiguration.swift */; };
5FF7B96621CC95FA00A7DD74 /* PeerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FF7B96421CC95FA00A7DD74 /* PeerConfiguration.swift */; };
5FF7B96E21CC967B00A7DD74 /* LegacyInterfaceConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FF7B96821CC967B00A7DD74 /* LegacyInterfaceConfiguration.swift */; };
5FF7B96F21CC967B00A7DD74 /* LegacyInterfaceConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FF7B96821CC967B00A7DD74 /* LegacyInterfaceConfiguration.swift */; };
5FF7B97021CC967B00A7DD74 /* LegacyIPAddressRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FF7B96921CC967B00A7DD74 /* LegacyIPAddressRange.swift */; };
5FF7B97121CC967B00A7DD74 /* LegacyIPAddressRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FF7B96921CC967B00A7DD74 /* LegacyIPAddressRange.swift */; };
5FF7B97221CC967B00A7DD74 /* LegacyEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FF7B96A21CC967B00A7DD74 /* LegacyEndpoint.swift */; };
5FF7B97321CC967B00A7DD74 /* LegacyEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FF7B96A21CC967B00A7DD74 /* LegacyEndpoint.swift */; };
5FF7B97421CC967B00A7DD74 /* LegacyDNSServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FF7B96B21CC967B00A7DD74 /* LegacyDNSServer.swift */; };
5FF7B97521CC967B00A7DD74 /* LegacyDNSServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FF7B96B21CC967B00A7DD74 /* LegacyDNSServer.swift */; };
5FF7B97621CC967B00A7DD74 /* LegacyPeerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FF7B96C21CC967B00A7DD74 /* LegacyPeerConfiguration.swift */; };
5FF7B97721CC967B00A7DD74 /* LegacyPeerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FF7B96C21CC967B00A7DD74 /* LegacyPeerConfiguration.swift */; };
5FF7B97821CC967B00A7DD74 /* LegacyTunnelConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FF7B96D21CC967B00A7DD74 /* LegacyTunnelConfiguration.swift */; };
5FF7B97921CC967B00A7DD74 /* LegacyTunnelConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FF7B96D21CC967B00A7DD74 /* LegacyTunnelConfiguration.swift */; };
6F5A2B4621AFDED40081EDD8 /* FileManager+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F5A2B4421AFDE020081EDD8 /* FileManager+Extension.swift */; };
6F5A2B4821AFF49A0081EDD8 /* FileManager+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F5A2B4421AFDE020081EDD8 /* FileManager+Extension.swift */; };
6F5D0C1D218352EF000F85AD /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F5D0C1C218352EF000F85AD /* PacketTunnelProvider.swift */; };
@ -34,7 +50,7 @@
6F7774E1217181B1006A79B3 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7774DF217181B1006A79B3 /* MainViewController.swift */; };
6F7774E2217181B1006A79B3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7774E0217181B1006A79B3 /* AppDelegate.swift */; };
6F7774E421718281006A79B3 /* TunnelsListTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7774E321718281006A79B3 /* TunnelsListTableViewController.swift */; };
6F7774E82172020C006A79B3 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7774E72172020C006A79B3 /* Configuration.swift */; };
6F7774E82172020C006A79B3 /* TunnelConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7774E72172020C006A79B3 /* TunnelConfiguration.swift */; };
6F7774EA217229DB006A79B3 /* IPAddressRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7774E9217229DB006A79B3 /* IPAddressRange.swift */; };
6F7774EF21722D97006A79B3 /* TunnelsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7774EE21722D97006A79B3 /* TunnelsManager.swift */; };
6F7774F321774263006A79B3 /* TunnelEditTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7774F221774263006A79B3 /* TunnelEditTableViewController.swift */; };
@ -64,7 +80,7 @@
6FF4AC1F211EC472002C96EB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6FF4AC1E211EC472002C96EB /* Assets.xcassets */; };
6FF4AC22211EC472002C96EB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6FF4AC20211EC472002C96EB /* LaunchScreen.storyboard */; };
6FFA5D8921942F320001E2F7 /* PacketTunnelSettingsGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F5D0C472183C6A3000F85AD /* PacketTunnelSettingsGenerator.swift */; };
6FFA5D8E2194370D0001E2F7 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7774E72172020C006A79B3 /* Configuration.swift */; };
6FFA5D8E2194370D0001E2F7 /* TunnelConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7774E72172020C006A79B3 /* TunnelConfiguration.swift */; };
6FFA5D8F2194370D0001E2F7 /* IPAddressRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7774E9217229DB006A79B3 /* IPAddressRange.swift */; };
6FFA5D902194370D0001E2F7 /* Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F693A552179E556008551C1 /* Endpoint.swift */; };
6FFA5D912194370D0001E2F7 /* DNSServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F628C3E217F3413003482A3 /* DNSServer.swift */; };
@ -118,6 +134,14 @@
5F4541A821C451D100994C13 /* TunnelStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelStatus.swift; sourceTree = "<group>"; };
5F4541AD21C7704300994C13 /* NEVPNStatus+CustomStringConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NEVPNStatus+CustomStringConvertible.swift"; sourceTree = "<group>"; };
5F4541B121CBFAEE00994C13 /* String+ArrayConversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+ArrayConversion.swift"; sourceTree = "<group>"; };
5FF7B96121CC95DE00A7DD74 /* InterfaceConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterfaceConfiguration.swift; sourceTree = "<group>"; };
5FF7B96421CC95FA00A7DD74 /* PeerConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeerConfiguration.swift; sourceTree = "<group>"; };
5FF7B96821CC967B00A7DD74 /* LegacyInterfaceConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyInterfaceConfiguration.swift; sourceTree = "<group>"; };
5FF7B96921CC967B00A7DD74 /* LegacyIPAddressRange.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyIPAddressRange.swift; sourceTree = "<group>"; };
5FF7B96A21CC967B00A7DD74 /* LegacyEndpoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyEndpoint.swift; sourceTree = "<group>"; };
5FF7B96B21CC967B00A7DD74 /* LegacyDNSServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyDNSServer.swift; sourceTree = "<group>"; };
5FF7B96C21CC967B00A7DD74 /* LegacyPeerConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyPeerConfiguration.swift; sourceTree = "<group>"; };
5FF7B96D21CC967B00A7DD74 /* LegacyTunnelConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyTunnelConfiguration.swift; sourceTree = "<group>"; };
6F5A2B4421AFDE020081EDD8 /* FileManager+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+Extension.swift"; sourceTree = "<group>"; };
6F5D0C1421832391000F85AD /* DNSResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSResolver.swift; sourceTree = "<group>"; };
6F5D0C1A218352EF000F85AD /* WireGuardNetworkExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WireGuardNetworkExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
@ -140,7 +164,7 @@
6F7774DF217181B1006A79B3 /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
6F7774E0217181B1006A79B3 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
6F7774E321718281006A79B3 /* TunnelsListTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TunnelsListTableViewController.swift; sourceTree = "<group>"; };
6F7774E72172020C006A79B3 /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = "<group>"; };
6F7774E72172020C006A79B3 /* TunnelConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelConfiguration.swift; sourceTree = "<group>"; };
6F7774E9217229DB006A79B3 /* IPAddressRange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPAddressRange.swift; sourceTree = "<group>"; };
6F7774EE21722D97006A79B3 /* TunnelsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelsManager.swift; sourceTree = "<group>"; };
6F7774F221774263006A79B3 /* TunnelEditTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelEditTableViewController.swift; sourceTree = "<group>"; };
@ -227,6 +251,19 @@
path = ViewController;
sourceTree = "<group>";
};
5FF7B96721CC966300A7DD74 /* Legacy */ = {
isa = PBXGroup;
children = (
5FF7B96B21CC967B00A7DD74 /* LegacyDNSServer.swift */,
5FF7B96A21CC967B00A7DD74 /* LegacyEndpoint.swift */,
5FF7B96821CC967B00A7DD74 /* LegacyInterfaceConfiguration.swift */,
5FF7B96921CC967B00A7DD74 /* LegacyIPAddressRange.swift */,
5FF7B96C21CC967B00A7DD74 /* LegacyPeerConfiguration.swift */,
5FF7B96D21CC967B00A7DD74 /* LegacyTunnelConfiguration.swift */,
);
path = Legacy;
sourceTree = "<group>";
};
6F5D0C1B218352EF000F85AD /* WireGuardNetworkExtension */ = {
isa = PBXGroup;
children = (
@ -296,10 +333,13 @@
6F7774E6217201E0006A79B3 /* Model */ = {
isa = PBXGroup;
children = (
6F7774E72172020C006A79B3 /* Configuration.swift */,
5FF7B96721CC966300A7DD74 /* Legacy */,
6F7774E72172020C006A79B3 /* TunnelConfiguration.swift */,
6F7774E9217229DB006A79B3 /* IPAddressRange.swift */,
6F693A552179E556008551C1 /* Endpoint.swift */,
6F628C3E217F3413003482A3 /* DNSServer.swift */,
5FF7B96121CC95DE00A7DD74 /* InterfaceConfiguration.swift */,
5FF7B96421CC95FA00A7DD74 /* PeerConfiguration.swift */,
);
path = Model;
sourceTree = "<group>";
@ -652,13 +692,21 @@
6FF3527121C240160008484E /* Logger.swift in Sources */,
6F5A2B4621AFDED40081EDD8 /* FileManager+Extension.swift in Sources */,
6FFA5DA021958ECC0001E2F7 /* ErrorNotifier.swift in Sources */,
5FF7B97521CC967B00A7DD74 /* LegacyDNSServer.swift in Sources */,
6FFA5D96219446380001E2F7 /* NETunnelProviderProtocol+Extension.swift in Sources */,
6FFA5D8E2194370D0001E2F7 /* Configuration.swift in Sources */,
5FF7B97921CC967B00A7DD74 /* LegacyTunnelConfiguration.swift in Sources */,
6FFA5D8E2194370D0001E2F7 /* TunnelConfiguration.swift in Sources */,
5FF7B97721CC967B00A7DD74 /* LegacyPeerConfiguration.swift in Sources */,
5FF7B96621CC95FA00A7DD74 /* PeerConfiguration.swift in Sources */,
6FFA5D8F2194370D0001E2F7 /* IPAddressRange.swift in Sources */,
6FFA5D902194370D0001E2F7 /* Endpoint.swift in Sources */,
5FF7B96321CC95DE00A7DD74 /* InterfaceConfiguration.swift in Sources */,
6FFA5D9321943BC90001E2F7 /* DNSResolver.swift in Sources */,
5FF7B97121CC967B00A7DD74 /* LegacyIPAddressRange.swift in Sources */,
5FF7B96F21CC967B00A7DD74 /* LegacyInterfaceConfiguration.swift in Sources */,
6FFA5D912194370D0001E2F7 /* DNSServer.swift in Sources */,
6FFA5D8921942F320001E2F7 /* PacketTunnelSettingsGenerator.swift in Sources */,
5FF7B97321CC967B00A7DD74 /* LegacyEndpoint.swift in Sources */,
6F5D0C1D218352EF000F85AD /* PacketTunnelProvider.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -674,12 +722,17 @@
6F7774E421718281006A79B3 /* TunnelsListTableViewController.swift in Sources */,
6F7774EF21722D97006A79B3 /* TunnelsManager.swift in Sources */,
5F45417D21C1B23600994C13 /* UITableViewCell+Reuse.swift in Sources */,
5FF7B97821CC967B00A7DD74 /* LegacyTunnelConfiguration.swift in Sources */,
5F45419221C2D55800994C13 /* CheckmarkCell.swift in Sources */,
5FF7B97221CC967B00A7DD74 /* LegacyEndpoint.swift in Sources */,
6FE254FF219C60290028284D /* ZipExporter.swift in Sources */,
6F693A562179E556008551C1 /* Endpoint.swift in Sources */,
6FDEF7E62185EFB200D8FBF6 /* QRScanViewController.swift in Sources */,
6FFA5D952194454A0001E2F7 /* NETunnelProviderProtocol+Extension.swift in Sources */,
5FF7B97021CC967B00A7DD74 /* LegacyIPAddressRange.swift in Sources */,
5FF7B96221CC95DE00A7DD74 /* InterfaceConfiguration.swift in Sources */,
5F4541A921C451D100994C13 /* TunnelStatus.swift in Sources */,
5FF7B96E21CC967B00A7DD74 /* LegacyInterfaceConfiguration.swift in Sources */,
6F61F1E921B932F700483816 /* WireGuardAppError.swift in Sources */,
6F6899A62180447E0012E523 /* x25519.c in Sources */,
6F7774E2217181B1006A79B3 /* AppDelegate.swift in Sources */,
@ -696,8 +749,10 @@
6FDEF8082187442100D8FBF6 /* WgQuickConfigFileWriter.swift in Sources */,
6FE254FB219C10800028284D /* ZipImporter.swift in Sources */,
6F7774EA217229DB006A79B3 /* IPAddressRange.swift in Sources */,
5FF7B97621CC967B00A7DD74 /* LegacyPeerConfiguration.swift in Sources */,
5F4541AE21C7704300994C13 /* NEVPNStatus+CustomStringConvertible.swift in Sources */,
6F7774E82172020C006A79B3 /* Configuration.swift in Sources */,
6F7774E82172020C006A79B3 /* TunnelConfiguration.swift in Sources */,
5FF7B97421CC967B00A7DD74 /* LegacyDNSServer.swift in Sources */,
6FDEF7FB21863B6100D8FBF6 /* unzip.c in Sources */,
6F6899A8218044FC0012E523 /* Curve25519.swift in Sources */,
5F4541A021C2D6B700994C13 /* TunnelListCell.swift in Sources */,
@ -709,6 +764,7 @@
6FB1017921C57DE600766195 /* MockTunnels.swift in Sources */,
6FDEF806218725D200D8FBF6 /* SettingsTableViewController.swift in Sources */,
5F4541A221C2D6DF00994C13 /* BorderedTextButton.swift in Sources */,
5FF7B96521CC95FA00A7DD74 /* PeerConfiguration.swift in Sources */,
6F7774E1217181B1006A79B3 /* MainViewController.swift in Sources */,
6FFA5DA42197085D0001E2F7 /* ActivateOnDemandSetting.swift in Sources */,
5F4541B221CBFAEE00994C13 /* String+ArrayConversion.swift in Sources */,

View File

@ -12,11 +12,11 @@ class WgQuickConfigFileWriter {
output.append("ListenPort = \(listenPort)\n")
}
if !interface.addresses.isEmpty {
let addressString = interface.addresses.map { $0.stringRepresentation() }.joined(separator: ", ")
let addressString = interface.addresses.map { $0.stringRepresentation }.joined(separator: ", ")
output.append("Address = \(addressString)\n")
}
if !interface.dns.isEmpty {
let dnsString = interface.dns.map { $0.stringRepresentation() }.joined(separator: ", ")
let dnsString = interface.dns.map { $0.stringRepresentation }.joined(separator: ", ")
output.append("DNS = \(dnsString)\n")
}
if let mtu = interface.mtu {
@ -30,11 +30,11 @@ class WgQuickConfigFileWriter {
output.append("PresharedKey = \(preSharedKey.base64EncodedString())\n")
}
if !peer.allowedIPs.isEmpty {
let allowedIPsString = peer.allowedIPs.map { $0.stringRepresentation() }.joined(separator: ", ")
let allowedIPsString = peer.allowedIPs.map { $0.stringRepresentation }.joined(separator: ", ")
output.append("AllowedIPs = \(allowedIPsString)\n")
}
if let endpoint = peer.endpoint {
output.append("Endpoint = \(endpoint.stringRepresentation())\n")
output.append("Endpoint = \(endpoint.stringRepresentation)\n")
}
if let persistentKeepAlive = peer.persistentKeepAlive {
output.append("PersistentKeepalive = \(persistentKeepAlive)\n")

View File

@ -41,7 +41,14 @@ class TunnelsManager {
completionHandler(.failure(TunnelsManagerError.systemErrorOnListingTunnels(systemError: error)))
return
}
completionHandler(.success(TunnelsManager(tunnelProviders: managers ?? [])))
let tunnelManagers = managers ?? []
tunnelManagers.forEach {
if ($0.protocolConfiguration as? NETunnelProviderProtocol)?.migrateConfigurationIfNeeded() == true {
$0.saveToPreferences { _ in }
}
}
completionHandler(.success(TunnelsManager(tunnelProviders: tunnelManagers)))
}
#endif
}
@ -309,6 +316,7 @@ class TunnelsManager {
NotificationCenter.default.removeObserver(statusObservationToken)
}
}
}
class TunnelContainer: NSObject {
@ -344,6 +352,14 @@ class TunnelContainer: NSObject {
fileprivate let tunnelProvider: NETunnelProviderManager
private var lastTunnelConnectionStatus: NEVPNStatus?
var tunnelConfiguration: TunnelConfiguration? {
return (tunnelProvider.protocolConfiguration as? NETunnelProviderProtocol)?.tunnelConfiguration
}
var activateOnDemandSetting: ActivateOnDemandSetting {
return ActivateOnDemandSetting(from: tunnelProvider)
}
init(tunnel: NETunnelProviderManager) {
name = tunnel.localizedDescription ?? "Unnamed"
let status = TunnelStatus(from: tunnel.connection.status)
@ -353,14 +369,6 @@ class TunnelContainer: NSObject {
super.init()
}
func tunnelConfiguration() -> TunnelConfiguration? {
return (tunnelProvider.protocolConfiguration as? NETunnelProviderProtocol)?.tunnelConfiguration()
}
func activateOnDemandSetting() -> ActivateOnDemandSetting {
return ActivateOnDemandSetting(from: tunnelProvider)
}
func refreshStatus() {
let status = TunnelStatus(from: tunnelProvider.connection.status)
self.status = status

View File

@ -106,7 +106,7 @@ class TunnelViewModel {
scratchpad[.privateKey] = config.privateKey.base64EncodedString()
scratchpad[.publicKey] = config.publicKey.base64EncodedString()
if !config.addresses.isEmpty {
scratchpad[.addresses] = config.addresses.map { $0.stringRepresentation() }.joined(separator: ", ")
scratchpad[.addresses] = config.addresses.map { $0.stringRepresentation }.joined(separator: ", ")
}
if let listenPort = config.listenPort {
scratchpad[.listenPort] = String(listenPort)
@ -115,7 +115,7 @@ class TunnelViewModel {
scratchpad[.mtu] = String(mtu)
}
if !config.dns.isEmpty {
scratchpad[.dns] = config.dns.map { $0.stringRepresentation() }.joined(separator: ", ")
scratchpad[.dns] = config.dns.map { $0.stringRepresentation }.joined(separator: ", ")
}
}
@ -248,10 +248,10 @@ class TunnelViewModel {
scratchpad[.preSharedKey] = preSharedKey.base64EncodedString()
}
if !config.allowedIPs.isEmpty {
scratchpad[.allowedIPs] = config.allowedIPs.map { $0.stringRepresentation() }.joined(separator: ", ")
scratchpad[.allowedIPs] = config.allowedIPs.map { $0.stringRepresentation }.joined(separator: ", ")
}
if let endpoint = config.endpoint {
scratchpad[.endpoint] = endpoint.stringRepresentation()
scratchpad[.endpoint] = endpoint.stringRepresentation
}
if let persistentKeepAlive = config.persistentKeepAlive {
scratchpad[.persistentKeepAlive] = String(persistentKeepAlive)

View File

@ -93,7 +93,7 @@ class SettingsTableViewController: UITableViewController {
_ = FileManager.deleteFile(at: destinationURL)
let count = tunnelsManager.numberOfTunnels()
let tunnelConfigurations = (0 ..< count).compactMap { tunnelsManager.tunnel(at: $0).tunnelConfiguration() }
let tunnelConfigurations = (0 ..< count).compactMap { tunnelsManager.tunnel(at: $0).tunnelConfiguration }
ZipExporter.exportConfigFiles(tunnelConfigurations: tunnelConfigurations, to: destinationURL) { [weak self] error in
if let error = error {
ErrorPresenter.showErrorAlert(error: error, from: self)

View File

@ -35,7 +35,7 @@ class TunnelDetailTableViewController: UITableViewController {
init(tunnelsManager: TunnelsManager, tunnel: TunnelContainer) {
self.tunnelsManager = tunnelsManager
self.tunnel = tunnel
tunnelViewModel = TunnelViewModel(tunnelConfiguration: tunnel.tunnelConfiguration())
tunnelViewModel = TunnelViewModel(tunnelConfiguration: tunnel.tunnelConfiguration)
super.init(style: .grouped)
loadSections()
}
@ -101,7 +101,7 @@ class TunnelDetailTableViewController: UITableViewController {
extension TunnelDetailTableViewController: TunnelEditTableViewControllerDelegate {
func tunnelSaved(tunnel: TunnelContainer) {
tunnelViewModel = TunnelViewModel(tunnelConfiguration: tunnel.tunnelConfiguration())
tunnelViewModel = TunnelViewModel(tunnelConfiguration: tunnel.tunnelConfiguration)
loadSections()
title = tunnel.name
restorationIdentifier = "TunnelDetailVC:\(tunnel.name)"
@ -229,9 +229,9 @@ extension TunnelDetailTableViewController {
private func onDemandCell(for tableView: UITableView, at indexPath: IndexPath) -> UITableViewCell {
let cell: KeyValueCell = tableView.dequeueReusableCell(for: indexPath)
cell.key = tr("tunnelOnDemandKey")
cell.value = TunnelViewModel.activateOnDemandDetailText(for: tunnel.activateOnDemandSetting())
cell.value = TunnelViewModel.activateOnDemandDetailText(for: tunnel.activateOnDemandSetting)
onDemandStatusObservationToken = tunnel.observe(\.isActivateOnDemandEnabled) { [weak cell] tunnel, _ in
cell?.value = TunnelViewModel.activateOnDemandDetailText(for: tunnel.activateOnDemandSetting())
cell?.value = TunnelViewModel.activateOnDemandDetailText(for: tunnel.activateOnDemandSetting)
}
return cell
}

View File

@ -49,8 +49,8 @@ class TunnelEditTableViewController: UITableViewController {
// Use this initializer to edit an existing tunnel.
self.tunnelsManager = tunnelsManager
self.tunnel = tunnel
tunnelViewModel = TunnelViewModel(tunnelConfiguration: tunnel.tunnelConfiguration())
activateOnDemandSetting = tunnel.activateOnDemandSetting()
tunnelViewModel = TunnelViewModel(tunnelConfiguration: tunnel.tunnelConfiguration)
activateOnDemandSetting = tunnel.activateOnDemandSetting
super.init(style: .grouped)
loadSections()
}

View File

@ -26,12 +26,11 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
//swiftlint:disable:next function_body_length
override func startTunnel(options: [String: NSObject]?, completionHandler startTunnelCompletionHandler: @escaping (Error?) -> Void) {
let activationAttemptId = options?["activationAttemptId"] as? String
let errorNotifier = ErrorNotifier(activationAttemptId: activationAttemptId, tunnelProvider: self)
guard let tunnelProviderProtocol = protocolConfiguration as? NETunnelProviderProtocol,
let tunnelConfiguration = tunnelProviderProtocol.tunnelConfiguration() else {
let tunnelConfiguration = tunnelProviderProtocol.tunnelConfiguration else {
errorNotifier.notify(PacketTunnelProviderError.savedProtocolConfigurationIsInvalid)
startTunnelCompletionHandler(PacketTunnelProviderError.savedProtocolConfigurationIsInvalid)
return

View File

@ -6,7 +6,6 @@ import Network
import NetworkExtension
class PacketTunnelSettingsGenerator {
let tunnelConfiguration: TunnelConfiguration
let resolvedEndpoints: [Endpoint?]
@ -26,7 +25,7 @@ class PacketTunnelSettingsGenerator {
wgSettings.append("public_key=\(peer.publicKey.hexEncodedString())\n")
if let endpoint = resolvedEndpoints[index] {
if case .name(_, _) = endpoint.host { assert(false, "Endpoint is not resolved") }
wgSettings.append("endpoint=\(endpoint.stringRepresentation())\n")
wgSettings.append("endpoint=\(endpoint.stringRepresentation)\n")
}
}
@ -51,13 +50,13 @@ class PacketTunnelSettingsGenerator {
}
if let endpoint = resolvedEndpoints[index] {
if case .name(_, _) = endpoint.host { assert(false, "Endpoint is not resolved") }
wgSettings.append("endpoint=\(endpoint.stringRepresentation())\n")
wgSettings.append("endpoint=\(endpoint.stringRepresentation)\n")
}
let persistentKeepAlive = peer.persistentKeepAlive ?? 0
wgSettings.append("persistent_keepalive_interval=\(persistentKeepAlive)\n")
if !peer.allowedIPs.isEmpty {
wgSettings.append("replace_allowed_ips=true\n")
peer.allowedIPs.forEach { wgSettings.append("allowed_ip=\($0.stringRepresentation())\n") }
peer.allowedIPs.forEach { wgSettings.append("allowed_ip=\($0.stringRepresentation)\n") }
}
}
return wgSettings
@ -85,7 +84,7 @@ class PacketTunnelSettingsGenerator {
let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: remoteAddress)
let dnsServerStrings = tunnelConfiguration.interface.dns.map { $0.stringRepresentation() }
let dnsServerStrings = tunnelConfiguration.interface.dns.map { $0.stringRepresentation }
let dnsSettings = NEDNSSettings(servers: dnsServerStrings)
dnsSettings.matchDomains = [""] // All DNS queries must first go through the tunnel's DNS
networkSettings.dnsSettings = dnsSettings