From 581310742523b43a672ae406a8727ef78753bf14 Mon Sep 17 00:00:00 2001 From: anlicheng <244108715@qq.com> Date: Thu, 28 May 2026 23:39:12 +0800 Subject: [PATCH] fix SDLContext --- Tun/Context/SDLContextBootstrap.swift | 160 ++++++++++++++++++++++++++ Tun/PacketTunnelProvider.swift | 159 ++----------------------- 2 files changed, 172 insertions(+), 147 deletions(-) create mode 100644 Tun/Context/SDLContextBootstrap.swift diff --git a/Tun/Context/SDLContextBootstrap.swift b/Tun/Context/SDLContextBootstrap.swift new file mode 100644 index 0000000..a4eeef7 --- /dev/null +++ b/Tun/Context/SDLContextBootstrap.swift @@ -0,0 +1,160 @@ +// +// SDLContextBootstrap.swift +// Tun +// +// Created by Codex on 2026/5/28. +// + +import Foundation + +final class SDLContextBootstrap: @unchecked Sendable { + private enum RuntimeState { + case idle + case starting + case running + case stopping + } + + private weak var provider: PacketTunnelProvider? + private let runtimeLock = NSLock() + private var runtimeState: RuntimeState = .idle + private var config: SDLConfiguration? + private var rsaCipher: CCRSACipher? + private var contextActor: SDLContextActor? + private var startCompletionHandler: ((Error?) -> Void)? + + init(provider: PacketTunnelProvider) { + self.provider = provider + } + + func startCached(completionHandler: @escaping (Error?) -> Void) { + self.runtimeLock.lock() + let config = self.config + let rsaCipher = self.rsaCipher + self.runtimeLock.unlock() + + guard let config, let rsaCipher else { + SDLLogger.fatal("[SDLContextBootstrap] startCached failed: missing cached runtime configuration", category: .app) + completionHandler(TunnelError.invalidConfiguration) + return + } + + self.start(config: config, rsaCipher: rsaCipher, completionHandler: completionHandler) + } + + func start(config: SDLConfiguration, rsaCipher: CCRSACipher, completionHandler: @escaping (Error?) -> Void) { + guard let provider = self.provider else { + SDLLogger.fatal("[SDLContextBootstrap] start rejected: provider released", category: .app) + completionHandler(TunnelError.invalidContext) + return + } + + self.runtimeLock.lock() + switch self.runtimeState { + case .idle: + SDLTunnelAppNotifier.shared.clear() + + let contextActor = SDLContextActor( + provider: provider, + config: config, + rsaCipher: rsaCipher + ) + + self.config = config + self.rsaCipher = rsaCipher + self.contextActor = contextActor + self.startCompletionHandler = completionHandler + self.runtimeState = .starting + self.runtimeLock.unlock() + + Task { + do { + try await contextActor.start() + self.finishContextStart(contextActor, error: nil) + } catch { + SDLLogger.fatal("[SDLContextBootstrap] context start failed: \(error)", category: .app) + self.finishContextStart(contextActor, error: error) + } + } + + case .starting, .running, .stopping: + SDLLogger.fatal("[SDLContextBootstrap] start rejected: invalid runtime state \(self.runtimeState)", category: .app) + self.runtimeLock.unlock() + completionHandler(TunnelError.invalidContext) + } + } + + func stop(clearRuntimeConfiguration: Bool, completionHandler: @escaping () -> Void) { + self.runtimeLock.lock() + let contextActor = self.contextActor + let startCompletionHandler = self.startCompletionHandler + + guard let contextActor else { + SDLLogger.fatal("[SDLContextBootstrap] stop requested while context is nil, clearRuntimeConfiguration: \(clearRuntimeConfiguration)", category: .app) + self.runtimeState = .idle + self.startCompletionHandler = nil + if clearRuntimeConfiguration { + self.config = nil + self.rsaCipher = nil + } + self.runtimeLock.unlock() + startCompletionHandler?(TunnelError.invalidContext) + completionHandler() + return + } + + self.contextActor = nil + self.startCompletionHandler = nil + self.runtimeState = .stopping + if clearRuntimeConfiguration { + self.config = nil + self.rsaCipher = nil + } + self.runtimeLock.unlock() + + SDLLogger.fatal("[SDLContextBootstrap] stop will stop current context, clearRuntimeConfiguration: \(clearRuntimeConfiguration)", category: .app) + startCompletionHandler?(TunnelError.invalidContext) + + Task { + await contextActor.stop() + self.markContextStopped() + completionHandler() + } + } + + func currentContextActor() -> SDLContextActor? { + self.runtimeLock.lock() + let contextActor = self.contextActor + self.runtimeLock.unlock() + return contextActor + } + + private func finishContextStart(_ contextActor: SDLContextActor, error: Error?) { + self.runtimeLock.lock() + guard self.contextActor === contextActor else { + self.runtimeLock.unlock() + return + } + + let startCompletionHandler = self.startCompletionHandler + self.startCompletionHandler = nil + + if let error { + self.contextActor = nil + self.runtimeState = .idle + } else { + self.runtimeState = .running + } + self.runtimeLock.unlock() + + startCompletionHandler?(error) + } + + private func markContextStopped() { + self.runtimeLock.lock() + if self.contextActor == nil { + self.runtimeState = .idle + } + self.runtimeLock.unlock() + } +} diff --git a/Tun/PacketTunnelProvider.swift b/Tun/PacketTunnelProvider.swift index 47a4ad4..19db543 100644 --- a/Tun/PacketTunnelProvider.swift +++ b/Tun/PacketTunnelProvider.swift @@ -14,19 +14,7 @@ enum TunnelError: Error { } class PacketTunnelProvider: NEPacketTunnelProvider { - private enum RuntimeState { - case idle - case starting - case running - case stopping - } - - private let runtimeLock = NSLock() - private var runtimeState: RuntimeState = .idle - private var config: SDLConfiguration? - private var rsaCipher: CCRSACipher? - private var contextActor: SDLContextActor? - private var startCompletionHandler: ((Error?) -> Void)? + private lazy var contextBootstrap = SDLContextBootstrap(provider: self) override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) { guard let options, let config = SDLConfiguration.parse(options: options) else { @@ -35,13 +23,18 @@ class PacketTunnelProvider: NEPacketTunnelProvider { return } - let rsaCipher = try! CCRSACipher(keySize: 1024) - self.startContext(config: config, rsaCipher: rsaCipher, completionHandler: completionHandler) + do { + let rsaCipher = try CCRSACipher(keySize: 1024) + self.contextBootstrap.start(config: config, rsaCipher: rsaCipher, completionHandler: completionHandler) + } catch { + SDLLogger.fatal("[PacketTunnelProvider] startTunnel failed: rsa cipher initialization failed: \(error)", category: .app) + completionHandler(error) + } } override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { SDLLogger.fatal("[PacketTunnelProvider] stopTunnel requested, reason: \(reason.rawValue)", category: .app) - self.stopContext(clearRuntimeConfiguration: true, completionHandler: completionHandler) + self.contextBootstrap.stop(clearRuntimeConfiguration: true, completionHandler: completionHandler) } override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) { @@ -64,7 +57,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { override func sleep(completionHandler: @escaping () -> Void) { SDLLogger.fatal("[PacketTunnelProvider] sleep requested, will stop current context", category: .app) - self.stopContext(clearRuntimeConfiguration: false) { + self.contextBootstrap.stop(clearRuntimeConfiguration: false) { SDLLogger.log("[PacketTunnelProvider] sleep", category: .app) completionHandler() } @@ -72,7 +65,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { override func wake() { SDLLogger.log("[PacketTunnelProvider] wake up!!!!!!!", category: .app) - self.startCachedContext { err in + self.contextBootstrap.startCached { err in if let err { SDLLogger.fatal("[PacketTunnelProvider] wakeup start failed: \(err)", category: .app) SDLLogger.log("[PacketTunnelProvider] wakeup start failed: \(err.localizedDescription)", category: .app) @@ -83,7 +76,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { } private func handleAppRequest(message: AppRequest) async throws -> Data? { - guard let contextActor = self.currentContextActor() else { + guard let contextActor = self.contextBootstrap.currentContextActor() else { throw TunnelError.invalidContext } @@ -113,131 +106,3 @@ class PacketTunnelProvider: NEPacketTunnelProvider { } } - -private extension PacketTunnelProvider { - - func startCachedContext(completionHandler: @escaping (Error?) -> Void) { - self.runtimeLock.lock() - let config = self.config - let rsaCipher = self.rsaCipher - self.runtimeLock.unlock() - - guard let config, let rsaCipher else { - SDLLogger.fatal("[PacketTunnelProvider] startCachedContext failed: missing cached runtime configuration", category: .app) - completionHandler(TunnelError.invalidConfiguration) - return - } - - self.startContext(config: config, rsaCipher: rsaCipher, completionHandler: completionHandler) - } - - func startContext(config: SDLConfiguration, rsaCipher: CCRSACipher, completionHandler: @escaping (Error?) -> Void) { - self.runtimeLock.lock() - switch self.runtimeState { - case .idle: - SDLTunnelAppNotifier.shared.clear() - - let contextActor = SDLContextActor( - provider: self, - config: config, - rsaCipher: rsaCipher - ) - - self.config = config - self.rsaCipher = rsaCipher - self.contextActor = contextActor - self.startCompletionHandler = completionHandler - self.runtimeState = .starting - self.runtimeLock.unlock() - - Task { - do { - try await contextActor.start() - self.finishContextStart(contextActor, error: nil) - } catch { - SDLLogger.fatal("[PacketTunnelProvider] context start failed: \(error)", category: .app) - self.finishContextStart(contextActor, error: error) - } - } - - case .starting, .running, .stopping: - SDLLogger.fatal("[PacketTunnelProvider] startContext rejected: invalid runtime state \(self.runtimeState)", category: .app) - self.runtimeLock.unlock() - completionHandler(TunnelError.invalidContext) - } - } - - func stopContext(clearRuntimeConfiguration: Bool, completionHandler: @escaping () -> Void) { - self.runtimeLock.lock() - let contextActor = self.contextActor - let startCompletionHandler = self.startCompletionHandler - - guard contextActor != nil else { - SDLLogger.fatal("[PacketTunnelProvider] stopContext requested while context is nil, clearRuntimeConfiguration: \(clearRuntimeConfiguration)", category: .app) - self.runtimeState = .idle - self.startCompletionHandler = nil - if clearRuntimeConfiguration { - self.config = nil - self.rsaCipher = nil - } - self.runtimeLock.unlock() - startCompletionHandler?(TunnelError.invalidContext) - completionHandler() - return - } - - self.contextActor = nil - self.startCompletionHandler = nil - self.runtimeState = .stopping - if clearRuntimeConfiguration { - self.config = nil - self.rsaCipher = nil - } - self.runtimeLock.unlock() - - SDLLogger.fatal("[PacketTunnelProvider] stopContext will stop current context, clearRuntimeConfiguration: \(clearRuntimeConfiguration)", category: .app) - startCompletionHandler?(TunnelError.invalidContext) - - Task { - await contextActor?.stop() - self.markContextStopped() - completionHandler() - } - } - - func currentContextActor() -> SDLContextActor? { - self.runtimeLock.lock() - let contextActor = self.contextActor - self.runtimeLock.unlock() - return contextActor - } - - func finishContextStart(_ contextActor: SDLContextActor, error: Error?) { - self.runtimeLock.lock() - guard self.contextActor === contextActor else { - self.runtimeLock.unlock() - return - } - - let startCompletionHandler = self.startCompletionHandler - self.startCompletionHandler = nil - - if let error { - self.contextActor = nil - self.runtimeState = .idle - } else { - self.runtimeState = .running - } - self.runtimeLock.unlock() - - startCompletionHandler?(error) - } - - func markContextStopped() { - self.runtimeLock.lock() - if self.contextActor == nil { - self.runtimeState = .idle - } - self.runtimeLock.unlock() - } -}