// // PacketTunnelProvider.swift // punchnet // // Created by 安礼成 on 2025/8/3. // import NetworkExtension enum TunnelError: Error { case invalidConfiguration case invalidContext } class PacketTunnelProvider: NEPacketTunnelProvider { private var runtimeEnv: SDLRuntimeEnvironment? override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) { // 如果当前在运行状态,不允许重复请求 guard self.runtimeEnv == nil else { completionHandler(TunnelError.invalidContext) return } guard let options else { completionHandler(TunnelError.invalidConfiguration) return } self.runtimeEnv = SDLRuntimeEnvironment(options: options) Task { do { try await self.runtimeEnv?.start(provider: self) completionHandler(nil) } catch let err { completionHandler(err) } } } override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { // Add code here to start the process of stopping the tunnel. Task { await self.runtimeEnv?.stop() completionHandler() } } override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) { // Add code here to handle the message. Task { do { let message = try AppRequest(serializedBytes: messageData) let replyData = try await self.handleAppRequest(message: message) completionHandler?(replyData) } catch let err { var reply = TunnelResponse() reply.code = 1 reply.message = err.localizedDescription let errorReplyData = try? reply.serializedData() completionHandler?(errorReplyData) } } } override func sleep(completionHandler: @escaping () -> Void) { // Add code here to get ready to sleep. Task { await self.runtimeEnv?.stop() completionHandler() } } override func wake() { // Add code here to wake up. Task { try await self.runtimeEnv?.start(provider: self) } } private func handleAppRequest(message: AppRequest) async throws -> Data? { guard let contextActor = self.runtimeEnv?.getContextActor() else { throw TunnelError.invalidContext } switch message.command { case .changeExitNode(let changeExitNode): let exitNodeIp = changeExitNode.ip do { try await contextActor.updateExitNode(exitNodeIp: exitNodeIp) var reply = TunnelResponse() reply.code = 0 reply.message = "操作成功" return try reply.serializedData() } catch let err { var reply = TunnelResponse() reply.code = 1 reply.message = err.localizedDescription return try reply.serializedData() } case .none: var reply = TunnelResponse() reply.code = 1 reply.message = "无效请求" return try reply.serializedData() } } } private class SDLRuntimeEnvironment { var contextActor: SDLContextActor? private var options: [String: NSObject] init(options: [String: NSObject]) { self.options = options } func start(provider: PacketTunnelProvider) async throws { // 重置通知中心 SDLTunnelAppNotifier.shared.clear() guard let config = await SDLConfiguration.parse(options: options) else { throw TunnelError.invalidConfiguration } // 加密算法 let rsaCipher = try! CCRSACipher(keySize: 1024) let contextActor = SDLContextActor(provider: provider, config: config, rsaCipher: rsaCipher) self.contextActor = contextActor await contextActor.start() } func getContextActor() -> SDLContextActor? { return self.contextActor } func stop() async { await self.contextActor?.stop() } }