fix command
This commit is contained in:
parent
5813107425
commit
49750099ea
@ -149,7 +149,7 @@ actor SDLContextActor {
|
|||||||
do {
|
do {
|
||||||
try await self.runRootBody()
|
try await self.runRootBody()
|
||||||
} catch is CancellationError {
|
} catch is CancellationError {
|
||||||
if let terminalError = await self.consumeTerminalError() {
|
if let terminalError = self.consumeTerminalError() {
|
||||||
SDLLogger.fatal("[SDLContext] root task stopped by terminal error: \(terminalError)", category: .context)
|
SDLLogger.fatal("[SDLContext] root task stopped by terminal error: \(terminalError)", category: .context)
|
||||||
result = .failure(terminalError)
|
result = .failure(terminalError)
|
||||||
} else {
|
} else {
|
||||||
@ -163,7 +163,7 @@ actor SDLContextActor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await self.cleanupRoot()
|
await self.cleanupRoot()
|
||||||
await self.finishRootTask(id: rootTaskID)
|
self.finishRootTask(id: rootTaskID)
|
||||||
try result.get()
|
try result.get()
|
||||||
}
|
}
|
||||||
self.rootTaskID = rootTaskID
|
self.rootTaskID = rootTaskID
|
||||||
@ -196,6 +196,27 @@ actor SDLContextActor {
|
|||||||
self.terminalError = nil
|
self.terminalError = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func recoverAfterWake() async throws {
|
||||||
|
SDLLogger.log("[SDLContext] recoverAfterWake requested", category: .context)
|
||||||
|
|
||||||
|
guard self.rootTask != nil else {
|
||||||
|
throw TunnelError.invalidContext
|
||||||
|
}
|
||||||
|
|
||||||
|
try await self.readySignal.wait(timeout: .seconds(30))
|
||||||
|
|
||||||
|
guard let dataCipher = self.dataCipher else {
|
||||||
|
throw TunnelError.invalidContext
|
||||||
|
}
|
||||||
|
|
||||||
|
let prunedSessions = await self.sessionManager.pruneExpiredSessions()
|
||||||
|
await self.packetOutboundActor.updateRuntime(config: self.config, dataCipher: dataCipher)
|
||||||
|
await self.packetInboundActor.updateRuntime(config: self.config, dataCipher: dataCipher)
|
||||||
|
try await self.tunNetworkManager.apply(settings: .init(config: self.config), dnsServer: DNSHelper.dnsServer)
|
||||||
|
|
||||||
|
SDLLogger.log("[SDLContext] recoverAfterWake completed, prunedSessions: \(prunedSessions)", category: .context)
|
||||||
|
}
|
||||||
|
|
||||||
private func runRootBody() async throws {
|
private func runRootBody() async throws {
|
||||||
self.prepareTunnelNotifier()
|
self.prepareTunnelNotifier()
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,16 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
final class SDLContextBootstrap: @unchecked Sendable {
|
final class SDLContextBootstrap: @unchecked Sendable {
|
||||||
|
private typealias StartCompletion = (Error?) -> Void
|
||||||
|
private typealias StopCompletion = () -> Void
|
||||||
|
private typealias WakeCompletion = (Error?) -> Void
|
||||||
|
|
||||||
|
private enum BootstrapCommand {
|
||||||
|
case start(config: SDLConfiguration, rsaCipher: CCRSACipher, completion: StartCompletion)
|
||||||
|
case stop(clearRuntimeConfiguration: Bool, completion: StopCompletion)
|
||||||
|
case recoverAfterWake(completion: WakeCompletion)
|
||||||
|
}
|
||||||
|
|
||||||
private enum RuntimeState {
|
private enum RuntimeState {
|
||||||
case idle
|
case idle
|
||||||
case starting
|
case starting
|
||||||
@ -22,9 +32,22 @@ final class SDLContextBootstrap: @unchecked Sendable {
|
|||||||
private var rsaCipher: CCRSACipher?
|
private var rsaCipher: CCRSACipher?
|
||||||
private var contextActor: SDLContextActor?
|
private var contextActor: SDLContextActor?
|
||||||
private var startCompletionHandler: ((Error?) -> Void)?
|
private var startCompletionHandler: ((Error?) -> Void)?
|
||||||
|
private let commandContinuation: AsyncStream<BootstrapCommand>.Continuation
|
||||||
|
private var commandWorker: Task<Void, Never>?
|
||||||
|
|
||||||
init(provider: PacketTunnelProvider) {
|
init(provider: PacketTunnelProvider) {
|
||||||
|
let commandPair = AsyncStream.makeStream(of: BootstrapCommand.self, bufferingPolicy: .unbounded)
|
||||||
|
|
||||||
self.provider = provider
|
self.provider = provider
|
||||||
|
self.commandContinuation = commandPair.continuation
|
||||||
|
self.commandWorker = Task { [weak self, stream = commandPair.stream] in
|
||||||
|
await self?.runCommandLoop(stream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.commandContinuation.finish()
|
||||||
|
self.commandWorker?.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
func startCached(completionHandler: @escaping (Error?) -> Void) {
|
func startCached(completionHandler: @escaping (Error?) -> Void) {
|
||||||
@ -43,6 +66,46 @@ final class SDLContextBootstrap: @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func start(config: SDLConfiguration, rsaCipher: CCRSACipher, completionHandler: @escaping (Error?) -> Void) {
|
func start(config: SDLConfiguration, rsaCipher: CCRSACipher, completionHandler: @escaping (Error?) -> Void) {
|
||||||
|
self.submit(.start(config: config, rsaCipher: rsaCipher, completion: completionHandler))
|
||||||
|
}
|
||||||
|
|
||||||
|
func stop(clearRuntimeConfiguration: Bool, completionHandler: @escaping () -> Void) {
|
||||||
|
self.submit(.stop(clearRuntimeConfiguration: clearRuntimeConfiguration, completion: completionHandler))
|
||||||
|
}
|
||||||
|
|
||||||
|
func recoverAfterWake(completionHandler: @escaping (Error?) -> Void) {
|
||||||
|
self.submit(.recoverAfterWake(completion: completionHandler))
|
||||||
|
}
|
||||||
|
|
||||||
|
func currentContextActor() -> SDLContextActor? {
|
||||||
|
self.runtimeLock.lock()
|
||||||
|
let contextActor = self.contextActor
|
||||||
|
self.runtimeLock.unlock()
|
||||||
|
return contextActor
|
||||||
|
}
|
||||||
|
|
||||||
|
private func submit(_ command: BootstrapCommand) {
|
||||||
|
self.commandContinuation.yield(command)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func runCommandLoop(_ stream: AsyncStream<BootstrapCommand>) async {
|
||||||
|
for await command in stream {
|
||||||
|
self.handle(command)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handle(_ command: BootstrapCommand) {
|
||||||
|
switch command {
|
||||||
|
case .start(let config, let rsaCipher, let completion):
|
||||||
|
self.handleStart(config: config, rsaCipher: rsaCipher, completionHandler: completion)
|
||||||
|
case .stop(let clearRuntimeConfiguration, let completion):
|
||||||
|
self.handleStop(clearRuntimeConfiguration: clearRuntimeConfiguration, completionHandler: completion)
|
||||||
|
case .recoverAfterWake(let completion):
|
||||||
|
self.handleRecoverAfterWake(completionHandler: completion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleStart(config: SDLConfiguration, rsaCipher: CCRSACipher, completionHandler: @escaping (Error?) -> Void) {
|
||||||
guard let provider = self.provider else {
|
guard let provider = self.provider else {
|
||||||
SDLLogger.fatal("[SDLContextBootstrap] start rejected: provider released", category: .app)
|
SDLLogger.fatal("[SDLContextBootstrap] start rejected: provider released", category: .app)
|
||||||
completionHandler(TunnelError.invalidContext)
|
completionHandler(TunnelError.invalidContext)
|
||||||
@ -84,7 +147,7 @@ final class SDLContextBootstrap: @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func stop(clearRuntimeConfiguration: Bool, completionHandler: @escaping () -> Void) {
|
private func handleStop(clearRuntimeConfiguration: Bool, completionHandler: @escaping () -> Void) {
|
||||||
self.runtimeLock.lock()
|
self.runtimeLock.lock()
|
||||||
let contextActor = self.contextActor
|
let contextActor = self.contextActor
|
||||||
let startCompletionHandler = self.startCompletionHandler
|
let startCompletionHandler = self.startCompletionHandler
|
||||||
@ -122,11 +185,50 @@ final class SDLContextBootstrap: @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func currentContextActor() -> SDLContextActor? {
|
private func handleRecoverAfterWake(completionHandler: @escaping (Error?) -> Void) {
|
||||||
self.runtimeLock.lock()
|
self.runtimeLock.lock()
|
||||||
|
let runtimeState = self.runtimeState
|
||||||
let contextActor = self.contextActor
|
let contextActor = self.contextActor
|
||||||
|
let config = self.config
|
||||||
|
let rsaCipher = self.rsaCipher
|
||||||
self.runtimeLock.unlock()
|
self.runtimeLock.unlock()
|
||||||
return contextActor
|
|
||||||
|
switch runtimeState {
|
||||||
|
case .running:
|
||||||
|
guard let contextActor else {
|
||||||
|
SDLLogger.fatal("[SDLContextBootstrap] recoverAfterWake failed: missing running context", category: .app)
|
||||||
|
completionHandler(TunnelError.invalidContext)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
try await contextActor.recoverAfterWake()
|
||||||
|
completionHandler(nil)
|
||||||
|
} catch {
|
||||||
|
SDLLogger.fatal("[SDLContextBootstrap] recoverAfterWake failed: \(error)", category: .app)
|
||||||
|
completionHandler(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case .idle:
|
||||||
|
guard let config, let rsaCipher else {
|
||||||
|
SDLLogger.fatal("[SDLContextBootstrap] recoverAfterWake failed: missing cached runtime configuration", category: .app)
|
||||||
|
completionHandler(TunnelError.invalidConfiguration)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
SDLLogger.log("[SDLContextBootstrap] recoverAfterWake will start cached context", category: .app)
|
||||||
|
self.handleStart(config: config, rsaCipher: rsaCipher, completionHandler: completionHandler)
|
||||||
|
|
||||||
|
case .starting:
|
||||||
|
SDLLogger.log("[SDLContextBootstrap] recoverAfterWake ignored while context is starting", category: .app)
|
||||||
|
completionHandler(nil)
|
||||||
|
|
||||||
|
case .stopping:
|
||||||
|
SDLLogger.log("[SDLContextBootstrap] recoverAfterWake ignored while context is stopping", category: .app)
|
||||||
|
completionHandler(nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func finishContextStart(_ contextActor: SDLContextActor, error: Error?) {
|
private func finishContextStart(_ contextActor: SDLContextActor, error: Error?) {
|
||||||
|
|||||||
@ -56,21 +56,18 @@ 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.log("[PacketTunnelProvider] sleep requested", category: .app)
|
||||||
self.contextBootstrap.stop(clearRuntimeConfiguration: false) {
|
completionHandler()
|
||||||
SDLLogger.log("[PacketTunnelProvider] sleep", category: .app)
|
|
||||||
completionHandler()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func wake() {
|
override func wake() {
|
||||||
SDLLogger.log("[PacketTunnelProvider] wake up!!!!!!!", category: .app)
|
SDLLogger.log("[PacketTunnelProvider] wake up!!!!!!!", category: .app)
|
||||||
self.contextBootstrap.startCached { err in
|
self.contextBootstrap.recoverAfterWake { err in
|
||||||
if let err {
|
if let err {
|
||||||
SDLLogger.fatal("[PacketTunnelProvider] wakeup start failed: \(err)", category: .app)
|
SDLLogger.fatal("[PacketTunnelProvider] wakeup recovery failed: \(err)", category: .app)
|
||||||
SDLLogger.log("[PacketTunnelProvider] wakeup start failed: \(err.localizedDescription)", category: .app)
|
SDLLogger.log("[PacketTunnelProvider] wakeup recovery failed: \(err.localizedDescription)", category: .app)
|
||||||
} else {
|
} else {
|
||||||
SDLLogger.log("[PacketTunnelProvider] wakeup and try start", category: .app)
|
SDLLogger.log("[PacketTunnelProvider] wakeup recovery completed", category: .app)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -71,6 +71,15 @@ actor SessionManager {
|
|||||||
self.publishSnapshot()
|
self.publishSnapshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
func pruneExpiredSessions() -> Int {
|
||||||
|
let oldCount = self.sessionCount()
|
||||||
|
self.sessions = self.validSessions()
|
||||||
|
let newCount = self.sessionCount()
|
||||||
|
self.publishSnapshot()
|
||||||
|
return oldCount - newCount
|
||||||
|
}
|
||||||
|
|
||||||
nonisolated func snapshot() -> SessionSnapshot {
|
nonisolated func snapshot() -> SessionSnapshot {
|
||||||
return self.snapshotPublisher.current()
|
return self.snapshotPublisher.current()
|
||||||
}
|
}
|
||||||
@ -83,12 +92,21 @@ actor SessionManager {
|
|||||||
self.snapshotPublisher.publish(self.compileSnapshot())
|
self.snapshotPublisher.publish(self.compileSnapshot())
|
||||||
}
|
}
|
||||||
|
|
||||||
private func compileSnapshot() -> SessionSnapshot {
|
private func validSessions() -> [Data: [Session.AddressType: Session]] {
|
||||||
let timestamp = Int32(Date().timeIntervalSince1970)
|
let timestamp = Int32(Date().timeIntervalSince1970)
|
||||||
let sessions = self.sessions.compactMapValues { peerSessions in
|
return self.sessions.compactMapValues { peerSessions in
|
||||||
let validSessions = peerSessions.filter { $0.value.lastTimestamp + self.ttl >= timestamp }
|
let validSessions = peerSessions.filter { $0.value.lastTimestamp + self.ttl >= timestamp }
|
||||||
return validSessions.isEmpty ? nil : validSessions
|
return validSessions.isEmpty ? nil : validSessions
|
||||||
}
|
}
|
||||||
return SessionSnapshot(sessions: sessions)
|
}
|
||||||
|
|
||||||
|
private func sessionCount() -> Int {
|
||||||
|
return self.sessions.values.reduce(0) { count, peerSessions in
|
||||||
|
count + peerSessions.count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func compileSnapshot() -> SessionSnapshot {
|
||||||
|
return SessionSnapshot(sessions: self.validSessions())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
#! /bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
log stream --predicate 'subsystem == "com.jihe.punchnet.debug"' --info --style compact
|
log stream --style compact --predicate 'subsystem == "com.jihe.punchnet.tun"'
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user