punchnet-macos/Tun/PacketTunnelProvider.swift

147 lines
4.3 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 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()
}
}