Refactoring/ios (#300)
iOS app refactoring (native part): - connection bugs fixed - improved stability - logs from network extension
This commit is contained in:
parent
3ce1e40b43
commit
ece15c7394
32 changed files with 935 additions and 3191 deletions
|
@ -3,7 +3,7 @@ import NetworkExtension
|
|||
import os
|
||||
import Darwin
|
||||
import OpenVPNAdapter
|
||||
//import Tun2socks
|
||||
|
||||
enum TunnelProtoType: String {
|
||||
case wireguard, openvpn, shadowsocks, none
|
||||
}
|
||||
|
@ -11,25 +11,19 @@ enum TunnelProtoType: String {
|
|||
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 wireGuardConfigKey = "wireguard"
|
||||
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 kActionStatus = "status"
|
||||
static let kActionIsServerReachable = "isServerReachable"
|
||||
static let kMessageKeyAction = "action"
|
||||
static let kMessageKeyTunnelid = "tunnelId"
|
||||
static let kMessageKeyTunnelId = "tunnelId"
|
||||
static let kMessageKeyConfig = "config"
|
||||
static let kMessageKeyErrorCode = "errorCode"
|
||||
static let kMessageKeyHost = "host"
|
||||
|
@ -37,8 +31,6 @@ struct Constants {
|
|||
static let kMessageKeyOnDemand = "is-on-demand"
|
||||
}
|
||||
|
||||
typealias ShadowsocksProxyCompletion = ((Int32, NSError?) -> Void)?
|
||||
|
||||
class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
|
||||
private lazy var wgAdapter: WireGuardAdapter = {
|
||||
|
@ -46,90 +38,128 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
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
|
||||
/// Internal queue.
|
||||
private let dispatchQueue = DispatchQueue(label: "PacketTunnel", qos: .utility)
|
||||
|
||||
// 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?
|
||||
|
||||
private var openVPNConfig: Data? = nil
|
||||
|
||||
let vpnReachability = OpenVPNReachability()
|
||||
|
||||
var startHandler: ((Error?) -> Void)?
|
||||
var stopHandler: (() -> Void)?
|
||||
var protoType: TunnelProtoType = .wireguard
|
||||
var protoType: TunnelProtoType = .none
|
||||
|
||||
override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
||||
let activationAttemptId = options?[Constants.kActivationAttemptId] as? String
|
||||
let errorNotifier = ErrorNotifier(activationAttemptId: activationAttemptId)
|
||||
|
||||
override init() {
|
||||
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
|
||||
Logger.global?.log(message: "Init NEPacketTunnelProvider")
|
||||
super.init()
|
||||
}
|
||||
|
||||
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||
guard let message = try? JSONSerialization.jsonObject(with: messageData, options: []) as? [String: Any] else {
|
||||
Logger.global?.log(message: "Failed to serialize message from app")
|
||||
return
|
||||
}
|
||||
|
||||
guard let completionHandler = completionHandler else {
|
||||
Logger.global?.log(message: "Missing message completion handler")
|
||||
return
|
||||
}
|
||||
|
||||
guard let action = message[Constants.kMessageKeyAction] as? String else {
|
||||
Logger.global?.log(message: "Missing action key in app message")
|
||||
completionHandler(nil)
|
||||
return
|
||||
}
|
||||
|
||||
if action == Constants.kActionStatus {
|
||||
handleStatusAppMessage(messageData, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
switch protoType {
|
||||
case .wireguard:
|
||||
startWireguard(activationAttemptId: activationAttemptId,
|
||||
errorNotifier: errorNotifier,
|
||||
completionHandler: completionHandler)
|
||||
case .openvpn:
|
||||
startOpenVPN(completionHandler: completionHandler)
|
||||
case .shadowsocks:
|
||||
break
|
||||
// startShadowSocks(completionHandler: completionHandler)
|
||||
case .none:
|
||||
break
|
||||
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) {
|
||||
dispatchQueue.async {
|
||||
let activationAttemptId = options?[Constants.kActivationAttemptId] as? String
|
||||
let errorNotifier = ErrorNotifier(activationAttemptId: activationAttemptId)
|
||||
|
||||
Logger.global?.log(message: "PacketTunnelProvider startTunnel")
|
||||
|
||||
if let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol {
|
||||
let providerConfiguration = protocolConfiguration.providerConfiguration
|
||||
if let _: Data = providerConfiguration?[Constants.ovpnConfigKey] as? Data {
|
||||
self.protoType = .openvpn
|
||||
}
|
||||
else if let _: Data = providerConfiguration?[Constants.wireGuardConfigKey] as? Data {
|
||||
self.protoType = .wireguard
|
||||
}
|
||||
}
|
||||
else {
|
||||
self.protoType = .none
|
||||
}
|
||||
|
||||
switch self.protoType {
|
||||
case .wireguard:
|
||||
self.startWireguard(activationAttemptId: activationAttemptId,
|
||||
errorNotifier: errorNotifier,
|
||||
completionHandler: completionHandler)
|
||||
case .openvpn:
|
||||
self.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:
|
||||
stopOpenVPN(with: reason, completionHandler: completionHandler)
|
||||
case .shadowsocks:
|
||||
break
|
||||
// stopShadowSocks(with: reason, completionHandler: completionHandler)
|
||||
case .none:
|
||||
break
|
||||
dispatchQueue.async {
|
||||
|
||||
switch self.protoType {
|
||||
case .wireguard:
|
||||
self.stopWireguard(with: reason, completionHandler: completionHandler)
|
||||
case .openvpn:
|
||||
self.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) {
|
||||
func handleStatusAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||
switch protoType {
|
||||
case .wireguard:
|
||||
handleWireguardAppMessage(messageData, completionHandler: completionHandler)
|
||||
handleWireguardStatusMessage(messageData, completionHandler: completionHandler)
|
||||
case .openvpn:
|
||||
handleOpenVPNAppMessage(messageData, completionHandler: completionHandler)
|
||||
handleOpenVPNStatusMessage(messageData, completionHandler: completionHandler)
|
||||
case .shadowsocks:
|
||||
break
|
||||
// handleShadowSocksAppMessage(messageData, completionHandler: completionHandler)
|
||||
case .none:
|
||||
break
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,48 +167,56 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
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
|
||||
}
|
||||
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
||||
let providerConfiguration = protocolConfiguration.providerConfiguration,
|
||||
let wgConfig: Data = providerConfiguration[Constants.wireGuardConfigKey] as? Data else {
|
||||
wg_log(.error, message: "Can't start WireGuard config missing")
|
||||
completionHandler(nil)
|
||||
return
|
||||
}
|
||||
|
||||
let wgConfigStr = String(data: wgConfig, encoding: .utf8)!
|
||||
|
||||
guard let tunnelConfiguration = try? TunnelConfiguration(fromWgQuickConfig: wgConfigStr) else {
|
||||
wg_log(.error, message: "Can't parse WireGuard config")
|
||||
completionHandler(nil)
|
||||
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()
|
||||
|
@ -197,35 +235,18 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
|
||||
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
|
||||
|
@ -242,16 +263,34 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
}
|
||||
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)
|
||||
|
||||
func handleWireguardStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||
guard let completionHandler = completionHandler else { return }
|
||||
wgAdapter.getRuntimeConfiguration { settings in
|
||||
var data: Data?
|
||||
if let settings = settings {
|
||||
data = settings.data(using: .utf8)!
|
||||
}
|
||||
|
||||
let components = settings!.components(separatedBy: "\n")
|
||||
|
||||
var settingsDictionary: [String: String] = [:]
|
||||
for component in components{
|
||||
let pair = component.components(separatedBy: "=")
|
||||
if pair.count == 2 {
|
||||
settingsDictionary[pair[0]] = pair[1]
|
||||
}
|
||||
}
|
||||
|
||||
let response: [String: Any] = [
|
||||
"rx_bytes" : settingsDictionary["rx_bytes"] ?? "0",
|
||||
"tx_bytes" : settingsDictionary["tx_bytes"] ?? "0"
|
||||
]
|
||||
|
||||
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
private func handleWireguardAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||
guard let completionHandler = completionHandler else { return }
|
||||
if messageData.count == 1 && messageData[0] == 0 {
|
||||
|
@ -270,7 +309,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
completionHandler(nil)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
do {
|
||||
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: configString)
|
||||
wgAdapter.update(tunnelConfiguration: tunnelConfiguration) { error in
|
||||
|
@ -279,7 +318,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
completionHandler(nil)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
self.wgAdapter.getRuntimeConfiguration { settings in
|
||||
var data: Data?
|
||||
if let settings = settings {
|
||||
|
@ -295,399 +334,25 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
completionHandler(nil)
|
||||
}
|
||||
}
|
||||
|
||||
private func handleOpenVPNAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||
|
||||
private func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||
guard let completionHandler = completionHandler else { return }
|
||||
if messageData.count == 1 && messageData[0] == 0 {
|
||||
let bytesin = ovpnAdapter.transportStatistics.bytesIn
|
||||
let strBytesin = "rx_bytes=" + String(bytesin);
|
||||
|
||||
let bytesout = ovpnAdapter.transportStatistics.bytesOut
|
||||
let strBytesout = "tx_bytes=" + String(bytesout);
|
||||
|
||||
let response: [String: Any] = [
|
||||
"rx_bytes" : bytesin,
|
||||
"tx_bytes" : bytesout
|
||||
]
|
||||
|
||||
let strData = strBytesin + "\n" + strBytesout;
|
||||
let data = Data(strData.utf8)
|
||||
completionHandler(data)
|
||||
}
|
||||
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
|
||||
}
|
||||
|
||||
/*
|
||||
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 assign 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 processed 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 processed")
|
||||
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()
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
// TODO review
|
||||
private func setupAndlaunchOpenVPN(withConfig ovpnConfiguration: Data, withShadowSocks viaSS: Bool = false, completionHandler: @escaping (Error?) -> Void) {
|
||||
wg_log(.info, message: "setupAndlaunchOpenVPN")
|
||||
|
||||
let str = String(decoding: ovpnConfiguration, as: UTF8.self)
|
||||
|
||||
let configuration = OpenVPNConfiguration()
|
||||
|
@ -699,53 +364,33 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
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)
|
||||
|
@ -779,23 +424,43 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
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
|
||||
private func startEmptyTunnel(completionHandler: @escaping (Error?) -> Void) {
|
||||
dispatchPrecondition(condition: .onQueue(dispatchQueue))
|
||||
|
||||
let emptyTunnelConfiguration = TunnelConfiguration(
|
||||
name: nil,
|
||||
interface: InterfaceConfiguration(privateKey: PrivateKey()),
|
||||
peers: []
|
||||
)
|
||||
|
||||
wgAdapter.start(tunnelConfiguration: emptyTunnelConfiguration) { error in
|
||||
self.dispatchQueue.async {
|
||||
if let error {
|
||||
Logger.global?.log(message: "Failed to start an empty tunnel")
|
||||
completionHandler(error)
|
||||
} else {
|
||||
Logger.global?.log(message: "Started an empty tunnel")
|
||||
self.tunnelAdapterDidStart()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let settings = NETunnelNetworkSettings(tunnelRemoteAddress: "1.1.1.1")
|
||||
|
||||
self.setTunnelNetworkSettings(settings) { error in
|
||||
completionHandler(error)
|
||||
}
|
||||
}
|
||||
|
||||
private func tunnelAdapterDidStart() {
|
||||
dispatchPrecondition(condition: .onQueue(dispatchQueue))
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
extension NEPacketTunnelFlow: OpenVPNAdapterPacketFlow {}
|
||||
|
||||
/* extension NEPacketTunnelFlow: ShadowSocksAdapterPacketFlow {} */
|
||||
|
||||
extension PacketTunnelProvider: OpenVPNAdapterDelegate {
|
||||
|
||||
// OpenVPNAdapter calls this delegate method to configure a VPN tunnel.
|
||||
|
@ -873,14 +538,14 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate {
|
|||
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)])
|
||||
|
||||
extension WireGuardLogLevel {
|
||||
var osLogLevel: OSLogType {
|
||||
switch self {
|
||||
case .verbose:
|
||||
return .debug
|
||||
case .error:
|
||||
return .error
|
||||
}
|
||||
}
|
||||
|
||||
func close() throws {}
|
||||
}
|
||||
*/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue