2020-11-04 15:59:33 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2023-02-14 15:10:32 +00:00
|
|
|
// Copyright © 2018-2023 WireGuard LLC. All Rights Reserved.
|
2020-11-04 15:59:33 +00:00
|
|
|
|
|
|
|
import Foundation
|
|
|
|
import NetworkExtension
|
2020-12-02 15:10:44 +00:00
|
|
|
|
|
|
|
#if SWIFT_PACKAGE
|
2020-12-02 11:32:20 +00:00
|
|
|
import WireGuardKitGo
|
2021-06-17 11:23:22 +00:00
|
|
|
import WireGuardKitC
|
2020-12-02 15:10:44 +00:00
|
|
|
#endif
|
2020-11-04 15:59:33 +00:00
|
|
|
|
|
|
|
public enum WireGuardAdapterError: Error {
|
2020-12-02 12:42:15 +00:00
|
|
|
/// Failure to locate tunnel file descriptor.
|
|
|
|
case cannotLocateTunnelFileDescriptor
|
2020-11-04 15:59:33 +00:00
|
|
|
|
2020-12-03 12:19:31 +00:00
|
|
|
/// Failure to perform an operation in such state.
|
2020-11-04 15:59:33 +00:00
|
|
|
case invalidState
|
|
|
|
|
2020-12-03 12:19:31 +00:00
|
|
|
/// Failure to resolve endpoints.
|
2020-11-04 15:59:33 +00:00
|
|
|
case dnsResolution([DNSResolutionError])
|
|
|
|
|
2020-12-03 12:19:31 +00:00
|
|
|
/// Failure to set network settings.
|
2020-11-04 15:59:33 +00:00
|
|
|
case setNetworkSettings(Error)
|
|
|
|
|
2020-12-03 12:19:31 +00:00
|
|
|
/// Failure to start WireGuard backend.
|
2020-11-04 15:59:33 +00:00
|
|
|
case startWireGuardBackend(Int32)
|
|
|
|
}
|
|
|
|
|
2020-12-01 10:18:31 +00:00
|
|
|
/// Enum representing internal state of the `WireGuardAdapter`
|
|
|
|
private enum State {
|
|
|
|
/// The tunnel is stopped
|
|
|
|
case stopped
|
|
|
|
|
|
|
|
/// The tunnel is up and running
|
|
|
|
case started(_ handle: Int32, _ settingsGenerator: PacketTunnelSettingsGenerator)
|
|
|
|
|
|
|
|
/// The tunnel is temporarily shutdown due to device going offline
|
|
|
|
case temporaryShutdown(_ settingsGenerator: PacketTunnelSettingsGenerator)
|
|
|
|
}
|
|
|
|
|
2020-11-04 15:59:33 +00:00
|
|
|
public class WireGuardAdapter {
|
|
|
|
public typealias LogHandler = (WireGuardLogLevel, String) -> Void
|
|
|
|
|
|
|
|
/// Network routes monitor.
|
|
|
|
private var networkMonitor: NWPathMonitor?
|
|
|
|
|
|
|
|
/// Packet tunnel provider.
|
|
|
|
private weak var packetTunnelProvider: NEPacketTunnelProvider?
|
|
|
|
|
|
|
|
/// Log handler closure.
|
2020-12-02 12:48:24 +00:00
|
|
|
private let logHandler: LogHandler
|
2020-11-04 15:59:33 +00:00
|
|
|
|
|
|
|
/// Private queue used to synchronize access to `WireGuardAdapter` members.
|
|
|
|
private let workQueue = DispatchQueue(label: "WireGuardAdapterWorkQueue")
|
|
|
|
|
2020-12-01 10:18:31 +00:00
|
|
|
/// Adapter state.
|
|
|
|
private var state: State = .stopped
|
2020-11-04 15:59:33 +00:00
|
|
|
|
|
|
|
/// Tunnel device file descriptor.
|
|
|
|
private var tunnelFileDescriptor: Int32? {
|
2021-06-16 15:09:40 +00:00
|
|
|
var ctlInfo = ctl_info()
|
|
|
|
withUnsafeMutablePointer(to: &ctlInfo.ctl_name) {
|
|
|
|
$0.withMemoryRebound(to: CChar.self, capacity: MemoryLayout.size(ofValue: $0.pointee)) {
|
|
|
|
_ = strcpy($0, "com.apple.net.utun_control")
|
|
|
|
}
|
|
|
|
}
|
2021-06-16 13:56:21 +00:00
|
|
|
for fd: Int32 in 0...1024 {
|
2021-06-16 15:09:40 +00:00
|
|
|
var addr = sockaddr_ctl()
|
|
|
|
var ret: Int32 = -1
|
|
|
|
var len = socklen_t(MemoryLayout.size(ofValue: addr))
|
|
|
|
withUnsafeMutablePointer(to: &addr) {
|
|
|
|
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
|
|
|
|
ret = getpeername(fd, $0, &len)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ret != 0 || addr.sc_family != AF_SYSTEM {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if ctlInfo.ctl_id == 0 {
|
|
|
|
ret = ioctl(fd, CTLIOCGINFO, &ctlInfo)
|
|
|
|
if ret != 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if addr.sc_id == ctlInfo.ctl_id {
|
2021-06-16 13:56:21 +00:00
|
|
|
return fd
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
2020-11-04 15:59:33 +00:00
|
|
|
}
|
|
|
|
|
2020-12-02 12:38:38 +00:00
|
|
|
/// Returns a WireGuard version.
|
2020-12-02 17:05:17 +00:00
|
|
|
class var backendVersion: String {
|
2021-03-08 19:29:12 +00:00
|
|
|
guard let ver = wgVersion() else { return "unknown" }
|
|
|
|
let str = String(cString: ver)
|
|
|
|
free(UnsafeMutableRawPointer(mutating: ver))
|
|
|
|
return str
|
2020-11-04 15:59:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the tunnel device interface name, or nil on error.
|
|
|
|
/// - Returns: String.
|
|
|
|
public var interfaceName: String? {
|
|
|
|
guard let tunnelFileDescriptor = self.tunnelFileDescriptor else { return nil }
|
|
|
|
|
|
|
|
var buffer = [UInt8](repeating: 0, count: Int(IFNAMSIZ))
|
|
|
|
|
2020-12-02 14:08:45 +00:00
|
|
|
return buffer.withUnsafeMutableBufferPointer { mutableBufferPointer in
|
2020-11-04 15:59:33 +00:00
|
|
|
guard let baseAddress = mutableBufferPointer.baseAddress else { return nil }
|
|
|
|
|
|
|
|
var ifnameSize = socklen_t(IFNAMSIZ)
|
|
|
|
let result = getsockopt(
|
|
|
|
tunnelFileDescriptor,
|
|
|
|
2 /* SYSPROTO_CONTROL */,
|
|
|
|
2 /* UTUN_OPT_IFNAME */,
|
|
|
|
baseAddress,
|
|
|
|
&ifnameSize)
|
|
|
|
|
|
|
|
if result == 0 {
|
|
|
|
return String(cString: baseAddress)
|
|
|
|
} else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - Initialization
|
|
|
|
|
|
|
|
/// Designated initializer.
|
|
|
|
/// - Parameter packetTunnelProvider: an instance of `NEPacketTunnelProvider`. Internally stored
|
|
|
|
/// as a weak reference.
|
2020-12-02 12:48:24 +00:00
|
|
|
/// - Parameter logHandler: a log handler closure.
|
|
|
|
public init(with packetTunnelProvider: NEPacketTunnelProvider, logHandler: @escaping LogHandler) {
|
2020-11-04 15:59:33 +00:00
|
|
|
self.packetTunnelProvider = packetTunnelProvider
|
2020-12-02 12:48:24 +00:00
|
|
|
self.logHandler = logHandler
|
|
|
|
|
|
|
|
setupLogHandler()
|
2020-11-04 15:59:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
deinit {
|
2020-12-02 12:48:24 +00:00
|
|
|
// Force remove logger to make sure that no further calls to the instance of this class
|
2020-11-04 15:59:33 +00:00
|
|
|
// can happen after deallocation.
|
2020-12-02 12:48:24 +00:00
|
|
|
wgSetLogger(nil, nil)
|
2020-11-04 15:59:33 +00:00
|
|
|
|
|
|
|
// Cancel network monitor
|
|
|
|
networkMonitor?.cancel()
|
|
|
|
|
|
|
|
// Shutdown the tunnel
|
2020-12-01 10:18:31 +00:00
|
|
|
if case .started(let handle, _) = self.state {
|
2020-11-04 15:59:33 +00:00
|
|
|
wgTurnOff(handle)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - Public methods
|
|
|
|
|
|
|
|
/// Returns a runtime configuration from WireGuard.
|
|
|
|
/// - Parameter completionHandler: completion handler.
|
|
|
|
public func getRuntimeConfiguration(completionHandler: @escaping (String?) -> Void) {
|
|
|
|
workQueue.async {
|
2020-12-01 10:18:31 +00:00
|
|
|
guard case .started(let handle, _) = self.state else {
|
2020-11-04 15:59:33 +00:00
|
|
|
completionHandler(nil)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if let settings = wgGetConfig(handle) {
|
|
|
|
completionHandler(String(cString: settings))
|
|
|
|
free(settings)
|
|
|
|
} else {
|
|
|
|
completionHandler(nil)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Start the tunnel tunnel.
|
|
|
|
/// - Parameters:
|
|
|
|
/// - tunnelConfiguration: tunnel configuration.
|
|
|
|
/// - completionHandler: completion handler.
|
|
|
|
public func start(tunnelConfiguration: TunnelConfiguration, completionHandler: @escaping (WireGuardAdapterError?) -> Void) {
|
|
|
|
workQueue.async {
|
2020-12-01 10:18:31 +00:00
|
|
|
guard case .stopped = self.state else {
|
2020-11-04 15:59:33 +00:00
|
|
|
completionHandler(.invalidState)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
let networkMonitor = NWPathMonitor()
|
|
|
|
networkMonitor.pathUpdateHandler = { [weak self] path in
|
|
|
|
self?.didReceivePathUpdate(path: path)
|
|
|
|
}
|
|
|
|
networkMonitor.start(queue: self.workQueue)
|
|
|
|
|
2020-12-01 10:18:31 +00:00
|
|
|
do {
|
|
|
|
let settingsGenerator = try self.makeSettingsGenerator(with: tunnelConfiguration)
|
|
|
|
try self.setNetworkSettings(settingsGenerator.generateNetworkSettings())
|
2020-11-04 15:59:33 +00:00
|
|
|
|
2020-12-01 10:18:31 +00:00
|
|
|
let (wgConfig, resolutionResults) = settingsGenerator.uapiConfiguration()
|
|
|
|
self.logEndpointResolutionResults(resolutionResults)
|
2020-11-04 15:59:33 +00:00
|
|
|
|
2020-12-01 10:18:31 +00:00
|
|
|
self.state = .started(
|
|
|
|
try self.startWireGuardBackend(wgConfig: wgConfig),
|
|
|
|
settingsGenerator
|
|
|
|
)
|
|
|
|
self.networkMonitor = networkMonitor
|
|
|
|
completionHandler(nil)
|
|
|
|
} catch let error as WireGuardAdapterError {
|
|
|
|
networkMonitor.cancel()
|
|
|
|
completionHandler(error)
|
|
|
|
} catch {
|
|
|
|
fatalError()
|
2020-11-04 15:59:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Stop the tunnel.
|
|
|
|
/// - Parameter completionHandler: completion handler.
|
|
|
|
public func stop(completionHandler: @escaping (WireGuardAdapterError?) -> Void) {
|
|
|
|
workQueue.async {
|
2020-12-01 10:18:31 +00:00
|
|
|
switch self.state {
|
|
|
|
case .started(let handle, _):
|
|
|
|
wgTurnOff(handle)
|
|
|
|
|
|
|
|
case .temporaryShutdown:
|
|
|
|
break
|
|
|
|
|
|
|
|
case .stopped:
|
2020-11-04 15:59:33 +00:00
|
|
|
completionHandler(.invalidState)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
self.networkMonitor?.cancel()
|
|
|
|
self.networkMonitor = nil
|
|
|
|
|
2020-12-01 10:18:31 +00:00
|
|
|
self.state = .stopped
|
2020-11-04 15:59:33 +00:00
|
|
|
|
|
|
|
completionHandler(nil)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Update runtime configuration.
|
|
|
|
/// - Parameters:
|
|
|
|
/// - tunnelConfiguration: tunnel configuration.
|
|
|
|
/// - completionHandler: completion handler.
|
|
|
|
public func update(tunnelConfiguration: TunnelConfiguration, completionHandler: @escaping (WireGuardAdapterError?) -> Void) {
|
|
|
|
workQueue.async {
|
2020-12-01 10:18:31 +00:00
|
|
|
if case .stopped = self.state {
|
2020-11-04 15:59:33 +00:00
|
|
|
completionHandler(.invalidState)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tell the system that the tunnel is going to reconnect using new WireGuard
|
|
|
|
// configuration.
|
|
|
|
// This will broadcast the `NEVPNStatusDidChange` notification to the GUI process.
|
|
|
|
self.packetTunnelProvider?.reasserting = true
|
2020-12-01 10:18:31 +00:00
|
|
|
defer {
|
|
|
|
self.packetTunnelProvider?.reasserting = false
|
|
|
|
}
|
2020-11-04 15:59:33 +00:00
|
|
|
|
2020-12-01 10:18:31 +00:00
|
|
|
do {
|
|
|
|
let settingsGenerator = try self.makeSettingsGenerator(with: tunnelConfiguration)
|
|
|
|
try self.setNetworkSettings(settingsGenerator.generateNetworkSettings())
|
|
|
|
|
|
|
|
switch self.state {
|
|
|
|
case .started(let handle, _):
|
|
|
|
let (wgConfig, resolutionResults) = settingsGenerator.uapiConfiguration()
|
|
|
|
self.logEndpointResolutionResults(resolutionResults)
|
|
|
|
|
|
|
|
wgSetConfig(handle, wgConfig)
|
2020-12-15 12:16:35 +00:00
|
|
|
#if os(iOS)
|
|
|
|
wgDisableSomeRoamingForBrokenMobileSemantics(handle)
|
|
|
|
#endif
|
2020-12-01 10:18:31 +00:00
|
|
|
|
|
|
|
self.state = .started(handle, settingsGenerator)
|
|
|
|
|
|
|
|
case .temporaryShutdown:
|
|
|
|
self.state = .temporaryShutdown(settingsGenerator)
|
|
|
|
|
|
|
|
case .stopped:
|
|
|
|
fatalError()
|
2020-11-04 15:59:33 +00:00
|
|
|
}
|
|
|
|
|
2020-12-01 10:18:31 +00:00
|
|
|
completionHandler(nil)
|
|
|
|
} catch let error as WireGuardAdapterError {
|
|
|
|
completionHandler(error)
|
|
|
|
} catch {
|
|
|
|
fatalError()
|
2020-11-04 15:59:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - Private methods
|
|
|
|
|
2020-12-02 12:48:24 +00:00
|
|
|
/// Setup WireGuard log handler.
|
|
|
|
private func setupLogHandler() {
|
2020-11-04 15:59:33 +00:00
|
|
|
let context = Unmanaged.passUnretained(self).toOpaque()
|
2020-12-02 14:08:45 +00:00
|
|
|
wgSetLogger(context) { context, logLevel, message in
|
2020-11-04 15:59:33 +00:00
|
|
|
guard let context = context, let message = message else { return }
|
|
|
|
|
|
|
|
let unretainedSelf = Unmanaged<WireGuardAdapter>.fromOpaque(context)
|
|
|
|
.takeUnretainedValue()
|
|
|
|
|
|
|
|
let swiftString = String(cString: message).trimmingCharacters(in: .newlines)
|
2021-03-08 19:29:12 +00:00
|
|
|
let tunnelLogLevel = WireGuardLogLevel(rawValue: logLevel) ?? .verbose
|
2020-11-04 15:59:33 +00:00
|
|
|
|
2020-12-02 12:53:16 +00:00
|
|
|
unretainedSelf.logHandler(tunnelLogLevel, swiftString)
|
2020-11-04 15:59:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-01 10:18:31 +00:00
|
|
|
/// Set network tunnel configuration.
|
|
|
|
/// This method ensures that the call to `setTunnelNetworkSettings` does not time out, as in
|
|
|
|
/// certain scenarios the completion handler given to it may not be invoked by the system.
|
|
|
|
///
|
2020-11-04 15:59:33 +00:00
|
|
|
/// - Parameters:
|
2020-12-01 10:18:31 +00:00
|
|
|
/// - networkSettings: an instance of type `NEPacketTunnelNetworkSettings`.
|
|
|
|
/// - Throws: an error of type `WireGuardAdapterError`.
|
|
|
|
/// - Returns: `PacketTunnelSettingsGenerator`.
|
|
|
|
private func setNetworkSettings(_ networkSettings: NEPacketTunnelNetworkSettings) throws {
|
2020-11-04 15:59:33 +00:00
|
|
|
var systemError: Error?
|
|
|
|
let condition = NSCondition()
|
|
|
|
|
|
|
|
// Activate the condition
|
|
|
|
condition.lock()
|
|
|
|
defer { condition.unlock() }
|
|
|
|
|
2020-12-02 14:08:45 +00:00
|
|
|
self.packetTunnelProvider?.setTunnelNetworkSettings(networkSettings) { error in
|
2020-11-04 15:59:33 +00:00
|
|
|
systemError = error
|
|
|
|
condition.signal()
|
2020-12-02 14:08:45 +00:00
|
|
|
}
|
2020-11-04 15:59:33 +00:00
|
|
|
|
|
|
|
// Packet tunnel's `setTunnelNetworkSettings` times out in certain
|
|
|
|
// scenarios & never calls the given callback.
|
|
|
|
let setTunnelNetworkSettingsTimeout: TimeInterval = 5 // seconds
|
|
|
|
|
|
|
|
if condition.wait(until: Date().addingTimeInterval(setTunnelNetworkSettingsTimeout)) {
|
2020-12-01 10:18:31 +00:00
|
|
|
if let systemError = systemError {
|
|
|
|
throw WireGuardAdapterError.setNetworkSettings(systemError)
|
2020-12-02 12:53:58 +00:00
|
|
|
}
|
2020-11-04 15:59:33 +00:00
|
|
|
} else {
|
2020-12-16 23:24:39 +00:00
|
|
|
self.logHandler(.error, "setTunnelNetworkSettings timed out after 5 seconds; proceeding anyway")
|
2020-11-04 15:59:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Resolve peers of the given tunnel configuration.
|
|
|
|
/// - Parameter tunnelConfiguration: tunnel configuration.
|
|
|
|
/// - Throws: an error of type `WireGuardAdapterError`.
|
|
|
|
/// - Returns: The list of resolved endpoints.
|
|
|
|
private func resolvePeers(for tunnelConfiguration: TunnelConfiguration) throws -> [Endpoint?] {
|
|
|
|
let endpoints = tunnelConfiguration.peers.map { $0.endpoint }
|
|
|
|
let resolutionResults = DNSResolver.resolveSync(endpoints: endpoints)
|
2020-12-02 14:08:45 +00:00
|
|
|
let resolutionErrors = resolutionResults.compactMap { result -> DNSResolutionError? in
|
2020-11-04 15:59:33 +00:00
|
|
|
if case .failure(let error) = result {
|
|
|
|
return error
|
|
|
|
} else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(endpoints.count == resolutionResults.count)
|
|
|
|
guard resolutionErrors.isEmpty else {
|
|
|
|
throw WireGuardAdapterError.dnsResolution(resolutionErrors)
|
|
|
|
}
|
|
|
|
|
2020-12-02 14:08:45 +00:00
|
|
|
let resolvedEndpoints = resolutionResults.map { result -> Endpoint? in
|
|
|
|
// swiftlint:disable:next force_try
|
2020-12-02 12:54:31 +00:00
|
|
|
return try! result?.get()
|
2020-11-04 15:59:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return resolvedEndpoints
|
|
|
|
}
|
|
|
|
|
2020-12-01 10:18:31 +00:00
|
|
|
/// Start WireGuard backend.
|
|
|
|
/// - Parameter wgConfig: WireGuard configuration
|
|
|
|
/// - Throws: an error of type `WireGuardAdapterError`
|
|
|
|
/// - Returns: tunnel handle
|
|
|
|
private func startWireGuardBackend(wgConfig: String) throws -> Int32 {
|
|
|
|
guard let tunnelFileDescriptor = self.tunnelFileDescriptor else {
|
|
|
|
throw WireGuardAdapterError.cannotLocateTunnelFileDescriptor
|
|
|
|
}
|
|
|
|
|
|
|
|
let handle = wgTurnOn(wgConfig, tunnelFileDescriptor)
|
2020-12-15 12:16:35 +00:00
|
|
|
if handle < 0 {
|
2020-12-01 10:18:31 +00:00
|
|
|
throw WireGuardAdapterError.startWireGuardBackend(handle)
|
|
|
|
}
|
2020-12-15 12:16:35 +00:00
|
|
|
#if os(iOS)
|
|
|
|
wgDisableSomeRoamingForBrokenMobileSemantics(handle)
|
|
|
|
#endif
|
|
|
|
return handle
|
2020-12-01 10:18:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Resolves the hostnames in the given tunnel configuration and return settings generator.
|
|
|
|
/// - Parameter tunnelConfiguration: an instance of type `TunnelConfiguration`.
|
|
|
|
/// - Throws: an error of type `WireGuardAdapterError`.
|
|
|
|
/// - Returns: an instance of type `PacketTunnelSettingsGenerator`.
|
|
|
|
private func makeSettingsGenerator(with tunnelConfiguration: TunnelConfiguration) throws -> PacketTunnelSettingsGenerator {
|
|
|
|
return PacketTunnelSettingsGenerator(
|
|
|
|
tunnelConfiguration: tunnelConfiguration,
|
|
|
|
resolvedEndpoints: try self.resolvePeers(for: tunnelConfiguration)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Log DNS resolution results.
|
|
|
|
/// - Parameter resolutionErrors: an array of type `[DNSResolutionError]`.
|
|
|
|
private func logEndpointResolutionResults(_ resolutionResults: [EndpointResolutionResult?]) {
|
|
|
|
for case .some(let result) in resolutionResults {
|
|
|
|
switch result {
|
|
|
|
case .success((let sourceEndpoint, let resolvedEndpoint)):
|
|
|
|
if sourceEndpoint.host == resolvedEndpoint.host {
|
2021-03-08 19:29:12 +00:00
|
|
|
self.logHandler(.verbose, "DNS64: mapped \(sourceEndpoint.host) to itself.")
|
2020-12-01 10:18:31 +00:00
|
|
|
} else {
|
2021-03-08 19:29:12 +00:00
|
|
|
self.logHandler(.verbose, "DNS64: mapped \(sourceEndpoint.host) to \(resolvedEndpoint.host)")
|
2020-12-01 10:18:31 +00:00
|
|
|
}
|
|
|
|
case .failure(let resolutionError):
|
|
|
|
self.logHandler(.error, "Failed to resolve endpoint \(resolutionError.address): \(resolutionError.errorDescription ?? "(nil)")")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-04 15:59:33 +00:00
|
|
|
/// Helper method used by network path monitor.
|
|
|
|
/// - Parameter path: new network path
|
|
|
|
private func didReceivePathUpdate(path: Network.NWPath) {
|
2021-03-08 19:29:12 +00:00
|
|
|
self.logHandler(.verbose, "Network change detected with \(path.status) route and interface order \(path.availableInterfaces)")
|
2020-11-04 15:59:33 +00:00
|
|
|
|
2020-12-11 10:49:56 +00:00
|
|
|
#if os(macOS)
|
|
|
|
if case .started(let handle, _) = self.state {
|
|
|
|
wgBumpSockets(handle)
|
|
|
|
}
|
|
|
|
#elseif os(iOS)
|
2020-12-01 10:18:31 +00:00
|
|
|
switch self.state {
|
|
|
|
case .started(let handle, let settingsGenerator):
|
|
|
|
if path.status.isSatisfiable {
|
|
|
|
let (wgConfig, resolutionResults) = settingsGenerator.endpointUapiConfiguration()
|
|
|
|
self.logEndpointResolutionResults(resolutionResults)
|
|
|
|
|
|
|
|
wgSetConfig(handle, wgConfig)
|
2020-12-15 12:16:35 +00:00
|
|
|
wgDisableSomeRoamingForBrokenMobileSemantics(handle)
|
2020-12-01 10:18:31 +00:00
|
|
|
wgBumpSockets(handle)
|
|
|
|
} else {
|
2021-03-08 19:29:12 +00:00
|
|
|
self.logHandler(.verbose, "Connectivity offline, pausing backend.")
|
2020-12-01 10:18:31 +00:00
|
|
|
|
|
|
|
self.state = .temporaryShutdown(settingsGenerator)
|
|
|
|
wgTurnOff(handle)
|
2020-12-03 14:10:29 +00:00
|
|
|
}
|
2020-12-02 12:53:16 +00:00
|
|
|
|
2020-12-01 10:18:31 +00:00
|
|
|
case .temporaryShutdown(let settingsGenerator):
|
|
|
|
guard path.status.isSatisfiable else { return }
|
|
|
|
|
2021-03-08 19:29:12 +00:00
|
|
|
self.logHandler(.verbose, "Connectivity online, resuming backend.")
|
2020-12-01 10:18:31 +00:00
|
|
|
|
|
|
|
do {
|
|
|
|
try self.setNetworkSettings(settingsGenerator.generateNetworkSettings())
|
|
|
|
|
|
|
|
let (wgConfig, resolutionResults) = settingsGenerator.uapiConfiguration()
|
|
|
|
self.logEndpointResolutionResults(resolutionResults)
|
|
|
|
|
|
|
|
self.state = .started(
|
|
|
|
try self.startWireGuardBackend(wgConfig: wgConfig),
|
|
|
|
settingsGenerator
|
|
|
|
)
|
|
|
|
} catch {
|
|
|
|
self.logHandler(.error, "Failed to restart backend: \(error.localizedDescription)")
|
|
|
|
}
|
|
|
|
|
|
|
|
case .stopped:
|
|
|
|
// no-op
|
|
|
|
break
|
|
|
|
}
|
2020-12-11 10:49:56 +00:00
|
|
|
#else
|
|
|
|
#error("Unsupported")
|
|
|
|
#endif
|
2020-11-04 15:59:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-15 13:18:21 +00:00
|
|
|
/// A enum describing WireGuard log levels defined in `api-apple.go`.
|
2020-11-04 15:59:33 +00:00
|
|
|
public enum WireGuardLogLevel: Int32 {
|
2021-03-08 19:29:12 +00:00
|
|
|
case verbose = 0
|
|
|
|
case error = 1
|
2020-11-04 15:59:33 +00:00
|
|
|
}
|
2020-12-01 10:18:31 +00:00
|
|
|
|
|
|
|
private extension Network.NWPath.Status {
|
|
|
|
/// Returns `true` if the path is potentially satisfiable.
|
|
|
|
var isSatisfiable: Bool {
|
|
|
|
switch self {
|
|
|
|
case .requiresConnection, .satisfied:
|
|
|
|
return true
|
|
|
|
case .unsatisfied:
|
|
|
|
return false
|
|
|
|
@unknown default:
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|