From f39893685fa3c8b4728303416aff502af4adbbfe Mon Sep 17 00:00:00 2001 From: Eric Kuck Date: Thu, 20 Dec 2018 22:52:45 -0600 Subject: [PATCH] Updated NETunnelProvider save format --- WireGuard/Shared/Model/Configuration.swift | 68 --------------- WireGuard/Shared/Model/DNSServer.swift | 64 +++++++------- WireGuard/Shared/Model/Endpoint.swift | 68 ++++++++------- WireGuard/Shared/Model/IPAddressRange.swift | 63 ++++++-------- .../Shared/Model/InterfaceConfiguration.swift | 59 +++++++++++++ .../Shared/Model/Legacy/LegacyDNSServer.swift | 46 ++++++++++ .../Shared/Model/Legacy/LegacyEndpoint.swift | 64 ++++++++++++++ .../Model/Legacy/LegacyIPAddressRange.swift | 57 ++++++++++++ .../Legacy/LegacyInterfaceConfiguration.swift | 24 +++++ .../Legacy/LegacyPeerConfiguration.swift | 29 +++++++ .../Legacy/LegacyTunnelConfiguration.swift | 15 ++++ .../Shared/Model/PeerConfiguration.swift | 66 ++++++++++++++ .../Shared/Model/TunnelConfiguration.swift | 42 +++++++++ .../NETunnelProviderProtocol+Extension.swift | 87 +++++++++++++++---- WireGuard/WireGuard.xcodeproj/project.pbxproj | 68 +++++++++++++-- .../ConfigFile/WgQuickConfigFileWriter.swift | 8 +- .../WireGuard/Tunnel/TunnelsManager.swift | 26 ++++-- WireGuard/WireGuard/UI/TunnelViewModel.swift | 8 +- .../SettingsTableViewController.swift | 2 +- .../TunnelDetailTableViewController.swift | 8 +- .../TunnelEditTableViewController.swift | 4 +- .../PacketTunnelProvider.swift | 3 +- .../PacketTunnelSettingsGenerator.swift | 9 +- 23 files changed, 664 insertions(+), 224 deletions(-) delete mode 100644 WireGuard/Shared/Model/Configuration.swift create mode 100644 WireGuard/Shared/Model/InterfaceConfiguration.swift create mode 100644 WireGuard/Shared/Model/Legacy/LegacyDNSServer.swift create mode 100644 WireGuard/Shared/Model/Legacy/LegacyEndpoint.swift create mode 100644 WireGuard/Shared/Model/Legacy/LegacyIPAddressRange.swift create mode 100644 WireGuard/Shared/Model/Legacy/LegacyInterfaceConfiguration.swift create mode 100644 WireGuard/Shared/Model/Legacy/LegacyPeerConfiguration.swift create mode 100644 WireGuard/Shared/Model/Legacy/LegacyTunnelConfiguration.swift create mode 100644 WireGuard/Shared/Model/PeerConfiguration.swift create mode 100644 WireGuard/Shared/Model/TunnelConfiguration.swift diff --git a/WireGuard/Shared/Model/Configuration.swift b/WireGuard/Shared/Model/Configuration.swift deleted file mode 100644 index 77dfe97..0000000 --- a/WireGuard/Shared/Model/Configuration.swift +++ /dev/null @@ -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(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") - } - } -} diff --git a/WireGuard/Shared/Model/DNSServer.swift b/WireGuard/Shared/Model/DNSServer.swift index d1c75fc..8703fbb 100644 --- a/WireGuard/Shared/Model/DNSServer.swift +++ b/WireGuard/Shared/Model/DNSServer.swift @@ -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 - } } diff --git a/WireGuard/Shared/Model/Endpoint.swift b/WireGuard/Shared/Model/Endpoint.swift index 3a4beee..891c564 100644 --- a/WireGuard/Shared/Model/Endpoint.swift +++ b/WireGuard/Shared/Model/Endpoint.swift @@ -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 { diff --git a/WireGuard/Shared/Model/IPAddressRange.swift b/WireGuard/Shared/Model/IPAddressRange.swift index 0098c32..da4cbd5 100644 --- a/WireGuard/Shared/Model/IPAddressRange.swift +++ b/WireGuard/Shared/Model/IPAddressRange.swift @@ -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 } diff --git a/WireGuard/Shared/Model/InterfaceConfiguration.swift b/WireGuard/Shared/Model/InterfaceConfiguration.swift new file mode 100644 index 0000000..dfcb1fc --- /dev/null +++ b/WireGuard/Shared/Model/InterfaceConfiguration.swift @@ -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) + } +} diff --git a/WireGuard/Shared/Model/Legacy/LegacyDNSServer.swift b/WireGuard/Shared/Model/Legacy/LegacyDNSServer.swift new file mode 100644 index 0000000..f2afcd7 --- /dev/null +++ b/WireGuard/Shared/Model/Legacy/LegacyDNSServer.swift @@ -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 } + } +} diff --git a/WireGuard/Shared/Model/Legacy/LegacyEndpoint.swift b/WireGuard/Shared/Model/Legacy/LegacyEndpoint.swift new file mode 100644 index 0000000..7a80be4 --- /dev/null +++ b/WireGuard/Shared/Model/Legacy/LegacyEndpoint.swift @@ -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) + } +} diff --git a/WireGuard/Shared/Model/Legacy/LegacyIPAddressRange.swift b/WireGuard/Shared/Model/Legacy/LegacyIPAddressRange.swift new file mode 100644 index 0000000..ade87f2 --- /dev/null +++ b/WireGuard/Shared/Model/Legacy/LegacyIPAddressRange.swift @@ -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 } + } +} diff --git a/WireGuard/Shared/Model/Legacy/LegacyInterfaceConfiguration.swift b/WireGuard/Shared/Model/Legacy/LegacyInterfaceConfiguration.swift new file mode 100644 index 0000000..680c8d7 --- /dev/null +++ b/WireGuard/Shared/Model/Legacy/LegacyInterfaceConfiguration.swift @@ -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 + } +} diff --git a/WireGuard/Shared/Model/Legacy/LegacyPeerConfiguration.swift b/WireGuard/Shared/Model/Legacy/LegacyPeerConfiguration.swift new file mode 100644 index 0000000..5a61b4a --- /dev/null +++ b/WireGuard/Shared/Model/Legacy/LegacyPeerConfiguration.swift @@ -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 } + } +} diff --git a/WireGuard/Shared/Model/Legacy/LegacyTunnelConfiguration.swift b/WireGuard/Shared/Model/Legacy/LegacyTunnelConfiguration.swift new file mode 100644 index 0000000..ac369b9 --- /dev/null +++ b/WireGuard/Shared/Model/Legacy/LegacyTunnelConfiguration.swift @@ -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) + } +} diff --git a/WireGuard/Shared/Model/PeerConfiguration.swift b/WireGuard/Shared/Model/PeerConfiguration.swift new file mode 100644 index 0000000..0fad842 --- /dev/null +++ b/WireGuard/Shared/Model/PeerConfiguration.swift @@ -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) + } + } +} diff --git a/WireGuard/Shared/Model/TunnelConfiguration.swift b/WireGuard/Shared/Model/TunnelConfiguration.swift new file mode 100644 index 0000000..87812cd --- /dev/null +++ b/WireGuard/Shared/Model/TunnelConfiguration.swift @@ -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(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) + } +} diff --git a/WireGuard/Shared/NETunnelProviderProtocol+Extension.swift b/WireGuard/Shared/NETunnelProviderProtocol+Extension.swift index 776e03b..e5cfac8 100644 --- a/WireGuard/Shared/NETunnelProviderProtocol+Extension.swift +++ b/WireGuard/Shared/NETunnelProviderProtocol+Extension.swift @@ -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 + ] + } + } diff --git a/WireGuard/WireGuard.xcodeproj/project.pbxproj b/WireGuard/WireGuard.xcodeproj/project.pbxproj index 24c59c5..782f2bd 100644 --- a/WireGuard/WireGuard.xcodeproj/project.pbxproj +++ b/WireGuard/WireGuard.xcodeproj/project.pbxproj @@ -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 = ""; }; 5F4541AD21C7704300994C13 /* NEVPNStatus+CustomStringConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NEVPNStatus+CustomStringConvertible.swift"; sourceTree = ""; }; 5F4541B121CBFAEE00994C13 /* String+ArrayConversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+ArrayConversion.swift"; sourceTree = ""; }; + 5FF7B96121CC95DE00A7DD74 /* InterfaceConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterfaceConfiguration.swift; sourceTree = ""; }; + 5FF7B96421CC95FA00A7DD74 /* PeerConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeerConfiguration.swift; sourceTree = ""; }; + 5FF7B96821CC967B00A7DD74 /* LegacyInterfaceConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyInterfaceConfiguration.swift; sourceTree = ""; }; + 5FF7B96921CC967B00A7DD74 /* LegacyIPAddressRange.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyIPAddressRange.swift; sourceTree = ""; }; + 5FF7B96A21CC967B00A7DD74 /* LegacyEndpoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyEndpoint.swift; sourceTree = ""; }; + 5FF7B96B21CC967B00A7DD74 /* LegacyDNSServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyDNSServer.swift; sourceTree = ""; }; + 5FF7B96C21CC967B00A7DD74 /* LegacyPeerConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyPeerConfiguration.swift; sourceTree = ""; }; + 5FF7B96D21CC967B00A7DD74 /* LegacyTunnelConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyTunnelConfiguration.swift; sourceTree = ""; }; 6F5A2B4421AFDE020081EDD8 /* FileManager+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+Extension.swift"; sourceTree = ""; }; 6F5D0C1421832391000F85AD /* DNSResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSResolver.swift; sourceTree = ""; }; 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 = ""; }; 6F7774E0217181B1006A79B3 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 6F7774E321718281006A79B3 /* TunnelsListTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TunnelsListTableViewController.swift; sourceTree = ""; }; - 6F7774E72172020C006A79B3 /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = ""; }; + 6F7774E72172020C006A79B3 /* TunnelConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelConfiguration.swift; sourceTree = ""; }; 6F7774E9217229DB006A79B3 /* IPAddressRange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPAddressRange.swift; sourceTree = ""; }; 6F7774EE21722D97006A79B3 /* TunnelsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelsManager.swift; sourceTree = ""; }; 6F7774F221774263006A79B3 /* TunnelEditTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelEditTableViewController.swift; sourceTree = ""; }; @@ -227,6 +251,19 @@ path = ViewController; sourceTree = ""; }; + 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 = ""; + }; 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 = ""; @@ -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 */, diff --git a/WireGuard/WireGuard/ConfigFile/WgQuickConfigFileWriter.swift b/WireGuard/WireGuard/ConfigFile/WgQuickConfigFileWriter.swift index 78c4f8b..2dab266 100644 --- a/WireGuard/WireGuard/ConfigFile/WgQuickConfigFileWriter.swift +++ b/WireGuard/WireGuard/ConfigFile/WgQuickConfigFileWriter.swift @@ -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") diff --git a/WireGuard/WireGuard/Tunnel/TunnelsManager.swift b/WireGuard/WireGuard/Tunnel/TunnelsManager.swift index f6dcc32..b56cb37 100644 --- a/WireGuard/WireGuard/Tunnel/TunnelsManager.swift +++ b/WireGuard/WireGuard/Tunnel/TunnelsManager.swift @@ -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 diff --git a/WireGuard/WireGuard/UI/TunnelViewModel.swift b/WireGuard/WireGuard/UI/TunnelViewModel.swift index f7ebb68..0b5b8c0 100644 --- a/WireGuard/WireGuard/UI/TunnelViewModel.swift +++ b/WireGuard/WireGuard/UI/TunnelViewModel.swift @@ -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) diff --git a/WireGuard/WireGuard/UI/iOS/ViewController/SettingsTableViewController.swift b/WireGuard/WireGuard/UI/iOS/ViewController/SettingsTableViewController.swift index 22edcbc..426e053 100644 --- a/WireGuard/WireGuard/UI/iOS/ViewController/SettingsTableViewController.swift +++ b/WireGuard/WireGuard/UI/iOS/ViewController/SettingsTableViewController.swift @@ -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) diff --git a/WireGuard/WireGuard/UI/iOS/ViewController/TunnelDetailTableViewController.swift b/WireGuard/WireGuard/UI/iOS/ViewController/TunnelDetailTableViewController.swift index 50c0e33..8378311 100644 --- a/WireGuard/WireGuard/UI/iOS/ViewController/TunnelDetailTableViewController.swift +++ b/WireGuard/WireGuard/UI/iOS/ViewController/TunnelDetailTableViewController.swift @@ -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 } diff --git a/WireGuard/WireGuard/UI/iOS/ViewController/TunnelEditTableViewController.swift b/WireGuard/WireGuard/UI/iOS/ViewController/TunnelEditTableViewController.swift index 3d9724c..79dc7b6 100644 --- a/WireGuard/WireGuard/UI/iOS/ViewController/TunnelEditTableViewController.swift +++ b/WireGuard/WireGuard/UI/iOS/ViewController/TunnelEditTableViewController.swift @@ -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() } diff --git a/WireGuard/WireGuardNetworkExtension/PacketTunnelProvider.swift b/WireGuard/WireGuardNetworkExtension/PacketTunnelProvider.swift index 3a9066d..f32a004 100644 --- a/WireGuard/WireGuardNetworkExtension/PacketTunnelProvider.swift +++ b/WireGuard/WireGuardNetworkExtension/PacketTunnelProvider.swift @@ -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 diff --git a/WireGuard/WireGuardNetworkExtension/PacketTunnelSettingsGenerator.swift b/WireGuard/WireGuardNetworkExtension/PacketTunnelSettingsGenerator.swift index fd706d9..462d110 100644 --- a/WireGuard/WireGuardNetworkExtension/PacketTunnelSettingsGenerator.swift +++ b/WireGuard/WireGuardNetworkExtension/PacketTunnelSettingsGenerator.swift @@ -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