增加对dns的解析

This commit is contained in:
anlicheng 2026-04-10 11:19:37 +08:00
parent 4538466f6b
commit 831a86947e
6 changed files with 293 additions and 20 deletions

View File

@ -40,7 +40,7 @@ actor SDLContextActor {
private var udpHoleWorkers: [Task<Void, Never>]?
// dnsclient
private var dnsClient: SDLDNSClient?
private var dnsClient: DNSCloudClient?
private var dnsWorker: Task<Void, Never>?
private var quicClient: SDLQUICClient?
@ -141,7 +141,7 @@ actor SDLContextActor {
} else {
self.config.exitNode = nil
}
try await self.setNetworkSettings(config: config, dnsServer: SDLDNSClient.Helper.dnsServer)
try await self.setNetworkSettings(config: config, dnsServer: DNSHelper.dnsServer)
}
private func startQUICClient() async throws -> SDLQUICClient {
@ -225,12 +225,12 @@ actor SDLContextActor {
}
}
private func startDnsClient() async throws -> SDLDNSClient {
private func startDnsClient() async throws -> DNSCloudClient {
self.dnsWorker?.cancel()
self.dnsWorker = nil
// dns
let dnsClient = SDLDNSClient(host: self.config.serverHost, port: 15353, logger: SDLLogger.shared)
let dnsClient = DNSCloudClient(host: self.config.serverHost, port: 15353, logger: SDLLogger.shared)
dnsClient.start()
SDLLogger.shared.log("[SDLContext] dnsClient started")
self.dnsClient = dnsClient
@ -424,7 +424,7 @@ actor SDLContextActor {
SDLLogger.shared.log("[SDLContext] get registerSuperAck, aes_key len: \(key.count)", level: .info)
// tun
do {
try await self.setNetworkSettings(config: self.config, dnsServer: SDLDNSClient.Helper.dnsServer)
try await self.setNetworkSettings(config: self.config, dnsServer: DNSHelper.dnsServer)
SDLLogger.shared.log("[SDLContext] setNetworkSettings successed")
self.state = .registered
self.startReader()
@ -665,7 +665,7 @@ actor SDLContextActor {
private func dealTunPacket(packet: IPPacket) async {
let networkAddr = self.config.networkAddress
if SDLDNSClient.Helper.isDnsRequestPacket(ipPacket: packet) {
if DNSHelper.isDnsRequestPacket(ipPacket: packet) {
self.dnsClient?.forward(ipPacketData: packet.data)
return
}
@ -794,6 +794,13 @@ actor SDLContextActor {
let ipv4Settings = NEIPv4Settings(addresses: [networkAddress.ipAddress], subnetMasks: [networkAddress.maskAddress])
//
ipv4Settings.includedRoutes = routes
let rs = self.getIpv4ExcludeRoutes()
rs.forEach { x in
SDLLogger.shared.log("excludsL: \(x.destinationAddress)")
}
//
ipv4Settings.excludedRoutes = self.getIpv4ExcludeRoutes()

View File

@ -7,7 +7,7 @@
import Foundation
import Network
final class SDLDNSClient {
final class DNSCloudClient {
private var connection: NWConnection?
private let logger: SDLLogger
private let dnsServerAddress: NWEndpoint
@ -105,16 +105,3 @@ final class SDLDNSClient {
}
extension SDLDNSClient {
struct Helper {
static let dnsServer: String = "100.100.100.100"
// dns
static let dnsDestIpAddr: UInt32 = 1684300900
// dns
static func isDnsRequestPacket(ipPacket: IPPacket) -> Bool {
return ipPacket.header.destination == dnsDestIpAddr
}
}
}

View File

@ -0,0 +1,44 @@
//
// DNSDispatcher.swift
// punchnet
//
// Created by on 2026/4/10.
//
import Foundation
// MARK: - DNS 調
final class DNSDispatcher {
// private let localClient: SDLLocalDNSClient
// private let cloudClient: SDLDNSClient // Client
// private let logger: SDLLogger
// private let internalDomain = "punchsky.com"
//
// init(localClient: SDLLocalDNSClient, cloudClient: SDLDNSClient, logger: SDLLogger) {
// self.localClient = localClient
// self.cloudClient = cloudClient
// self.logger = logger
// }
//
// /// TUN IP
// func dispatch(packet: IPPacket) {
// // 1. UDP ( IPv4 20 + UDP 8)
// let udpPayload = packet.data.suffix(from: 28)
//
// // 2. DNS
// let parser = DNSParser(data: udpPayload)
// guard let dnsMsg = parser.parse(), let firstQuestion = dnsMsg.questions.first else {
// return
// }
//
// let domain = firstQuestion.name
//
// // 3.
// if domain.hasSuffix(internalDomain) {
// logger.log("[Dispatcher] Cloud Route: \(domain)", level: .debug)
// cloudClient.forward(ipPacket: packet) //
// } else {
// logger.log("[Dispatcher] Local Route: \(domain)", level: .debug)
// localClient.query(dnsPayload: udpPayload) //
// }
// }
}

View File

@ -0,0 +1,19 @@
//
// Helper.swift
// punchnet
//
// Created by on 2026/4/10.
//
import Foundation
struct DNSHelper {
static let dnsServer: String = "100.100.100.100"
// dns
static let dnsDestIpAddr: UInt32 = 1684300900
// dns
static func isDnsRequestPacket(ipPacket: IPPacket) -> Bool {
return ipPacket.header.destination == dnsDestIpAddr
}
}

View File

@ -0,0 +1,89 @@
//
// 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 { }
}
}

View File

@ -0,0 +1,127 @@
//
// DNSQuestion.swift
// punchnet
//
// Created by on 2026/4/10.
//
import Foundation
import Network
// MARK: - DNS
struct DNSQuestion {
let name: String
let type: UInt16
let qclass: UInt16
}
struct DNSResourceRecord {
let name: String
let type: UInt16
let rclass: UInt16
let ttl: UInt32
let rdLength: UInt16
let rdata: Data
}
struct DNSMessage {
var transactionID: UInt16
var flags: UInt16
var questions: [DNSQuestion] = []
var answers: [DNSResourceRecord] = []
var isResponse: Bool { (flags & 0x8000) != 0 }
}
// MARK: - DNS
final class DNSParser {
private let data: Data
private var offset: Int = 0
init(data: Data) {
self.data = data
}
func parse() -> DNSMessage? {
guard data.count >= 12 else { return nil }
offset = 0
let id = readUInt16()
let flags = readUInt16()
let qdCount = readUInt16()
let anCount = readUInt16()
let _ = readUInt16() // NSCount
let _ = readUInt16() // ARCount
var message = DNSMessage(transactionID: id, flags: flags)
for _ in 0..<qdCount {
if let q = parseQuestion() { message.questions.append(q) }
}
for _ in 0..<anCount {
if let rr = parseRR() { message.answers.append(rr) }
}
return message
}
private func parseName() -> String {
var parts: [String] = []
var jumped = false
var nextOffset = 0
var currentOffset = self.offset
while currentOffset < data.count {
let length = Int(data[currentOffset])
if length == 0 {
currentOffset += 1
break
}
if (length & 0xC0) == 0xC0 {
let pointer = Int(UInt16(data[currentOffset] & 0x3F) << 8 | UInt16(data[currentOffset + 1]))
if !jumped {
nextOffset = currentOffset + 2
jumped = true
}
currentOffset = pointer
} else {
currentOffset += 1
if let label = String(data: data.subdata(in: currentOffset..<currentOffset+length), encoding: .ascii) {
parts.append(label)
}
currentOffset += length
}
}
self.offset = jumped ? nextOffset : currentOffset
return parts.joined(separator: ".")
}
private func parseQuestion() -> DNSQuestion? {
let name = parseName()
return DNSQuestion(name: name, type: readUInt16(), qclass: readUInt16())
}
private func parseRR() -> DNSResourceRecord? {
let name = parseName()
let type = readUInt16()
let rclass = readUInt16()
let ttl = readUInt32()
let rdLength = readUInt16()
guard offset + Int(rdLength) <= data.count else { return nil }
let rdata = data.subdata(in: offset..<offset + Int(rdLength))
offset += Int(rdLength)
return DNSResourceRecord(name: name, type: type, rclass: rclass, ttl: ttl, rdLength: rdLength, rdata: rdata)
}
private func readUInt16() -> UInt16 {
guard offset + 2 <= data.count else { return 0 }
let val = UInt16(data[offset]) << 8 | UInt16(data[offset + 1])
offset += 2
return val
}
private func readUInt32() -> UInt32 {
guard offset + 4 <= data.count else { return 0 }
let val = UInt32(data[offset]) << 24 | UInt32(data[offset+1]) << 16 | UInt32(data[offset+2]) << 8 | UInt32(data[offset+3])
offset += 4
return val
}
}