punchnet-macos/punchnet/Core/VPNManager.swift
2026-03-25 16:09:06 +08:00

117 lines
3.7 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.

//
// 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 vpnStatus: VPNStatus = .disconnected
var isConnected: Bool = false
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)
}
}
}