// // PacketTunnelProvider.swift // punchnet // // Created by 安礼成 on 2025/8/3. // import Foundation import NetworkExtension enum TunnelError: Error { case invalidConfiguration case invalidContext } class PacketTunnelProvider: NEPacketTunnelProvider { 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 { SDLLogger.fatal("[PacketTunnelProvider] startTunnel failed: invalid configuration", category: .app) completionHandler(TunnelError.invalidConfiguration) return } 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.contextBootstrap.stop(clearRuntimeConfiguration: true, completionHandler: 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) { SDLLogger.fatal("[PacketTunnelProvider] sleep requested, will stop current context", category: .app) self.contextBootstrap.stop(clearRuntimeConfiguration: false) { SDLLogger.log("[PacketTunnelProvider] sleep", category: .app) completionHandler() } } override func wake() { SDLLogger.log("[PacketTunnelProvider] wake up!!!!!!!", category: .app) 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) } else { SDLLogger.log("[PacketTunnelProvider] wakeup and try start", category: .app) } } } private func handleAppRequest(message: AppRequest) async throws -> Data? { guard let contextActor = self.contextBootstrap.currentContextActor() 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() } } }