[WIP] shadowsocks implementation added
This commit is contained in:
parent
40996888c9
commit
6583090d4f
2259 changed files with 417734 additions and 92 deletions
|
|
@ -50,4 +50,5 @@ func wg_log(_ type: OSLogType, staticMessage msg: StaticString) {
|
|||
func wg_log(_ type: OSLogType, message msg: String) {
|
||||
os_log("%{public}s", log: OSLog.default, type: type, msg)
|
||||
Logger.global?.log(message: msg)
|
||||
NSLog("AMNEZIA: \(msg)")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,15 @@ import Foundation
|
|||
import NetworkExtension
|
||||
import os
|
||||
import OpenVPNAdapter
|
||||
import ShadowSocks
|
||||
//import Tun2Socks
|
||||
|
||||
enum TunnelProtoType: String {
|
||||
case wireguard, openvpn, none
|
||||
case wireguard, openvpn, shadowsocks, none
|
||||
}
|
||||
|
||||
typealias ShadowsocksProxyCompletion = ((Int32, NSError?) -> Void)?
|
||||
|
||||
class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
|
||||
private lazy var wgAdapter: WireGuardAdapter = {
|
||||
|
|
@ -24,6 +28,16 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
return adapter
|
||||
}()
|
||||
|
||||
private var shadowSocksPort: Int32 = 0
|
||||
private var isShadowsocksRunning: Bool = false
|
||||
var ssCompletion: ShadowsocksProxyCompletion = nil
|
||||
private let ssQueue = DispatchQueue(label: "org.amnezia.shadowsocks")
|
||||
private var shadowSocksConfig: Data? = nil
|
||||
|
||||
// private var tun2socksWriter: AmneziaTun2SocksWriter? = nil
|
||||
// private var tun2socksTunnel: Tun2socksOutlineTunnelProtocol? = nil
|
||||
// private let processQueue = DispatchQueue(label: "org.amnezia.process-packets")
|
||||
|
||||
let vpnReachability = OpenVPNReachability()
|
||||
|
||||
var startHandler: ((Error?) -> Void)?
|
||||
|
|
@ -39,7 +53,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
if let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
||||
let providerConfiguration = protocolConfiguration.providerConfiguration,
|
||||
let _: Data = providerConfiguration["ovpn"] as? Data {
|
||||
protoType = .openvpn
|
||||
let withoutShadowSocks = providerConfiguration["ss"] as? Data == nil
|
||||
protoType = withoutShadowSocks ? .openvpn : .shadowsocks
|
||||
} else {
|
||||
protoType = .wireguard
|
||||
}
|
||||
|
|
@ -51,6 +66,10 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
completionHandler: completionHandler)
|
||||
case .openvpn:
|
||||
startOpenVPN(completionHandler: completionHandler)
|
||||
case .shadowsocks:
|
||||
startShadowSocks { error in
|
||||
|
||||
}
|
||||
case .none:
|
||||
break
|
||||
}
|
||||
|
|
@ -62,6 +81,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
stopWireguard(with: reason, completionHandler: completionHandler)
|
||||
case .openvpn:
|
||||
stopOpenVPN(with: reason, completionHandler: completionHandler)
|
||||
case .shadowsocks:
|
||||
stopShadowSocks(with: reason, completionHandler: completionHandler)
|
||||
case .none:
|
||||
break
|
||||
}
|
||||
|
|
@ -169,32 +190,62 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
||||
let providerConfiguration = protocolConfiguration.providerConfiguration,
|
||||
let ovpnConfiguration: Data = providerConfiguration["ovpn"] as? Data else {
|
||||
// TODO: handle errors properly
|
||||
wg_log(.error, message: "Can't start startOpenVPN()")
|
||||
return
|
||||
}
|
||||
|
||||
let configuration = OpenVPNConfiguration()
|
||||
configuration.fileContent = ovpnConfiguration
|
||||
// configuration.settings = [] // Additional setting if needed any
|
||||
// configuration.tunPersist = true // keep tun active during pauses/reconections
|
||||
let evaluation: OpenVPNConfigurationEvaluation
|
||||
do {
|
||||
evaluation = try ovpnAdapter.apply(configuration: configuration)
|
||||
} catch {
|
||||
completionHandler(error)
|
||||
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["ss"] as? Data,
|
||||
let ovpnConfiguration: Data = providerConfiguration["ovpn"] as? Data else {
|
||||
// TODO: handle errors properly
|
||||
wg_log(.error, message: "Cannot start startShadowSocks()")
|
||||
return
|
||||
}
|
||||
// self.shadowSocksConfig = ssConfiguration
|
||||
//
|
||||
// guard let config = self.shadowSocksConfig else { return }
|
||||
// guard let ssConfig = try? JSONSerialization.jsonObject(with: config, 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
|
||||
// }
|
||||
|
||||
if !evaluation.autologin {
|
||||
print("Implement login with user credentials")
|
||||
}
|
||||
// let sshost = ssConfig["local_addr"] as? String
|
||||
// let ssport = ssConfig["local_port"] as? Int ?? Int(self.shadowSocksPort)
|
||||
//
|
||||
// let method = ssConfig["method"] as? String
|
||||
// let password = ssConfig["password"] as? String
|
||||
|
||||
vpnReachability.startTracking { [weak self] status in
|
||||
guard status == .reachableViaWiFi else { return }
|
||||
self?.ovpnAdapter.reconnect(afterTimeInterval: 5)
|
||||
}
|
||||
|
||||
startHandler = completionHandler
|
||||
ovpnAdapter.connect(using: packetFlow)
|
||||
// Thread.detachNewThread { [weak self] in
|
||||
setupAndLaunchShadowSocksProxy(withConfig: ssConfiguration, ssHandler: { [weak self] port, error in
|
||||
wg_log(.info,
|
||||
message: "Prepare to start openvpn, self is \(self == nil ? "null" : "not null")")
|
||||
guard error == nil else {
|
||||
wg_log(.error, message: "Stopping tunnel: \(error?.localizedDescription ?? "none")")
|
||||
completionHandler(error!)
|
||||
return
|
||||
}
|
||||
|
||||
self?.setupAndlaunchOpenVPN(withConfig: ovpnConfiguration) { error in
|
||||
guard error == nil else {
|
||||
wg_log(.error, message: "Start OpenVPN tunnel error : \(error?.localizedDescription ?? "none")")
|
||||
completionHandler(error!)
|
||||
return
|
||||
}
|
||||
wg_log(.error, message: "OpenVPN tunnel connected.")
|
||||
}
|
||||
|
||||
// self?.startTun2Socks(host: sshost, port: ssport, password: password, cipher: method, isUDPEnabled: false, error: nil)
|
||||
})
|
||||
// }
|
||||
}
|
||||
|
||||
private func stopWireguard(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
||||
|
|
@ -224,6 +275,186 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
}
|
||||
ovpnAdapter.disconnect()
|
||||
}
|
||||
|
||||
private func stopShadowSocks(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
||||
stopOpenVPN(with: reason) {
|
||||
// if self.tun2socksTunnel != nil && self.tun2socksTunnel!.isConnected() {
|
||||
// self.tun2socksTunnel?.disconnect()
|
||||
// try? self.tun2socksWriter?.close()
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
// private func startTun2Socks(host: String?, port: Int, password: String?, cipher: String?, isUDPEnabled: Bool, error: NSErrorPointer) {
|
||||
// let isOn = self.tun2socksTunnel != nil && self.tun2socksTunnel!.isConnected()
|
||||
// if isOn { tun2socksTunnel?.disconnect() }
|
||||
// self.tun2socksWriter = AmneziaTun2SocksWriter(tunnelFlow: self.packetFlow)
|
||||
// self.tun2socksTunnel = Tun2socksConnectShadowsocksTunnel(self.tun2socksWriter, host, port, password, cipher, isUDPEnabled, error)
|
||||
// if (!isOn) {
|
||||
// self.processQueue.sync { self.processPackets() }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private func processPackets() {
|
||||
// wg_log(.info, message: "Inside startTun2SocksPacketForwarder")
|
||||
// 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.sync {
|
||||
// self.processPackets()
|
||||
// }
|
||||
// }
|
||||
// } catch (let err) {
|
||||
// wg_log(.debug, message: "Error in tun2sock: \(err.localizedDescription)")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
private func setupAndlaunchOpenVPN(withConfig ovpnConfiguration: Data, 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
|
||||
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)
|
||||
}
|
||||
|
||||
private func setupAndLaunchShadowSocksProxy(withConfig config: Data, ssHandler: ShadowsocksProxyCompletion) {
|
||||
let str = String(decoding: config, as: UTF8.self)
|
||||
wg_log(.info, message: "config: \(str)")
|
||||
ssCompletion = ssHandler
|
||||
guard let ssConfig = try? JSONSerialization.jsonObject(with: config, options: []) as? [String: Any] else {
|
||||
ssHandler?(0, NSError(domain: Bundle.main.bundleIdentifier ?? "unknown",
|
||||
code: 100,
|
||||
userInfo: [NSLocalizedDescriptionKey: "Cannot parse json for ss in tunnel"]))
|
||||
return
|
||||
}
|
||||
|
||||
wg_log(.info, message: "SS Config: \(ssConfig)")
|
||||
|
||||
guard let remoteHost = ssConfig["server"] as? String, // UnsafeMutablePointer<CChar>,
|
||||
let remotePort = ssConfig["server_port"] as? Int32,
|
||||
let localAddress = ssConfig["local_addr"] as? String, //UnsafeMutablePointer<CChar>,
|
||||
let localPort = ssConfig["local_port"] as? Int32,
|
||||
let method = ssConfig["method"] as? String, //UnsafeMutablePointer<CChar>,
|
||||
let password = ssConfig["password"] as? String,//UnsafeMutablePointer<CChar>,
|
||||
let timeout = ssConfig["timeout"] as? Int32
|
||||
else {
|
||||
ssHandler?(0, NSError(domain: Bundle.main.bundleIdentifier ?? "unknown",
|
||||
code: 100,
|
||||
userInfo: [NSLocalizedDescriptionKey: "Cannot assing profile params for ss in tunnel"]))
|
||||
return
|
||||
}
|
||||
|
||||
/* An example profile
|
||||
*
|
||||
* const profile_t EXAMPLE_PROFILE = {
|
||||
* .remote_host = "example.com",
|
||||
* .local_addr = "127.0.0.1",
|
||||
* .method = "bf-cfb",
|
||||
* .password = "barfoo!",
|
||||
* .remote_port = 8338,
|
||||
* .local_port = 1080,
|
||||
* .timeout = 600;
|
||||
* .acl = NULL,
|
||||
* .log = NULL,
|
||||
* .fast_open = 0,
|
||||
* .mode = 0,
|
||||
* .verbose = 0
|
||||
* };
|
||||
*/
|
||||
|
||||
var profile: profile_t = .init()
|
||||
memset(&profile, 0, MemoryLayout<profile_t>.size)
|
||||
profile.remote_host = strdup(remoteHost)
|
||||
profile.remote_port = remotePort
|
||||
profile.local_addr = strdup(localAddress)
|
||||
profile.local_port = localPort
|
||||
profile.method = strdup(method)
|
||||
profile.password = strdup(password)
|
||||
profile.timeout = timeout
|
||||
profile.acl = nil
|
||||
profile.log = nil
|
||||
profile.mtu = 1600
|
||||
profile.fast_open = 1
|
||||
profile.mode = 0
|
||||
profile.verbose = 1
|
||||
|
||||
wg_log(.debug, message: "Prepare to start shadowsocks proxy server...")
|
||||
let observer = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())
|
||||
ssQueue.sync { [weak self] in
|
||||
let success = start_ss_local_server_with_callback(profile, { socks_fd, udp_fd, data in
|
||||
wg_log(.debug, message: "Inside cb callback")
|
||||
wg_log(.debug, message: "Params: socks_fd -> \(socks_fd), udp_fd -> \(udp_fd)")
|
||||
if let obs = data {
|
||||
wg_log(.debug, message: "Prepare to call onShadowsocksCallback() with socks port \(socks_fd) and udp port \(udp_fd)")
|
||||
let mySelf = Unmanaged<PacketTunnelProvider>.fromOpaque(obs).takeUnretainedValue()
|
||||
mySelf.onShadowsocksCallback(fd: socks_fd)
|
||||
}
|
||||
}, observer)
|
||||
if success != -1 {
|
||||
wg_log(.error, message: "ss proxy started on port \(localPort)")
|
||||
self?.shadowSocksPort = localPort
|
||||
self?.isShadowsocksRunning = true
|
||||
} else {
|
||||
wg_log(.error, message: "Failed to start ss proxy")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func onShadowsocksCallback(fd: Int32) {
|
||||
wg_log(.debug, message: "Inside onShadowsocksCallback() with port \(fd)")
|
||||
var error: NSError? = nil
|
||||
if fd > 0 {
|
||||
// shadowSocksPort = getSockPort(for: fd)
|
||||
isShadowsocksRunning = true
|
||||
} else {
|
||||
error = NSError(domain: Bundle.main.bundleIdentifier ?? "unknown", code: 100, userInfo: [NSLocalizedDescriptionKey : "Failed to start shadowsocks proxy"])
|
||||
}
|
||||
ssCompletion?(shadowSocksPort, error)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension WireGuardLogLevel {
|
||||
|
|
@ -276,7 +507,6 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate {
|
|||
|
||||
startHandler(nil)
|
||||
self.startHandler = nil
|
||||
|
||||
case .disconnected:
|
||||
guard let stopHandler = stopHandler else { return }
|
||||
|
||||
|
|
@ -286,10 +516,8 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate {
|
|||
|
||||
stopHandler()
|
||||
self.stopHandler = nil
|
||||
|
||||
case .reconnecting:
|
||||
reasserting = true
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
|
@ -316,7 +544,23 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate {
|
|||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//class AmneziaTun2SocksWriter: Tun2socksTunWriter {
|
||||
// private var tunnelFlow: NEPacketTunnelFlow
|
||||
//
|
||||
// init(tunnelFlow: NEPacketTunnelFlow) {
|
||||
// self.tunnelFlow = tunnelFlow
|
||||
// super.init()
|
||||
// }
|
||||
//
|
||||
// override func write(_ p0: Data?, n: UnsafeMutablePointer<Int>?) throws {
|
||||
// if let packets = p0 {
|
||||
// tunnelFlow.writePackets([packets], withProtocols: [NSNumber(value: AF_INET)])
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override func close() throws {}
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ public class IOSVpnProtocolImpl : NSObject {
|
|||
private var privateKey : PrivateKey? = nil
|
||||
private var deviceIpv4Address: String? = nil
|
||||
private var deviceIpv6Address: String? = nil
|
||||
private var openVPNConfig: String? = nil
|
||||
private var shadowSocksConfig: String? = nil
|
||||
|
||||
@objc enum ConnectionState: Int { case Error, Connected, Disconnected }
|
||||
|
||||
|
|
@ -45,6 +47,8 @@ public class IOSVpnProtocolImpl : NSObject {
|
|||
precondition(!vpnBundleID.isEmpty)
|
||||
|
||||
stateChangeCallback = callback
|
||||
self.openVPNConfig = config
|
||||
self.shadowSocksConfig = nil
|
||||
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
NotificationCenter.default.addObserver(self,
|
||||
|
|
@ -117,6 +121,8 @@ public class IOSVpnProtocolImpl : NSObject {
|
|||
self.privateKey = PrivateKey(rawValue: privateKey)
|
||||
self.deviceIpv4Address = deviceIpv4Address
|
||||
self.deviceIpv6Address = deviceIpv6Address
|
||||
self.openVPNConfig = nil
|
||||
self.shadowSocksConfig = nil
|
||||
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
|
||||
|
|
@ -170,6 +176,63 @@ public class IOSVpnProtocolImpl : NSObject {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc init(bundleID: String,
|
||||
tunnelConfig: String,
|
||||
ssConfig: String,
|
||||
closure: @escaping (ConnectionState, Date?) -> Void,
|
||||
callback: @escaping (Bool) -> Void) {
|
||||
super.init()
|
||||
|
||||
vpnBundleID = bundleID;
|
||||
precondition(!vpnBundleID.isEmpty)
|
||||
|
||||
stateChangeCallback = callback
|
||||
self.openVPNConfig = tunnelConfig
|
||||
self.shadowSocksConfig = ssConfig
|
||||
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.vpnStatusDidChange(notification:)), name: Notification.Name.NEVPNStatusDidChange, object: nil)
|
||||
|
||||
NETunnelProviderManager.loadAllFromPreferences { [weak self] managers, error in
|
||||
if let error = error {
|
||||
Logger.global?.log(message: "Loading from preference failed: \(error)")
|
||||
closure(ConnectionState.Error, nil)
|
||||
return
|
||||
}
|
||||
|
||||
if self == nil {
|
||||
Logger.global?.log(message: "We are shutting down.")
|
||||
return
|
||||
}
|
||||
|
||||
let nsManagers = managers ?? []
|
||||
Logger.global?.log(message: "We have received \(nsManagers.count) managers.")
|
||||
print("We have received \(nsManagers.count) managers.")
|
||||
|
||||
let tunnel = nsManagers.first(where: IOSVpnProtocolImpl.isOurManager(_:))
|
||||
|
||||
if tunnel == nil {
|
||||
Logger.global?.log(message: "Creating the tunnel via shadowsocks")
|
||||
print("Creating the tunnel via SS")
|
||||
self!.tunnel = NETunnelProviderManager()
|
||||
closure(ConnectionState.Disconnected, nil)
|
||||
return
|
||||
}
|
||||
|
||||
Logger.global?.log(message: "Tunnel already exists")
|
||||
print("SS Tunnel already exists")
|
||||
|
||||
self!.tunnel = tunnel
|
||||
|
||||
if tunnel?.connection.status == .connected {
|
||||
closure(ConnectionState.Connected, tunnel?.connection.connectedDate)
|
||||
} else {
|
||||
closure(ConnectionState.Disconnected, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func vpnStatusDidChange(notification: Notification) {
|
||||
guard let session = (notification.object as? NETunnelProviderSession), tunnel?.connection == session else { return }
|
||||
|
|
@ -230,6 +293,27 @@ public class IOSVpnProtocolImpl : NSObject {
|
|||
return true
|
||||
}
|
||||
|
||||
@objc func connect(ssConfig: String,
|
||||
ovpnConfig: String,
|
||||
failureCallback: @escaping () -> Void) {
|
||||
Logger.global?.log(message: "Connecting")
|
||||
assert(tunnel != nil)
|
||||
|
||||
self.openVPNConfig = ovpnConfig
|
||||
self.shadowSocksConfig = ssConfig
|
||||
|
||||
let addr: String = ovpnConfig
|
||||
.splitToArray(separator: "\n", trimmingCharacters: nil)
|
||||
.first { $0.starts(with: "remote ") }
|
||||
.splitToArray(separator: " ", trimmingCharacters: nil)[1]
|
||||
print("server: \(addr)")
|
||||
|
||||
// Let's remove the previous config if it exists.
|
||||
(tunnel?.protocolConfiguration as? NETunnelProviderProtocol)?.destroyConfigurationReference()
|
||||
|
||||
self.configureTunnel(withShadowSocks: self.shadowSocksConfig, serverAddress: addr, config: self.openVPNConfig, failureCallback: failureCallback)
|
||||
}
|
||||
|
||||
@objc func connect(ovpnConfig: String, failureCallback: @escaping () -> Void) {
|
||||
Logger.global?.log(message: "Connecting")
|
||||
assert(tunnel != nil)
|
||||
|
|
@ -299,6 +383,8 @@ public class IOSVpnProtocolImpl : NSObject {
|
|||
failureCallback()
|
||||
return
|
||||
}
|
||||
|
||||
guard tunnel != nil else { failureCallback(); return }
|
||||
proto.providerBundleIdentifier = vpnBundleID
|
||||
|
||||
tunnel!.protocolConfiguration = proto
|
||||
|
|
@ -351,6 +437,47 @@ public class IOSVpnProtocolImpl : NSObject {
|
|||
}
|
||||
}
|
||||
|
||||
func configureTunnel(withShadowSocks ssConfig: String?, serverAddress: String, config: String?, failureCallback: @escaping () -> Void) {
|
||||
guard let ss = ssConfig, let ovpn = config else { failureCallback(); return }
|
||||
let tunnelProtocol = NETunnelProviderProtocol()
|
||||
tunnelProtocol.serverAddress = serverAddress
|
||||
tunnelProtocol.providerBundleIdentifier = vpnBundleID
|
||||
tunnelProtocol.providerConfiguration = ["ovpn": Data(ovpn.utf8), "ss": Data(ss.utf8)]
|
||||
tunnel?.protocolConfiguration = tunnelProtocol
|
||||
tunnel?.localizedDescription = "Amnezia ShadowSocks"
|
||||
tunnel?.isEnabled = true
|
||||
|
||||
tunnel?.saveToPreferences { [unowned self] saveError in
|
||||
if let error = saveError {
|
||||
Logger.global?.log(message: "Connect ShadowSocks Tunnel Save Error: \(error)")
|
||||
failureCallback()
|
||||
return
|
||||
}
|
||||
|
||||
Logger.global?.log(message: "Saving ShadowSocks tunnel succeeded")
|
||||
|
||||
self.tunnel?.loadFromPreferences { error in
|
||||
if let error = error {
|
||||
Logger.global?.log(message: "Connect ShadowSocks Tunnel Load Error: \(error)")
|
||||
failureCallback()
|
||||
return
|
||||
}
|
||||
|
||||
Logger.global?.log(message: "Loading the ShadowSocks tunnel succeeded")
|
||||
print("Loading the ss tunnel succeeded")
|
||||
|
||||
do {
|
||||
print("starting ss tunnel")
|
||||
try self.tunnel?.connection.startVPNTunnel()
|
||||
} catch let error {
|
||||
Logger.global?.log(message: "Something went wrong: \(error)")
|
||||
failureCallback()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func configureOpenVPNTunnel(serverAddress: String, config: String, failureCallback: @escaping () -> Void) {
|
||||
let tunnelProtocol = NETunnelProviderProtocol()
|
||||
tunnelProtocol.serverAddress = serverAddress
|
||||
|
|
@ -393,7 +520,7 @@ public class IOSVpnProtocolImpl : NSObject {
|
|||
|
||||
@objc func disconnect() {
|
||||
Logger.global?.log(message: "Disconnecting")
|
||||
assert(tunnel != nil)
|
||||
guard tunnel != nil else { return }
|
||||
(tunnel!.connection as? NETunnelProviderSession)?.stopTunnel()
|
||||
}
|
||||
|
||||
|
|
@ -408,12 +535,60 @@ public class IOSVpnProtocolImpl : NSObject {
|
|||
checkWireguardStatus(callback: callback)
|
||||
case .openvpn:
|
||||
checkOVPNStatus(callback: callback)
|
||||
case .shadowsocks:
|
||||
checkShadowSocksStatus(callback: callback)
|
||||
case .empty:
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private func checkShadowSocksStatus(callback: @escaping (String, String, String) -> Void) {
|
||||
Logger.global?.log(message: "Check ShadowSocks")
|
||||
guard let proto = tunnel?.protocolConfiguration as? NETunnelProviderProtocol else {
|
||||
callback("", "", "")
|
||||
return
|
||||
}
|
||||
|
||||
guard let ssData = proto.providerConfiguration?["ss"] as? Data,
|
||||
let ssConfig = try? JSONSerialization.jsonObject(with: ssData, options: []) as? [String: Any],
|
||||
let serverIpv4Gateway = ssConfig["remote_host"] as? String else {
|
||||
callback("", "", "")
|
||||
return
|
||||
}
|
||||
|
||||
print("server IP: \(serverIpv4Gateway)")
|
||||
|
||||
let deviceIpv4Address = getTunIPAddress()
|
||||
print("device IP: \(serverIpv4Gateway)")
|
||||
if deviceIpv4Address == nil {
|
||||
callback("", "", "")
|
||||
return
|
||||
}
|
||||
|
||||
guard let session = tunnel?.connection as? NETunnelProviderSession else {
|
||||
callback("", "", "")
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
try session.sendProviderMessage(Data([UInt8(0)])) { [callback] data in
|
||||
guard let data = data,
|
||||
let configString = String(data: data, encoding: .utf8)
|
||||
else {
|
||||
Logger.global?.log(message: "Failed to convert data to string")
|
||||
callback("", "", "")
|
||||
return
|
||||
}
|
||||
|
||||
callback("\(serverIpv4Gateway)", "\(deviceIpv4Address!)", configString)
|
||||
}
|
||||
} catch {
|
||||
Logger.global?.log(message: "Failed to retrieve data from session")
|
||||
callback("", "", "")
|
||||
}
|
||||
}
|
||||
|
||||
private func checkOVPNStatus(callback: @escaping (String, String, String) -> Void) {
|
||||
Logger.global?.log(message: "Check OpenVPN")
|
||||
guard let proto = tunnel?.protocolConfiguration as? NETunnelProviderProtocol else {
|
||||
|
|
@ -463,7 +638,6 @@ public class IOSVpnProtocolImpl : NSObject {
|
|||
Logger.global?.log(message: "Failed to retrieve data from session")
|
||||
callback("", "", "")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private func checkWireguardStatus(callback: @escaping (String, String, String) -> Void) {
|
||||
|
|
@ -549,7 +723,7 @@ public class IOSVpnProtocolImpl : NSObject {
|
|||
}
|
||||
|
||||
enum TunnelType: String {
|
||||
case wireguard, openvpn, empty
|
||||
case wireguard, openvpn, shadowsocks, empty
|
||||
}
|
||||
|
||||
extension String {
|
||||
|
|
@ -557,6 +731,7 @@ extension String {
|
|||
switch self {
|
||||
case "wireguard": return .wireguard
|
||||
case "openvpn": return .openvpn
|
||||
case "shadowsocks": return .shadowsocks
|
||||
default:
|
||||
return .empty
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue