Sync configs
This commit is contained in:
parent
e648054c7a
commit
73eb85f2f4
4 changed files with 293 additions and 261 deletions
|
@ -18,40 +18,32 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate {
|
||||||
// send empty string to NEDNSSettings.matchDomains
|
// send empty string to NEDNSSettings.matchDomains
|
||||||
networkSettings?.dnsSettings?.matchDomains = [""]
|
networkSettings?.dnsSettings?.matchDomains = [""]
|
||||||
|
|
||||||
if splitTunnelType == "1" {
|
if splitTunnelType == 1 {
|
||||||
var ipv4IncludedRoutes = [NEIPv4Route]()
|
var ipv4IncludedRoutes = [NEIPv4Route]()
|
||||||
let STSdata = Data(splitTunnelSites!.utf8)
|
|
||||||
do {
|
for allowedIPString in splitTunnelSites {
|
||||||
guard let STSArray = try JSONSerialization.jsonObject(with: STSdata) as? [String] else { return }
|
if let allowedIP = IPAddressRange(from: allowedIPString) {
|
||||||
for allowedIPString in STSArray {
|
ipv4IncludedRoutes.append(NEIPv4Route(
|
||||||
if let allowedIP = IPAddressRange(from: allowedIPString) {
|
destinationAddress: "\(allowedIP.address)",
|
||||||
ipv4IncludedRoutes.append(NEIPv4Route(
|
subnetMask: "\(allowedIP.subnetMask())"))
|
||||||
destinationAddress: "\(allowedIP.address)",
|
|
||||||
subnetMask: "\(allowedIP.subnetMask())"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch {
|
|
||||||
wg_log(.error, message: "Parse JSONSerialization Error")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
networkSettings?.ipv4Settings?.includedRoutes = ipv4IncludedRoutes
|
networkSettings?.ipv4Settings?.includedRoutes = ipv4IncludedRoutes
|
||||||
} else {
|
} else {
|
||||||
if splitTunnelType == "2" {
|
if splitTunnelType == 2 {
|
||||||
var ipv4ExcludedRoutes = [NEIPv4Route]()
|
var ipv4ExcludedRoutes = [NEIPv4Route]()
|
||||||
var ipv4IncludedRoutes = [NEIPv4Route]()
|
var ipv4IncludedRoutes = [NEIPv4Route]()
|
||||||
var ipv6IncludedRoutes = [NEIPv6Route]()
|
var ipv6IncludedRoutes = [NEIPv6Route]()
|
||||||
let STSdata = Data(splitTunnelSites!.utf8)
|
|
||||||
do {
|
for excludeIPString in splitTunnelSites {
|
||||||
guard let STSArray = try JSONSerialization.jsonObject(with: STSdata) as? [String] else { return }
|
if let excludeIP = IPAddressRange(from: excludeIPString) {
|
||||||
for excludeIPString in STSArray {
|
ipv4ExcludedRoutes.append(NEIPv4Route(
|
||||||
if let excludeIP = IPAddressRange(from: excludeIPString) {
|
destinationAddress: "\(excludeIP.address)",
|
||||||
ipv4ExcludedRoutes.append(NEIPv4Route(
|
subnetMask: "\(excludeIP.subnetMask())"))
|
||||||
destinationAddress: "\(excludeIP.address)",
|
|
||||||
subnetMask: "\(excludeIP.subnetMask())"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch {
|
|
||||||
wg_log(.error, message: "Parse JSONSerialization Error")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let allIPv4 = IPAddressRange(from: "0.0.0.0/0") {
|
if let allIPv4 = IPAddressRange(from: "0.0.0.0/0") {
|
||||||
ipv4IncludedRoutes.append(NEIPv4Route(
|
ipv4IncludedRoutes.append(NEIPv4Route(
|
||||||
destinationAddress: "\(allIPv4.address)",
|
destinationAddress: "\(allIPv4.address)",
|
||||||
|
|
|
@ -50,8 +50,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
private let dispatchQueue = DispatchQueue(label: "PacketTunnel", qos: .utility)
|
private let dispatchQueue = DispatchQueue(label: "PacketTunnel", qos: .utility)
|
||||||
|
|
||||||
private var openVPNConfig: Data?
|
private var openVPNConfig: Data?
|
||||||
var splitTunnelType: String?
|
var splitTunnelType: Int!
|
||||||
var splitTunnelSites: String?
|
var splitTunnelSites: [String]!
|
||||||
|
|
||||||
let vpnReachability = OpenVPNReachability()
|
let vpnReachability = OpenVPNReachability()
|
||||||
|
|
||||||
|
@ -81,22 +81,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
if action == Constants.kActionStatus {
|
if action == Constants.kActionStatus {
|
||||||
handleStatusAppMessage(messageData, completionHandler: completionHandler)
|
handleStatusAppMessage(messageData, completionHandler: completionHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
if action == Constants.kActionStart {
|
|
||||||
splitTunnelType = message[Constants.kMessageKeySplitTunnelType] as? String
|
|
||||||
splitTunnelSites = message[Constants.kMessageKeySplitTunnelSites] as? String
|
|
||||||
}
|
|
||||||
|
|
||||||
let callbackWrapper: (NSNumber?) -> Void = { errorCode in
|
|
||||||
// let tunnelId = self.tunnelConfig?.id ?? ""
|
|
||||||
let response: [String: Any] = [
|
|
||||||
Constants.kMessageKeyAction: action,
|
|
||||||
Constants.kMessageKeyErrorCode: errorCode ?? NSNull(),
|
|
||||||
Constants.kMessageKeyTunnelId: 0
|
|
||||||
]
|
|
||||||
|
|
||||||
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
||||||
|
@ -169,110 +153,118 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
completionHandler: @escaping (Error?) -> Void) {
|
completionHandler: @escaping (Error?) -> Void) {
|
||||||
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
||||||
let providerConfiguration = protocolConfiguration.providerConfiguration,
|
let providerConfiguration = protocolConfiguration.providerConfiguration,
|
||||||
let wgConfig: Data = providerConfiguration[Constants.wireGuardConfigKey] as? Data else {
|
let wgConfigData: Data = providerConfiguration[Constants.wireGuardConfigKey] as? Data else {
|
||||||
wg_log(.error, message: "Can't start WireGuard config missing")
|
wg_log(.error, message: "Can't start WireGuard config missing")
|
||||||
completionHandler(nil)
|
completionHandler(nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let wgConfigStr = try? JSONDecoder().decode(WGConfig.self, from: wgConfig).str,
|
do {
|
||||||
let tunnelConfiguration = try? TunnelConfiguration(fromWgQuickConfig: wgConfigStr)
|
let wgConfig = try JSONDecoder().decode(WGConfig.self, from: wgConfigData)
|
||||||
else {
|
let wgConfigStr = wgConfig.str
|
||||||
wg_log(.error, message: "Can't parse WireGuard config")
|
log(.info, message: "wgConfig: \(wgConfig.redux.replacingOccurrences(of: "\n", with: " "))")
|
||||||
completionHandler(nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log(.info, message: "wgConfig: \(wgConfigStr.replacingOccurrences(of: "\n", with: " "))")
|
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: wgConfigStr)
|
||||||
|
|
||||||
if tunnelConfiguration.peers.first!.allowedIPs
|
if tunnelConfiguration.peers.first!.allowedIPs
|
||||||
.map({ $0.stringRepresentation })
|
.map({ $0.stringRepresentation })
|
||||||
.joined(separator: ", ") == "0.0.0.0/0, ::/0" {
|
.joined(separator: ", ") == "0.0.0.0/0, ::/0" {
|
||||||
if splitTunnelType == "1" {
|
if wgConfig.splitTunnelType == 1 {
|
||||||
for index in tunnelConfiguration.peers.indices {
|
for index in tunnelConfiguration.peers.indices {
|
||||||
tunnelConfiguration.peers[index].allowedIPs.removeAll()
|
tunnelConfiguration.peers[index].allowedIPs.removeAll()
|
||||||
var allowedIPs = [IPAddressRange]()
|
var allowedIPs = [IPAddressRange]()
|
||||||
let STSdata = Data(splitTunnelSites!.utf8)
|
|
||||||
do {
|
for allowedIPString in wgConfig.splitTunnelSites {
|
||||||
guard let STSArray = try JSONSerialization.jsonObject(with: STSdata) as? [String] else { return }
|
|
||||||
for allowedIPString in STSArray {
|
|
||||||
if let allowedIP = IPAddressRange(from: allowedIPString) {
|
if let allowedIP = IPAddressRange(from: allowedIPString) {
|
||||||
allowedIPs.append(allowedIP)
|
allowedIPs.append(allowedIP)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
|
||||||
wg_log(.error, message: "Parse JSONSerialization Error")
|
tunnelConfiguration.peers[index].allowedIPs = allowedIPs
|
||||||
}
|
}
|
||||||
tunnelConfiguration.peers[index].allowedIPs = allowedIPs
|
} else if wgConfig.splitTunnelType == 2 {
|
||||||
}
|
for index in tunnelConfiguration.peers.indices {
|
||||||
} else if splitTunnelType == "2" {
|
var excludeIPs = [IPAddressRange]()
|
||||||
for index in tunnelConfiguration.peers.indices {
|
|
||||||
var excludeIPs = [IPAddressRange]()
|
for excludeIPString in wgConfig.splitTunnelSites {
|
||||||
let STSdata = Data(splitTunnelSites!.utf8)
|
|
||||||
do {
|
|
||||||
guard let STSArray = try JSONSerialization.jsonObject(with: STSdata) as? [String] else { return }
|
|
||||||
for excludeIPString in STSArray {
|
|
||||||
if let excludeIP = IPAddressRange(from: excludeIPString) {
|
if let excludeIP = IPAddressRange(from: excludeIPString) {
|
||||||
excludeIPs.append(excludeIP)
|
excludeIPs.append(excludeIP)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
|
||||||
wg_log(.error, message: "Parse JSONSerialization Error")
|
tunnelConfiguration.peers[index].excludeIPs = excludeIPs
|
||||||
}
|
}
|
||||||
tunnelConfiguration.peers[index].excludeIPs = excludeIPs
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
wg_log(.info, message: "Starting wireguard tunnel from the " +
|
wg_log(.info, message: "Starting wireguard tunnel from the " +
|
||||||
(activationAttemptId == nil ? "OS directly, rather than the app" : "app"))
|
(activationAttemptId == nil ? "OS directly, rather than the app" : "app"))
|
||||||
|
|
||||||
// Start the tunnel
|
// Start the tunnel
|
||||||
wgAdapter.start(tunnelConfiguration: tunnelConfiguration) { adapterError in
|
wgAdapter.start(tunnelConfiguration: tunnelConfiguration) { adapterError in
|
||||||
guard let adapterError else {
|
guard let adapterError else {
|
||||||
let interfaceName = self.wgAdapter.interfaceName ?? "unknown"
|
let interfaceName = self.wgAdapter.interfaceName ?? "unknown"
|
||||||
wg_log(.info, message: "Tunnel interface is \(interfaceName)")
|
wg_log(.info, message: "Tunnel interface is \(interfaceName)")
|
||||||
completionHandler(nil)
|
completionHandler(nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch adapterError {
|
switch adapterError {
|
||||||
case .cannotLocateTunnelFileDescriptor:
|
case .cannotLocateTunnelFileDescriptor:
|
||||||
wg_log(.error, staticMessage: "Starting tunnel failed: could not determine file descriptor")
|
wg_log(.error, staticMessage: "Starting tunnel failed: could not determine file descriptor")
|
||||||
errorNotifier.notify(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
|
errorNotifier.notify(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
|
||||||
completionHandler(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
|
completionHandler(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
|
||||||
case .dnsResolution(let dnsErrors):
|
case .dnsResolution(let dnsErrors):
|
||||||
let hostnamesWithDnsResolutionFailure = dnsErrors.map { $0.address }
|
let hostnamesWithDnsResolutionFailure = dnsErrors.map { $0.address }
|
||||||
.joined(separator: ", ")
|
.joined(separator: ", ")
|
||||||
wg_log(.error, message:
|
wg_log(.error, message:
|
||||||
"DNS resolution failed for the following hostnames: \(hostnamesWithDnsResolutionFailure)")
|
"DNS resolution failed for the following hostnames: \(hostnamesWithDnsResolutionFailure)")
|
||||||
errorNotifier.notify(PacketTunnelProviderError.dnsResolutionFailure)
|
errorNotifier.notify(PacketTunnelProviderError.dnsResolutionFailure)
|
||||||
completionHandler(PacketTunnelProviderError.dnsResolutionFailure)
|
completionHandler(PacketTunnelProviderError.dnsResolutionFailure)
|
||||||
case .setNetworkSettings(let error):
|
case .setNetworkSettings(let error):
|
||||||
wg_log(.error, message:
|
wg_log(.error, message:
|
||||||
"Starting tunnel failed with setTunnelNetworkSettings returning \(error.localizedDescription)")
|
"Starting tunnel failed with setTunnelNetworkSettings returning \(error.localizedDescription)")
|
||||||
errorNotifier.notify(PacketTunnelProviderError.couldNotSetNetworkSettings)
|
errorNotifier.notify(PacketTunnelProviderError.couldNotSetNetworkSettings)
|
||||||
completionHandler(PacketTunnelProviderError.couldNotSetNetworkSettings)
|
completionHandler(PacketTunnelProviderError.couldNotSetNetworkSettings)
|
||||||
case .startWireGuardBackend(let errorCode):
|
case .startWireGuardBackend(let errorCode):
|
||||||
wg_log(.error, message: "Starting tunnel failed with wgTurnOn returning \(errorCode)")
|
wg_log(.error, message: "Starting tunnel failed with wgTurnOn returning \(errorCode)")
|
||||||
errorNotifier.notify(PacketTunnelProviderError.couldNotStartBackend)
|
errorNotifier.notify(PacketTunnelProviderError.couldNotStartBackend)
|
||||||
completionHandler(PacketTunnelProviderError.couldNotStartBackend)
|
completionHandler(PacketTunnelProviderError.couldNotStartBackend)
|
||||||
case .invalidState:
|
case .invalidState:
|
||||||
fatalError()
|
fatalError()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch {
|
||||||
|
log(.error, message: "Can't parse WG config: \(error.localizedDescription)")
|
||||||
|
completionHandler(nil)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func startOpenVPN(completionHandler: @escaping (Error?) -> Void) {
|
private func startOpenVPN(completionHandler: @escaping (Error?) -> Void) {
|
||||||
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
||||||
let providerConfiguration = protocolConfiguration.providerConfiguration,
|
let providerConfiguration = protocolConfiguration.providerConfiguration,
|
||||||
let ovpnConfiguration: Data = providerConfiguration[Constants.ovpnConfigKey] as? Data else {
|
let openVPNConfigData = providerConfiguration[Constants.ovpnConfigKey] as? Data else {
|
||||||
|
|
||||||
wg_log(.error, message: "Can't start startOpenVPN()")
|
wg_log(.error, message: "Can't start startOpenVPN()")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setupAndlaunchOpenVPN(withConfig: ovpnConfiguration, completionHandler: completionHandler)
|
do {
|
||||||
|
log(.info, message: "providerConfiguration: \(String(decoding: openVPNConfigData, as: UTF8.self).replacingOccurrences(of: "\n", with: " "))")
|
||||||
|
|
||||||
|
let openVPNConfig = try JSONDecoder().decode(OpenVPNConfig.self, from: openVPNConfigData)
|
||||||
|
log(.info, message: "openVPNConfig: \(openVPNConfig.str.replacingOccurrences(of: "\n", with: " "))")
|
||||||
|
let ovpnConfiguration = Data(openVPNConfig.config.utf8)
|
||||||
|
setupAndlaunchOpenVPN(withConfig: ovpnConfiguration, completionHandler: completionHandler)
|
||||||
|
} catch {
|
||||||
|
log(.error, message: "Can't parse OpenVPN config: \(error.localizedDescription)")
|
||||||
|
|
||||||
|
if let underlyingError = (error as NSError).userInfo[NSUnderlyingErrorKey] as? NSError {
|
||||||
|
log(.error, message: "Can't parse OpenVPN config: \(underlyingError.localizedDescription)")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func stopWireguard(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
private func stopWireguard(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
||||||
|
|
|
@ -1,10 +1,41 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct WGConfigData: Decodable {
|
struct WGConfig: Decodable {
|
||||||
let initPacketMagicHeader, responsePacketMagicHeader: String?
|
let initPacketMagicHeader, responsePacketMagicHeader: String?
|
||||||
let underloadPacketMagicHeader, transportPacketMagicHeader: String?
|
let underloadPacketMagicHeader, transportPacketMagicHeader: String?
|
||||||
let junkPacketCount, junkPacketMinSize, junkPacketMaxSize: String?
|
let junkPacketCount, junkPacketMinSize, junkPacketMaxSize: String?
|
||||||
let initPacketJunkSize, responsePacketJunkSize: String?
|
let initPacketJunkSize, responsePacketJunkSize: String?
|
||||||
|
let dns1: String
|
||||||
|
let dns2: String
|
||||||
|
let hostName: String
|
||||||
|
let port: Int
|
||||||
|
let clientIP: String
|
||||||
|
let clientPrivateKey: String
|
||||||
|
let serverPublicKey: String
|
||||||
|
let presharedKey: String
|
||||||
|
var allowedIPs: [String]
|
||||||
|
var persistentKeepAlive: String
|
||||||
|
let splitTunnelType: Int
|
||||||
|
let splitTunnelSites: [String]
|
||||||
|
|
||||||
|
enum CodingKeys: String, CodingKey {
|
||||||
|
case initPacketMagicHeader = "H1", responsePacketMagicHeader = "H2"
|
||||||
|
case underloadPacketMagicHeader = "H3", transportPacketMagicHeader = "H4"
|
||||||
|
case junkPacketCount = "Jc", junkPacketMinSize = "Jmin", junkPacketMaxSize = "Jmax"
|
||||||
|
case initPacketJunkSize = "S1", responsePacketJunkSize = "S2"
|
||||||
|
case dns1
|
||||||
|
case dns2
|
||||||
|
case hostName
|
||||||
|
case port
|
||||||
|
case clientIP = "client_ip"
|
||||||
|
case clientPrivateKey = "client_priv_key"
|
||||||
|
case serverPublicKey = "server_pub_key"
|
||||||
|
case presharedKey = "psk_key"
|
||||||
|
case allowedIPs = "allowed_ips"
|
||||||
|
case persistentKeepAlive = "persistent_keep_alive"
|
||||||
|
case splitTunnelType
|
||||||
|
case splitTunnelSites
|
||||||
|
}
|
||||||
|
|
||||||
var settings: String {
|
var settings: String {
|
||||||
junkPacketCount == nil ? "" :
|
junkPacketCount == nil ? "" :
|
||||||
|
@ -22,114 +53,45 @@ struct WGConfigData: Decodable {
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
let clientIP: String
|
|
||||||
let clientPrivateKey: String
|
|
||||||
let clientPublicKey: String
|
|
||||||
let serverPublicKey: String
|
|
||||||
let presharedKey: String
|
|
||||||
let hostName: String
|
|
||||||
let port: Int
|
|
||||||
|
|
||||||
var allowedIPs: [String]
|
|
||||||
var persistentKeepAlive: String
|
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
|
||||||
case initPacketMagicHeader = "H1", responsePacketMagicHeader = "H2"
|
|
||||||
case underloadPacketMagicHeader = "H3", transportPacketMagicHeader = "H4"
|
|
||||||
case junkPacketCount = "Jc", junkPacketMinSize = "Jmin", junkPacketMaxSize = "Jmax"
|
|
||||||
case initPacketJunkSize = "S1", responsePacketJunkSize = "S2"
|
|
||||||
|
|
||||||
case clientIP = "client_ip" // "10.8.1.16"
|
|
||||||
case clientPrivateKey = "client_priv_key"
|
|
||||||
case clientPublicKey = "client_pub_key"
|
|
||||||
case serverPublicKey = "server_pub_key"
|
|
||||||
case presharedKey = "psk_key"
|
|
||||||
|
|
||||||
case allowedIPs = "allowed_ips"
|
|
||||||
case persistentKeepAlive = "persistent_keep_alive"
|
|
||||||
case hostName
|
|
||||||
case port
|
|
||||||
}
|
|
||||||
|
|
||||||
init(from decoder: Decoder) throws {
|
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
||||||
self.initPacketMagicHeader = try container.decodeIfPresent(String.self, forKey: .initPacketMagicHeader)
|
|
||||||
self.responsePacketMagicHeader = try container.decodeIfPresent(String.self, forKey: .responsePacketMagicHeader)
|
|
||||||
self.underloadPacketMagicHeader = try container.decodeIfPresent(String.self, forKey: .underloadPacketMagicHeader)
|
|
||||||
self.transportPacketMagicHeader = try container.decodeIfPresent(String.self, forKey: .transportPacketMagicHeader)
|
|
||||||
self.junkPacketCount = try container.decodeIfPresent(String.self, forKey: .junkPacketCount)
|
|
||||||
self.junkPacketMinSize = try container.decodeIfPresent(String.self, forKey: .junkPacketMinSize)
|
|
||||||
self.junkPacketMaxSize = try container.decodeIfPresent(String.self, forKey: .junkPacketMaxSize)
|
|
||||||
self.initPacketJunkSize = try container.decodeIfPresent(String.self, forKey: .initPacketJunkSize)
|
|
||||||
self.responsePacketJunkSize = try container.decodeIfPresent(String.self, forKey: .responsePacketJunkSize)
|
|
||||||
self.clientIP = try container.decode(String.self, forKey: .clientIP)
|
|
||||||
self.clientPrivateKey = try container.decode(String.self, forKey: .clientPrivateKey)
|
|
||||||
self.clientPublicKey = try container.decode(String.self, forKey: .clientPublicKey)
|
|
||||||
self.serverPublicKey = try container.decode(String.self, forKey: .serverPublicKey)
|
|
||||||
self.presharedKey = try container.decode(String.self, forKey: .presharedKey)
|
|
||||||
self.allowedIPs = try container.decodeIfPresent([String].self, forKey: .allowedIPs) ?? ["0.0.0.0/0", "::/0"]
|
|
||||||
self.persistentKeepAlive = try container.decodeIfPresent(String.self, forKey: .persistentKeepAlive) ?? "25"
|
|
||||||
self.hostName = try container.decode(String.self, forKey: .hostName)
|
|
||||||
self.port = try container.decode(Int.self, forKey: .port)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct WGConfig: Decodable {
|
|
||||||
let data: WGConfigData
|
|
||||||
let configVersion: Int
|
|
||||||
let description: String
|
|
||||||
let dns1: String
|
|
||||||
let dns2: String
|
|
||||||
let hostName: String
|
|
||||||
let `protocol`: String
|
|
||||||
let splitTunnelSites: [String]
|
|
||||||
let splitTunnelType: Int
|
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
|
||||||
case awgConfigData = "awg_config_data", wgConfigData = "wireguard_config_data"
|
|
||||||
case configData
|
|
||||||
case configVersion = "config_version"
|
|
||||||
case description
|
|
||||||
case dns1
|
|
||||||
case dns2
|
|
||||||
case hostName
|
|
||||||
case `protocol`
|
|
||||||
case splitTunnelSites
|
|
||||||
case splitTunnelType
|
|
||||||
}
|
|
||||||
|
|
||||||
init(from decoder: Decoder) throws {
|
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
||||||
|
|
||||||
if container.contains(.awgConfigData) {
|
|
||||||
self.data = try container.decode(WGConfigData.self, forKey: .awgConfigData)
|
|
||||||
} else {
|
|
||||||
self.data = try container.decode(WGConfigData.self, forKey: .wgConfigData)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.configVersion = try container.decode(Int.self, forKey: .configVersion)
|
|
||||||
self.description = try container.decode(String.self, forKey: .description)
|
|
||||||
self.dns1 = try container.decode(String.self, forKey: .dns1)
|
|
||||||
self.dns2 = try container.decode(String.self, forKey: .dns2)
|
|
||||||
self.hostName = try container.decode(String.self, forKey: .hostName)
|
|
||||||
self.protocol = try container.decode(String.self, forKey: .protocol)
|
|
||||||
self.splitTunnelSites = try container.decode([String].self, forKey: .splitTunnelSites)
|
|
||||||
self.splitTunnelType = try container.decode(Int.self, forKey: .splitTunnelType)
|
|
||||||
}
|
|
||||||
|
|
||||||
var str: String {
|
var str: String {
|
||||||
"""
|
"""
|
||||||
[Interface]
|
[Interface]
|
||||||
Address = \(data.clientIP)/32
|
Address = \(clientIP)
|
||||||
DNS = \(dns1), \(dns2)
|
DNS = \(dns1), \(dns2)
|
||||||
PrivateKey = \(data.clientPrivateKey)
|
PrivateKey = \(clientPrivateKey)
|
||||||
\(data.settings)
|
\(settings)
|
||||||
[Peer]
|
[Peer]
|
||||||
PublicKey = \(data.serverPublicKey)
|
PublicKey = \(serverPublicKey)
|
||||||
PresharedKey = \(data.presharedKey)
|
PresharedKey = \(presharedKey)
|
||||||
AllowedIPs = \(data.allowedIPs.joined(separator: ", "))
|
AllowedIPs = \(allowedIPs.joined(separator: ", "))
|
||||||
Endpoint = \(data.hostName):\(data.port)
|
Endpoint = \(hostName):\(port)
|
||||||
PersistentKeepalive = \(data.persistentKeepAlive)
|
PersistentKeepalive = \(persistentKeepAlive)
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
var redux: String {
|
||||||
|
"""
|
||||||
|
[Interface]
|
||||||
|
Address = \(clientIP)
|
||||||
|
DNS = \(dns1), \(dns2)
|
||||||
|
PrivateKey = ***
|
||||||
|
\(settings)
|
||||||
|
[Peer]
|
||||||
|
PublicKey = ***
|
||||||
|
PresharedKey = ***
|
||||||
|
AllowedIPs = \(allowedIPs.joined(separator: ", "))
|
||||||
|
Endpoint = \(hostName):\(port)
|
||||||
|
PersistentKeepalive = \(persistentKeepAlive)
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct OpenVPNConfig: Decodable {
|
||||||
|
let config: String
|
||||||
|
let splitTunnelType: Int
|
||||||
|
let splitTunnelSites: [String]
|
||||||
|
|
||||||
|
var str: String {
|
||||||
|
"splitTunnelType: \(splitTunnelType) splitTunnelSites: \(splitTunnelSites) config: \(config)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -235,7 +235,6 @@ void IosController::checkStatus()
|
||||||
m_rxBytes = rxBytes;
|
m_rxBytes = rxBytes;
|
||||||
m_txBytes = txBytes;
|
m_txBytes = txBytes;
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IosController::vpnStatusDidChange(void *pNotification)
|
void IosController::vpnStatusDidChange(void *pNotification)
|
||||||
|
@ -244,13 +243,13 @@ void IosController::vpnStatusDidChange(void *pNotification)
|
||||||
|
|
||||||
if (session /* && session == TunnelManager.session */ ) {
|
if (session /* && session == TunnelManager.session */ ) {
|
||||||
qDebug() << "IosController::vpnStatusDidChange" << iosStatusToState(session.status) << session;
|
qDebug() << "IosController::vpnStatusDidChange" << iosStatusToState(session.status) << session;
|
||||||
|
|
||||||
if (session.status == NEVPNStatusDisconnected) {
|
if (session.status == NEVPNStatusDisconnected) {
|
||||||
if (@available(iOS 16.0, *)) {
|
if (@available(iOS 16.0, *)) {
|
||||||
[session fetchLastDisconnectErrorWithCompletionHandler:^(NSError * _Nullable error) {
|
[session fetchLastDisconnectErrorWithCompletionHandler:^(NSError * _Nullable error) {
|
||||||
if (error != nil) {
|
if (error != nil) {
|
||||||
qDebug() << "Disconnect error" << error.domain << error.code << error.localizedDescription;
|
qDebug() << "Disconnect error" << error.domain << error.code << error.localizedDescription;
|
||||||
|
|
||||||
if ([error.domain isEqualToString:NEVPNConnectionErrorDomain]) {
|
if ([error.domain isEqualToString:NEVPNConnectionErrorDomain]) {
|
||||||
switch (error.code) {
|
switch (error.code) {
|
||||||
case NEVPNConnectionErrorOverslept:
|
case NEVPNConnectionErrorOverslept:
|
||||||
|
@ -315,11 +314,11 @@ void IosController::vpnStatusDidChange(void *pNotification)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NSError *underlyingError = error.userInfo[@"NSUnderlyingError"];
|
NSError *underlyingError = error.userInfo[@"NSUnderlyingError"];
|
||||||
if (underlyingError != nil) {
|
if (underlyingError != nil) {
|
||||||
qDebug() << "Disconnect underlying error" << underlyingError.domain << underlyingError.code << underlyingError.localizedDescription;
|
qDebug() << "Disconnect underlying error" << underlyingError.domain << underlyingError.code << underlyingError.localizedDescription;
|
||||||
|
|
||||||
if ([underlyingError.domain isEqualToString:@"NEAgentErrorDomain"]) {
|
if ([underlyingError.domain isEqualToString:@"NEAgentErrorDomain"]) {
|
||||||
switch (underlyingError.code) {
|
switch (underlyingError.code) {
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -342,7 +341,7 @@ void IosController::vpnStatusDidChange(void *pNotification)
|
||||||
qDebug() << "Disconnect error is unavailable on iOS < 16.0";
|
qDebug() << "Disconnect error is unavailable on iOS < 16.0";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emit connectionStateChanged(iosStatusToState(session.status));
|
emit connectionStateChanged(iosStatusToState(session.status));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -357,7 +356,22 @@ bool IosController::setupOpenVPN()
|
||||||
QJsonObject ovpn = m_rawConfig[ProtocolProps::key_proto_config_data(amnezia::Proto::OpenVpn)].toObject();
|
QJsonObject ovpn = m_rawConfig[ProtocolProps::key_proto_config_data(amnezia::Proto::OpenVpn)].toObject();
|
||||||
QString ovpnConfig = ovpn[config_key::config].toString();
|
QString ovpnConfig = ovpn[config_key::config].toString();
|
||||||
|
|
||||||
return startOpenVPN(ovpnConfig);
|
QJsonObject openVPNConfig {};
|
||||||
|
openVPNConfig.insert(config_key::config, ovpnConfig);
|
||||||
|
openVPNConfig.insert(config_key::splitTunnelType, m_rawConfig[config_key::splitTunnelType]);
|
||||||
|
|
||||||
|
QJsonArray splitTunnelSites = m_rawConfig[config_key::splitTunnelSites].toArray();
|
||||||
|
|
||||||
|
for(int index = 0; index < splitTunnelSites.count(); index++) {
|
||||||
|
splitTunnelSites[index] = splitTunnelSites[index].toString().remove(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
openVPNConfig.insert(config_key::splitTunnelSites, splitTunnelSites);
|
||||||
|
|
||||||
|
QJsonDocument openVPNConfigDoc(openVPNConfig);
|
||||||
|
QString openVPNConfigStr(openVPNConfigDoc.toJson(QJsonDocument::Compact));
|
||||||
|
|
||||||
|
return startOpenVPN(openVPNConfigStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IosController::setupCloak()
|
bool IosController::setupCloak()
|
||||||
|
@ -394,27 +408,123 @@ bool IosController::setupCloak()
|
||||||
ovpnConfig.append(cloakBase64);
|
ovpnConfig.append(cloakBase64);
|
||||||
ovpnConfig.append("\n</cloak>\n");
|
ovpnConfig.append("\n</cloak>\n");
|
||||||
|
|
||||||
return startOpenVPN(ovpnConfig);
|
QJsonObject openVPNConfig {};
|
||||||
|
openVPNConfig.insert(config_key::config, ovpnConfig);
|
||||||
|
openVPNConfig.insert(config_key::splitTunnelType, m_rawConfig[config_key::splitTunnelType]);
|
||||||
|
|
||||||
|
QJsonArray splitTunnelSites = m_rawConfig[config_key::splitTunnelSites].toArray();
|
||||||
|
|
||||||
|
for(int index = 0; index < splitTunnelSites.count(); index++) {
|
||||||
|
splitTunnelSites[index] = splitTunnelSites[index].toString().remove(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
openVPNConfig.insert(config_key::splitTunnelSites, splitTunnelSites);
|
||||||
|
|
||||||
|
QJsonDocument openVPNConfigDoc(openVPNConfig);
|
||||||
|
QString openVPNConfigStr(openVPNConfigDoc.toJson(QJsonDocument::Compact));
|
||||||
|
|
||||||
|
return startOpenVPN(openVPNConfigStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IosController::setupWireGuard()
|
bool IosController::setupWireGuard()
|
||||||
{
|
{
|
||||||
QJsonObject config = m_rawConfig[ProtocolProps::key_proto_config_data(amnezia::Proto::WireGuard)].toObject();
|
QJsonObject config = m_rawConfig[ProtocolProps::key_proto_config_data(amnezia::Proto::WireGuard)].toObject();
|
||||||
|
|
||||||
QJsonDocument doc(m_rawConfig);
|
|
||||||
QString wgConfig(doc.toJson(QJsonDocument::Compact));
|
|
||||||
|
|
||||||
return startWireGuard(wgConfig);
|
QJsonObject wgConfig {};
|
||||||
|
wgConfig.insert(config_key::dns1, m_rawConfig[config_key::dns1]);
|
||||||
|
wgConfig.insert(config_key::dns2, m_rawConfig[config_key::dns2]);
|
||||||
|
wgConfig.insert(config_key::hostName, config[config_key::hostName]);
|
||||||
|
wgConfig.insert(config_key::port, config[config_key::port]);
|
||||||
|
wgConfig.insert(config_key::client_ip, config[config_key::client_ip]);
|
||||||
|
wgConfig.insert(config_key::client_priv_key, config[config_key::client_priv_key]);
|
||||||
|
wgConfig.insert(config_key::server_pub_key, config[config_key::server_pub_key]);
|
||||||
|
wgConfig.insert(config_key::psk_key, config[config_key::psk_key]);
|
||||||
|
wgConfig.insert(config_key::splitTunnelType, m_rawConfig[config_key::splitTunnelType]);
|
||||||
|
|
||||||
|
QJsonArray splitTunnelSites = m_rawConfig[config_key::splitTunnelSites].toArray();
|
||||||
|
|
||||||
|
for(int index = 0; index < splitTunnelSites.count(); index++) {
|
||||||
|
splitTunnelSites[index] = splitTunnelSites[index].toString().remove(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
wgConfig.insert(config_key::splitTunnelSites, splitTunnelSites);
|
||||||
|
|
||||||
|
if (config.contains(config_key::allowed_ips)) {
|
||||||
|
QJsonArray allowed_ips;
|
||||||
|
QStringList allowed_ips_list = config[config_key::allowed_ips].toString().split(", ");
|
||||||
|
|
||||||
|
for(int index = 0; index < allowed_ips_list.length(); index++) {
|
||||||
|
allowed_ips.append(allowed_ips_list[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
wgConfig.insert(config_key::allowed_ips, allowed_ips);
|
||||||
|
} else {
|
||||||
|
QJsonArray allowed_ips { "0.0.0.0/0", "::/0" };
|
||||||
|
wgConfig.insert(config_key::allowed_ips, allowed_ips);
|
||||||
|
}
|
||||||
|
|
||||||
|
wgConfig.insert("persistent_keep_alive", "25");
|
||||||
|
|
||||||
|
QJsonDocument wgConfigDoc(wgConfig);
|
||||||
|
QString wgConfigDocStr(wgConfigDoc.toJson(QJsonDocument::Compact));
|
||||||
|
|
||||||
|
return startWireGuard(wgConfigDocStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IosController::setupAwg()
|
bool IosController::setupAwg()
|
||||||
{
|
{
|
||||||
QJsonObject config = m_rawConfig[ProtocolProps::key_proto_config_data(amnezia::Proto::Awg)].toObject();
|
QJsonObject config = m_rawConfig[ProtocolProps::key_proto_config_data(amnezia::Proto::Awg)].toObject();
|
||||||
|
|
||||||
QJsonDocument doc(m_rawConfig);
|
QJsonObject wgConfig {};
|
||||||
QString wgConfig(doc.toJson(QJsonDocument::Compact));
|
wgConfig.insert(config_key::dns1, m_rawConfig[config_key::dns1]);
|
||||||
|
wgConfig.insert(config_key::dns2, m_rawConfig[config_key::dns2]);
|
||||||
|
wgConfig.insert(config_key::hostName, config[config_key::hostName]);
|
||||||
|
wgConfig.insert(config_key::port, config[config_key::port]);
|
||||||
|
wgConfig.insert(config_key::client_ip, config[config_key::client_ip]);
|
||||||
|
wgConfig.insert(config_key::client_priv_key, config[config_key::client_priv_key]);
|
||||||
|
wgConfig.insert(config_key::server_pub_key, config[config_key::server_pub_key]);
|
||||||
|
wgConfig.insert(config_key::psk_key, config[config_key::psk_key]);
|
||||||
|
wgConfig.insert(config_key::splitTunnelType, m_rawConfig[config_key::splitTunnelType]);
|
||||||
|
|
||||||
return startWireGuard(wgConfig);
|
QJsonArray splitTunnelSites = m_rawConfig[config_key::splitTunnelSites].toArray();
|
||||||
|
|
||||||
|
for(int index = 0; index < splitTunnelSites.count(); index++) {
|
||||||
|
splitTunnelSites[index] = splitTunnelSites[index].toString().remove(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
wgConfig.insert(config_key::splitTunnelSites, splitTunnelSites);
|
||||||
|
|
||||||
|
if (config.contains(config_key::allowed_ips)) {
|
||||||
|
QJsonArray allowed_ips;
|
||||||
|
QStringList allowed_ips_list = config[config_key::allowed_ips].toString().split(", ");
|
||||||
|
|
||||||
|
for(int index = 0; index < allowed_ips_list.length(); index++) {
|
||||||
|
allowed_ips.append(allowed_ips_list[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
wgConfig.insert(config_key::allowed_ips, allowed_ips);
|
||||||
|
} else {
|
||||||
|
QJsonArray allowed_ips { "0.0.0.0/0", "::/0" };
|
||||||
|
wgConfig.insert(config_key::allowed_ips, allowed_ips);
|
||||||
|
}
|
||||||
|
|
||||||
|
wgConfig.insert("persistent_keep_alive", "25");
|
||||||
|
wgConfig.insert(config_key::initPacketMagicHeader, config[config_key::initPacketMagicHeader]);
|
||||||
|
wgConfig.insert(config_key::responsePacketMagicHeader, config[config_key::responsePacketMagicHeader]);
|
||||||
|
wgConfig.insert(config_key::underloadPacketMagicHeader, config[config_key::underloadPacketMagicHeader]);
|
||||||
|
wgConfig.insert(config_key::transportPacketMagicHeader, config[config_key::transportPacketMagicHeader]);
|
||||||
|
|
||||||
|
wgConfig.insert(config_key::initPacketJunkSize, config[config_key::initPacketJunkSize]);
|
||||||
|
wgConfig.insert(config_key::responsePacketJunkSize, config[config_key::responsePacketJunkSize]);
|
||||||
|
|
||||||
|
wgConfig.insert(config_key::junkPacketCount, config[config_key::junkPacketCount]);
|
||||||
|
wgConfig.insert(config_key::junkPacketMinSize, config[config_key::junkPacketMinSize]);
|
||||||
|
wgConfig.insert(config_key::junkPacketMaxSize, config[config_key::junkPacketMaxSize]);
|
||||||
|
|
||||||
|
QJsonDocument wgConfigDoc(wgConfig);
|
||||||
|
QString wgConfigDocStr(wgConfigDoc.toJson(QJsonDocument::Compact));
|
||||||
|
|
||||||
|
return startWireGuard(wgConfigDocStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IosController::startOpenVPN(const QString &config)
|
bool IosController::startOpenVPN(const QString &config)
|
||||||
|
@ -448,23 +558,17 @@ bool IosController::startWireGuard(const QString &config)
|
||||||
void IosController::startTunnel()
|
void IosController::startTunnel()
|
||||||
{
|
{
|
||||||
NSString *protocolName = @"Unknown";
|
NSString *protocolName = @"Unknown";
|
||||||
|
|
||||||
NETunnelProviderProtocol *tunnelProtocol = (NETunnelProviderProtocol *)m_currentTunnel.protocolConfiguration;
|
NETunnelProviderProtocol *tunnelProtocol = (NETunnelProviderProtocol *)m_currentTunnel.protocolConfiguration;
|
||||||
if (tunnelProtocol.providerConfiguration[@"wireguard"] != nil) {
|
if (tunnelProtocol.providerConfiguration[@"wireguard"] != nil) {
|
||||||
protocolName = @"WireGuard";
|
protocolName = @"WireGuard";
|
||||||
} else if (tunnelProtocol.providerConfiguration[@"ovpn"] != nil) {
|
} else if (tunnelProtocol.providerConfiguration[@"ovpn"] != nil) {
|
||||||
protocolName = @"OpenVPN";
|
protocolName = @"OpenVPN";
|
||||||
}
|
}
|
||||||
|
|
||||||
m_rxBytes = 0;
|
m_rxBytes = 0;
|
||||||
m_txBytes = 0;
|
m_txBytes = 0;
|
||||||
|
|
||||||
int STT = m_rawConfig["splitTunnelType"].toInt();
|
|
||||||
QJsonArray splitTunnelSites = m_rawConfig["splitTunnelSites"].toArray();
|
|
||||||
QJsonDocument doc;
|
|
||||||
doc.setArray(splitTunnelSites);
|
|
||||||
QString STS(doc.toJson());
|
|
||||||
|
|
||||||
[m_currentTunnel setEnabled:YES];
|
[m_currentTunnel setEnabled:YES];
|
||||||
|
|
||||||
[m_currentTunnel saveToPreferencesWithCompletionHandler:^(NSError *saveError) {
|
[m_currentTunnel saveToPreferencesWithCompletionHandler:^(NSError *saveError) {
|
||||||
|
@ -485,23 +589,6 @@ void IosController::startTunnel()
|
||||||
NSError *startError = nil;
|
NSError *startError = nil;
|
||||||
qDebug() << iosStatusToState(m_currentTunnel.connection.status);
|
qDebug() << iosStatusToState(m_currentTunnel.connection.status);
|
||||||
|
|
||||||
|
|
||||||
NSString *actionKey = [NSString stringWithUTF8String:MessageKey::action];
|
|
||||||
NSString *actionValue = [NSString stringWithUTF8String:Action::start];
|
|
||||||
NSString *tunnelIdKey = [NSString stringWithUTF8String:MessageKey::tunnelId];
|
|
||||||
NSString *tunnelIdValue = !m_tunnelId.isEmpty() ? m_tunnelId.toNSString() : @"";
|
|
||||||
NSString *SplitTunnelTypeKey = [NSString stringWithUTF8String:MessageKey::SplitTunnelType];
|
|
||||||
NSString *SplitTunnelTypeValue = [NSString stringWithFormat:@"%d",STT];
|
|
||||||
NSString *SplitTunnelSitesKey = [NSString stringWithUTF8String:MessageKey::SplitTunnelSites];
|
|
||||||
NSString *SplitTunnelSitesValue = STS.toNSString();
|
|
||||||
|
|
||||||
|
|
||||||
NSDictionary* message = @{actionKey: actionValue, tunnelIdKey: tunnelIdValue,
|
|
||||||
SplitTunnelTypeKey: SplitTunnelTypeValue, SplitTunnelSitesKey: SplitTunnelSitesValue};
|
|
||||||
|
|
||||||
sendVpnExtensionMessage(message);
|
|
||||||
|
|
||||||
|
|
||||||
BOOL started = [m_currentTunnel.connection startVPNTunnelWithOptions:nil andReturnError:&startError];
|
BOOL started = [m_currentTunnel.connection startVPNTunnelWithOptions:nil andReturnError:&startError];
|
||||||
|
|
||||||
if (!started || startError) {
|
if (!started || startError) {
|
||||||
|
@ -516,7 +603,6 @@ void IosController::startTunnel()
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool IosController::isOurManager(NETunnelProviderManager* manager) {
|
bool IosController::isOurManager(NETunnelProviderManager* manager) {
|
||||||
NETunnelProviderProtocol* tunnelProto = (NETunnelProviderProtocol*)manager.protocolConfiguration;
|
NETunnelProviderProtocol* tunnelProto = (NETunnelProviderProtocol*)manager.protocolConfiguration;
|
||||||
|
|
||||||
|
@ -578,7 +664,7 @@ void IosController::sendVpnExtensionMessage(NSDictionary* message, std::function
|
||||||
NETunnelProviderSession *session = (NETunnelProviderSession *)m_currentTunnel.connection;
|
NETunnelProviderSession *session = (NETunnelProviderSession *)m_currentTunnel.connection;
|
||||||
|
|
||||||
NSError *sendError = nil;
|
NSError *sendError = nil;
|
||||||
|
|
||||||
if ([session respondsToSelector:@selector(sendProviderMessage:returnError:responseHandler:)]) {
|
if ([session respondsToSelector:@selector(sendProviderMessage:returnError:responseHandler:)]) {
|
||||||
[session sendProviderMessage:data returnError:&sendError responseHandler:completionHandler];
|
[session sendProviderMessage:data returnError:&sendError responseHandler:completionHandler];
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue