// // SDLLocalDNSClient.swift // punchnet // // Created by 安礼成 on 2026/4/10. // import Foundation import Network /// 纯粹的本地 DNS 解析器:负责将 DNS 载荷绕过 TUN 发送到公网 DNS final class DNSLocalClient { private var connection: NWConnection? private let logger: SDLLogger private let remoteEndpoint: NWEndpoint // 用于对外输出收到的原始 DNS 载荷 (注意:这里只输出 UDP 载荷,不带 IP 头) public let payloadFlow: AsyncStream private let payloadContinuation: AsyncStream.Continuation private let (closeStream, closeContinuation) = AsyncStream.makeStream(of: Void.self) /// - Parameter dnsServer: 物理 DNS 地址,例如 "114.114.114.114" init(dnsServer: String, logger: SDLLogger) { self.logger = logger self.remoteEndpoint = .hostPort(host: NWEndpoint.Host(dnsServer), port: 53) let (stream, continuation) = AsyncStream.makeStream(of: Data.self, bufferingPolicy: .unbounded) self.payloadFlow = stream self.payloadContinuation = continuation } func start() { let parameters = NWParameters.udp // 关键:强制绕过 TUN 网卡 parameters.prohibitedInterfaceTypes = [.other] parameters.multipathServiceType = .handover let connection = NWConnection(to: self.remoteEndpoint, using: parameters) self.connection = connection connection.stateUpdateHandler = { [weak self] state in switch state { case .ready: self?.logger.log("[LocalDNS] Path ready", level: .debug) self?.receiveLoop() case .failed(let error): self?.logger.log("[LocalDNS] Failed: \(error)", level: .error) self?.closeContinuation.finish() case .cancelled: self?.payloadContinuation.finish() self?.closeContinuation.finish() default: break } } connection.start(queue: .global()) } /// 发送 DNS 载荷(由上层剥离 IP/UDP 头后的 Data) func query(dnsPayload: Data) { guard let connection = self.connection, connection.state == .ready else { return } connection.send(content: dnsPayload, completion: .contentProcessed { [weak self] error in if let error = error { self?.logger.log("[LocalDNS] Send error: \(error)", level: .error) } }) } private func receiveLoop() { connection?.receiveMessage { [weak self] content, _, _, error in if let data = content, !data.isEmpty { // 直接输出收到的 DNS 响应载荷 self?.payloadContinuation.yield(data) } if error == nil && self?.connection?.state == .ready { self?.receiveLoop() } } } func stop() { connection?.cancel() } public func waitClose() async { for await _ in closeStream { } } }