增加对dns的解析
This commit is contained in:
parent
4538466f6b
commit
831a86947e
@ -40,7 +40,7 @@ actor SDLContextActor {
|
|||||||
private var udpHoleWorkers: [Task<Void, Never>]?
|
private var udpHoleWorkers: [Task<Void, Never>]?
|
||||||
|
|
||||||
// dns的client对象
|
// dns的client对象
|
||||||
private var dnsClient: SDLDNSClient?
|
private var dnsClient: DNSCloudClient?
|
||||||
private var dnsWorker: Task<Void, Never>?
|
private var dnsWorker: Task<Void, Never>?
|
||||||
|
|
||||||
private var quicClient: SDLQUICClient?
|
private var quicClient: SDLQUICClient?
|
||||||
@ -141,7 +141,7 @@ actor SDLContextActor {
|
|||||||
} else {
|
} else {
|
||||||
self.config.exitNode = nil
|
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 {
|
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?.cancel()
|
||||||
self.dnsWorker = nil
|
self.dnsWorker = nil
|
||||||
|
|
||||||
// 启动dns服务
|
// 启动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()
|
dnsClient.start()
|
||||||
SDLLogger.shared.log("[SDLContext] dnsClient started")
|
SDLLogger.shared.log("[SDLContext] dnsClient started")
|
||||||
self.dnsClient = dnsClient
|
self.dnsClient = dnsClient
|
||||||
@ -424,7 +424,7 @@ actor SDLContextActor {
|
|||||||
SDLLogger.shared.log("[SDLContext] get registerSuperAck, aes_key len: \(key.count)", level: .info)
|
SDLLogger.shared.log("[SDLContext] get registerSuperAck, aes_key len: \(key.count)", level: .info)
|
||||||
// 服务器分配的tun网卡信息
|
// 服务器分配的tun网卡信息
|
||||||
do {
|
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")
|
SDLLogger.shared.log("[SDLContext] setNetworkSettings successed")
|
||||||
self.state = .registered
|
self.state = .registered
|
||||||
self.startReader()
|
self.startReader()
|
||||||
@ -665,7 +665,7 @@ actor SDLContextActor {
|
|||||||
private func dealTunPacket(packet: IPPacket) async {
|
private func dealTunPacket(packet: IPPacket) async {
|
||||||
let networkAddr = self.config.networkAddress
|
let networkAddr = self.config.networkAddress
|
||||||
|
|
||||||
if SDLDNSClient.Helper.isDnsRequestPacket(ipPacket: packet) {
|
if DNSHelper.isDnsRequestPacket(ipPacket: packet) {
|
||||||
self.dnsClient?.forward(ipPacketData: packet.data)
|
self.dnsClient?.forward(ipPacketData: packet.data)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -794,6 +794,13 @@ actor SDLContextActor {
|
|||||||
let ipv4Settings = NEIPv4Settings(addresses: [networkAddress.ipAddress], subnetMasks: [networkAddress.maskAddress])
|
let ipv4Settings = NEIPv4Settings(addresses: [networkAddress.ipAddress], subnetMasks: [networkAddress.maskAddress])
|
||||||
// 设置路由表
|
// 设置路由表
|
||||||
ipv4Settings.includedRoutes = routes
|
ipv4Settings.includedRoutes = routes
|
||||||
|
|
||||||
|
let rs = self.getIpv4ExcludeRoutes()
|
||||||
|
|
||||||
|
rs.forEach { x in
|
||||||
|
SDLLogger.shared.log("excludsL: \(x.destinationAddress)")
|
||||||
|
}
|
||||||
|
|
||||||
// 配置要排除的路由
|
// 配置要排除的路由
|
||||||
ipv4Settings.excludedRoutes = self.getIpv4ExcludeRoutes()
|
ipv4Settings.excludedRoutes = self.getIpv4ExcludeRoutes()
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Network
|
import Network
|
||||||
|
|
||||||
final class SDLDNSClient {
|
final class DNSCloudClient {
|
||||||
private var connection: NWConnection?
|
private var connection: NWConnection?
|
||||||
private let logger: SDLLogger
|
private let logger: SDLLogger
|
||||||
private let dnsServerAddress: NWEndpoint
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
44
Tun/Punchnet/DNS/DNSDispatcher.swift
Normal file
44
Tun/Punchnet/DNS/DNSDispatcher.swift
Normal 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) // 本地只需轉發載荷
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
19
Tun/Punchnet/DNS/DNSHelper.swift
Normal file
19
Tun/Punchnet/DNS/DNSHelper.swift
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
89
Tun/Punchnet/DNS/DNSLocalClient.swift
Normal file
89
Tun/Punchnet/DNS/DNSLocalClient.swift
Normal 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 { }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
127
Tun/Punchnet/DNS/DNSParser.swift
Normal file
127
Tun/Punchnet/DNS/DNSParser.swift
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user