// // SDLContext.swift // Tun // // Created by 安礼成 on 2024/2/29. // import Foundation import NetworkExtension import NIOCore // 上下文环境变量,全局共享 /* 1. 处理rsa的加解密逻辑 */ func startMonitorTask(name: String, _ body: @escaping () async throws -> Void, retryDelay: Duration = .seconds(5)) -> Task { return Task(name: name) { while true { do { try Task.checkCancellation() try await body() } catch is CancellationError { SDLLogger.log("[SDLContext] worker \(name) cancelled", for: .debug) break } catch let err { SDLLogger.log("[SDLContext] worker \(name) crashed: \(err.localizedDescription), will restart", for: .debug) do { try await Task.sleep(for: retryDelay) } catch is CancellationError { break } catch { break } } } } } enum SDLContextError: Error { case udpHoleClosed case dnsLocalClientClosed case dnsLocalClientCancelled case dnsClientClosed case dnsClientCancelled } actor SDLContextActor { private var config: SDLConfiguration // nat的网络类型 var natType: SDLNATProberActor.NatType = .blocked // AES加密,授权通过后,对象才会被创建 private var dataCipher: CCDataCipher? // session token private var sessionToken: Data? // rsa的相关配置, public_key是本地生成的 // 加密算法相关 nonisolated let rsaCipher: RSACipher private var dnsService: DNSService? private let superServiceProxy: SDLSuperServiceProxy private let udpHoleServiceProxy: SDLUDPHoleServiceProxy private let packetOutboundActor: PacketOutboundActor private let packetInboundActor: PacketInboundActor private let tunNetworkManager: SDLTunNetworkManager private let publicDnsServers = ["223.5.5.5", "119.29.29.29"] nonisolated private let puncherActor: SDLPuncherActor // 网络探测对象 nonisolated private let proberActor: SDLNATProberActor // 本地ipv6地址信息探测 private var ipv6AssistClient: SDLIPV6AssistClient? private let sessionManager: SessionManager nonisolated private let arpResolver: ArpResolver // 内部socket通讯 // 改为基于 App Group + Darwin Notification 的通知 // 流量统计 nonisolated private let flowTracer: SDLFlowTracer nonisolated private let provider: NEPacketTunnelProvider // 处理权限控制 private let policyService: PolicyService private var updatePolicyWorker: PeriodicWorker? // stunRequest任务 private var stunRequestWorker: PeriodicWorker? public init(provider: NEPacketTunnelProvider, config: SDLConfiguration, rsaCipher: RSACipher) { let puncherActor = SDLPuncherActor() let proberActor = SDLNATProberActor(addressArray: config.stunProbeSocketAddressArray) let sessionManager = SessionManager() let arpResolver = ArpResolver() let flowTracer = SDLFlowTracer() let policyService = PolicyService(identityId: config.identityId) let superServiceProxy = SDLSuperServiceProxy() let udpHoleServiceProxy = SDLUDPHoleServiceProxy() let tunNetworkManager = SDLTunNetworkManager(provider: provider) let packetOutboundActor = PacketOutboundActor( provider: provider, config: config, dataCipher: nil, sessionManager: sessionManager, arpResolver: arpResolver, puncherActor: puncherActor, policyService: policyService, superServiceProxy: superServiceProxy, udpHoleServiceProxy: udpHoleServiceProxy, flowTracer: flowTracer ) let packetInboundActor = PacketInboundActor( provider: provider, config: config, dataCipher: nil, policyService: policyService, packetOutboundActor: packetOutboundActor, arpResolver: arpResolver, superServiceProxy: superServiceProxy, flowTracer: flowTracer ) self.provider = provider self.config = config self.rsaCipher = rsaCipher self.puncherActor = puncherActor self.proberActor = proberActor self.sessionManager = sessionManager self.arpResolver = arpResolver self.flowTracer = flowTracer // 权限控制 self.policyService = policyService self.superServiceProxy = superServiceProxy self.udpHoleServiceProxy = udpHoleServiceProxy self.packetOutboundActor = packetOutboundActor self.packetInboundActor = packetInboundActor self.tunNetworkManager = tunNetworkManager } public func start() async { self.prepareTunnelNotifier() // 启动arp的定时清理任务 await self.puncherActor.start() await self.arpResolver.start() let dnsService = DNSService(serverIP: self.config.serverEndpoint.ip, publicDnsServers: self.publicDnsServers) { [weak self] event in await self?.handleDNSEvent(event) } self.dnsService = dnsService await self.packetOutboundActor.updateDNSService(dnsService) await dnsService.start() let udpHoleEventHandler = await self.udpHoleServiceProxy.makeEventHandler { [weak self] event in await self?.handleUDPHoleControlEvent(event) } let packetInboundActor = self.packetInboundActor let udpHoleService = SDLUDPHoleService( proberActor: self.proberActor, onEvent: udpHoleEventHandler, onData: { data in await packetInboundActor.handleData(data) } ) await self.udpHoleServiceProxy.replace(udpHoleService) await udpHoleService.start(includeV6: false) let superService = SDLSuperService(serverEndpoint: self.config.serverEndpoint) { [weak self] message in await self?.handleSuperMessage(message: message) } await self.superServiceProxy.replace(superService) await superService.start() } // 处理context的停止问题 public func stop() async { await self.puncherActor.stop() await self.arpResolver.stop() await self.sessionManager.clear() await self.stunRequestWorker?.stop() self.stunRequestWorker = nil await self.updatePolicyWorker?.stop() self.updatePolicyWorker = nil await self.policyService.clear() await self.packetOutboundActor.stop() await self.udpHoleServiceProxy.stop() let dnsService = self.dnsService self.dnsService = nil await self.packetOutboundActor.updateDNSService(nil) await dnsService?.stop() await self.superServiceProxy.stop() self.sessionToken = nil self.dataCipher = nil self.natType = .blocked await self.packetOutboundActor.updateRuntime(config: self.config, dataCipher: nil) await self.packetInboundActor.updateRuntime(config: self.config, dataCipher: nil) await self.ipv6AssistClient?.stop() self.ipv6AssistClient = nil } deinit { SDLLogger.log("[SDLContext] deinit", for: .debug) } } // MARK: probe网络类型 extension SDLContextActor { private func setNatType(natType: SDLNATProberActor.NatType) { self.natType = natType } } // MARK: Notifier通知机制 extension SDLContextActor { private func prepareTunnelNotifier() { // 启动noticeClient // 旧的 UDP NoticeClient 已移除,改为初始化基于 App Group 的通知通道。 SDLTunnelAppNotifier.shared.clear() SDLLogger.log("[SDLContext] tunnelAppNotifier ready") } private func publishTunnelEvent(code: Int? = nil, message: String) { SDLTunnelAppNotifier.shared.publish(code: code, message: message) } } // MARK: 数据发送 extension SDLContextActor { // 发送给super/stun节点的数据 private func sendSuperPacket(type: SDLPacketType, data: Data) async { await self.sendPacket(type: type, data: data, remoteAddress: self.config.stunSocketAddress) } // 发送给peer的数据 private func sendPeerPacket(type: SDLPacketType, data: Data, remoteAddress: SocketAddress) async { await self.sendPacket(type: type, data: data, remoteAddress: remoteAddress) } private func sendPacket(type: SDLPacketType, data: Data, remoteAddress: SocketAddress) async { await self.udpHoleServiceProxy.send(type: type, data: data, remoteAddress: remoteAddress) } } // MARK: 处理和Super之间的通讯 extension SDLContextActor { private func handleSuperMessage(message: SDLQUICInboundMessage) async { switch message { case .welcome(let welcome): SDLLogger.log("[SDLContext] quic welcome: \(welcome)") await self.ipv6AssistClient?.stop() self.ipv6AssistClient = nil // 尝试创建v6地址的辅助探测器 if welcome.hasIpv6Assist { self.ipv6AssistClient = SDLIPV6AssistClient(assistServerInfo: welcome.ipv6Assist) await self.ipv6AssistClient?.start() } // 注册 await self.doRegisterSuper() SDLLogger.log("[SDLContext] quic doRegisterSuper") case .pong: //SDLLogger.shared.log("[SDLContext] quic pong") () case .registerSuperAck(let registerSuperAck): await self.handleRegisterSuperAck(registerSuperAck: registerSuperAck) case .registerSuperNak(let registerSuperNak): self.handleRegisterSuperNak(nakPacket: registerSuperNak) case .peerInfo(let peerInfo): SDLLogger.log("[SDLContext] peer message: \(peerInfo)") let packets = await self.puncherActor.makeRegisterPackets(peerInfo: peerInfo) for packet in packets { await self.udpHoleServiceProxy.send(type: .register, data: packet.data, remoteAddress: packet.remoteAddress) } case .event(let event): await self.handleEvent(event: event) case .policyReponse(let policyResponse): // 处理权限的请求问题 await self.policyService.applyPolicyResponse(policyResponse) case .arpResponse(let arpResponse): SDLLogger.log("[SDLContext] get arp response: \(arpResponse)") await self.arpResolver.handleArpResponse(arpResponse: arpResponse) } } private func makeSuperEventProcessor() -> SDLSuperEventProcessor { return .init(networkAddress: self.config.networkAddress) } private func handleRegisterSuperAck(registerSuperAck: SDLRegisterSuperAck) async { // 需要对数据通过rsa的私钥解码 guard let key = try? self.rsaCipher.decode(data: Data(registerSuperAck.key)) else { SDLLogger.log("[SDLContext] registerSuperAck invalid key") let error = SDLError.invalidKey self.provider.cancelTunnelWithError(error) return } let algorithm = registerSuperAck.algorithm.lowercased() let regionId = registerSuperAck.regionID self.sessionToken = registerSuperAck.sessionToken switch algorithm { case "aes": self.dataCipher = CCAESChiper(key: key) case "chacha20": self.dataCipher = CCChaCha20Cipher(regionId: regionId, keyData: key) default: SDLLogger.log("[SDLContext] registerSuperAck invalid algorithm \(algorithm)") let error = SDLError.unsupportedAlgorithm(algorithm: algorithm) self.provider.cancelTunnelWithError(error) return } await self.packetOutboundActor.updateRuntime(config: self.config, dataCipher: self.dataCipher) await self.packetInboundActor.updateRuntime(config: self.config, dataCipher: self.dataCipher) SDLLogger.log("[SDLContext] registerSuperAck, use algorithm \(algorithm), key len: \(key.count)") // 服务器分配的tun网卡信息 do { try await self.tunNetworkManager.apply(settings: .init(config: self.config), dnsServer: DNSHelper.dnsServer) SDLLogger.log("[SDLContext] setNetworkSettings successed") await self.packetOutboundActor.startPacketReader() // 开启权限的定时更新 await self.whenRegistedSuper() } catch let err { SDLLogger.log("[SDLContext] setTunnelNetworkSettings get error: \(err)") self.provider.cancelTunnelWithError(err) } } // 注册成功super的回调函数 private func whenRegistedSuper() async { await self.updatePolicyWorker?.stop() let policyService = self.policyService let superServiceProxy = self.superServiceProxy let updatePolicyWorker = PeriodicWorker( configuration: .init( interval: .seconds(10), runImmediately: true, mode: .fixedDelay, errorPolicy: .keepRunning(delay: .seconds(5)) ), operation: { SDLLogger.log("[SDLContext] updatePolicyTask execute") await policyService.updatePolicy(superServiceProxy: superServiceProxy) }, onError: { err in SDLLogger.log("[SDLContext] updatePolicyTask stop with err: \(err)") } ) self.updatePolicyWorker = updatePolicyWorker await updatePolicyWorker.start() // 启动stun任务 await self.startStunRequestTask() } private func handleRegisterSuperNak(nakPacket: SDLRegisterSuperNak) { let errorMessage = nakPacket.errorMessage guard let errorCode = SDLNAKErrorCode(rawValue: UInt8(nakPacket.errorCode)) else { return } switch errorCode { case .invalidToken, .nodeDisabled: self.publishTunnelEvent(code: Int(errorCode.rawValue), message: errorMessage) // 报告错误并退出 let error = NSError(domain: "com.jihe.punchnet.tun", code: -1) self.provider.cancelTunnelWithError(error) case .noIpAddress, .networkFault, .internalFault: self.publishTunnelEvent(code: Int(errorCode.rawValue), message: errorMessage) } SDLLogger.log("[SDLContext] Get a SuperNak message exit") } private func handleEvent(event: SDLEvent) async { let processor = self.makeSuperEventProcessor() let plan = await processor.makeProcessingPlan(event: event) if let logMessage = plan.logMessage { SDLLogger.log(logMessage) } switch plan.action { case .removeSession(let dstMac): await self.sessionManager.removeSession(dstMac: dstMac) case .sendRegister(let registerData, let remoteAddresses): for remoteAddress in remoteAddresses { await self.sendPeerPacket(type: .register, data: registerData, remoteAddress: remoteAddress) } case .shutdown(let message): self.publishTunnelEvent(message: message) // 报告错误并退出 let error = NSError(domain: "com.jihe.punchnet.tun", code: -2) self.provider.cancelTunnelWithError(error) case .none: () } } private func doRegisterSuper() async { // 注册 var registerSuper = SDLRegisterSuper() registerSuper.clientID = self.config.clientId registerSuper.networkID = self.config.networkAddress.networkId registerSuper.mac = self.config.networkAddress.mac registerSuper.ip = self.config.networkAddress.ip registerSuper.maskLen = UInt32(self.config.networkAddress.maskLen) registerSuper.hostname = self.config.hostname registerSuper.pubKey = self.rsaCipher.pubKey registerSuper.accessToken = self.config.accessToken if let registerSuperData = try? registerSuper.serializedData() { SDLLogger.log("[SDLContext] will send register super") await self.superServiceProxy.send(type: .registerSuper, data: registerSuperData) } } } // MARK: DNS service events extension SDLContextActor { private func handleDNSEvent(_ event: DNSService.Event) async { switch event { case .packet(let packet): let nePacket = NEPacket(data: packet, protocolFamily: 2) self.provider.packetFlow.writePacketObjects([nePacket]) } } } // MARK: 处理从Hole收到的数据 extension SDLContextActor { private func handleUDPHoleControlEvent(_ event: SDLUDPHoleService.Event) async { switch event { case .ready(let localAddress): SDLLogger.log("[SDLContext] udpHole ready: \(localAddress)") case .natType(let natType): self.setNatType(natType: natType) SDLLogger.log("[SDLContext] nat_type is: \(natType)") case .packet(let remoteAddress, let message, let source): await self.handleUDPHolePacket(remoteAddress: remoteAddress, message: message, source: source) case .closed(let error): SDLLogger.log("[SDLContext] udpHole closed: \(error)", for: .debug) } } private func handleUDPHolePacket(remoteAddress: SocketAddress, message: SDLHoleControlMessage, source: SDLUDPHoleKind) async { switch message { case .stunReply(_), .stunProbeReply(_): SDLLogger.log("[SDLContext] get a stun reply", for: .debug) case .register(let register): try? await self.handleRegister(remoteAddress: remoteAddress, register: register, source: source) case .registerAck(let registerAck): await self.handleRegisterAck(remoteAddress: remoteAddress, registerAck: registerAck, source: source) } } private func handleRegister(remoteAddress: SocketAddress, register: SDLRegister, source: SDLUDPHoleKind) async throws { let networkAddr = config.networkAddress SDLLogger.log("[SDLContext] register packet: \(register), network_address: \(networkAddr)") // 判断目标地址是否是tun的网卡地址, 并且是在同一个网络下 if register.dstMac == networkAddr.mac && register.networkID == networkAddr.networkId { // 回复ack包 var registerAck = SDLRegisterAck() registerAck.networkID = networkAddr.networkId registerAck.srcMac = networkAddr.mac registerAck.dstMac = register.srcMac await self.sendPeerPacket(type: .registerAck, data: try registerAck.serializedData(), remoteAddress: remoteAddress) // 这里需要建立到来源的会话, 在复杂网络下,通过super-node查询到的nat地址不一定靠谱,需要通过udp包的来源地址作为nat地址 if let session = Session(dstMac: register.srcMac, natAddress: remoteAddress, addressType: source.convertAddressType()) { await self.sessionManager.addSession(session: session) } else { SDLLogger.log("[SDLContext] didReadRegister get unsupported remoteAddress: \(remoteAddress)", for: .debug) } } else { SDLLogger.log("[SDLContext] didReadRegister get a invalid packet, because dst_ip not matched: \(register.dstMac)") } } private func handleRegisterAck(remoteAddress: SocketAddress, registerAck: SDLRegisterAck, source: SDLUDPHoleKind) async { // 判断目标地址是否是tun的网卡地址, 并且是在同一个网络下 let networkAddr = config.networkAddress if registerAck.dstMac == networkAddr.mac && registerAck.networkID == networkAddr.networkId { if let session = Session(dstMac: registerAck.srcMac, natAddress: remoteAddress, addressType: source.convertAddressType()) { await self.sessionManager.addSession(session: session) } else { SDLLogger.log("[SDLContext] didReadRegisterAck get unsupported remoteAddress: \(remoteAddress)", for: .debug) } } else { SDLLogger.log("[SDLContext] didReadRegisterAck get a invalid packet, because dst_mac not matched: \(registerAck.dstMac)") } } } // MARK: 和Stun相关的心跳机制 extension SDLContextActor { // MARK: -- StunRequestTask private func startStunRequestTask() async { await self.stunRequestWorker?.stop() let stunRequestWorker = PeriodicWorker( configuration: .init( interval: .seconds(8), runImmediately: true, mode: .fixedDelay, errorPolicy: .keepRunning(delay: .seconds(5)) ), operation: { [weak self] in let probeReply = try? await self?.ipv6AssistClient?.probe(requestTimeout: .seconds(3)) if let v6Info = probeReply?.v6Info, let v6Address = SDLUtil.ipv6DataToString(v6Info.v6) { SDLLogger.log("[SDLContext] probe ipv6 address: \(v6Address)") } else { SDLLogger.log("[SDLContext] probe ipv6 address: empty") } await self?.sendStunRequest(v6Info: probeReply?.v6Info) }, onError: { err in SDLLogger.log("[SDLContext] udp stunRequestTask stop with err: \(err)") } ) self.stunRequestWorker = stunRequestWorker await stunRequestWorker.start() } private func sendStunRequest(v6Info: SDLV6Info?) async { guard let sessionToken = self.sessionToken else { return } var stunRequest = SDLStunRequest() stunRequest.clientID = self.config.clientId stunRequest.networkID = self.config.networkAddress.networkId stunRequest.ip = self.config.networkAddress.ip stunRequest.mac = self.config.networkAddress.mac stunRequest.natType = UInt32(self.natType.rawValue) stunRequest.sessionToken = sessionToken if let v6Info { stunRequest.v6Info = v6Info } if let stunData = try? stunRequest.serializedData() { await self.sendSuperPacket(type: .stunRequest, data: stunData) } } } // MARK: NEPacketTunnelProvider相关的逻辑 extension SDLContextActor { // 取消出口节点的时候,ip地址为: 0.0.0.0 public func updateExitNode(exitNodeIp: String) async throws { if let ip = SDLUtil.ipv4StrToInt32(exitNodeIp), ip > 0 { self.config.exitNode = .init(exitNodeIp: ip) } else { self.config.exitNode = nil } await self.packetOutboundActor.updateRuntime(config: self.config, dataCipher: self.dataCipher) await self.packetInboundActor.updateRuntime(config: self.config, dataCipher: self.dataCipher) try await self.tunNetworkManager.apply(settings: .init(config: self.config), dnsServer: DNSHelper.dnsServer) } }