feat(client/macos): OSSystemExtensionRequest 激活 sysext
VpnChannel.activateSystemExtensionIfNeeded 实装:start 前请求系统加载/更新 PacketTunnel sysext;首启弹「隐私与安全性」待用户允许(await 阻塞到批准, 扩展没加载隧道起不来)。SysExtActivationDelegate 处理 finish/fail/replace。 至此 macOS 原生 VPN 栈构建完整:Runner 嵌入 sysext 到 Contents/Library/SystemExtensions,编译全过。运行待:sysext 签名放行 + systemextensionsctl developer on(或 Developer ID 公证)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
|
||||
import FlutterMacOS
|
||||
import NetworkExtension
|
||||
import SystemExtensions
|
||||
|
||||
// NSLog 兼容老部署目标(Runner < macOS 11,os.Logger 不可用)。
|
||||
private func vpnLog(_ message: String) { NSLog("[pangolin/vpn] %@", message) }
|
||||
@@ -24,6 +25,7 @@ final class VpnChannel: NSObject {
|
||||
private var statusObserver: NSObjectProtocol?
|
||||
private var statsTimer: Timer?
|
||||
private var manager: NETunnelProviderManager?
|
||||
private var sysextDelegate: SysExtActivationDelegate?
|
||||
|
||||
static func register(with registrar: FlutterPluginRegistrar) {
|
||||
let instance = VpnChannel()
|
||||
@@ -100,11 +102,17 @@ final class VpnChannel: NSObject {
|
||||
return mgr
|
||||
}
|
||||
|
||||
// 站外 sysex 首次需用户授权激活。
|
||||
// 请求系统加载/更新 PacketTunnel System Extension。首启系统会弹「隐私与安全性」
|
||||
// 让用户允许;允许后 didFinishWithResult 回来。已是最新则快速完成。
|
||||
private func activateSystemExtensionIfNeeded() async throws {
|
||||
// TODO(P1): OSSystemExtensionRequest.activationRequest(
|
||||
// forExtensionWithIdentifier: tunnelBundleId, queue: .main) + delegate 等待结果。
|
||||
// 已激活则直接返回。详见安装文档步骤 4。
|
||||
try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Void, Error>) in
|
||||
let req = OSSystemExtensionRequest.activationRequest(
|
||||
forExtensionWithIdentifier: Self.tunnelBundleId, queue: .main)
|
||||
let delegate = SysExtActivationDelegate(continuation: cont)
|
||||
self.sysextDelegate = delegate // 保活到回调结束
|
||||
req.delegate = delegate
|
||||
OSSystemExtensionManager.shared.submitRequest(req)
|
||||
}
|
||||
}
|
||||
|
||||
// ── 状态 / 速率回传 ─────────────────────────────────────────────
|
||||
@@ -161,3 +169,32 @@ private final class StatsStreamHandler: NSObject, FlutterStreamHandler {
|
||||
}
|
||||
func onCancel(withArguments _: Any?) -> FlutterError? { owner?.onStatsCancel(); return nil }
|
||||
}
|
||||
|
||||
// System Extension 激活请求回调。首启需用户在「系统设置→隐私与安全性」允许,
|
||||
// 允许后收到 didFinishWithResult(故 await 会阻塞到用户批准 —— 这是对的,
|
||||
// 扩展没加载前隧道起不来)。
|
||||
private final class SysExtActivationDelegate: NSObject, OSSystemExtensionRequestDelegate {
|
||||
private let continuation: CheckedContinuation<Void, Error>
|
||||
private var resumed = false
|
||||
init(continuation: CheckedContinuation<Void, Error>) { self.continuation = continuation }
|
||||
|
||||
func request(_ request: OSSystemExtensionRequest,
|
||||
didFinishWithResult result: OSSystemExtensionRequest.Result) {
|
||||
guard !resumed else { return }
|
||||
resumed = true
|
||||
continuation.resume()
|
||||
}
|
||||
func request(_ request: OSSystemExtensionRequest, didFailWithError error: Error) {
|
||||
guard !resumed else { return }
|
||||
resumed = true
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
func requestNeedsUserApproval(_ request: OSSystemExtensionRequest) {
|
||||
vpnLog("system extension 待用户允许(系统设置 → 隐私与安全性)")
|
||||
}
|
||||
func request(_ request: OSSystemExtensionRequest,
|
||||
actionForReplacingExtension existing: OSSystemExtensionProperties,
|
||||
withExtension ext: OSSystemExtensionProperties) -> OSSystemExtensionRequest.ReplacementAction {
|
||||
.replace
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user