aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2019-01-24 00:00:46 +0100
committerJason A. Donenfeld <Jason@zx2c4.com>2019-01-24 01:37:57 +0100
commit22625e8cc4792a71174af5fc69f0f63ba4b808d8 (patch)
tree2907ae3cde1b5d61acc831374058e9c4bde4b9f0
parentwireguard-go-bridge: allow querying internal settings (diff)
downloadwireguard-apple-22625e8cc4792a71174af5fc69f0f63ba4b808d8.tar.xz
wireguard-apple-22625e8cc4792a71174af5fc69f0f63ba4b808d8.zip
Tunnel: support getting runtime configuration
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
-rw-r--r--WireGuard/Shared/Model/PeerConfiguration.swift3
-rw-r--r--WireGuard/Shared/Model/TunnelConfiguration+WgQuickConfig.swift2
-rw-r--r--WireGuard/WireGuard.xcodeproj/project.pbxproj6
-rw-r--r--WireGuard/WireGuard/Base.lproj/Localizable.strings3
-rw-r--r--WireGuard/WireGuard/Tunnel/TunnelConfiguration+UapiConfig.swift207
-rw-r--r--WireGuard/WireGuard/Tunnel/TunnelsManager.swift17
-rw-r--r--WireGuard/WireGuard/UI/TunnelViewModel.swift18
-rw-r--r--WireGuard/WireGuard/UI/macOS/ParseError+WireGuardAppError.swift4
-rw-r--r--WireGuard/WireGuardNetworkExtension/PacketTunnelProvider.swift18
9 files changed, 278 insertions, 0 deletions
diff --git a/WireGuard/Shared/Model/PeerConfiguration.swift b/WireGuard/Shared/Model/PeerConfiguration.swift
index 5271aa61..7fd3f87e 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 625c25d4..043914a4 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 18f09354..1d800178 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 = "<group>"; };
5FF7B96121CC95DE00A7DD74 /* InterfaceConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterfaceConfiguration.swift; sourceTree = "<group>"; };
5FF7B96421CC95FA00A7DD74 /* PeerConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeerConfiguration.swift; sourceTree = "<group>"; };
+ 6B707D8321F918D4000A8F73 /* TunnelConfiguration+UapiConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TunnelConfiguration+UapiConfig.swift"; sourceTree = "<group>"; };
6F4DD16721DA552B00690EAE /* NSTableView+Reuse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTableView+Reuse.swift"; sourceTree = "<group>"; };
6F4DD16A21DA558800690EAE /* TunnelListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelListRow.swift; sourceTree = "<group>"; };
6F4DD16D21DBEA0700690EAE /* ManageTunnelsRootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManageTunnelsRootViewController.swift; sourceTree = "<group>"; };
@@ -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 0b2a33d0..2133331f 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 00000000..63a8570f
--- /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[..<equalsIndex])
+ value = String(line[line.index(equalsIndex, offsetBy: 1)...])
+ }
+
+ if line.isEmpty || key == "public_key" {
+ // Previous section has ended; process the attributes collected so far
+ if parserState == .inInterfaceSection {
+ let interface = try TunnelConfiguration.collate(interfaceAttributes: attributes)
+ guard interfaceConfiguration == nil else { throw ParseError.multipleInterfaces }
+ interfaceConfiguration = interface
+ parserState = .inPeerSection
+ } else if parserState == .inPeerSection {
+ let peer = try TunnelConfiguration.collate(peerAttributes: attributes)
+ peerConfigurations.append(peer)
+ }
+ attributes.removeAll()
+ if line.isEmpty {
+ break
+ }
+ }
+
+ if let presentValue = attributes[key] {
+ if key == "allowed_ip" {
+ attributes[key] = presentValue + "," + value
+ } else {
+ throw ParseError.multipleEntriesForKey(key)
+ }
+ } else {
+ attributes[key] = value
+ }
+
+ let interfaceSectionKeys: Set<String> = ["private_key", "listen_port", "fwmark"]
+ let peerSectionKeys: Set<String> = ["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<Data>(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..<preSharedKey.count {
+ accumulator |= preSharedKey[index]
+ }
+ if accumulator != 0 {
+ peer.preSharedKey = preSharedKey
+ }
+ }
+ if let allowedIPsString = attributes["allowed_ip"] {
+ var allowedIPs = [IPAddressRange]()
+ for allowedIPString in allowedIPsString.splitToArray(trimmingCharacters: .whitespacesAndNewlines) {
+ guard let allowedIP = IPAddressRange(from: allowedIPString) else {
+ throw ParseError.peerHasInvalidAllowedIP(allowedIPString)
+ }
+ allowedIPs.append(allowedIP)
+ }
+ peer.allowedIPs = allowedIPs
+ }
+ if let endpointString = attributes["endpoint"] {
+ guard let endpoint = Endpoint(from: endpointString) else {
+ throw ParseError.peerHasInvalidEndpoint(endpointString)
+ }
+ peer.endpoint = endpoint
+ }
+ if let persistentKeepAliveString = attributes["persistent_keepalive_interval"] {
+ guard let persistentKeepAlive = UInt16(persistentKeepAliveString) else {
+ throw ParseError.peerHasInvalidPersistentKeepAlive(persistentKeepAliveString)
+ }
+ if persistentKeepAlive != 0 {
+ peer.persistentKeepAlive = persistentKeepAlive
+ }
+ }
+ if let rxBytesString = attributes["rx_bytes"] {
+ guard let rxBytes = UInt64(rxBytesString) else {
+ throw ParseError.peerHasInvalidTransferBytes(rxBytesString)
+ }
+ if rxBytes != 0 {
+ peer.rxBytes = rxBytes
+ }
+ }
+ if let txBytesString = attributes["tx_bytes"] {
+ guard let txBytes = UInt64(txBytesString) else {
+ throw ParseError.peerHasInvalidTransferBytes(txBytesString)
+ }
+ if txBytes != 0 {
+ peer.txBytes = txBytes
+ }
+ }
+ if let lastHandshakeTimeSecString = attributes["last_handshake_time_sec"] {
+ var lastHandshakeTimeSince1970: TimeInterval = 0
+ guard let lastHandshakeTimeSec = UInt64(lastHandshakeTimeSecString) else {
+ throw ParseError.peerHasInvalidLastHandshakeTime(lastHandshakeTimeSecString)
+ }
+ if lastHandshakeTimeSec != 0 {
+ lastHandshakeTimeSince1970 += Double(lastHandshakeTimeSec)
+ if let lastHandshakeTimeNsecString = attributes["last_handshake_time_nsec"] {
+ guard let lastHandshakeTimeNsec = UInt64(lastHandshakeTimeNsecString) else {
+ throw ParseError.peerHasInvalidLastHandshakeTime(lastHandshakeTimeNsecString)
+ }
+ lastHandshakeTimeSince1970 += Double(lastHandshakeTimeNsec) / 1000000000.0
+ }
+ peer.lastHandshakeTime = Date(timeIntervalSince1970: lastHandshakeTimeSince1970)
+ }
+ }
+ return peer
+ }
+}
+
+extension Data {
+ //swiftlint:disable identifier_name
+ init?(hexEncoded hexString: String) {
+ if hexString.count % 2 != 0 {
+ return nil
+ }
+ let len = hexString.count / 2
+ self.init(capacity: len)
+ for i in 0..<len {
+ let j = hexString.index(hexString.startIndex, offsetBy: i * 2)
+ let k = hexString.index(j, offsetBy: 2)
+ let bytes = hexString[j..<k]
+ if var num = UInt8(bytes, radix: 16) {
+ append(&num, count: 1)
+ } else {
+ return nil
+ }
+ }
+ }
+}
diff --git a/WireGuard/WireGuard/Tunnel/TunnelsManager.swift b/WireGuard/WireGuard/Tunnel/TunnelsManager.swift
index 75d82304..5e6ad5c1 100644
--- a/WireGuard/WireGuard/Tunnel/TunnelsManager.swift
+++ b/WireGuard/WireGuard/Tunnel/TunnelsManager.swift
@@ -397,6 +397,23 @@ class TunnelContainer: NSObject {
super.init()
}
+ func getRuntimeTunnelConfiguration(completionHandler: @escaping ((TunnelConfiguration?) -> 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 07d1bac1..9124a004 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 9f2c0090..6afbadb8 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 7c84491a..5ac6b282 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