From 4cccd411e04276f5d8cf4e3f9a581c0f918b6360 Mon Sep 17 00:00:00 2001 From: anlicheng <244108715@qq.com> Date: Tue, 10 Mar 2026 13:35:45 +0800 Subject: [PATCH] fix ip packet --- Tun/Punchnet/Actors/SDLContextActor.swift | 27 ++- Tun/Punchnet/NetworkStack/IPPacket.swift | 273 +++++++++++++++++----- Tun/Punchnet/UDPPacket.swift | 38 --- punchnet/punchnetApp.swift | 40 ++-- 4 files changed, 252 insertions(+), 126 deletions(-) delete mode 100644 Tun/Punchnet/UDPPacket.swift diff --git a/Tun/Punchnet/Actors/SDLContextActor.swift b/Tun/Punchnet/Actors/SDLContextActor.swift index d305264..66525db 100644 --- a/Tun/Punchnet/Actors/SDLContextActor.swift +++ b/Tun/Punchnet/Actors/SDLContextActor.swift @@ -566,22 +566,23 @@ actor SDLContextActor { // 检查权限逻辑 let identitySnapshot = self.snapshotPublisher.current() if let ruleMap = identitySnapshot.lookup(data.identityID) { - SDLLogger.shared.log("[SDLContext] identity: \(data.identityID), ruleMap: \(ruleMap)", level: .debug) - let proto = ipPacket.header.proto - switch TransportProtocol(rawValue: proto) { - case .udp, .tcp: - if let dstPort = ipPacket.getDstPort(), ruleMap.isAllow(proto: proto, port: dstPort) { + switch ipPacket.transportPacket() { + case .tcp(let tcpPacket): + let dstPort = tcpPacket.header.dstPort + if ruleMap.isAllow(proto: proto, port: dstPort) { let packet = NEPacket(data: ipPacket.data, protocolFamily: 2) self.provider.packetFlow.writePacketObjects([packet]) - } else { - if let dstPort = ipPacket.getDstPort() { - SDLLogger.shared.log("[SDLContext] identity: \(data.identityID), dst port: \(dstPort) not allow", level: .debug) - } else { - SDLLogger.shared.log("[SDLContext] identity: \(data.identityID), invalid ip packet, not allow", level: .debug) - } + SDLLogger.shared.log("[SDLContext] identity: \(data.identityID), ruleMap: \(ruleMap), dstPort: \(dstPort) allow", level: .debug) } - case .icmp: + case .udp(let udpPacket): + let dstPort = udpPacket.dstPort + if ruleMap.isAllow(proto: proto, port: dstPort) { + let packet = NEPacket(data: ipPacket.data, protocolFamily: 2) + self.provider.packetFlow.writePacketObjects([packet]) + SDLLogger.shared.log("[SDLContext] identity: \(data.identityID), ruleMap: \(ruleMap), dstPort: \(dstPort) allow", level: .debug) + } + case .icmp(_): let packet = NEPacket(data: ipPacket.data, protocolFamily: 2) self.provider.packetFlow.writePacketObjects([packet]) default: @@ -638,8 +639,6 @@ actor SDLContextActor { let networkAddr = self.config.networkAddress if SDLDNSClient.Helper.isDnsRequestPacket(ipPacket: packet) { - let destIp = packet.header.destination_ip - SDLLogger.shared.log("[DNSQuery] destIp: \(destIp), int: \(packet.header.destination.asIpAddress())", level: .debug) self.dnsClient?.forward(ipPacket: packet) return } diff --git a/Tun/Punchnet/NetworkStack/IPPacket.swift b/Tun/Punchnet/NetworkStack/IPPacket.swift index 0f1fe4f..78d8e7c 100644 --- a/Tun/Punchnet/NetworkStack/IPPacket.swift +++ b/Tun/Punchnet/NetworkStack/IPPacket.swift @@ -7,35 +7,6 @@ import Foundation -struct IPHeader { - let version: UInt8 - let headerLength: UInt8 - let typeOfService: UInt8 - let totalLength: UInt16 - let id: UInt16 - let offset: UInt16 - let timeToLive: UInt8 - let proto: UInt8 - let checksum: UInt16 - let source: UInt32 - let destination: UInt32 - - var source_ip: String { - return SDLUtil.int32ToIp(source) - } - - var destination_ip: String { - return SDLUtil.int32ToIp(destination) - } - - public var description: String { - """ - IPHeader version: \(version), header length: \(headerLength), type of service: \(typeOfService), total length: \(totalLength), - id: \(id), offset: \(offset), time ot live: \(timeToLive), proto: \(proto), checksum: \(checksum), source ip: \(source_ip), destination ip:\(destination_ip) - """ - } -} - enum IPVersion: UInt8 { case ipv4 = 4 case ipv6 = 6 @@ -43,46 +14,236 @@ enum IPVersion: UInt8 { enum TransportProtocol: UInt8 { case icmp = 1 - case tcp = 6 - case udp = 17 + case tcp = 6 + case udp = 17 } +// MARK: - IP Header + +struct IPHeader { + let version: UInt8 + let headerLength: UInt8 + let typeOfService: UInt8 + let totalLength: UInt16 + let id: UInt16 + let offset: UInt16 + let ttl: UInt8 + let proto: UInt8 + let checksum: UInt16 + let source: UInt32 + let destination: UInt32 + + var headerBytes: Int { + Int(headerLength) + } +} + +// MARK: - IP Packet + struct IPPacket { let header: IPHeader let data: Data + var payload: Data.SubSequence { + let offset = Int(header.headerLength) + + return data[offset..= 20 else { return nil } - - self.header = IPHeader(version: data[0] >> 4, - headerLength: (data[0] & 0b1111) * 4, - typeOfService: data[1], - totalLength: UInt16(bytes: (data[2], data[3])), - id: UInt16(bytes: (data[4], data[5])), - offset: 1, - timeToLive: data[8], - proto: data[9], - checksum: UInt16(bytes: (data[10], data[11])), - source: UInt32(bytes: (data[12], data[13], data[14], data[15])), - destination: UInt32(bytes: (data[16], data[17], data[18], data[19]))) - self.data = data - } - - // 获取负载部分 - func getPayload() -> Data { - return data.subdata(in: 20.. UInt16? { - guard case .ipv4 = IPVersion(rawValue: self.header.version), self.data.count >= 24 else { + + let version = data[0] >> 4 + let headerLen = (data[0] & 0x0F) * 4 + + guard data.count >= headerLen else { return nil } + + self.header = IPHeader( + version: version, + headerLength: headerLen, + typeOfService: data[1], + totalLength: UInt16(bytes: (data[2], data[3])), + id: UInt16(bytes: (data[4], data[5])), + offset: UInt16(bytes: (data[6], data[7])), + ttl: data[8], + proto: data[9], + checksum: UInt16(bytes: (data[10], data[11])), + source: UInt32(bytes: (data[12], data[13], data[14], data[15])), + destination: UInt32(bytes: (data[16], data[17], data[18], data[19])) + ) + + self.data = data + } +} + +// MARK: - TCP Flags + +struct TCPFlags: OptionSet { + let rawValue: UInt16 + + static let fin = TCPFlags(rawValue: 1 << 0) + static let syn = TCPFlags(rawValue: 1 << 1) + static let rst = TCPFlags(rawValue: 1 << 2) + static let psh = TCPFlags(rawValue: 1 << 3) + static let ack = TCPFlags(rawValue: 1 << 4) + static let urg = TCPFlags(rawValue: 1 << 5) + static let ece = TCPFlags(rawValue: 1 << 6) + static let cwr = TCPFlags(rawValue: 1 << 7) +} + +// MARK: - TCP Header + +struct TCPHeader { + let srcPort: UInt16 + let dstPort: UInt16 + + let seq: UInt32 + let ack: UInt32 + + let dataOffset: UInt8 + let flags: TCPFlags + + let window: UInt16 + let checksum: UInt16 + let urgentPointer: UInt16 + + var headerLength: Int { + Int(dataOffset) * 4 + } +} + +// MARK: - TCP Packet + +struct TCPPacket { + let header: TCPHeader + let payload: Data + + init?(_ data: Data) { + guard data.count >= 20 else { + return nil + } + + let srcPort = UInt16(bytes: (data[0], data[1])) + let dstPort = UInt16(bytes: (data[2], data[3])) + + let seq = UInt32(bytes: (data[4], data[5], data[6], data[7])) + let ack = UInt32(bytes: (data[8], data[9], data[10], data[11])) + + let offsetAndFlags = UInt16(bytes: (data[12], data[13])) + + let dataOffset = UInt8(offsetAndFlags >> 12) + let flags = TCPFlags(rawValue: offsetAndFlags & 0x01FF) + + let window = UInt16(bytes: (data[14], data[15])) + let checksum = UInt16(bytes: (data[16], data[17])) + let urgent = UInt16(bytes: (data[18], data[19])) + + let header = TCPHeader( + srcPort: srcPort, + dstPort: dstPort, + seq: seq, + ack: ack, + dataOffset: dataOffset, + flags: flags, + window: window, + checksum: checksum, + urgentPointer: urgent + ) + + let headerLen = header.headerLength + + guard data.count >= headerLen else { + return nil + } + + self.header = header + self.payload = data.subdata(in: headerLen..= 8 else { + return nil + } + + self.srcPort = UInt16(bytes: (data[0], data[1])) + self.dstPort = UInt16(bytes: (data[2], data[3])) + self.length = UInt16(bytes: (data[4], data[5])) + self.checksum = UInt16(bytes: (data[6], data[7])) + + self.payload = data.subdata(in: 8..= 4 else { + return nil + } + + self.type = data[0] + self.code = data[1] + self.checksum = UInt16(bytes: (data[2], data[3])) + self.payload = data.subdata(in: 4.. TransportPacket { + guard let proto = TransportProtocol(rawValue: header.proto) else { + return .unsupported(header.proto) + } - // 系统只会读取到ipv4的数据包,(srcPort:16, dstPort:16, ...) - return UInt16(bytes: (self.data[22], self.data[23])) + switch proto { + case .tcp: + guard let tcp = TCPPacket(payload) else { + return .malformed + } + return .tcp(tcp) + + case .udp: + guard let udp = UDPPacket(payload) else { + return .malformed + } + return .udp(udp) + + case .icmp: + guard let icmp = ICMPPacket(payload) else { + return .malformed + } + return .icmp(icmp) + } } } diff --git a/Tun/Punchnet/UDPPacket.swift b/Tun/Punchnet/UDPPacket.swift deleted file mode 100644 index 60185e9..0000000 --- a/Tun/Punchnet/UDPPacket.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// UDPPacket.swift -// Tun -// -// Created by 安礼成 on 2025/12/13. -// - -import Foundation - -struct UDPHeader { - let sourcePort: UInt16 - let destinationPort: UInt16 - let length: UInt16 - let checksum: UInt16 -} - -struct UDPPacket { - let header: UDPHeader - let payload: Data - - init?(_ data: Data) { - // UDP header 至少 8 字节 - guard data.count >= 8 else { - return nil - } - - let header = UDPHeader(sourcePort: UInt16(bytes: (data[0], data[1])), - destinationPort: UInt16(bytes: (data[2], data[3])), - length: UInt16(bytes: (data[4], data[5])), - checksum: UInt16(bytes: (data[6], data[7])) - ) - // UDP payload = length - 8 - let payloadLength = Int(header.length) - 8 - - self.header = header - self.payload = data.subdata(in: 8..<(8 + payloadLength)) - } -} diff --git a/punchnet/punchnetApp.swift b/punchnet/punchnetApp.swift index 8af7095..b1462d8 100644 --- a/punchnet/punchnetApp.swift +++ b/punchnet/punchnetApp.swift @@ -50,22 +50,22 @@ struct punchnetApp: App { RootView() .frame(width: 800, height: 500) .onAppear { - // 获取主屏幕的尺寸 - guard let screenFrame = NSScreen.main?.frame else { - return - } - - // 获取当前应用的窗口(假设只有一个窗口) - NSApplication.shared.windows.forEach { window in - // 计算窗口的中心位置 - let windowWidth = window.frame.width - let windowHeight = window.frame.height - let centerX = (screenFrame.width - windowWidth) / 2 - let centerY = (screenFrame.height - windowHeight) / 2 - - // 设置窗口位置 - window.setFrameOrigin(NSPoint(x: centerX, y: centerY)) - } +// // 获取主屏幕的尺寸 +// guard let screenFrame = NSScreen.main?.frame else { +// return +// } +// +// // 获取当前应用的窗口(假设只有一个窗口) +// NSApplication.shared.windows.forEach { window in +// // 计算窗口的中心位置 +// let windowWidth = window.frame.width +// let windowHeight = window.frame.height +// let centerX = (screenFrame.width - windowWidth) / 2 +// let centerY = (screenFrame.height - windowHeight) / 2 +// +// // 设置窗口位置 +// window.setFrameOrigin(NSPoint(x: centerX, y: centerY)) +// } } //.toolbar(.hidden) .navigationTitle("") @@ -83,25 +83,29 @@ struct punchnetApp: App { } .windowResizability(.contentSize) .windowToolbarStyle(.unified) + .defaultPosition(.center) Window("设置", id: "settings") { SettingsView() .environment(self.userContext) } .defaultSize(width: 800, height: 500) + .defaultPosition(.center) Window("重置密码", id: "resetPassword") { ResetPasswordRootView() .environment(self.userContext) } .defaultSize(width: 800, height: 500) - + .defaultPosition(.center) + Window("注册", id: "register") { ResetPasswordRootView() .environment(self.userContext) } .defaultSize(width: 800, height: 500) - + .defaultPosition(.center) + MenuBarExtra("punchnet", image: "logo_32") { VStack { Button(action: {