// // SDLDNSClient 2.swift // punchnet // // Created by 安礼成 on 2026/4/9. // import Foundation import Network actor DNSCloudClient { private enum State { case idle case running case stopped } private var state: State = .idle private var connection: NWConnection? private var receiveTask: Task? private let dnsServerAddress: NWEndpoint // 用于对外输出收到的 DNS 响应包 public let packetFlow: AsyncStream private let packetContinuation: AsyncStream.Continuation private var didFinishPacketFlow = false // 用来处理关闭事件 private let closeStream: AsyncStream private let closeContinuation: AsyncStream.Continuation private var didFinishCloseStream = false /// - Parameter host: 你的 sn-server 地址 (如 "8.8.8.8") /// - Parameter port: 端口 (如 53) init(host: String, port: UInt16 ) { self.dnsServerAddress = .hostPort(host: NWEndpoint.Host(host), port: NWEndpoint.Port(integerLiteral: port)) let (packetStream, packetContinuation) = AsyncStream.makeStream(of: Data.self, bufferingPolicy: .bufferingNewest(256)) self.packetFlow = packetStream self.packetContinuation = packetContinuation let (closeStream, closeContinuation) = AsyncStream.makeStream(of: Void.self, bufferingPolicy: .bufferingNewest(1)) self.closeStream = closeStream self.closeContinuation = closeContinuation } func start() { guard case .idle = self.state else { return } self.state = .running // 1. 配置参数:这是解决环路的关键 let parameters = NWParameters.udp // 禁止此连接走 TUN 网卡(在 NE 中 TUN 通常被归类为 .other) parameters.prohibitedInterfaceTypes = [.other] // 2. 增强健壮性:启用多路径切换(替代 pathSelectionOptions 的意图) parameters.multipathServiceType = .handover // 2. 创建连接 let connection = NWConnection(to: self.dnsServerAddress, using: parameters) self.connection = connection connection.stateUpdateHandler = { [weak self] state in Task { await self?.handleConnectionStateUpdate(state, for: connection) } } // 启动连接队列 connection.start(queue: .global()) } public func waitClose() async { for await _ in self.closeStream { } } /// 接收数据的递归循环 private static func makeReceiveStream(for connection: NWConnection) -> AsyncStream { return AsyncStream(bufferingPolicy: .bufferingNewest(256)) { continuation in func receiveNext() { connection.receiveMessage { content, _, _, error in if let data = content, !data.isEmpty { // 将收到的 DNS 响应写回 AsyncStream continuation.yield(data) } if error == nil && connection.state == .ready { receiveNext() // 继续监听下一个包 } else { continuation.finish() } } } receiveNext() } } /// 发送 DNS 查询包(由 TUN 拦截到的原始 IP 包数据) func forward(ipPacketData: Data) { guard case .running = self.state, let connection = self.connection, connection.state == .ready else { return } connection.send(content: ipPacketData, completion: .contentProcessed { error in if let error = error { SDLLogger.log("[DNSClient] Send error: \(error)", for: .debug) } }) } func stop() { guard self.state != .stopped else { return } self.state = .stopped self.receiveTask?.cancel() self.receiveTask = nil self.connection?.cancel() self.connection = nil self.finishPacketFlowIfNeeded() self.finishCloseStreamIfNeeded() } private func handleConnectionStateUpdate(_ state: NWConnection.State, for connection: NWConnection) { guard case .running = self.state else { return } switch state { case .ready: SDLLogger.log("[DNSClient] Connection ready", for: .debug) self.startReceiveTask(for: connection) case .failed(let error): SDLLogger.log("[DNSClient] Connection failed: \(error)", for: .debug) self.stop() case .cancelled: self.stop() default: break } } private func startReceiveTask(for connection: NWConnection) { guard self.receiveTask == nil else { return } let stream = Self.makeReceiveStream(for: connection) self.receiveTask = Task { [weak self] in for await data in stream { guard let self else { break } await self.handleReceivedPacket(data) } await self?.didFinishReceiving(for: connection) } } private func handleReceivedPacket(_ data: Data) { guard case .running = self.state else { return } self.packetContinuation.yield(data) } private func didFinishReceiving(for connection: NWConnection) { guard case .running = self.state else { return } if self.connection === connection, connection.state != .ready { self.stop() } else { self.receiveTask = nil } } private func finishPacketFlowIfNeeded() { guard !self.didFinishPacketFlow else { return } self.didFinishPacketFlow = true self.packetContinuation.finish() } private func finishCloseStreamIfNeeded() { guard !self.didFinishCloseStream else { return } self.didFinishCloseStream = true self.closeContinuation.finish() } deinit { self.connection?.cancel() } }