使用 xRay VPN 核心 (LibXray)。连接到 VPN 时尝试 ping 我的代理服务器列表。需要绕过 VPN 检查从设备到 IP 地址的 tcp ping(就像未连接一样)。
在 xRayConfiguration 中制定直接路由规则,仅根据 ping 需求进行连接,并在使用套接字测量连接到它时 - 不准确,但我猜它离事实不远了:
import Network
class TCPSocks5Ping {
//local socks5 proxy
private let proxyHost: NWEndpoint.Host
private let proxyPort: NWEndpoint.Port
//ping destination server
private let destinationHost: NWEndpoint.Host
private let destinationPort: NWEndpoint.Port
private let queue: DispatchQueue
private let destinationHostString: String
init(proxyHost: String, proxyPort: UInt16, destinationHost: String, destinationPort: UInt16, queue: DispatchQueue) {
self.proxyHost = NWEndpoint.Host(proxyHost)
self.proxyPort = NWEndpoint.Port(integerLiteral: proxyPort)
self.destinationHost = NWEndpoint.Host(destinationHost)
self.destinationPort = NWEndpoint.Port(integerLiteral: destinationPort)
self.destinationHostString = destinationHost
self.queue = queue
}
func ping(completion: @escaping (TimeInterval?, Error?) -> Void) {
let connection = NWConnection(host: proxyHost, port: proxyPort, using: .tcp)
connection.stateUpdateHandler = { state in
switch state {
case .waiting(let error):
print("Connection: Waiting - \(error.localizedDescription)")
completion(nil, error)
case .ready:
print("Connection: Ready")
self.socks5Handshake(connection: connection, completion: completion)
case .failed(let error):
print("Connection: Failed - \(error.localizedDescription)")
completion(nil, error)
default:
break
}
}
connection.start(queue: queue)
}
private func socks5Handshake(connection: NWConnection, completion: @escaping (TimeInterval?, Error?) -> Void) {
let socks5InitData = Data([0x05, 0x01, 0x00]) // SOCKS5, 1 method, no authentication
connection.send(content: socks5InitData, completion: .contentProcessed({ error in
if let error = error {
print("SOCKS5 Init Error: \(error.localizedDescription)")
completion(nil, error)
return
}
connection.receive(minimumIncompleteLength: 2, maximumLength: 2) { data, _, _, error in
if let error = error {
print("SOCKS5 Init Receive Error: \(error.localizedDescription)")
completion(nil, error)
return
}
if let data = data, data.count == 2, data[0] == 0x05, data[1] == 0x00 {
print("SOCKS5 Init Successful")
self.connectThroughSocks5(connection: connection, completion: completion)
} else {
print("SOCKS5 Init Failed: Invalid response data")
completion(nil, NSError(domain: "SOCKS5InitFailed", code: -1, userInfo: nil))
}
}
}))
}
private func connectThroughSocks5(connection: NWConnection, completion: @escaping (TimeInterval?, Error?) -> Void) {
let destinationAddress = self.destinationHostString
guard let destinationAddressData = destinationAddress.data(using: .utf8) else {
print("Invalid Destination Host")
completion(nil, NSError(domain: "InvalidDestinationHost", code: -1, userInfo: nil))
return
}
var connectData = Data([0x05, 0x01, 0x00, 0x03]) // SOCKS5, CONNECT, reserved, domain name
connectData.append(UInt8(destinationAddressData.count)) // domain name length
connectData.append(destinationAddressData) // domain name
connectData.append(UInt8(destinationPort.rawValue >> 8)) // port high byte
connectData.append(UInt8(destinationPort.rawValue & 0xFF)) // port low byte
connection.send(content: connectData, completion: .contentProcessed({ error in
if let error = error {
print("SOCKS5 Connect Error: \(error.localizedDescription)")
completion(nil, error)
return
}
connection.receive(minimumIncompleteLength: 10, maximumLength: 10) { data, _, _, error in
if let error = error {
print("SOCKS5 Connect Receive Error: \(error.localizedDescription)")
completion(nil, error)
return
}
if let data = data, data.count >= 10, data[1] == 0x00 {
print("SOCKS5 Connect Successful")
self.sendHTTPRequest(final: false, connection: connection, completion: completion)
} else {
print("SOCKS5 Connect Failed: Invalid response data")
completion(nil, NSError(domain: "SOCKS5ConnectFailed", code: -1, userInfo: nil))
}
}
}))
}
private func sendHTTPRequest(final: Bool, connection: NWConnection, completion: @escaping (TimeInterval?, Error?) -> Void) {
let httpRequest = "HEAD / HTTP /1.1\r\nHost: \(self.destinationHost)\r\nConnection: close\r\n\r\n"
guard let pingData = httpRequest.data(using: .utf8) else {
print("Invalid HTTP Request")
completion(nil, NSError(domain: "InvalidHTTPRequest", code: -1, userInfo: nil))
return
}
var startTime: DispatchTime?
if final {
startTime = DispatchTime.now()
}
connection.send(content: pingData, completion: .contentProcessed { error in
if let error = error {
print("HTTP Request Send Error: \(error.localizedDescription)")
completion(nil, error)
return
}
connection.receive(minimumIncompleteLength: 1, maximumLength: 1024) { data, _, _, error in
if final {
let endTime = DispatchTime.now()
connection.cancel()
if let error = error {
print("HTTP Request Receive Error: \(error.localizedDescription)")
completion(nil, error)
return
}
if let _ = data {
guard let startTime else { return }
let duration = Double((endTime.uptimeNanoseconds - startTime.uptimeNanoseconds) / 1_000_000)
completion(duration, nil)
} else {
completion(nil, NSError(domain: "HTTPRequestFailed", code: -1, userInfo: nil))
}
} else {
self.sendHTTPRequest(final: true, connection: connection, completion: completion)
}
}
})
}
}
尝试直接连接到 proxyHost 和 proxyPort - 也不起作用。 尝试仅使用“PING”等词发送请求。
我想知道是否有人可以提供帮助,因为我很绝望。
声明 NWParameters 的实例
let parameters = NWParameters(tls: nil, tcp: .init())
设置首选NoProxies
parameters.preferNoProxies = true
使用参数启动连接
connection = NWConnection(to: endpoint, using: parameters)