punchnet-macos/Tun/PacketTunnelProvider.swift
2026-06-21 22:38:49 +08:00

106 lines
3.9 KiB
Swift

//
// 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.log("[PacketTunnelProvider] sleep requested", category: .app)
completionHandler()
}
override func wake() {
SDLLogger.log("[PacketTunnelProvider] wake up!!!!!!!", category: .app)
self.contextBootstrap.recoverAfterWake { err in
if let err {
SDLLogger.fatal("[PacketTunnelProvider] wakeup recovery failed: \(err)", category: .app)
SDLLogger.log("[PacketTunnelProvider] wakeup recovery failed: \(err.localizedDescription)", category: .app)
} else {
SDLLogger.log("[PacketTunnelProvider] wakeup recovery completed", 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()
}
}
}