Files
pangolin/client/macos/PacketTunnel/PacketTunnelProvider.swift
T
wangjia 8367984e23
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
fix(client/macos): PacketTunnelProvider 改用 completion-handler 绕开 Swift 6.2 编译器崩溃
Swift 6.2.3 在为 async 重写的 startTunnel 生成 ObjC 桥接 thunk 时崩溃
(emitInjectLoadableEnum)。改用 completion-handler 形式(直接是 @objc 原型)
绕开。startTunnel/stopTunnel/handleAppMessage 三个 override 同步改。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 14:30:11 +08:00

136 lines
6.8 KiB
Swift
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// PacketTunnelProvider.swift Pangolin macOS System Extension(B / P1 )
//
// : sysex NEPacketTunnelProvider sing-box(libbox)
// TUN, root( PoC `sudo sing-box run` )
//
// ( docs/p1-macos-system-extension.md):
// app(Flutter) NETunnelProviderManager.startVPNTunnel(options:) sing-box
// JSON startTunnel LibboxSetup(App Group ) LibboxNewService(json,
// platformInterface) start;libbox platformInterface.openTun ,
// NEPacketTunnelNetworkSettings + setTunnelNetworkSettings, packetFlow fd
// / libbox CommandServer(loopback) app CommandClient
//
// :
// 1. Libbox.xcframework(scripts/build-libbox.sh apple macos )
// 2. target NetworkExtension + Libbox
// 3. PangolinPlatformInterface LibboxPlatformInterfaceProtocol ****
// sing-box-for-apple ExtensionPlatformInterface ()
import NetworkExtension
import os
// import Libbox // gomobile bind ; xcframework
private let log = Logger(subsystem: "com.pangolin.pangolin.PacketTunnel", category: "provider")
class PacketTunnelProvider: NEPacketTunnelProvider {
// libbox ( Libbox , xcframework LibboxBoxService)
private var boxService: AnyObject?
private var platformInterface: PangolinPlatformInterface?
// App Group : sing-box (/) app
private static let appGroup = "group.com.pangolin.pangolin"
// completion-handler ( async):Swift 6.2 async
// ObjC thunk (emitInjectLoadableEnum),completion-handler
// @objc , bug completionHandler(nil), error
override func startTunnel(options: [String: NSObject]?,
completionHandler: @escaping (Error?) -> Void) {
log.info("startTunnel")
// 1) sing-box JSON: startVPNTunnel options["configContent"],
// 退 App Group ( app )
let configContent: String
if let inline = options?["configContent"] as? String, !inline.isEmpty {
configContent = inline
} else {
configContent = (try? Self.readSharedConfig()) ?? ""
}
guard !configContent.isEmpty else {
completionHandler(NSError(domain: "pangolin.tunnel", code: 1,
userInfo: [NSLocalizedDescriptionKey: "empty sing-box config"]))
return
}
// 2) libbox ( App Group ,)
guard let base = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: Self.appGroup) else {
completionHandler(NSError(domain: "pangolin.tunnel", code: 2,
userInfo: [NSLocalizedDescriptionKey: "no app group container"]))
return
}
let work = base.appendingPathComponent("work", isDirectory: true)
try? FileManager.default.createDirectory(at: work, withIntermediateDirectories: true)
/* Libbox.xcframework 线
var setupError: NSError?
let setup = LibboxSetupOptions()
setup.basePath = base.path
setup.workingPath = work.path
setup.tempPath = NSTemporaryDirectory()
LibboxSetup(setup, &setupError)
if let setupError { completionHandler(setupError); return }
let platform = PangolinPlatformInterface(provider: self)
self.platformInterface = platform
var newError: NSError?
guard let service = LibboxNewService(configContent, platform, &newError) else {
completionHandler(newError ?? NSError(domain: "pangolin.tunnel", code: 3)); return
}
self.boxService = service
do { try service.start() } catch { completionHandler(error); return }
completionHandler(nil) //
return
*/
// :xcframework ,,
completionHandler(NSError(domain: "pangolin.tunnel", code: 99, userInfo: [
NSLocalizedDescriptionKey:
"Libbox 尚未接入(见 docs/p1-macos-system-extension.md 步骤 3)",
]))
}
override func stopTunnel(with reason: NEProviderStopReason,
completionHandler: @escaping () -> Void) {
log.info("stopTunnel reason=\(reason.rawValue)")
/* if let service = boxService as? LibboxBoxService { try? service.close() } */
boxService = nil
platformInterface = nil
completionHandler()
}
// app sendProviderMessage (); libbox CommandServer
override func handleAppMessage(_ messageData: Data,
completionHandler: ((Data?) -> Void)?) {
completionHandler?(nil)
}
private static func readSharedConfig() throws -> String {
guard let base = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: appGroup) else {
throw NSError(domain: "pangolin.tunnel", code: 2)
}
let url = base.appendingPathComponent("config.json")
return try String(contentsOf: url, encoding: .utf8)
}
}
/// libbox NetworkExtension (LibboxPlatformInterfaceProtocol)
///
/// openTun:libbox (//DNS/MTU),
/// NEPacketTunnelNetworkSettingssetTunnelNetworkSettings, packetFlow tun fd
///
/// (openTun / writeLog / useProcFS / findConnectionOwner /
/// defaultInterfaceMonitor / getInterfaces / underNetworkExtension / systemCertificates )
/// ExtensionPlatformInterface 3
final class PangolinPlatformInterface: NSObject {
private weak var provider: NEPacketTunnelProvider?
init(provider: NEPacketTunnelProvider) {
self.provider = provider
}
// :openTun libbox NEPacketTunnelNetworkSettings
// Libbox (LibboxTunOptions / tun fd)
}