From cd24d2ac7c5c62a1501b13c92ca09fd17b66865e Mon Sep 17 00:00:00 2001 From: anlicheng <244108715@qq.com> Date: Fri, 1 Aug 2025 10:57:13 +0800 Subject: [PATCH] fix --- Sources/Punchnet/SDLContextNew.swift | 567 +++++++++++++++++++++ Sources/Punchnet/SDLSuperClientActor.swift | 5 +- Sources/Punchnet/SDLUDPHoleActor.swift | 6 +- 3 files changed, 574 insertions(+), 4 deletions(-) create mode 100644 Sources/Punchnet/SDLContextNew.swift diff --git a/Sources/Punchnet/SDLContextNew.swift b/Sources/Punchnet/SDLContextNew.swift new file mode 100644 index 0000000..e46b2bd --- /dev/null +++ b/Sources/Punchnet/SDLContextNew.swift @@ -0,0 +1,567 @@ +// +// SDLContext.swift +// Tun +// +// Created by 安礼成 on 2024/2/29. +// + +import Foundation +import NetworkExtension +import NIOCore +import Combine + +// 上下文环境变量,全局共享 +/* +1. 处理rsa的加解密逻辑 + */ + +public class SDLContextNew: @unchecked Sendable { + + // 路由信息 + struct Route { + let dstAddress: String + let subnetMask: String + + var debugInfo: String { + return "\(dstAddress):\(subnetMask)" + } + } + + let config: SDLConfiguration + + // tun网络地址信息 + var devAddr: SDLDevAddr + + // nat映射的相关信息, 暂时没有用处 + //var natAddress: SDLNatAddress? + // nat的网络类型 + var natType: SDLNatProber.NatType = .blocked + + // AES加密,授权通过后,对象才会被创建 + var aesCipher: AESCipher + + // aes + var aesKey: Data = Data() + + // rsa的相关配置, public_key是本地生成的 + let rsaCipher: RSACipher + + // 依赖的变量 + var udpHoleActor: SDLUDPHoleActor? + private var udpCancel: AnyCancellable? + + var superClientActor: SDLSuperClientActor? + private var superCancel: AnyCancellable? + + // 数据包读取任务 + private var readTask: Task<(), Never>? + + let provider: NEPacketTunnelProvider + + private var sessionManager: SessionManager + private var holerManager: HolerManager + private var arpServer: ArpServer + + // 记录最后发送的stunRequest的cookie + private var lastCookie: UInt32? = 0 + + // 定时器 + private var stunCancel: AnyCancellable? + + // 网络状态变化的健康 + private var monitor = SDLNetworkMonitor() + private var monitorCancel: AnyCancellable? + + // 内部socket通讯 + private var noticeClient: SDLNoticeClient + + // 流量统计 + private var flowTracer = SDLFlowTracerActor() + private var flowTracerCancel: AnyCancellable? + + public init(provider: NEPacketTunnelProvider, config: SDLConfiguration, rsaCipher: RSACipher, aesCipher: AESCipher) { + self.config = config + self.rsaCipher = rsaCipher + self.aesCipher = aesCipher + + // 生成mac地址 + var devAddr = SDLDevAddr() + devAddr.mac = Self.getMacAddress() + self.devAddr = devAddr + + self.provider = provider + self.sessionManager = SessionManager() + self.holerManager = HolerManager() + self.arpServer = ArpServer(known_macs: [:]) + self.noticeClient = SDLNoticeClient() + } + + public func start() async throws { + try await self.startSuperClient() + try await self.startUDPHole() + self.noticeClient.start() + + // 启动网络监控 + self.monitorCancel = self.monitor.eventFlow.sink { event in + switch event { + case .changed: + // 需要重新探测网络的nat类型 + Task { + self.natType = await SDLNatProber.getNatType(udpHole: self.udpHole, config: self.config) + NSLog("didNetworkPathChanged, nat type is: \(self.natType)") + } + case .unreachable: + NSLog("didNetworkPathUnreachable") + } + } + self.monitor.start() + } + + public func stop() async { + self.superCancel?.cancel() + self.superClient = nil + + self.udpCancel?.cancel() + self.udpHole = nil + + self.readTask?.cancel() + } + + private func startSuperClient() async throws { + self.superClient = SDLSuperClient(host: config.superHost, port: config.superPort) + // 建立super的绑定关系 + self.superCancel?.cancel() + self.superCancel = self.superClient?.eventFlow.sink { event in + Task { + await self.handleSuperEvent(event: event) + } + } + try await self.superClient?.start() + } + + private func handleSuperEvent(event: SDLSuperClient.SuperEvent) async { + switch event { + case .ready: + NSLog("[SDLContext] get registerSuper, mac address: \(Self.formatMacAddress(mac: self.devAddr.mac))") + guard let message = await self.superClient?.registerSuper(context: self) else { + return + } + + switch message.packet { + case .registerSuperAck(let registerSuperAck): + // 需要对数据通过rsa的私钥解码 + let aesKey = try! self.rsaCipher.decode(data: Data(registerSuperAck.aesKey)) + let upgradeType = SDLUpgradeType(rawValue: registerSuperAck.upgradeType) + + NSLog("[SDLContext] get registerSuperAck, aes_key len: \(aesKey.count)") + self.devAddr = registerSuperAck.devAddr + + if upgradeType == .force { + let forceUpgrade = NoticeMessage.UpgradeMessage(prompt: registerSuperAck.upgradePrompt, address: registerSuperAck.upgradeAddress) + self.noticeClient.send(data: forceUpgrade.binaryData) + exit(-1) + } + + // 服务器分配的tun网卡信息 + await self.didNetworkConfigChanged(devAddr: self.devAddr) + self.aesKey = aesKey + + if upgradeType == .normal { + let normalUpgrade = NoticeMessage.UpgradeMessage(prompt: registerSuperAck.upgradePrompt, address: registerSuperAck.upgradeAddress) + self.noticeClient.send(data: normalUpgrade.binaryData) + } + + case .registerSuperNak(let nakPacket): + let errorMessage = nakPacket.errorMessage + guard let errorCode = SDLNAKErrorCode(rawValue: UInt8(nakPacket.errorCode)) else { + return + } + + switch errorCode { + case .invalidToken, .nodeDisabled: + let alertNotice = NoticeMessage.AlertMessage(alert: errorMessage) + self.noticeClient.send(data: alertNotice.binaryData) + exit(-1) + case .noIpAddress, .networkFault, .internalFault: + let alertNotice = NoticeMessage.AlertMessage(alert: errorMessage) + self.noticeClient.send(data: alertNotice.binaryData) + } + NSLog("[SDLContext] Get a SuperNak message exit") + default: + () + } + + case .closed: + NSLog("[SDLContext] super client closed") + await self.arpServer.clear() + DispatchQueue.main.asyncAfter(deadline: .now() + 5) { + Task {@MainActor in + try await self.startSuperClient() + } + } + case .event(let evt): + switch evt { + case .natChanged(let natChangedEvent): + let dstMac = natChangedEvent.mac + NSLog("[SDLContext] natChangedEvent, dstMac: \(dstMac)") + await sessionManager.removeSession(dstMac: dstMac) + case .sendRegister(let sendRegisterEvent): + NSLog("[SDLContext] sendRegisterEvent, ip: \(sendRegisterEvent)") + let address = SDLUtil.int32ToIp(sendRegisterEvent.natIp) + if let remoteAddress = try? SocketAddress.makeAddressResolvingHost(address, port: Int(sendRegisterEvent.natPort)) { + // 发送register包 + self.udpHole?.sendRegister(context: self, remoteAddress: remoteAddress, dst_mac: sendRegisterEvent.dstMac) + } + + case .networkShutdown(let shutdownEvent): + let alertNotice = NoticeMessage.AlertMessage(alert: shutdownEvent.message) + self.noticeClient.send(data: alertNotice.binaryData) + exit(-1) + } + case .command(let packetId, let command): + switch command { + case .changeNetwork(let changeNetworkCommand): + // 需要对数据通过rsa的私钥解码 + let aesKey = try! self.rsaCipher.decode(data: Data(changeNetworkCommand.aesKey)) + NSLog("[SDLContext] change network command get aes_key len: \(aesKey.count)") + self.devAddr = changeNetworkCommand.devAddr + + // 服务器分配的tun网卡信息 + await self.didNetworkConfigChanged(devAddr: self.devAddr) + self.aesKey = aesKey + + var commandAck = SDLCommandAck() + commandAck.status = true + + self.superClient?.commandAck(packetId: packetId, ack: commandAck) + } + } + + } + + private func startUDPHole() async throws { + self.udpHole = SDLUDPHole() + + self.udpCancel?.cancel() + self.udpCancel = self.udpHole?.eventFlow.sink { event in + Task.detached { + await self.handleUDPEvent(event: event) + } + } + + try await self.udpHole?.start() + } + + private func handleUDPEvent(event: SDLUDPHole.UDPEvent) async { + switch event { + case .ready: + // 获取当前网络的类型 + self.natType = await SDLNatProber.getNatType(udpHole: self.udpHole, config: self.config) + SDLLogger.log("[SDLContext] nat type is: \(self.natType)", level: .debug) + + let timer = Timer.publish(every: 5.0, on: .main, in: .common).autoconnect() + self.stunCancel = Just(Date()).merge(with: timer).sink { _ in + self.lastCookie = self.udpHole?.stunRequest(context: self) + } + + case .closed: + DispatchQueue.main.asyncAfter(deadline: .now() + 5) { + Task { + try await self.startUDPHole() + } + } + + case .message(let remoteAddress, let message): + switch message { + case .register(let register): + NSLog("register packet: \(register), dev_addr: \(self.devAddr)") + // 判断目标地址是否是tun的网卡地址, 并且是在同一个网络下 + if register.dstMac == self.devAddr.mac && register.networkID == self.devAddr.networkID { + // 回复ack包 + self.udpHole?.sendRegisterAck(context: self, remoteAddress: remoteAddress, dst_mac: register.srcMac) + // 这里需要建立到来源的会话, 在复杂网络下,通过super-node查询到的nat地址不一定靠谱,需要通过udp包的来源地址作为nat地址 + let session = Session(dstMac: register.srcMac, natAddress: remoteAddress) + await self.sessionManager.addSession(session: session) + } else { + SDLLogger.log("SDLContext didReadRegister get a invalid packet, because dst_ip not matched: \(register.dstMac)", level: .warning) + } + case .registerAck(let registerAck): + // 判断目标地址是否是tun的网卡地址, 并且是在同一个网络下 + if registerAck.dstMac == self.devAddr.mac && registerAck.networkID == self.devAddr.networkID { + let session = Session(dstMac: registerAck.srcMac, natAddress: remoteAddress) + await self.sessionManager.addSession(session: session) + } else { + SDLLogger.log("SDLContext didReadRegisterAck get a invalid packet, because dst_mac not matched: \(registerAck.dstMac)", level: .warning) + } + case .stunReply(let stunReply): + let cookie = stunReply.cookie + if cookie == self.lastCookie { + // 记录下当前在nat上的映射信息,暂时没有用;后续会用来判断网络类型 + //self.natAddress = stunReply.natAddress + SDLLogger.log("[SDLContext] get a stunReply: \(try! stunReply.jsonString())") + } + default: + () + } + + case .data(let data): + let mac = LayerPacket.MacAddress(data: data.dstMac) + guard (data.dstMac == self.devAddr.mac || mac.isBroadcast() || mac.isMulticast()) else { + NSLog("[SDLContext] didReadData 1") + return + } + + guard let decyptedData = try? self.aesCipher.decypt(aesKey: self.aesKey, data: Data(data.data)) else { + NSLog("[SDLContext] didReadData 2") + return + } + + do { + let layerPacket = try LayerPacket(layerData: decyptedData) + + await self.flowTracer.inc(num: decyptedData.count, type: .inbound) + // 处理arp请求 + switch layerPacket.type { + case .arp: + // 判断如果收到的是arp请求 + if let arpPacket = ARPPacket(data: layerPacket.data) { + if arpPacket.targetIP == self.devAddr.netAddr { + switch arpPacket.opcode { + case .request: + NSLog("[SDLContext] get arp request packet") + let response = ARPPacket.arpResponse(for: arpPacket, mac: self.devAddr.mac, ip: self.devAddr.netAddr) + await self.routeLayerPacket(dstMac: arpPacket.senderMAC, type: .arp, data: response.marshal()) + case .response: + NSLog("[SDLContext] get arp response packet") + await self.arpServer.append(ip: arpPacket.senderIP, mac: arpPacket.senderMAC) + } + } else { + NSLog("[SDLContext] get invalid arp packet, target_ip: \(arpPacket)") + } + } else { + NSLog("[SDLContext] get invalid arp packet") + } + case .ipv4: + NSLog("[SDLContext] get ipv4 packet") + guard let ipPacket = IPPacket(layerPacket.data), ipPacket.header.destination == self.devAddr.netAddr else { + return + } + + let packet = NEPacket(data: ipPacket.data, protocolFamily: 2) + self.provider.packetFlow.writePacketObjects([packet]) + default: + NSLog("[SDLContext] get invalid packet") + } + } catch let err { + NSLog("[SDLContext] didReadData err: \(err)") + } + } + + } + + // 流量统计 + public func flowReportTask() { + Task { + // 每分钟汇报一次 + self.flowTracerCancel = Timer.publish(every: 60.0, on: .main, in: .common).autoconnect() + .sink { _ in + Task { + let (forwardNum, p2pNum, inboundNum) = await self.flowTracer.reset() + self.superClient?.flowReport(forwardNum: forwardNum, p2pNum: p2pNum, inboundNum: inboundNum) + } + } + } + } + + // 网络改变时需要重新配置网络信息 + private func didNetworkConfigChanged(devAddr: SDLDevAddr, dnsServers: [String]? = nil) async { + let netAddress = SDLNetAddress(ip: devAddr.netAddr, maskLen: UInt8(devAddr.netBitLen)) + let routes = [Route(dstAddress: netAddress.networkAddress, subnetMask: netAddress.maskAddress)] + + // Add code here to start the process of connecting the tunnel. + let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "8.8.8.8") + networkSettings.mtu = 1460 + + // 设置网卡的DNS解析 + if let dnsServers { + networkSettings.dnsSettings = NEDNSSettings(servers: dnsServers) + } else { + networkSettings.dnsSettings = NEDNSSettings(servers: ["8.8.8.8", "114.114.114.114"]) + } + + NSLog("[SDLContext] Tun started at network ip: \(netAddress.ipAddress), mask: \(netAddress.maskAddress)") + + let ipv4Settings = NEIPv4Settings(addresses: [netAddress.ipAddress], subnetMasks: [netAddress.maskAddress]) + // 设置路由表 + //NEIPv4Route.default() + ipv4Settings.includedRoutes = routes.map { route in + NEIPv4Route(destinationAddress: route.dstAddress, subnetMask: route.subnetMask) + } + networkSettings.ipv4Settings = ipv4Settings + // 网卡配置设置必须成功 + do { + try await self.provider.setTunnelNetworkSettings(networkSettings) + + await self.holerManager.cleanup() + self.startReader() + + NSLog("[SDLContext] setTunnelNetworkSettings success, start read packet") + } catch let err { + NSLog("[SDLContext] setTunnelNetworkSettings get error: \(err)") + exit(-1) + } + } + + // 开始读取数据, 用单独的线程处理packetFlow + private func startReader() { + // 停止之前的任务 + self.readTask?.cancel() + + // 开启新的任务 + self.readTask = Task(priority: .high) { + repeat { + if Task.isCancelled { + break + } + + let (packets, numbers) = await self.provider.packetFlow.readPackets() + for (data, number) in zip(packets, numbers) where number == 2 { + if let packet = IPPacket(data) { + Task.detached { + let dstIp = packet.header.destination + // 本地通讯, 目标地址是本地服务器的ip地址 + if dstIp == self.devAddr.netAddr { + let nePacket = NEPacket(data: packet.data, protocolFamily: 2) + self.provider.packetFlow.writePacketObjects([nePacket]) + return + } + + // 查找arp缓存中是否有目标mac地址 + if let dstMac = await self.arpServer.query(ip: dstIp) { + await self.routeLayerPacket(dstMac: dstMac, type: .ipv4, data: packet.data) + } + else { + // 构造arp请求 + let broadcastMac = Data([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]) + let arpReqeust: ARPPacket = ARPPacket.arpRequest(senderIP: self.devAddr.netAddr, senderMAC: self.devAddr.mac, targetIP: dstIp) + await self.routeLayerPacket(dstMac: broadcastMac, type: .arp, data: arpReqeust.marshal()) + + NSLog("[SDLContext] dstIp: \(dstIp) arp query not found") + } + } + } + } + + } while true + } + + } + + private func routeLayerPacket(dstMac: Data, type: LayerPacket.PacketType, data: Data) async { + // 将数据封装层2层的数据包 + let layerPacket = LayerPacket(dstMac: dstMac, srcMac: self.devAddr.mac, type: type, data: data) + guard let encodedPacket = try? self.aesCipher.encrypt(aesKey: self.aesKey, data: layerPacket.marshal()) else { + return + } + + // 通过session发送到对端 + if let session = await self.sessionManager.getSession(toAddress: dstMac) { + NSLog("[SDLContext] send packet by session: \(session)") + self.udpHole?.sendPacket(context: self, session: session, data: encodedPacket) + + await self.flowTracer.inc(num: data.count, type: .p2p) + } + else { + // 通过super_node进行转发 + self.udpHole?.forwardPacket(context: self, dst_mac: dstMac, data: encodedPacket) + // 流量统计 + await self.flowTracer.inc(num: data.count, type: .forward) + + // 尝试打洞 + await self.holerManager.addHoler(dstMac: dstMac) { + self.holerTask(dstMac: dstMac) + } + } + } + + func holerTask(dstMac: Data) -> Task<(), Never> { + return Task { + guard let message = try? await self.superClient?.queryInfo(context: self, dst_mac: dstMac) else { + return + } + + switch message.packet { + case .empty: + SDLLogger.log("[SDLContext] hole query_info get empty: \(message)", level: .debug) + case .peerInfo(let peerInfo): + if let remoteAddress = peerInfo.v4Info.socketAddress() { + SDLLogger.log("[SDLContext] hole sock address: \(remoteAddress)", level: .warning) + // 发送register包 + self.udpHole?.sendRegister(context: self, remoteAddress: remoteAddress, dst_mac: dstMac) + } else { + SDLLogger.log("[SDLContext] hole sock address is invalid: \(peerInfo.v4Info)", level: .warning) + } + default: + SDLLogger.log("[SDLContext] hole query_info is packet: \(message)", level: .warning) + } + } + } + + deinit { + self.stunCancel?.cancel() + self.udpHole = nil + self.superClient = nil + } + +} + +//--MARK: 获取设备的UUID + +extension SDLContextNew { + + public static func getUUID() -> String { + let userDefaults = UserDefaults.standard + if let uuid = userDefaults.value(forKey: "gClientId") as? String { + return uuid + } else { + let uuid = UUID().uuidString.replacingOccurrences(of: "-", with: "").lowercased() + userDefaults.setValue(uuid, forKey: "gClientId") + + return uuid + } + } + + // 获取mac地址 + public static func getMacAddress() -> Data { + let key = "gMacAddress2" + + let userDefaults = UserDefaults.standard + if let mac = userDefaults.value(forKey: key) as? Data { + return mac + } + else { + let mac = generateMacAddress() + userDefaults.setValue(mac, forKey: key) + + return mac + } + } + + // 随机生成mac地址 + private static func generateMacAddress() -> Data { + var macAddress = [UInt8](repeating: 0, count: 6) + for i in 0..<6 { + macAddress[i] = UInt8.random(in: 0...255) + } + + return Data(macAddress) + } + + // 将mac地址转换成字符串 + private static func formatMacAddress(mac: Data) -> String { + let bytes = [UInt8](mac) + + return bytes.map { String(format: "%02X", $0) }.joined(separator: ":").lowercased() + } + +} diff --git a/Sources/Punchnet/SDLSuperClientActor.swift b/Sources/Punchnet/SDLSuperClientActor.swift index 2c03aa9..f86ee51 100644 --- a/Sources/Punchnet/SDLSuperClientActor.swift +++ b/Sources/Punchnet/SDLSuperClientActor.swift @@ -56,7 +56,9 @@ actor SDLSuperClientActor { )) } .get() - + } + + func start() async throws { try await self.asyncChannel.executeThenClose { inbound, outbound in try await withThrowingTaskGroup(of: Void.self) { group in group.addTask { @@ -105,7 +107,6 @@ actor SDLSuperClientActor { } } } - // -- MARK: apis diff --git a/Sources/Punchnet/SDLUDPHoleActor.swift b/Sources/Punchnet/SDLUDPHoleActor.swift index d93e222..a4cede4 100644 --- a/Sources/Punchnet/SDLUDPHoleActor.swift +++ b/Sources/Punchnet/SDLUDPHoleActor.swift @@ -39,7 +39,7 @@ actor SDLUDPHoleActor { init() async throws { let bootstrap = DatagramBootstrap(group: group) .channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1) - + self.asyncChannel = try await bootstrap.bind(host: "0.0.0.0", port: 0) .flatMapThrowing { channel in return try NIOAsyncChannel(wrappingChannelSynchronously: channel, configuration: .init( @@ -51,7 +51,9 @@ actor SDLUDPHoleActor { self.localAddress = self.asyncChannel.channel.localAddress SDLLogger.log("[UDPHole] started and listening on: \(self.localAddress!)", level: .debug) - + } + + func start() async throws { try await self.asyncChannel.executeThenClose { inbound, outbound in try await withThrowingTaskGroup(of: Void.self) { group in group.addTask {