mirror of
https://github.com/wanghongenpin/proxypin.git
synced 2026-05-20 16:15:47 +08:00
ios IP Layer Proxy Beta (#215)
This commit is contained in:
@@ -6,10 +6,12 @@
|
||||
//
|
||||
|
||||
import NetworkExtension
|
||||
|
||||
import Network
|
||||
import os.log
|
||||
|
||||
class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
|
||||
private var proxyVpnService: ProxyVpnService?
|
||||
|
||||
override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
||||
NSLog("startTunnel")
|
||||
|
||||
@@ -17,11 +19,15 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
NSLog("[ERROR] No ProtocolConfiguration Found")
|
||||
exit(EXIT_FAILURE)
|
||||
}
|
||||
let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1")
|
||||
NSLog(conf.debugDescription)
|
||||
//http代理
|
||||
|
||||
let host = conf["proxyHost"] as! String
|
||||
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)
|
||||
NSLog(conf.debugDescription)
|
||||
//http代理
|
||||
let proxySettings = NEProxySettings()
|
||||
proxySettings.httpEnabled = true
|
||||
proxySettings.httpServer = NEProxyServer(address: host, port: proxyPort)
|
||||
@@ -30,26 +36,49 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
proxySettings.matchDomains = [""]
|
||||
|
||||
networkSettings.proxySettings = proxySettings
|
||||
networkSettings.mtu = 1500
|
||||
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.ipv4Settings = ipv4Settings
|
||||
|
||||
setTunnelNetworkSettings(networkSettings) {
|
||||
error in
|
||||
setTunnelNetworkSettings(networkSettings) { error in
|
||||
guard error == nil else {
|
||||
NSLog(error.debugDescription)
|
||||
NSLog("startTunnel Encountered an error setting up the network: \(error.debugDescription)")
|
||||
completionHandler(error)
|
||||
return
|
||||
}
|
||||
completionHandler(nil)
|
||||
|
||||
if (ipProxy){
|
||||
let proxyAddress = Network.NWEndpoint.hostPort(host: NWEndpoint.Host(host), port: NWEndpoint.Port(rawValue: UInt16(proxyPort))!)
|
||||
self.proxyVpnService = ProxyVpnService(packetFlow: self.packetFlow, proxyAddress: proxyAddress)
|
||||
self.proxyVpnService!.start()
|
||||
}
|
||||
completionHandler(nil)
|
||||
}
|
||||
NSLog("startTunnelend")
|
||||
}
|
||||
|
||||
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
||||
proxyVpnService?.stop()
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
|
||||
105
ios/ProxyPin/vpn/Connection.swift
Normal file
105
ios/ProxyPin/vpn/Connection.swift
Normal file
@@ -0,0 +1,105 @@
|
||||
//
|
||||
// Connection.swift
|
||||
// ProxyPin
|
||||
//
|
||||
// Created by wanghongen on 2024/9/17.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
import os.log
|
||||
|
||||
class Connection{
|
||||
var nwProtocol: NWProtocol
|
||||
var sourceIp: UInt32
|
||||
var sourcePort: UInt16
|
||||
var destinationIp: UInt32
|
||||
var destinationPort: UInt16
|
||||
var channel: NWConnection?
|
||||
|
||||
var isInitConnect: Bool = false
|
||||
var isConnected: Bool = false
|
||||
var isClosingConnection: Bool = false
|
||||
var isAbortingConnection: Bool = false
|
||||
var isAckedToFin: Bool = false
|
||||
|
||||
private let connectionCloser: ConnectionManager
|
||||
|
||||
init(nwProtocol: NWProtocol, sourceIp: UInt32, sourcePort: UInt16, destinationIp: UInt32, destinationPort: UInt16, connectionCloser: ConnectionManager) {
|
||||
self.nwProtocol = nwProtocol
|
||||
self.sourceIp = sourceIp
|
||||
self.sourcePort = sourcePort
|
||||
self.destinationIp = destinationIp
|
||||
self.destinationPort = destinationPort
|
||||
self.connectionCloser = connectionCloser
|
||||
}
|
||||
|
||||
//发送缓冲区,用于存储要从vpn客户端发送到目标主机的数据
|
||||
var sendBuffer = Data()
|
||||
|
||||
var hasReceivedLastSegment = false
|
||||
|
||||
//从客户端接收的最后一个数据包
|
||||
var lastIpHeader: IP4Header?
|
||||
var lastTcpHeader: TCPHeader?
|
||||
var lastUdpHeader: UDPHeader?
|
||||
|
||||
var timestampSender = 0
|
||||
var timestampReplyTo = 0
|
||||
|
||||
//从客户端接收的序列
|
||||
var recSequence: UInt32 = 0
|
||||
|
||||
//在tcp选项内的SYN期间由客户端发送
|
||||
var maxSegmentSize = 0
|
||||
|
||||
//跟踪我们发送给客户端的ack,并等待客户端返回ack
|
||||
var sendUnAck: UInt32 = 0
|
||||
|
||||
//发送到客户端的下一个ack
|
||||
var sendNext: UInt32 = 0
|
||||
|
||||
static func getConnectionKey(nwProtocol: NWProtocol, destIp: UInt32, destPort: UInt16, sourceIp: UInt32, sourcePort: UInt16) -> String {
|
||||
let destIpString = PacketUtil.intToIPAddress(destIp)
|
||||
let sourceIpString = PacketUtil.intToIPAddress(sourceIp)
|
||||
return "\(nwProtocol)|\(sourceIpString):\(sourcePort)->\(destIpString):\(destPort)"
|
||||
}
|
||||
|
||||
func closeConnection() {
|
||||
connectionCloser.closeConnection(connection: self)
|
||||
}
|
||||
|
||||
func addSendData(data: Data) {
|
||||
QueueFactory.instance.getQueue().sync {
|
||||
// sendBuffer.append(data)
|
||||
// if (channel?.state != .ready) {
|
||||
// return
|
||||
// }
|
||||
self.sendToDestination(data: data)
|
||||
}
|
||||
}
|
||||
|
||||
//发送到目标服务器的数据
|
||||
func sendToDestination(data: Data) {
|
||||
// QueueFactory.instance.getQueue().sync {
|
||||
// os_log("Sending data to destination key %{public}@", log: OSLog.default, type: .debug, self.description)
|
||||
//发送数据, 数据包大拆分
|
||||
|
||||
|
||||
self.channel?.send(content: data, completion: .contentProcessed { error in
|
||||
if let error = error {
|
||||
os_log("Error sending data to destination %{public}@: %{public}@", log: OSLog.default, type: .error, self.description, error.localizedDescription)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
var description: String {
|
||||
return Connection.getConnectionKey(nwProtocol: nwProtocol, destIp: destinationIp, destPort: destinationPort, sourceIp: sourceIp, sourcePort: sourcePort)
|
||||
}
|
||||
}
|
||||
399
ios/ProxyPin/vpn/ConnectionHandler.swift
Normal file
399
ios/ProxyPin/vpn/ConnectionHandler.swift
Normal file
@@ -0,0 +1,399 @@
|
||||
//
|
||||
// ConnectionHandler.swift
|
||||
// ProxyPin
|
||||
//
|
||||
// Created by wanghongen on 2024/9/16.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import NetworkExtension
|
||||
import os.log
|
||||
|
||||
|
||||
enum ProtocolType: UInt8 {
|
||||
case icmp = 1, tcp = 6, udp = 17
|
||||
}
|
||||
|
||||
/// Handles incoming packets and routes them to the appropriate connection.
|
||||
class ConnectionHandler {
|
||||
private let manager: ConnectionManager
|
||||
private let writer: NEPacketTunnelFlow
|
||||
private let ioService: SocketIOService
|
||||
|
||||
init(manager: ConnectionManager, writer: NEPacketTunnelFlow, ioService: SocketIOService) {
|
||||
self.manager = manager
|
||||
self.writer = writer
|
||||
self.ioService = ioService
|
||||
}
|
||||
|
||||
//Handle unknown raw IP packet data
|
||||
public func handlePacket(packet: Data, version: NSNumber?) {
|
||||
guard let ipHeader = IPPacketFactory.createIP4Header(data: packet) else {
|
||||
os_log("Malformed IP packet", log: OSLog.default, type: .error)
|
||||
return
|
||||
}
|
||||
|
||||
if ipHeader.ipVersion != 4 {
|
||||
os_log("Unsupported IP version: %d", log: OSLog.default, type: .error, ipHeader.ipVersion)
|
||||
return
|
||||
}
|
||||
|
||||
// os_log("Handling packet length:%d, protocolNumber: %d", log: OSLog.default, type: .default, packet.count, ipHeader.protocolNumber)
|
||||
|
||||
var clientPacketData = packet.subdata(in: IPPacketFactory.IP4_HEADER_SIZE..<packet.count)
|
||||
|
||||
switch ipHeader.protocolNumber {
|
||||
case ProtocolType.tcp.rawValue:
|
||||
handleTCPPacket(packet: clientPacketData, ipHeader: ipHeader)
|
||||
case ProtocolType.udp.rawValue:
|
||||
handleUDPPacket(clientPacketData: clientPacketData, ipHeader: ipHeader)
|
||||
case ProtocolType.icmp.rawValue:
|
||||
handleICMPPacket(clientPacketData: &clientPacketData, ipHeader: ipHeader)
|
||||
default:
|
||||
os_log("Unsupported IP protocol: %d", log: OSLog.default, type: .error, ipHeader.protocolNumber)
|
||||
}
|
||||
}
|
||||
|
||||
func synchronized(_ lock: AnyObject, closure: () -> Void) {
|
||||
// objc_sync_enter(lock)
|
||||
closure()
|
||||
// objc_sync_exit(lock)
|
||||
}
|
||||
|
||||
private func handleUDPPacket(clientPacketData: Data, ipHeader: IP4Header) {
|
||||
guard let udpHeader = UDPPacketFactory.createUDPHeader(from: clientPacketData) else {
|
||||
os_log("Malformed UDP packet", log: OSLog.default, type: .error)
|
||||
return
|
||||
}
|
||||
|
||||
var connection = manager.getConnection(
|
||||
nwProtocol: .UDP,
|
||||
ip: ipHeader.destinationIP,
|
||||
port: udpHeader.destinationPort,
|
||||
srcIp: ipHeader.sourceIP,
|
||||
srcPort: udpHeader.sourcePort
|
||||
)
|
||||
|
||||
let newSession = connection == nil
|
||||
if connection == nil {
|
||||
connection = manager.createUDPConnection(
|
||||
ip: ipHeader.destinationIP,
|
||||
port: udpHeader.destinationPort,
|
||||
srcIp: ipHeader.sourceIP,
|
||||
srcPort: udpHeader.sourcePort
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
guard let connection = connection else {
|
||||
os_log("Failed to create UDP connection", log: OSLog.default, type: .error)
|
||||
return
|
||||
}
|
||||
|
||||
synchronized(connection) {
|
||||
// os_log("Received UDP packet", log: OSLog.default, type: .default)
|
||||
if newSession {
|
||||
ioService.registerSession(connection: connection)
|
||||
}
|
||||
|
||||
let payload = clientPacketData.subdata(in: UDPPacketFactory.UDP_HEADER_LENGTH..<clientPacketData.count)
|
||||
|
||||
connection.lastIpHeader = ipHeader
|
||||
connection.lastUdpHeader = udpHeader
|
||||
manager.addClientData(data: payload, connection: connection)
|
||||
}
|
||||
manager.keepSessionAlive(connection: connection)
|
||||
}
|
||||
|
||||
func printByteArray(_ byteArray: Data) {
|
||||
let byteArrayString = byteArray.map { String( format: "0x%02X",$0) }.joined(separator: ",")
|
||||
os_log("Packet data: %{public}@", log: OSLog.default, type: .default, byteArrayString)
|
||||
}
|
||||
|
||||
private func handleTCPPacket(packet: Data, ipHeader: IP4Header) {
|
||||
guard let tcpHeader = TCPPacketFactory.createTCPHeader(data: packet) else {
|
||||
os_log("Malformed TCP packet", log: OSLog.default, type: .error)
|
||||
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)
|
||||
|
||||
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)
|
||||
// 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)
|
||||
|
||||
guard let connection = manager.getConnectionByKey(key: key) else {
|
||||
os_log("Ack for unknown session: %{public}@", log: OSLog.default, type: .default, key)
|
||||
if tcpHeader.isFIN() {
|
||||
sendLastAck(ip: ipHeader, tcp: tcpHeader)
|
||||
} else if !tcpHeader.isRST() {
|
||||
sendRstPacket(ip: ipHeader, tcp: tcpHeader, dataLength: dataLength)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
synchronized(connection) {
|
||||
connection.lastIpHeader = ipHeader
|
||||
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
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
//received the last segment of data from vpn client
|
||||
if tcpHeader.isPSH() {
|
||||
// Tell the NIO thread to immediately send data to the destination
|
||||
pushDataToDestination(connection: connection, tcpHeader: tcpHeader)
|
||||
} else if tcpHeader.isFIN() {
|
||||
//fin from vpn client is the last packet
|
||||
//ack it
|
||||
ackFinAck(ipHeader: ipHeader, tcpHeader: tcpHeader, connection: connection)
|
||||
} else if tcpHeader.isRST() {
|
||||
resetTCPConnection(ip: ipHeader, tcp: tcpHeader)
|
||||
}
|
||||
|
||||
if !connection.isAbortingConnection {
|
||||
manager.keepSessionAlive(connection: connection)
|
||||
}
|
||||
}
|
||||
} else if tcpHeader.isFIN() {
|
||||
os_log("Received FIN packet %{public}@:%d seq:%u", log: OSLog.default, type: .default, PacketUtil.intToIPAddress(destinationIP), destinationPort, tcpHeader.sequenceNumber)
|
||||
//case client sent FIN without ACK
|
||||
guard let connection = manager.getConnection(nwProtocol: .TCP, ip: destinationIP, port: destinationPort, srcIp: sourceIP, srcPort: sourcePort) else {
|
||||
ackFinAck(ipHeader: ipHeader, tcpHeader: tcpHeader, connection: nil)
|
||||
return
|
||||
}
|
||||
|
||||
manager.keepSessionAlive(connection: connection)
|
||||
} else if tcpHeader.isRST() {
|
||||
os_log("Received RST packet %{public}@:%d seq:%u", log: OSLog.default, type: .debug, PacketUtil.intToIPAddress(destinationIP), destinationPort, tcpHeader.sequenceNumber)
|
||||
resetTCPConnection(ip: ipHeader, tcp: tcpHeader)
|
||||
} else {
|
||||
os_log("Unknown TCP flag", log: OSLog.default, type: .error)
|
||||
}
|
||||
}
|
||||
|
||||
//set connection as aborting so that background worker will close it.
|
||||
func resetTCPConnection(ip: IP4Header, tcp: TCPHeader) {
|
||||
let session = manager.getConnection(nwProtocol: .TCP, ip: ip.destinationIP, port: tcp.destinationPort, srcIp: ip.sourceIP, srcPort: tcp.sourcePort)
|
||||
if let session = session {
|
||||
session.isAbortingConnection = true
|
||||
}
|
||||
}
|
||||
|
||||
func ackFinAck(ipHeader: IP4Header, tcpHeader: TCPHeader, connection: Connection?) {
|
||||
let ackNumber = tcpHeader.sequenceNumber + 1
|
||||
let seqNumber = tcpHeader.ackNumber
|
||||
let finAckData = TCPPacketFactory.createFinAckData(ipHeader: ipHeader, tcpHeader: tcpHeader, ackToClient: ackNumber, seqToClient: seqNumber, isFin: true, isAck: true)
|
||||
write(data: finAckData)
|
||||
// os_log("Sent FIN-ACK packet ack# %{public}d, seq# %{public}d", log: OSLog.default, type: .default, ackNumber, seqNumber)
|
||||
if let connection = connection {
|
||||
manager.closeConnection(connection: connection)
|
||||
}
|
||||
}
|
||||
|
||||
func pushDataToDestination(connection: Connection, tcpHeader: TCPHeader) {
|
||||
connection.timestampReplyTo = tcpHeader.timeStampSender
|
||||
connection.timestampSender = Int(Date().timeIntervalSince1970)
|
||||
}
|
||||
|
||||
func sendFinAck(ipHeader: IP4Header, tcpHeader: TCPHeader, connection: Connection) {
|
||||
let ackNumber = tcpHeader.sequenceNumber
|
||||
let seqNumber = tcpHeader.ackNumber
|
||||
let finAckData = TCPPacketFactory.createFinAckData(ipHeader: ipHeader, tcpHeader: tcpHeader, ackToClient: ackNumber, seqToClient: seqNumber, isFin: true, isAck: false)
|
||||
write(data: finAckData)
|
||||
|
||||
connection.sendNext = seqNumber + 1
|
||||
connection.isClosingConnection = false
|
||||
}
|
||||
|
||||
//acknowledge a packet.
|
||||
func acceptAck(tcpHeader: TCPHeader, connection: Connection) {
|
||||
let isCorrupted = PacketUtil.isPacketCorrupted(tcpHeader: tcpHeader)
|
||||
|
||||
if isCorrupted {
|
||||
os_log("Packet is corrupted", log: OSLog.default, type: .error)
|
||||
}
|
||||
|
||||
if (tcpHeader.sequenceNumber > connection.recSequence) {
|
||||
connection.recSequence = tcpHeader.sequenceNumber
|
||||
}
|
||||
|
||||
if tcpHeader.ackNumber >= connection.sendUnAck - 1 || tcpHeader.ackNumber == connection.sendNext {
|
||||
connection.sendUnAck = tcpHeader.ackNumber
|
||||
|
||||
connection.timestampReplyTo = tcpHeader.timeStampSender
|
||||
connection.timestampSender = Int(Date().timeIntervalSince1970)
|
||||
} else {
|
||||
os_log("%{public}@ Not accepting ack# %d, it should be: %d", log: OSLog.default, type: .error, connection.description ,tcpHeader.ackNumber, connection.sendNext)
|
||||
os_log("%{public}@ Previous sendUnAck: %d", log: OSLog.default, type: .error, connection.description, connection.sendUnAck)
|
||||
}
|
||||
}
|
||||
|
||||
func sendAckForDisorder(ipHeader: IP4Header, tcpHeader: TCPHeader, acceptedDataLength: Int) {
|
||||
let ackNumber = tcpHeader.sequenceNumber + UInt32(acceptedDataLength)
|
||||
// os_log("Sent disorder ack, ack# %{public}d", log: OSLog.default, type: .debug, ackNumber)
|
||||
let ackData = TCPPacketFactory.createResponseAckData(ipHeader: ipHeader, tcpHeader: tcpHeader, ackToClient: ackNumber)
|
||||
write(data: ackData)
|
||||
}
|
||||
|
||||
func sendAck(ipHeader: IP4Header, tcpHeader: TCPHeader, acceptedDataLength: Int, connection: Connection) {
|
||||
synchronized(connection) {
|
||||
let ackNumber = (tcpHeader.sequenceNumber + UInt32(acceptedDataLength)) % UInt32.max
|
||||
connection.recSequence = ackNumber
|
||||
let ackData = TCPPacketFactory.createResponseAckData(ipHeader: ipHeader, tcpHeader: tcpHeader, ackToClient: ackNumber)
|
||||
self.write(data: ackData)
|
||||
}
|
||||
|
||||
// os_log("Sent ACK packet ack# %{public}u", log: OSLog.default, type: .default, ackNumber)
|
||||
}
|
||||
|
||||
private func sendLastAck(ip: IP4Header, tcp: TCPHeader) {
|
||||
let data = TCPPacketFactory.createResponseAckData(ipHeader: ip, tcpHeader: tcp, ackToClient: tcp.sequenceNumber + 1)
|
||||
self.write(data: data)
|
||||
os_log("Sent last ACK Packet to client with dest => %{public}@:%{public}d", log: OSLog.default, type: .debug, PacketUtil.intToIPAddress(ip.destinationIP), tcp.destinationPort)
|
||||
}
|
||||
|
||||
private func sendRstPacket(ip: IP4Header, tcp: TCPHeader, dataLength: Int) {
|
||||
let data = TCPPacketFactory.createRstData(ipHeader: ip, tcpHeader: tcp, dataLength: dataLength)
|
||||
self.write(data: data)
|
||||
os_log("Sent RST Packet to client with dest => %{public}@:%{public}d", log: OSLog.default, type: .debug, PacketUtil.intToIPAddress(ip.destinationIP), tcp.destinationPort)
|
||||
}
|
||||
|
||||
//create a new client's session and SYN-ACK packet data to respond to client
|
||||
private func replySynAck(ipHeader: IP4Header, tcpHeader: TCPHeader) -> Void {
|
||||
ipHeader.identification = 0
|
||||
let packet = TCPPacketFactory.createSynAckPacketData(ipHeader: ipHeader, tcpHeader: tcpHeader)
|
||||
|
||||
guard let tcpTransport = packet.transportHeader as? TCPHeader else {
|
||||
os_log("Failed to extract TCP header from packet", log: OSLog.default, type: .error)
|
||||
return
|
||||
}
|
||||
|
||||
let connection = manager.createTCPConnection(
|
||||
ip: ipHeader.destinationIP,
|
||||
port: tcpHeader.destinationPort,
|
||||
srcIp: ipHeader.sourceIP,
|
||||
srcPort: tcpHeader.sourcePort
|
||||
)
|
||||
|
||||
if connection.lastIpHeader != nil {
|
||||
resendAck(connection: connection)
|
||||
return
|
||||
}
|
||||
|
||||
synchronized(connection) {
|
||||
connection.maxSegmentSize = Int(tcpTransport.maxSegmentSize)
|
||||
connection.sendUnAck = tcpTransport.sequenceNumber
|
||||
connection.sendNext = tcpTransport.sequenceNumber + 1
|
||||
|
||||
//client initial sequence has been incremented by 1 and set to ack
|
||||
connection.recSequence = tcpTransport.ackNumber
|
||||
connection.lastIpHeader = ipHeader
|
||||
connection.lastTcpHeader = tcpHeader
|
||||
if connection.isInitConnect {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* resend the last acknowledgment packet to VPN client, e.g. when an unexpected out of order
|
||||
* packet arrives.
|
||||
*/
|
||||
private func resendAck(connection: Connection) {
|
||||
let data = TCPPacketFactory.createResponseAckData(
|
||||
ipHeader: connection.lastIpHeader!,
|
||||
tcpHeader: connection.lastTcpHeader!,
|
||||
ackToClient: connection.recSequence
|
||||
)
|
||||
// os_log("Resending ACK packet %{public}@ ackToClient: %d", log: OSLog.default, type: .default, connection.description, connection.recSequence)
|
||||
self.write(data: data)
|
||||
}
|
||||
|
||||
|
||||
private func write(data: Data) {
|
||||
self.writer.writePackets([data], withProtocols: [NSNumber(value: AF_INET)])
|
||||
}
|
||||
|
||||
private func handleICMPPacket(clientPacketData: inout Data, ipHeader: IP4Header) {
|
||||
guard let requestPacket = ICMPPacketFactory.parseICMPPacket(&clientPacketData) else {
|
||||
os_log("Failed to parse ICMP packet", log: OSLog.default, type: .error)
|
||||
return
|
||||
}
|
||||
|
||||
// os_log("Handling ICMP packet type: %d", log: OSLog.default, type: .default, requestPacket.type)
|
||||
if requestPacket.type == ICMPPacket.DESTINATION_UNREACHABLE_TYPE {
|
||||
// This is a packet from the phone, telling somebody that a destination is unreachable.
|
||||
// Might be caused by issues on our end, but it's unclear what kind of issues. Regardless,
|
||||
// we can't send ICMP messages ourselves or react usefully, so we drop these silently.
|
||||
|
||||
return
|
||||
} else if requestPacket.type != ICMPPacket.ECHO_REQUEST_TYPE {
|
||||
// We only actually support outgoing ping packets. Loudly drop anything else:
|
||||
os_log("Unknown ICMP type: %d", log: OSLog.default, type: .error, requestPacket.type)
|
||||
return
|
||||
}
|
||||
|
||||
QueueFactory.instance.getQueue().async {
|
||||
|
||||
if !self.isReachable(ipAddress: PacketUtil.intToIPAddress(ipHeader.destinationIP)) {
|
||||
os_log("Failed ping, ignoring", log: OSLog.default, type: .default)
|
||||
return
|
||||
}
|
||||
|
||||
let response = ICMPPacketFactory.buildSuccessPacket(requestPacket)
|
||||
|
||||
// Flip the address
|
||||
let destination = ipHeader.destinationIP
|
||||
let source = ipHeader.sourceIP
|
||||
ipHeader.sourceIP = destination
|
||||
ipHeader.destinationIP = source
|
||||
|
||||
let responseData = ICMPPacketFactory.packetToBuffer(ipHeader: ipHeader, packet: response)
|
||||
os_log("Successful ping response", log: OSLog.default, type: .default)
|
||||
self.write(data: responseData)
|
||||
}
|
||||
}
|
||||
|
||||
private func isReachable(ipAddress: String) -> Bool {
|
||||
do {
|
||||
return true
|
||||
// return try InetAddress.getByName(ipAddress).isReachable(timeout: 10000)
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
159
ios/ProxyPin/vpn/ConnectionManager.swift
Normal file
159
ios/ProxyPin/vpn/ConnectionManager.swift
Normal file
@@ -0,0 +1,159 @@
|
||||
//
|
||||
// ConnectionManager.swift
|
||||
// ProxyPin
|
||||
//
|
||||
// Created by wanghongen on 2024/9/16.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
import os.log
|
||||
|
||||
//管理VPN客户端的连接
|
||||
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, 443]
|
||||
|
||||
// private init() {}
|
||||
|
||||
|
||||
func getConnection(nwProtocol: NWProtocol, ip: UInt32, port: UInt16, srcIp: UInt32, srcPort: UInt16) -> Connection? {
|
||||
let key = Connection.getConnectionKey(nwProtocol: nwProtocol, destIp: ip, destPort: port, sourceIp: srcIp, sourcePort: srcPort)
|
||||
return getConnectionByKey(key: key)
|
||||
}
|
||||
|
||||
func getConnectionByKey(key: String) -> Connection? {
|
||||
return tableQueue.sync {
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
func closeConnection(connection: Connection) {
|
||||
closeConnection(
|
||||
nwProtocol: connection.nwProtocol, ip: connection.destinationIp, port: connection.destinationPort,
|
||||
srcIp: connection.sourceIp, srcPort: connection.sourcePort
|
||||
)
|
||||
}
|
||||
|
||||
// 从内存中删除连接,然后关闭套接字。
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//添加来自客户端的数据,该数据稍后将在接收到PSH标志时发送到目的服务器。
|
||||
func addClientData(data: Data, connection: Connection) {
|
||||
guard data.count > 0 else {
|
||||
return
|
||||
}
|
||||
|
||||
connection.addSendData(data: data)
|
||||
}
|
||||
|
||||
//阻止java垃圾收集器收集会话
|
||||
func keepSessionAlive(connection: Connection) {
|
||||
let key = Connection.getConnectionKey(
|
||||
nwProtocol: connection.nwProtocol,
|
||||
destIp: connection.destinationIp,
|
||||
destPort: connection.destinationPort,
|
||||
sourceIp: connection.sourceIp,
|
||||
sourcePort: connection.sourcePort
|
||||
)
|
||||
tableQueue.async(flags: .barrier) {
|
||||
self.table[key] = connection
|
||||
}
|
||||
}
|
||||
}
|
||||
13
ios/ProxyPin/vpn/NWProtocol.swift
Normal file
13
ios/ProxyPin/vpn/NWProtocol.swift
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// NWProtocol.swift
|
||||
// ProxyPin
|
||||
//
|
||||
// Created by wanghongen on 2024/9/17.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
public enum NWProtocol {
|
||||
case TCP,UDP
|
||||
}
|
||||
53
ios/ProxyPin/vpn/ProxyVpnService.swift
Normal file
53
ios/ProxyPin/vpn/ProxyVpnService.swift
Normal file
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// ProxyService.swift
|
||||
// ProxyPin
|
||||
//
|
||||
// Created by wanghongen on 2024/9/17.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import NetworkExtension
|
||||
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
|
||||
|
||||
init(packetFlow: NEPacketTunnelFlow, proxyAddress: Network.NWEndpoint?) {
|
||||
self.packetFlow = packetFlow
|
||||
self.socketIOService = SocketIOService(clientPacketWriter: packetFlow)
|
||||
let manager = ConnectionManager()
|
||||
manager.proxyAddress = proxyAddress
|
||||
self.connectionHandler = ConnectionHandler(manager: manager, writer: packetFlow, ioService: socketIOService)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Start processing packets, this should be called after registering all IP stacks.
|
||||
|
||||
A stopped interface should never start again. Create a new interface instead.
|
||||
*/
|
||||
func start() {
|
||||
self.readPackets()
|
||||
}
|
||||
|
||||
func stop() {
|
||||
self.socketIOService.stop()
|
||||
queue.suspend()
|
||||
}
|
||||
|
||||
func readPackets() -> Void {
|
||||
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.readPackets()
|
||||
}
|
||||
}
|
||||
}
|
||||
29
ios/ProxyPin/vpn/QueueFactory.swift
Normal file
29
ios/ProxyPin/vpn/QueueFactory.swift
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// QueueFactory.swift
|
||||
// ProxyPin
|
||||
//
|
||||
// Created by wanghongen on 2024/9/17.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class QueueFactory {
|
||||
static let instance = QueueFactory()
|
||||
|
||||
private let queue: DispatchQueue
|
||||
|
||||
private init() {
|
||||
queue = DispatchQueue(label: "com.network.ProxyPin.queue")
|
||||
}
|
||||
|
||||
func getQueue() -> DispatchQueue {
|
||||
return queue
|
||||
}
|
||||
|
||||
func executeAsync(block: @escaping () -> Void) {
|
||||
queue.async {
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
33
ios/ProxyPin/vpn/socket/ClientPacketWriter.swift
Normal file
33
ios/ProxyPin/vpn/socket/ClientPacketWriter.swift
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// ClientPacketWriter.swift
|
||||
// ProxyPin
|
||||
//
|
||||
// Created by wanghongen on 2024/9/
|
||||
|
||||
import Foundation
|
||||
import NetworkExtension
|
||||
|
||||
class ClientPacketWriter: NSObject {
|
||||
private var packetFlow: NEPacketTunnelFlow
|
||||
private let packetQueue = DispatchQueue(label: "packetQueue", attributes: .concurrent)
|
||||
private var isShutdown = false
|
||||
|
||||
init(packetFlow: NEPacketTunnelFlow) {
|
||||
self.packetFlow = packetFlow
|
||||
}
|
||||
|
||||
func write(data: Data) {
|
||||
if !self.isShutdown {
|
||||
packetQueue.async {
|
||||
self.packetFlow.writePackets([data], withProtocols: [NSNumber(value: AF_INET)])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func shutdown() {
|
||||
packetQueue.async(flags: .barrier) {
|
||||
self.isShutdown = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
14
ios/ProxyPin/vpn/socket/CloseableConnection.swift
Normal file
14
ios/ProxyPin/vpn/socket/CloseableConnection.swift
Normal file
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// CloseableConnection.swift
|
||||
// ProxyPin
|
||||
//
|
||||
// Created by wanghongen on 2024/9/17.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
protocol CloseableConnection {
|
||||
/// Closes the connection
|
||||
func closeConnection(connection: Connection)
|
||||
}
|
||||
214
ios/ProxyPin/vpn/socket/SocketIOService.swift
Normal file
214
ios/ProxyPin/vpn/socket/SocketIOService.swift
Normal file
@@ -0,0 +1,214 @@
|
||||
//
|
||||
// ProxySocketIOService.swift
|
||||
// ProxyPin
|
||||
//
|
||||
// Created by wanghongen on 2024/9/17.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import NetworkExtension
|
||||
import os.log
|
||||
|
||||
class SocketIOService {
|
||||
//private static let maxReceiveBufferSize = 16384
|
||||
private static let maxReceiveBufferSize = 1024
|
||||
|
||||
private let queue: DispatchQueue = DispatchQueue(label: "ProxyPin.SocketIOService", attributes: .concurrent)
|
||||
|
||||
private var clientPacketWriter: NEPacketTunnelFlow
|
||||
|
||||
private var shutdown = false
|
||||
|
||||
init(clientPacketWriter: NEPacketTunnelFlow) {
|
||||
self.clientPacketWriter = clientPacketWriter
|
||||
}
|
||||
|
||||
public func stop() {
|
||||
os_log("Stopping SocketIOService", log: OSLog.default, type: .default)
|
||||
queue.async(flags: .barrier) {
|
||||
self.shutdown = true
|
||||
}
|
||||
queue.suspend()
|
||||
}
|
||||
|
||||
//从connection接受数据 写到client
|
||||
public func registerSession(connection: Connection) {
|
||||
connection.channel!.stateUpdateHandler = { state in
|
||||
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)
|
||||
connection.closeConnection()
|
||||
case .failed(let error):
|
||||
connection.isConnected = false
|
||||
os_log("Failed to connect: %{public}@", log: OSLog.default, type: .error, error.localizedDescription)
|
||||
connection.closeConnection()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
connection.channel!.start(queue: self.queue)
|
||||
}
|
||||
|
||||
private func receiveMessage(connection: Connection) {
|
||||
if (shutdown) {
|
||||
os_log("SocketIOService is shutting down", log: OSLog.default, type: .default)
|
||||
return
|
||||
}
|
||||
|
||||
if (connection.nwProtocol == .UDP) {
|
||||
readUDP(connection: connection)
|
||||
} else {
|
||||
readTCP(connection: connection)
|
||||
}
|
||||
|
||||
if (connection.isAbortingConnection) {
|
||||
os_log("Connection is aborting", log: OSLog.default, type: .default)
|
||||
connection.closeConnection()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func readTCP(connection: Connection) {
|
||||
// os_log("Reading from TCP socket")
|
||||
if connection.isAbortingConnection {
|
||||
os_log("Connection is aborting", log: OSLog.default, type: .default)
|
||||
return
|
||||
}
|
||||
|
||||
queue.async {
|
||||
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
|
||||
// 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)
|
||||
connection.isAbortingConnection = true
|
||||
return
|
||||
}
|
||||
|
||||
guard let data = data, !data.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
self.pushDataToClient(buffer: data, connection: connection)
|
||||
|
||||
// Recursively call readTCP to continue reading messages
|
||||
self.receiveMessage(connection: connection)
|
||||
|
||||
if (isComplete) {
|
||||
self.sendFin(connection: connection)
|
||||
connection.isAbortingConnection = true
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func synchronized(_ lock: AnyObject, closure: () -> Void) {
|
||||
// objc_sync_enter(lock)
|
||||
closure()
|
||||
// objc_sync_exit(lock)
|
||||
}
|
||||
|
||||
///create packet data and send it to VPN client
|
||||
private func pushDataToClient(buffer: Data, connection: Connection) {
|
||||
// Last piece of data is usually smaller than MAX_RECEIVE_BUFFER_SIZE. We use this as a
|
||||
// trigger to set PSH on the resulting TCP packet that goes to the VPN.
|
||||
|
||||
connection.hasReceivedLastSegment = buffer.count < Self.maxReceiveBufferSize
|
||||
|
||||
guard let ipHeader = connection.lastIpHeader, let tcpHeader = connection.lastTcpHeader else {
|
||||
os_log("Invalid ipHeader or tcpHeader", log: OSLog.default, type: .error)
|
||||
return
|
||||
}
|
||||
|
||||
synchronized(connection) {
|
||||
let unAck = connection.sendNext
|
||||
//处理益处问题
|
||||
let nextUnAck = UInt32(truncatingIfNeeded: (connection.sendNext + UInt32(buffer.count)) % UInt32.max)
|
||||
connection.sendNext = nextUnAck
|
||||
|
||||
let data = TCPPacketFactory.createResponsePacketData(
|
||||
ipHeader: ipHeader,
|
||||
tcpHeader: tcpHeader,
|
||||
packetData: buffer,
|
||||
isPsh: connection.hasReceivedLastSegment,
|
||||
ackNumber: connection.recSequence,
|
||||
seqNumber: unAck,
|
||||
timeSender: connection.timestampSender,
|
||||
timeReplyTo: connection.timestampReplyTo
|
||||
)
|
||||
|
||||
self.clientPacketWriter.writePackets([data], withProtocols: [NSNumber(value: AF_INET)])
|
||||
// os_log("Sent TCP data packet to client %{public}@ length:%d ack:%u", log: OSLog.default, type: .default, connection.description, data.count, connection.recSequence)
|
||||
}
|
||||
}
|
||||
|
||||
private func sendFin(connection: Connection) {
|
||||
guard let ipHeader = connection.lastIpHeader, let tcpHeader = connection.lastTcpHeader else {
|
||||
os_log("Invalid ipHeader or tcpHeader", log: OSLog.default, type: .error)
|
||||
return
|
||||
}
|
||||
synchronized(connection) {
|
||||
let data = TCPPacketFactory.createFinData(
|
||||
ipHeader: ipHeader,
|
||||
tcpHeader: tcpHeader,
|
||||
ackNumber: connection.recSequence,
|
||||
seqNumber: connection.sendNext,
|
||||
timeSender: connection.timestampSender,
|
||||
timeReplyTo: connection.timestampReplyTo
|
||||
)
|
||||
|
||||
self.clientPacketWriter.writePackets([data], withProtocols: [NSNumber(value: AF_INET)])
|
||||
}
|
||||
}
|
||||
|
||||
func readUDP(connection: Connection) {
|
||||
queue.async {
|
||||
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
|
||||
if let error = error {
|
||||
os_log("Failed to read from UDP socket: %@", log: OSLog.default, type: .error, error as CVarArg)
|
||||
connection.isAbortingConnection = true
|
||||
return
|
||||
}
|
||||
|
||||
// os_log("Received UDP data packet length %d", log: OSLog.default, type: .debug, data?.count ?? 0)
|
||||
|
||||
guard let data = data, !data.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
let packetData = UDPPacketFactory.createResponsePacket(
|
||||
ip: connection.lastIpHeader!,
|
||||
udp: connection.lastUdpHeader!,
|
||||
packetData: data
|
||||
)
|
||||
// os_log("Sending UDP data packet to client", log: OSLog.default, type: .default)
|
||||
|
||||
self.clientPacketWriter.writePackets([packetData], withProtocols: [NSNumber(value: AF_INET)])
|
||||
|
||||
// Recursively call receiveMessage to continue receiving messages
|
||||
self.receiveMessage(connection: connection)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
21
ios/ProxyPin/vpn/transport/Packet.swift
Normal file
21
ios/ProxyPin/vpn/transport/Packet.swift
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Packet.swift
|
||||
// ProxyPin
|
||||
//
|
||||
// Created by wanghongen on 2024/9/17.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
class Packet {
|
||||
var ipHeader: IP4Header
|
||||
var transportHeader: TransportHeader
|
||||
var buffer: Data
|
||||
|
||||
init(ipHeader: IP4Header, transportHeader: TransportHeader, buffer: Data) {
|
||||
self.ipHeader = ipHeader
|
||||
self.transportHeader = transportHeader
|
||||
self.buffer = buffer
|
||||
}
|
||||
}
|
||||
96
ios/ProxyPin/vpn/transport/protocol/ICMPPacket.swift
Normal file
96
ios/ProxyPin/vpn/transport/protocol/ICMPPacket.swift
Normal file
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// ICMPPacket.swift
|
||||
// ProxyPin
|
||||
//
|
||||
// Created by wanghongen on 2024/10/3.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class ICMPPacket {
|
||||
// Two ICMP packets we can handle: simple ping & pong
|
||||
static let ECHO_REQUEST_TYPE: UInt8 = 8
|
||||
static let ECHO_SUCCESS_TYPE: UInt8 = 0
|
||||
|
||||
// One very common packet we ignore: connection rejection. Unclear why this happens,
|
||||
// random incoming connections that the phone tries to reply to? Nothing we can do though,
|
||||
// as we can't forward ICMP onwards, and we can't usefully respond or react.
|
||||
static let DESTINATION_UNREACHABLE_TYPE: UInt8 = 3
|
||||
|
||||
let type: UInt8
|
||||
let code: UInt8 // 0 for request, 0 for success, 0 - 15 for error subtypes
|
||||
let checksum: UInt16
|
||||
let identifier: UInt16
|
||||
let sequenceNumber: UInt16
|
||||
let data: [UInt8]
|
||||
|
||||
init(type: UInt8, code: UInt8, checksum: UInt16, identifier: UInt16, sequenceNumber: UInt16, data: [UInt8]) {
|
||||
self.type = type
|
||||
self.code = code
|
||||
self.checksum = checksum
|
||||
self.identifier = identifier
|
||||
self.sequenceNumber = sequenceNumber
|
||||
self.data = data
|
||||
}
|
||||
|
||||
var description: String {
|
||||
return "ICMP packet type \(type)/\(code) id:\(identifier) seq:\(sequenceNumber) and \(data.count) bytes of data"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ICMPPacketFactory {
|
||||
|
||||
static func parseICMPPacket(_ stream: inout Data) -> ICMPPacket? {
|
||||
guard stream.count >= 8 else { return nil }
|
||||
|
||||
let type = stream.removeFirst()
|
||||
let code = stream.removeFirst()
|
||||
let checksum = stream.withUnsafeBytes { $0.load(as: UInt16.self) }
|
||||
stream.removeFirst(2)
|
||||
|
||||
let identifier = stream.withUnsafeBytes { $0.load(as: UInt16.self) }
|
||||
stream.removeFirst(2)
|
||||
let sequenceNumber = stream.withUnsafeBytes { $0.load(as: UInt16.self) }
|
||||
stream.removeFirst(2)
|
||||
|
||||
let data = Array(stream)
|
||||
|
||||
return ICMPPacket(type: type, code: code, checksum: checksum, identifier: identifier, sequenceNumber: sequenceNumber, data: data)
|
||||
}
|
||||
|
||||
static func buildSuccessPacket(_ requestPacket: ICMPPacket) -> ICMPPacket {
|
||||
return ICMPPacket(
|
||||
type: ICMPPacket.ECHO_SUCCESS_TYPE,
|
||||
code: 0,
|
||||
checksum: 0,
|
||||
identifier: requestPacket.identifier,
|
||||
sequenceNumber: requestPacket.sequenceNumber,
|
||||
data: requestPacket.data
|
||||
)
|
||||
}
|
||||
|
||||
static func packetToBuffer(ipHeader: IP4Header, packet: ICMPPacket) -> Data {
|
||||
var buffer = Data()
|
||||
buffer.append(ipHeader.toBytes())
|
||||
|
||||
var icmpDataBuffer = Data()
|
||||
icmpDataBuffer.append(packet.type)
|
||||
icmpDataBuffer.append(packet.code)
|
||||
icmpDataBuffer.append(contentsOf: withUnsafeBytes(of: UInt16(0), Array.init))
|
||||
|
||||
if packet.type == ICMPPacket.ECHO_REQUEST_TYPE || packet.type == ICMPPacket.ECHO_SUCCESS_TYPE {
|
||||
icmpDataBuffer.append(contentsOf: packet.identifier.bytes)
|
||||
icmpDataBuffer.append(contentsOf: packet.sequenceNumber.bytes)
|
||||
icmpDataBuffer.append(contentsOf: packet.data)
|
||||
} else {
|
||||
fatalError("Can't serialize unrecognized ICMP packet type")
|
||||
}
|
||||
|
||||
let checksum = PacketUtil.calculateChecksum(data: icmpDataBuffer, offset: 0, length: icmpDataBuffer.count)
|
||||
icmpDataBuffer.replaceSubrange(2..<4, with: checksum)
|
||||
buffer.append(icmpDataBuffer)
|
||||
|
||||
return buffer
|
||||
}
|
||||
}
|
||||
161
ios/ProxyPin/vpn/transport/protocol/IP4Header.swift
Normal file
161
ios/ProxyPin/vpn/transport/protocol/IP4Header.swift
Normal file
@@ -0,0 +1,161 @@
|
||||
//
|
||||
// IP4Header.swift
|
||||
// ProxyPin
|
||||
//
|
||||
// Created by wanghongen on 2024/9/16.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import os.log
|
||||
|
||||
// IPv4 header data structure
|
||||
class IP4Header {
|
||||
var ipVersion: UInt8 // 对于IPv4,其值为4(因此命名为IPv4)。 4bit
|
||||
var internetHeaderLength: UInt8 // 头部长度 4bit
|
||||
var diffTypeOfService: UInt8 // 差分服务代码点 =>6位
|
||||
var ecn: UInt8 // 显式拥塞通知(ECN)
|
||||
var totalLength: UInt16 // 此IP数据包的总长度 16bit
|
||||
var identification: UInt16 // 主要用于唯一标识单个IP数据报的片段组。 16bit
|
||||
var mayFragment: Bool // 用于指示数据报是否可以分段。 1bit
|
||||
var lastFragment: Bool // 用于指示数据报是否是片段中的最后一个。 1bit
|
||||
var fragmentOffset: UInt16 // 指定特定片段相对于原始未分段的IP数据报的开始的偏移量。 13bit
|
||||
var timeToLive: UInt8 // 用于防止数据报持续存在。8bit
|
||||
var protocolNumber: UInt8 // 定义IP数据报的数据部分中使用的协议。 8bit
|
||||
var headerChecksum: UInt16 // 用于对头部进行错误检查的16位字段。 16bit
|
||||
var sourceIP: UInt32 // 发送者的IPv4地址。 32bit
|
||||
var destinationIP: UInt32 // 接收者的IPv4地址。 32bit
|
||||
|
||||
//用于控制或识别片段的3比特字段。
|
||||
//bit 0: 保留;必须为零
|
||||
//bit 1: Don't Fragment (DF)
|
||||
//bit 2: More Fragments (MF)
|
||||
private var flag: UInt8
|
||||
|
||||
init(
|
||||
ipVersion: UInt8, internetHeaderLength: UInt8, diffTypeOfService: UInt8, ecn: UInt8, totalLength: UInt16, identification: UInt16,
|
||||
mayFragment: Bool, lastFragment: Bool, fragmentOffset: UInt16, timeToLive: UInt8, protocolNumber: UInt8, headerChecksum: UInt16,
|
||||
sourceIP: UInt32, destinationIP: UInt32
|
||||
) {
|
||||
self.ipVersion = ipVersion
|
||||
self.internetHeaderLength = internetHeaderLength
|
||||
self.diffTypeOfService = diffTypeOfService
|
||||
self.ecn = ecn
|
||||
self.totalLength = totalLength
|
||||
self.identification = identification
|
||||
self.mayFragment = mayFragment
|
||||
self.lastFragment = lastFragment
|
||||
self.fragmentOffset = fragmentOffset
|
||||
self.timeToLive = timeToLive
|
||||
self.protocolNumber = protocolNumber
|
||||
self.headerChecksum = headerChecksum
|
||||
self.sourceIP = sourceIP
|
||||
self.destinationIP = destinationIP
|
||||
self.flag = IP4Header.initFlag(mayFragment: mayFragment, lastFragment: lastFragment)
|
||||
}
|
||||
|
||||
|
||||
private static func initFlag(mayFragment: Bool, lastFragment: Bool) -> UInt8 {
|
||||
var initFlag: UInt8 = 0
|
||||
if mayFragment {
|
||||
initFlag = 0x40
|
||||
}
|
||||
if lastFragment {
|
||||
initFlag |= 0x20
|
||||
}
|
||||
return initFlag
|
||||
}
|
||||
|
||||
func setMayFragment(_ mayFragment: Bool) {
|
||||
self.mayFragment = mayFragment
|
||||
flag = mayFragment ? (flag | 0x40) : (flag & 0xBF)
|
||||
}
|
||||
|
||||
func getIPHeaderLength() -> Int {
|
||||
return Int(internetHeaderLength * 4)
|
||||
}
|
||||
|
||||
func copy() -> IP4Header {
|
||||
return IP4Header(
|
||||
ipVersion: ipVersion, internetHeaderLength: internetHeaderLength, diffTypeOfService: diffTypeOfService, ecn: ecn, totalLength: totalLength, identification: identification,
|
||||
mayFragment: mayFragment, lastFragment: lastFragment, fragmentOffset: fragmentOffset, timeToLive: timeToLive, protocolNumber: protocolNumber, headerChecksum: headerChecksum,
|
||||
sourceIP: sourceIP, destinationIP: destinationIP
|
||||
)
|
||||
}
|
||||
|
||||
func toBytes() -> Data {
|
||||
var buffer = Data()
|
||||
buffer.append(UInt8((ipVersion << 4) + internetHeaderLength))
|
||||
buffer.append(UInt8((diffTypeOfService << 2) + ecn))
|
||||
|
||||
buffer.append(contentsOf: totalLength.bytes)
|
||||
buffer.append(contentsOf: identification.bytes)
|
||||
|
||||
//组合标志和部分片段偏移
|
||||
buffer.append(UInt8((fragmentOffset >> 8) & 0x1F) | flag)
|
||||
buffer.append(UInt8(fragmentOffset & 0xFF))
|
||||
|
||||
buffer.append(timeToLive)
|
||||
buffer.append(protocolNumber)
|
||||
|
||||
buffer.append(contentsOf: headerChecksum.bytes)
|
||||
|
||||
buffer.append(contentsOf: sourceIP.bytes)
|
||||
buffer.append(contentsOf: destinationIP.bytes)
|
||||
return buffer
|
||||
}
|
||||
}
|
||||
|
||||
class IPPacketFactory {
|
||||
static let IP4_HEADER_SIZE = 20
|
||||
static let IP4_VERSION: UInt8 = 0x04
|
||||
|
||||
//从给定的ByteBuffer流创建IPv4标头
|
||||
static func createIP4Header(data: Data) -> IP4Header? {
|
||||
guard data.count >= IP4_HEADER_SIZE else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let buffer = [UInt8](data)
|
||||
let versionAndHeaderLength = buffer[0]
|
||||
let ipVersion = versionAndHeaderLength >> 4
|
||||
guard ipVersion == IP4_VERSION else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let internetHeaderLength = versionAndHeaderLength & 0x0F
|
||||
let typeOfService = buffer[1]
|
||||
let diffTypeOfService = typeOfService >> 2
|
||||
let ecn = typeOfService & 0x03
|
||||
let totalLength = UInt16(buffer[2]) << 8 | UInt16(buffer[3])
|
||||
let identification = UInt16(buffer[4]) << 8 | UInt16(buffer[5])
|
||||
let flagsAndFragmentOffset = UInt16(buffer[6]) << 8 | UInt16(buffer[7])
|
||||
let mayFragment = (flagsAndFragmentOffset & 0x4000) != 0
|
||||
let lastFragment = (flagsAndFragmentOffset & 0x2000) != 0
|
||||
let fragmentOffset = flagsAndFragmentOffset & 0x1FFF
|
||||
let timeToLive = buffer[8]
|
||||
let protocolNumber = buffer[9]
|
||||
let checksum = UInt16(buffer[10]) << 8 | UInt16(buffer[11])
|
||||
let sourceIp = UInt32(buffer[12]) << 24 | UInt32(buffer[13]) << 16 | UInt32(buffer[14]) << 8 | UInt32(buffer[15])
|
||||
let desIp = UInt32(buffer[16]) << 24 | UInt32(buffer[17]) << 16 | UInt32(buffer[18]) << 8 | UInt32(buffer[19])
|
||||
|
||||
if internetHeaderLength > 5 {
|
||||
// drop the IP option
|
||||
for _ in 0..<(internetHeaderLength - 5) {
|
||||
// Skip the IP options
|
||||
}
|
||||
}
|
||||
|
||||
return IP4Header(
|
||||
ipVersion: ipVersion, internetHeaderLength: internetHeaderLength, diffTypeOfService: diffTypeOfService, ecn: ecn, totalLength: totalLength, identification: identification,
|
||||
mayFragment: mayFragment, lastFragment: lastFragment, fragmentOffset: fragmentOffset, timeToLive: timeToLive, protocolNumber: protocolNumber, headerChecksum: checksum,
|
||||
sourceIP: sourceIp, destinationIP: desIp
|
||||
)
|
||||
}
|
||||
|
||||
public static func printPacket(data: Data) {
|
||||
guard let ipHeader = createIP4Header(data: data) else {
|
||||
return
|
||||
}
|
||||
os_log("IP Header: version: %{public}d, internetHeaderLength: %{public}d, diffTypeOfService: %{public}d, ecn: %{public}d, totalLength: %{public}d, identification: %{public}d, mayFragment: %{public}d, lastFragment: %{public}d, fragmentOffset: %{public}d, timeToLive: %{public}d, protocolNumber: %{public}d, headerChecksum: %{public}d, sourceIP: %{public}@, destinationIP: %{public}@", log: OSLog.default, type: .default, ipHeader.ipVersion, ipHeader.internetHeaderLength, ipHeader.diffTypeOfService, ipHeader.ecn, ipHeader.totalLength, ipHeader.identification, ipHeader.mayFragment, ipHeader.lastFragment, ipHeader.fragmentOffset, ipHeader.timeToLive, ipHeader.protocolNumber, ipHeader.headerChecksum, PacketUtil.intToIPAddress(ipHeader.sourceIP), PacketUtil.intToIPAddress(ipHeader.destinationIP))
|
||||
}
|
||||
}
|
||||
182
ios/ProxyPin/vpn/transport/protocol/TCPHeader.swift
Normal file
182
ios/ProxyPin/vpn/transport/protocol/TCPHeader.swift
Normal file
@@ -0,0 +1,182 @@
|
||||
//
|
||||
// TCPHeader.swift
|
||||
// ProxyPin
|
||||
//
|
||||
// Created by wanghongen on 2024/9/16.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Represents a TCP header in a network packet.
|
||||
class TCPHeader : TransportHeader{
|
||||
|
||||
/// Source port number (16 bits)
|
||||
var sourcePort: UInt16
|
||||
/// Destination port number (16 bits)
|
||||
var destinationPort: UInt16
|
||||
/// Sequence number (32 bits)
|
||||
var sequenceNumber: UInt32
|
||||
/// Acknowledgment number (32 bits)
|
||||
var ackNumber: UInt32
|
||||
/// Data offset (4 bits)
|
||||
var dataOffset: UInt8
|
||||
var isNS: Bool = false // ECN-nonce concealment protection (experimental: see RFC 3540)
|
||||
/// Flags (9 bits)
|
||||
var flags: UInt8
|
||||
/// Window size (16 bits)
|
||||
var windowSize: UInt16
|
||||
/// Checksum (16 bits)
|
||||
var checksum: UInt16
|
||||
/// Urgent pointer (16 bits)
|
||||
var urgentPointer: UInt16
|
||||
/// Options (variable length)
|
||||
var options: Data?
|
||||
var payload: Data?
|
||||
|
||||
//Static section for constants
|
||||
static let END_OF_OPTIONS_LIST: UInt8 = 0
|
||||
static let NO_OPERATION: UInt8 = 1
|
||||
static let MAX_SEGMENT_SIZE: UInt8 = 2
|
||||
static let WINDOW_SCALE: UInt8 = 3
|
||||
static let SELECTIVE_ACK_PERMITTED: UInt8 = 4
|
||||
static let TIME_STAMP: UInt8 = 8
|
||||
|
||||
init(sourcePort: UInt16, destinationPort: UInt16, sequenceNumber: UInt32, ackNumber: UInt32, dataOffset: UInt8, isNS: Bool, flags: UInt8, windowSize: UInt16, checksum: UInt16, urgentPointer: UInt16, options: Data?, payload: Data? = nil) {
|
||||
self.sourcePort = sourcePort
|
||||
self.destinationPort = destinationPort
|
||||
self.sequenceNumber = sequenceNumber
|
||||
self.ackNumber = ackNumber
|
||||
self.dataOffset = dataOffset
|
||||
self.isNS = isNS
|
||||
self.flags = flags
|
||||
self.windowSize = windowSize
|
||||
self.checksum = checksum
|
||||
self.urgentPointer = urgentPointer
|
||||
self.options = options
|
||||
self.payload = payload
|
||||
}
|
||||
|
||||
//options
|
||||
var maxSegmentSize: UInt16 = 0
|
||||
private var windowScale: UInt8 = 0
|
||||
private var isSelectiveAckPermitted = false
|
||||
var timeStampSender = 0
|
||||
var timeStampReplyTo = 0
|
||||
|
||||
func getSourcePort() -> Int {
|
||||
return Int(sourcePort)
|
||||
}
|
||||
|
||||
func getDestinationPort() -> Int {
|
||||
return Int(destinationPort)
|
||||
}
|
||||
|
||||
func isFIN() -> Bool {
|
||||
return flags & 0x01 != 0
|
||||
}
|
||||
|
||||
/// Checks if the SYN flag is set.
|
||||
func isSYN() -> Bool {
|
||||
return flags & 0x02 != 0
|
||||
}
|
||||
|
||||
/// Checks if the RST flag is set.
|
||||
func isRST() -> Bool {
|
||||
return flags & 0x04 != 0
|
||||
}
|
||||
|
||||
/// Checks if the PSH flag is set.
|
||||
func isPSH() -> Bool {
|
||||
return flags & 0x08 != 0
|
||||
}
|
||||
|
||||
/// Checks if the ACK flag is set.
|
||||
func isACK() -> Bool {
|
||||
return flags & 0x10 != 0
|
||||
}
|
||||
|
||||
/// Checks if the URG flag is set.
|
||||
func isURG() -> Bool {
|
||||
return flags & 0x20 != 0
|
||||
}
|
||||
|
||||
/// Checks if the ECE flag is set.
|
||||
func isECE() -> Bool {
|
||||
return flags & 0x40 != 0
|
||||
}
|
||||
|
||||
/// Checks if the CWR flag is set.
|
||||
func isCWR() -> Bool {
|
||||
return flags & 0x80 != 0
|
||||
}
|
||||
|
||||
/// Sets or clears the RST flag.
|
||||
func setIsRST(_ isRST: Bool) {
|
||||
flags = isRST ? (flags | 0x04) : (flags & 0xFB)
|
||||
}
|
||||
|
||||
/// Sets or clears the SYN flag.
|
||||
func setIsSYN(_ isSYN: Bool) {
|
||||
flags = isSYN ? (flags | 0x02) : (flags & 0xFD)
|
||||
}
|
||||
|
||||
/// Sets or clears the FIN flag.
|
||||
func setIsFIN(_ isFIN: Bool) {
|
||||
flags = isFIN ? (flags | 0x01) : (flags & 0xFE)
|
||||
}
|
||||
|
||||
/// Sets or clears the PSH flag.
|
||||
func setIsPSH(_ isPSH: Bool) {
|
||||
flags = isPSH ? (flags | 0x08) : (flags & 0xF7)
|
||||
}
|
||||
|
||||
/// Sets or clears the ACK flag.
|
||||
func setIsACK(_ isACK: Bool) {
|
||||
flags = isACK ? (flags | 0x10) : (flags & 0xEF)
|
||||
}
|
||||
|
||||
/// Returns the length of the TCP header.
|
||||
func getTCPHeaderLength() -> Int {
|
||||
return Int(dataOffset) * 4
|
||||
}
|
||||
|
||||
/// Converts the TCP header to a byte array.
|
||||
func toBytes() -> Data {
|
||||
var buffer = Data()
|
||||
|
||||
buffer.append(contentsOf: sourcePort.bytes)
|
||||
buffer.append(contentsOf: destinationPort.bytes)
|
||||
buffer.append(contentsOf: sequenceNumber.bytes)
|
||||
buffer.append(contentsOf: ackNumber.bytes)
|
||||
|
||||
//is ns and data offset
|
||||
let headerLength = 5
|
||||
buffer.append(UInt8((headerLength << 4) | (isNS ? 1 : 0)))
|
||||
buffer.append(flags)
|
||||
buffer.append(contentsOf: windowSize.bytes)
|
||||
buffer.append(contentsOf: checksum.bytes)
|
||||
buffer.append(contentsOf: urgentPointer.bytes)
|
||||
// if let options = options {
|
||||
// buffer.append(options)
|
||||
// }
|
||||
return buffer
|
||||
}
|
||||
|
||||
/// Creates a copy of the TCP header.
|
||||
func copy() -> TCPHeader {
|
||||
return TCPHeader(
|
||||
sourcePort: sourcePort,
|
||||
destinationPort: destinationPort,
|
||||
sequenceNumber: sequenceNumber,
|
||||
ackNumber: ackNumber,
|
||||
dataOffset: dataOffset,
|
||||
isNS: isNS,
|
||||
flags: flags,
|
||||
windowSize: windowSize,
|
||||
checksum: checksum,
|
||||
urgentPointer: urgentPointer,
|
||||
options: options
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
325
ios/ProxyPin/vpn/transport/protocol/TCPPacketFactory.swift
Normal file
325
ios/ProxyPin/vpn/transport/protocol/TCPPacketFactory.swift
Normal file
@@ -0,0 +1,325 @@
|
||||
//
|
||||
// TCPPacketFactory.swift
|
||||
// ProxyPin
|
||||
//
|
||||
// Created by wanghongen on 2024/9/16.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import os.log
|
||||
|
||||
/// Factory class for creating TCP packets.
|
||||
class TCPPacketFactory {
|
||||
|
||||
public static let TCP_HEADER_LENGTH = 20
|
||||
|
||||
//从tcp报文创建tcpHeader
|
||||
static func createTCPHeader(data: Data) -> TCPHeader? {
|
||||
if data.count < TCP_HEADER_LENGTH {
|
||||
os_log("Data is too short to be a TCP packet", log: OSLog.default, type: .error)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
var offset = 0
|
||||
|
||||
func readUInt16() -> UInt16 {
|
||||
let value = data.withUnsafeBytes { $0.load(fromByteOffset: offset, as: UInt16.self).bigEndian }
|
||||
offset += 2
|
||||
return value
|
||||
}
|
||||
|
||||
func readUInt32() -> UInt32 {
|
||||
let value = data.withUnsafeBytes { $0.load(fromByteOffset: offset, as: UInt32.self).bigEndian }
|
||||
offset += 4
|
||||
return value
|
||||
}
|
||||
|
||||
let sourcePort = readUInt16()
|
||||
let destinationPort = readUInt16()
|
||||
|
||||
let sequenceNumber = readUInt32()
|
||||
let ackNumber = readUInt32()
|
||||
|
||||
let dataOffsetAndReserved = data[offset]
|
||||
offset += 1
|
||||
let dataOffset = UInt8((dataOffsetAndReserved & 0xF0) >> 4)
|
||||
let isNs = (dataOffsetAndReserved & 0x01) == 1
|
||||
let flags = UInt8(data[offset])
|
||||
offset += 1
|
||||
|
||||
let windowSize = readUInt16()
|
||||
let checksum = readUInt16()
|
||||
let urgentPointer = readUInt16()
|
||||
|
||||
var optionsSize = Int(dataOffset) - 5
|
||||
var options: Data?
|
||||
if (optionsSize > 0) {
|
||||
optionsSize *= 4
|
||||
options = data.subdata(in: offset..<offset + optionsSize)
|
||||
}
|
||||
|
||||
let payload: Data? = offset < data.count ? data.subdata(in: offset..<data.count) : nil
|
||||
return TCPHeader(
|
||||
sourcePort: sourcePort,
|
||||
destinationPort: destinationPort,
|
||||
sequenceNumber: sequenceNumber,
|
||||
ackNumber: ackNumber,
|
||||
dataOffset: dataOffset,
|
||||
isNS: isNs,
|
||||
flags: flags,
|
||||
windowSize: windowSize,
|
||||
checksum: checksum,
|
||||
urgentPointer: urgentPointer,
|
||||
options: options,
|
||||
payload: payload
|
||||
)
|
||||
}
|
||||
|
||||
//向客户端确认服务器已收到请求
|
||||
static func createResponseAckData(ipHeader: IP4Header, tcpHeader: TCPHeader, ackToClient: UInt32) -> Data {
|
||||
var ip = ipHeader.copy()
|
||||
var tcp = tcpHeader.copy()
|
||||
|
||||
flipIp(ip: &ip, tcp: &tcp)
|
||||
let seqNumber = tcp.ackNumber
|
||||
tcp.ackNumber = ackToClient
|
||||
tcp.sequenceNumber = seqNumber
|
||||
|
||||
ip.identification = UInt16(truncatingIfNeeded: PacketUtil.getPacketId())
|
||||
|
||||
// Set TCP flags
|
||||
tcp.setIsACK(true)
|
||||
tcp.setIsSYN(false)
|
||||
tcp.setIsPSH(false)
|
||||
tcp.setIsFIN(false)
|
||||
|
||||
tcp.dataOffset = 5 // tcp header length 5 * 4 = 20 bytes
|
||||
tcp.options = nil
|
||||
|
||||
ip.totalLength = UInt16(ip.getIPHeaderLength() + tcp.getTCPHeaderLength())
|
||||
|
||||
return createPacketData(ipHeader: ip, tcpHeader: tcp, data: nil)
|
||||
}
|
||||
|
||||
///创建带有RST标志的数据包,以便在需要重置时发送到客户端。
|
||||
static func createRstData(ipHeader: IP4Header, tcpHeader: TCPHeader, dataLength: Int) -> Data {
|
||||
var ip = ipHeader.copy()
|
||||
var tcp = tcpHeader.copy()
|
||||
|
||||
var ackNumber: UInt32 = 0
|
||||
var seqNumber: UInt32 = 0
|
||||
|
||||
if tcp.ackNumber > 0 {
|
||||
seqNumber = tcp.ackNumber
|
||||
} else {
|
||||
ackNumber = tcp.sequenceNumber + UInt32(dataLength)
|
||||
}
|
||||
|
||||
tcp.ackNumber = ackNumber
|
||||
tcp.sequenceNumber = seqNumber
|
||||
|
||||
// Flip IP from source to destination
|
||||
flipIp(ip: &ip, tcp: &tcp)
|
||||
|
||||
ip.identification = 0
|
||||
|
||||
tcp.flags = 0
|
||||
tcp.isNS = false
|
||||
tcp.setIsRST(true)
|
||||
|
||||
tcp.dataOffset = 5
|
||||
tcp.options = nil
|
||||
tcp.windowSize = 0
|
||||
|
||||
// Recalculate IP length
|
||||
let totalLength = ip.getIPHeaderLength() + tcp.getTCPHeaderLength()
|
||||
ip.totalLength = UInt16(totalLength)
|
||||
|
||||
return createPacketData(ipHeader: ip, tcpHeader: tcp, data: nil)
|
||||
}
|
||||
|
||||
//创建发送到客户端的FIN-ACK
|
||||
static func createFinAckData(ipHeader: IP4Header, tcpHeader: TCPHeader, ackToClient: UInt32, seqToClient: UInt32, isFin: Bool, isAck: Bool) -> Data {
|
||||
var ip = ipHeader.copy()
|
||||
var tcp = tcpHeader.copy()
|
||||
|
||||
flipIp(ip: &ip, tcp: &tcp)
|
||||
|
||||
tcp.dataOffset = 5 // tcp header length 5 * 4 = 20 bytes
|
||||
tcp.options = nil
|
||||
|
||||
tcp.ackNumber = ackToClient
|
||||
tcp.sequenceNumber = seqToClient
|
||||
ip.identification = UInt16(truncatingIfNeeded: PacketUtil.getPacketId())
|
||||
|
||||
tcp.setIsACK(isAck)
|
||||
tcp.setIsSYN(false)
|
||||
tcp.setIsPSH(false)
|
||||
tcp.setIsFIN(isFin)
|
||||
|
||||
ip.totalLength = UInt16(ip.getIPHeaderLength() + tcp.getTCPHeaderLength())
|
||||
return createPacketData(ipHeader: ip, tcpHeader: tcp, data: nil)
|
||||
}
|
||||
|
||||
//通过写回客户端流创建SYN-ACK数据包数据
|
||||
public static func createSynAckPacketData(ipHeader: IP4Header, tcpHeader: TCPHeader) -> Packet {
|
||||
var ip = ipHeader.copy()
|
||||
var tcp = tcpHeader.copy()
|
||||
flipIp(ip: &ip, tcp: &tcp)
|
||||
|
||||
tcp.dataOffset = 5 // tcp header length 5 * 4 = 20 bytes
|
||||
tcp.options = nil
|
||||
|
||||
// ack = received sequence + 1
|
||||
let ackNumber = tcpHeader.sequenceNumber + 1
|
||||
tcp.ackNumber = ackNumber
|
||||
|
||||
// Server-generated initial sequence number
|
||||
let seqNumber = UInt64.random(in: 0..<100000)
|
||||
tcp.sequenceNumber = UInt32(seqNumber)
|
||||
|
||||
// SYN-ACK
|
||||
tcp.setIsACK(true)
|
||||
tcp.setIsSYN(true)
|
||||
|
||||
tcp.timeStampReplyTo = tcp.timeStampSender
|
||||
tcp.timeStampSender = PacketUtil.currentTime
|
||||
|
||||
ip.totalLength = UInt16(ip.getIPHeaderLength() + tcp.getTCPHeaderLength())
|
||||
|
||||
return Packet(ipHeader: ip, transportHeader: tcp, buffer: createPacketData(ipHeader: ip, tcpHeader: tcp, data: nil))
|
||||
}
|
||||
|
||||
//创建数据包数据以发送回客户端
|
||||
public static func createResponsePacketData(
|
||||
ipHeader: IP4Header, tcpHeader: TCPHeader, packetData: Data?, isPsh: Bool,
|
||||
ackNumber: UInt32, seqNumber: UInt32, timeSender: Int, timeReplyTo: Int
|
||||
) -> Data {
|
||||
var ip = ipHeader.copy()
|
||||
var tcp = tcpHeader.copy()
|
||||
|
||||
flipIp(ip: &ip, tcp: &tcp)
|
||||
|
||||
tcp.dataOffset = 5 // tcp header length 5 * 4 = 20 bytes
|
||||
tcp.options = nil
|
||||
|
||||
tcp.ackNumber = ackNumber
|
||||
tcp.sequenceNumber = seqNumber
|
||||
ip.identification = UInt16(truncatingIfNeeded: PacketUtil.getPacketId())
|
||||
|
||||
// ACK is always sent
|
||||
tcp.setIsACK(true)
|
||||
tcp.setIsSYN(false)
|
||||
tcp.setIsPSH(isPsh)
|
||||
tcp.setIsFIN(false)
|
||||
tcp.timeStampSender = timeSender
|
||||
tcp.timeStampReplyTo = timeReplyTo
|
||||
|
||||
var totalLength = ip.getIPHeaderLength() + tcp.getTCPHeaderLength()
|
||||
if let packetData = packetData {
|
||||
totalLength += packetData.count
|
||||
}
|
||||
ip.totalLength = UInt16(totalLength)
|
||||
|
||||
return createPacketData(ipHeader: ip, tcpHeader: tcp, data: packetData)
|
||||
}
|
||||
|
||||
//将IP从源翻转到目标
|
||||
private static func flipIp(ip: inout IP4Header, tcp: inout TCPHeader) {
|
||||
let sourceIp = ip.destinationIP
|
||||
let destIp = ip.sourceIP
|
||||
let sourcePort = tcp.destinationPort
|
||||
let destPort = tcp.sourcePort
|
||||
|
||||
ip.destinationIP = destIp
|
||||
ip.sourceIP = sourceIp
|
||||
tcp.destinationPort = destPort
|
||||
tcp.sourcePort = sourcePort
|
||||
}
|
||||
|
||||
public static func createFinData(
|
||||
ipHeader: IP4Header, tcpHeader: TCPHeader, ackNumber: UInt32, seqNumber: UInt32,
|
||||
timeSender: Int, timeReplyTo: Int
|
||||
) -> Data {
|
||||
var ip = ipHeader.copy()
|
||||
var tcp = tcpHeader.copy()
|
||||
|
||||
flipIp(ip: &ip, tcp: &tcp)
|
||||
|
||||
tcp.ackNumber = ackNumber
|
||||
tcp.sequenceNumber = seqNumber
|
||||
|
||||
ip.identification = UInt16(truncatingIfNeeded: PacketUtil.getPacketId())
|
||||
|
||||
tcp.timeStampReplyTo = timeReplyTo
|
||||
tcp.timeStampSender = timeSender
|
||||
|
||||
tcp.flags = 0
|
||||
tcp.isNS = false
|
||||
tcp.setIsACK(true)
|
||||
tcp.setIsFIN(true)
|
||||
|
||||
tcp.options = nil
|
||||
tcp.windowSize = 0
|
||||
|
||||
ip.totalLength = UInt16(ip.getIPHeaderLength() + TCP_HEADER_LENGTH)
|
||||
return createPacketData(ipHeader: ip, tcpHeader: tcp, data: nil)
|
||||
}
|
||||
|
||||
|
||||
//从tcpHeader创建tcp报文
|
||||
private static func createPacketData(ipHeader: IP4Header, tcpHeader: TCPHeader, data: Data?) -> Data {
|
||||
let dataLength = data?.count ?? 0
|
||||
|
||||
var buffer = Data()
|
||||
|
||||
// Add IP header
|
||||
let ipBuffer = ipHeader.toBytes()
|
||||
buffer.append(ipBuffer)
|
||||
|
||||
// Add TCP header
|
||||
let tcpBuffer = tcpHeader.toBytes()
|
||||
buffer.append(tcpBuffer)
|
||||
|
||||
// Add data if exists
|
||||
if let data = data {
|
||||
buffer.append(data)
|
||||
}
|
||||
|
||||
// Zero out IP checksum
|
||||
buffer[10] = 0
|
||||
buffer[11] = 0
|
||||
|
||||
// Calculate IP checksum
|
||||
let ipChecksum = PacketUtil.calculateChecksum(data: buffer, offset: 0, length: ipBuffer.count)
|
||||
buffer[10] = ipChecksum[0]
|
||||
buffer[11] = ipChecksum[1]
|
||||
// IPPacketFactory.printPacket(data: ipBuffer)
|
||||
|
||||
// Zero out TCP checksum
|
||||
let tcpStart = ipBuffer.count
|
||||
buffer[tcpStart + 16] = 0
|
||||
buffer[tcpStart + 17] = 0
|
||||
|
||||
// Calculate TCP checksum
|
||||
let tcpChecksum = PacketUtil.calculateTCPHeaderChecksum(
|
||||
data: buffer, offset: tcpStart, tcpLength: tcpBuffer.count + dataLength,
|
||||
sourceIP: ipHeader.sourceIP, destinationIP: ipHeader.destinationIP
|
||||
)
|
||||
buffer[tcpStart + 16] = tcpChecksum[0]
|
||||
buffer[tcpStart + 17] = tcpChecksum[1]
|
||||
return buffer
|
||||
}
|
||||
|
||||
static func printPacket(data: Data) {
|
||||
guard let tcpHeader = createTCPHeader(data: data) else {
|
||||
os_log("Failed to create TCP header", log: OSLog.default, type: .error)
|
||||
return
|
||||
}
|
||||
|
||||
os_log("TCP Header: sourcePort: %{public}d, destinationPort: %{public}d, sequenceNumber: %{public}u, ackNumber: %{public}u, dataOffset: %{public}d, isNS: %{public}d, flags: %{public}d, windowSize: %{public}d, checksum: %{public}u, urgentPointer: %{public}u",
|
||||
log: OSLog.default, type: .default, tcpHeader.sourcePort, tcpHeader.destinationPort, tcpHeader.sequenceNumber, tcpHeader.ackNumber, tcpHeader.dataOffset, tcpHeader.isNS ? 1 : 0, tcpHeader.flags, tcpHeader.windowSize, tcpHeader.checksum, tcpHeader.urgentPointer)
|
||||
}
|
||||
}
|
||||
13
ios/ProxyPin/vpn/transport/protocol/TransportHeader.swift
Normal file
13
ios/ProxyPin/vpn/transport/protocol/TransportHeader.swift
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// TransportHeader.swift
|
||||
// ProxyPin
|
||||
//
|
||||
// Created by wanghongen on 2024/9/17.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol TransportHeader {
|
||||
func getSourcePort() -> Int
|
||||
func getDestinationPort() -> Int
|
||||
}
|
||||
112
ios/ProxyPin/vpn/transport/protocol/UDPHeader.swift
Normal file
112
ios/ProxyPin/vpn/transport/protocol/UDPHeader.swift
Normal file
@@ -0,0 +1,112 @@
|
||||
//
|
||||
// UDPHeader.swift
|
||||
// ProxyPin
|
||||
//
|
||||
// Created by wanghongen on 2024/9/17.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import os.log
|
||||
|
||||
///UDP报头的数据
|
||||
struct UDPHeader {
|
||||
var sourcePort: UInt16 //源端口号 16bit
|
||||
var destinationPort: UInt16 //源端口号 16bit
|
||||
var length: UInt16 //UDP数据报长度 16bit
|
||||
var checksum: UInt16 //校验和 16bit
|
||||
|
||||
init(sourcePort: UInt16, destinationPort: UInt16, length: UInt16, checksum: UInt16) {
|
||||
self.sourcePort = sourcePort
|
||||
self.destinationPort = destinationPort
|
||||
self.length = length
|
||||
self.checksum = checksum
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class UDPPacketFactory {
|
||||
static let UDP_HEADER_LENGTH = 8
|
||||
|
||||
static func createUDPHeader(from data: Data) -> UDPHeader? {
|
||||
guard data.count >= UDP_HEADER_LENGTH else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let srcPort = data.withUnsafeBytes { $0.load(fromByteOffset: 0, as: UInt16.self).bigEndian }
|
||||
let destPort = data.withUnsafeBytes { $0.load(fromByteOffset: 2, as: UInt16.self).bigEndian }
|
||||
let length = data.withUnsafeBytes { $0.load(fromByteOffset: 4, as: UInt16.self).bigEndian }
|
||||
let checksum = data.withUnsafeBytes { $0.load(fromByteOffset: 6, as: UInt16.self).bigEndian }
|
||||
|
||||
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
|
||||
|
||||
ipHeader.setMayFragment(false)
|
||||
ipHeader.sourceIP = srcIp
|
||||
ipHeader.destinationIP = destIp
|
||||
ipHeader.identification = UInt16(truncatingIfNeeded: PacketUtil.getPacketId())
|
||||
|
||||
//ip的长度是整个数据包的长度 => IP header length + UDP header length (8) + UDP body length
|
||||
let totalLength = ipHeader.getIPHeaderLength() + udpLen
|
||||
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)
|
||||
|
||||
// write result of checksum back to buffer
|
||||
ipData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) in
|
||||
bytes[10] = ipChecksum[0]
|
||||
bytes[11] = ipChecksum[1]
|
||||
}
|
||||
|
||||
var buffer = Data()
|
||||
|
||||
// copy IP header to buffer
|
||||
buffer.append(ipData)
|
||||
|
||||
// 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)
|
||||
|
||||
if let packetData = 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)
|
||||
}
|
||||
|
||||
}
|
||||
138
ios/ProxyPin/vpn/utils/PacketUtil.swift
Normal file
138
ios/ProxyPin/vpn/utils/PacketUtil.swift
Normal file
@@ -0,0 +1,138 @@
|
||||
//
|
||||
// PacketUtil.swift
|
||||
// ProxyPin
|
||||
//
|
||||
// Created by wanghongen on 2024/9/17.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import os.log
|
||||
|
||||
class PacketUtil {
|
||||
private static var packetId: Int = 0
|
||||
|
||||
static func getPacketId() -> Int {
|
||||
defer { packetId += 1 }
|
||||
return packetId
|
||||
}
|
||||
|
||||
static var currentTime: Int {
|
||||
return Int(Date().timeIntervalSince1970)
|
||||
}
|
||||
|
||||
static func writeIntToBytes(value: UInt32, buffer: inout Data, offset: Int) {
|
||||
guard buffer.count >= offset + 4 else { return }
|
||||
var intValue = value.bigEndian
|
||||
let intData = Data(bytes: &intValue, count: 4)
|
||||
buffer.replaceSubrange(offset..<offset+4, with: intData)
|
||||
}
|
||||
|
||||
static func intToIPAddress(_ ip: UInt32) -> String {
|
||||
return String(format: "%d.%d.%d.%d", (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF)
|
||||
}
|
||||
|
||||
static func calculateTCPHeaderChecksum(data: Data, offset: Int, tcpLength: Int, sourceIP: UInt32, destinationIP: UInt32) -> Data {
|
||||
var bufferSize = tcpLength + 12
|
||||
var isOdd = false
|
||||
if bufferSize % 2 != 0 {
|
||||
bufferSize += 1
|
||||
isOdd = true
|
||||
}
|
||||
|
||||
var buffer = Data()
|
||||
|
||||
// Add source IP
|
||||
buffer.append(contentsOf: sourceIP.bytes)
|
||||
// Add destination IP
|
||||
buffer.append(contentsOf: destinationIP.bytes)
|
||||
|
||||
// Add reserved byte and protocol (6 for TCP)
|
||||
buffer.append(0)
|
||||
buffer.append(6)
|
||||
|
||||
// Add TCP length
|
||||
buffer.append(contentsOf: UInt16(tcpLength).bytes)
|
||||
|
||||
// Add TCP header and data
|
||||
buffer.append(contentsOf: data[offset..<offset + tcpLength])
|
||||
|
||||
// Pad with zero if odd length
|
||||
if isOdd {
|
||||
buffer.append(0)
|
||||
}
|
||||
|
||||
// Calculate checksum
|
||||
return calculateChecksum(data: buffer, offset: 0, length: bufferSize)
|
||||
}
|
||||
|
||||
static func calculateChecksum(data: Data, offset: Int, length: Int) -> Data {
|
||||
var start = offset
|
||||
var sum = 0
|
||||
|
||||
while start < length {
|
||||
sum += getNetworkInt(buffer: data, start: start, length: 2)
|
||||
start += 2
|
||||
}
|
||||
|
||||
// Carry over one's complement
|
||||
while (sum >> 16) > 0 {
|
||||
sum = (sum & 0xFFFF) + (sum >> 16)
|
||||
}
|
||||
|
||||
// Flip the bits to get one's complement
|
||||
sum = ~sum
|
||||
|
||||
// Extract the last two bytes of the int
|
||||
let checksum = Data([UInt8(truncatingIfNeeded: (sum >> 8) & 0xFF), UInt8(truncatingIfNeeded: sum & 0xFF)])
|
||||
return checksum
|
||||
}
|
||||
|
||||
static func getNetworkInt(buffer: Data, start: Int, length: Int) -> Int {
|
||||
var value = 0
|
||||
var end = start + min(length, 4)
|
||||
if end > buffer.count { end = buffer.count }
|
||||
for i in start..<end {
|
||||
value = value | (Int(buffer[i]) & 0xFF)
|
||||
if i < end - 1 { value = value << 8 }
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
static func isPacketCorrupted(tcpHeader: TCPHeader) -> Bool {
|
||||
guard let options = tcpHeader.options else {
|
||||
return false
|
||||
}
|
||||
|
||||
var i = 0
|
||||
while i < options.count {
|
||||
let kind = options[i]
|
||||
switch kind {
|
||||
case 0, 1:
|
||||
break
|
||||
case 2:
|
||||
i += 3
|
||||
case 3, 14:
|
||||
i += 2
|
||||
case 4:
|
||||
i += 1
|
||||
case 5, 15:
|
||||
i += Int(options[i + 1]) - 2
|
||||
case 8:
|
||||
i += 9
|
||||
case 23:
|
||||
return true
|
||||
default:
|
||||
print("Unknown option: \(kind)")
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension FixedWidthInteger {
|
||||
var bytes: [UInt8] {
|
||||
withUnsafeBytes(of: self.bigEndian) { Array($0) }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user