aboutsummaryrefslogtreecommitdiffstats
path: root/Sources/WireGuardNetworkExtension/PacketTunnelProvider.swift
diff options
context:
space:
mode:
Diffstat (limited to 'Sources/WireGuardNetworkExtension/PacketTunnelProvider.swift')
-rw-r--r--Sources/WireGuardNetworkExtension/PacketTunnelProvider.swift118
1 files changed, 118 insertions, 0 deletions
diff --git a/Sources/WireGuardNetworkExtension/PacketTunnelProvider.swift b/Sources/WireGuardNetworkExtension/PacketTunnelProvider.swift
new file mode 100644
index 0000000..913b7e6
--- /dev/null
+++ b/Sources/WireGuardNetworkExtension/PacketTunnelProvider.swift
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: MIT
+// Copyright © 2018-2023 WireGuard LLC. All Rights Reserved.
+
+import Foundation
+import NetworkExtension
+import os
+
+class PacketTunnelProvider: NEPacketTunnelProvider {
+
+ private lazy var adapter: WireGuardAdapter = {
+ return WireGuardAdapter(with: self) { logLevel, message in
+ wg_log(logLevel.osLogLevel, message: message)
+ }
+ }()
+
+ override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
+ let activationAttemptId = options?["activationAttemptId"] as? String
+ let errorNotifier = ErrorNotifier(activationAttemptId: activationAttemptId)
+
+ Logger.configureGlobal(tagged: "NET", withFilePath: FileManager.logFileURL?.path)
+
+ wg_log(.info, message: "Starting tunnel from the " + (activationAttemptId == nil ? "OS directly, rather than the app" : "app"))
+
+ guard let tunnelProviderProtocol = self.protocolConfiguration as? NETunnelProviderProtocol,
+ let tunnelConfiguration = tunnelProviderProtocol.asTunnelConfiguration() else {
+ errorNotifier.notify(PacketTunnelProviderError.savedProtocolConfigurationIsInvalid)
+ completionHandler(PacketTunnelProviderError.savedProtocolConfigurationIsInvalid)
+ return
+ }
+
+ // 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(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
+ completionHandler(PacketTunnelProviderError.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(PacketTunnelProviderError.dnsResolutionFailure)
+ completionHandler(PacketTunnelProviderError.dnsResolutionFailure)
+
+ case .setNetworkSettings(let error):
+ wg_log(.error, message: "Starting tunnel failed with setTunnelNetworkSettings returning \(error.localizedDescription)")
+ errorNotifier.notify(PacketTunnelProviderError.couldNotSetNetworkSettings)
+ completionHandler(PacketTunnelProviderError.couldNotSetNetworkSettings)
+
+ case .startWireGuardBackend(let errorCode):
+ wg_log(.error, message: "Starting tunnel failed with wgTurnOn returning \(errorCode)")
+ errorNotifier.notify(PacketTunnelProviderError.couldNotStartBackend)
+ completionHandler(PacketTunnelProviderError.couldNotStartBackend)
+
+ case .invalidState:
+ // Must never happen
+ fatalError()
+ }
+ }
+ }
+
+ override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
+ wg_log(.info, staticMessage: "Stopping tunnel")
+
+ adapter.stop { error in
+ ErrorNotifier.removeLastErrorFile()
+
+ 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
+ }
+ }
+
+ 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
+ }
+ }
+}