fix SDLContext
This commit is contained in:
parent
362117aa84
commit
5813107425
160
Tun/Context/SDLContextBootstrap.swift
Normal file
160
Tun/Context/SDLContextBootstrap.swift
Normal file
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -14,19 +14,7 @@ enum TunnelError: Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class PacketTunnelProvider: NEPacketTunnelProvider {
|
class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
private enum RuntimeState {
|
private lazy var contextBootstrap = SDLContextBootstrap(provider: self)
|
||||||
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)?
|
|
||||||
|
|
||||||
override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
||||||
guard let options, let config = SDLConfiguration.parse(options: options) else {
|
guard let options, let config = SDLConfiguration.parse(options: options) else {
|
||||||
@ -35,13 +23,18 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let rsaCipher = try! CCRSACipher(keySize: 1024)
|
do {
|
||||||
self.startContext(config: config, rsaCipher: rsaCipher, completionHandler: completionHandler)
|
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) {
|
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
||||||
SDLLogger.fatal("[PacketTunnelProvider] stopTunnel requested, reason: \(reason.rawValue)", category: .app)
|
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)?) {
|
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
|
||||||
@ -64,7 +57,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
|
|
||||||
override func sleep(completionHandler: @escaping () -> Void) {
|
override func sleep(completionHandler: @escaping () -> Void) {
|
||||||
SDLLogger.fatal("[PacketTunnelProvider] sleep requested, will stop current context", category: .app)
|
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)
|
SDLLogger.log("[PacketTunnelProvider] sleep", category: .app)
|
||||||
completionHandler()
|
completionHandler()
|
||||||
}
|
}
|
||||||
@ -72,7 +65,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
|
|
||||||
override func wake() {
|
override func wake() {
|
||||||
SDLLogger.log("[PacketTunnelProvider] wake up!!!!!!!", category: .app)
|
SDLLogger.log("[PacketTunnelProvider] wake up!!!!!!!", category: .app)
|
||||||
self.startCachedContext { err in
|
self.contextBootstrap.startCached { err in
|
||||||
if let err {
|
if let err {
|
||||||
SDLLogger.fatal("[PacketTunnelProvider] wakeup start failed: \(err)", category: .app)
|
SDLLogger.fatal("[PacketTunnelProvider] wakeup start failed: \(err)", category: .app)
|
||||||
SDLLogger.log("[PacketTunnelProvider] wakeup start failed: \(err.localizedDescription)", 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? {
|
private func handleAppRequest(message: AppRequest) async throws -> Data? {
|
||||||
guard let contextActor = self.currentContextActor() else {
|
guard let contextActor = self.contextBootstrap.currentContextActor() else {
|
||||||
throw TunnelError.invalidContext
|
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user