This commit is contained in:
anlicheng 2026-04-10 11:25:14 +08:00
parent 831a86947e
commit eb2b4e7167

View File

@ -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<Data>
private let payloadContinuation: AsyncStream<Data>.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 { }
}
}