punchnet-macos/Tun/Punchnet/DNS/DNSLocalClient.swift
2026-04-10 11:19:37 +08:00

90 lines
3.0 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 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<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) {
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 { }
}
}