Updated NETunnelProvider save format

Signed-off-by: Eric Kuck <eric@bluelinelabs.com>
This commit is contained in:
Eric Kuck
2018-12-20 22:52:45 -06:00
committed by Jason A. Donenfeld
parent 38445114e0
commit 8553723e04
23 changed files with 664 additions and 224 deletions
@@ -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")
}
}
}
+31 -33
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
}
}
+35 -33
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 {
+27 -36
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
}
@@ -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)
}
}
@@ -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 }
}
}
@@ -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)
}
}
@@ -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 }
}
}
@@ -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
}
}
@@ -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 }
}
}
@@ -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)
}
}
@@ -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)
}
}
}
@@ -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)
}
}