From 22625e8cc4792a71174af5fc69f0f63ba4b808d8 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Thu, 24 Jan 2019 00:00:46 +0100 Subject: Tunnel: support getting runtime configuration Signed-off-by: Jason A. Donenfeld --- WireGuard/Shared/Model/PeerConfiguration.swift | 3 + .../Model/TunnelConfiguration+WgQuickConfig.swift | 2 + WireGuard/WireGuard.xcodeproj/project.pbxproj | 6 + WireGuard/WireGuard/Base.lproj/Localizable.strings | 3 + .../Tunnel/TunnelConfiguration+UapiConfig.swift | 207 +++++++++++++++++++++ WireGuard/WireGuard/Tunnel/TunnelsManager.swift | 17 ++ WireGuard/WireGuard/UI/TunnelViewModel.swift | 18 ++ .../UI/macOS/ParseError+WireGuardAppError.swift | 4 + .../PacketTunnelProvider.swift | 18 ++ 9 files changed, 278 insertions(+) create mode 100644 WireGuard/WireGuard/Tunnel/TunnelConfiguration+UapiConfig.swift (limited to 'WireGuard') diff --git a/WireGuard/Shared/Model/PeerConfiguration.swift b/WireGuard/Shared/Model/PeerConfiguration.swift index 5271aa6..7fd3f87 100644 --- a/WireGuard/Shared/Model/PeerConfiguration.swift +++ b/WireGuard/Shared/Model/PeerConfiguration.swift @@ -17,6 +17,9 @@ struct PeerConfiguration { var allowedIPs = [IPAddressRange]() var endpoint: Endpoint? var persistentKeepAlive: UInt16? + var rxBytes: UInt64? + var txBytes: UInt64? + var lastHandshakeTime: Date? init(publicKey: Data) { self.publicKey = publicKey diff --git a/WireGuard/Shared/Model/TunnelConfiguration+WgQuickConfig.swift b/WireGuard/Shared/Model/TunnelConfiguration+WgQuickConfig.swift index 625c25d..043914a 100644 --- a/WireGuard/Shared/Model/TunnelConfiguration+WgQuickConfig.swift +++ b/WireGuard/Shared/Model/TunnelConfiguration+WgQuickConfig.swift @@ -28,6 +28,8 @@ extension TunnelConfiguration { case peerHasInvalidAllowedIP(String) case peerHasInvalidEndpoint(String) case peerHasInvalidPersistentKeepAlive(String) + case peerHasInvalidTransferBytes(String) + case peerHasInvalidLastHandshakeTime(String) case peerHasUnrecognizedKey(String) case multiplePeersWithSamePublicKey case multipleEntriesForKey(String) diff --git a/WireGuard/WireGuard.xcodeproj/project.pbxproj b/WireGuard/WireGuard.xcodeproj/project.pbxproj index 18f0935..1d80017 100644 --- a/WireGuard/WireGuard.xcodeproj/project.pbxproj +++ b/WireGuard/WireGuard.xcodeproj/project.pbxproj @@ -30,6 +30,8 @@ 5FF7B96321CC95DE00A7DD74 /* InterfaceConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FF7B96121CC95DE00A7DD74 /* InterfaceConfiguration.swift */; }; 5FF7B96521CC95FA00A7DD74 /* PeerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FF7B96421CC95FA00A7DD74 /* PeerConfiguration.swift */; }; 5FF7B96621CC95FA00A7DD74 /* PeerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FF7B96421CC95FA00A7DD74 /* PeerConfiguration.swift */; }; + 6B707D8421F918D4000A8F73 /* TunnelConfiguration+UapiConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B707D8321F918D4000A8F73 /* TunnelConfiguration+UapiConfig.swift */; }; + 6B707D8621F918D4000A8F73 /* TunnelConfiguration+UapiConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B707D8321F918D4000A8F73 /* TunnelConfiguration+UapiConfig.swift */; }; 6F4DD16B21DA558800690EAE /* TunnelListRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16A21DA558800690EAE /* TunnelListRow.swift */; }; 6F4DD16C21DA558F00690EAE /* NSTableView+Reuse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16721DA552B00690EAE /* NSTableView+Reuse.swift */; }; 6F4DD16E21DBEA0700690EAE /* ManageTunnelsRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16D21DBEA0700690EAE /* ManageTunnelsRootViewController.swift */; }; @@ -236,6 +238,7 @@ 5F9696AF21CD7128008063FE /* TunnelConfiguration+WgQuickConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TunnelConfiguration+WgQuickConfig.swift"; sourceTree = ""; }; 5FF7B96121CC95DE00A7DD74 /* InterfaceConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterfaceConfiguration.swift; sourceTree = ""; }; 5FF7B96421CC95FA00A7DD74 /* PeerConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeerConfiguration.swift; sourceTree = ""; }; + 6B707D8321F918D4000A8F73 /* TunnelConfiguration+UapiConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TunnelConfiguration+UapiConfig.swift"; sourceTree = ""; }; 6F4DD16721DA552B00690EAE /* NSTableView+Reuse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTableView+Reuse.swift"; sourceTree = ""; }; 6F4DD16A21DA558800690EAE /* TunnelListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelListRow.swift; sourceTree = ""; }; 6F4DD16D21DBEA0700690EAE /* ManageTunnelsRootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManageTunnelsRootViewController.swift; sourceTree = ""; }; @@ -487,6 +490,7 @@ isa = PBXGroup; children = ( 6F7774EE21722D97006A79B3 /* TunnelsManager.swift */, + 6B707D8321F918D4000A8F73 /* TunnelConfiguration+UapiConfig.swift */, 6FFA5DA32197085D0001E2F7 /* ActivateOnDemandSetting.swift */, 5F4541A821C451D100994C13 /* TunnelStatus.swift */, 6FB1017821C57DE600766195 /* MockTunnels.swift */, @@ -1111,6 +1115,7 @@ 6F4DD16E21DBEA0700690EAE /* ManageTunnelsRootViewController.swift in Sources */, 6F4DD16C21DA558F00690EAE /* NSTableView+Reuse.swift in Sources */, 6FB1BDD821D50F5300A991BF /* WireGuardResult.swift in Sources */, + 6B707D8621F918D4000A8F73 /* TunnelConfiguration+UapiConfig.swift in Sources */, 6FB1BDD921D50F5300A991BF /* LocalizationHelper.swift in Sources */, 6F89E17C21F090CC00C97BB9 /* TunnelsTracker.swift in Sources */, 6FCD99B121E0EDA900BA4C82 /* TunnelEditViewController.swift in Sources */, @@ -1218,6 +1223,7 @@ 6FDEF802218646BA00D8FBF6 /* ZipArchive.swift in Sources */, 5F45419021C2D53800994C13 /* SwitchCell.swift in Sources */, 6FB1017921C57DE600766195 /* MockTunnels.swift in Sources */, + 6B707D8421F918D4000A8F73 /* TunnelConfiguration+UapiConfig.swift in Sources */, 6FDEF806218725D200D8FBF6 /* SettingsTableViewController.swift in Sources */, 5F4541A221C2D6DF00994C13 /* BorderedTextButton.swift in Sources */, 5FF7B96521CC95FA00A7DD74 /* PeerConfiguration.swift in Sources */, diff --git a/WireGuard/WireGuard/Base.lproj/Localizable.strings b/WireGuard/WireGuard/Base.lproj/Localizable.strings index 0b2a33d..2133331 100644 --- a/WireGuard/WireGuard/Base.lproj/Localizable.strings +++ b/WireGuard/WireGuard/Base.lproj/Localizable.strings @@ -62,6 +62,9 @@ "tunnelPeerEndpoint" = "Endpoint"; "tunnelPeerPersistentKeepalive" = "Persistent keepalive"; "tunnelPeerAllowedIPs" = "Allowed IPs"; +"tunnelPeerRxBytes" = "Data received"; +"tunnelPeerTxBytes" = "Data sent"; +"tunnelPeerLastHandshakeTime" = "Latest handshake"; "tunnelPeerExcludePrivateIPs" = "Exclude private IPs"; "tunnelSectionTitleOnDemand" = "On-Demand Activation"; diff --git a/WireGuard/WireGuard/Tunnel/TunnelConfiguration+UapiConfig.swift b/WireGuard/WireGuard/Tunnel/TunnelConfiguration+UapiConfig.swift new file mode 100644 index 0000000..63a8570 --- /dev/null +++ b/WireGuard/WireGuard/Tunnel/TunnelConfiguration+UapiConfig.swift @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: MIT +// Copyright © 2018-2019 WireGuard LLC. All Rights Reserved. + +import Foundation + +extension TunnelConfiguration { + //swiftlint:disable:next function_body_length cyclomatic_complexity + convenience init(fromUapiConfig uapiConfig: String, basedOn base: TunnelConfiguration? = nil) throws { + var interfaceConfiguration: InterfaceConfiguration? + var peerConfigurations = [PeerConfiguration]() + + var lines = uapiConfig.split(separator: "\n") + lines.append("") + + var parserState = ParserState.inInterfaceSection + var attributes = [String: String]() + + for line in lines { + var key = "" + var value = "" + + if !line.isEmpty { + guard let equalsIndex = line.firstIndex(of: "=") else { throw ParseError.invalidLine(line) } + key = String(line[.. = ["private_key", "listen_port", "fwmark"] + let peerSectionKeys: Set = ["public_key", "preshared_key", "allowed_ip", "endpoint", "persistent_keepalive_interval", "last_handshake_time_sec", "last_handshake_time_nsec", "rx_bytes", "tx_bytes", "protocol_version"] + + if parserState == .inInterfaceSection { + guard interfaceSectionKeys.contains(key) else { + throw ParseError.interfaceHasUnrecognizedKey(key) + } + } + if parserState == .inPeerSection { + guard peerSectionKeys.contains(key) else { + throw ParseError.peerHasUnrecognizedKey(key) + } + } + } + + let peerPublicKeysArray = peerConfigurations.map { $0.publicKey } + let peerPublicKeysSet = Set(peerPublicKeysArray) + if peerPublicKeysArray.count != peerPublicKeysSet.count { + throw ParseError.multiplePeersWithSamePublicKey + } + + interfaceConfiguration?.addresses = base?.interface.addresses ?? [] + interfaceConfiguration?.dns = base?.interface.dns ?? [] + interfaceConfiguration?.mtu = base?.interface.mtu + + if let interfaceConfiguration = interfaceConfiguration { + self.init(name: base?.name, interface: interfaceConfiguration, peers: peerConfigurations) + } else { + throw ParseError.noInterface + } + } + + private static func collate(interfaceAttributes attributes: [String: String]) throws -> InterfaceConfiguration { + guard let privateKeyString = attributes["private_key"] else { + throw ParseError.interfaceHasNoPrivateKey + } + guard let privateKey = Data(hexEncoded: privateKeyString), privateKey.count == TunnelConfiguration.keyLength else { + throw ParseError.interfaceHasInvalidPrivateKey(privateKeyString) + } + var interface = InterfaceConfiguration(privateKey: privateKey) + if let listenPortString = attributes["listen_port"] { + guard let listenPort = UInt16(listenPortString) else { + throw ParseError.interfaceHasInvalidListenPort(listenPortString) + } + if listenPort != 0 { + interface.listenPort = listenPort + } + } + return interface + } + + //swiftlint:disable:next cyclomatic_complexity + private static func collate(peerAttributes attributes: [String: String]) throws -> PeerConfiguration { + guard let publicKeyString = attributes["public_key"] else { + throw ParseError.peerHasNoPublicKey + } + guard let publicKey = Data(hexEncoded: publicKeyString), publicKey.count == TunnelConfiguration.keyLength else { + throw ParseError.peerHasInvalidPublicKey(publicKeyString) + } + var peer = PeerConfiguration(publicKey: publicKey) + if let preSharedKeyString = attributes["preshared_key"] { + guard let preSharedKey = Data(hexEncoded: preSharedKeyString), preSharedKey.count == TunnelConfiguration.keyLength else { + throw ParseError.peerHasInvalidPreSharedKey(preSharedKeyString) + } + // TODO(zx2c4): does the compiler optimize this away? + var accumulator: UInt8 = 0 + for index in 0.. Void)) { + guard status != .inactive, let session = tunnelProvider.connection as? NETunnelProviderSession else { + completionHandler(tunnelConfiguration) + return + } + guard nil != (try? session.sendProviderMessage(Data(bytes: [ 0 ]), responseHandler: { + guard self.status != .inactive, let data = $0, let base = self.tunnelConfiguration, let settings = String(data: data, encoding: .utf8) else { + completionHandler(self.tunnelConfiguration) + return + } + completionHandler((try? TunnelConfiguration(fromUapiConfig: settings, basedOn: base)) ?? self.tunnelConfiguration) + })) else { + completionHandler(tunnelConfiguration) + return + } + } + func refreshStatus() { let status = TunnelStatus(from: tunnelProvider.connection.status) self.status = status diff --git a/WireGuard/WireGuard/UI/TunnelViewModel.swift b/WireGuard/WireGuard/UI/TunnelViewModel.swift index 07d1bac..9124a00 100644 --- a/WireGuard/WireGuard/UI/TunnelViewModel.swift +++ b/WireGuard/WireGuard/UI/TunnelViewModel.swift @@ -40,6 +40,9 @@ class TunnelViewModel { case endpoint case persistentKeepAlive case allowedIPs + case rxBytes + case txBytes + case lastHandshakeTime case excludePrivateIPs case deletePeer @@ -50,6 +53,9 @@ class TunnelViewModel { case .endpoint: return tr("tunnelPeerEndpoint") case .persistentKeepAlive: return tr("tunnelPeerPersistentKeepalive") case .allowedIPs: return tr("tunnelPeerAllowedIPs") + case .rxBytes: return tr("tunnelPeerRxBytes") + case .txBytes: return tr("tunnelPeerTxBytes") + case .lastHandshakeTime: return tr("tunnelPeerLastHandshakeTime") case .excludePrivateIPs: return tr("tunnelPeerExcludePrivateIPs") case .deletePeer: return tr("deletePeerButtonTitle") } @@ -248,6 +254,18 @@ class TunnelViewModel { if let persistentKeepAlive = config.persistentKeepAlive { scratchpad[.persistentKeepAlive] = String(persistentKeepAlive) } + // TODO(roopc): These next 3 fields should be prettier + // - bytes() in https://git.zx2c4.com/WireGuard/tree/src/tools/show.c#n185 + // - ago() in https://git.zx2c4.com/WireGuard/tree/src/tools/show.c#n158 + if let rxBytes = config.rxBytes { + scratchpad[.rxBytes] = String(rxBytes) + } + if let txBytes = config.txBytes { + scratchpad[.txBytes] = String(txBytes) + } + if let lastHandshakeTime = config.lastHandshakeTime { + scratchpad[.lastHandshakeTime] = lastHandshakeTime.description + } updateExcludePrivateIPsFieldState() } diff --git a/WireGuard/WireGuard/UI/macOS/ParseError+WireGuardAppError.swift b/WireGuard/WireGuard/UI/macOS/ParseError+WireGuardAppError.swift index 9f2c009..6afbadb 100644 --- a/WireGuard/WireGuard/UI/macOS/ParseError+WireGuardAppError.swift +++ b/WireGuard/WireGuard/UI/macOS/ParseError+WireGuardAppError.swift @@ -43,6 +43,10 @@ extension TunnelConfiguration.ParseError: WireGuardAppError { return (tr(format: "macAlertPersistentKeepliveInvalid (%@)", value), tr("alertInvalidPeerMessagePersistentKeepaliveInvalid")) case .peerHasUnrecognizedKey(let value): return (tr(format: "macAlertUnrecognizedPeerKey (%@)", value), tr("macAlertInfoUnrecognizedPeerKey")) + case .peerHasInvalidTransferBytes(let line): + return (tr(format: "macAlertInvalidLine (%@)", String(line)), "") + case .peerHasInvalidLastHandshakeTime(let line): + return (tr(format: "macAlertInvalidLine (%@)", String(line)), "") case .multiplePeersWithSamePublicKey: return (tr("alertInvalidPeerMessagePublicKeyDuplicated"), "") case .multipleEntriesForKey(let value): diff --git a/WireGuard/WireGuardNetworkExtension/PacketTunnelProvider.swift b/WireGuard/WireGuardNetworkExtension/PacketTunnelProvider.swift index 7c84491..5ac6b28 100644 --- a/WireGuard/WireGuardNetworkExtension/PacketTunnelProvider.swift +++ b/WireGuard/WireGuardNetworkExtension/PacketTunnelProvider.swift @@ -97,6 +97,24 @@ class PacketTunnelProvider: NEPacketTunnelProvider { completionHandler() } + override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) { + guard let completionHandler = completionHandler else { return } + guard let handle = handle else { + completionHandler(nil) + return + } + if messageData.count == 1 && messageData[0] == 0 { + guard let settings = wgGetConfig(handle) else { + completionHandler(nil) + return + } + completionHandler(String(cString: settings).data(using: .utf8)!) + free(settings) + } else { + completionHandler(nil) + } + } + private func configureLogger() { Logger.configureGlobal(withFilePath: FileManager.networkExtensionLogFileURL?.path) wgSetLogger { level, msgC in -- cgit v1.2.3-59-g8ed1b