Tons more swiftlint warnings fixed. Still a few remaining.

This commit is contained in:
Eric Kuck 2018-12-12 12:28:27 -06:00
parent fa558a4019
commit ea29a0c7d1
21 changed files with 222 additions and 237 deletions

View File

@ -17,15 +17,15 @@ extension Endpoint {
init?(from string: String) { init?(from string: String) {
// Separation of host and port is based on 'parse_endpoint' function in // Separation of host and port is based on 'parse_endpoint' function in
// https://git.zx2c4.com/WireGuard/tree/src/tools/config.c // https://git.zx2c4.com/WireGuard/tree/src/tools/config.c
guard (!string.isEmpty) else { return nil } guard !string.isEmpty else { return nil }
let startOfPort: String.Index let startOfPort: String.Index
let hostString: String let hostString: String
if (string.first! == "[") { if string.first! == "[" {
// Look for IPv6-style endpoint, like [::1]:80 // Look for IPv6-style endpoint, like [::1]:80
let startOfHost = string.index(after: string.startIndex) let startOfHost = string.index(after: string.startIndex)
guard let endOfHost = string.dropFirst().firstIndex(of: "]") else { return nil } guard let endOfHost = string.dropFirst().firstIndex(of: "]") else { return nil }
let afterEndOfHost = string.index(after: endOfHost) let afterEndOfHost = string.index(after: endOfHost)
guard (string[afterEndOfHost] == ":") else { return nil } guard string[afterEndOfHost] == ":" else { return nil }
startOfPort = string.index(after: afterEndOfHost) startOfPort = string.index(after: afterEndOfHost)
hostString = String(string[startOfHost ..< endOfHost]) hostString = String(string[startOfHost ..< endOfHost])
} else { } else {
@ -38,12 +38,12 @@ extension Endpoint {
let invalidCharacterIndex = hostString.unicodeScalars.firstIndex { char in let invalidCharacterIndex = hostString.unicodeScalars.firstIndex { char in
return !CharacterSet.urlHostAllowed.contains(char) return !CharacterSet.urlHostAllowed.contains(char)
} }
guard (invalidCharacterIndex == nil) else { return nil } guard invalidCharacterIndex == nil else { return nil }
host = NWEndpoint.Host(hostString) host = NWEndpoint.Host(hostString)
port = endpointPort port = endpointPort
} }
func stringRepresentation() -> String { func stringRepresentation() -> String {
switch (host) { switch host {
case .name(let hostname, _): case .name(let hostname, _):
return "\(hostname):\(port)" return "\(hostname):\(port)"
case .ipv4(let address): case .ipv4(let address):
@ -78,7 +78,7 @@ extension Endpoint: Codable {
extension Endpoint { extension Endpoint {
func hasHostAsIPAddress() -> Bool { func hasHostAsIPAddress() -> Bool {
switch (host) { switch host {
case .name: case .name:
return false return false
case .ipv4: case .ipv4:
@ -89,7 +89,7 @@ extension Endpoint {
} }
func hostname() -> String? { func hostname() -> String? {
switch (host) { switch host {
case .name(let hostname, _): case .name(let hostname, _):
return hostname return hostname
case .ipv4: case .ipv4:

View File

@ -122,29 +122,29 @@ class WgQuickConfigFileParser {
attributes[key] = value attributes[key] = value
} }
} else { } else {
if (lowercasedLine != "[interface]" && lowercasedLine != "[peer]") { if lowercasedLine != "[interface]" && lowercasedLine != "[peer]" {
throw ParseError.invalidLine(line) throw ParseError.invalidLine(line)
} }
} }
let isLastLine: Bool = (lineIndex == lines.count - 1) let isLastLine: Bool = (lineIndex == lines.count - 1)
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 = collate(interfaceAttributes: attributes) else { throw ParseError.invalidInterface } guard let interface = 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 {
guard let peer = collate(peerAttributes: attributes) else { throw ParseError.invalidPeer } guard let peer = collate(peerAttributes: attributes) else { throw ParseError.invalidPeer }
peerConfigurations.append(peer) peerConfigurations.append(peer)
} }
} }
if (lowercasedLine == "[interface]") { if lowercasedLine == "[interface]" {
parserState = .inInterfaceSection parserState = .inInterfaceSection
attributes.removeAll() attributes.removeAll()
} else if (lowercasedLine == "[peer]") { } else if lowercasedLine == "[peer]" {
parserState = .inPeerSection parserState = .inPeerSection
attributes.removeAll() attributes.removeAll()
} }
@ -152,7 +152,7 @@ class WgQuickConfigFileParser {
let peerPublicKeysArray = peerConfigurations.map { $0.publicKey } let peerPublicKeysArray = peerConfigurations.map { $0.publicKey }
let peerPublicKeysSet = Set<Data>(peerPublicKeysArray) let peerPublicKeysSet = Set<Data>(peerPublicKeysArray)
if (peerPublicKeysArray.count != peerPublicKeysSet.count) { if peerPublicKeysArray.count != peerPublicKeysSet.count {
throw ParseError.multiplePeersWithSamePublicKey throw ParseError.multiplePeersWithSamePublicKey
} }

View File

@ -4,18 +4,18 @@
import UIKit import UIKit
class WgQuickConfigFileWriter { class WgQuickConfigFileWriter {
static func writeConfigFile(from tc: TunnelConfiguration) -> Data? { static func writeConfigFile(from configuration: TunnelConfiguration) -> Data? {
let interface = tc.interface let interface = configuration.interface
var output = "[Interface]\n" var output = "[Interface]\n"
output.append("PrivateKey = \(interface.privateKey.base64EncodedString())\n") output.append("PrivateKey = \(interface.privateKey.base64EncodedString())\n")
if let listenPort = interface.listenPort { if let listenPort = interface.listenPort {
output.append("ListenPort = \(listenPort)\n") output.append("ListenPort = \(listenPort)\n")
} }
if (!interface.addresses.isEmpty) { 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") output.append("Address = \(addressString)\n")
} }
if (!interface.dns.isEmpty) { 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") output.append("DNS = \(dnsString)\n")
} }
@ -23,13 +23,13 @@ class WgQuickConfigFileWriter {
output.append("MTU = \(mtu)\n") output.append("MTU = \(mtu)\n")
} }
for peer in tc.peers { for peer in configuration.peers {
output.append("\n[Peer]\n") output.append("\n[Peer]\n")
output.append("PublicKey = \(peer.publicKey.base64EncodedString())\n") output.append("PublicKey = \(peer.publicKey.base64EncodedString())\n")
if let preSharedKey = peer.preSharedKey { if let preSharedKey = peer.preSharedKey {
output.append("PresharedKey = \(preSharedKey.base64EncodedString())\n") output.append("PresharedKey = \(preSharedKey.base64EncodedString())\n")
} }
if (!peer.allowedIPs.isEmpty) { 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") output.append("AllowedIPs = \(allowedIPsString)\n")
} }

View File

@ -43,7 +43,7 @@ class TunnelViewModel {
subscript(field: InterfaceField) -> String { subscript(field: InterfaceField) -> String {
get { get {
if (scratchpad.isEmpty) { if scratchpad.isEmpty {
// When starting to read a config, setup the scratchpad. // When starting to read a config, setup the scratchpad.
// The scratchpad shall serve as a cache of what we want to show in the UI. // The scratchpad shall serve as a cache of what we want to show in the UI.
populateScratchpad() populateScratchpad()
@ -51,18 +51,18 @@ class TunnelViewModel {
return scratchpad[field] ?? "" return scratchpad[field] ?? ""
} }
set(stringValue) { set(stringValue) {
if (scratchpad.isEmpty) { if scratchpad.isEmpty {
// When starting to edit a config, setup the scratchpad and remove the configuration. // When starting to edit a config, setup the scratchpad and remove the configuration.
// The scratchpad shall be the sole source of the being-edited configuration. // The scratchpad shall be the sole source of the being-edited configuration.
populateScratchpad() populateScratchpad()
} }
validatedConfiguration = nil validatedConfiguration = nil
if (stringValue.isEmpty) { if stringValue.isEmpty {
scratchpad.removeValue(forKey: field) scratchpad.removeValue(forKey: field)
} else { } else {
scratchpad[field] = stringValue scratchpad[field] = stringValue
} }
if (field == .privateKey) { if field == .privateKey {
if (stringValue.count == TunnelViewModel.keyLengthInBase64), if (stringValue.count == TunnelViewModel.keyLengthInBase64),
let privateKey = Data(base64Encoded: stringValue), let privateKey = Data(base64Encoded: stringValue),
privateKey.count == TunnelConfiguration.keyLength { privateKey.count == TunnelConfiguration.keyLength {
@ -81,7 +81,7 @@ class TunnelViewModel {
scratchpad[.name] = config.name scratchpad[.name] = config.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 {
scratchpad[.addresses] = config.addresses.map { $0.stringRepresentation() }.joined(separator: ", ") scratchpad[.addresses] = config.addresses.map { $0.stringRepresentation() }.joined(separator: ", ")
} }
if let listenPort = config.listenPort { if let listenPort = config.listenPort {
@ -90,7 +90,7 @@ class TunnelViewModel {
if let mtu = config.mtu { if let mtu = config.mtu {
scratchpad[.mtu] = String(mtu) scratchpad[.mtu] = String(mtu)
} }
if (!config.dns.isEmpty) { if !config.dns.isEmpty {
scratchpad[.dns] = config.dns.map { $0.stringRepresentation() }.joined(separator: ", ") scratchpad[.dns] = config.dns.map { $0.stringRepresentation() }.joined(separator: ", ")
} }
} }
@ -158,16 +158,15 @@ class TunnelViewModel {
config.dns = dnsServers config.dns = dnsServers
} }
guard (errorMessages.isEmpty) else { guard errorMessages.isEmpty else { return .error(errorMessages.first!) }
return .error(errorMessages.first!)
}
validatedConfiguration = config validatedConfiguration = config
return .saved(config) return .saved(config)
} }
func filterFieldsWithValueOrControl(interfaceFields: [InterfaceField]) -> [InterfaceField] { func filterFieldsWithValueOrControl(interfaceFields: [InterfaceField]) -> [InterfaceField] {
return interfaceFields.filter { (field) -> Bool in return interfaceFields.filter { (field) -> Bool in
if (TunnelViewModel.interfaceFieldsWithControl.contains(field)) { if TunnelViewModel.interfaceFieldsWithControl.contains(field) {
return true return true
} }
return (!self[field].isEmpty) return (!self[field].isEmpty)
@ -193,7 +192,7 @@ class TunnelViewModel {
subscript(field: PeerField) -> String { subscript(field: PeerField) -> String {
get { get {
if (scratchpad.isEmpty) { if scratchpad.isEmpty {
// When starting to read a config, setup the scratchpad. // When starting to read a config, setup the scratchpad.
// The scratchpad shall serve as a cache of what we want to show in the UI. // The scratchpad shall serve as a cache of what we want to show in the UI.
populateScratchpad() populateScratchpad()
@ -201,18 +200,18 @@ class TunnelViewModel {
return scratchpad[field] ?? "" return scratchpad[field] ?? ""
} }
set(stringValue) { set(stringValue) {
if (scratchpad.isEmpty) { if scratchpad.isEmpty {
// When starting to edit a config, setup the scratchpad and remove the configuration. // When starting to edit a config, setup the scratchpad and remove the configuration.
// The scratchpad shall be the sole source of the being-edited configuration. // The scratchpad shall be the sole source of the being-edited configuration.
populateScratchpad() populateScratchpad()
} }
validatedConfiguration = nil validatedConfiguration = nil
if (stringValue.isEmpty) { if stringValue.isEmpty {
scratchpad.removeValue(forKey: field) scratchpad.removeValue(forKey: field)
} else { } else {
scratchpad[field] = stringValue scratchpad[field] = stringValue
} }
if (field == .allowedIPs) { if field == .allowedIPs {
updateExcludePrivateIPsFieldState() updateExcludePrivateIPsFieldState()
} }
} }
@ -225,7 +224,7 @@ class TunnelViewModel {
if let preSharedKey = config.preSharedKey { if let preSharedKey = config.preSharedKey {
scratchpad[.preSharedKey] = preSharedKey.base64EncodedString() scratchpad[.preSharedKey] = preSharedKey.base64EncodedString()
} }
if (!config.allowedIPs.isEmpty) { 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 { if let endpoint = config.endpoint {
@ -291,16 +290,15 @@ class TunnelViewModel {
} }
} }
guard (errorMessages.isEmpty) else { guard errorMessages.isEmpty else { return .error(errorMessages.first!) }
return .error(errorMessages.first!)
}
validatedConfiguration = config validatedConfiguration = config
return .saved(config) return .saved(config)
} }
func filterFieldsWithValueOrControl(peerFields: [PeerField]) -> [PeerField] { func filterFieldsWithValueOrControl(peerFields: [PeerField]) -> [PeerField] {
return peerFields.filter { (field) -> Bool in return peerFields.filter { (field) -> Bool in
if (TunnelViewModel.peerFieldsWithControl.contains(field)) { if TunnelViewModel.peerFieldsWithControl.contains(field) {
return true return true
} }
return (!self[field].isEmpty) return (!self[field].isEmpty)
@ -319,12 +317,12 @@ class TunnelViewModel {
] ]
func updateExcludePrivateIPsFieldState() { func updateExcludePrivateIPsFieldState() {
guard (numberOfPeers == 1) else { guard numberOfPeers == 1 else {
shouldAllowExcludePrivateIPsControl = false shouldAllowExcludePrivateIPsControl = false
excludePrivateIPsValue = false excludePrivateIPsValue = false
return return
} }
if (scratchpad.isEmpty) { if scratchpad.isEmpty {
populateScratchpad() populateScratchpad()
} }
let allowedIPStrings = Set<String>( let allowedIPStrings = Set<String>(
@ -332,10 +330,10 @@ class TunnelViewModel {
.split(separator: ",") .split(separator: ",")
.map { $0.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) } .map { $0.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) }
) )
if (allowedIPStrings.contains(TunnelViewModel.PeerData.ipv4DefaultRouteString)) { if allowedIPStrings.contains(TunnelViewModel.PeerData.ipv4DefaultRouteString) {
shouldAllowExcludePrivateIPsControl = true shouldAllowExcludePrivateIPsControl = true
excludePrivateIPsValue = false excludePrivateIPsValue = false
} else if (allowedIPStrings.isSuperset(of: TunnelViewModel.PeerData.ipv4DefaultRouteModRFC1918String)) { } else if allowedIPStrings.isSuperset(of: TunnelViewModel.PeerData.ipv4DefaultRouteModRFC1918String) {
shouldAllowExcludePrivateIPsControl = true shouldAllowExcludePrivateIPsControl = true
excludePrivateIPsValue = true excludePrivateIPsValue = true
} else { } else {
@ -353,7 +351,7 @@ class TunnelViewModel {
.map { $0.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) } .map { $0.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) }
let ipv6Addresses = allowedIPStrings.filter { $0.contains(":") } let ipv6Addresses = allowedIPStrings.filter { $0.contains(":") }
let modifiedAllowedIPStrings: [String] let modifiedAllowedIPStrings: [String]
if (isOn) { if isOn {
modifiedAllowedIPStrings = ipv6Addresses + modifiedAllowedIPStrings = ipv6Addresses +
TunnelViewModel.PeerData.ipv4DefaultRouteModRFC1918String + dnsServerStrings TunnelViewModel.PeerData.ipv4DefaultRouteModRFC1918String + dnsServerStrings
} else { } else {
@ -421,14 +419,14 @@ class TunnelViewModel {
let interfaceSaveResult = interfaceData.save() let interfaceSaveResult = interfaceData.save()
let peerSaveResults = peersData.map { $0.save() } let peerSaveResults = peersData.map { $0.save() }
// Collate the results // Collate the results
switch (interfaceSaveResult) { switch interfaceSaveResult {
case .error(let errorMessage): case .error(let errorMessage):
return .error(errorMessage) return .error(errorMessage)
case .saved(let interfaceConfiguration): case .saved(let interfaceConfiguration):
var peerConfigurations: [PeerConfiguration] = [] var peerConfigurations: [PeerConfiguration] = []
peerConfigurations.reserveCapacity(peerSaveResults.count) peerConfigurations.reserveCapacity(peerSaveResults.count)
for peerSaveResult in peerSaveResults { for peerSaveResult in peerSaveResults {
switch (peerSaveResult) { switch peerSaveResult {
case .error(let errorMessage): case .error(let errorMessage):
return .error(errorMessage) return .error(errorMessage)
case .saved(let peerConfiguration): case .saved(let peerConfiguration):
@ -438,7 +436,7 @@ class TunnelViewModel {
let peerPublicKeysArray = peerConfigurations.map { $0.publicKey } let peerPublicKeysArray = peerConfigurations.map { $0.publicKey }
let peerPublicKeysSet = Set<Data>(peerPublicKeysArray) let peerPublicKeysSet = Set<Data>(peerPublicKeysArray)
if (peerPublicKeysArray.count != peerPublicKeysSet.count) { if peerPublicKeysArray.count != peerPublicKeysSet.count {
return .error("Two or more peers cannot have the same public key") return .error("Two or more peers cannot have the same public key")
} }
@ -452,7 +450,7 @@ class TunnelViewModel {
extension TunnelViewModel { extension TunnelViewModel {
static func activateOnDemandOptionText(for activateOnDemandOption: ActivateOnDemandOption) -> String { static func activateOnDemandOptionText(for activateOnDemandOption: ActivateOnDemandOption) -> String {
switch (activateOnDemandOption) { switch activateOnDemandOption {
case .none: case .none:
return "Off" return "Off"
case .useOnDemandOverWiFiOrCellular: case .useOnDemandOverWiFiOrCellular:
@ -466,7 +464,7 @@ extension TunnelViewModel {
static func activateOnDemandDetailText(for activateOnDemandSetting: ActivateOnDemandSetting?) -> String { static func activateOnDemandDetailText(for activateOnDemandSetting: ActivateOnDemandSetting?) -> String {
if let activateOnDemandSetting = activateOnDemandSetting { if let activateOnDemandSetting = activateOnDemandSetting {
if (activateOnDemandSetting.isActivateOnDemandEnabled) { if activateOnDemandSetting.isActivateOnDemandEnabled {
return TunnelViewModel.activateOnDemandOptionText(for: activateOnDemandSetting.activateOnDemandOption) return TunnelViewModel.activateOnDemandOptionText(for: activateOnDemandSetting.activateOnDemandOption)
} else { } else {
return TunnelViewModel.activateOnDemandOptionText(for: .none) return TunnelViewModel.activateOnDemandOptionText(for: .none)

View File

@ -53,7 +53,7 @@ extension AppDelegate {
viewControllerWithRestorationIdentifierPath identifierComponents: [String], viewControllerWithRestorationIdentifierPath identifierComponents: [String],
coder: NSCoder) -> UIViewController? { coder: NSCoder) -> UIViewController? {
guard let vcIdentifier = identifierComponents.last else { return nil } guard let vcIdentifier = identifierComponents.last else { return nil }
if (vcIdentifier.hasPrefix("TunnelDetailVC:")) { if vcIdentifier.hasPrefix("TunnelDetailVC:") {
let tunnelName = String(vcIdentifier.suffix(vcIdentifier.count - "TunnelDetailVC:".count)) let tunnelName = String(vcIdentifier.suffix(vcIdentifier.count - "TunnelDetailVC:".count))
if let tunnelsManager = mainVC?.tunnelsManager { if let tunnelsManager = mainVC?.tunnelsManager {
if let tunnel = tunnelsManager.tunnel(named: tunnelName) { if let tunnel = tunnelsManager.tunnel(named: tunnelName) {

View File

@ -81,7 +81,7 @@ extension MainViewController {
let tunnelDetailNC = UINavigationController(rootViewController: tunnelDetailVC) let tunnelDetailNC = UINavigationController(rootViewController: tunnelDetailVC)
tunnelDetailNC.restorationIdentifier = "DetailNC" tunnelDetailNC.restorationIdentifier = "DetailNC"
if let self = self { if let self = self {
if (animated) { if animated {
self.showDetailViewController(tunnelDetailNC, sender: self) self.showDetailViewController(tunnelDetailNC, sender: self)
} else { } else {
UIView.performWithoutAnimation { UIView.performWithoutAnimation {

View File

@ -40,8 +40,8 @@ class SettingsTableViewController: UITableViewController {
self.tableView.rowHeight = UITableView.automaticDimension self.tableView.rowHeight = UITableView.automaticDimension
self.tableView.allowsSelection = false self.tableView.allowsSelection = false
self.tableView.register(TunnelSettingsTableViewKeyValueCell.self, forCellReuseIdentifier: TunnelSettingsTableViewKeyValueCell.id) self.tableView.register(TunnelSettingsTableViewKeyValueCell.self, forCellReuseIdentifier: TunnelSettingsTableViewKeyValueCell.reuseIdentifier)
self.tableView.register(TunnelSettingsTableViewButtonCell.self, forCellReuseIdentifier: TunnelSettingsTableViewButtonCell.id) self.tableView.register(TunnelSettingsTableViewButtonCell.self, forCellReuseIdentifier: TunnelSettingsTableViewButtonCell.reuseIdentifier)
let logo = UIImageView(image: UIImage(named: "wireguard.pdf", in: Bundle.main, compatibleWith: nil)!) let logo = UIImageView(image: UIImage(named: "wireguard.pdf", in: Bundle.main, compatibleWith: nil)!)
logo.contentMode = .scaleAspectFit logo.contentMode = .scaleAspectFit
@ -99,9 +99,9 @@ class SettingsTableViewController: UITableViewController {
DispatchQueue.global(qos: .userInitiated).async { DispatchQueue.global(qos: .userInitiated).async {
if (FileManager.default.fileExists(atPath: destinationURL.path)) { if FileManager.default.fileExists(atPath: destinationURL.path) {
let isDeleted = FileManager.deleteFile(at: destinationURL) let isDeleted = FileManager.deleteFile(at: destinationURL)
if (!isDeleted) { if !isDeleted {
ErrorPresenter.showErrorAlert(title: "No log available", message: "The pre-existing log could not be cleared", from: self) ErrorPresenter.showErrorAlert(title: "No log available", message: "The pre-existing log could not be cleared", from: self)
return return
} }
@ -149,7 +149,7 @@ extension SettingsTableViewController {
} }
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch (section) { switch section {
case 0: case 0:
return "About" return "About"
case 1: case 1:
@ -163,21 +163,21 @@ extension SettingsTableViewController {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let field = settingsFieldsBySection[indexPath.section][indexPath.row] let field = settingsFieldsBySection[indexPath.section][indexPath.row]
if (field == .iosAppVersion || field == .goBackendVersion) { if field == .iosAppVersion || field == .goBackendVersion {
let cell = tableView.dequeueReusableCell(withIdentifier: TunnelSettingsTableViewKeyValueCell.id, for: indexPath) as! TunnelSettingsTableViewKeyValueCell let cell = tableView.dequeueReusableCell(withIdentifier: TunnelSettingsTableViewKeyValueCell.reuseIdentifier, for: indexPath) as! TunnelSettingsTableViewKeyValueCell
cell.key = field.rawValue cell.key = field.rawValue
if (field == .iosAppVersion) { if field == .iosAppVersion {
var appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown version" var appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown version"
if let appBuild = Bundle.main.infoDictionary?["CFBundleVersion"] as? String { if let appBuild = Bundle.main.infoDictionary?["CFBundleVersion"] as? String {
appVersion += " (\(appBuild))" appVersion += " (\(appBuild))"
} }
cell.value = appVersion cell.value = appVersion
} else if (field == .goBackendVersion) { } else if field == .goBackendVersion {
cell.value = WIREGUARD_GO_VERSION cell.value = WIREGUARD_GO_VERSION
} }
return cell return cell
} else if (field == .exportZipArchive) { } else if field == .exportZipArchive {
let cell = tableView.dequeueReusableCell(withIdentifier: TunnelSettingsTableViewButtonCell.id, for: indexPath) as! TunnelSettingsTableViewButtonCell let cell = tableView.dequeueReusableCell(withIdentifier: TunnelSettingsTableViewButtonCell.reuseIdentifier, for: indexPath) as! TunnelSettingsTableViewButtonCell
cell.buttonText = field.rawValue cell.buttonText = field.rawValue
cell.onTapped = { [weak self] in cell.onTapped = { [weak self] in
self?.exportConfigurationsAsZipFile(sourceView: cell.button) self?.exportConfigurationsAsZipFile(sourceView: cell.button)
@ -185,7 +185,7 @@ extension SettingsTableViewController {
return cell return cell
} else { } else {
assert(field == .exportLogFile) assert(field == .exportLogFile)
let cell = tableView.dequeueReusableCell(withIdentifier: TunnelSettingsTableViewButtonCell.id, for: indexPath) as! TunnelSettingsTableViewButtonCell let cell = tableView.dequeueReusableCell(withIdentifier: TunnelSettingsTableViewButtonCell.reuseIdentifier, for: indexPath) as! TunnelSettingsTableViewButtonCell
cell.buttonText = field.rawValue cell.buttonText = field.rawValue
cell.onTapped = { [weak self] in cell.onTapped = { [weak self] in
self?.exportLogForLastActivatedTunnel(sourceView: cell.button) self?.exportLogForLastActivatedTunnel(sourceView: cell.button)
@ -196,7 +196,7 @@ extension SettingsTableViewController {
} }
class TunnelSettingsTableViewKeyValueCell: UITableViewCell { class TunnelSettingsTableViewKeyValueCell: UITableViewCell {
static let id: String = "TunnelSettingsTableViewKeyValueCell" static let reuseIdentifier = "TunnelSettingsTableViewKeyValueCell"
var key: String { var key: String {
get { return textLabel?.text ?? "" } get { return textLabel?.text ?? "" }
set(value) { textLabel?.text = value } set(value) { textLabel?.text = value }
@ -207,7 +207,7 @@ class TunnelSettingsTableViewKeyValueCell: UITableViewCell {
} }
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .value1, reuseIdentifier: TunnelSettingsTableViewKeyValueCell.id) super.init(style: .value1, reuseIdentifier: TunnelSettingsTableViewKeyValueCell.reuseIdentifier)
} }
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
@ -222,7 +222,7 @@ class TunnelSettingsTableViewKeyValueCell: UITableViewCell {
} }
class TunnelSettingsTableViewButtonCell: UITableViewCell { class TunnelSettingsTableViewButtonCell: UITableViewCell {
static let id: String = "TunnelSettingsTableViewButtonCell" static let reuseIdentifier = "TunnelSettingsTableViewButtonCell"
var buttonText: String { var buttonText: String {
get { return button.title(for: .normal) ?? "" } get { return button.title(for: .normal) ?? "" }
set(value) { button.setTitle(value, for: .normal) } set(value) { button.setTitle(value, for: .normal) }

View File

@ -40,10 +40,10 @@ class TunnelDetailTableViewController: UITableViewController {
self.tableView.estimatedRowHeight = 44 self.tableView.estimatedRowHeight = 44
self.tableView.rowHeight = UITableView.automaticDimension self.tableView.rowHeight = UITableView.automaticDimension
self.tableView.allowsSelection = false self.tableView.allowsSelection = false
self.tableView.register(TunnelDetailTableViewStatusCell.self, forCellReuseIdentifier: TunnelDetailTableViewStatusCell.id) self.tableView.register(TunnelDetailTableViewStatusCell.self, forCellReuseIdentifier: TunnelDetailTableViewStatusCell.reuseIdentifier)
self.tableView.register(TunnelDetailTableViewKeyValueCell.self, forCellReuseIdentifier: TunnelDetailTableViewKeyValueCell.id) self.tableView.register(TunnelDetailTableViewKeyValueCell.self, forCellReuseIdentifier: TunnelDetailTableViewKeyValueCell.reuseIdentifier)
self.tableView.register(TunnelDetailTableViewButtonCell.self, forCellReuseIdentifier: TunnelDetailTableViewButtonCell.id) self.tableView.register(TunnelDetailTableViewButtonCell.self, forCellReuseIdentifier: TunnelDetailTableViewButtonCell.reuseIdentifier)
self.tableView.register(TunnelDetailTableViewActivateOnDemandCell.self, forCellReuseIdentifier: TunnelDetailTableViewActivateOnDemandCell.id) self.tableView.register(TunnelDetailTableViewActivateOnDemandCell.self, forCellReuseIdentifier: TunnelDetailTableViewActivateOnDemandCell.reuseIdentifier)
// State restoration // State restoration
self.restorationIdentifier = "TunnelDetailVC:\(tunnel.name)" self.restorationIdentifier = "TunnelDetailVC:\(tunnel.name)"
@ -99,17 +99,17 @@ extension TunnelDetailTableViewController {
let interfaceData = tunnelViewModel.interfaceData let interfaceData = tunnelViewModel.interfaceData
let numberOfPeerSections = tunnelViewModel.peersData.count let numberOfPeerSections = tunnelViewModel.peersData.count
if (section == 0) { if section == 0 {
// Status // Status
return 1 return 1
} else if (section == 1) { } else if section == 1 {
// Interface // Interface
return interfaceData.filterFieldsWithValueOrControl(interfaceFields: interfaceFields).count return interfaceData.filterFieldsWithValueOrControl(interfaceFields: interfaceFields).count
} else if ((numberOfPeerSections > 0) && (section < (2 + numberOfPeerSections))) { } else if (numberOfPeerSections > 0) && (section < (2 + numberOfPeerSections)) {
// Peer // Peer
let peerData = tunnelViewModel.peersData[section - 2] let peerData = tunnelViewModel.peersData[section - 2]
return peerData.filterFieldsWithValueOrControl(peerFields: peerFields).count return peerData.filterFieldsWithValueOrControl(peerFields: peerFields).count
} else if (section < (3 + numberOfPeerSections)) { } else if section < (3 + numberOfPeerSections) {
// Activate on demand // Activate on demand
return 1 return 1
} else { } else {
@ -122,16 +122,16 @@ extension TunnelDetailTableViewController {
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let numberOfPeerSections = tunnelViewModel.peersData.count let numberOfPeerSections = tunnelViewModel.peersData.count
if (section == 0) { if section == 0 {
// Status // Status
return "Status" return "Status"
} else if (section == 1) { } else if section == 1 {
// Interface // Interface
return "Interface" return "Interface"
} else if ((numberOfPeerSections > 0) && (section < (2 + numberOfPeerSections))) { } else if (numberOfPeerSections > 0) && (section < (2 + numberOfPeerSections)) {
// Peer // Peer
return "Peer" return "Peer"
} else if (section < (3 + numberOfPeerSections)) { } else if section < (3 + numberOfPeerSections) {
// On-Demand Activation // On-Demand Activation
return "On-Demand Activation" return "On-Demand Activation"
} else { } else {
@ -147,13 +147,13 @@ extension TunnelDetailTableViewController {
let section = indexPath.section let section = indexPath.section
let row = indexPath.row let row = indexPath.row
if (section == 0) { if section == 0 {
// Status // Status
let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewStatusCell.id, for: indexPath) as! TunnelDetailTableViewStatusCell let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewStatusCell.reuseIdentifier, for: indexPath) as! TunnelDetailTableViewStatusCell
cell.tunnel = self.tunnel cell.tunnel = self.tunnel
cell.onSwitchToggled = { [weak self] isOn in cell.onSwitchToggled = { [weak self] isOn in
guard let self = self else { return } guard let self = self else { return }
if (isOn) { if isOn {
self.tunnelsManager.startActivation(of: self.tunnel) { [weak self] error in self.tunnelsManager.startActivation(of: self.tunnel) { [weak self] error in
if let error = error { if let error = error {
ErrorPresenter.showErrorAlert(error: error, from: self, onPresented: { ErrorPresenter.showErrorAlert(error: error, from: self, onPresented: {
@ -168,33 +168,33 @@ extension TunnelDetailTableViewController {
} }
} }
return cell return cell
} else if (section == 1) { } else if section == 1 {
// Interface // Interface
let field = interfaceData.filterFieldsWithValueOrControl(interfaceFields: interfaceFields)[row] let field = interfaceData.filterFieldsWithValueOrControl(interfaceFields: interfaceFields)[row]
let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewKeyValueCell.id, for: indexPath) as! TunnelDetailTableViewKeyValueCell let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewKeyValueCell.reuseIdentifier, for: indexPath) as! TunnelDetailTableViewKeyValueCell
// Set key and value // Set key and value
cell.key = field.rawValue cell.key = field.rawValue
cell.value = interfaceData[field] cell.value = interfaceData[field]
return cell return cell
} else if ((numberOfPeerSections > 0) && (section < (2 + numberOfPeerSections))) { } else if (numberOfPeerSections > 0) && (section < (2 + numberOfPeerSections)) {
// Peer // Peer
let peerData = tunnelViewModel.peersData[section - 2] let peerData = tunnelViewModel.peersData[section - 2]
let field = peerData.filterFieldsWithValueOrControl(peerFields: peerFields)[row] let field = peerData.filterFieldsWithValueOrControl(peerFields: peerFields)[row]
let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewKeyValueCell.id, for: indexPath) as! TunnelDetailTableViewKeyValueCell let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewKeyValueCell.reuseIdentifier, for: indexPath) as! TunnelDetailTableViewKeyValueCell
// Set key and value // Set key and value
cell.key = field.rawValue cell.key = field.rawValue
cell.value = peerData[field] cell.value = peerData[field]
return cell return cell
} else if (section < (3 + numberOfPeerSections)) { } else if section < (3 + numberOfPeerSections) {
// On-Demand Activation // On-Demand Activation
let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewActivateOnDemandCell.id, for: indexPath) as! TunnelDetailTableViewActivateOnDemandCell let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewActivateOnDemandCell.reuseIdentifier, for: indexPath) as! TunnelDetailTableViewActivateOnDemandCell
cell.tunnel = self.tunnel cell.tunnel = self.tunnel
return cell return cell
} else { } else {
assert(section == (3 + numberOfPeerSections)) assert(section == (3 + numberOfPeerSections))
// Delete configuration // Delete configuration
let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewButtonCell.id, for: indexPath) as! TunnelDetailTableViewButtonCell let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewButtonCell.reuseIdentifier, for: indexPath) as! TunnelDetailTableViewButtonCell
cell.buttonText = "Delete tunnel" cell.buttonText = "Delete tunnel"
cell.hasDestructiveAction = true cell.hasDestructiveAction = true
cell.onTapped = { [weak self] in cell.onTapped = { [weak self] in
@ -202,7 +202,7 @@ extension TunnelDetailTableViewController {
self.showConfirmationAlert(message: "Delete this tunnel?", buttonTitle: "Delete", from: cell) { [weak self] in self.showConfirmationAlert(message: "Delete this tunnel?", buttonTitle: "Delete", from: cell) { [weak self] in
guard let tunnelsManager = self?.tunnelsManager, let tunnel = self?.tunnel else { return } guard let tunnelsManager = self?.tunnelsManager, let tunnel = self?.tunnel else { return }
tunnelsManager.remove(tunnel: tunnel) { (error) in tunnelsManager.remove(tunnel: tunnel) { (error) in
if (error != nil) { if error != nil {
print("Error removing tunnel: \(String(describing: error))") print("Error removing tunnel: \(String(describing: error))")
return return
} }
@ -216,7 +216,7 @@ extension TunnelDetailTableViewController {
} }
class TunnelDetailTableViewStatusCell: UITableViewCell { class TunnelDetailTableViewStatusCell: UITableViewCell {
static let id: String = "TunnelDetailTableViewStatusCell" static let reuseIdentifier = "TunnelDetailTableViewStatusCell"
var tunnel: TunnelContainer? { var tunnel: TunnelContainer? {
didSet(value) { didSet(value) {
@ -238,14 +238,14 @@ class TunnelDetailTableViewStatusCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
statusSwitch = UISwitch() statusSwitch = UISwitch()
super.init(style: .default, reuseIdentifier: TunnelDetailTableViewKeyValueCell.id) super.init(style: .default, reuseIdentifier: TunnelDetailTableViewKeyValueCell.reuseIdentifier)
accessoryView = statusSwitch accessoryView = statusSwitch
statusSwitch.addTarget(self, action: #selector(switchToggled), for: .valueChanged) statusSwitch.addTarget(self, action: #selector(switchToggled), for: .valueChanged)
} }
@objc func switchToggled() { @objc func switchToggled() {
if (isOnSwitchToggledHandlerEnabled) { if isOnSwitchToggledHandlerEnabled {
onSwitchToggled?(statusSwitch.isOn) onSwitchToggled?(statusSwitch.isOn)
} }
} }
@ -256,7 +256,7 @@ class TunnelDetailTableViewStatusCell: UITableViewCell {
return return
} }
let text: String let text: String
switch (status) { switch status {
case .inactive: case .inactive:
text = "Inactive" text = "Inactive"
case .activating: case .activating:
@ -297,7 +297,7 @@ class TunnelDetailTableViewStatusCell: UITableViewCell {
} }
class TunnelDetailTableViewKeyValueCell: CopyableLabelTableViewCell { class TunnelDetailTableViewKeyValueCell: CopyableLabelTableViewCell {
static let id: String = "TunnelDetailTableViewKeyValueCell" static let reuseIdentifier = "TunnelDetailTableViewKeyValueCell"
var key: String { var key: String {
get { return keyLabel.text ?? "" } get { return keyLabel.text ?? "" }
set(value) { keyLabel.text = value } set(value) { keyLabel.text = value }
@ -357,9 +357,9 @@ class TunnelDetailTableViewKeyValueCell: CopyableLabelTableViewCell {
func configureForContentSize() { func configureForContentSize() {
var constraints: [NSLayoutConstraint] = [] var constraints: [NSLayoutConstraint] = []
if (self.traitCollection.preferredContentSizeCategory.isAccessibilityCategory) { if self.traitCollection.preferredContentSizeCategory.isAccessibilityCategory {
// Stack vertically // Stack vertically
if (!isStackedVertically) { if !isStackedVertically {
constraints = [ constraints = [
valueLabel.topAnchor.constraint(equalToSystemSpacingBelow: keyLabel.bottomAnchor, multiplier: 0.5), valueLabel.topAnchor.constraint(equalToSystemSpacingBelow: keyLabel.bottomAnchor, multiplier: 0.5),
valueLabel.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor), valueLabel.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor),
@ -370,7 +370,7 @@ class TunnelDetailTableViewKeyValueCell: CopyableLabelTableViewCell {
} }
} else { } else {
// Stack horizontally // Stack horizontally
if (!isStackedHorizontally) { if !isStackedHorizontally {
constraints = [ constraints = [
contentView.layoutMarginsGuide.bottomAnchor.constraint(equalToSystemSpacingBelow: keyLabel.bottomAnchor, multiplier: 0.5), contentView.layoutMarginsGuide.bottomAnchor.constraint(equalToSystemSpacingBelow: keyLabel.bottomAnchor, multiplier: 0.5),
valueLabel.leftAnchor.constraint(equalToSystemSpacingAfter: keyLabel.rightAnchor, multiplier: 1), valueLabel.leftAnchor.constraint(equalToSystemSpacingAfter: keyLabel.rightAnchor, multiplier: 1),
@ -380,7 +380,7 @@ class TunnelDetailTableViewKeyValueCell: CopyableLabelTableViewCell {
isStackedVertically = false isStackedVertically = false
} }
} }
if (!constraints.isEmpty) { if !constraints.isEmpty {
NSLayoutConstraint.deactivate(self.contentSizeBasedConstraints) NSLayoutConstraint.deactivate(self.contentSizeBasedConstraints)
NSLayoutConstraint.activate(constraints) NSLayoutConstraint.activate(constraints)
self.contentSizeBasedConstraints = constraints self.contentSizeBasedConstraints = constraints
@ -400,7 +400,7 @@ class TunnelDetailTableViewKeyValueCell: CopyableLabelTableViewCell {
} }
class TunnelDetailTableViewButtonCell: UITableViewCell { class TunnelDetailTableViewButtonCell: UITableViewCell {
static let id: String = "TunnelDetailTableViewButtonCell" static let reuseIdentifier = "TunnelDetailTableViewButtonCell"
var buttonText: String { var buttonText: String {
get { return button.title(for: .normal) ?? "" } get { return button.title(for: .normal) ?? "" }
set(value) { button.setTitle(value, for: .normal) } set(value) { button.setTitle(value, for: .normal) }
@ -447,7 +447,7 @@ class TunnelDetailTableViewButtonCell: UITableViewCell {
} }
class TunnelDetailTableViewActivateOnDemandCell: UITableViewCell { class TunnelDetailTableViewActivateOnDemandCell: UITableViewCell {
static let id: String = "TunnelDetailTableViewActivateOnDemandCell" static let reuseIdentifier = "TunnelDetailTableViewActivateOnDemandCell"
var tunnel: TunnelContainer? { var tunnel: TunnelContainer? {
didSet(value) { didSet(value) {

View File

@ -82,7 +82,7 @@ class TunnelEditTableViewController: UITableViewController {
@objc func saveTapped() { @objc func saveTapped() {
self.tableView.endEditing(false) self.tableView.endEditing(false)
let tunnelSaveResult = tunnelViewModel.save() let tunnelSaveResult = tunnelViewModel.save()
switch (tunnelSaveResult) { switch tunnelSaveResult {
case .error(let errorMessage): case .error(let errorMessage):
let erroringConfiguration = (tunnelViewModel.interfaceData.validatedConfiguration == nil) ? "Interface" : "Peer" let erroringConfiguration = (tunnelViewModel.interfaceData.validatedConfiguration == nil) ? "Interface" : "Peer"
ErrorPresenter.showErrorAlert(title: "Invalid \(erroringConfiguration)", message: errorMessage, from: self) ErrorPresenter.showErrorAlert(title: "Invalid \(erroringConfiguration)", message: errorMessage, from: self)
@ -130,21 +130,21 @@ extension TunnelEditTableViewController {
} }
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (section < interfaceSectionCount) { if section < interfaceSectionCount {
// Interface // Interface
return interfaceFieldsBySection[section].count return interfaceFieldsBySection[section].count
} else if ((peerSectionCount > 0) && (section < (interfaceSectionCount + peerSectionCount))) { } else if (peerSectionCount > 0) && (section < (interfaceSectionCount + peerSectionCount)) {
// Peer // Peer
let peerIndex = (section - interfaceSectionCount) let peerIndex = (section - interfaceSectionCount)
let peerData = tunnelViewModel.peersData[peerIndex] let peerData = tunnelViewModel.peersData[peerIndex]
let peerFieldsToShow = peerData.shouldAllowExcludePrivateIPsControl ? peerFields : peerFields.filter { $0 != .excludePrivateIPs } let peerFieldsToShow = peerData.shouldAllowExcludePrivateIPsControl ? peerFields : peerFields.filter { $0 != .excludePrivateIPs }
return peerFieldsToShow.count return peerFieldsToShow.count
} else if (section < (interfaceSectionCount + peerSectionCount + 1)) { } else if section < (interfaceSectionCount + peerSectionCount + 1) {
// Add peer // Add peer
return 1 return 1
} else { } else {
// On-Demand Rules // On-Demand Rules
if (activateOnDemandSetting.isActivateOnDemandEnabled) { if activateOnDemandSetting.isActivateOnDemandEnabled {
return 4 return 4
} else { } else {
return 1 return 1
@ -153,13 +153,13 @@ extension TunnelEditTableViewController {
} }
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if (section < interfaceSectionCount) { if section < interfaceSectionCount {
// Interface // Interface
return (section == 0) ? "Interface" : nil return (section == 0) ? "Interface" : nil
} else if ((peerSectionCount > 0) && (section < (interfaceSectionCount + peerSectionCount))) { } else if (peerSectionCount > 0) && (section < (interfaceSectionCount + peerSectionCount)) {
// Peer // Peer
return "Peer" return "Peer"
} else if (section == (interfaceSectionCount + peerSectionCount)) { } else if section == (interfaceSectionCount + peerSectionCount) {
// Add peer // Add peer
return nil return nil
} else { } else {
@ -169,11 +169,11 @@ extension TunnelEditTableViewController {
} }
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath.section < interfaceSectionCount) { if indexPath.section < interfaceSectionCount {
return interfaceFieldCell(for: tableView, at: indexPath) return interfaceFieldCell(for: tableView, at: indexPath)
} else if ((peerSectionCount > 0) && (indexPath.section < (interfaceSectionCount + peerSectionCount))) { } else if (peerSectionCount > 0) && (indexPath.section < (interfaceSectionCount + peerSectionCount)) {
return peerCell(for: tableView, at: indexPath) return peerCell(for: tableView, at: indexPath)
} else if (indexPath.section == (interfaceSectionCount + peerSectionCount)) { } else if indexPath.section == (interfaceSectionCount + peerSectionCount) {
return addPeerCell(for: tableView, at: indexPath) return addPeerCell(for: tableView, at: indexPath)
} else { } else {
return onDemandCell(for: tableView, at: indexPath) return onDemandCell(for: tableView, at: indexPath)
@ -183,7 +183,7 @@ extension TunnelEditTableViewController {
private func interfaceFieldCell(for tableView: UITableView, at indexPath: IndexPath) -> UITableViewCell { private func interfaceFieldCell(for tableView: UITableView, at indexPath: IndexPath) -> UITableViewCell {
let interfaceData = tunnelViewModel.interfaceData let interfaceData = tunnelViewModel.interfaceData
let field = interfaceFieldsBySection[indexPath.section][indexPath.row] let field = interfaceFieldsBySection[indexPath.section][indexPath.row]
if (field == .generateKeyPair) { if field == .generateKeyPair {
let cell = tableView.dequeueReusableCell(withIdentifier: TunnelEditTableViewButtonCell.reuseIdentifier, for: indexPath) as! TunnelEditTableViewButtonCell let cell = tableView.dequeueReusableCell(withIdentifier: TunnelEditTableViewButtonCell.reuseIdentifier, for: indexPath) as! TunnelEditTableViewButtonCell
cell.buttonText = field.rawValue cell.buttonText = field.rawValue
cell.onTapped = { [weak self, weak interfaceData] in cell.onTapped = { [weak self, weak interfaceData] in
@ -550,9 +550,9 @@ class TunnelEditTableViewKeyValueCell: UITableViewCell {
func configureForContentSize() { func configureForContentSize() {
var constraints: [NSLayoutConstraint] = [] var constraints: [NSLayoutConstraint] = []
if (self.traitCollection.preferredContentSizeCategory.isAccessibilityCategory) { if self.traitCollection.preferredContentSizeCategory.isAccessibilityCategory {
// Stack vertically // Stack vertically
if (!isStackedVertically) { if !isStackedVertically {
constraints = [ constraints = [
valueTextField.topAnchor.constraint(equalToSystemSpacingBelow: keyLabel.bottomAnchor, multiplier: 0.5), valueTextField.topAnchor.constraint(equalToSystemSpacingBelow: keyLabel.bottomAnchor, multiplier: 0.5),
valueTextField.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor), valueTextField.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor),
@ -563,7 +563,7 @@ class TunnelEditTableViewKeyValueCell: UITableViewCell {
} }
} else { } else {
// Stack horizontally // Stack horizontally
if (!isStackedHorizontally) { if !isStackedHorizontally {
constraints = [ constraints = [
contentView.layoutMarginsGuide.bottomAnchor.constraint(equalToSystemSpacingBelow: keyLabel.bottomAnchor, multiplier: 0.5), contentView.layoutMarginsGuide.bottomAnchor.constraint(equalToSystemSpacingBelow: keyLabel.bottomAnchor, multiplier: 0.5),
valueTextField.leftAnchor.constraint(equalToSystemSpacingAfter: keyLabel.rightAnchor, multiplier: 1), valueTextField.leftAnchor.constraint(equalToSystemSpacingAfter: keyLabel.rightAnchor, multiplier: 1),
@ -573,7 +573,7 @@ class TunnelEditTableViewKeyValueCell: UITableViewCell {
isStackedVertically = false isStackedVertically = false
} }
} }
if (!constraints.isEmpty) { if !constraints.isEmpty {
NSLayoutConstraint.deactivate(self.contentSizeBasedConstraints) NSLayoutConstraint.deactivate(self.contentSizeBasedConstraints)
NSLayoutConstraint.activate(constraints) NSLayoutConstraint.activate(constraints)
self.contentSizeBasedConstraints = constraints self.contentSizeBasedConstraints = constraints
@ -604,7 +604,7 @@ extension TunnelEditTableViewKeyValueCell: UITextFieldDelegate {
} }
func textFieldDidEndEditing(_ textField: UITextField) { func textFieldDidEndEditing(_ textField: UITextField) {
let isModified = (textField.text ?? "" != textFieldValueOnBeginEditing) let isModified = (textField.text ?? "" != textFieldValueOnBeginEditing)
guard (isModified) else { return } guard isModified else { return }
if let onValueChanged = onValueChanged { if let onValueChanged = onValueChanged {
onValueChanged(textField.text ?? "") onValueChanged(textField.text ?? "")
} }

View File

@ -43,7 +43,7 @@ class TunnelsListTableViewController: UIViewController {
} }
func setTunnelsManager(tunnelsManager: TunnelsManager) { func setTunnelsManager(tunnelsManager: TunnelsManager) {
if (self.tunnelsManager != nil) { if self.tunnelsManager != nil {
// If a tunnels manager is already set, do nothing // If a tunnels manager is already set, do nothing
return return
} }
@ -54,7 +54,7 @@ class TunnelsListTableViewController: UIViewController {
tableView.estimatedRowHeight = 60 tableView.estimatedRowHeight = 60
tableView.rowHeight = UITableView.automaticDimension tableView.rowHeight = UITableView.automaticDimension
tableView.separatorStyle = .none tableView.separatorStyle = .none
tableView.register(TunnelsListTableViewCell.self, forCellReuseIdentifier: TunnelsListTableViewCell.id) tableView.register(TunnelsListTableViewCell.self, forCellReuseIdentifier: TunnelsListTableViewCell.reuseIdentifier)
self.view.addSubview(tableView) self.view.addSubview(tableView)
tableView.translatesAutoresizingMaskIntoConstraints = false tableView.translatesAutoresizingMaskIntoConstraints = false
@ -103,7 +103,7 @@ class TunnelsListTableViewController: UIViewController {
} }
@objc func addButtonTapped(sender: AnyObject) { @objc func addButtonTapped(sender: AnyObject) {
if (self.tunnelsManager == nil) { return } // Do nothing until we've loaded the tunnels if self.tunnelsManager == nil { return } // Do nothing until we've loaded the tunnels
let alert = UIAlertController(title: "", message: "Add a new WireGuard tunnel", preferredStyle: .actionSheet) let alert = UIAlertController(title: "", message: "Add a new WireGuard tunnel", preferredStyle: .actionSheet)
let importFileAction = UIAlertAction(title: "Create from file or archive", style: .default) { [weak self] (_) in let importFileAction = UIAlertAction(title: "Create from file or archive", style: .default) { [weak self] (_) in
self?.presentViewControllerForFileImport() self?.presentViewControllerForFileImport()
@ -136,7 +136,7 @@ class TunnelsListTableViewController: UIViewController {
} }
@objc func settingsButtonTapped(sender: UIBarButtonItem!) { @objc func settingsButtonTapped(sender: UIBarButtonItem!) {
if (self.tunnelsManager == nil) { return } // Do nothing until we've loaded the tunnels if self.tunnelsManager == nil { return } // Do nothing until we've loaded the tunnels
let settingsVC = SettingsTableViewController(tunnelsManager: tunnelsManager) let settingsVC = SettingsTableViewController(tunnelsManager: tunnelsManager)
let settingsNC = UINavigationController(rootViewController: settingsVC) let settingsNC = UINavigationController(rootViewController: settingsVC)
settingsNC.modalPresentationStyle = .formSheet settingsNC.modalPresentationStyle = .formSheet
@ -167,7 +167,7 @@ class TunnelsListTableViewController: UIViewController {
func importFromFile(url: URL, completionHandler: (() -> Void)?) { func importFromFile(url: URL, completionHandler: (() -> Void)?) {
guard let tunnelsManager = tunnelsManager else { return } guard let tunnelsManager = tunnelsManager else { return }
if (url.pathExtension == "zip") { if url.pathExtension == "zip" {
ZipImporter.importConfigFiles(from: url) { [weak self] result in ZipImporter.importConfigFiles(from: url) { [weak self] result in
if let error = result.error { if let error = result.error {
ErrorPresenter.showErrorAlert(error: error, from: self) ErrorPresenter.showErrorAlert(error: error, from: self)
@ -241,13 +241,13 @@ extension TunnelsListTableViewController: UITableViewDataSource {
} }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: TunnelsListTableViewCell.id, for: indexPath) as! TunnelsListTableViewCell let cell = tableView.dequeueReusableCell(withIdentifier: TunnelsListTableViewCell.reuseIdentifier, for: indexPath) as! TunnelsListTableViewCell
if let tunnelsManager = tunnelsManager { if let tunnelsManager = tunnelsManager {
let tunnel = tunnelsManager.tunnel(at: indexPath.row) let tunnel = tunnelsManager.tunnel(at: indexPath.row)
cell.tunnel = tunnel cell.tunnel = tunnel
cell.onSwitchToggled = { [weak self] isOn in cell.onSwitchToggled = { [weak self] isOn in
guard let self = self, let tunnelsManager = self.tunnelsManager else { return } guard let self = self, let tunnelsManager = self.tunnelsManager else { return }
if (isOn) { if isOn {
tunnelsManager.startActivation(of: tunnel) { [weak self] error in tunnelsManager.startActivation(of: tunnel) { [weak self] error in
if let error = error { if let error = error {
ErrorPresenter.showErrorAlert(error: error, from: self, onPresented: { ErrorPresenter.showErrorAlert(error: error, from: self, onPresented: {
@ -285,7 +285,7 @@ extension TunnelsListTableViewController: UITableViewDelegate {
guard let tunnelsManager = self?.tunnelsManager else { return } guard let tunnelsManager = self?.tunnelsManager else { return }
let tunnel = tunnelsManager.tunnel(at: indexPath.row) let tunnel = tunnelsManager.tunnel(at: indexPath.row)
tunnelsManager.remove(tunnel: tunnel, completionHandler: { (error) in tunnelsManager.remove(tunnel: tunnel, completionHandler: { (error) in
if (error != nil) { if error != nil {
ErrorPresenter.showErrorAlert(error: error!, from: self) ErrorPresenter.showErrorAlert(error: error!, from: self)
completionHandler(false) completionHandler(false)
} else { } else {
@ -309,7 +309,7 @@ extension TunnelsListTableViewController: TunnelsManagerListDelegate {
tableView?.reloadRows(at: [IndexPath(row: index, section: 0)], with: .automatic) tableView?.reloadRows(at: [IndexPath(row: index, section: 0)], with: .automatic)
} }
func tunnelMoved(at oldIndex: Int, to newIndex: Int) { func tunnelMoved(from oldIndex: Int, to newIndex: Int) {
tableView?.moveRow(at: IndexPath(row: oldIndex, section: 0), to: IndexPath(row: newIndex, section: 0)) tableView?.moveRow(at: IndexPath(row: oldIndex, section: 0), to: IndexPath(row: newIndex, section: 0))
} }
@ -320,7 +320,7 @@ extension TunnelsListTableViewController: TunnelsManagerListDelegate {
} }
class TunnelsListTableViewCell: UITableViewCell { class TunnelsListTableViewCell: UITableViewCell {
static let id: String = "TunnelsListTableViewCell" static let reuseIdentifier = "TunnelsListTableViewCell"
var tunnel: TunnelContainer? { var tunnel: TunnelContainer? {
didSet(value) { didSet(value) {
// Bind to the tunnel's name // Bind to the tunnel's name
@ -396,7 +396,7 @@ class TunnelsListTableViewCell: UITableViewCell {
guard let statusSwitch = statusSwitch, let busyIndicator = busyIndicator else { return } guard let statusSwitch = statusSwitch, let busyIndicator = busyIndicator else { return }
statusSwitch.isOn = !(status == .deactivating || status == .inactive) statusSwitch.isOn = !(status == .deactivating || status == .inactive)
statusSwitch.isUserInteractionEnabled = (status == .inactive || status == .active) statusSwitch.isUserInteractionEnabled = (status == .inactive || status == .active)
if (status == .inactive || status == .active) { if status == .inactive || status == .active {
busyIndicator.stopAnimating() busyIndicator.stopAnimating()
} else { } else {
busyIndicator.startAnimating() busyIndicator.startAnimating()

View File

@ -21,7 +21,7 @@ extension ActivateOnDemandSetting {
let rules: [NEOnDemandRule]? let rules: [NEOnDemandRule]?
let connectRule = NEOnDemandRuleConnect() let connectRule = NEOnDemandRuleConnect()
let disconnectRule = NEOnDemandRuleDisconnect() let disconnectRule = NEOnDemandRuleDisconnect()
switch (activateOnDemandOption) { switch activateOnDemandOption {
case .none: case .none:
rules = nil rules = nil
case .useOnDemandOverWiFiOrCellular: case .useOnDemandOverWiFiOrCellular:
@ -41,7 +41,7 @@ extension ActivateOnDemandSetting {
init(from tunnelProviderManager: NETunnelProviderManager) { init(from tunnelProviderManager: NETunnelProviderManager) {
let rules = tunnelProviderManager.onDemandRules ?? [] let rules = tunnelProviderManager.onDemandRules ?? []
let activateOnDemandOption: ActivateOnDemandOption let activateOnDemandOption: ActivateOnDemandOption
switch (rules.count) { switch rules.count {
case 0: case 0:
activateOnDemandOption = .none activateOnDemandOption = .none
case 1: case 1:
@ -51,9 +51,9 @@ extension ActivateOnDemandSetting {
case 2: case 2:
let connectRule = rules.first(where: { $0.action == .connect })! let connectRule = rules.first(where: { $0.action == .connect })!
let disconnectRule = rules.first(where: { $0.action == .disconnect })! let disconnectRule = rules.first(where: { $0.action == .disconnect })!
if (connectRule.interfaceTypeMatch == .wiFi && disconnectRule.interfaceTypeMatch == .cellular) { if connectRule.interfaceTypeMatch == .wiFi && disconnectRule.interfaceTypeMatch == .cellular {
activateOnDemandOption = .useOnDemandOverWiFiOnly activateOnDemandOption = .useOnDemandOverWiFiOnly
} else if (connectRule.interfaceTypeMatch == .cellular && disconnectRule.interfaceTypeMatch == .wiFi) { } else if connectRule.interfaceTypeMatch == .cellular && disconnectRule.interfaceTypeMatch == .wiFi {
activateOnDemandOption = .useOnDemandOverCellularOnly activateOnDemandOption = .useOnDemandOverCellularOnly
} else { } else {
fatalError("Unexpected onDemandRules set on tunnel provider manager") fatalError("Unexpected onDemandRules set on tunnel provider manager")
@ -62,7 +62,7 @@ extension ActivateOnDemandSetting {
fatalError("Unexpected number of onDemandRules set on tunnel provider manager") fatalError("Unexpected number of onDemandRules set on tunnel provider manager")
} }
self.activateOnDemandOption = activateOnDemandOption self.activateOnDemandOption = activateOnDemandOption
if (activateOnDemandOption == .none) { if activateOnDemandOption == .none {
self.isActivateOnDemandEnabled = false self.isActivateOnDemandEnabled = false
} else { } else {
self.isActivateOnDemandEnabled = tunnelProviderManager.isOnDemandEnabled self.isActivateOnDemandEnabled = tunnelProviderManager.isOnDemandEnabled

View File

@ -39,8 +39,8 @@ class InternetReachability {
extension InternetReachability.Status { extension InternetReachability.Status {
init(reachabilityFlags flags: SCNetworkReachabilityFlags) { init(reachabilityFlags flags: SCNetworkReachabilityFlags) {
var status: InternetReachability.Status = .notReachable var status: InternetReachability.Status = .notReachable
if (flags.contains(.reachable)) { if flags.contains(.reachable) {
if (flags.contains(.isWWAN)) { if flags.contains(.isWWAN) {
status = .reachableOverCellular status = .reachableOverCellular
} else { } else {
status = .reachableOverWiFi status = .reachableOverWiFi

View File

@ -6,10 +6,10 @@ import NetworkExtension
import os.log import os.log
protocol TunnelsManagerListDelegate: class { protocol TunnelsManagerListDelegate: class {
func tunnelAdded(at: Int) func tunnelAdded(at index: Int)
func tunnelModified(at: Int) func tunnelModified(at index: Int)
func tunnelMoved(at oldIndex: Int, to newIndex: Int) func tunnelMoved(from oldIndex: Int, to newIndex: Int)
func tunnelRemoved(at: Int) func tunnelRemoved(at index: Int)
} }
protocol TunnelsManagerActivationDelegate: class { protocol TunnelsManagerActivationDelegate: class {
@ -33,7 +33,7 @@ enum TunnelsManagerError: WireGuardAppError {
case tunnelActivationFailedNoInternetConnection // startTunnel() succeeded, but activation failed since no internet case tunnelActivationFailedNoInternetConnection // startTunnel() succeeded, but activation failed since no internet
func alertText() -> (String, String)? { func alertText() -> (String, String)? {
switch (self) { switch self {
case .tunnelNameEmpty: case .tunnelNameEmpty:
return ("No name provided", "Can't create tunnel with an empty name") return ("No name provided", "Can't create tunnel with an empty name")
case .tunnelAlreadyExistsWithThatName: case .tunnelAlreadyExistsWithThatName:
@ -113,7 +113,7 @@ class TunnelsManager {
activateOnDemandSetting.apply(on: tunnelProviderManager) activateOnDemandSetting.apply(on: tunnelProviderManager)
tunnelProviderManager.saveToPreferences { [weak self] (error) in tunnelProviderManager.saveToPreferences { [weak self] (error) in
guard (error == nil) else { guard error == nil else {
os_log("Add: Saving configuration failed: %{public}@", log: OSLog.default, type: .error, "\(error!)") os_log("Add: Saving configuration failed: %{public}@", log: OSLog.default, type: .error, "\(error!)")
completionHandler(.failure(TunnelsManagerError.vpnSystemErrorOnAddTunnel)) completionHandler(.failure(TunnelsManagerError.vpnSystemErrorOnAddTunnel))
return return
@ -155,7 +155,7 @@ class TunnelsManager {
let tunnelProviderManager = tunnel.tunnelProvider let tunnelProviderManager = tunnel.tunnelProvider
let isNameChanged = (tunnelName != tunnelProviderManager.localizedDescription) let isNameChanged = (tunnelName != tunnelProviderManager.localizedDescription)
if (isNameChanged) { if isNameChanged {
if self.tunnels.contains(where: { $0.name == tunnelName }) { if self.tunnels.contains(where: { $0.name == tunnelName }) {
completionHandler(TunnelsManagerError.tunnelAlreadyExistsWithThatName) completionHandler(TunnelsManagerError.tunnelAlreadyExistsWithThatName)
return return
@ -170,33 +170,33 @@ class TunnelsManager {
activateOnDemandSetting.apply(on: tunnelProviderManager) activateOnDemandSetting.apply(on: tunnelProviderManager)
tunnelProviderManager.saveToPreferences { [weak self] (error) in tunnelProviderManager.saveToPreferences { [weak self] (error) in
guard (error == nil) else { guard error == nil else {
os_log("Modify: Saving configuration failed: %{public}@", log: OSLog.default, type: .error, "\(error!)") os_log("Modify: Saving configuration failed: %{public}@", log: OSLog.default, type: .error, "\(error!)")
completionHandler(TunnelsManagerError.vpnSystemErrorOnModifyTunnel) completionHandler(TunnelsManagerError.vpnSystemErrorOnModifyTunnel)
return return
} }
if let self = self { if let self = self {
if (isNameChanged) { if isNameChanged {
let oldIndex = self.tunnels.firstIndex(of: tunnel)! let oldIndex = self.tunnels.firstIndex(of: tunnel)!
self.tunnels.sort { $0.name < $1.name } self.tunnels.sort { $0.name < $1.name }
let newIndex = self.tunnels.firstIndex(of: tunnel)! let newIndex = self.tunnels.firstIndex(of: tunnel)!
self.tunnelsListDelegate?.tunnelMoved(at: oldIndex, to: newIndex) self.tunnelsListDelegate?.tunnelMoved(from: oldIndex, to: newIndex)
} }
self.tunnelsListDelegate?.tunnelModified(at: self.tunnels.firstIndex(of: tunnel)!) self.tunnelsListDelegate?.tunnelModified(at: self.tunnels.firstIndex(of: tunnel)!)
if (tunnel.status == .active || tunnel.status == .activating || tunnel.status == .reasserting) { if tunnel.status == .active || tunnel.status == .activating || tunnel.status == .reasserting {
// Turn off the tunnel, and then turn it back on, so the changes are made effective // Turn off the tunnel, and then turn it back on, so the changes are made effective
let session = (tunnel.tunnelProvider.connection as! NETunnelProviderSession) let session = (tunnel.tunnelProvider.connection as! NETunnelProviderSession)
tunnel.status = .restarting tunnel.status = .restarting
session.stopTunnel() session.stopTunnel()
} }
if (isActivatingOnDemand) { if isActivatingOnDemand {
// Reload tunnel after saving. // Reload tunnel after saving.
// Without this, the tunnel stopes getting updates on the tunnel status from iOS. // Without this, the tunnel stopes getting updates on the tunnel status from iOS.
tunnelProviderManager.loadFromPreferences { (error) in tunnelProviderManager.loadFromPreferences { (error) in
tunnel.isActivateOnDemandEnabled = tunnelProviderManager.isOnDemandEnabled tunnel.isActivateOnDemandEnabled = tunnelProviderManager.isOnDemandEnabled
guard (error == nil) else { guard error == nil else {
os_log("Modify: Re-loading after saving configuration failed: %{public}@", log: OSLog.default, type: .error, "\(error!)") os_log("Modify: Re-loading after saving configuration failed: %{public}@", log: OSLog.default, type: .error, "\(error!)")
completionHandler(TunnelsManagerError.vpnSystemErrorOnModifyTunnel) completionHandler(TunnelsManagerError.vpnSystemErrorOnModifyTunnel)
return return
@ -214,7 +214,7 @@ class TunnelsManager {
let tunnelProviderManager = tunnel.tunnelProvider let tunnelProviderManager = tunnel.tunnelProvider
tunnelProviderManager.removeFromPreferences { [weak self] (error) in tunnelProviderManager.removeFromPreferences { [weak self] (error) in
guard (error == nil) else { guard error == nil else {
os_log("Remove: Saving configuration failed: %{public}@", log: OSLog.default, type: .error, "\(error!)") os_log("Remove: Saving configuration failed: %{public}@", log: OSLog.default, type: .error, "\(error!)")
completionHandler(TunnelsManagerError.vpnSystemErrorOnRemoveTunnel) completionHandler(TunnelsManagerError.vpnSystemErrorOnRemoveTunnel)
return return
@ -242,7 +242,7 @@ class TunnelsManager {
func startActivation(of tunnel: TunnelContainer, completionHandler: @escaping (TunnelsManagerError?) -> Void) { func startActivation(of tunnel: TunnelContainer, completionHandler: @escaping (TunnelsManagerError?) -> Void) {
guard tunnels.contains(tunnel) else { return } // Ensure it's not deleted guard tunnels.contains(tunnel) else { return } // Ensure it's not deleted
guard (tunnel.status == .inactive) else { guard tunnel.status == .inactive else {
completionHandler(TunnelsManagerError.attemptingActivationWhenTunnelIsNotInactive) completionHandler(TunnelsManagerError.attemptingActivationWhenTunnelIsNotInactive)
return return
} }
@ -257,7 +257,7 @@ class TunnelsManager {
} }
func startDeactivation(of tunnel: TunnelContainer) { func startDeactivation(of tunnel: TunnelContainer) {
if (tunnel.status == .inactive || tunnel.status == .deactivating) { if tunnel.status == .inactive || tunnel.status == .deactivating {
return return
} }
tunnel.startDeactivation() tunnel.startDeactivation()
@ -268,7 +268,7 @@ class TunnelsManager {
} }
private func startObservingTunnelStatuses() { private func startObservingTunnelStatuses() {
if (statusObservationToken != nil) { return } if statusObservationToken != nil { return }
statusObservationToken = NotificationCenter.default.addObserver( statusObservationToken = NotificationCenter.default.addObserver(
forName: .NEVPNStatusDidChange, forName: .NEVPNStatusDidChange,
object: nil, object: nil,
@ -282,22 +282,22 @@ class TunnelsManager {
log: OSLog.default, type: .debug, tunnel.name, "\(tunnel.tunnelProvider.connection.status)") log: OSLog.default, type: .debug, tunnel.name, "\(tunnel.tunnelProvider.connection.status)")
// In case our attempt to start the tunnel, didn't succeed // In case our attempt to start the tunnel, didn't succeed
if (tunnel == self.tunnelBeingActivated) { if tunnel == self.tunnelBeingActivated {
if (session.status == .disconnected) { if session.status == .disconnected {
if (InternetReachability.currentStatus() == .notReachable) { if InternetReachability.currentStatus() == .notReachable {
let error = TunnelsManagerError.tunnelActivationFailedNoInternetConnection let error = TunnelsManagerError.tunnelActivationFailedNoInternetConnection
self.activationDelegate?.tunnelActivationFailed(tunnel: tunnel, error: error) self.activationDelegate?.tunnelActivationFailed(tunnel: tunnel, error: error)
} }
self.tunnelBeingActivated = nil self.tunnelBeingActivated = nil
} else if (session.status == .connected) { } else if session.status == .connected {
self.tunnelBeingActivated = nil self.tunnelBeingActivated = nil
} }
} }
// In case we're restarting the tunnel // In case we're restarting the tunnel
if ((tunnel.status == .restarting) && (session.status == .disconnected || session.status == .disconnecting)) { if (tunnel.status == .restarting) && (session.status == .disconnected || session.status == .disconnecting) {
// Don't change tunnel.status when disconnecting for a restart // Don't change tunnel.status when disconnecting for a restart
if (session.status == .disconnected) { if session.status == .disconnected {
self.tunnelBeingActivated = tunnel self.tunnelBeingActivated = tunnel
tunnel.startActivation(completionHandler: { _ in }) tunnel.startActivation(completionHandler: { _ in })
} }
@ -364,7 +364,7 @@ class TunnelContainer: NSObject {
lastError: Error? = nil, lastError: Error? = nil,
tunnelConfiguration: TunnelConfiguration, tunnelConfiguration: TunnelConfiguration,
completionHandler: @escaping (TunnelsManagerError?) -> Void) { completionHandler: @escaping (TunnelsManagerError?) -> Void) {
if (recursionCount >= 8) { if recursionCount >= 8 {
os_log("startActivation: Failed after 8 attempts. Giving up with %{public}@", log: OSLog.default, type: .error, "\(lastError!)") os_log("startActivation: Failed after 8 attempts. Giving up with %{public}@", log: OSLog.default, type: .error, "\(lastError!)")
completionHandler(TunnelsManagerError.tunnelActivationAttemptFailed) completionHandler(TunnelsManagerError.tunnelActivationAttemptFailed)
return return
@ -372,13 +372,13 @@ class TunnelContainer: NSObject {
os_log("startActivation: Entering (tunnel: %{public}@)", log: OSLog.default, type: .debug, self.name) os_log("startActivation: Entering (tunnel: %{public}@)", log: OSLog.default, type: .debug, self.name)
guard (tunnelProvider.isEnabled) else { guard tunnelProvider.isEnabled else {
// In case the tunnel had gotten disabled, re-enable and save it, // In case the tunnel had gotten disabled, re-enable and save it,
// then call this function again. // then call this function again.
os_log("startActivation: Tunnel is disabled. Re-enabling and saving", log: OSLog.default, type: .info) os_log("startActivation: Tunnel is disabled. Re-enabling and saving", log: OSLog.default, type: .info)
tunnelProvider.isEnabled = true tunnelProvider.isEnabled = true
tunnelProvider.saveToPreferences { [weak self] (error) in tunnelProvider.saveToPreferences { [weak self] (error) in
if (error != nil) { if error != nil {
os_log("Error saving tunnel after re-enabling: %{public}@", log: OSLog.default, type: .error, "\(error!)") os_log("Error saving tunnel after re-enabling: %{public}@", log: OSLog.default, type: .error, "\(error!)")
completionHandler(TunnelsManagerError.tunnelActivationAttemptFailed) completionHandler(TunnelsManagerError.tunnelActivationAttemptFailed)
return return
@ -398,23 +398,22 @@ class TunnelContainer: NSObject {
try session.startTunnel() try session.startTunnel()
os_log("startActivation: Success", log: OSLog.default, type: .debug) os_log("startActivation: Success", log: OSLog.default, type: .debug)
completionHandler(nil) completionHandler(nil)
} catch (let error) { } catch let error {
guard let vpnError = error as? NEVPNError else { guard let vpnError = error as? NEVPNError else {
os_log("Failed to activate tunnel: Error: %{public}@", log: OSLog.default, type: .debug, "\(error)") os_log("Failed to activate tunnel: Error: %{public}@", log: OSLog.default, type: .debug, "\(error)")
status = .inactive status = .inactive
completionHandler(TunnelsManagerError.tunnelActivationAttemptFailed) completionHandler(TunnelsManagerError.tunnelActivationAttemptFailed)
return return
} }
guard (vpnError.code == NEVPNError.configurationInvalid || vpnError.code == NEVPNError.configurationStale) else { guard vpnError.code == NEVPNError.configurationInvalid || vpnError.code == NEVPNError.configurationStale else {
os_log("Failed to activate tunnel: VPN Error: %{public}@", log: OSLog.default, type: .debug, "\(error)") os_log("Failed to activate tunnel: VPN Error: %{public}@", log: OSLog.default, type: .debug, "\(error)")
status = .inactive status = .inactive
completionHandler(TunnelsManagerError.tunnelActivationAttemptFailed) completionHandler(TunnelsManagerError.tunnelActivationAttemptFailed)
return return
} }
assert(vpnError.code == NEVPNError.configurationInvalid || vpnError.code == NEVPNError.configurationStale)
os_log("startActivation: Will reload tunnel and then try to start it. ", log: OSLog.default, type: .info) os_log("startActivation: Will reload tunnel and then try to start it. ", log: OSLog.default, type: .info)
tunnelProvider.loadFromPreferences { [weak self] (error) in tunnelProvider.loadFromPreferences { [weak self] (error) in
if (error != nil) { if error != nil {
os_log("startActivation: Error reloading tunnel: %{public}@", log: OSLog.default, type: .debug, "\(error!)") os_log("startActivation: Error reloading tunnel: %{public}@", log: OSLog.default, type: .debug, "\(error!)")
self?.status = .inactive self?.status = .inactive
completionHandler(TunnelsManagerError.tunnelActivationAttemptFailed) completionHandler(TunnelsManagerError.tunnelActivationAttemptFailed)
@ -443,7 +442,7 @@ class TunnelContainer: NSObject {
case restarting // Restarting tunnel (done after saving modifications to an active tunnel) case restarting // Restarting tunnel (done after saving modifications to an active tunnel)
init(from vpnStatus: NEVPNStatus) { init(from vpnStatus: NEVPNStatus) {
switch (vpnStatus) { switch vpnStatus {
case .connected: case .connected:
self = .active self = .active
case .connecting: case .connecting:
@ -462,7 +461,7 @@ class TunnelContainer: NSObject {
extension TunnelStatus: CustomDebugStringConvertible { extension TunnelStatus: CustomDebugStringConvertible {
public var debugDescription: String { public var debugDescription: String {
switch (self) { switch self {
case .inactive: return "inactive" case .inactive: return "inactive"
case .activating: return "activating" case .activating: return "activating"
case .active: return "active" case .active: return "active"
@ -475,7 +474,7 @@ extension TunnelStatus: CustomDebugStringConvertible {
extension NEVPNStatus: CustomDebugStringConvertible { extension NEVPNStatus: CustomDebugStringConvertible {
public var debugDescription: String { public var debugDescription: String {
switch (self) { switch self {
case .connected: return "connected" case .connected: return "connected"
case .connecting: return "connecting" case .connecting: return "connecting"
case .disconnected: return "disconnected" case .disconnected: return "disconnected"

View File

@ -6,21 +6,21 @@ enum WireGuardResult<T> {
case failure(_ error: WireGuardAppError) case failure(_ error: WireGuardAppError)
var value: T? { var value: T? {
switch (self) { switch self {
case .success(let value): return value case .success(let value): return value
case .failure: return nil case .failure: return nil
} }
} }
var error: WireGuardAppError? { var error: WireGuardAppError? {
switch (self) { switch self {
case .success: return nil case .success: return nil
case .failure(let error): return error case .failure(let error): return error
} }
} }
var isSuccess: Bool { var isSuccess: Bool {
switch (self) { switch self {
case .success: return true case .success: return true
case .failure: return false case .failure: return false
} }

View File

@ -9,7 +9,7 @@ enum ZipArchiveError: WireGuardAppError {
case badArchive case badArchive
func alertText() -> (String, String)? { func alertText() -> (String, String)? {
switch (self) { switch self {
case .cantOpenInputZipFile: case .cantOpenInputZipFile:
return ("Unable to read zip archive", "The zip archive could not be read.") return ("Unable to read zip archive", "The zip archive could not be read.")
case .cantOpenOutputZipFileForWriting: case .cantOpenOutputZipFileForWriting:
@ -49,15 +49,11 @@ class ZipArchive {
defer { defer {
unzClose(zipFile) unzClose(zipFile)
} }
guard (unzGoToFirstFile(zipFile) == UNZ_OK) else { guard unzGoToFirstFile(zipFile) == UNZ_OK else { throw ZipArchiveError.badArchive }
throw ZipArchiveError.badArchive
}
var resultOfGoToNextFile: Int32 var resultOfGoToNextFile: Int32
repeat { repeat {
guard (unzOpenCurrentFile(zipFile) == UNZ_OK) else { guard unzOpenCurrentFile(zipFile) == UNZ_OK else { throw ZipArchiveError.badArchive }
throw ZipArchiveError.badArchive
}
let bufferSize = 16384 // 16 KiB let bufferSize = 16384 // 16 KiB
var fileNameBuffer = UnsafeMutablePointer<Int8>.allocate(capacity: bufferSize) var fileNameBuffer = UnsafeMutablePointer<Int8>.allocate(capacity: bufferSize)
@ -68,38 +64,33 @@ class ZipArchive {
dataBuffer.deallocate() dataBuffer.deallocate()
} }
guard (unzGetCurrentFileInfo64(zipFile, nil, fileNameBuffer, UInt(bufferSize), nil, 0, nil, 0) == UNZ_OK) else { guard unzGetCurrentFileInfo64(zipFile, nil, fileNameBuffer, UInt(bufferSize), nil, 0, nil, 0) == UNZ_OK else { throw ZipArchiveError.badArchive }
throw ZipArchiveError.badArchive
}
let lastChar = String(cString: fileNameBuffer).suffix(1) let lastChar = String(cString: fileNameBuffer).suffix(1)
let isDirectory = (lastChar == "/" || lastChar == "\\") let isDirectory = (lastChar == "/" || lastChar == "\\")
let fileURL = URL(fileURLWithFileSystemRepresentation: fileNameBuffer, isDirectory: isDirectory, relativeTo: nil) let fileURL = URL(fileURLWithFileSystemRepresentation: fileNameBuffer, isDirectory: isDirectory, relativeTo: nil)
if (!isDirectory && requiredFileExtensions.contains(fileURL.pathExtension)) { if !isDirectory && requiredFileExtensions.contains(fileURL.pathExtension) {
var unzippedData = Data() var unzippedData = Data()
var bytesRead: Int32 = 0 var bytesRead: Int32 = 0
repeat { repeat {
bytesRead = unzReadCurrentFile(zipFile, dataBuffer, UInt32(bufferSize)) bytesRead = unzReadCurrentFile(zipFile, dataBuffer, UInt32(bufferSize))
if (bytesRead > 0) { if bytesRead > 0 {
let dataRead = dataBuffer.withMemoryRebound(to: UInt8.self, capacity: bufferSize) { let dataRead = dataBuffer.withMemoryRebound(to: UInt8.self, capacity: bufferSize) {
(buf: UnsafeMutablePointer<UInt8>) -> Data in return Data(bytes: $0, count: Int(bytesRead))
return Data(bytes: buf, count: Int(bytesRead))
} }
unzippedData.append(dataRead) unzippedData.append(dataRead)
} }
} while (bytesRead > 0) } while bytesRead > 0
results.append((fileBaseName: fileURL.deletingPathExtension().lastPathComponent, contents: unzippedData)) results.append((fileBaseName: fileURL.deletingPathExtension().lastPathComponent, contents: unzippedData))
} }
guard (unzCloseCurrentFile(zipFile) == UNZ_OK) else { guard unzCloseCurrentFile(zipFile) == UNZ_OK else { throw ZipArchiveError.badArchive }
throw ZipArchiveError.badArchive
}
resultOfGoToNextFile = unzGoToNextFile(zipFile) resultOfGoToNextFile = unzGoToNextFile(zipFile)
} while (resultOfGoToNextFile == UNZ_OK) } while resultOfGoToNextFile == UNZ_OK
if (resultOfGoToNextFile == UNZ_END_OF_LIST_OF_FILE) { if resultOfGoToNextFile == UNZ_END_OF_LIST_OF_FILE {
return results return results
} else { } else {
throw ZipArchiveError.badArchive throw ZipArchiveError.badArchive

View File

@ -7,7 +7,7 @@ enum ZipExporterError: WireGuardAppError {
case noTunnelsToExport case noTunnelsToExport
func alertText() -> (String, String)? { func alertText() -> (String, String)? {
switch (self) { switch self {
case .noTunnelsToExport: case .noTunnelsToExport:
return ("Nothing to export", "There are no tunnels to export") return ("Nothing to export", "There are no tunnels to export")
} }
@ -18,7 +18,7 @@ class ZipExporter {
static func exportConfigFiles(tunnelConfigurations: [TunnelConfiguration], to url: URL, static func exportConfigFiles(tunnelConfigurations: [TunnelConfiguration], to url: URL,
completion: @escaping (WireGuardAppError?) -> Void) { completion: @escaping (WireGuardAppError?) -> Void) {
guard (!tunnelConfigurations.isEmpty) else { guard !tunnelConfigurations.isEmpty else {
completion(ZipExporterError.noTunnelsToExport) completion(ZipExporterError.noTunnelsToExport)
return return
} }
@ -28,14 +28,14 @@ class ZipExporter {
for tunnelConfiguration in tunnelConfigurations { for tunnelConfiguration in tunnelConfigurations {
if let contents = WgQuickConfigFileWriter.writeConfigFile(from: tunnelConfiguration) { if let contents = WgQuickConfigFileWriter.writeConfigFile(from: tunnelConfiguration) {
let name = tunnelConfiguration.interface.name let name = tunnelConfiguration.interface.name
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
} }
} }
do { do {
try ZipArchive.archive(inputs: inputsToArchiver, to: url) try ZipArchive.archive(inputs: inputsToArchiver, to: url)
} catch (let error as WireGuardAppError) { } catch let error as WireGuardAppError {
DispatchQueue.main.async { completion(error) } DispatchQueue.main.async { completion(error) }
return return
} catch { } catch {

View File

@ -7,7 +7,7 @@ enum ZipImporterError: WireGuardAppError {
case noTunnelsInZipArchive case noTunnelsInZipArchive
func alertText() -> (String, String)? { func alertText() -> (String, String)? {
switch (self) { switch self {
case .noTunnelsInZipArchive: case .noTunnelsInZipArchive:
return ("No tunnels in zip archive", "No .conf tunnel files were found inside the zip archive.") return ("No tunnels in zip archive", "No .conf tunnel files were found inside the zip archive.")
} }
@ -23,17 +23,17 @@ class ZipImporter {
for (index, unarchivedFile) in unarchivedFiles.enumerated().reversed() { for (index, unarchivedFile) in unarchivedFiles.enumerated().reversed() {
let fileBaseName = unarchivedFile.fileBaseName let fileBaseName = unarchivedFile.fileBaseName
let trimmedName = fileBaseName.trimmingCharacters(in: .whitespacesAndNewlines) let trimmedName = fileBaseName.trimmingCharacters(in: .whitespacesAndNewlines)
if (!trimmedName.isEmpty) { if !trimmedName.isEmpty {
unarchivedFiles[index].fileBaseName = trimmedName unarchivedFiles[index].fileBaseName = trimmedName
} else { } else {
unarchivedFiles.remove(at: index) unarchivedFiles.remove(at: index)
} }
} }
if (unarchivedFiles.isEmpty) { if unarchivedFiles.isEmpty {
throw ZipImporterError.noTunnelsInZipArchive throw ZipImporterError.noTunnelsInZipArchive
} }
} catch (let error as WireGuardAppError) { } catch let error as WireGuardAppError {
DispatchQueue.main.async { completion(.failure(error)) } DispatchQueue.main.async { completion(.failure(error)) }
return return
} catch { } catch {
@ -41,9 +41,9 @@ class ZipImporter {
} }
unarchivedFiles.sort { $0.fileBaseName < $1.fileBaseName } unarchivedFiles.sort { $0.fileBaseName < $1.fileBaseName }
var configs = Array<TunnelConfiguration?>(repeating: nil, count: unarchivedFiles.count) var configs: [TunnelConfiguration?] = Array(repeating: nil, count: unarchivedFiles.count)
for (index, file) in unarchivedFiles.enumerated() { for (index, file) in unarchivedFiles.enumerated() {
if (index > 0 && file == unarchivedFiles[index - 1]) { if index > 0 && file == unarchivedFiles[index - 1] {
continue continue
} }
guard let fileContents = String(data: file.contents, encoding: .utf8) else { guard let fileContents = String(data: file.contents, encoding: .utf8) else {

View File

@ -13,7 +13,7 @@ class DNSResolver {
static func isAllEndpointsAlreadyResolved(endpoints: [Endpoint?]) -> Bool { static func isAllEndpointsAlreadyResolved(endpoints: [Endpoint?]) -> Bool {
for endpoint in endpoints { for endpoint in endpoints {
guard let endpoint = endpoint else { continue } guard let endpoint = endpoint else { continue }
if (!endpoint.hasHostAsIPAddress()) { if !endpoint.hasHostAsIPAddress() {
return false return false
} }
} }
@ -23,14 +23,14 @@ class DNSResolver {
static func resolveSync(endpoints: [Endpoint?]) throws -> [Endpoint?] { static func resolveSync(endpoints: [Endpoint?]) throws -> [Endpoint?] {
let dispatchGroup: DispatchGroup = DispatchGroup() let dispatchGroup: DispatchGroup = DispatchGroup()
if (isAllEndpointsAlreadyResolved(endpoints: endpoints)) { if isAllEndpointsAlreadyResolved(endpoints: endpoints) {
return endpoints return endpoints
} }
var resolvedEndpoints: [Endpoint?] = Array<Endpoint?>(repeating: nil, count: endpoints.count) var resolvedEndpoints: [Endpoint?] = Array(repeating: nil, count: endpoints.count)
for (index, endpoint) in endpoints.enumerated() { for (index, endpoint) in endpoints.enumerated() {
guard let endpoint = endpoint else { continue } guard let endpoint = endpoint else { continue }
if (endpoint.hasHostAsIPAddress()) { if endpoint.hasHostAsIPAddress() {
resolvedEndpoints[index] = endpoint resolvedEndpoints[index] = endpoint
} else { } else {
let workItem = DispatchWorkItem { let workItem = DispatchWorkItem {
@ -48,14 +48,14 @@ class DNSResolver {
let endpoint = tuple.0 let endpoint = tuple.0
let resolvedEndpoint = tuple.1 let resolvedEndpoint = tuple.1
if let endpoint = endpoint { if let endpoint = endpoint {
if (resolvedEndpoint == nil) { if resolvedEndpoint == nil {
// DNS resolution failed // DNS resolution failed
guard let hostname = endpoint.hostname() else { fatalError() } guard let hostname = endpoint.hostname() else { fatalError() }
hostnamesWithDnsResolutionFailure.append(hostname) hostnamesWithDnsResolutionFailure.append(hostname)
} }
} }
} }
if (!hostnamesWithDnsResolutionFailure.isEmpty) { if !hostnamesWithDnsResolutionFailure.isEmpty {
throw DNSResolverError.dnsResolutionFailed(hostnames: hostnamesWithDnsResolutionFailure) throw DNSResolverError.dnsResolutionFailed(hostnames: hostnamesWithDnsResolutionFailure)
} }
return resolvedEndpoints return resolvedEndpoints
@ -76,7 +76,7 @@ extension DNSResolver {
ai_addr: nil, ai_addr: nil,
ai_next: nil) ai_next: nil)
var resultPointer = UnsafeMutablePointer<addrinfo>(OpaquePointer(bitPattern: 0)) var resultPointer = UnsafeMutablePointer<addrinfo>(OpaquePointer(bitPattern: 0))
switch (endpoint.host) { switch endpoint.host {
case .name(let name, _): case .name(let name, _):
// The endpoint is a hostname and needs DNS resolution // The endpoint is a hostname and needs DNS resolution
let returnValue = getaddrinfo( let returnValue = getaddrinfo(
@ -84,29 +84,29 @@ extension DNSResolver {
"\(endpoint.port)".cString(using: .utf8), // Port "\(endpoint.port)".cString(using: .utf8), // Port
&hints, &hints,
&resultPointer) &resultPointer)
if (returnValue == 0) { if returnValue == 0 {
// getaddrinfo succeeded // getaddrinfo succeeded
let ipv4Buffer = UnsafeMutablePointer<Int8>.allocate(capacity: Int(INET_ADDRSTRLEN)) let ipv4Buffer = UnsafeMutablePointer<Int8>.allocate(capacity: Int(INET_ADDRSTRLEN))
let ipv6Buffer = UnsafeMutablePointer<Int8>.allocate(capacity: Int(INET6_ADDRSTRLEN)) let ipv6Buffer = UnsafeMutablePointer<Int8>.allocate(capacity: Int(INET6_ADDRSTRLEN))
var ipv4AddressString: String? var ipv4AddressString: String?
var ipv6AddressString: String? var ipv6AddressString: String?
while (resultPointer != nil) { while resultPointer != nil {
let result = resultPointer!.pointee let result = resultPointer!.pointee
resultPointer = result.ai_next resultPointer = result.ai_next
if (result.ai_family == AF_INET && result.ai_addrlen == MemoryLayout<sockaddr_in>.size) { if result.ai_family == AF_INET && result.ai_addrlen == MemoryLayout<sockaddr_in>.size {
var sa4 = UnsafeRawPointer(result.ai_addr)!.assumingMemoryBound(to: sockaddr_in.self).pointee var sa4 = UnsafeRawPointer(result.ai_addr)!.assumingMemoryBound(to: sockaddr_in.self).pointee
if (inet_ntop(result.ai_family, &sa4.sin_addr, ipv4Buffer, socklen_t(INET_ADDRSTRLEN)) != nil) { if inet_ntop(result.ai_family, &sa4.sin_addr, ipv4Buffer, socklen_t(INET_ADDRSTRLEN)) != nil {
ipv4AddressString = String(cString: ipv4Buffer) ipv4AddressString = String(cString: ipv4Buffer)
// If we found an IPv4 address, we can stop // If we found an IPv4 address, we can stop
break break
} }
} else if (result.ai_family == AF_INET6 && result.ai_addrlen == MemoryLayout<sockaddr_in6>.size) { } else if result.ai_family == AF_INET6 && result.ai_addrlen == MemoryLayout<sockaddr_in6>.size {
if (ipv6AddressString != nil) { if ipv6AddressString != nil {
// If we already have an IPv6 address, we can skip this one // If we already have an IPv6 address, we can skip this one
continue continue
} }
var sa6 = UnsafeRawPointer(result.ai_addr)!.assumingMemoryBound(to: sockaddr_in6.self).pointee var sa6 = UnsafeRawPointer(result.ai_addr)!.assumingMemoryBound(to: sockaddr_in6.self).pointee
if (inet_ntop(result.ai_family, &sa6.sin6_addr, ipv6Buffer, socklen_t(INET6_ADDRSTRLEN)) != nil) { if inet_ntop(result.ai_family, &sa6.sin6_addr, ipv6Buffer, socklen_t(INET6_ADDRSTRLEN)) != nil {
ipv6AddressString = String(cString: ipv6Buffer) ipv6AddressString = String(cString: ipv6Buffer)
} }
} }

View File

@ -5,7 +5,7 @@ import NetworkExtension
class ErrorNotifier { class ErrorNotifier {
static func errorMessage(for error: PacketTunnelProviderError) -> (String, String)? { static func errorMessage(for error: PacketTunnelProviderError) -> (String, String)? {
switch (error) { switch error {
case .savedProtocolConfigurationIsInvalid: case .savedProtocolConfigurationIsInvalid:
return ("Activation failure", "Could not retrieve tunnel information from the saved configuration") return ("Activation failure", "Could not retrieve tunnel information from the saved configuration")
case .dnsResolutionFailure: case .dnsResolutionFailure:

View File

@ -75,13 +75,12 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
// Setup packetTunnelSettingsGenerator // Setup packetTunnelSettingsGenerator
let packetTunnelSettingsGenerator = PacketTunnelSettingsGenerator(tunnelConfiguration: tunnelConfiguration, let packetTunnelSettingsGenerator = PacketTunnelSettingsGenerator(tunnelConfiguration: tunnelConfiguration, resolvedEndpoints: resolvedEndpoints)
resolvedEndpoints: resolvedEndpoints)
// Bring up wireguard-go backend // Bring up wireguard-go backend
let fd = packetFlow.value(forKeyPath: "socket.fileDescriptor") as! Int32 let fileDescriptor = packetFlow.value(forKeyPath: "socket.fileDescriptor") as! Int32
if fd < 0 { if fileDescriptor < 0 {
wg_log(.error, staticMessage: "Starting tunnel failed: Could not determine file descriptor") wg_log(.error, staticMessage: "Starting tunnel failed: Could not determine file descriptor")
ErrorNotifier.notify(PacketTunnelProviderError.couldNotStartWireGuard, from: self) ErrorNotifier.notify(PacketTunnelProviderError.couldNotStartWireGuard, from: self)
startTunnelCompletionHandler(PacketTunnelProviderError.couldNotStartWireGuard) startTunnelCompletionHandler(PacketTunnelProviderError.couldNotStartWireGuard)
@ -111,7 +110,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
} }
networkMonitor?.start(queue: DispatchQueue(label: "NetworkMonitor")) networkMonitor?.start(queue: DispatchQueue(label: "NetworkMonitor"))
handle = connect(interfaceName: tunnelConfiguration.interface.name, settings: wireguardSettings, fd: fd) handle = connect(interfaceName: tunnelConfiguration.interface.name, settings: wireguardSettings, fileDescriptor: fileDescriptor)
if handle < 0 { if handle < 0 {
wg_log(.error, staticMessage: "Starting tunnel failed: Could not start WireGuard") wg_log(.error, staticMessage: "Starting tunnel failed: Could not start WireGuard")
@ -187,9 +186,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
} }
} }
private func connect(interfaceName: String, settings: String, fd: Int32) -> Int32 { // swiftlint:disable:this cyclomatic_complexity private func connect(interfaceName: String, settings: String, fileDescriptor: Int32) -> Int32 {
return withStringsAsGoStrings(interfaceName, settings) { (nameGoStr, settingsGoStr) -> Int32 in return withStringsAsGoStrings(interfaceName, settings) { (nameGoStr, settingsGoStr) -> Int32 in
return wgTurnOn(nameGoStr, settingsGoStr, fd) return wgTurnOn(nameGoStr, settingsGoStr, fileDescriptor)
} }
} }
@ -237,7 +236,7 @@ private func file_log(type: OSLogType, message: String) {
let formatter = DateFormatter() let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS: " formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS: "
var msgLine = formatter.string(from: Date()) + message var msgLine = formatter.string(from: Date()) + message
if (msgLine.last! != "\n") { if msgLine.last! != "\n" {
msgLine.append("\n") msgLine.append("\n")
} }
let data = msgLine.data(using: .utf8) let data = msgLine.data(using: .utf8)

View File

@ -36,7 +36,7 @@ class PacketTunnelSettingsGenerator {
if let listenPort = tunnelConfiguration.interface.listenPort { if let listenPort = tunnelConfiguration.interface.listenPort {
wgSettings.append("listen_port=\(listenPort)\n") wgSettings.append("listen_port=\(listenPort)\n")
} }
if (tunnelConfiguration.peers.count > 0) { if tunnelConfiguration.peers.count > 0 {
wgSettings.append("replace_peers=true\n") wgSettings.append("replace_peers=true\n")
} }
assert(tunnelConfiguration.peers.count == resolvedEndpoints.count) assert(tunnelConfiguration.peers.count == resolvedEndpoints.count)
@ -51,11 +51,9 @@ class PacketTunnelSettingsGenerator {
} }
let persistentKeepAlive = peer.persistentKeepAlive ?? 0 let persistentKeepAlive = peer.persistentKeepAlive ?? 0
wgSettings.append("persistent_keepalive_interval=\(persistentKeepAlive)\n") wgSettings.append("persistent_keepalive_interval=\(persistentKeepAlive)\n")
if (!peer.allowedIPs.isEmpty) { if !peer.allowedIPs.isEmpty {
wgSettings.append("replace_allowed_ips=true\n") wgSettings.append("replace_allowed_ips=true\n")
for ip in peer.allowedIPs { peer.allowedIPs.forEach { wgSettings.append("allowed_ip=\($0.stringRepresentation())\n") }
wgSettings.append("allowed_ip=\(ip.stringRepresentation())\n")
}
} }
} }
return wgSettings return wgSettings
@ -74,7 +72,7 @@ class PacketTunnelSettingsGenerator {
var remoteAddress: String = "0.0.0.0" var remoteAddress: String = "0.0.0.0"
let endpointsCompact = resolvedEndpoints.compactMap({ $0 }) let endpointsCompact = resolvedEndpoints.compactMap({ $0 })
if endpointsCompact.count == 1 { if endpointsCompact.count == 1 {
switch (endpointsCompact.first!.host) { switch endpointsCompact.first!.host {
case .ipv4(let address): case .ipv4(let address):
remoteAddress = "\(address)" remoteAddress = "\(address)"
case .ipv6(let address): case .ipv6(let address):
@ -96,7 +94,7 @@ class PacketTunnelSettingsGenerator {
// MTU // MTU
let mtu = tunnelConfiguration.interface.mtu ?? 0 let mtu = tunnelConfiguration.interface.mtu ?? 0
if (mtu == 0) { if mtu == 0 {
// 0 imples automatic MTU, where we set overhead as 80 bytes, which is the worst case for WireGuard // 0 imples automatic MTU, where we set overhead as 80 bytes, which is the worst case for WireGuard
networkSettings.tunnelOverheadBytes = 80 networkSettings.tunnelOverheadBytes = 80
} else { } else {
@ -112,10 +110,10 @@ class PacketTunnelSettingsGenerator {
var ipv6NetworkPrefixLengths: [NSNumber] = [] var ipv6NetworkPrefixLengths: [NSNumber] = []
for addressRange in tunnelConfiguration.interface.addresses { for addressRange in tunnelConfiguration.interface.addresses {
if (addressRange.address is IPv4Address) { if addressRange.address is IPv4Address {
ipv4Addresses.append("\(addressRange.address)") ipv4Addresses.append("\(addressRange.address)")
ipv4SubnetMasks.append(PacketTunnelSettingsGenerator.ipv4SubnetMaskString(of: addressRange)) ipv4SubnetMasks.append(PacketTunnelSettingsGenerator.ipv4SubnetMaskString(of: addressRange))
} else if (addressRange.address is IPv6Address) { } else if addressRange.address is IPv6Address {
ipv6Addresses.append("\(addressRange.address)") ipv6Addresses.append("\(addressRange.address)")
ipv6NetworkPrefixLengths.append(NSNumber(value: addressRange.networkPrefixLength)) ipv6NetworkPrefixLengths.append(NSNumber(value: addressRange.networkPrefixLength))
} }
@ -131,10 +129,10 @@ class PacketTunnelSettingsGenerator {
for peer in tunnelConfiguration.peers { for peer in tunnelConfiguration.peers {
for addressRange in peer.allowedIPs { for addressRange in peer.allowedIPs {
if (addressRange.address is IPv4Address) { if addressRange.address is IPv4Address {
ipv4IncludedRouteAddresses.append("\(addressRange.address)") ipv4IncludedRouteAddresses.append("\(addressRange.address)")
ipv4IncludedRouteSubnetMasks.append(PacketTunnelSettingsGenerator.ipv4SubnetMaskString(of: addressRange)) ipv4IncludedRouteSubnetMasks.append(PacketTunnelSettingsGenerator.ipv4SubnetMaskString(of: addressRange))
} else if (addressRange.address is IPv6Address) { } else if addressRange.address is IPv6Address {
ipv6IncludedRouteAddresses.append("\(addressRange.address)") ipv6IncludedRouteAddresses.append("\(addressRange.address)")
ipv6IncludedRouteNetworkPrefixLengths.append(NSNumber(value: addressRange.networkPrefixLength)) ipv6IncludedRouteNetworkPrefixLengths.append(NSNumber(value: addressRange.networkPrefixLength))
} }
@ -151,7 +149,7 @@ class PacketTunnelSettingsGenerator {
for endpoint in resolvedEndpoints { for endpoint in resolvedEndpoints {
guard let endpoint = endpoint else { continue } guard let endpoint = endpoint else { continue }
switch (endpoint.host) { switch endpoint.host {
case .ipv4(let address): case .ipv4(let address):
ipv4ExcludedRouteAddresses.append("\(address)") ipv4ExcludedRouteAddresses.append("\(address)")
ipv4ExcludedRouteSubnetMasks.append("255.255.255.255") // A single IPv4 address ipv4ExcludedRouteSubnetMasks.append("255.255.255.255") // A single IPv4 address