2024-02-11 01:55:54 +03:00
|
|
|
|
import Foundation
|
|
|
|
|
|
import NetworkExtension
|
2025-12-15 14:54:34 +01:00
|
|
|
|
import Network
|
2024-02-11 01:55:54 +03:00
|
|
|
|
import os
|
|
|
|
|
|
import Darwin
|
|
|
|
|
|
import OpenVPNAdapter
|
|
|
|
|
|
|
|
|
|
|
|
enum TunnelProtoType: String {
|
2024-06-30 12:19:38 +03:00
|
|
|
|
case wireguard, openvpn, xray
|
|
|
|
|
|
|
2024-02-11 01:55:54 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct Constants {
|
|
|
|
|
|
static let kDefaultPathKey = "defaultPath"
|
|
|
|
|
|
static let processQueueName = "org.amnezia.process-packets"
|
|
|
|
|
|
static let kActivationAttemptId = "activationAttemptId"
|
|
|
|
|
|
static let ovpnConfigKey = "ovpn"
|
2024-06-30 12:19:38 +03:00
|
|
|
|
static let xrayConfigKey = "xray"
|
2024-02-11 01:55:54 +03:00
|
|
|
|
static let wireGuardConfigKey = "wireguard"
|
|
|
|
|
|
static let loggerTag = "NET"
|
2024-03-20 18:35:36 +03:00
|
|
|
|
|
2024-02-11 01:55:54 +03:00
|
|
|
|
static let kActionStart = "start"
|
|
|
|
|
|
static let kActionRestart = "restart"
|
|
|
|
|
|
static let kActionStop = "stop"
|
|
|
|
|
|
static let kActionGetTunnelId = "getTunnelId"
|
|
|
|
|
|
static let kActionStatus = "status"
|
|
|
|
|
|
static let kActionIsServerReachable = "isServerReachable"
|
|
|
|
|
|
static let kMessageKeyAction = "action"
|
|
|
|
|
|
static let kMessageKeyTunnelId = "tunnelId"
|
|
|
|
|
|
static let kMessageKeyConfig = "config"
|
|
|
|
|
|
static let kMessageKeyErrorCode = "errorCode"
|
|
|
|
|
|
static let kMessageKeyHost = "host"
|
|
|
|
|
|
static let kMessageKeyPort = "port"
|
|
|
|
|
|
static let kMessageKeyOnDemand = "is-on-demand"
|
|
|
|
|
|
static let kMessageKeySplitTunnelType = "SplitTunnelType"
|
|
|
|
|
|
static let kMessageKeySplitTunnelSites = "SplitTunnelSites"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class PacketTunnelProvider: NEPacketTunnelProvider {
|
2024-05-17 18:17:08 +07:00
|
|
|
|
var wgAdapter: WireGuardAdapter?
|
|
|
|
|
|
var ovpnAdapter: OpenVPNAdapter?
|
2025-12-15 14:54:34 +01:00
|
|
|
|
private lazy var openVPNPacketFlowAdapter = PacketTunnelFlowAdapter(flow: packetFlow)
|
|
|
|
|
|
private let pathMonitorQueue = DispatchQueue(label: Constants.processQueueName + ".path-monitor")
|
2026-03-20 14:51:36 +02:00
|
|
|
|
private let networkChangeQueue = DispatchQueue(label: Constants.processQueueName + ".network-change")
|
2025-12-15 14:54:34 +01:00
|
|
|
|
private let pathMonitor = NWPathMonitor()
|
|
|
|
|
|
private var didReceiveInitialPathUpdate = false
|
|
|
|
|
|
private var currentPath: Network.NWPath?
|
|
|
|
|
|
private var currentPathSignature: String?
|
2026-03-20 14:51:36 +02:00
|
|
|
|
private var pendingNetworkChangeWorkItem: DispatchWorkItem?
|
|
|
|
|
|
private var isApplyingNetworkChange = false
|
2024-05-17 18:17:08 +07:00
|
|
|
|
|
|
|
|
|
|
var splitTunnelType: Int?
|
|
|
|
|
|
var splitTunnelSites: [String]?
|
|
|
|
|
|
|
|
|
|
|
|
let vpnReachability = OpenVPNReachability()
|
|
|
|
|
|
|
|
|
|
|
|
var startHandler: ((Error?) -> Void)?
|
|
|
|
|
|
var stopHandler: (() -> Void)?
|
|
|
|
|
|
var protoType: TunnelProtoType?
|
2025-12-15 14:54:34 +01:00
|
|
|
|
|
|
|
|
|
|
var activeIfaceIdx: UInt32 = 0
|
|
|
|
|
|
|
|
|
|
|
|
func openVPNPacketFlow() -> OpenVPNAdapterPacketFlow {
|
|
|
|
|
|
openVPNPacketFlowAdapter
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
override init() {
|
|
|
|
|
|
super.init()
|
|
|
|
|
|
pathMonitor.pathUpdateHandler = { [weak self] path in
|
|
|
|
|
|
guard let self else { return }
|
|
|
|
|
|
self.currentPath = path
|
|
|
|
|
|
let signature = self.pathSignature(for: path)
|
|
|
|
|
|
let hasMeaningfulChange = self.currentPathSignature != signature
|
|
|
|
|
|
self.currentPathSignature = signature
|
|
|
|
|
|
self.updateActiveInterfaceIndex(for: path)
|
|
|
|
|
|
|
|
|
|
|
|
guard self.didReceiveInitialPathUpdate else {
|
|
|
|
|
|
self.didReceiveInitialPathUpdate = true
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-30 04:45:32 +02:00
|
|
|
|
guard hasMeaningfulChange, let proto = self.protoType else { return }
|
|
|
|
|
|
|
2026-03-20 14:51:36 +02:00
|
|
|
|
// OpenVPN and WireGuard/AWG handle network changes internally.
|
|
|
|
|
|
// Restarting them here can race their own reconnect logic and break tunnel setup.
|
|
|
|
|
|
if proto == .wireguard || proto == .openvpn {
|
2025-12-30 04:45:32 +02:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-12-15 14:54:34 +01:00
|
|
|
|
|
2026-03-20 14:51:36 +02:00
|
|
|
|
self.scheduleNetworkChangeHandling(for: proto, path: path)
|
2025-12-15 14:54:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
pathMonitor.start(queue: pathMonitorQueue)
|
|
|
|
|
|
|
|
|
|
|
|
currentPath = pathMonitor.currentPath
|
|
|
|
|
|
currentPathSignature = pathSignature(for: pathMonitor.currentPath)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func updateActiveInterfaceIndex(for path: Network.NWPath?) {
|
|
|
|
|
|
guard let path else {
|
|
|
|
|
|
activeIfaceIdx = 0
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let preferredTypes: [NWInterface.InterfaceType] = [.wiredEthernet, .wifi, .cellular, .other]
|
|
|
|
|
|
|
|
|
|
|
|
let nonLoopbackInterfaces = path.availableInterfaces.filter { $0.type != .loopback }
|
|
|
|
|
|
let activeInterfaces = nonLoopbackInterfaces.filter { path.usesInterfaceType($0.type) }
|
|
|
|
|
|
|
|
|
|
|
|
let candidate = preferredTypes.compactMap { type in
|
|
|
|
|
|
activeInterfaces.first { $0.type == type }
|
|
|
|
|
|
}.first ?? activeInterfaces.first ?? nonLoopbackInterfaces.first
|
|
|
|
|
|
|
|
|
|
|
|
if let candidate {
|
|
|
|
|
|
activeIfaceIdx = UInt32(candidate.index)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
activeIfaceIdx = 0
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func updateActiveInterfaceIndexForCurrentPath() {
|
|
|
|
|
|
if let currentPath {
|
|
|
|
|
|
currentPathSignature = pathSignature(for: currentPath)
|
|
|
|
|
|
updateActiveInterfaceIndex(for: currentPath)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
currentPath = pathMonitor.currentPath
|
|
|
|
|
|
currentPathSignature = pathSignature(for: pathMonitor.currentPath)
|
|
|
|
|
|
updateActiveInterfaceIndex(for: pathMonitor.currentPath)
|
|
|
|
|
|
}
|
2024-05-17 18:17:08 +07:00
|
|
|
|
|
2024-02-11 01:55:54 +03:00
|
|
|
|
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
2025-12-30 04:45:32 +02:00
|
|
|
|
if messageData.count == 1 && messageData[0] == 0 {
|
|
|
|
|
|
guard let completionHandler else { return }
|
|
|
|
|
|
if protoType == .wireguard {
|
|
|
|
|
|
handleWireguardAppMessage(messageData, completionHandler: completionHandler)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
completionHandler(nil)
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-05-17 18:17:08 +07:00
|
|
|
|
guard let message = String(data: messageData, encoding: .utf8) else {
|
|
|
|
|
|
if let completionHandler {
|
|
|
|
|
|
completionHandler(nil)
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
2024-03-20 18:35:36 +03:00
|
|
|
|
}
|
2024-05-17 18:17:08 +07:00
|
|
|
|
|
|
|
|
|
|
neLog(.info, title: "App said: ", message: message)
|
|
|
|
|
|
|
|
|
|
|
|
guard let message = try? JSONSerialization.jsonObject(with: messageData, options: []) as? [String: Any] else {
|
2025-12-30 04:45:32 +02:00
|
|
|
|
if protoType == .wireguard {
|
|
|
|
|
|
handleWireguardAppMessage(messageData, completionHandler: completionHandler)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2024-05-17 18:17:08 +07:00
|
|
|
|
neLog(.error, message: "Failed to serialize message from app")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
guard let completionHandler else {
|
|
|
|
|
|
neLog(.error, message: "Missing message completion handler")
|
|
|
|
|
|
return
|
2024-02-11 01:55:54 +03:00
|
|
|
|
}
|
2024-05-17 18:17:08 +07:00
|
|
|
|
|
|
|
|
|
|
guard let action = message[Constants.kMessageKeyAction] as? String else {
|
|
|
|
|
|
neLog(.error, message: "Missing action key in app message")
|
|
|
|
|
|
completionHandler(nil)
|
|
|
|
|
|
return
|
2024-02-11 01:55:54 +03:00
|
|
|
|
}
|
2024-05-17 18:17:08 +07:00
|
|
|
|
|
|
|
|
|
|
if action == Constants.kActionStatus {
|
|
|
|
|
|
handleStatusAppMessage(messageData,
|
|
|
|
|
|
completionHandler: completionHandler)
|
2024-02-11 01:55:54 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-05-17 18:17:08 +07:00
|
|
|
|
|
|
|
|
|
|
override func startTunnel(options: [String : NSObject]? = nil,
|
|
|
|
|
|
completionHandler: @escaping ((any Error)?) -> Void) {
|
|
|
|
|
|
let activationAttemptId = options?[Constants.kActivationAttemptId] as? String
|
|
|
|
|
|
let errorNotifier = ErrorNotifier(activationAttemptId: activationAttemptId)
|
|
|
|
|
|
|
|
|
|
|
|
neLog(.info, message: "Start tunnel")
|
|
|
|
|
|
|
|
|
|
|
|
if let protocolConfiguration = protocolConfiguration as? NETunnelProviderProtocol {
|
|
|
|
|
|
let providerConfiguration = protocolConfiguration.providerConfiguration
|
|
|
|
|
|
if (providerConfiguration?[Constants.ovpnConfigKey] as? Data) != nil {
|
|
|
|
|
|
protoType = .openvpn
|
|
|
|
|
|
} else if (providerConfiguration?[Constants.wireGuardConfigKey] as? Data) != nil {
|
|
|
|
|
|
protoType = .wireguard
|
2024-06-30 12:19:38 +03:00
|
|
|
|
} else if (providerConfiguration?[Constants.xrayConfigKey] as? Data) != nil {
|
|
|
|
|
|
protoType = .xray
|
2024-05-17 18:17:08 +07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
guard let protoType else {
|
|
|
|
|
|
let error = NSError(domain: "Protocol is not selected", code: 0)
|
|
|
|
|
|
completionHandler(error)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-15 14:54:34 +01:00
|
|
|
|
didReceiveInitialPathUpdate = false
|
|
|
|
|
|
updateActiveInterfaceIndexForCurrentPath()
|
|
|
|
|
|
|
2024-05-17 18:17:08 +07:00
|
|
|
|
switch protoType {
|
|
|
|
|
|
case .wireguard:
|
|
|
|
|
|
startWireguard(activationAttemptId: activationAttemptId,
|
|
|
|
|
|
errorNotifier: errorNotifier,
|
|
|
|
|
|
completionHandler: completionHandler)
|
|
|
|
|
|
case .openvpn:
|
|
|
|
|
|
startOpenVPN(completionHandler: completionHandler)
|
2024-06-30 12:19:38 +03:00
|
|
|
|
case .xray:
|
|
|
|
|
|
startXray(completionHandler: completionHandler)
|
|
|
|
|
|
|
2024-05-17 18:17:08 +07:00
|
|
|
|
}
|
2024-02-11 01:55:54 +03:00
|
|
|
|
}
|
2024-05-17 18:17:08 +07:00
|
|
|
|
|
2024-03-20 18:35:36 +03:00
|
|
|
|
|
2024-05-17 18:17:08 +07:00
|
|
|
|
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
|
|
|
|
|
guard let protoType else {
|
|
|
|
|
|
completionHandler()
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch protoType {
|
|
|
|
|
|
case .wireguard:
|
|
|
|
|
|
stopWireguard(with: reason,
|
|
|
|
|
|
completionHandler: completionHandler)
|
|
|
|
|
|
case .openvpn:
|
|
|
|
|
|
stopOpenVPN(with: reason,
|
|
|
|
|
|
completionHandler: completionHandler)
|
2024-06-30 12:19:38 +03:00
|
|
|
|
case .xray:
|
|
|
|
|
|
stopXray(completionHandler: completionHandler)
|
2024-05-17 18:17:08 +07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-03-20 18:35:36 +03:00
|
|
|
|
|
2024-05-17 18:17:08 +07:00
|
|
|
|
func handleStatusAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
|
|
|
|
|
guard let protoType else {
|
|
|
|
|
|
completionHandler?(nil)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch protoType {
|
|
|
|
|
|
case .wireguard:
|
|
|
|
|
|
handleWireguardStatusMessage(messageData, completionHandler: completionHandler)
|
|
|
|
|
|
case .openvpn:
|
|
|
|
|
|
handleOpenVPNStatusMessage(messageData, completionHandler: completionHandler)
|
2024-06-30 12:19:38 +03:00
|
|
|
|
case .xray:
|
|
|
|
|
|
break;
|
2024-05-17 18:17:08 +07:00
|
|
|
|
}
|
2024-02-11 01:55:54 +03:00
|
|
|
|
}
|
2024-05-17 18:17:08 +07:00
|
|
|
|
|
|
|
|
|
|
// MARK: Network observing methods
|
|
|
|
|
|
override func observeValue(forKeyPath keyPath: String?,
|
|
|
|
|
|
of object: Any?,
|
|
|
|
|
|
change: [NSKeyValueChangeKey: Any]?,
|
|
|
|
|
|
context: UnsafeMutableRawPointer?) {
|
2025-12-15 14:54:34 +01:00
|
|
|
|
guard Constants.kDefaultPathKey == keyPath else {
|
2024-05-17 18:17:08 +07:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2024-02-11 01:55:54 +03:00
|
|
|
|
}
|
2024-03-20 18:35:36 +03:00
|
|
|
|
|
2025-12-15 14:54:34 +01:00
|
|
|
|
private func handle(networkChange changePath: Network.NWPath, completion: @escaping (Error?) -> Void) {
|
2026-03-20 14:51:36 +02:00
|
|
|
|
guard protoType == .xray else {
|
|
|
|
|
|
updateActiveInterfaceIndex(for: changePath)
|
|
|
|
|
|
completion(nil)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-15 14:54:34 +01:00
|
|
|
|
updateActiveInterfaceIndex(for: changePath)
|
2026-03-20 14:51:36 +02:00
|
|
|
|
reasserting = true
|
|
|
|
|
|
xrayLog(.info, message: "Applying network change to xray tunnel")
|
|
|
|
|
|
stopXray { }
|
|
|
|
|
|
startXray { [weak self] error in
|
|
|
|
|
|
self?.reasserting = false
|
|
|
|
|
|
completion(error)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private func scheduleNetworkChangeHandling(for proto: TunnelProtoType, path: Network.NWPath) {
|
|
|
|
|
|
guard proto == .xray else { return }
|
|
|
|
|
|
|
|
|
|
|
|
pendingNetworkChangeWorkItem?.cancel()
|
|
|
|
|
|
|
|
|
|
|
|
let workItem = DispatchWorkItem { [weak self] in
|
|
|
|
|
|
guard let self else { return }
|
|
|
|
|
|
|
|
|
|
|
|
if self.isApplyingNetworkChange {
|
|
|
|
|
|
xrayLog(.debug, message: "Skipping network change while restart is already in progress")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
self.isApplyingNetworkChange = true
|
|
|
|
|
|
DispatchQueue.main.async {
|
|
|
|
|
|
self.handle(networkChange: path) { [weak self] _ in
|
|
|
|
|
|
self?.networkChangeQueue.async {
|
|
|
|
|
|
self?.isApplyingNetworkChange = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pendingNetworkChangeWorkItem = workItem
|
|
|
|
|
|
networkChangeQueue.asyncAfter(deadline: .now() + 1.0, execute: workItem)
|
2024-05-17 18:17:08 +07:00
|
|
|
|
}
|
2024-02-11 01:55:54 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-15 14:54:34 +01:00
|
|
|
|
private extension PacketTunnelProvider {
|
|
|
|
|
|
func pathSignature(for path: Network.NWPath) -> String {
|
|
|
|
|
|
var signatureComponents = [String(describing: path.status)]
|
|
|
|
|
|
signatureComponents.append(path.isExpensive ? "exp" : "noexp")
|
|
|
|
|
|
signatureComponents.append(path.isConstrained ? "con" : "nocon")
|
|
|
|
|
|
|
|
|
|
|
|
let preferredTypes: [NWInterface.InterfaceType] = [.wiredEthernet, .wifi, .cellular, .loopback, .other]
|
|
|
|
|
|
let sortedInterfaces = path.availableInterfaces.sorted { lhs, rhs in
|
|
|
|
|
|
if lhs.type == rhs.type {
|
|
|
|
|
|
return lhs.index < rhs.index
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let lhsOrder = preferredTypes.firstIndex(of: lhs.type) ?? preferredTypes.count
|
|
|
|
|
|
let rhsOrder = preferredTypes.firstIndex(of: rhs.type) ?? preferredTypes.count
|
|
|
|
|
|
|
|
|
|
|
|
if lhsOrder == rhsOrder {
|
|
|
|
|
|
return lhs.index < rhs.index
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return lhsOrder < rhsOrder
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for interface in sortedInterfaces {
|
|
|
|
|
|
let typeName: String
|
|
|
|
|
|
switch interface.type {
|
|
|
|
|
|
case .wiredEthernet: typeName = "ethernet"
|
|
|
|
|
|
case .wifi: typeName = "wifi"
|
|
|
|
|
|
case .cellular: typeName = "cellular"
|
|
|
|
|
|
case .loopback: typeName = "loopback"
|
|
|
|
|
|
case .other: typeName = "other"
|
|
|
|
|
|
@unknown default: typeName = "unknown"
|
|
|
|
|
|
}
|
|
|
|
|
|
signatureComponents.append("\(typeName):\(interface.index)")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Include currently used interface preference ordering
|
|
|
|
|
|
for type in preferredTypes {
|
|
|
|
|
|
let usesType = path.usesInterfaceType(type)
|
|
|
|
|
|
signatureComponents.append("uses-\(type):\(usesType)")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return signatureComponents.joined(separator: "|")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-02-11 01:55:54 +03:00
|
|
|
|
extension WireGuardLogLevel {
|
|
|
|
|
|
var osLogLevel: OSLogType {
|
|
|
|
|
|
switch self {
|
|
|
|
|
|
case .verbose:
|
|
|
|
|
|
return .debug
|
|
|
|
|
|
case .error:
|
|
|
|
|
|
return .error
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-04-15 01:04:01 +03:00
|
|
|
|
|
2025-12-15 14:54:34 +01:00
|
|
|
|
final class PacketTunnelFlowAdapter: NSObject, OpenVPNAdapterPacketFlow {
|
|
|
|
|
|
private let flow: NEPacketTunnelFlow
|
|
|
|
|
|
|
|
|
|
|
|
init(flow: NEPacketTunnelFlow) {
|
|
|
|
|
|
self.flow = flow
|
|
|
|
|
|
super.init()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@objc(readPacketsWithCompletionHandler:)
|
|
|
|
|
|
func readPackets(completionHandler: @escaping ([Data], [NSNumber]) -> Void) {
|
|
|
|
|
|
flow.readPackets(completionHandler: completionHandler)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@objc(writePackets:withProtocols:)
|
|
|
|
|
|
func writePackets(_ packets: [Data], withProtocols protocols: [NSNumber]) -> Bool {
|
|
|
|
|
|
flow.writePackets(packets, withProtocols: protocols)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
extension NEProviderStopReason {
|
|
|
|
|
|
var amneziaDescription: String {
|
2024-04-15 01:04:01 +03:00
|
|
|
|
switch self {
|
|
|
|
|
|
case .none:
|
|
|
|
|
|
return "No specific reason"
|
|
|
|
|
|
case .userInitiated:
|
|
|
|
|
|
return "The user stopped the NE"
|
|
|
|
|
|
case .providerFailed:
|
|
|
|
|
|
return "The NE failed to function correctly"
|
|
|
|
|
|
case .noNetworkAvailable:
|
|
|
|
|
|
return "No network connectivity is currently available"
|
|
|
|
|
|
case .unrecoverableNetworkChange:
|
|
|
|
|
|
return "The device’s network connectivity changed"
|
|
|
|
|
|
case .providerDisabled:
|
|
|
|
|
|
return "The NE was disabled"
|
|
|
|
|
|
case .authenticationCanceled:
|
|
|
|
|
|
return "The authentication process was canceled"
|
|
|
|
|
|
case .configurationFailed:
|
|
|
|
|
|
return "The VPNC is invalid"
|
|
|
|
|
|
case .idleTimeout:
|
|
|
|
|
|
return "The session timed out"
|
|
|
|
|
|
case .configurationDisabled:
|
|
|
|
|
|
return "The VPNC was disabled"
|
|
|
|
|
|
case .configurationRemoved:
|
|
|
|
|
|
return "The VPNC was removed"
|
|
|
|
|
|
case .superceded:
|
|
|
|
|
|
return "VPNC was superceded by a higher-priority VPNC"
|
|
|
|
|
|
case .userLogout:
|
|
|
|
|
|
return "The user logged out"
|
|
|
|
|
|
case .userSwitch:
|
|
|
|
|
|
return "The current console user changed"
|
|
|
|
|
|
case .connectionFailed:
|
|
|
|
|
|
return "The connection failed"
|
2025-12-15 14:54:34 +01:00
|
|
|
|
case .internalError:
|
|
|
|
|
|
return "The network extension reported an internal error"
|
2024-04-15 01:04:01 +03:00
|
|
|
|
case .sleep:
|
|
|
|
|
|
return "A stop reason indicating the VPNC enabled disconnect on sleep and the device went to sleep"
|
|
|
|
|
|
case .appUpdate:
|
|
|
|
|
|
return "appUpdat"
|
|
|
|
|
|
@unknown default:
|
|
|
|
|
|
return "@unknown default"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|