import TunnelKitWireGuardCore import TunnelKitWireGuardManager import WireGuardKit // SPDX-License-Identifier: MIT // Copyright © 2018-2021 WireGuard LLC. All Rights Reserved. import Foundation import NetworkExtension import os open class WireGuardTunnelProvider: NEPacketTunnelProvider { private var persistentErrorNotifier: ErrorNotifier? 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 appGroup = tunnelProviderProtocol.providerConfiguration?["AppGroup"] as? String else { fatalError("AppGroup not found in providerConfiguration") } let errorNotifier = ErrorNotifier(appGroupId: appGroup) persistentErrorNotifier = errorNotifier let tunnelConfiguration: TunnelConfiguration do { tunnelConfiguration = try WireGuardProvider.Configuration.parsed(from: tunnelProviderProtocol).tunnelConfiguration } catch { errorNotifier.notify(WireGuardProviderError.savedProtocolConfigurationIsInvalid) completionHandler(WireGuardProviderError.savedProtocolConfigurationIsInvalid) return } // 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") errorNotifier.notify(WireGuardProviderError.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)") errorNotifier.notify(WireGuardProviderError.dnsResolutionFailure) completionHandler(WireGuardProviderError.dnsResolutionFailure) case .setNetworkSettings(let error): wg_log(.error, message: "Starting tunnel failed with setTunnelNetworkSettings returning \(error.localizedDescription)") errorNotifier.notify(WireGuardProviderError.couldNotSetNetworkSettings) completionHandler(WireGuardProviderError.couldNotSetNetworkSettings) case .startWireGuardBackend(let errorCode): wg_log(.error, message: "Starting tunnel failed with wgTurnOn returning \(errorCode)") errorNotifier.notify(WireGuardProviderError.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.persistentErrorNotifier?.removeLastErrorFile() // 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 WireGuardLogLevel { var osLogLevel: OSLogType { switch self { case .verbose: return .debug case .error: return .error } } }