diff --git a/Tun/Punchnet/DNS/DNSLocalClient.swift b/Tun/Punchnet/DNS/DNSLocalClient.swift index aa81182..c67330e 100644 --- a/Tun/Punchnet/DNS/DNSLocalClient.swift +++ b/Tun/Punchnet/DNS/DNSLocalClient.swift @@ -1,89 +1,58 @@ -// -// SDLLocalDNSClient.swift -// punchnet -// -// Created by 安礼成 on 2026/4/10. -// import Foundation import Network -/// 纯粹的本地 DNS 解析器:负责将 DNS 载荷绕过 TUN 发送到公网 DNS -final class DNSLocalClient { - private var connection: NWConnection? +final class SDLLocalDNSClient { + private var connections: [NWConnection] = [] private let logger: SDLLogger - private let remoteEndpoint: NWEndpoint - // 用于对外输出收到的原始 DNS 载荷 (注意:这里只输出 UDP 载荷,不带 IP 头) + // 准备多个公共 DNS + private let dnsServers = ["114.114.114.114", "223.5.5.5", "8.8.8.8"] + 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) { + + init(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 + for server in dnsServers { + let endpoint = NWEndpoint.hostPort(host: NWEndpoint.Host(server), port: 53) + let parameters = NWParameters.udp + parameters.prohibitedInterfaceTypes = [.other] + + let conn = NWConnection(to: endpoint, using: parameters) + + conn.stateUpdateHandler = { [weak self] state in + if case .ready = state { self?.receiveLoop(for: conn) } } + conn.start(queue: .global()) + connections.append(conn) } - 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) - } - }) + for conn in connections where conn.state == .ready { + conn.send(content: dnsPayload, completion: .contentProcessed({ _ in })) + } } - - private func receiveLoop() { - connection?.receiveMessage { [weak self] content, _, _, error in - if let data = content, !data.isEmpty { - // 直接输出收到的 DNS 响应载荷 + + private func receiveLoop(for conn: NWConnection) { + conn.receiveMessage { [weak self] content, _, _, error in + if let data = content { + // !!!核心:由于 AsyncStream 是流式的 + // 谁先 yield,上层就先收到谁。 + // 只要上层收到了第一个有效响应并回填给系统, + // 后面迟到的重复响应会被系统协议栈自动忽略(因为 Transaction ID 已失效) self?.payloadContinuation.yield(data) } - if error == nil && self?.connection?.state == .ready { - self?.receiveLoop() + if error == nil && conn.state == .ready { + self?.receiveLoop(for: conn) } } } - - func stop() { - connection?.cancel() - } - - public func waitClose() async { - for await _ in closeStream { } - } - }