增加对dns的解析
This commit is contained in:
parent
4538466f6b
commit
831a86947e
@ -40,7 +40,7 @@ actor SDLContextActor {
|
||||
private var udpHoleWorkers: [Task<Void, Never>]?
|
||||
|
||||
// dns的client对象
|
||||
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()
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
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