
Botan cmake changes, cmake APPLE/IOS fixes, wireguard added, Currently removed openvpn implementation, once wireguard is stable then will add openVPN framework. Current progress is it is generating xcode project using mkdir build-ios /Users/shahzainali/Qt/6.4.1/ios/bin/qt-cmake . -B build-ios -GXcode Need to select team only for Network extension only. select AmneizaVPN to run. current issue is related to Botan.
892 lines
39 KiB
Swift
892 lines
39 KiB
Swift
import Foundation
|
|
import NetworkExtension
|
|
import os
|
|
import Darwin
|
|
//import OpenVPNAdapter
|
|
//import Tun2socks
|
|
|
|
enum TunnelProtoType: String {
|
|
case wireguard, openvpn, shadowsocks, none
|
|
}
|
|
|
|
struct Constants {
|
|
static let kDefaultPathKey = "defaultPath"
|
|
static let processQueueName = "org.amnezia.process-packets"
|
|
static let ssQueueName = "org.amnezia.shadowsocks"
|
|
static let kActivationAttemptId = "activationAttemptId"
|
|
static let ovpnConfigKey = "ovpn"
|
|
static let ssConfigKey = "ss"
|
|
static let loggerTag = "NET"
|
|
static let ssRemoteHost = "server"
|
|
static let ssRemotePort = "server_port"
|
|
static let ssLocalAddressKey = "local_addr"
|
|
static let ssLocalPortKey = "local_port"
|
|
static let ssTimeoutKey = "timeout"
|
|
static let ssCipherKey = "method"
|
|
static let ssPasswordKey = "password"
|
|
static let kActionStart = "start"
|
|
static let kActionRestart = "restart"
|
|
static let kActionStop = "stop"
|
|
static let kActionGetTunnelId = "getTunnelId"
|
|
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"
|
|
}
|
|
|
|
typealias ShadowsocksProxyCompletion = ((Int32, NSError?) -> Void)?
|
|
|
|
class PacketTunnelProvider: NEPacketTunnelProvider {
|
|
|
|
private lazy var wgAdapter: WireGuardAdapter = {
|
|
return WireGuardAdapter(with: self) { logLevel, message in
|
|
wg_log(logLevel.osLogLevel, message: message)
|
|
}
|
|
}()
|
|
|
|
// private lazy var ovpnAdapter: OpenVPNAdapter = {
|
|
// let adapter = OpenVPNAdapter()
|
|
// adapter.delegate = self
|
|
// return adapter
|
|
// }()
|
|
|
|
private var shadowSocksConfig: Data? = nil
|
|
private var openVPNConfig: Data? = nil
|
|
var ssCompletion: ShadowsocksProxyCompletion = nil
|
|
|
|
// private var ssProvider: ShadowSocksTunnel? = nil
|
|
// private var ssLocalPort: Int = 8585
|
|
// private var ssRemoteHost = ""
|
|
// private var leafProvider: TunProvider? = nil
|
|
//
|
|
// private var tun2socksTunnel: Tun2socksOutlineTunnelProtocol? = nil
|
|
// private var tun2socksWriter: Tun2socksTunWriter? = nil
|
|
// private let processQueue = DispatchQueue(label: Constants.processQueueName)
|
|
// private var connection: NWTCPConnection? = nil
|
|
// private var session: NWUDPSession? = nil
|
|
// private var observer: AnyObject?
|
|
|
|
// let vpnReachability = OpenVPNReachability()
|
|
|
|
var startHandler: ((Error?) -> Void)?
|
|
var stopHandler: (() -> Void)?
|
|
var protoType: TunnelProtoType = .wireguard
|
|
|
|
override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
|
let activationAttemptId = options?[Constants.kActivationAttemptId] as? String
|
|
let errorNotifier = ErrorNotifier(activationAttemptId: activationAttemptId)
|
|
|
|
Logger.configureGlobal(tagged: Constants.loggerTag, withFilePath: FileManager.logFileURL?.path)
|
|
|
|
if let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
|
let providerConfiguration = protocolConfiguration.providerConfiguration,
|
|
let _: Data = providerConfiguration[Constants.ovpnConfigKey] as? Data {
|
|
let withoutShadowSocks = providerConfiguration[Constants.ssConfigKey] as? Data == nil
|
|
protoType = withoutShadowSocks ? .openvpn : .shadowsocks
|
|
} else {
|
|
protoType = .wireguard
|
|
}
|
|
|
|
switch protoType {
|
|
case .wireguard:
|
|
startWireguard(activationAttemptId: activationAttemptId,
|
|
errorNotifier: errorNotifier,
|
|
completionHandler: completionHandler)
|
|
case .openvpn:
|
|
break
|
|
//startOpenVPN(completionHandler: completionHandler)
|
|
case .shadowsocks:
|
|
break
|
|
// startShadowSocks(completionHandler: completionHandler)
|
|
case .none:
|
|
break
|
|
}
|
|
}
|
|
|
|
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
|
switch protoType {
|
|
case .wireguard:
|
|
stopWireguard(with: reason, completionHandler: completionHandler)
|
|
case .openvpn:
|
|
break
|
|
// stopOpenVPN(with: reason, completionHandler: completionHandler)
|
|
case .shadowsocks:
|
|
break
|
|
// stopShadowSocks(with: reason, completionHandler: completionHandler)
|
|
case .none:
|
|
break
|
|
}
|
|
}
|
|
|
|
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
|
switch protoType {
|
|
case .wireguard:
|
|
handleWireguardAppMessage(messageData, completionHandler: completionHandler)
|
|
case .openvpn:
|
|
handleWireguardAppMessage(messageData, completionHandler: completionHandler)
|
|
case .shadowsocks:
|
|
break
|
|
// handleShadowSocksAppMessage(messageData, completionHandler: completionHandler)
|
|
case .none:
|
|
break
|
|
}
|
|
}
|
|
|
|
// MARK: Private methods
|
|
private func startWireguard(activationAttemptId: String?,
|
|
errorNotifier: ErrorNotifier,
|
|
completionHandler: @escaping (Error?) -> Void) {
|
|
guard let tunnelProviderProtocol = self.protocolConfiguration as? NETunnelProviderProtocol,
|
|
let tunnelConfiguration = tunnelProviderProtocol.asTunnelConfiguration() else {
|
|
errorNotifier.notify(PacketTunnelProviderError.savedProtocolConfigurationIsInvalid)
|
|
completionHandler(PacketTunnelProviderError.savedProtocolConfigurationIsInvalid)
|
|
return
|
|
}
|
|
wg_log(.info, message: "Starting wireguard tunnel from the " + (activationAttemptId == nil ? "OS directly, rather than the app" : "app"))
|
|
|
|
// Start the tunnel
|
|
wgAdapter.start(tunnelConfiguration: tunnelConfiguration) { adapterError in
|
|
guard let adapterError = adapterError else {
|
|
let interfaceName = self.wgAdapter.interfaceName ?? "unknown"
|
|
|
|
wg_log(.info, message: "Tunnel interface is \(interfaceName)")
|
|
|
|
completionHandler(nil)
|
|
return
|
|
}
|
|
|
|
switch adapterError {
|
|
case .cannotLocateTunnelFileDescriptor:
|
|
wg_log(.error, staticMessage: "Starting tunnel failed: could not determine file descriptor")
|
|
errorNotifier.notify(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
|
|
completionHandler(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
|
|
|
|
case .dnsResolution(let dnsErrors):
|
|
let hostnamesWithDnsResolutionFailure = dnsErrors.map { $0.address }
|
|
.joined(separator: ", ")
|
|
wg_log(.error, message: "DNS resolution failed for the following hostnames: \(hostnamesWithDnsResolutionFailure)")
|
|
errorNotifier.notify(PacketTunnelProviderError.dnsResolutionFailure)
|
|
completionHandler(PacketTunnelProviderError.dnsResolutionFailure)
|
|
|
|
case .setNetworkSettings(let error):
|
|
wg_log(.error, message: "Starting tunnel failed with setTunnelNetworkSettings returning \(error.localizedDescription)")
|
|
errorNotifier.notify(PacketTunnelProviderError.couldNotSetNetworkSettings)
|
|
completionHandler(PacketTunnelProviderError.couldNotSetNetworkSettings)
|
|
|
|
case .startWireGuardBackend(let errorCode):
|
|
wg_log(.error, message: "Starting tunnel failed with wgTurnOn returning \(errorCode)")
|
|
errorNotifier.notify(PacketTunnelProviderError.couldNotStartBackend)
|
|
completionHandler(PacketTunnelProviderError.couldNotStartBackend)
|
|
|
|
case .invalidState:
|
|
// Must never happen
|
|
fatalError()
|
|
}
|
|
}
|
|
}
|
|
|
|
// private func startOpenVPN(completionHandler: @escaping (Error?) -> Void) {
|
|
// guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
|
// let providerConfiguration = protocolConfiguration.providerConfiguration,
|
|
// let ovpnConfiguration: Data = providerConfiguration[Constants.ovpnConfigKey] as? Data else {
|
|
// // TODO: handle errors properly
|
|
// wg_log(.error, message: "Can't start startOpenVPN()")
|
|
// return
|
|
// }
|
|
//
|
|
// setupAndlaunchOpenVPN(withConfig: ovpnConfiguration, completionHandler: completionHandler)
|
|
// }
|
|
/*
|
|
private func startShadowSocks(completionHandler: @escaping (Error?) -> Void) {
|
|
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
|
let providerConfiguration = protocolConfiguration.providerConfiguration,
|
|
let ssConfiguration: Data = providerConfiguration[Constants.ssConfigKey] as? Data,
|
|
let ovpnConfiguration: Data = providerConfiguration[Constants.ovpnConfigKey] as? Data else {
|
|
// TODO: handle errors properly
|
|
wg_log(.error, message: "Cannot start startShadowSocks()")
|
|
return
|
|
}
|
|
self.shadowSocksConfig = ssConfiguration
|
|
self.openVPNConfig = ovpnConfiguration
|
|
wg_log(.info, message: "Prepare to start shadowsocks/tun2socks/leaf")
|
|
// self.startSSProvider(completion: completionHandler)
|
|
// startTun2SocksTunnel(completion: completionHandler)
|
|
self.startLeafRedirector(completion: completionHandler)
|
|
}
|
|
*/
|
|
private func stopWireguard(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
|
wg_log(.info, staticMessage: "Stopping tunnel")
|
|
|
|
wgAdapter.stop { error in
|
|
ErrorNotifier.removeLastErrorFile()
|
|
|
|
if let error = error {
|
|
wg_log(.error, message: "Failed to stop WireGuard adapter: \(error.localizedDescription)")
|
|
}
|
|
completionHandler()
|
|
|
|
#if os(macOS)
|
|
// HACK: This is a filthy hack to work around Apple bug 32073323 (dup'd by us as 47526107).
|
|
// Remove it when they finally fix this upstream and the fix has been rolled out to
|
|
// sufficient quantities of users.
|
|
exit(0)
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// private func stopOpenVPN(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
|
// stopHandler = completionHandler
|
|
// if vpnReachability.isTracking {
|
|
// vpnReachability.stopTracking()
|
|
// }
|
|
// ovpnAdapter.disconnect()
|
|
// }
|
|
/*
|
|
private func stopShadowSocks(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
|
stopOpenVPN(with: reason) { [weak self] in
|
|
guard let `self` = self else { return }
|
|
// self.stopSSProvider(completionHandler: completionHandler)
|
|
// self.stopTun2SocksTunnel(completionHandler: completionHandler)
|
|
self.stopLeafRedirector(completion: completionHandler)
|
|
}
|
|
}
|
|
*/
|
|
private func handleWireguardAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
|
guard let completionHandler = completionHandler else { return }
|
|
if messageData.count == 1 && messageData[0] == 0 {
|
|
wgAdapter.getRuntimeConfiguration { settings in
|
|
var data: Data?
|
|
if let settings = settings {
|
|
data = settings.data(using: .utf8)!
|
|
}
|
|
completionHandler(data)
|
|
}
|
|
} else if messageData.count >= 1 {
|
|
// Updates the tunnel configuration and responds with the active configuration
|
|
wg_log(.info, message: "Switching tunnel configuration")
|
|
guard let configString = String(data: messageData, encoding: .utf8)
|
|
else {
|
|
completionHandler(nil)
|
|
return
|
|
}
|
|
|
|
do {
|
|
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: configString)
|
|
wgAdapter.update(tunnelConfiguration: tunnelConfiguration) { error in
|
|
if let error = error {
|
|
wg_log(.error, message: "Failed to switch tunnel configuration: \(error.localizedDescription)")
|
|
completionHandler(nil)
|
|
return
|
|
}
|
|
|
|
self.wgAdapter.getRuntimeConfiguration { settings in
|
|
var data: Data?
|
|
if let settings = settings {
|
|
data = settings.data(using: .utf8)!
|
|
}
|
|
completionHandler(data)
|
|
}
|
|
}
|
|
} catch {
|
|
completionHandler(nil)
|
|
}
|
|
} else {
|
|
completionHandler(nil)
|
|
}
|
|
}
|
|
|
|
/*
|
|
private func handleShadowSocksAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
|
guard let completionHandler = completionHandler else { return }
|
|
if let configString = String(data: messageData, encoding: .utf8) {
|
|
wg_log(.debug, message: configString)
|
|
}
|
|
|
|
completionHandler(messageData)
|
|
}
|
|
*/
|
|
// MARK: -- Tun2sock provider methods
|
|
/*
|
|
private func startTun2SocksTunnel(completion: @escaping (Error?) -> Void) {
|
|
guard let ssConfiguration = self.shadowSocksConfig,
|
|
let ovpnConfiguration = self.openVPNConfig,
|
|
let ssConfig = try? JSONSerialization.jsonObject(with: ssConfiguration, options: []) as? [String: Any]
|
|
else {
|
|
wg_log(.info, message: "Cannot parse shadowsocks config")
|
|
let tun2socksError: NSError = .init(domain: "", code: 100, userInfo: nil)
|
|
completion(tun2socksError)
|
|
return
|
|
}
|
|
wg_log(.info, message: "SS Config: \(ssConfig)")
|
|
|
|
guard let remoteHost = ssConfig[Constants.ssRemoteHost] as? String,
|
|
let remotePort = ssConfig[Constants.ssRemotePort] as? Int,
|
|
let method = ssConfig[Constants.ssCipherKey] as? String,
|
|
let password = ssConfig[Constants.ssPasswordKey] as? String else {
|
|
wg_log(.error, message: "Cannot parse ss config")
|
|
let tun2socksError: NSError = .init(domain: "", code: 100, userInfo: nil)
|
|
completion(tun2socksError)
|
|
return
|
|
}
|
|
|
|
let connError: AutoreleasingUnsafeMutablePointer<NSError?>? = nil
|
|
ShadowsocksCheckConnectivity(remoteHost, remotePort, password, method, nil, connError)
|
|
if (connError?.pointee != nil) {
|
|
wg_log(.error, message: "Failed to start tun2socks tunnel with error: \(connError?.pointee?.localizedDescription ?? "oops")")
|
|
let tun2socksError: NSError = .init(domain: "", code: 100, userInfo: nil)
|
|
completion(tun2socksError)
|
|
return
|
|
}
|
|
setupAndlaunchOpenVPN(withConfig: ovpnConfiguration, withShadowSocks: true) { vpnError in
|
|
guard vpnError == nil else {
|
|
wg_log(.error, message: "Failed to start openvpn with tun2socks tunnel with error: \(vpnError?.localizedDescription ?? "oops")")
|
|
let tun2socksError: NSError = .init(domain: "", code: 100, userInfo: nil)
|
|
completion(tun2socksError)
|
|
return
|
|
}
|
|
// let ipv4settings: NEIPv4Settings = .init(addresses: ["192.0.2.1"], subnetMasks: ["255.255.255.0"])
|
|
// ipv4settings.includedRoutes = [.default()]
|
|
// ipv4settings.excludedRoutes = []
|
|
//
|
|
// let dnsSettings: NEDNSSettings = .init(servers: ["1.1.1.1", "9.9.9.9", "208.67.222.222", "208.67.220.220"])
|
|
// let settings: NEPacketTunnelNetworkSettings = .init(tunnelRemoteAddress: "192.0.2.2")
|
|
// settings.ipv4Settings = ipv4settings
|
|
// settings.dnsSettings = dnsSettings
|
|
// settings.mtu = 1600
|
|
//
|
|
// setTunnelNetworkSettings(settings) { tunError in
|
|
let ifaces = Interface.allInterfaces()
|
|
.filter { $0.family == .ipv4 }
|
|
.map { iface in iface.name }
|
|
|
|
wg_log(.error, message: "Available TUN Interfaces: \(ifaces)")
|
|
self.tun2socksWriter = Tun2socksTunWriter()
|
|
let tunError: AutoreleasingUnsafeMutablePointer<NSError?>? = nil
|
|
self.tun2socksTunnel = Tun2socksConnectShadowsocksTunnel(self.tun2socksWriter, remoteHost, remotePort, password, method, false, tunError)
|
|
if (tunError?.pointee != nil) {
|
|
wg_log(.error, message: "Failed to start tun2socks tunnel with error: \(tunError?.pointee?.localizedDescription ?? "oops")")
|
|
let tun2socksError: NSError = .init(domain: "", code: 100, userInfo: nil)
|
|
completion(tun2socksError)
|
|
return
|
|
}
|
|
self.processQueue.async { self.processPackets() }
|
|
completion(nil)
|
|
}
|
|
}
|
|
|
|
private func stopTun2SocksTunnel(completionHandler: @escaping () -> Void) {
|
|
if self.tun2socksTunnel != nil && self.tun2socksTunnel!.isConnected() {
|
|
self.tun2socksTunnel?.disconnect()
|
|
}
|
|
try? self.tun2socksWriter?.close()
|
|
completionHandler()
|
|
}
|
|
|
|
private func processPackets() {
|
|
packetFlow.readPacketObjects { [weak self] packets in
|
|
guard let `self` = self else { return }
|
|
do {
|
|
let _ = try packets.map {
|
|
var bytesWritten: Int = 0
|
|
try self.tun2socksTunnel?.write($0.data, ret0_: &bytesWritten)
|
|
self.processQueue.async {
|
|
self.processPackets()
|
|
}
|
|
}
|
|
} catch (let err) {
|
|
wg_log(.debug, message: "Error in tun2sock: \(err.localizedDescription)")
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: -- Leaf provider methods
|
|
|
|
private func prepareConfig(onInterface iface: String, fromSSConfig ssConfig: Data, andOvpnConfig ovpnConfig: Data) -> UnsafePointer<CChar>? {
|
|
guard let ssConfig = try? JSONSerialization.jsonObject(with: ssConfig, options: []) as? [String: Any] else {
|
|
self.ssCompletion?(0, NSError(domain: Bundle.main.bundleIdentifier ?? "unknown",
|
|
code: 100,
|
|
userInfo: [NSLocalizedDescriptionKey: "Cannot parse json for ss in tunnel"]))
|
|
return nil
|
|
}
|
|
|
|
guard let remoteHost = ssConfig[Constants.ssRemoteHost] as? String,
|
|
let remotePort = ssConfig[Constants.ssRemotePort] as? Int,
|
|
let method = ssConfig[Constants.ssCipherKey] as? String,
|
|
let password = ssConfig[Constants.ssPasswordKey] as? String else {
|
|
self.ssCompletion?(0, NSError(domain: Bundle.main.bundleIdentifier ?? "unknown",
|
|
code: 100,
|
|
userInfo: [NSLocalizedDescriptionKey: "Cannot asign profile params for ss in tunnel"]))
|
|
return nil
|
|
}
|
|
|
|
var insettings: [String: Any] = .init()
|
|
insettings["name"] = iface
|
|
insettings["address"] = "127.0.0.2"
|
|
insettings["netmask"] = "255.255.255.0"
|
|
insettings["gateway"] = "127.0.0.1"
|
|
insettings["mtu"] = 1600
|
|
|
|
var inbounds: [String: Any] = .init()
|
|
inbounds["protocol"] = "tun"
|
|
inbounds["settings"] = insettings
|
|
inbounds["tag"] = "tun_in"
|
|
|
|
var outbounds: [String: Any] = .init()
|
|
var outsettings: [String: Any] = .init()
|
|
outsettings["address"] = remoteHost
|
|
outsettings["port"] = remotePort
|
|
outsettings["method"] = method
|
|
outsettings["password"] = password
|
|
outbounds["protocol"] = "shadowsocks"
|
|
outbounds["settings"] = outsettings
|
|
outbounds["tag"] = "shadowsocks_out"
|
|
|
|
var params: [String: Any] = .init()
|
|
params["inbounds"] = [inbounds]
|
|
params["outbounds"] = [outbounds]
|
|
|
|
wg_log(.error, message: "Config dictionary: \(params)")
|
|
|
|
guard let jsonData = try? JSONSerialization.data(withJSONObject: params, options: .prettyPrinted),
|
|
let jsonString = String(data: jsonData, encoding: .utf8) else { return nil }
|
|
|
|
wg_log(.error, message: "JSON String: \(jsonString)")
|
|
|
|
var path = ""
|
|
if let documentDirectory = FileManager.default.urls(for: .documentDirectory,
|
|
in: .userDomainMask).first {
|
|
let pathWithFilename = documentDirectory.appendingPathComponent("config.json")
|
|
do {
|
|
try jsonString.write(to: pathWithFilename,
|
|
atomically: true,
|
|
encoding: .utf8)
|
|
path = pathWithFilename.path
|
|
} catch {
|
|
// Handle error
|
|
}
|
|
}
|
|
|
|
return UnsafePointer(strdup(path))
|
|
}
|
|
|
|
private func startLeafRedirector(completion: @escaping (Error?) -> Void) {
|
|
let ipv4settings: NEIPv4Settings = .init(addresses: ["127.0.0.2"], subnetMasks: ["255.255.255.0"])
|
|
ipv4settings.includedRoutes = [.default()]
|
|
ipv4settings.excludedRoutes = []
|
|
|
|
let dnsSettings: NEDNSSettings = .init(servers: ["1.1.1.1", "9.9.9.9", "208.67.222.222", "208.67.220.220"])
|
|
dnsSettings.matchDomains = []
|
|
let settings: NEPacketTunnelNetworkSettings = .init(tunnelRemoteAddress: "127.0.0.1")
|
|
settings.ipv4Settings = ipv4settings
|
|
settings.dnsSettings = dnsSettings
|
|
settings.mtu = 1600
|
|
|
|
self.setTunnelNetworkSettings(settings) { tunError in
|
|
let ifaces = Interface.allInterfaces()
|
|
.filter { $0.name.contains("tun") && $0.family == .ipv4 }
|
|
.map { iface in iface.name }
|
|
wg_log(.error, message: "Try on interface: \(ifaces)")
|
|
guard let ssConf = self.shadowSocksConfig,
|
|
let ovpnConf = self.openVPNConfig,
|
|
let config = self.prepareConfig(onInterface: ifaces.first ?? "utun2",
|
|
fromSSConfig: ssConf,
|
|
andOvpnConfig: ovpnConf) else {
|
|
let ret: NSError = .init(domain: "", code: 100, userInfo: nil)
|
|
completion(ret)
|
|
return
|
|
}
|
|
self.leafProvider = TunProvider(withConfig: config)
|
|
self.leafProvider?.testConfig(onPath: config) { configError in
|
|
wg_log(.error, message: "Config check status: \(configError!.desc)")
|
|
guard configError! == .noError else {
|
|
wg_log(.error, message: "Config check status: \(configError!.desc)")
|
|
let ret: NSError = .init(domain: "", code: 100, userInfo: nil)
|
|
completion(ret)
|
|
return
|
|
}
|
|
|
|
wg_log(.error, message: "Available TUN Interfaces: \(ifaces)")
|
|
|
|
self.leafProvider?.startTunnel { tunError in
|
|
wg_log(.error, message: "Leaf tunnel start status: \(tunError!.desc)")
|
|
guard tunError! == .noError else {
|
|
wg_log(.error, message: "Leaf tunnel start error: \(tunError!.desc)")
|
|
let ret: NSError = .init(domain: "", code: 100, userInfo: nil)
|
|
completion(ret)
|
|
return
|
|
}
|
|
completion(nil)
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
private func stopLeafRedirector(completion: @escaping () -> Void) {
|
|
leafProvider?.stopTunnel { error in
|
|
// TODO: handle errors
|
|
completion()
|
|
}
|
|
}
|
|
|
|
// MARK: -- ShadowSocks Provider methods
|
|
|
|
private func startSSProvider(completion: @escaping (Error?) -> Void) {
|
|
guard let ssConfig = self.shadowSocksConfig, let ovpnConfig = self.openVPNConfig else { return }
|
|
if ssProvider == nil {
|
|
guard let config = try? JSONSerialization.jsonObject(with: ssConfig, options: []) as? [String: Any],
|
|
let remoteHost = config[Constants.ssRemoteHost] as? String,
|
|
let port = config[Constants.ssLocalPortKey] as? Int else {
|
|
self.ssCompletion?(0, NSError(domain: Bundle.main.bundleIdentifier ?? "unknown",
|
|
code: 100,
|
|
userInfo: [NSLocalizedDescriptionKey: "Cannot parse json for ss in tunnel"]))
|
|
return
|
|
}
|
|
ssProvider = SSProvider(config: ssConfig, localPort: port)
|
|
ssLocalPort = port
|
|
ssRemoteHost = remoteHost
|
|
}
|
|
|
|
ssProvider?.start(usingPacketFlow: packetFlow, withConnectivityCheck: false) { errorCode in
|
|
wg_log(.info, message: "After starting shadowsocks")
|
|
wg_log(.error, message: "Starting ShadowSocks State: \(String(describing: errorCode))")
|
|
if (errorCode != nil && errorCode! != .noError) {
|
|
wg_log(.error, message: "Error starting ShadowSocks: \(String(describing: errorCode))")
|
|
return
|
|
}
|
|
|
|
// self.setupAndHandleOpenVPNOverSSConnection(withConfig: ovpnConfig)
|
|
self.startAndHandleTunnelOverSS(completionHandler: completion)
|
|
}
|
|
}
|
|
|
|
private func startAndHandleTunnelOverSS(completionHandler: @escaping (Error?) -> Void) {
|
|
// let ipv4settings: NEIPv4Settings = .init(addresses: ["192.0.2.2"], subnetMasks: ["255.255.255.0"])
|
|
// let addedRoute1 = NEIPv4Route(destinationAddress: "0.0.0.0", subnetMask: "0.0.0.0")
|
|
// addedRoute1.gatewayAddress = "192.0.2.1"
|
|
// ipv4settings.includedRoutes = [addedRoute1]
|
|
// ipv4settings.excludedRoutes = []
|
|
//
|
|
// let dnsSettings: NEDNSSettings = .init(servers: ["1.1.1.1", "9.9.9.9", "208.67.222.222", "208.67.220.220"])
|
|
// let settings: NEPacketTunnelNetworkSettings = .init(tunnelRemoteAddress: "192.0.2.1")
|
|
// settings.ipv4Settings = ipv4settings
|
|
// settings.dnsSettings = dnsSettings
|
|
// settings.mtu = 1600
|
|
//
|
|
// setTunnelNetworkSettings(settings) { tunError in
|
|
//
|
|
// }
|
|
|
|
let ifaces = Interface.allInterfaces()
|
|
.filter { $0.family == .ipv4 }
|
|
.map { iface in iface.name }
|
|
|
|
wg_log(.error, message: "Available TUN Interfaces: \(ifaces)")
|
|
let endpoint = NWHostEndpoint(hostname: "127.0.0.1", port: "\(self.ssLocalPort)")
|
|
self.session = self.createUDPSession(to: endpoint, from: nil)
|
|
self.setupWriteToFlow()
|
|
self.observer = self.session!.observe(\.state, options: [.new]) { conn, _ in
|
|
switch conn.state {
|
|
case .ready:
|
|
self.readFromFlow()
|
|
completionHandler(nil)
|
|
case .cancelled, .failed, .invalid:
|
|
self.stopSSProvider {
|
|
self.cancelTunnelWithError(nil)
|
|
completionHandler(nil)
|
|
}
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
private func setupAndHandleOpenVPNOverSSConnection(withConfig ovpnConfig: Data) {
|
|
let endpoint = NWHostEndpoint(hostname: "127.0.0.1", port: "\(self.ssLocalPort)")
|
|
self.session = self.createUDPSession(to: endpoint, from: nil)
|
|
// self.connection = self.createTCPConnection(to: endpoint, enableTLS: false, tlsParameters: nil, delegate: nil)
|
|
self.observer = self.session!.observe(\.state, options: [.new]) { conn, _ in
|
|
switch conn.state {
|
|
case .ready:
|
|
self.processQueue.async {
|
|
self.setupWriteToFlow()
|
|
}
|
|
self.processQueue.async {
|
|
self.readFromFlow()
|
|
}
|
|
|
|
self.setupAndlaunchOpenVPN(withConfig: ovpnConfig, withShadowSocks: true) { vpnError in
|
|
wg_log(.info, message: "After starting openVPN")
|
|
guard vpnError == nil else {
|
|
wg_log(.error, message: "Failed to start openvpn with error: \(vpnError?.localizedDescription ?? "oops")")
|
|
return
|
|
}
|
|
}
|
|
case .cancelled, .failed, .invalid:
|
|
self.stopSSProvider {
|
|
self.cancelTunnelWithError(nil)
|
|
}
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
private func readFromFlow() {
|
|
wg_log(.error, message: "Start reading packets to connection")
|
|
wg_log(.error, message: "Connection is \(session != nil ? "not null" : "null")")
|
|
packetFlow.readPackets { [weak self] packets, protocols in
|
|
wg_log(.error, message: "\(packets.count) outcoming packets proccessed of \(protocols.first?.stringValue ?? "unknown") type")
|
|
guard let `self` = self else { return }
|
|
self.session?.writeMultipleDatagrams(packets, completionHandler: { _ in
|
|
self.processQueue.async {
|
|
self.readFromFlow()
|
|
}
|
|
})
|
|
// let _ = packets.map {
|
|
// wg_log(.error, message: "Packet: \($0.data) of \($0.protocolFamily)")
|
|
// self.connection?.write($0.data, completionHandler: { _ in })
|
|
// self.processQueue.async {
|
|
// self.readFromFlow()
|
|
// }
|
|
// }
|
|
}
|
|
}
|
|
|
|
private func setupWriteToFlow() {
|
|
wg_log(.error, message: "Start writing packets from connection")
|
|
wg_log(.error, message: "Connection is \(session != nil ? "not null" : "null")")
|
|
session?.setReadHandler({ ssdata, error in
|
|
wg_log(.error, message: "Packets are \(ssdata != nil ? "not null" : "null"), error: \(error?.localizedDescription ?? "none")")
|
|
guard error == nil, let packets = ssdata else { return }
|
|
wg_log(.error, message: "\(packets.count) incoming packets proccessed")
|
|
self.packetFlow.writePackets(packets, withProtocols: [NSNumber(value: AF_INET)])
|
|
}, maxDatagrams: Int.max)
|
|
|
|
// connection?.readLength(1450, completionHandler: { [weak self] ssdata, readError in
|
|
// wg_log(.error, message: "Packets are \(ssdata != nil ? "not null" : "null")")
|
|
// guard let `self` = self, let packets = ssdata else { return }
|
|
// wg_log(.error, message: "Packet: \(packets) or error: \(readError?.localizedDescription ?? "")")
|
|
// self.packetFlow.writePackets([packets], withProtocols: [NSNumber(value: AF_INET)])
|
|
// self.processQueue.async {
|
|
// self.writeToFlow()
|
|
// }
|
|
// })
|
|
}
|
|
|
|
private func stopSSProvider(completionHandler: @escaping () -> Void) {
|
|
self.ssProvider?.stop { _ in
|
|
if let provider = self.ssProvider, let threadId = provider.ssLocalThreadId {
|
|
pthread_kill(threadId, SIGUSR1)
|
|
}
|
|
self.ssProvider = nil
|
|
completionHandler()
|
|
}
|
|
}
|
|
*/
|
|
// private func setupAndlaunchOpenVPN(withConfig ovpnConfiguration: Data, withShadowSocks viaSS: Bool = false, completionHandler: @escaping (Error?) -> Void) {
|
|
// wg_log(.info, message: "Inside setupAndlaunchOpenVPN()")
|
|
// let str = String(decoding: ovpnConfiguration, as: UTF8.self)
|
|
// wg_log(.info, message: "OPENVPN config: \(str)")
|
|
//
|
|
// let configuration = OpenVPNConfiguration()
|
|
// configuration.fileContent = ovpnConfiguration
|
|
// if viaSS {
|
|
//// configuration.settings = [
|
|
//// "remote": "137.74.6.148 1194",
|
|
//// "proto": "tcp",
|
|
//// "link-mtu": "1480",
|
|
//// "tun-mtu": "1460",
|
|
//// ]
|
|
// }
|
|
// let evaluation: OpenVPNConfigurationEvaluation
|
|
// do {
|
|
// evaluation = try ovpnAdapter.apply(configuration: configuration)
|
|
// } catch {
|
|
// completionHandler(error)
|
|
// return
|
|
// }
|
|
//
|
|
// if !evaluation.autologin {
|
|
// wg_log(.info, message: "Implement login with user credentials")
|
|
// }
|
|
//
|
|
// vpnReachability.startTracking { [weak self] status in
|
|
// guard status == .reachableViaWiFi else { return }
|
|
// self?.ovpnAdapter.reconnect(afterTimeInterval: 5)
|
|
// }
|
|
//
|
|
// startHandler = completionHandler
|
|
// ovpnAdapter.connect(using: packetFlow)
|
|
//
|
|
// let ifaces = Interface.allInterfaces()
|
|
// .filter { $0.family == .ipv4 }
|
|
// .map { iface in iface.name }
|
|
//
|
|
// wg_log(.error, message: "Available TUN Interfaces: \(ifaces)")
|
|
// }
|
|
|
|
// MARK: -- Network observing methods
|
|
|
|
private func getSockPort(for fd: Int32) -> Int32 {
|
|
var addr_in = sockaddr_in();
|
|
addr_in.sin_len = UInt8(MemoryLayout.size(ofValue: addr_in));
|
|
addr_in.sin_family = sa_family_t(AF_INET);
|
|
|
|
var len = socklen_t(addr_in.sin_len);
|
|
let result = withUnsafeMutablePointer(to: &addr_in, {
|
|
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
|
|
return Darwin.getsockname(fd, $0, &len);
|
|
}
|
|
});
|
|
|
|
if result == 0 {
|
|
return Int32(addr_in.sin_port);
|
|
} else {
|
|
wg_log(.error, message: "getSockPort(\(fd)) error: \(String(describing: strerror(errno)))")
|
|
return 0
|
|
}
|
|
}
|
|
|
|
private func startListeningForNetworkChanges() {
|
|
stopListeningForNetworkChanges()
|
|
addObserver(self, forKeyPath: Constants.kDefaultPathKey, options: .old, context: nil)
|
|
}
|
|
|
|
private func stopListeningForNetworkChanges() {
|
|
removeObserver(self, forKeyPath: Constants.kDefaultPathKey)
|
|
}
|
|
|
|
override func observeValue(forKeyPath keyPath: String?,
|
|
of object: Any?,
|
|
change: [NSKeyValueChangeKey : Any]?,
|
|
context: UnsafeMutableRawPointer?) {
|
|
guard Constants.kDefaultPathKey != keyPath else { return }
|
|
// Since iOS 11, we have observed that this KVO event fires repeatedly when connecting over Wifi,
|
|
// even though the underlying network has not changed (i.e. `isEqualToPath` returns false),
|
|
// leading to "wakeup crashes" due to excessive network activity. Guard against false positives by
|
|
// comparing the paths' string description, which includes properties not exposed by the class
|
|
guard let lastPath: NWPath = change?[.oldKey] as? NWPath,
|
|
let defPath = defaultPath,
|
|
lastPath != defPath || lastPath.description != defPath.description else {
|
|
return
|
|
}
|
|
DispatchQueue.main.async { [weak self] in
|
|
guard let `self` = self, self.defaultPath != nil else { return }
|
|
self.handle(networkChange: self.defaultPath!) { _ in }
|
|
}
|
|
}
|
|
|
|
private func handle(networkChange changePath: NWPath, completion: @escaping (Error?) -> Void) {
|
|
wg_log(.info, message: "Tunnel restarted.")
|
|
startTunnel(options: nil, completionHandler: completion)
|
|
}
|
|
}
|
|
|
|
extension WireGuardLogLevel {
|
|
var osLogLevel: OSLogType {
|
|
switch self {
|
|
case .verbose:
|
|
return .debug
|
|
case .error:
|
|
return .error
|
|
}
|
|
}
|
|
}
|
|
|
|
//extension NEPacketTunnelFlow: OpenVPNAdapterPacketFlow {}
|
|
|
|
/* extension NEPacketTunnelFlow: ShadowSocksAdapterPacketFlow {} */
|
|
|
|
//extension PacketTunnelProvider: OpenVPNAdapterDelegate {
|
|
//
|
|
// // OpenVPNAdapter calls this delegate method to configure a VPN tunnel.
|
|
// // `completionHandler` callback requires an object conforming to `OpenVPNAdapterPacketFlow`
|
|
// // protocol if the tunnel is configured without errors. Otherwise send nil.
|
|
// // `OpenVPNAdapterPacketFlow` method signatures are similar to `NEPacketTunnelFlow` so
|
|
// // you can just extend that class to adopt `OpenVPNAdapterPacketFlow` protocol and
|
|
// // send `self.packetFlow` to `completionHandler` callback.
|
|
// func openVPNAdapter(
|
|
// _ openVPNAdapter: OpenVPNAdapter,
|
|
// configureTunnelWithNetworkSettings networkSettings: NEPacketTunnelNetworkSettings?,
|
|
// completionHandler: @escaping (Error?) -> Void
|
|
// ) {
|
|
// // In order to direct all DNS queries first to the VPN DNS servers before the primary DNS servers
|
|
// // send empty string to NEDNSSettings.matchDomains
|
|
// networkSettings?.dnsSettings?.matchDomains = [""]
|
|
//
|
|
// // Set the network settings for the current tunneling session.
|
|
// setTunnelNetworkSettings(networkSettings, completionHandler: completionHandler)
|
|
// }
|
|
//
|
|
// // Process events returned by the OpenVPN library
|
|
// func openVPNAdapter(
|
|
// _ openVPNAdapter: OpenVPNAdapter,
|
|
// handleEvent event:
|
|
// OpenVPNAdapterEvent, message: String?
|
|
// ) {
|
|
// switch event {
|
|
// case .connected:
|
|
// if reasserting {
|
|
// reasserting = false
|
|
// }
|
|
//
|
|
// guard let startHandler = startHandler else { return }
|
|
//
|
|
// startHandler(nil)
|
|
// self.startHandler = nil
|
|
// case .disconnected:
|
|
// guard let stopHandler = stopHandler else { return }
|
|
//
|
|
// if vpnReachability.isTracking {
|
|
// vpnReachability.stopTracking()
|
|
// }
|
|
//
|
|
// stopHandler()
|
|
// self.stopHandler = nil
|
|
// case .reconnecting:
|
|
// reasserting = true
|
|
// default:
|
|
// break
|
|
// }
|
|
// }
|
|
//
|
|
// // Handle errors thrown by the OpenVPN library
|
|
// func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleError error: Error) {
|
|
// // Handle only fatal errors
|
|
// guard let fatal = (error as NSError).userInfo[OpenVPNAdapterErrorFatalKey] as? Bool,
|
|
// fatal == true else { return }
|
|
//
|
|
// if vpnReachability.isTracking {
|
|
// vpnReachability.stopTracking()
|
|
// }
|
|
//
|
|
// if let startHandler = startHandler {
|
|
// startHandler(error)
|
|
// self.startHandler = nil
|
|
// } else {
|
|
// cancelTunnelWithError(error)
|
|
// }
|
|
// }
|
|
//
|
|
// // Use this method to process any log message returned by OpenVPN library.
|
|
// func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleLogMessage logMessage: String) {
|
|
// // Handle log messages
|
|
// wg_log(.info, message: logMessage)
|
|
// }
|
|
//}
|
|
/*
|
|
extension PacketTunnelProvider: Tun2socksTunWriterProtocol {
|
|
func write(_ p0: Data?, n: UnsafeMutablePointer<Int>?) throws {
|
|
if let packets = p0 {
|
|
self.packetFlow.writePackets([packets], withProtocols: [NSNumber(value: AF_INET)])
|
|
}
|
|
}
|
|
|
|
func close() throws {}
|
|
}
|
|
*/
|