From 3a6b04aa9b90e831ef8a37de5f9db2e3cc1e4436 Mon Sep 17 00:00:00 2001 From: anlicheng <244108715@qq.com> Date: Mon, 13 Apr 2026 17:09:29 +0800 Subject: [PATCH] fix Context --- Tun/PacketTunnelProvider.swift | 2 + Tun/Punchnet/Actors/SDLContextActor.swift | 78 ++++++++++++++++++- .../Networking/SDLAPIClient+Network.swift | 13 ++++ punchnet/Networking/SDLAPIClient+User.swift | 13 ---- .../Views/Settings/SettingsNetworkView.swift | 6 +- 5 files changed, 94 insertions(+), 18 deletions(-) diff --git a/Tun/PacketTunnelProvider.swift b/Tun/PacketTunnelProvider.swift index 9b2fccd..0c08dc3 100644 --- a/Tun/PacketTunnelProvider.swift +++ b/Tun/PacketTunnelProvider.swift @@ -41,6 +41,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider { self.contextActor = SDLContextActor(provider: self, config: config, rsaCipher: rsaCipher) await self.contextActor?.start() + try await self.contextActor?.waitForReady() + completionHandler(nil) } } diff --git a/Tun/Punchnet/Actors/SDLContextActor.swift b/Tun/Punchnet/Actors/SDLContextActor.swift index a32c7e5..fff71b8 100644 --- a/Tun/Punchnet/Actors/SDLContextActor.swift +++ b/Tun/Punchnet/Actors/SDLContextActor.swift @@ -19,7 +19,17 @@ actor SDLContextActor { case registered } + enum ReadyState { + case idle + case starting + case ready + case failed(any Error) + case stopped + } + private var state: State = .unregistered + private var readyState: ReadyState = .idle + private var readyWaiters: [CheckedContinuation] = [] var config: SDLConfiguration // nat的网络类型 @@ -103,6 +113,11 @@ actor SDLContextActor { } public func start() async { + guard case .idle = self.readyState else { + return + } + + self.readyState = .starting self.startMonitor() // 启动arp的定时清理任务 @@ -133,6 +148,21 @@ actor SDLContextActor { } } + public func waitForReady() async throws { + switch self.readyState { + case .ready: + return + case .failed(let error): + throw error + case .stopped: + throw CancellationError() + case .idle, .starting: + try await withCheckedThrowingContinuation { continuation in + self.readyWaiters.append(continuation) + } + } + } + // 取消出口节点的时候,ip地址为: 0.0.0.0 public func updateExitNode(exitNodeIp: String) async throws { if let ip = SDLUtil.ipv4StrToInt32(exitNodeIp), ip > 0 { @@ -338,6 +368,9 @@ actor SDLContextActor { // 处理context的停止问题 public func stop() async { + self.resumeReadyWaiters(.failure(CancellationError())) + self.readyState = .stopped + await self.supervisor.stop() self.udpHoleWorkers?.forEach { $0.cancel() } @@ -427,7 +460,9 @@ actor SDLContextActor { // 需要对数据通过rsa的私钥解码 guard let key = try? self.rsaCipher.decode(data: Data(registerSuperAck.key)) else { SDLLogger.log("[SDLContext] registerSuperAck invalid key") - self.provider.cancelTunnelWithError(SDLError.invalidKey) + let error = SDLError.invalidKey + self.failReady(error) + self.provider.cancelTunnelWithError(error) return } @@ -442,7 +477,9 @@ actor SDLContextActor { self.dataCipher = CCChaCha20Cipher(regionId: regionId, keyData: key) default: SDLLogger.log("[SDLContext] registerSuperAck invalid algorithm \(algorithm)") - self.provider.cancelTunnelWithError(SDLError.unsupportedAlgorithm(algorithm: algorithm)) + let error = SDLError.unsupportedAlgorithm(algorithm: algorithm) + self.failReady(error) + self.provider.cancelTunnelWithError(error) return } @@ -453,8 +490,10 @@ actor SDLContextActor { SDLLogger.log("[SDLContext] setNetworkSettings successed") self.state = .registered self.startReader() + self.markReady() } catch let err { SDLLogger.log("[SDLContext] setTunnelNetworkSettings get error: \(err)") + self.failReady(err) self.provider.cancelTunnelWithError(err) } } @@ -471,6 +510,7 @@ actor SDLContextActor { self.noticeClient?.send(data: alertNotice) // 报告错误并退出 let error = NSError(domain: "com.jihe.punchnet.tun", code: -1) + self.failReady(error) self.provider.cancelTunnelWithError(error) case .noIpAddress, .networkFault, .internalFault: @@ -504,6 +544,7 @@ actor SDLContextActor { // 报告错误并退出 let error = NSError(domain: "com.jihe.punchnet.tun", code: -2) + self.failReady(error) self.provider.cancelTunnelWithError(error) case .none: () @@ -911,6 +952,39 @@ actor SDLContextActor { return ipv4DnsServers.map { NEIPv4Route(destinationAddress: $0, subnetMask: "255.255.255.255") } } + private func markReady() { + guard case .starting = self.readyState else { + return + } + + self.readyState = .ready + self.resumeReadyWaiters(.success(())) + } + + private func failReady(_ error: any Error) { + switch self.readyState { + case .ready, .failed, .stopped: + return + case .idle, .starting: + self.readyState = .failed(error) + self.resumeReadyWaiters(.failure(error)) + } + } + + private func resumeReadyWaiters(_ result: Result) { + let waiters = self.readyWaiters + self.readyWaiters.removeAll() + + waiters.forEach { continuation in + switch result { + case .success: + continuation.resume() + case .failure(let error): + continuation.resume(throwing: error) + } + } + } + deinit { self.udpHole = nil self.dnsClient = nil diff --git a/punchnet/Networking/SDLAPIClient+Network.swift b/punchnet/Networking/SDLAPIClient+Network.swift index 7126991..291142b 100644 --- a/punchnet/Networking/SDLAPIClient+Network.swift +++ b/punchnet/Networking/SDLAPIClient+Network.swift @@ -17,6 +17,18 @@ extension SDLAPIClient { let identityId: UInt32 let resourceList: [Resource] let nodeList: [Node] + let exitNodeList: [ExitNode] + + struct ExitNode: Codable { + let uuid = UUID().uuidString + let nnid: Int + let nodeName: String + + enum CodingKeys: String, CodingKey { + case nnid = "node_id" + case nodeName = "node_name" + } + } // 资源列表 struct Resource: Codable { @@ -85,6 +97,7 @@ extension SDLAPIClient { case identityId = "identity_id" case resourceList = "resource_list" case nodeList = "node_list" + case exitNodeList = "exit_node" } func getNode(id: Int?) -> Node? { diff --git a/punchnet/Networking/SDLAPIClient+User.swift b/punchnet/Networking/SDLAPIClient+User.swift index f70b9ea..7cd7b46 100644 --- a/punchnet/Networking/SDLAPIClient+User.swift +++ b/punchnet/Networking/SDLAPIClient+User.swift @@ -10,17 +10,6 @@ extension SDLAPIClient { // 登陆后的网络会话信息 struct NetworkSession: Codable { - struct ExitNode: Codable { - let uuid = UUID().uuidString - let nnid: Int - let nodeName: String - - enum CodingKeys: String, CodingKey { - case nnid = "node_id" - case nodeName = "node_name" - } - } - let accessToken: String let username: String let userType: String @@ -28,7 +17,6 @@ extension SDLAPIClient { let networkId: Int let networkName: String let networkDomain: String - let exitNodes: [ExitNode] // TODO var networkUrl: String { @@ -43,7 +31,6 @@ extension SDLAPIClient { case networkId = "network_id" case networkName = "network_name" case networkDomain = "network_domain" - case exitNodes = "exit_node" } } diff --git a/punchnet/Views/Settings/SettingsNetworkView.swift b/punchnet/Views/Settings/SettingsNetworkView.swift index a1093aa..c0c4bc2 100644 --- a/punchnet/Views/Settings/SettingsNetworkView.swift +++ b/punchnet/Views/Settings/SettingsNetworkView.swift @@ -10,7 +10,7 @@ struct SettingsNetworkView: View { @Environment(AppContext.self) var appContext: AppContext @Environment(\.openURL) var openURL - @State private var selectedExitNode: SDLAPIClient.NetworkSession.ExitNode? + @State private var selectedExitNode: SDLAPIClient.NetworkContext.ExitNode? var body: some View { ScrollView(.vertical, showsIndicators: false) { @@ -73,7 +73,7 @@ struct SettingsNetworkView: View { Spacer() Menu { - ForEach(networkSession.exitNodes, id: \.uuid) { node in + ForEach(appContext.networkContext?.exitNodeList ?? [], id: \.uuid) { node in Button { self.selectedExitNode = node } label: { @@ -113,7 +113,7 @@ struct SettingsNetworkView: View { .frame(maxWidth: 600, alignment: .leading) } .onAppear { - self.selectedExitNode = self.appContext.networkSession?.exitNodes.first + self.selectedExitNode = self.appContext.networkContext?.exitNodeList.first } }