Store path into App Group. Do not read it from UserDefaults in app extension because value is immediately available in provider configuration.
161 lines
5.8 KiB
Swift
161 lines
5.8 KiB
Swift
import TunnelKitWireGuardCore
|
|
import TunnelKitWireGuardManager
|
|
import WireGuardKit
|
|
import __TunnelKitUtils
|
|
import SwiftyBeaver
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
|
|
|
import Foundation
|
|
import NetworkExtension
|
|
import os
|
|
|
|
open class WireGuardTunnelProvider: NEPacketTunnelProvider {
|
|
private var cfg: WireGuard.ProviderConfiguration!
|
|
|
|
private lazy var adapter: WireGuardAdapter = {
|
|
return WireGuardAdapter(with: self) { logLevel, message in
|
|
wg_log(logLevel.osLogLevel, message: message)
|
|
}
|
|
}()
|
|
|
|
open override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
|
|
|
// BEGIN: TunnelKit
|
|
|
|
guard let tunnelProviderProtocol = protocolConfiguration as? NETunnelProviderProtocol else {
|
|
fatalError("Not a NETunnelProviderProtocol")
|
|
}
|
|
guard let providerConfiguration = tunnelProviderProtocol.providerConfiguration else {
|
|
fatalError("Missing providerConfiguration")
|
|
}
|
|
|
|
let tunnelConfiguration: TunnelConfiguration
|
|
do {
|
|
cfg = try fromDictionary(WireGuard.ProviderConfiguration.self, providerConfiguration)
|
|
tunnelConfiguration = cfg.configuration.tunnelConfiguration
|
|
} catch {
|
|
completionHandler(WireGuardProviderError.savedProtocolConfigurationIsInvalid)
|
|
return
|
|
}
|
|
|
|
configureLogging()
|
|
|
|
// END: TunnelKit
|
|
|
|
// Start the tunnel
|
|
adapter.start(tunnelConfiguration: tunnelConfiguration) { adapterError in
|
|
guard let adapterError = adapterError else {
|
|
let interfaceName = self.adapter.interfaceName ?? "unknown"
|
|
|
|
wg_log(.info, message: "Tunnel interface is \(interfaceName)")
|
|
|
|
completionHandler(nil)
|
|
return
|
|
}
|
|
|
|
switch adapterError {
|
|
case .cannotLocateTunnelFileDescriptor:
|
|
wg_log(.error, staticMessage: "Starting tunnel failed: could not determine file descriptor")
|
|
self.cfg._appexSetLastError(.couldNotDetermineFileDescriptor)
|
|
completionHandler(WireGuardProviderError.couldNotDetermineFileDescriptor)
|
|
|
|
case .dnsResolution(let dnsErrors):
|
|
let hostnamesWithDnsResolutionFailure = dnsErrors.map { $0.address }
|
|
.joined(separator: ", ")
|
|
wg_log(.error, message: "DNS resolution failed for the following hostnames: \(hostnamesWithDnsResolutionFailure)")
|
|
self.cfg._appexSetLastError(.dnsResolutionFailure)
|
|
completionHandler(WireGuardProviderError.dnsResolutionFailure)
|
|
|
|
case .setNetworkSettings(let error):
|
|
wg_log(.error, message: "Starting tunnel failed with setTunnelNetworkSettings returning \(error.localizedDescription)")
|
|
self.cfg._appexSetLastError(.couldNotSetNetworkSettings)
|
|
completionHandler(WireGuardProviderError.couldNotSetNetworkSettings)
|
|
|
|
case .startWireGuardBackend(let errorCode):
|
|
wg_log(.error, message: "Starting tunnel failed with wgTurnOn returning \(errorCode)")
|
|
self.cfg._appexSetLastError(.couldNotStartBackend)
|
|
completionHandler(WireGuardProviderError.couldNotStartBackend)
|
|
|
|
case .invalidState:
|
|
// Must never happen
|
|
fatalError()
|
|
}
|
|
}
|
|
}
|
|
|
|
open override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
|
wg_log(.info, staticMessage: "Stopping tunnel")
|
|
|
|
adapter.stop { error in
|
|
// BEGIN: TunnelKit
|
|
self.cfg._appexSetLastError(nil)
|
|
// END: TunnelKit
|
|
|
|
if let error = error {
|
|
wg_log(.error, message: "Failed to stop WireGuard adapter: \(error.localizedDescription)")
|
|
}
|
|
completionHandler()
|
|
|
|
#if os(macOS)
|
|
// HACK: This is a filthy hack to work around Apple bug 32073323 (dup'd by us as 47526107).
|
|
// Remove it when they finally fix this upstream and the fix has been rolled out to
|
|
// sufficient quantities of users.
|
|
exit(0)
|
|
#endif
|
|
}
|
|
}
|
|
|
|
open override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
|
guard let completionHandler = completionHandler else { return }
|
|
|
|
if messageData.count == 1 && messageData[0] == 0 {
|
|
adapter.getRuntimeConfiguration { settings in
|
|
var data: Data?
|
|
if let settings = settings {
|
|
data = settings.data(using: .utf8)!
|
|
}
|
|
completionHandler(data)
|
|
}
|
|
} else {
|
|
completionHandler(nil)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension WireGuardTunnelProvider {
|
|
private func configureLogging() {
|
|
let logLevel: SwiftyBeaver.Level = (cfg.shouldDebug ? .debug : .info)
|
|
let logFormat = cfg.debugLogFormat ?? "$Dyyyy-MM-dd HH:mm:ss.SSS$d $L $N.$F:$l - $M"
|
|
|
|
if cfg.shouldDebug {
|
|
let console = ConsoleDestination()
|
|
console.useNSLog = true
|
|
console.minLevel = logLevel
|
|
console.format = logFormat
|
|
SwiftyBeaver.addDestination(console)
|
|
}
|
|
|
|
let file = FileDestination(logFileURL: cfg._appexDebugLogURL)
|
|
file.minLevel = logLevel
|
|
file.format = logFormat
|
|
file.logFileMaxSize = 20000
|
|
SwiftyBeaver.addDestination(file)
|
|
|
|
// store path for clients
|
|
cfg._appexSetDebugLogPath()
|
|
}
|
|
}
|
|
|
|
extension WireGuardLogLevel {
|
|
var osLogLevel: OSLogType {
|
|
switch self {
|
|
case .verbose:
|
|
return .debug
|
|
case .error:
|
|
return .error
|
|
}
|
|
}
|
|
}
|