Move name from interface to tunnel

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jason A. Donenfeld 2018-12-22 00:28:18 +01:00
parent 9295895e3a
commit 4ed646973e
13 changed files with 41 additions and 35 deletions

View File

@ -96,7 +96,7 @@ struct LegacyInterfaceConfiguration: LegacyModel {
let dns: [LegacyDNSServer] let dns: [LegacyDNSServer]
var migrated: InterfaceConfiguration { var migrated: InterfaceConfiguration {
var interface = InterfaceConfiguration(name: name, privateKey: privateKey) var interface = InterfaceConfiguration(privateKey: privateKey)
interface.addresses = addresses.migrated interface.addresses = addresses.migrated
interface.listenPort = listenPort interface.listenPort = listenPort
interface.mtu = mtu interface.mtu = mtu
@ -167,7 +167,7 @@ final class LegacyTunnelConfiguration: LegacyModel {
let peers: [LegacyPeerConfiguration] let peers: [LegacyPeerConfiguration]
var migrated: TunnelConfiguration { var migrated: TunnelConfiguration {
return TunnelConfiguration(interface: interface.migrated, peers: peers.migrated) return TunnelConfiguration(name: interface.name, interface: interface.migrated, peers: peers.migrated)
} }
} }

View File

@ -4,18 +4,16 @@
import Foundation import Foundation
struct InterfaceConfiguration { struct InterfaceConfiguration {
var name: String?
var privateKey: Data var privateKey: Data
var addresses = [IPAddressRange]() var addresses = [IPAddressRange]()
var listenPort: UInt16? var listenPort: UInt16?
var mtu: UInt16? var mtu: UInt16?
var dns = [DNSServer]() var dns = [DNSServer]()
init(name: String?, privateKey: Data) { init(privateKey: Data) {
self.name = name
self.privateKey = privateKey
if privateKey.count != TunnelConfiguration.keyLength { if privateKey.count != TunnelConfiguration.keyLength {
fatalError("Invalid private key") fatalError("Invalid private key")
} }
self.privateKey = privateKey
} }
} }

View File

@ -4,14 +4,16 @@
import Foundation import Foundation
final class TunnelConfiguration { final class TunnelConfiguration {
var name: String?
var interface: InterfaceConfiguration var interface: InterfaceConfiguration
let peers: [PeerConfiguration] let peers: [PeerConfiguration]
static let keyLength = 32 static let keyLength = 32
init(interface: InterfaceConfiguration, peers: [PeerConfiguration]) { init(name: String?, interface: InterfaceConfiguration, peers: [PeerConfiguration]) {
self.interface = interface self.interface = interface
self.peers = peers self.peers = peers
self.name = name
let peerPublicKeysArray = peers.map { $0.publicKey } let peerPublicKeysArray = peers.map { $0.publicKey }
let peerPublicKeysSet = Set<Data>(peerPublicKeysArray) let peerPublicKeysSet = Set<Data>(peerPublicKeysArray)

View File

@ -27,13 +27,14 @@ extension NETunnelProviderProtocol {
serverAddress = "Multiple endpoints" serverAddress = "Multiple endpoints"
} }
username = tunnelConfiguration.interface.name //TODO(roopc): Why are we doing this? Just for kicks? Is it useful? Seems needless.
username = tunnelConfiguration.name
} }
func tunnelConfiguration(name: String?) -> TunnelConfiguration? { func asTunnelConfiguration(called name: String? = nil) -> TunnelConfiguration? {
migrateConfigurationIfNeeded() migrateConfigurationIfNeeded()
guard let serializedConfig = providerConfiguration?[Keys.wgQuickConfig.rawValue] as? String else { return nil } guard let serializedConfig = providerConfiguration?[Keys.wgQuickConfig.rawValue] as? String else { return nil }
return try? TunnelConfiguration(serializedConfig, name: name) return try? TunnelConfiguration(fromWgQuickConfig: serializedConfig, called: name)
} }
} }

View File

@ -20,8 +20,8 @@ extension TunnelConfiguration {
case invalidPeer case invalidPeer
} }
//swiftlint:disable:next cyclomatic_complexity function_body_length //swiftlint:disable:next function_body_length cyclomatic_complexity
convenience init(_ wgQuickConfig: String, name: String?) throws { convenience init(fromWgQuickConfig wgQuickConfig: String, called name: String? = nil) throws {
var interfaceConfiguration: InterfaceConfiguration? var interfaceConfiguration: InterfaceConfiguration?
var peerConfigurations = [PeerConfiguration]() var peerConfigurations = [PeerConfiguration]()
@ -62,7 +62,7 @@ extension TunnelConfiguration {
if isLastLine || lowercasedLine == "[interface]" || lowercasedLine == "[peer]" { if isLastLine || lowercasedLine == "[interface]" || lowercasedLine == "[peer]" {
// Previous section has ended; process the attributes collected so far // Previous section has ended; process the attributes collected so far
if parserState == .inInterfaceSection { if parserState == .inInterfaceSection {
guard let interface = TunnelConfiguration.collate(interfaceAttributes: attributes, name: name) else { throw ParseError.invalidInterface } guard let interface = TunnelConfiguration.collate(interfaceAttributes: attributes) else { throw ParseError.invalidInterface }
guard interfaceConfiguration == nil else { throw ParseError.multipleInterfaces } guard interfaceConfiguration == nil else { throw ParseError.multipleInterfaces }
interfaceConfiguration = interface interfaceConfiguration = interface
} else if parserState == .inPeerSection { } else if parserState == .inPeerSection {
@ -87,7 +87,7 @@ extension TunnelConfiguration {
} }
if let interfaceConfiguration = interfaceConfiguration { if let interfaceConfiguration = interfaceConfiguration {
self.init(interface: interfaceConfiguration, peers: peerConfigurations) self.init(name: name, interface: interfaceConfiguration, peers: peerConfigurations)
} else { } else {
throw ParseError.noInterface throw ParseError.noInterface
} }
@ -133,11 +133,11 @@ extension TunnelConfiguration {
} }
//swiftlint:disable:next cyclomatic_complexity //swiftlint:disable:next cyclomatic_complexity
private static func collate(interfaceAttributes attributes: [String: String], name: String?) -> InterfaceConfiguration? { private static func collate(interfaceAttributes attributes: [String: String]) -> InterfaceConfiguration? {
// required wg fields // required wg fields
guard let privateKeyString = attributes["privatekey"] else { return nil } guard let privateKeyString = attributes["privatekey"] else { return nil }
guard let privateKey = Data(base64Encoded: privateKeyString), privateKey.count == TunnelConfiguration.keyLength else { return nil } guard let privateKey = Data(base64Encoded: privateKeyString), privateKey.count == TunnelConfiguration.keyLength else { return nil }
var interface = InterfaceConfiguration(name: name, privateKey: privateKey) var interface = InterfaceConfiguration(privateKey: privateKey)
// other wg fields // other wg fields
if let listenPortString = attributes["listenport"] { if let listenPortString = attributes["listenport"] {
guard let listenPort = UInt16(listenPortString) else { return nil } guard let listenPort = UInt16(listenPortString) else { return nil }

View File

@ -54,7 +54,7 @@ class TunnelsManager {
} }
func add(tunnelConfiguration: TunnelConfiguration, activateOnDemandSetting: ActivateOnDemandSetting = ActivateOnDemandSetting.defaultSetting, completionHandler: @escaping (WireGuardResult<TunnelContainer>) -> Void) { func add(tunnelConfiguration: TunnelConfiguration, activateOnDemandSetting: ActivateOnDemandSetting = ActivateOnDemandSetting.defaultSetting, completionHandler: @escaping (WireGuardResult<TunnelContainer>) -> Void) {
let tunnelName = tunnelConfiguration.interface.name ?? "" let tunnelName = tunnelConfiguration.name ?? ""
if tunnelName.isEmpty { if tunnelName.isEmpty {
completionHandler(.failure(TunnelsManagerError.tunnelNameEmpty)) completionHandler(.failure(TunnelsManagerError.tunnelNameEmpty))
return return
@ -67,7 +67,7 @@ class TunnelsManager {
let tunnelProviderManager = NETunnelProviderManager() let tunnelProviderManager = NETunnelProviderManager()
tunnelProviderManager.protocolConfiguration = NETunnelProviderProtocol(tunnelConfiguration: tunnelConfiguration) tunnelProviderManager.protocolConfiguration = NETunnelProviderProtocol(tunnelConfiguration: tunnelConfiguration)
tunnelProviderManager.localizedDescription = tunnelConfiguration.interface.name tunnelProviderManager.localizedDescription = tunnelConfiguration.name
tunnelProviderManager.isEnabled = true tunnelProviderManager.isEnabled = true
activateOnDemandSetting.apply(on: tunnelProviderManager) activateOnDemandSetting.apply(on: tunnelProviderManager)
@ -107,7 +107,7 @@ class TunnelsManager {
} }
func modify(tunnel: TunnelContainer, tunnelConfiguration: TunnelConfiguration, activateOnDemandSetting: ActivateOnDemandSetting, completionHandler: @escaping (TunnelsManagerError?) -> Void) { func modify(tunnel: TunnelContainer, tunnelConfiguration: TunnelConfiguration, activateOnDemandSetting: ActivateOnDemandSetting, completionHandler: @escaping (TunnelsManagerError?) -> Void) {
let tunnelName = tunnelConfiguration.interface.name ?? "" let tunnelName = tunnelConfiguration.name ?? ""
if tunnelName.isEmpty { if tunnelName.isEmpty {
completionHandler(TunnelsManagerError.tunnelNameEmpty) completionHandler(TunnelsManagerError.tunnelNameEmpty)
return return
@ -124,7 +124,7 @@ class TunnelsManager {
} }
tunnelProviderManager.protocolConfiguration = NETunnelProviderProtocol(tunnelConfiguration: tunnelConfiguration) tunnelProviderManager.protocolConfiguration = NETunnelProviderProtocol(tunnelConfiguration: tunnelConfiguration)
tunnelProviderManager.localizedDescription = tunnelConfiguration.interface.name tunnelProviderManager.localizedDescription = tunnelConfiguration.name
tunnelProviderManager.isEnabled = true tunnelProviderManager.isEnabled = true
let isActivatingOnDemand = !tunnelProviderManager.isOnDemandEnabled && activateOnDemandSetting.isActivateOnDemandEnabled let isActivatingOnDemand = !tunnelProviderManager.isOnDemandEnabled && activateOnDemandSetting.isActivateOnDemandEnabled
@ -349,7 +349,7 @@ class TunnelContainer: NSObject {
private var lastTunnelConnectionStatus: NEVPNStatus? private var lastTunnelConnectionStatus: NEVPNStatus?
var tunnelConfiguration: TunnelConfiguration? { var tunnelConfiguration: TunnelConfiguration? {
return (tunnelProvider.protocolConfiguration as? NETunnelProviderProtocol)?.tunnelConfiguration(name: tunnelProvider.localizedDescription) return (tunnelProvider.protocolConfiguration as? NETunnelProviderProtocol)?.asTunnelConfiguration(called: tunnelProvider.localizedDescription)
} }
var activateOnDemandSetting: ActivateOnDemandSetting { var activateOnDemandSetting: ActivateOnDemandSetting {

View File

@ -66,6 +66,7 @@ class TunnelViewModel {
var scratchpad = [InterfaceField: String]() var scratchpad = [InterfaceField: String]()
var fieldsWithError = Set<InterfaceField>() var fieldsWithError = Set<InterfaceField>()
var validatedConfiguration: InterfaceConfiguration? var validatedConfiguration: InterfaceConfiguration?
var validatedName: String?
subscript(field: InterfaceField) -> String { subscript(field: InterfaceField) -> String {
get { get {
@ -83,6 +84,7 @@ class TunnelViewModel {
populateScratchpad() populateScratchpad()
} }
validatedConfiguration = nil validatedConfiguration = nil
validatedName = nil
if stringValue.isEmpty { if stringValue.isEmpty {
scratchpad.removeValue(forKey: field) scratchpad.removeValue(forKey: field)
} else { } else {
@ -102,7 +104,8 @@ class TunnelViewModel {
func populateScratchpad() { func populateScratchpad() {
// Populate the scratchpad from the configuration object // Populate the scratchpad from the configuration object
guard let config = validatedConfiguration else { return } guard let config = validatedConfiguration else { return }
scratchpad[.name] = config.name guard let name = validatedName else { return }
scratchpad[.name] = name
scratchpad[.privateKey] = config.privateKey.base64EncodedString() scratchpad[.privateKey] = config.privateKey.base64EncodedString()
scratchpad[.publicKey] = config.publicKey.base64EncodedString() scratchpad[.publicKey] = config.publicKey.base64EncodedString()
if !config.addresses.isEmpty { if !config.addresses.isEmpty {
@ -120,10 +123,10 @@ class TunnelViewModel {
} }
//swiftlint:disable:next cyclomatic_complexity function_body_length //swiftlint:disable:next cyclomatic_complexity function_body_length
func save() -> SaveResult<InterfaceConfiguration> { func save() -> SaveResult<(String, InterfaceConfiguration)> {
if let validatedConfiguration = validatedConfiguration { if let config = validatedConfiguration, let name = validatedName {
// It's already validated and saved // It's already validated and saved
return .saved(validatedConfiguration) return .saved((name, config))
} }
fieldsWithError.removeAll() fieldsWithError.removeAll()
guard let name = scratchpad[.name]?.trimmingCharacters(in: .whitespacesAndNewlines), (!name.isEmpty) else { guard let name = scratchpad[.name]?.trimmingCharacters(in: .whitespacesAndNewlines), (!name.isEmpty) else {
@ -138,7 +141,7 @@ class TunnelViewModel {
fieldsWithError.insert(.privateKey) fieldsWithError.insert(.privateKey)
return .error(tr("alertInvalidInterfaceMessagePrivateKeyInvalid")) return .error(tr("alertInvalidInterfaceMessagePrivateKeyInvalid"))
} }
var config = InterfaceConfiguration(name: name, privateKey: privateKey) var config = InterfaceConfiguration(privateKey: privateKey)
var errorMessages = [String]() var errorMessages = [String]()
if let addressesString = scratchpad[.addresses] { if let addressesString = scratchpad[.addresses] {
var addresses = [IPAddressRange]() var addresses = [IPAddressRange]()
@ -184,7 +187,8 @@ class TunnelViewModel {
guard errorMessages.isEmpty else { return .error(errorMessages.first!) } guard errorMessages.isEmpty else { return .error(errorMessages.first!) }
validatedConfiguration = config validatedConfiguration = config
return .saved(config) validatedName = name
return .saved((name, config))
} }
func filterFieldsWithValueOrControl(interfaceFields: [InterfaceField]) -> [InterfaceField] { func filterFieldsWithValueOrControl(interfaceFields: [InterfaceField]) -> [InterfaceField] {
@ -390,6 +394,7 @@ class TunnelViewModel {
var peersData = [PeerData]() var peersData = [PeerData]()
if let tunnelConfiguration = tunnelConfiguration { if let tunnelConfiguration = tunnelConfiguration {
interfaceData.validatedConfiguration = tunnelConfiguration.interface interfaceData.validatedConfiguration = tunnelConfiguration.interface
interfaceData.validatedName = tunnelConfiguration.name
for (index, peerConfiguration) in tunnelConfiguration.peers.enumerated() { for (index, peerConfiguration) in tunnelConfiguration.peers.enumerated() {
let peerData = PeerData(index: index) let peerData = PeerData(index: index)
peerData.validatedConfiguration = peerConfiguration peerData.validatedConfiguration = peerConfiguration
@ -453,7 +458,7 @@ class TunnelViewModel {
return .error(tr("alertInvalidPeerMessagePublicKeyDuplicated")) return .error(tr("alertInvalidPeerMessagePublicKeyDuplicated"))
} }
let tunnelConfiguration = TunnelConfiguration(interface: interfaceConfiguration, peers: peerConfigurations) let tunnelConfiguration = TunnelConfiguration(name: interfaceConfiguration.0, interface: interfaceConfiguration.1, peers: peerConfigurations)
return .saved(tunnelConfiguration) return .saved(tunnelConfiguration)
} }
} }

View File

@ -101,7 +101,7 @@ class QRScanViewController: UIViewController {
} }
func scanDidComplete(withCode code: String) { func scanDidComplete(withCode code: String) {
let scannedTunnelConfiguration = try? TunnelConfiguration(code, name: "Scanned") let scannedTunnelConfiguration = try? TunnelConfiguration(fromWgQuickConfig: code, called: "Scanned")
guard let tunnelConfiguration = scannedTunnelConfiguration else { guard let tunnelConfiguration = scannedTunnelConfiguration else {
scanDidEncounterError(title: tr("alertScanQRCodeInvalidQRCodeTitle"), message: tr("alertScanQRCodeInvalidQRCodeMessage")) scanDidEncounterError(title: tr("alertScanQRCodeInvalidQRCodeTitle"), message: tr("alertScanQRCodeInvalidQRCodeMessage"))
return return
@ -114,7 +114,7 @@ class QRScanViewController: UIViewController {
}) })
alert.addAction(UIAlertAction(title: tr("actionSave"), style: .default) { [weak self] _ in alert.addAction(UIAlertAction(title: tr("actionSave"), style: .default) { [weak self] _ in
guard let title = alert.textFields?[0].text?.trimmingCharacters(in: .whitespacesAndNewlines), !title.isEmpty else { return } guard let title = alert.textFields?[0].text?.trimmingCharacters(in: .whitespacesAndNewlines), !title.isEmpty else { return }
tunnelConfiguration.interface.name = title tunnelConfiguration.name = title
if let self = self { if let self = self {
self.delegate?.addScannedQRCode(tunnelConfiguration: tunnelConfiguration, qrScanViewController: self) { self.delegate?.addScannedQRCode(tunnelConfiguration: tunnelConfiguration, qrScanViewController: self) {
self.dismiss(animated: true, completion: nil) self.dismiss(animated: true, completion: nil)

View File

@ -98,7 +98,7 @@ class TunnelEditTableViewController: UITableViewController {
let tunnelSaveResult = tunnelViewModel.save() let tunnelSaveResult = tunnelViewModel.save()
switch tunnelSaveResult { switch tunnelSaveResult {
case .error(let errorMessage): case .error(let errorMessage):
let alertTitle = (tunnelViewModel.interfaceData.validatedConfiguration == nil) ? let alertTitle = (tunnelViewModel.interfaceData.validatedConfiguration == nil || tunnelViewModel.interfaceData.validatedName == nil) ?
tr("alertInvalidInterfaceTitle") : tr("alertInvalidPeerTitle") tr("alertInvalidInterfaceTitle") : tr("alertInvalidPeerTitle")
ErrorPresenter.showErrorAlert(title: alertTitle, message: errorMessage, from: self) ErrorPresenter.showErrorAlert(title: alertTitle, message: errorMessage, from: self)
tableView.reloadData() // Highlight erroring fields tableView.reloadData() // Highlight erroring fields

View File

@ -180,7 +180,7 @@ class TunnelsListTableViewController: UIViewController {
} else /* if (url.pathExtension == "conf") -- we assume everything else is a conf */ { } else /* if (url.pathExtension == "conf") -- we assume everything else is a conf */ {
let fileBaseName = url.deletingPathExtension().lastPathComponent.trimmingCharacters(in: .whitespacesAndNewlines) let fileBaseName = url.deletingPathExtension().lastPathComponent.trimmingCharacters(in: .whitespacesAndNewlines)
if let fileContents = try? String(contentsOf: url), if let fileContents = try? String(contentsOf: url),
let tunnelConfiguration = try? TunnelConfiguration(fileContents, name: fileBaseName) { let tunnelConfiguration = try? TunnelConfiguration(fromWgQuickConfig: fileContents, called: fileBaseName) {
tunnelsManager.add(tunnelConfiguration: tunnelConfiguration) { [weak self] result in tunnelsManager.add(tunnelConfiguration: tunnelConfiguration) { [weak self] result in
if let error = result.error { if let error = result.error {
ErrorPresenter.showErrorAlert(error: error, from: self, onPresented: completionHandler) ErrorPresenter.showErrorAlert(error: error, from: self, onPresented: completionHandler)

View File

@ -23,7 +23,7 @@ class ZipExporter {
var lastTunnelName: String = "" var lastTunnelName: String = ""
for tunnelConfiguration in tunnelConfigurations { for tunnelConfiguration in tunnelConfigurations {
if let contents = tunnelConfiguration.asWgQuickConfig().data(using: .utf8) { if let contents = tunnelConfiguration.asWgQuickConfig().data(using: .utf8) {
let name = tunnelConfiguration.interface.name ?? "" let name = tunnelConfiguration.name ?? "untitled"
if name.isEmpty || name == lastTunnelName { continue } if name.isEmpty || name == lastTunnelName { continue }
inputsToArchiver.append((fileName: "\(name).conf", contents: contents)) inputsToArchiver.append((fileName: "\(name).conf", contents: contents))
lastTunnelName = name lastTunnelName = name

View File

@ -44,7 +44,7 @@ class ZipImporter {
continue continue
} }
guard let fileContents = String(data: file.contents, encoding: .utf8) else { continue } guard let fileContents = String(data: file.contents, encoding: .utf8) else { continue }
guard let tunnelConfig = try? TunnelConfiguration(fileContents, name: file.fileBaseName) else { continue } guard let tunnelConfig = try? TunnelConfiguration(fromWgQuickConfig: fileContents, called: file.fileBaseName) else { continue }
configs[index] = tunnelConfig configs[index] = tunnelConfig
} }
DispatchQueue.main.async { completion(.success(configs)) } DispatchQueue.main.async { completion(.success(configs)) }

View File

@ -29,7 +29,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
let errorNotifier = ErrorNotifier(activationAttemptId: activationAttemptId, tunnelProvider: self) let errorNotifier = ErrorNotifier(activationAttemptId: activationAttemptId, tunnelProvider: self)
guard let tunnelProviderProtocol = protocolConfiguration as? NETunnelProviderProtocol, guard let tunnelProviderProtocol = protocolConfiguration as? NETunnelProviderProtocol,
let tunnelConfiguration = tunnelProviderProtocol.tunnelConfiguration(name: nil) else { let tunnelConfiguration = tunnelProviderProtocol.asTunnelConfiguration() else {
errorNotifier.notify(PacketTunnelProviderError.savedProtocolConfigurationIsInvalid) errorNotifier.notify(PacketTunnelProviderError.savedProtocolConfigurationIsInvalid)
startTunnelCompletionHandler(PacketTunnelProviderError.savedProtocolConfigurationIsInvalid) startTunnelCompletionHandler(PacketTunnelProviderError.savedProtocolConfigurationIsInvalid)
return return