parent
eeeb2805c5
commit
760f935965
10 changed files with 285 additions and 3 deletions
73
client/platforms/ios/HevSocksTunnel.swift
Normal file
73
client/platforms/ios/HevSocksTunnel.swift
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import HevSocks5Tunnel
|
||||
|
||||
public enum Socks5Tunnel {
|
||||
|
||||
private static var tunnelFileDescriptor: Int32? {
|
||||
var ctlInfo = ctl_info()
|
||||
withUnsafeMutablePointer(to: &ctlInfo.ctl_name) {
|
||||
$0.withMemoryRebound(to: CChar.self, capacity: MemoryLayout.size(ofValue: $0.pointee)) {
|
||||
_ = strcpy($0, "com.apple.net.utun_control")
|
||||
}
|
||||
}
|
||||
for fd: Int32 in 0...1024 {
|
||||
var addr = sockaddr_ctl()
|
||||
var ret: Int32 = -1
|
||||
var len = socklen_t(MemoryLayout.size(ofValue: addr))
|
||||
withUnsafeMutablePointer(to: &addr) {
|
||||
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
|
||||
ret = getpeername(fd, $0, &len)
|
||||
}
|
||||
}
|
||||
if ret != 0 || addr.sc_family != AF_SYSTEM {
|
||||
continue
|
||||
}
|
||||
if ctlInfo.ctl_id == 0 {
|
||||
ret = ioctl(fd, CTLIOCGINFO, &ctlInfo)
|
||||
if ret != 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if addr.sc_id == ctlInfo.ctl_id {
|
||||
return fd
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private static var interfaceName: String? {
|
||||
guard let tunnelFileDescriptor = self.tunnelFileDescriptor else {
|
||||
return nil
|
||||
}
|
||||
var buffer = [UInt8](repeating: 0, count: Int(IFNAMSIZ))
|
||||
return buffer.withUnsafeMutableBufferPointer { mutableBufferPointer in
|
||||
guard let baseAddress = mutableBufferPointer.baseAddress else {
|
||||
return nil
|
||||
}
|
||||
var ifnameSize = socklen_t(IFNAMSIZ)
|
||||
let result = getsockopt(
|
||||
tunnelFileDescriptor,
|
||||
2 /* SYSPROTO_CONTROL */,
|
||||
2 /* UTUN_OPT_IFNAME */,
|
||||
baseAddress,
|
||||
&ifnameSize
|
||||
)
|
||||
if result == 0 {
|
||||
return String(cString: baseAddress)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public static func run(withConfig filePath: String) -> Int32 {
|
||||
guard let fileDescriptor = self.tunnelFileDescriptor else {
|
||||
fatalError("Get tunnel file descriptor failed.")
|
||||
}
|
||||
return hev_socks5_tunnel_main(filePath.cString(using: .utf8), fileDescriptor)
|
||||
}
|
||||
|
||||
public static func quit() {
|
||||
hev_socks5_tunnel_quit()
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,10 @@ public func ovpnLog(_ type: OSLogType, title: String = "", message: String) {
|
|||
neLog(type, title: "OVPN: \(title)", message: message)
|
||||
}
|
||||
|
||||
public func xrayLog(_ type: OSLogType, title: String = "", message: String) {
|
||||
neLog(type, title: "XRAY: \(title)", message: message)
|
||||
}
|
||||
|
||||
public func neLog(_ type: OSLogType, title: String = "", message: String) {
|
||||
Log.log(type, title: "NE: \(title)", message: message)
|
||||
}
|
||||
|
|
|
|||
159
client/platforms/ios/PacketTunnelProvider+Xray.swift
Normal file
159
client/platforms/ios/PacketTunnelProvider+Xray.swift
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
import Foundation
|
||||
import NetworkExtension
|
||||
import WireGuardKitGo
|
||||
|
||||
enum XrayErrors: Error {
|
||||
case noXrayConfig
|
||||
case cantSaveXrayConfig
|
||||
case cantParseListenAndPort
|
||||
case cantSaveHevSocksConfig
|
||||
}
|
||||
|
||||
extension Constants {
|
||||
static let cachesDirectory: URL = {
|
||||
if let cachesDirectoryURL = FileManager.default.urls(for: .cachesDirectory,
|
||||
in: .userDomainMask).first {
|
||||
return cachesDirectoryURL
|
||||
} else {
|
||||
fatalError("Unable to retrieve caches directory.")
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
extension PacketTunnelProvider {
|
||||
func startXray(completionHandler: @escaping (Error?) -> Void) {
|
||||
|
||||
// Xray configuration
|
||||
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
||||
let providerConfiguration = protocolConfiguration.providerConfiguration,
|
||||
let xrayConfigData = providerConfiguration[Constants.xrayConfigKey] as? Data else {
|
||||
xrayLog(.error, message: "Can't get xray configuration")
|
||||
completionHandler(XrayErrors.noXrayConfig)
|
||||
return
|
||||
}
|
||||
|
||||
// Tunnel settings
|
||||
let ipv6Enabled = true
|
||||
let hideVPNIcon = false
|
||||
|
||||
let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "254.1.1.1")
|
||||
settings.mtu = 9000
|
||||
|
||||
settings.ipv4Settings = {
|
||||
let settings = NEIPv4Settings(addresses: ["198.18.0.1"], subnetMasks: ["255.255.0.0"])
|
||||
settings.includedRoutes = [NEIPv4Route.default()]
|
||||
return settings
|
||||
}()
|
||||
|
||||
settings.ipv6Settings = {
|
||||
guard ipv6Enabled else {
|
||||
return nil
|
||||
}
|
||||
let settings = NEIPv6Settings(addresses: ["fd6e:a81b:704f:1211::1"], networkPrefixLengths: [64])
|
||||
settings.includedRoutes = [NEIPv6Route.default()]
|
||||
if hideVPNIcon {
|
||||
settings.excludedRoutes = [NEIPv6Route(destinationAddress: "::", networkPrefixLength: 128)]
|
||||
}
|
||||
return settings
|
||||
}()
|
||||
|
||||
let dns = ["8.8.4.4","1.1.1.1"]
|
||||
settings.dnsSettings = NEDNSSettings(servers: dns)
|
||||
|
||||
setTunnelNetworkSettings(settings) { [weak self] error in
|
||||
if let error {
|
||||
completionHandler(error)
|
||||
return
|
||||
}
|
||||
|
||||
// Launch xray
|
||||
self?.setupAndStartXray(configData: xrayConfigData) { xrayError in
|
||||
if let xrayError {
|
||||
completionHandler(xrayError)
|
||||
return
|
||||
}
|
||||
|
||||
// Launch hevSocks
|
||||
self?.setupAndRunTun2socks(configData: xrayConfigData,
|
||||
completionHandler: completionHandler)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stopXray(completionHandler: () -> Void) {
|
||||
Socks5Tunnel.quit()
|
||||
LibXrayStopXray()
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
private func setupAndStartXray(configData: Data,
|
||||
completionHandler: @escaping (Error?) -> Void) {
|
||||
let path = Constants.cachesDirectory.appendingPathComponent("config.json", isDirectory: false).path
|
||||
guard FileManager.default.createFile(atPath: path, contents: configData) else {
|
||||
xrayLog(.error, message: "Can't save xray configuration")
|
||||
completionHandler(XrayErrors.cantSaveXrayConfig)
|
||||
return
|
||||
}
|
||||
|
||||
LibXrayRunXray(nil,
|
||||
path,
|
||||
Int64.max)
|
||||
|
||||
completionHandler(nil)
|
||||
xrayLog(.info, message: "Xray started")
|
||||
}
|
||||
|
||||
private func setupAndRunTun2socks(configData: Data,
|
||||
completionHandler: @escaping (Error?) -> Void) {
|
||||
var port = 10808
|
||||
var address = "::1"
|
||||
|
||||
let jsonDict = try? JSONSerialization.jsonObject(with: configData, options: []) as? [String: Any]
|
||||
|
||||
guard let jsonDict else {
|
||||
xrayLog(.error, message: "Can't parse address and port for hevSocks")
|
||||
completionHandler(XrayErrors.cantParseListenAndPort)
|
||||
return
|
||||
}
|
||||
|
||||
// Xray listen and port should be the same as port and address in hevSocks
|
||||
if let inbounds = jsonDict["inbounds"] as? [[String: Any]], let inbound = inbounds.first {
|
||||
if let listen = inbound["listen"] as? String {
|
||||
address = listen
|
||||
address.removeAll { $0 == "[" || $0 == "]" }
|
||||
}
|
||||
if let portFromConfig = inbound["port"] as? Int {
|
||||
port = portFromConfig
|
||||
}
|
||||
}
|
||||
|
||||
let config = """
|
||||
tunnel:
|
||||
mtu: 9000
|
||||
socks5:
|
||||
port: \(port)
|
||||
address: \(address)
|
||||
udp: 'udp'
|
||||
misc:
|
||||
task-stack-size: 20480
|
||||
connect-timeout: 5000
|
||||
read-write-timeout: 60000
|
||||
log-file: stderr
|
||||
log-level: error
|
||||
limit-nofile: 65535
|
||||
"""
|
||||
|
||||
let configurationFilePath = Constants.cachesDirectory.appendingPathComponent("config.yml", isDirectory: false).path
|
||||
guard FileManager.default.createFile(atPath: configurationFilePath, contents: config.data(using: .utf8)!) else {
|
||||
xrayLog(.info, message: "Cant save hevSocks configuration")
|
||||
completionHandler(XrayErrors.cantSaveHevSocksConfig)
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.global().async {
|
||||
xrayLog(.info, message: "Hev socks started")
|
||||
completionHandler(nil)
|
||||
Socks5Tunnel.run(withConfig: configurationFilePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,8 @@ import Darwin
|
|||
import OpenVPNAdapter
|
||||
|
||||
enum TunnelProtoType: String {
|
||||
case wireguard, openvpn
|
||||
case wireguard, openvpn, xray
|
||||
|
||||
}
|
||||
|
||||
struct Constants {
|
||||
|
|
@ -13,6 +14,7 @@ struct Constants {
|
|||
static let processQueueName = "org.amnezia.process-packets"
|
||||
static let kActivationAttemptId = "activationAttemptId"
|
||||
static let ovpnConfigKey = "ovpn"
|
||||
static let xrayConfigKey = "xray"
|
||||
static let wireGuardConfigKey = "wireguard"
|
||||
static let loggerTag = "NET"
|
||||
|
||||
|
|
@ -91,6 +93,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
protoType = .openvpn
|
||||
} else if (providerConfiguration?[Constants.wireGuardConfigKey] as? Data) != nil {
|
||||
protoType = .wireguard
|
||||
} else if (providerConfiguration?[Constants.xrayConfigKey] as? Data) != nil {
|
||||
protoType = .xray
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -107,6 +111,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
completionHandler: completionHandler)
|
||||
case .openvpn:
|
||||
startOpenVPN(completionHandler: completionHandler)
|
||||
case .xray:
|
||||
startXray(completionHandler: completionHandler)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -124,6 +131,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
case .openvpn:
|
||||
stopOpenVPN(with: reason,
|
||||
completionHandler: completionHandler)
|
||||
case .xray:
|
||||
stopXray(completionHandler: completionHandler)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -138,6 +147,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
handleWireguardStatusMessage(messageData, completionHandler: completionHandler)
|
||||
case .openvpn:
|
||||
handleOpenVPNStatusMessage(messageData, completionHandler: completionHandler)
|
||||
case .xray:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -72,9 +72,11 @@ private:
|
|||
bool setupCloak();
|
||||
bool setupWireGuard();
|
||||
bool setupAwg();
|
||||
bool setupXray();
|
||||
|
||||
bool startOpenVPN(const QString &config);
|
||||
bool startWireGuard(const QString &jsonConfig);
|
||||
bool startXray(const QString &jsonConfig);
|
||||
|
||||
void startTunnel();
|
||||
|
||||
|
|
|
|||
|
|
@ -216,6 +216,9 @@ bool IosController::connectVpn(amnezia::Proto proto, const QJsonObject& configur
|
|||
if (proto == amnezia::Proto::Awg) {
|
||||
return setupAwg();
|
||||
}
|
||||
if (proto == amnezia::Proto::Xray) {
|
||||
return setupXray();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -501,6 +504,15 @@ bool IosController::setupWireGuard()
|
|||
return startWireGuard(wgConfigDocStr);
|
||||
}
|
||||
|
||||
bool IosController::setupXray()
|
||||
{
|
||||
QJsonObject config = m_rawConfig[ProtocolProps::key_proto_config_data(amnezia::Proto::Xray)].toObject();
|
||||
QJsonDocument xrayConfigDoc(config);
|
||||
QString xrayConfigStr(xrayConfigDoc.toJson(QJsonDocument::Compact));
|
||||
|
||||
return startXray(xrayConfigStr);
|
||||
}
|
||||
|
||||
bool IosController::setupAwg()
|
||||
{
|
||||
QJsonObject config = m_rawConfig[ProtocolProps::key_proto_config_data(amnezia::Proto::Awg)].toObject();
|
||||
|
|
@ -590,6 +602,20 @@ bool IosController::startWireGuard(const QString &config)
|
|||
startTunnel();
|
||||
}
|
||||
|
||||
bool IosController::startXray(const QString &config)
|
||||
{
|
||||
qDebug() << "IosController::startXray";
|
||||
|
||||
NETunnelProviderProtocol *tunnelProtocol = [[NETunnelProviderProtocol alloc] init];
|
||||
tunnelProtocol.providerBundleIdentifier = [NSString stringWithUTF8String:VPN_NE_BUNDLEID];
|
||||
tunnelProtocol.providerConfiguration = @{@"xray": [[NSString stringWithUTF8String:config.toStdString().c_str()] dataUsingEncoding:NSUTF8StringEncoding]};
|
||||
tunnelProtocol.serverAddress = m_serverAddress;
|
||||
|
||||
m_currentTunnel.protocolConfiguration = tunnelProtocol;
|
||||
|
||||
startTunnel();
|
||||
}
|
||||
|
||||
void IosController::startTunnel()
|
||||
{
|
||||
NSString *protocolName = @"Unknown";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue