mirror of
https://github.com/wanghongenpin/proxypin.git
synced 2026-05-16 15:56:51 +08:00
ios optimize tun proxy
This commit is contained in:
@@ -21,12 +21,36 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
}
|
||||
|
||||
let host = conf["proxyHost"] as! String
|
||||
let proxyPort = conf["proxyPort"] as! Int
|
||||
let ipProxy = conf["ipProxy"] as! Bool? ?? false
|
||||
let proxyPort = conf["proxyPort"] as! Int
|
||||
let ipProxy = conf["ipProxy"] as! Bool? ?? false
|
||||
|
||||
// let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1")
|
||||
let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: host)
|
||||
// let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: host)
|
||||
NSLog(conf.debugDescription)
|
||||
|
||||
networkSettings.mtu = 9000
|
||||
|
||||
let ipv4Settings = NEIPv4Settings(addresses: ["10.0.0.2"], subnetMasks: ["255.255.255.255"])
|
||||
|
||||
if (ipProxy){
|
||||
ipv4Settings.includedRoutes = [NEIPv4Route.default()]
|
||||
ipv4Settings.excludedRoutes = [
|
||||
NEIPv4Route(destinationAddress: "10.0.0.0", subnetMask: "255.0.0.0"),
|
||||
NEIPv4Route(destinationAddress: "100.64.0.0", subnetMask: "255.192.0.0"),
|
||||
// NEIPv4Route(destinationAddress: "127.0.0.0", subnetMask: "255.0.0.0"),
|
||||
NEIPv4Route(destinationAddress: "169.254.0.0", subnetMask: "255.255.0.0"),
|
||||
NEIPv4Route(destinationAddress: "172.16.0.0", subnetMask: "255.240.0.0"),
|
||||
NEIPv4Route(destinationAddress: "192.168.0.0", subnetMask: "255.255.0.0"),
|
||||
NEIPv4Route(destinationAddress: "17.0.0.0", subnetMask: "255.0.0.0"),
|
||||
]
|
||||
|
||||
let dns = "223.5.5.5,8.8.8.8"
|
||||
let dnsSettings = NEDNSSettings(servers: dns.components(separatedBy: ","))
|
||||
dnsSettings.matchDomains = [""]
|
||||
dnsSettings.matchDomainsNoSearch = true
|
||||
networkSettings.dnsSettings = dnsSettings
|
||||
}
|
||||
|
||||
//http代理
|
||||
let proxySettings = NEProxySettings()
|
||||
proxySettings.httpEnabled = true
|
||||
@@ -34,30 +58,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
proxySettings.httpsEnabled = true
|
||||
proxySettings.httpsServer = NEProxyServer(address: host, port: proxyPort)
|
||||
proxySettings.matchDomains = [""]
|
||||
|
||||
networkSettings.proxySettings = proxySettings
|
||||
networkSettings.mtu = 1480
|
||||
|
||||
let ipv4Settings = NEIPv4Settings(addresses: ["10.0.0.2"], subnetMasks: ["255.255.255.255"])
|
||||
|
||||
if (ipProxy){
|
||||
ipv4Settings.includedRoutes = [NEIPv4Route.default()]
|
||||
// ipv4Settings.excludedRoutes = [
|
||||
// NEIPv4Route(destinationAddress: "10.0.0.0", subnetMask: "255.0.0.0"),
|
||||
// NEIPv4Route(destinationAddress: "100.64.0.0", subnetMask: "255.192.0.0"),
|
||||
// NEIPv4Route(destinationAddress: "127.0.0.0", subnetMask: "255.0.0.0"),
|
||||
// NEIPv4Route(destinationAddress: "169.254.0.0", subnetMask: "255.255.0.0"),
|
||||
// NEIPv4Route(destinationAddress: "172.16.0.0", subnetMask: "255.240.0.0"),
|
||||
// NEIPv4Route(destinationAddress: "192.168.0.0", subnetMask: "255.255.0.0"),
|
||||
// NEIPv4Route(destinationAddress: "17.0.0.0", subnetMask: "255.0.0.0"),
|
||||
// ]
|
||||
|
||||
|
||||
let dns = "114.114.114.114,8.8.8.8"
|
||||
let dnsSettings = NEDNSSettings(servers: dns.components(separatedBy: ","))
|
||||
dnsSettings.matchDomains = [""]
|
||||
networkSettings.dnsSettings = dnsSettings
|
||||
}
|
||||
networkSettings.proxySettings = proxySettings
|
||||
|
||||
|
||||
networkSettings.ipv4Settings = ipv4Settings
|
||||
|
||||
|
||||
8
ios/ProxyPin/ProxyPin-Bridging-Header.h
Normal file
8
ios/ProxyPin/ProxyPin-Bridging-Header.h
Normal file
@@ -0,0 +1,8 @@
|
||||
//
|
||||
// ProxyPin-Bridging-Header.h
|
||||
// Runner
|
||||
//
|
||||
// Created by wanghongen on 2025/5/28.
|
||||
//
|
||||
|
||||
#import "GBPing.h"
|
||||
@@ -71,37 +71,34 @@ class Connection{
|
||||
func closeConnection() {
|
||||
connectionCloser.closeConnection(connection: self)
|
||||
}
|
||||
|
||||
|
||||
func addSendData(data: Data) {
|
||||
self.sendBuffer.append(data)
|
||||
|
||||
QueueFactory.instance.getQueue().async(flags: .barrier) {
|
||||
self.sendBuffer.append(data)
|
||||
if (self.channel?.state != .ready) {
|
||||
os_log("Connection %{public}@ is not ready, cannot send data", log: OSLog.default, type: .debug, self.description)
|
||||
return
|
||||
}
|
||||
|
||||
if (self.nwProtocol == .TCP && self.channel?.state != .ready) {
|
||||
return
|
||||
}
|
||||
self.sendToDestination()
|
||||
}
|
||||
self.sendToDestination()
|
||||
}
|
||||
|
||||
//发送到目标服务器的数据
|
||||
func sendToDestination() {
|
||||
QueueFactory.instance.getQueue().async(flags: .barrier) {
|
||||
os_log("Sending data to destination key %{public}@", log: OSLog.default, type: .debug, self.description)
|
||||
if (self.sendBuffer.count == 0) {
|
||||
return
|
||||
// os_log("Sending data to destination key %{public}@", log: OSLog.default, type: .debug, self.description)
|
||||
if (self.sendBuffer.count == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
let data = self.sendBuffer
|
||||
self.sendBuffer.removeAll()
|
||||
|
||||
self.channel?.send(content: data, completion: .contentProcessed({ error in
|
||||
if let error = error {
|
||||
os_log("Failed to send data to destination key %{public}@ error: %{public}@", log: OSLog.default, type: .error, self.description, error.localizedDescription)
|
||||
self.closeConnection()
|
||||
}
|
||||
|
||||
let data = self.sendBuffer
|
||||
self.sendBuffer.removeAll()
|
||||
|
||||
self.channel?.send(content: data, completion: .contentProcessed({ error in
|
||||
if let error = error {
|
||||
os_log("Failed to send data to destination key %{public}@ error: %{public}@", log: OSLog.default, type: .error, self.description, error.localizedDescription)
|
||||
self.closeConnection()
|
||||
}
|
||||
}))
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
var description: String {
|
||||
|
||||
@@ -45,10 +45,13 @@ class ConnectionHandler {
|
||||
switch ipHeader.protocolNumber {
|
||||
case ProtocolType.tcp.rawValue:
|
||||
handleTCPPacket(packet: clientPacketData, ipHeader: ipHeader)
|
||||
case ProtocolType.udp.rawValue:
|
||||
handleUDPPacket(clientPacketData: clientPacketData, ipHeader: ipHeader)
|
||||
break
|
||||
case ProtocolType.udp.rawValue:
|
||||
handleUDPPacket(clientPacketData: clientPacketData, ipHeader: ipHeader)
|
||||
break
|
||||
case ProtocolType.icmp.rawValue:
|
||||
handleICMPPacket(clientPacketData: &clientPacketData, ipHeader: ipHeader)
|
||||
break
|
||||
default:
|
||||
os_log("Unsupported IP protocol: %d", log: OSLog.default, type: .error, ipHeader.protocolNumber)
|
||||
}
|
||||
@@ -91,13 +94,17 @@ class ConnectionHandler {
|
||||
}
|
||||
|
||||
synchronized(connection) {
|
||||
// os_log("Received UDP packet", log: OSLog.default, type: .default)
|
||||
os_log("handle UDP Packet %{public}@", log: OSLog.default, type: .default, connection.description)
|
||||
if newSession {
|
||||
ioService.registerSession(connection: connection)
|
||||
}
|
||||
|
||||
let payload = clientPacketData.subdata(in: UDPPacketFactory.UDP_HEADER_LENGTH..<clientPacketData.count)
|
||||
|
||||
if ((payload.count + UDPPacketFactory.UDP_HEADER_LENGTH) != udpHeader.length) {
|
||||
os_log("UDP %{public}@ packet length mismatch: expected %d, got %d", log: OSLog.default, type: .error, connection.description, udpHeader.length, payload.count)
|
||||
}
|
||||
// os_log("Received UDP packet", log: OSLog.default, type: .default)
|
||||
connection.lastIpHeader = ipHeader
|
||||
connection.lastUdpHeader = udpHeader
|
||||
manager.addClientData(data: payload, connection: connection)
|
||||
@@ -116,24 +123,20 @@ class ConnectionHandler {
|
||||
return
|
||||
}
|
||||
|
||||
// printByteArray(packet)
|
||||
|
||||
let dataLength = tcpHeader.payload?.count ?? 0
|
||||
let sourceIP = ipHeader.sourceIP
|
||||
let destinationIP = ipHeader.destinationIP
|
||||
let sourcePort = tcpHeader.sourcePort
|
||||
let destinationPort = tcpHeader.destinationPort
|
||||
|
||||
// os_log("Handling TCP packet for %{public}@ flags:%d", log: OSLog.default, type: .default, Connection.getConnectionKey(nwProtocol: .TCP, destIp: destinationIP, destPort: destinationPort, sourceIp: sourceIP, sourcePort: sourcePort), tcpHeader.flags)
|
||||
let key = Connection.getConnectionKey(nwProtocol: .TCP, destIp: destinationIP, destPort: destinationPort, sourceIp: sourceIP, sourcePort: sourcePort)
|
||||
|
||||
if (tcpHeader.isSYN()) {
|
||||
// os_log("Received SYN packet %{public}@ seq:%u", log: OSLog.default, type: .default, Connection.getConnectionKey(nwProtocol: .TCP, destIp: destinationIP, destPort: destinationPort, sourceIp: sourceIP, sourcePort: sourcePort), tcpHeader.sequenceNumber)
|
||||
os_log("Received SYN packet %{public}@ seq:%u", log: OSLog.default, type: .default, key, tcpHeader.sequenceNumber)
|
||||
// 3-way handshake + create new session
|
||||
replySynAck(ipHeader: ipHeader, tcpHeader: tcpHeader)
|
||||
} else if (tcpHeader.isACK()) {
|
||||
|
||||
let key = Connection.getConnectionKey(nwProtocol: .TCP, destIp: destinationIP, destPort: destinationPort, sourceIp: sourceIP, sourcePort: sourcePort)
|
||||
// os_log("Received ACK packet for key: %{public}@", log: OSLog.default, type: .debug, key)
|
||||
// os_log("Received ACK packet for key: %{public}@", log: OSLog.default, type: .debug, key)
|
||||
|
||||
guard let connection = manager.getConnectionByKey(key: key) else {
|
||||
os_log("Ack for unknown session: %{public}@", log: OSLog.default, type: .default, key)
|
||||
@@ -150,24 +153,19 @@ class ConnectionHandler {
|
||||
connection.lastTcpHeader = tcpHeader
|
||||
|
||||
if dataLength > 0 {
|
||||
// initProxyConnect(packet: packet, destinationIP: destinationIP, destinationPort: destinationPort, connection: connection)
|
||||
// os_log("Received data packet %{public}@ length:%d seq:%u", log: OSLog.default, type: .default, connection.description, dataLength, tcpHeader.sequenceNumber)
|
||||
//accumulate data from client
|
||||
// os_log("Received data packet %{public}@ length:%d seq:%u, ack:%u", log: OSLog.default, type: .default, connection.description, dataLength, tcpHeader.sequenceNumber, tcpHeader.ackNumber)
|
||||
manager.addClientData(data: tcpHeader.payload!, connection: connection)
|
||||
|
||||
//send ack to client only if new data was added
|
||||
sendAck(ipHeader: ipHeader, tcpHeader: tcpHeader, acceptedDataLength: dataLength, connection: connection)
|
||||
|
||||
} else {
|
||||
// os_log("Received ACK packet %{public}@ seq:%u", log: OSLog.default, type: .default, connection.description, tcpHeader.sequenceNumber)
|
||||
//an ack from client for previously sent data
|
||||
acceptAck(tcpHeader: tcpHeader, connection: connection)
|
||||
if connection.isClosingConnection {
|
||||
sendFinAck(ipHeader: ipHeader, tcpHeader: tcpHeader, connection: connection)
|
||||
} else if connection.isAckedToFin && !tcpHeader.isFIN() {
|
||||
//the last ACK from client after FIN-ACK flag was sent
|
||||
manager.closeConnection(nwProtocol: .TCP, ip: destinationIP, port: destinationPort, srcIp: sourceIP, srcPort: sourcePort)
|
||||
}
|
||||
os_log("Received ACK packet %{public}@ seq:%u, ack:%u", log: OSLog.default, type: .default, connection.description, tcpHeader.sequenceNumber, tcpHeader.ackNumber)
|
||||
}
|
||||
|
||||
acceptAck(tcpHeader: tcpHeader, connection: connection)
|
||||
|
||||
if connection.isClosingConnection {
|
||||
sendFinAck(ipHeader: ipHeader, tcpHeader: tcpHeader, connection: connection)
|
||||
} else if connection.isAckedToFin && !tcpHeader.isFIN() {
|
||||
manager.closeConnection(nwProtocol: .TCP, ip: destinationIP, port: destinationPort, srcIp: sourceIP, srcPort: sourcePort)
|
||||
}
|
||||
|
||||
//received the last segment of data from vpn client
|
||||
@@ -274,7 +272,7 @@ class ConnectionHandler {
|
||||
let ackData = TCPPacketFactory.createResponseAckData(ipHeader: ipHeader, tcpHeader: tcpHeader, ackToClient: ackNumber)
|
||||
self.write(data: ackData)
|
||||
|
||||
// os_log("Sent ACK packet to client %{public}@:%{public}d ack# %{public}d", log: OSLog.default, type: .debug, PacketUtil.intToIPAddress(ipHeader.destinationIP), tcpHeader.destinationPort, ackNumber)
|
||||
// os_log("Sent ACK packet to client %{public}@ ack: %u", log: OSLog.default, type: .default, connection.description, ackNumber)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,7 +323,7 @@ class ConnectionHandler {
|
||||
self.ioService.registerSession(connection: connection)
|
||||
}
|
||||
self.write(data: packet.buffer)
|
||||
// os_log("SYN-ACK packet length:%d sent", log: OSLog.default, type: .default, packet.buffer.count)
|
||||
// os_log("SYN-ACK %{public}@ packet length:%d sent ack:%u", log: OSLog.default, type: .default, connection.description, packet.buffer.count, tcpTransport.ackNumber)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,11 +14,10 @@ class ConnectionManager : CloseableConnection{
|
||||
//static let instance = ConnectionManager()
|
||||
|
||||
private var table: [String: Connection] = [:]
|
||||
private let tableQueue = DispatchQueue(label: "ProxyPin.ConnectionManager")
|
||||
|
||||
public var proxyAddress: NWEndpoint?
|
||||
|
||||
private let defaultPorts: [UInt16] = [80, 8080, 8888, 443]
|
||||
private let defaultPorts: [UInt16] = [80, 443]
|
||||
|
||||
|
||||
func getConnection(nwProtocol: NWProtocol, ip: UInt32, port: UInt16, srcIp: UInt32, srcPort: UInt16) -> Connection? {
|
||||
@@ -27,87 +26,65 @@ class ConnectionManager : CloseableConnection{
|
||||
}
|
||||
|
||||
func getConnectionByKey(key: String) -> Connection? {
|
||||
return tableQueue.sync {
|
||||
return table[key]
|
||||
}
|
||||
return table[key]
|
||||
}
|
||||
|
||||
func createTCPConnection(ip: UInt32, port: UInt16, srcIp: UInt32, srcPort: UInt16) -> Connection {
|
||||
let key = Connection.getConnectionKey(nwProtocol: .TCP, destIp: ip, destPort: port, sourceIp: srcIp, sourcePort: srcPort)
|
||||
|
||||
return tableQueue.sync {
|
||||
if let existingConnection = table[key] {
|
||||
return existingConnection
|
||||
}
|
||||
|
||||
let connection = Connection(nwProtocol: .TCP, sourceIp: srcIp, sourcePort: srcPort, destinationIp: ip, destinationPort: port, connectionCloser: self)
|
||||
|
||||
let parameters = NWParameters.tcp
|
||||
parameters.allowLocalEndpointReuse = true
|
||||
parameters.includePeerToPeer = true
|
||||
|
||||
let endpoint: NWEndpoint
|
||||
if defaultPorts.contains(port) {
|
||||
endpoint = proxyAddress!
|
||||
} else {
|
||||
let ipString = PacketUtil.intToIPAddress(ip)
|
||||
endpoint = NWEndpoint.hostPort(host: NWEndpoint.Host(ipString), port: NWEndpoint.Port(rawValue: port)!)
|
||||
}
|
||||
|
||||
let nwConnection = NWConnection(to: endpoint, using: parameters)
|
||||
|
||||
connection.channel = nwConnection
|
||||
connection.isInitConnect = true
|
||||
|
||||
tableQueue.async(flags: .barrier) {
|
||||
self.table[key] = connection
|
||||
}
|
||||
os_log("Created TCP connection %{public}@", log: OSLog.default, type: .default, key)
|
||||
|
||||
return connection
|
||||
if let existingConnection = table[key] {
|
||||
return existingConnection
|
||||
}
|
||||
|
||||
// nwConnection.stateUpdateHandler = { state in
|
||||
// switch state {
|
||||
// case .ready:
|
||||
// connection.isConnected = true
|
||||
// os_log("Connected to %{public}@", log: OSLog.default, type: .debug, endpoint.debugDescription)
|
||||
// case .failed(let error):
|
||||
// connection.isConnected = false
|
||||
// os_log("Failed to connect: %{public}@", log: OSLog.default, type: .error, error.localizedDescription)
|
||||
//
|
||||
// default:
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
let connection = Connection(nwProtocol: .TCP, sourceIp: srcIp, sourcePort: srcPort, destinationIp: ip, destinationPort: port, connectionCloser: self)
|
||||
|
||||
let ipString = PacketUtil.intToIPAddress(ip)
|
||||
|
||||
let endpoint: NWEndpoint
|
||||
if (defaultPorts.contains(port) && !isPrivateIP(ipString)) {
|
||||
endpoint = proxyAddress!
|
||||
} else {
|
||||
endpoint = NWEndpoint.hostPort(host: NWEndpoint.Host(ipString), port: NWEndpoint.Port(rawValue: port)!)
|
||||
}
|
||||
|
||||
// 使用 TCP 协议
|
||||
let parameters = NWParameters.tcp
|
||||
|
||||
let nwConnection = NWConnection(to: endpoint, using: parameters)
|
||||
|
||||
connection.channel = nwConnection
|
||||
connection.isInitConnect = true
|
||||
|
||||
self.table[key] = connection
|
||||
os_log("Created TCP connection %{public}@", log: OSLog.default, type: .default, key)
|
||||
|
||||
return connection
|
||||
}
|
||||
|
||||
|
||||
private func isPrivateIP(_ ip: String) -> Bool {
|
||||
return ip.hasPrefix("10.") ||
|
||||
ip.hasPrefix("172.") && (16...31).contains(Int(ip.split(separator: ".")[1]) ?? -1) ||
|
||||
ip.hasPrefix("192.168.")
|
||||
}
|
||||
|
||||
func createUDPConnection(ip: UInt32, port: UInt16, srcIp: UInt32, srcPort: UInt16) -> Connection {
|
||||
let key = Connection.getConnectionKey(nwProtocol: .UDP, destIp: ip, destPort: port, sourceIp: srcIp, sourcePort: srcPort)
|
||||
|
||||
return tableQueue.sync {
|
||||
if let existingConnection = table[key] {
|
||||
return existingConnection
|
||||
}
|
||||
|
||||
let connection = Connection(nwProtocol: .UDP, sourceIp: srcIp, sourcePort: srcPort, destinationIp: ip, destinationPort: port, connectionCloser: self)
|
||||
|
||||
|
||||
let endpoint = NWEndpoint.hostPort(host: NWEndpoint.Host((PacketUtil.intToIPAddress(ip))), port: NWEndpoint.Port(rawValue: port)!)
|
||||
|
||||
let nwConnection = NWConnection(to: endpoint, using: .udp)
|
||||
connection.channel = nwConnection
|
||||
|
||||
os_log("Created UDP connection %{public}@", log: OSLog.default, type: .default, key)
|
||||
|
||||
connection.isConnected = true
|
||||
tableQueue.async(flags: .barrier) {
|
||||
self.table[key] = connection
|
||||
}
|
||||
|
||||
return connection
|
||||
if let existingConnection = table[key] {
|
||||
return existingConnection
|
||||
}
|
||||
|
||||
let connection = Connection(nwProtocol: .UDP, sourceIp: srcIp, sourcePort: srcPort, destinationIp: ip, destinationPort: port, connectionCloser: self)
|
||||
|
||||
let endpoint = NWEndpoint.hostPort(host: NWEndpoint.Host((PacketUtil.intToIPAddress(ip))), port: NWEndpoint.Port(rawValue: port)!)
|
||||
|
||||
let nwConnection = NWConnection(to: endpoint, using: .udp)
|
||||
connection.channel = nwConnection
|
||||
|
||||
os_log("Created UDP connection %{public}@", log: OSLog.default, type: .default, key)
|
||||
self.table[key] = connection
|
||||
|
||||
return connection
|
||||
}
|
||||
|
||||
func closeConnection(connection: Connection) {
|
||||
@@ -120,14 +97,13 @@ class ConnectionManager : CloseableConnection{
|
||||
// 从内存中删除连接,然后关闭套接字。
|
||||
func closeConnection(nwProtocol: NWProtocol, ip: UInt32, port: UInt16, srcIp: UInt32, srcPort: UInt16) {
|
||||
let key = Connection.getConnectionKey(nwProtocol: nwProtocol, destIp: ip, destPort: port, sourceIp: srcIp, sourcePort: srcPort)
|
||||
tableQueue.async(flags: .barrier) {
|
||||
if let connection = self.table.removeValue(forKey: key) {
|
||||
if connection.channel?.state != .cancelled {
|
||||
connection.channel?.cancel()
|
||||
os_log("Closed connection %{public}@", log: OSLog.default, type: .debug, key)
|
||||
} else {
|
||||
os_log("Connection %{public}@ is already cancelled", log: OSLog.default, type: .debug, key)
|
||||
}
|
||||
|
||||
if let connection = self.table.removeValue(forKey: key) {
|
||||
if connection.channel?.state != .cancelled {
|
||||
connection.channel?.cancel()
|
||||
os_log("Closed connection %{public}@", log: OSLog.default, type: .debug, key)
|
||||
} else {
|
||||
os_log("Connection %{public}@ is already cancelled", log: OSLog.default, type: .debug, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,8 +116,7 @@ class ConnectionManager : CloseableConnection{
|
||||
|
||||
connection.addSendData(data: data)
|
||||
}
|
||||
|
||||
//阻止java垃圾收集器收集会话
|
||||
|
||||
func keepSessionAlive(connection: Connection) {
|
||||
let key = Connection.getConnectionKey(
|
||||
nwProtocol: connection.nwProtocol,
|
||||
@@ -150,8 +125,7 @@ class ConnectionManager : CloseableConnection{
|
||||
sourceIp: connection.sourceIp,
|
||||
sourcePort: connection.sourcePort
|
||||
)
|
||||
tableQueue.async(flags: .barrier) {
|
||||
self.table[key] = connection
|
||||
}
|
||||
|
||||
self.table[key] = connection
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@ import Network
|
||||
import os.log
|
||||
|
||||
class ProxyVpnService {
|
||||
private let queue: DispatchQueue = DispatchQueue(label: "ProxyPin.ProxyVpnService")
|
||||
|
||||
private var packetFlow: NEPacketTunnelFlow
|
||||
private var connectionHandler: ConnectionHandler
|
||||
private var socketIOService: SocketIOService
|
||||
private var isRunning = true;
|
||||
|
||||
init(packetFlow: NEPacketTunnelFlow, proxyAddress: Network.NWEndpoint?) {
|
||||
init(packetFlow: NEPacketTunnelFlow, proxyAddress: Network.NWEndpoint?) {
|
||||
self.packetFlow = packetFlow
|
||||
self.socketIOService = SocketIOService(clientPacketWriter: packetFlow)
|
||||
let manager = ConnectionManager()
|
||||
@@ -32,20 +32,25 @@ class ProxyVpnService {
|
||||
A stopped interface should never start again. Create a new interface instead.
|
||||
*/
|
||||
func start() {
|
||||
isRunning = true;
|
||||
self.readPackets()
|
||||
}
|
||||
|
||||
func stop() {
|
||||
isRunning = false;
|
||||
self.socketIOService.stop()
|
||||
queue.suspend()
|
||||
}
|
||||
|
||||
func readPackets() -> Void {
|
||||
if (!isRunning) {
|
||||
return
|
||||
}
|
||||
|
||||
self.packetFlow.readPackets { (packets, protocols) in
|
||||
|
||||
|
||||
// os_log("Read %d packets", packets.count)
|
||||
for (i, packet) in packets.enumerated() {
|
||||
self.connectionHandler.handlePacket(packet: packet, version: protocols[i])
|
||||
self.connectionHandler.handlePacket(packet: packet, version: protocols[i])
|
||||
}
|
||||
self.readPackets()
|
||||
}
|
||||
|
||||
58
ios/ProxyPin/vpn/ping/GBPing.h
Normal file
58
ios/ProxyPin/vpn/ping/GBPing.h
Normal file
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// GBPing.h
|
||||
// GBPing
|
||||
//
|
||||
// Created by Luka Mirosevic on 05/11/2012.
|
||||
// Copyright (c) 2012 Goonbee. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "GBPingSummary.h"
|
||||
|
||||
@class GBPingSummary;
|
||||
@protocol GBPingDelegate;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void(^StartupCallback)(BOOL success, NSError * _Nullable error);
|
||||
|
||||
@interface GBPing : NSObject
|
||||
|
||||
@property (weak, nonatomic, nullable) id<GBPingDelegate> delegate;
|
||||
|
||||
@property (copy, nonatomic, nullable) NSString *host;
|
||||
@property (assign, atomic) NSTimeInterval pingPeriod;
|
||||
@property (assign, atomic) NSTimeInterval timeout;
|
||||
@property (assign, atomic) NSUInteger payloadSize;
|
||||
@property (assign, atomic) NSUInteger ttl;
|
||||
@property (assign, atomic) NSUInteger count;
|
||||
@property (assign, atomic, readonly) BOOL isPinging;
|
||||
@property (assign, atomic, readonly) BOOL isReady;
|
||||
@property (assign, atomic) BOOL useIpv4;
|
||||
@property (assign, atomic) BOOL useIpv6;
|
||||
|
||||
@property (assign, atomic) BOOL debug;
|
||||
|
||||
-(void)setupWithBlock:(StartupCallback)callback;
|
||||
-(void)startPinging;
|
||||
-(void)stop;
|
||||
|
||||
@end
|
||||
|
||||
@protocol GBPingDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
-(void)ping:(GBPing *)pinger didFinishWithTime:(NSTimeInterval)time;
|
||||
-(void)ping:(GBPing *)pinger didFailWithError:(NSError *)error;
|
||||
|
||||
-(void)ping:(GBPing *)pinger didSendPingWithSummary:(GBPingSummary *)summary;
|
||||
-(void)ping:(GBPing *)pinger didFailToSendPingWithSummary:(GBPingSummary *)summary error:(NSError *)error;
|
||||
-(void)ping:(GBPing *)pinger didTimeoutWithSummary:(GBPingSummary *)summary;
|
||||
-(void)ping:(GBPing *)pinger didReceiveReplyWithSummary:(GBPingSummary *)summary;
|
||||
-(void)ping:(GBPing *)pinger didReceiveUnexpectedReplyWithSummary:(GBPingSummary *)summary;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
1000
ios/ProxyPin/vpn/ping/GBPing.m
Normal file
1000
ios/ProxyPin/vpn/ping/GBPing.m
Normal file
File diff suppressed because it is too large
Load Diff
95
ios/ProxyPin/vpn/ping/GBPingHelper.swift
Normal file
95
ios/ProxyPin/vpn/ping/GBPingHelper.swift
Normal file
@@ -0,0 +1,95 @@
|
||||
//
|
||||
// GBPingHelper.swift
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public typealias Handler = ((_ response: [String: Any]) -> Void)
|
||||
|
||||
public class GBPingHelper: NSObject {
|
||||
private var ping: GBPing?
|
||||
private let delegate = PingDelegate()
|
||||
|
||||
func start(withHost host: String, ipv4: Bool, ipv6: Bool, count: UInt, interval: TimeInterval, timeout: TimeInterval, ttl: UInt, handler: @escaping Handler) {
|
||||
ping?.stop()
|
||||
ping = GBPing()
|
||||
guard let ping = ping else {
|
||||
return
|
||||
}
|
||||
ping.host = host
|
||||
ping.useIpv4 = ipv4
|
||||
ping.useIpv6 = ipv6
|
||||
ping.count = count
|
||||
ping.pingPeriod = interval
|
||||
ping.timeout = timeout
|
||||
if ttl > 0 {
|
||||
ping.ttl = ttl
|
||||
}
|
||||
|
||||
delegate.handler = handler
|
||||
ping.delegate = delegate
|
||||
ping.setup { success, err in
|
||||
if let err = err as NSError? {
|
||||
if err.domain == kCFErrorDomainCFNetwork as String {
|
||||
handler(["error": "UnknownHost"])
|
||||
} else {
|
||||
handler(["error": "UnknownError"])
|
||||
}
|
||||
return
|
||||
}
|
||||
if success {
|
||||
self.delegate.transmitted = 0
|
||||
self.delegate.received = 0
|
||||
ping.startPinging()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stop() {
|
||||
ping?.stop()
|
||||
}
|
||||
}
|
||||
|
||||
private class PingDelegate: NSObject, GBPingDelegate {
|
||||
public var handler: Handler?
|
||||
public var transmitted = 0
|
||||
public var received = 0
|
||||
|
||||
func handle(_ summary: GBPingSummary, error: String? = nil) {
|
||||
guard let handler = handler else {
|
||||
return
|
||||
}
|
||||
var ret: [String: Any] = [:]
|
||||
ret["seq"] = summary.sequenceNumber
|
||||
ret["host"] = summary.host
|
||||
ret["ip"] = summary.ip
|
||||
ret["ttl"] = summary.ttl
|
||||
ret["time"] = summary.rtt
|
||||
ret["error"] = error
|
||||
handler(ret)
|
||||
}
|
||||
|
||||
func ping(_ pinger: GBPing, didSendPingWith summary: GBPingSummary) {
|
||||
transmitted += 1
|
||||
}
|
||||
|
||||
func ping(_ pinger: GBPing, didTimeoutWith summary: GBPingSummary) {
|
||||
handle(summary, error: "RequestTimedOut")
|
||||
}
|
||||
|
||||
func ping(_ pinger: GBPing, didReceiveReplyWith summary: GBPingSummary) {
|
||||
received += 1
|
||||
handle(summary)
|
||||
}
|
||||
|
||||
func ping(_ pinger: GBPing, didFinishWithTime time: TimeInterval) {
|
||||
guard let handler = handler else {
|
||||
return
|
||||
}
|
||||
var ret: [String: Any] = [:]
|
||||
ret["time"] = time
|
||||
ret["received"] = received
|
||||
ret["transmitted"] = transmitted
|
||||
handler(ret)
|
||||
}
|
||||
}
|
||||
29
ios/ProxyPin/vpn/ping/GBPingSummary.h
Normal file
29
ios/ProxyPin/vpn/ping/GBPingSummary.h
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// GBPingSummary.h
|
||||
// GBPing
|
||||
//
|
||||
// Created by Luka Mirosevic on 05/11/2012.
|
||||
// Copyright (c) 2012 Goonbee. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface GBPingSummary : NSObject <NSCopying>
|
||||
|
||||
typedef enum {
|
||||
GBPingStatusPending,
|
||||
GBPingStatusSuccess,
|
||||
GBPingStatusFail,
|
||||
} GBPingStatus;
|
||||
|
||||
@property (assign, nonatomic) NSUInteger sequenceNumber;
|
||||
@property (assign, nonatomic) NSUInteger payloadSize;
|
||||
@property (assign, nonatomic) NSUInteger ttl;
|
||||
@property (strong, nonatomic, nullable) NSString *host;
|
||||
@property (strong, nonatomic, nullable) NSString *ip;
|
||||
@property (strong, nonatomic, nullable) NSDate *sendDate;
|
||||
@property (strong, nonatomic, nullable) NSDate *receiveDate;
|
||||
@property (assign, nonatomic) NSTimeInterval rtt;
|
||||
@property (assign, nonatomic) GBPingStatus status;
|
||||
|
||||
@end
|
||||
68
ios/ProxyPin/vpn/ping/GBPingSummary.m
Normal file
68
ios/ProxyPin/vpn/ping/GBPingSummary.m
Normal file
@@ -0,0 +1,68 @@
|
||||
//
|
||||
// GBPingSummary.m
|
||||
// GBPing
|
||||
//
|
||||
// Created by Luka Mirosevic on 05/11/2012.
|
||||
// Copyright (c) 2012 Goonbee. All rights reserved.
|
||||
//
|
||||
|
||||
#import "GBPingSummary.h"
|
||||
|
||||
@implementation GBPingSummary
|
||||
|
||||
#pragma mark - custom acc
|
||||
|
||||
-(void)setHost:(NSString *)host {
|
||||
_host = host;
|
||||
}
|
||||
|
||||
-(NSTimeInterval)rtt {
|
||||
if (self.sendDate) {
|
||||
return [self.receiveDate timeIntervalSinceDate:self.sendDate];
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - copying
|
||||
|
||||
-(id)copyWithZone:(NSZone *)zone {
|
||||
GBPingSummary *copy = [[[self class] allocWithZone:zone] init];
|
||||
|
||||
copy.sequenceNumber = self.sequenceNumber;
|
||||
copy.payloadSize = self.payloadSize;
|
||||
copy.ttl = self.ttl;
|
||||
copy.host = [self.host copy];
|
||||
copy.ip = [self.ip copy];
|
||||
copy.sendDate = [self.sendDate copy];
|
||||
copy.receiveDate = [self.receiveDate copy];
|
||||
copy.status = self.status;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
#pragma mark - memory
|
||||
|
||||
-(id)init {
|
||||
if (self = [super init]) {
|
||||
self.status = GBPingStatusPending;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)dealloc {
|
||||
self.host = nil;
|
||||
self.ip = nil;
|
||||
self.sendDate = nil;
|
||||
self.receiveDate = nil;
|
||||
}
|
||||
|
||||
#pragma mark - description
|
||||
|
||||
-(NSString *)description {
|
||||
return [NSString stringWithFormat:@"host: %@, ip:%@, seq: %lu, status: %d, ttl: %lu, payloadSize: %lu, sendDate: %@, receiveDate: %@, rtt: %f", self.host, self.ip, (unsigned long)self.sequenceNumber, self.status, (unsigned long)self.ttl, (unsigned long)self.payloadSize, self.sendDate, self.receiveDate, self.rtt];
|
||||
}
|
||||
|
||||
@end
|
||||
80
ios/ProxyPin/vpn/ping/ICMPHeader.h
Normal file
80
ios/ProxyPin/vpn/ping/ICMPHeader.h
Normal file
@@ -0,0 +1,80 @@
|
||||
//
|
||||
// ICMPHeader.h
|
||||
// GBPing
|
||||
//
|
||||
// Created by Luka Mirosevic on 15/11/2012.
|
||||
// Copyright (c) 2012 Goonbee. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef GBPing_ICMPHeader_h
|
||||
#define GBPing_ICMPHeader_h
|
||||
|
||||
#include <AssertMacros.h>
|
||||
|
||||
#pragma mark - IP and ICMP On-The-Wire Format
|
||||
|
||||
// The following declarations specify the structure of ping packets on the wire.
|
||||
|
||||
// IP header structure:
|
||||
|
||||
struct IPHeader {
|
||||
uint8_t versionAndHeaderLength;
|
||||
uint8_t differentiatedServices;
|
||||
uint16_t totalLength;
|
||||
uint16_t identification;
|
||||
uint16_t flagsAndFragmentOffset;
|
||||
uint8_t timeToLive;
|
||||
uint8_t protocol;
|
||||
uint16_t headerChecksum;
|
||||
uint8_t sourceAddress[4];
|
||||
uint8_t destinationAddress[4];
|
||||
// options...
|
||||
// data...
|
||||
};
|
||||
typedef struct IPHeader IPHeader;
|
||||
|
||||
__Check_Compile_Time(sizeof(IPHeader) == 20);
|
||||
__Check_Compile_Time(offsetof(IPHeader, versionAndHeaderLength) == 0);
|
||||
__Check_Compile_Time(offsetof(IPHeader, differentiatedServices) == 1);
|
||||
__Check_Compile_Time(offsetof(IPHeader, totalLength) == 2);
|
||||
__Check_Compile_Time(offsetof(IPHeader, identification) == 4);
|
||||
__Check_Compile_Time(offsetof(IPHeader, flagsAndFragmentOffset) == 6);
|
||||
__Check_Compile_Time(offsetof(IPHeader, timeToLive) == 8);
|
||||
__Check_Compile_Time(offsetof(IPHeader, protocol) == 9);
|
||||
__Check_Compile_Time(offsetof(IPHeader, headerChecksum) == 10);
|
||||
__Check_Compile_Time(offsetof(IPHeader, sourceAddress) == 12);
|
||||
__Check_Compile_Time(offsetof(IPHeader, destinationAddress) == 16);
|
||||
|
||||
// ICMP type and code combinations:
|
||||
|
||||
enum {
|
||||
kICMPv4TypeEchoRequest = 8,
|
||||
kICMPv4TypeEchoReply = 0
|
||||
};
|
||||
|
||||
enum {
|
||||
kICMPv6TypeEchoRequest = 128,
|
||||
kICMPv6TypeEchoReply = 129
|
||||
};
|
||||
|
||||
// ICMP header structure:
|
||||
|
||||
struct ICMPHeader {
|
||||
uint8_t type;
|
||||
uint8_t code;
|
||||
uint16_t checksum;
|
||||
uint16_t identifier;
|
||||
uint16_t sequenceNumber;
|
||||
// data...
|
||||
};
|
||||
typedef struct ICMPHeader ICMPHeader;
|
||||
|
||||
__Check_Compile_Time(sizeof(ICMPHeader) == 8);
|
||||
__Check_Compile_Time(offsetof(ICMPHeader, type) == 0);
|
||||
__Check_Compile_Time(offsetof(ICMPHeader, code) == 1);
|
||||
__Check_Compile_Time(offsetof(ICMPHeader, checksum) == 2);
|
||||
__Check_Compile_Time(offsetof(ICMPHeader, identifier) == 4);
|
||||
__Check_Compile_Time(offsetof(ICMPHeader, sequenceNumber) == 6);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -9,7 +9,6 @@ import NetworkExtension
|
||||
|
||||
class ClientPacketWriter: NSObject {
|
||||
private var packetFlow: NEPacketTunnelFlow
|
||||
private let packetQueue = DispatchQueue(label: "packetQueue", attributes: .concurrent)
|
||||
private var isShutdown = false
|
||||
|
||||
init(packetFlow: NEPacketTunnelFlow) {
|
||||
@@ -17,17 +16,11 @@ class ClientPacketWriter: NSObject {
|
||||
}
|
||||
|
||||
func write(data: Data) {
|
||||
if !self.isShutdown {
|
||||
packetQueue.async {
|
||||
self.packetFlow.writePackets([data], withProtocols: [NSNumber(value: AF_INET)])
|
||||
}
|
||||
}
|
||||
self.packetFlow.writePackets([data], withProtocols: [NSNumber(value: AF_INET)])
|
||||
}
|
||||
|
||||
func shutdown() {
|
||||
packetQueue.async(flags: .barrier) {
|
||||
self.isShutdown = true
|
||||
}
|
||||
self.isShutdown = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,29 +28,35 @@ class SocketIOService {
|
||||
queue.async(flags: .barrier) {
|
||||
self.shutdown = true
|
||||
}
|
||||
queue.suspend()
|
||||
// queue.suspend()
|
||||
}
|
||||
|
||||
//从connection接受数据 写到client
|
||||
public func registerSession(connection: Connection) {
|
||||
|
||||
connection.channel!.stateUpdateHandler = { state in
|
||||
// os_log("Connection %{public}@ state changed to %{public}@", log: OSLog.default, type: .default, connection.description, String(describing: state))
|
||||
switch state {
|
||||
|
||||
case .ready:
|
||||
connection.isConnected = true
|
||||
os_log("Connected to %{public}@ on receiveMessage", log: OSLog.default, type: .default, connection.description)
|
||||
|
||||
//接受远程服务器的数据
|
||||
connection.sendToDestination()
|
||||
self.receiveMessage(connection: connection)
|
||||
case .cancelled:
|
||||
connection.isConnected = false
|
||||
// os_log("Connection cancelled", log: OSLog.default, type: .default)
|
||||
os_log("Connection cancelled %{public}@", log: OSLog.default, type: .default, connection.description)
|
||||
connection.closeConnection()
|
||||
self.sendFin(connection: connection)
|
||||
case .failed(let error):
|
||||
connection.isConnected = false
|
||||
os_log("Failed to connect: %{public}@", log: OSLog.default, type: .error, error.localizedDescription)
|
||||
os_log("Failed to connect: %{public}@ %{public}@", log: OSLog.default, type: .error,connection.description, error.localizedDescription)
|
||||
connection.closeConnection()
|
||||
self.sendFin(connection: connection)
|
||||
default:
|
||||
os_log("Connection %{public}@ entered unhandled state: %{public}@", log: OSLog.default, type: .default, connection.description, String(describing: state))
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -84,14 +90,14 @@ class SocketIOService {
|
||||
return
|
||||
}
|
||||
|
||||
queue.async {
|
||||
guard let channel = connection.channel else {
|
||||
os_log("Invalid channel type", log: OSLog.default, type: .error)
|
||||
return
|
||||
}
|
||||
|
||||
channel.receive(minimumIncompleteLength: 0, maximumLength: Self.maxReceiveBufferSize) { (data, context, isComplete, error) in
|
||||
// os_log("Received TCP data packet %{public}@ length %d", log: OSLog.default, type: .default, connection.description, data?.count ?? 0)
|
||||
guard let channel = connection.channel else {
|
||||
os_log("Invalid channel type", log: OSLog.default, type: .error)
|
||||
return
|
||||
}
|
||||
|
||||
channel.receive(minimumIncompleteLength: 1, maximumLength: Self.maxReceiveBufferSize) { (data, context, isComplete, error) in
|
||||
self.queue.async(flags: .barrier) {
|
||||
os_log("Received TCP data packet %{public}@ length %d", log: OSLog.default, type: .default, connection.description, data?.count ?? 0)
|
||||
if let error = error {
|
||||
os_log("Failed to read from TCP socket: %@", log: OSLog.default, type: .error, error as CVarArg)
|
||||
self.sendFin(connection: connection)
|
||||
@@ -109,7 +115,6 @@ class SocketIOService {
|
||||
self.receiveMessage(connection: connection)
|
||||
|
||||
if (isComplete) {
|
||||
self.sendFin(connection: connection)
|
||||
connection.isAbortingConnection = true
|
||||
return
|
||||
}
|
||||
@@ -158,6 +163,10 @@ class SocketIOService {
|
||||
}
|
||||
|
||||
private func sendFin(connection: Connection) {
|
||||
if (connection.nwProtocol != .TCP) {
|
||||
return
|
||||
}
|
||||
|
||||
guard let ipHeader = connection.lastIpHeader, let tcpHeader = connection.lastTcpHeader else {
|
||||
os_log("Invalid ipHeader or tcpHeader", log: OSLog.default, type: .error)
|
||||
return
|
||||
@@ -177,13 +186,14 @@ class SocketIOService {
|
||||
}
|
||||
|
||||
func readUDP(connection: Connection) {
|
||||
queue.async {
|
||||
guard let channel = connection.channel else {
|
||||
os_log("Invalid channel type", log: OSLog.default, type: .error)
|
||||
return
|
||||
}
|
||||
|
||||
guard let channel = connection.channel else {
|
||||
os_log("Invalid channel type", log: OSLog.default, type: .error)
|
||||
return
|
||||
}
|
||||
|
||||
channel.receive(minimumIncompleteLength: 1, maximumLength: 4196) { (data, context, isComplete, error) in
|
||||
channel.receive(minimumIncompleteLength: 1, maximumLength: 65507) { (data, context, isComplete, error) in
|
||||
self.queue.async(flags: .barrier) {
|
||||
if let error = error {
|
||||
os_log("Failed to read from UDP socket: %@", log: OSLog.default, type: .error, error as CVarArg)
|
||||
connection.isAbortingConnection = true
|
||||
@@ -196,13 +206,16 @@ class SocketIOService {
|
||||
return
|
||||
}
|
||||
|
||||
guard let ipHeader = connection.lastIpHeader, let udpHeader = connection.lastUdpHeader else {
|
||||
os_log("Missing IP or UDP header for connection %{public}@", log: OSLog.default, type: .error, connection.description)
|
||||
return
|
||||
}
|
||||
|
||||
let packetData = UDPPacketFactory.createResponsePacket(
|
||||
ip: connection.lastIpHeader!,
|
||||
udp: connection.lastUdpHeader!,
|
||||
ip: ipHeader,
|
||||
udp: udpHeader,
|
||||
packetData: data
|
||||
)
|
||||
// os_log("Sending UDP data packet to client", log: OSLog.default, type: .default)
|
||||
|
||||
self.clientPacketWriter.writePackets([packetData], withProtocols: [NSNumber(value: AF_INET)])
|
||||
|
||||
@@ -210,5 +223,5 @@ class SocketIOService {
|
||||
self.receiveMessage(connection: connection)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ struct UDPHeader {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class UDPPacketFactory {
|
||||
static let UDP_HEADER_LENGTH = 8
|
||||
|
||||
@@ -40,15 +39,17 @@ class UDPPacketFactory {
|
||||
return UDPHeader(sourcePort: srcPort, destinationPort: destPort, length: length, checksum: checksum)
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
static func createResponsePacket(ip: IP4Header, udp: UDPHeader, packetData: Data?) -> Data {
|
||||
var udpLen = 8
|
||||
if let packetData = packetData {
|
||||
udpLen += packetData.count
|
||||
}
|
||||
|
||||
let srcPort = udp.destinationPort
|
||||
let destPort = udp.sourcePort
|
||||
|
||||
|
||||
let ipHeader = ip.copy()
|
||||
let srcIp = ip.destinationIP
|
||||
let destIp = ip.sourceIP
|
||||
@@ -63,15 +64,13 @@ class UDPPacketFactory {
|
||||
ipHeader.totalLength = UInt16(totalLength)
|
||||
|
||||
var ipData = ipHeader.toBytes()
|
||||
|
||||
|
||||
// clear IP checksum
|
||||
ipData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) in
|
||||
bytes[10] = 0
|
||||
bytes[11] = 0
|
||||
}
|
||||
|
||||
//os_log("Create UDP response packet from %{public}@:%{public}d to %{public}@:%{public}d totalLength:%{public}d", log: OSLog.default, type: .default, PacketUtil.intToIPAddress(srcIp), srcPort, PacketUtil.intToIPAddress(destIp), destPort, totalLength)
|
||||
|
||||
// calculate checksum for IP header
|
||||
let ipChecksum = PacketUtil.calculateChecksum(data: ipData, offset: 0, length: ipData.count)
|
||||
|
||||
@@ -89,24 +88,16 @@ class UDPPacketFactory {
|
||||
// copy UDP header to buffer
|
||||
buffer.append(contentsOf: srcPort.bytes)
|
||||
buffer.append(contentsOf: destPort.bytes)
|
||||
|
||||
buffer.append(contentsOf: UInt16(udpLen).bytes)
|
||||
|
||||
let checksum: UInt16 = 0
|
||||
buffer.append(contentsOf: checksum.bytes)
|
||||
// 计算UDP校验和
|
||||
let udpChecksum: UInt16 = 0
|
||||
buffer.append(contentsOf: udpChecksum.bytes)
|
||||
|
||||
if let packetData = packetData {
|
||||
buffer.append(packetData)
|
||||
buffer.append(packetData)
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
//打印数据包
|
||||
public static func printPacket(data: Data) {
|
||||
guard let udpHeader = createUDPHeader(from: data) else {
|
||||
return
|
||||
}
|
||||
os_log("UDP Header: sourcePort: %{public}d, destinationPort: %{public}d, length: %{public}d, checksum: %{public}d", log: OSLog.default, type: .default, udpHeader.sourcePort, udpHeader.destinationPort, udpHeader.length, udpHeader.checksum)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 54;
|
||||
objectVersion = 55;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
@@ -25,6 +25,9 @@
|
||||
9B70772D2A5718FB00F184A9 /* AudioManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B70772C2A5718FB00F184A9 /* AudioManager.swift */; };
|
||||
9B7077362A5728B900F184A9 /* silence.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 9B7077352A5728B900F184A9 /* silence.mp3 */; };
|
||||
9B90F5802C183CDE007D7A81 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9B90F5822C183CDE007D7A81 /* InfoPlist.strings */; };
|
||||
9BAB4FC02DE75CFE0093BFBA /* GBPing.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BAB4FBC2DE75CFE0093BFBA /* GBPing.m */; };
|
||||
9BAB4FC12DE75CFE0093BFBA /* GBPingSummary.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BAB4FBE2DE75CFE0093BFBA /* GBPingSummary.m */; };
|
||||
9BAB4FC32DE75D220093BFBA /* GBPingHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BAB4FC22DE75D220093BFBA /* GBPingHelper.swift */; };
|
||||
9BC4B8CC2B4B48710047DBDD /* PictureInPictureManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BC4B8CB2B4B48710047DBDD /* PictureInPictureManager.swift */; };
|
||||
9BCA28662C9772DD00C2B46C /* ConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BCA28652C9772DD00C2B46C /* ConnectionHandler.swift */; };
|
||||
9BCA286A2C97748100C2B46C /* IP4Header.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BCA28692C97748100C2B46C /* IP4Header.swift */; };
|
||||
@@ -124,6 +127,13 @@
|
||||
9B90F57D2C183C7E007D7A81 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/LaunchScreen.strings"; sourceTree = "<group>"; };
|
||||
9B90F5812C183CDE007D7A81 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
9B90F5832C183CE0007D7A81 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
9BAB4FBB2DE75CFE0093BFBA /* GBPing.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GBPing.h; sourceTree = "<group>"; };
|
||||
9BAB4FBC2DE75CFE0093BFBA /* GBPing.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GBPing.m; sourceTree = "<group>"; };
|
||||
9BAB4FBD2DE75CFE0093BFBA /* GBPingSummary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GBPingSummary.h; sourceTree = "<group>"; };
|
||||
9BAB4FBE2DE75CFE0093BFBA /* GBPingSummary.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GBPingSummary.m; sourceTree = "<group>"; };
|
||||
9BAB4FBF2DE75CFE0093BFBA /* ICMPHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ICMPHeader.h; sourceTree = "<group>"; };
|
||||
9BAB4FC22DE75D220093BFBA /* GBPingHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GBPingHelper.swift; sourceTree = "<group>"; };
|
||||
9BAB4FC42DE75E9A0093BFBA /* ProxyPin-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ProxyPin-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
9BC4B8CB2B4B48710047DBDD /* PictureInPictureManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PictureInPictureManager.swift; sourceTree = "<group>"; };
|
||||
9BCA28652C9772DD00C2B46C /* ConnectionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionHandler.swift; sourceTree = "<group>"; };
|
||||
9BCA28692C97748100C2B46C /* IP4Header.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IP4Header.swift; sourceTree = "<group>"; };
|
||||
@@ -266,10 +276,24 @@
|
||||
9B0912242A54593A001108B7 /* PacketTunnelProvider.swift */,
|
||||
9B0912262A54593A001108B7 /* Info.plist */,
|
||||
9B0912272A54593A001108B7 /* ProxyPin.entitlements */,
|
||||
9BAB4FC42DE75E9A0093BFBA /* ProxyPin-Bridging-Header.h */,
|
||||
);
|
||||
path = ProxyPin;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9BAB4FB12DE74F570093BFBA /* ping */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9BAB4FC22DE75D220093BFBA /* GBPingHelper.swift */,
|
||||
9BAB4FBB2DE75CFE0093BFBA /* GBPing.h */,
|
||||
9BAB4FBC2DE75CFE0093BFBA /* GBPing.m */,
|
||||
9BAB4FBD2DE75CFE0093BFBA /* GBPingSummary.h */,
|
||||
9BAB4FBE2DE75CFE0093BFBA /* GBPingSummary.m */,
|
||||
9BAB4FBF2DE75CFE0093BFBA /* ICMPHeader.h */,
|
||||
);
|
||||
path = ping;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9BC4B8D12B4C19ED0047DBDD /* pip */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -283,6 +307,7 @@
|
||||
9BCA28642C97729000C2B46C /* vpn */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9BAB4FB12DE74F570093BFBA /* ping */,
|
||||
9BCA287B2C989A8700C2B46C /* socket */,
|
||||
9BCA28762C98901800C2B46C /* utils */,
|
||||
9BCA28672C97746200C2B46C /* transport */,
|
||||
@@ -420,6 +445,7 @@
|
||||
};
|
||||
9B09121F2A54593A001108B7 = {
|
||||
CreatedOnToolsVersion = 14.2;
|
||||
LastSwiftMigration = 1630;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -484,10 +510,14 @@
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||
@@ -539,10 +569,14 @@
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||
@@ -625,9 +659,12 @@
|
||||
9BCA288C2C995B3700C2B46C /* UDPHeader.swift in Sources */,
|
||||
9BCA28732C988E9D00C2B46C /* Packet.swift in Sources */,
|
||||
9BCA288A2C98C82000C2B46C /* SocketIOService.swift in Sources */,
|
||||
9BAB4FC02DE75CFE0093BFBA /* GBPing.m in Sources */,
|
||||
9BAB4FC12DE75CFE0093BFBA /* GBPingSummary.m in Sources */,
|
||||
9BCA28852C98C6B300C2B46C /* QueueFactory.swift in Sources */,
|
||||
9BCA286A2C97748100C2B46C /* IP4Header.swift in Sources */,
|
||||
9BCA287A2C989A7200C2B46C /* Connection.swift in Sources */,
|
||||
9BAB4FC32DE75D220093BFBA /* GBPingHelper.swift in Sources */,
|
||||
9BCA28752C988EC400C2B46C /* TransportHeader.swift in Sources */,
|
||||
9BCA287F2C989AF300C2B46C /* NWProtocol.swift in Sources */,
|
||||
9BCA286F2C977E4C00C2B46C /* TCPPacketFactory.swift in Sources */,
|
||||
@@ -971,6 +1008,7 @@
|
||||
buildSettings = {
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
@@ -998,6 +1036,7 @@
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "ProxyPin/ProxyPin-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@@ -1009,6 +1048,7 @@
|
||||
buildSettings = {
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
@@ -1034,6 +1074,7 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "ProxyPin/ProxyPin-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
@@ -1044,6 +1085,7 @@
|
||||
buildSettings = {
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
@@ -1069,6 +1111,7 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "ProxyPin/ProxyPin-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
|
||||
@@ -74,5 +74,7 @@
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSLocalNetworkUsageDescription</key>
|
||||
<string>Remote Device Connect</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -48,8 +48,13 @@ class VpnManager{
|
||||
if let manager = manager {
|
||||
self.observerAdded = true
|
||||
NotificationCenter.default.addObserver(forName: NSNotification.Name.NEVPNStatusDidChange, object: manager.connection, queue: OperationQueue.main, using: { [unowned self] (notification) -> Void in
|
||||
|
||||
self.updateVPNStatus(manager)
|
||||
})
|
||||
|
||||
if (manager.connection.status == .invalid || manager.connection.status == .disconnected){
|
||||
print("VPN断开: \(String(describing: manager.debugDescription))")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@
|
||||
"pullConfigFail": "Failed to pull configuration, please check the network connection",
|
||||
"sync": "Sync",
|
||||
"invalidQRCode": "Unrecognized QR code",
|
||||
"remoteConnectFail": "connection failed,Please check if it is allowed on the same LAN and firewall",
|
||||
"remoteConnectFail": "Connection failed,Please check if it is allowed on the same LAN and firewall",
|
||||
"remoteConnectSuccessTips": "Your phone needs to enable packet capture in order to capture requests",
|
||||
|
||||
"windowMode": "Window Mode",
|
||||
|
||||
@@ -105,13 +105,13 @@ class CustomToast extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
void show(BuildContext context) {
|
||||
void show(BuildContext context, {Alignment alignment = Alignment.bottomLeft}) {
|
||||
toastification.show(
|
||||
context: context,
|
||||
title: Text(message),
|
||||
icon: icon == null ? null : Icon(icon),
|
||||
type: type._toastificationType,
|
||||
alignment: Alignment.bottomLeft,
|
||||
alignment: alignment,
|
||||
autoCloseDuration: duration,
|
||||
style: ToastificationStyle.flat,
|
||||
pauseOnHover: true,
|
||||
|
||||
@@ -28,6 +28,7 @@ import 'package:proxypin/network/components/manager/request_rewrite_manager.dart
|
||||
import 'package:proxypin/network/components/manager/script_manager.dart';
|
||||
import 'package:proxypin/network/http/http_client.dart';
|
||||
import 'package:proxypin/network/util/logger.dart';
|
||||
import 'package:proxypin/ui/component/app_dialog.dart';
|
||||
import 'package:proxypin/ui/component/qrcode/qr_scan_view.dart';
|
||||
import 'package:proxypin/ui/component/utils.dart';
|
||||
import 'package:proxypin/ui/component/widgets.dart';
|
||||
@@ -58,7 +59,12 @@ class RemoteModel {
|
||||
|
||||
factory RemoteModel.fromJson(Map<String, dynamic> json) {
|
||||
return RemoteModel(
|
||||
connect: json['connect'], host: json['host'], port: json['port'], os: json['os'], hostname: json['hostname']);
|
||||
connect: json['connect'],
|
||||
host: json['host'],
|
||||
port: json['port'],
|
||||
os: json['os'],
|
||||
hostname: json['hostname'],
|
||||
ipProxy: json['ipProxy'] == true);
|
||||
}
|
||||
|
||||
RemoteModel copyWith({
|
||||
@@ -87,7 +93,7 @@ class RemoteModel {
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {'connect': connect, 'host': host, 'port': port, 'os': os, 'hostname': hostname};
|
||||
return {'connect': connect, 'host': host, 'port': port, 'os': os, 'hostname': hostname, 'ipProxy': ipProxy};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,6 +396,7 @@ class _RemoteDevicePageState extends State<RemoteDevicePage> {
|
||||
Future<bool> doConnect(String host, int port, {bool? ipProxy}) async {
|
||||
if (doConnecting) return false;
|
||||
doConnecting = true;
|
||||
|
||||
try {
|
||||
var response = await HttpClients.get("http://$host:$port/ping", timeout: const Duration(milliseconds: 3000));
|
||||
if (response.bodyAsString == "pong") {
|
||||
@@ -424,11 +431,9 @@ class _RemoteDevicePageState extends State<RemoteDevicePage> {
|
||||
} catch (e) {
|
||||
logger.e(e);
|
||||
if (mounted) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(content: Text(localizations.remoteConnectFail));
|
||||
});
|
||||
if (mounted) {
|
||||
CustomToast.error(localizations.remoteConnectFail).show(context, alignment: Alignment.topCenter);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user