From ce764d231421c6e5437f492fbd69be1dba879ddc Mon Sep 17 00:00:00 2001 From: anlicheng <244108715@qq.com> Date: Thu, 28 May 2026 14:34:11 +0800 Subject: [PATCH] fix dns --- Tun/DNS/DNSLocalClient.swift | 108 ++++++++++++----------------------- 1 file changed, 38 insertions(+), 70 deletions(-) diff --git a/Tun/DNS/DNSLocalClient.swift b/Tun/DNS/DNSLocalClient.swift index fc9c504..e9f420f 100644 --- a/Tun/DNS/DNSLocalClient.swift +++ b/Tun/DNS/DNSLocalClient.swift @@ -14,23 +14,15 @@ actor DNSLocalClient { let tracker: DNSTracker } - private enum State { - case idle - case starting - case running - case stopped - } - enum DNSLocalError: Error { case failed(Error) case cancelled case sendFailed(Error) } - private var state: State = .idle + private let queue = DispatchQueue(label: "com.sdl.DNSCloudClient.queue") + private let connection: NWConnection - private let dnsServerEndpoint: NWEndpoint - private var connection: NWConnection? private let timeoutInterval: TimeInterval = 3.0 nonisolated let packetFlow: AsyncThrowingStream @@ -40,8 +32,11 @@ actor DNSLocalClient { private var pendingRequests: [UInt16: PendingRequest] = [:] private var nextTransactionID: UInt16 = 1 + private let readySignal = AsyncOneShot() + private var isStopped: Bool = false + init(host: String) { - self.dnsServerEndpoint = .hostPort(host: Self.makeEndpointHost(ip: host), port: 53) + let dnsServerEndpoint = NWEndpoint.hostPort(host: Self.makeEndpointHost(ip: host), port: 53) let (stream, continuation) = AsyncThrowingStream.makeStream(of: Data.self, bufferingPolicy: .bufferingNewest(256)) self.packetFlow = stream @@ -50,6 +45,13 @@ actor DNSLocalClient { self.packetContinuation.onTermination = { termination in SDLLogger.log("[DNSLocalClient] packetFlow terminated: \(termination)") } + + let parameters = NWParameters.udp + parameters.prohibitedInterfaceTypes = [.other] + // 2. 增强健壮性:启用多路径切换(替代 pathSelectionOptions 的意图) + parameters.multipathServiceType = .handover + + self.connection = NWConnection(to: dnsServerEndpoint, using: parameters) } private static func makeEndpointHost(ip: String) -> NWEndpoint.Host { @@ -65,61 +67,45 @@ actor DNSLocalClient { } func run() async throws { - guard self.state == .idle else { - return - } - self.state = .starting - - let parameters = NWParameters.udp - parameters.prohibitedInterfaceTypes = [.other] - // 2. 增强健壮性:启用多路径切换(替代 pathSelectionOptions 的意图) - parameters.multipathServiceType = .handover - - let connection = NWConnection(to: self.dnsServerEndpoint, using: parameters) - connection.stateUpdateHandler = { [weak self] state in + self.connection.stateUpdateHandler = { [weak self] state in Task { - await self?.handleConnectionStateUpdate(state, for: connection) + await self?.handleConnectionStateUpdate(state) } } - - self.connection = connection - - connection.start(queue: .global()) + self.connection.start(queue: self.queue) try await withTaskCancellationHandler { try await withThrowingTaskGroup(of: Void.self) { group in defer { group.cancelAll() } - - group.addTask { [weak self] in - let stream = Self.makeReceiveStream(for: connection) + + group.addTask { + try await self.readySignal.wait() + + let stream = Self.makeReceiveStream(for: self.connection) for await data in stream { try Task.checkCancellation() - guard let self else { - return - } await self.handleResponse(data: data) } } - - group.addTask { [weak self] in + + group.addTask { while !Task.isCancelled { try await Task.sleep(for: .seconds(3)) - await self?.performCleanup() + await self.performCleanup() } } - + try await group.next() } } onCancel: { - connection.cancel() + self.connection.cancel() } } func query(tracker: DNSTracker, dnsPayload: Data) { - guard self.state != .stopped, - let connection = self.connection, connection.state == .ready, dnsPayload.count >= 2 else { + guard connection.state == .ready, dnsPayload.count >= 2 else { return } @@ -142,32 +128,30 @@ actor DNSLocalClient { } func stop() { - guard self.state != .stopped else { + guard !self.isStopped else { return } + self.isStopped = true - self.state = .stopped - - let connection = self.connection - self.connection = nil - + self.connection.cancel() + self.pendingRequests.removeAll() self.nextTransactionID = 1 - connection?.cancel() self.finishPacketContinuationIfNeed(throwing: nil) SDLLogger.log("[SDLLocalClient] stopped") } - private func handleConnectionStateUpdate(_ state: NWConnection.State, for conn: NWConnection) { + private func handleConnectionStateUpdate(_ state: NWConnection.State) async { switch state { case .ready: - self.markConnectionReady(conn) + await self.readySignal.succeed(()) case .failed(let error): - SDLLogger.log("[DNSLocalClient] failed with error: \(error.localizedDescription)", for: .debug) + await self.readySignal.fail(DNSLocalError.failed(error)) self.finishPacketContinuationIfNeed(throwing: .failed(error)) case .cancelled: + await self.readySignal.fail(DNSLocalError.cancelled) self.finishPacketContinuationIfNeed(throwing: .cancelled) default: () @@ -206,22 +190,6 @@ actor DNSLocalClient { self.packetContinuation.yield(packet) } - private func markConnectionReady(_ conn: NWConnection) { - guard self.state != .stopped, self.isCurrentConnection(conn) else { - return - } - - self.state = .running - } - - private func isCurrentConnection(_ conn: NWConnection) -> Bool { - guard let currentConnection = self.connection else { - return false - } - - return currentConnection === conn - } - private func handleSendFailure(transactionID: UInt16, error: NWError) { self.pendingRequests.removeValue(forKey: transactionID) self.finishPacketContinuationIfNeed(throwing: .sendFailed(error)) @@ -273,15 +241,15 @@ actor DNSLocalClient { return rewrittenPayload } - private static func makeReceiveStream(for conn: NWConnection) -> AsyncStream { + private static func makeReceiveStream(for connection: NWConnection) -> AsyncStream { return AsyncStream(bufferingPolicy: .bufferingNewest(256)) { continuation in func receiveNext() { - conn.receiveMessage { content, _, _, error in + connection.receiveMessage { content, _, _, error in if let data = content, !data.isEmpty { continuation.yield(data) } - if error == nil && conn.state == .ready { + if error == nil && connection.state == .ready { receiveNext() } else { continuation.finish()