fix dns
This commit is contained in:
parent
8aeb543b17
commit
ce764d2314
@ -14,23 +14,15 @@ actor DNSLocalClient {
|
|||||||
let tracker: DNSTracker
|
let tracker: DNSTracker
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum State {
|
|
||||||
case idle
|
|
||||||
case starting
|
|
||||||
case running
|
|
||||||
case stopped
|
|
||||||
}
|
|
||||||
|
|
||||||
enum DNSLocalError: Error {
|
enum DNSLocalError: Error {
|
||||||
case failed(Error)
|
case failed(Error)
|
||||||
case cancelled
|
case cancelled
|
||||||
case sendFailed(Error)
|
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
|
private let timeoutInterval: TimeInterval = 3.0
|
||||||
|
|
||||||
nonisolated let packetFlow: AsyncThrowingStream<Data, Error>
|
nonisolated let packetFlow: AsyncThrowingStream<Data, Error>
|
||||||
@ -40,8 +32,11 @@ actor DNSLocalClient {
|
|||||||
private var pendingRequests: [UInt16: PendingRequest] = [:]
|
private var pendingRequests: [UInt16: PendingRequest] = [:]
|
||||||
private var nextTransactionID: UInt16 = 1
|
private var nextTransactionID: UInt16 = 1
|
||||||
|
|
||||||
|
private let readySignal = AsyncOneShot<Void>()
|
||||||
|
private var isStopped: Bool = false
|
||||||
|
|
||||||
init(host: String) {
|
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))
|
let (stream, continuation) = AsyncThrowingStream.makeStream(of: Data.self, bufferingPolicy: .bufferingNewest(256))
|
||||||
self.packetFlow = stream
|
self.packetFlow = stream
|
||||||
@ -50,6 +45,13 @@ actor DNSLocalClient {
|
|||||||
self.packetContinuation.onTermination = { termination in
|
self.packetContinuation.onTermination = { termination in
|
||||||
SDLLogger.log("[DNSLocalClient] packetFlow terminated: \(termination)")
|
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 {
|
private static func makeEndpointHost(ip: String) -> NWEndpoint.Host {
|
||||||
@ -65,26 +67,12 @@ actor DNSLocalClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func run() async throws {
|
func run() async throws {
|
||||||
guard self.state == .idle else {
|
self.connection.stateUpdateHandler = { [weak self] state in
|
||||||
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
|
|
||||||
Task {
|
Task {
|
||||||
await self?.handleConnectionStateUpdate(state, for: connection)
|
await self?.handleConnectionStateUpdate(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.connection.start(queue: self.queue)
|
||||||
self.connection = connection
|
|
||||||
|
|
||||||
connection.start(queue: .global())
|
|
||||||
|
|
||||||
try await withTaskCancellationHandler {
|
try await withTaskCancellationHandler {
|
||||||
try await withThrowingTaskGroup(of: Void.self) { group in
|
try await withThrowingTaskGroup(of: Void.self) { group in
|
||||||
@ -92,34 +80,32 @@ actor DNSLocalClient {
|
|||||||
group.cancelAll()
|
group.cancelAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
group.addTask { [weak self] in
|
group.addTask {
|
||||||
let stream = Self.makeReceiveStream(for: connection)
|
try await self.readySignal.wait()
|
||||||
|
|
||||||
|
let stream = Self.makeReceiveStream(for: self.connection)
|
||||||
for await data in stream {
|
for await data in stream {
|
||||||
try Task.checkCancellation()
|
try Task.checkCancellation()
|
||||||
guard let self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
await self.handleResponse(data: data)
|
await self.handleResponse(data: data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
group.addTask { [weak self] in
|
group.addTask {
|
||||||
while !Task.isCancelled {
|
while !Task.isCancelled {
|
||||||
try await Task.sleep(for: .seconds(3))
|
try await Task.sleep(for: .seconds(3))
|
||||||
await self?.performCleanup()
|
await self.performCleanup()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try await group.next()
|
try await group.next()
|
||||||
}
|
}
|
||||||
} onCancel: {
|
} onCancel: {
|
||||||
connection.cancel()
|
self.connection.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func query(tracker: DNSTracker, dnsPayload: Data) {
|
func query(tracker: DNSTracker, dnsPayload: Data) {
|
||||||
guard self.state != .stopped,
|
guard connection.state == .ready, dnsPayload.count >= 2 else {
|
||||||
let connection = self.connection, connection.state == .ready, dnsPayload.count >= 2 else {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,32 +128,30 @@ actor DNSLocalClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func stop() {
|
func stop() {
|
||||||
guard self.state != .stopped else {
|
guard !self.isStopped else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
self.isStopped = true
|
||||||
|
|
||||||
self.state = .stopped
|
self.connection.cancel()
|
||||||
|
|
||||||
let connection = self.connection
|
|
||||||
self.connection = nil
|
|
||||||
|
|
||||||
self.pendingRequests.removeAll()
|
self.pendingRequests.removeAll()
|
||||||
self.nextTransactionID = 1
|
self.nextTransactionID = 1
|
||||||
|
|
||||||
connection?.cancel()
|
|
||||||
self.finishPacketContinuationIfNeed(throwing: nil)
|
self.finishPacketContinuationIfNeed(throwing: nil)
|
||||||
|
|
||||||
SDLLogger.log("[SDLLocalClient] stopped")
|
SDLLogger.log("[SDLLocalClient] stopped")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handleConnectionStateUpdate(_ state: NWConnection.State, for conn: NWConnection) {
|
private func handleConnectionStateUpdate(_ state: NWConnection.State) async {
|
||||||
switch state {
|
switch state {
|
||||||
case .ready:
|
case .ready:
|
||||||
self.markConnectionReady(conn)
|
await self.readySignal.succeed(())
|
||||||
case .failed(let error):
|
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))
|
self.finishPacketContinuationIfNeed(throwing: .failed(error))
|
||||||
case .cancelled:
|
case .cancelled:
|
||||||
|
await self.readySignal.fail(DNSLocalError.cancelled)
|
||||||
self.finishPacketContinuationIfNeed(throwing: .cancelled)
|
self.finishPacketContinuationIfNeed(throwing: .cancelled)
|
||||||
default:
|
default:
|
||||||
()
|
()
|
||||||
@ -206,22 +190,6 @@ actor DNSLocalClient {
|
|||||||
self.packetContinuation.yield(packet)
|
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) {
|
private func handleSendFailure(transactionID: UInt16, error: NWError) {
|
||||||
self.pendingRequests.removeValue(forKey: transactionID)
|
self.pendingRequests.removeValue(forKey: transactionID)
|
||||||
self.finishPacketContinuationIfNeed(throwing: .sendFailed(error))
|
self.finishPacketContinuationIfNeed(throwing: .sendFailed(error))
|
||||||
@ -273,15 +241,15 @@ actor DNSLocalClient {
|
|||||||
return rewrittenPayload
|
return rewrittenPayload
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func makeReceiveStream(for conn: NWConnection) -> AsyncStream<Data> {
|
private static func makeReceiveStream(for connection: NWConnection) -> AsyncStream<Data> {
|
||||||
return AsyncStream(bufferingPolicy: .bufferingNewest(256)) { continuation in
|
return AsyncStream(bufferingPolicy: .bufferingNewest(256)) { continuation in
|
||||||
func receiveNext() {
|
func receiveNext() {
|
||||||
conn.receiveMessage { content, _, _, error in
|
connection.receiveMessage { content, _, _, error in
|
||||||
if let data = content, !data.isEmpty {
|
if let data = content, !data.isEmpty {
|
||||||
continuation.yield(data)
|
continuation.yield(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
if error == nil && conn.state == .ready {
|
if error == nil && connection.state == .ready {
|
||||||
receiveNext()
|
receiveNext()
|
||||||
} else {
|
} else {
|
||||||
continuation.finish()
|
continuation.finish()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user