117 lines
3.7 KiB
Swift
117 lines
3.7 KiB
Swift
//
|
||
// VPNManager.swift
|
||
// sdlan
|
||
//
|
||
// Created by 安礼成 on 2024/1/17.
|
||
//
|
||
|
||
import Foundation
|
||
import NetworkExtension
|
||
import SwiftUI
|
||
import Observation
|
||
|
||
// vpn管理类
|
||
@Observable
|
||
class VPNManager {
|
||
static let shared = VPNManager()
|
||
|
||
private var manager: NETunnelProviderManager?
|
||
private var statusObserver: NSObjectProtocol?
|
||
|
||
var isConnected: Bool = false
|
||
var vpnStatus: VPNStatus = .disconnected
|
||
|
||
var vpnStatusStream: AsyncStream<VPNStatus>
|
||
private var vpnStatusCont: AsyncStream<VPNStatus>.Continuation
|
||
|
||
enum VPNStatus {
|
||
case connected
|
||
case disconnected
|
||
}
|
||
|
||
private init() {
|
||
(self.vpnStatusStream, self.vpnStatusCont) = AsyncStream.makeStream(of: VPNStatus.self)
|
||
}
|
||
|
||
// 开启vpn
|
||
func enableVpn(options: [String : NSObject]) async throws {
|
||
NSLog("enable vpn with options: \(options)")
|
||
let manager = try await loadAndCreateProviderManager()
|
||
try await manager.loadFromPreferences()
|
||
self.manager = manager
|
||
|
||
self.addVPNStatusObserver(manager)
|
||
try manager.connection.startVPNTunnel(options: options)
|
||
}
|
||
|
||
// 关闭vpn
|
||
func disableVpn() async throws {
|
||
guard let manager = self.manager else {
|
||
return
|
||
}
|
||
|
||
try await manager.loadFromPreferences()
|
||
manager.connection.stopVPNTunnel()
|
||
}
|
||
|
||
// MARK: - Private Methods
|
||
|
||
// 监控系统VPN的状态的变化
|
||
private func addVPNStatusObserver(_ manager: NETunnelProviderManager) {
|
||
if let statusObserver {
|
||
NotificationCenter.default.removeObserver(statusObserver)
|
||
self.statusObserver = nil
|
||
}
|
||
|
||
self.statusObserver = NotificationCenter.default.addObserver(forName: .NEVPNStatusDidChange, object: manager.connection, queue: .main) {[weak self] _ in
|
||
NSLog("status channge: \(manager.connection.status)")
|
||
switch manager.connection.status {
|
||
case .invalid, .disconnected, .disconnecting:
|
||
self?.vpnStatusCont.yield(.disconnected)
|
||
self?.vpnStatus = .disconnected
|
||
self?.isConnected = false
|
||
case .connecting, .connected, .reasserting:
|
||
self?.vpnStatusCont.yield(.connected)
|
||
self?.vpnStatus = .connected
|
||
self?.isConnected = true
|
||
@unknown default:
|
||
self?.vpnStatusCont.yield(.disconnected)
|
||
self?.vpnStatus = .disconnected
|
||
self?.isConnected = false
|
||
}
|
||
}
|
||
}
|
||
|
||
// MARK: - Private Methods
|
||
|
||
// 加载或者创建相关的配置
|
||
private func loadAndCreateProviderManager() async throws -> NETunnelProviderManager {
|
||
let managers = try await NETunnelProviderManager.loadAllFromPreferences()
|
||
|
||
let manager = managers.first ?? NETunnelProviderManager()
|
||
manager.localizedDescription = "punchnetmac"
|
||
manager.isEnabled = true
|
||
|
||
// 设置相关参数,在PacketTunnel中可以用
|
||
let protocolConfiguration = NETunnelProviderProtocol()
|
||
protocolConfiguration.serverAddress = "punchnetmac"
|
||
protocolConfiguration.providerConfiguration = [String:AnyObject]()
|
||
protocolConfiguration.providerBundleIdentifier = "com.jihe.punchnetmac.tun"
|
||
manager.protocolConfiguration = protocolConfiguration
|
||
manager.isOnDemandEnabled = false
|
||
|
||
manager.isEnabled = true
|
||
|
||
try await manager.saveToPreferences()
|
||
|
||
return manager
|
||
}
|
||
|
||
deinit {
|
||
if let statusObserver {
|
||
NotificationCenter.default.removeObserver(statusObserver)
|
||
}
|
||
}
|
||
|
||
}
|