diff --git a/Tun/Punchnet/Actors/SDLContextActor.swift b/Tun/Punchnet/Actors/SDLContextActor.swift index def9d67..572c0ce 100644 --- a/Tun/Punchnet/Actors/SDLContextActor.swift +++ b/Tun/Punchnet/Actors/SDLContextActor.swift @@ -40,7 +40,7 @@ actor SDLContextActor { private var udpHoleWorkers: [Task]? // dns的client对象 - private var dnsClient: SDLDNSClient? + private var dnsClient: DNSCloudClient? private var dnsWorker: Task? 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() diff --git a/Tun/Punchnet/SDLDNSClient.swift b/Tun/Punchnet/DNS/DNSCloudClient.swift similarity index 89% rename from Tun/Punchnet/SDLDNSClient.swift rename to Tun/Punchnet/DNS/DNSCloudClient.swift index 8afd96e..8a7a6a6 100644 --- a/Tun/Punchnet/SDLDNSClient.swift +++ b/Tun/Punchnet/DNS/DNSCloudClient.swift @@ -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 - } - } - -} diff --git a/Tun/Punchnet/DNS/DNSDispatcher.swift b/Tun/Punchnet/DNS/DNSDispatcher.swift new file mode 100644 index 0000000..f341de3 --- /dev/null +++ b/Tun/Punchnet/DNS/DNSDispatcher.swift @@ -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) // 本地只需轉發載荷 +// } +// } +} diff --git a/Tun/Punchnet/DNS/DNSHelper.swift b/Tun/Punchnet/DNS/DNSHelper.swift new file mode 100644 index 0000000..b336a29 --- /dev/null +++ b/Tun/Punchnet/DNS/DNSHelper.swift @@ -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 + } + +} diff --git a/Tun/Punchnet/DNS/DNSLocalClient.swift b/Tun/Punchnet/DNS/DNSLocalClient.swift new file mode 100644 index 0000000..aa81182 --- /dev/null +++ b/Tun/Punchnet/DNS/DNSLocalClient.swift @@ -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 + private let payloadContinuation: AsyncStream.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 { } + } + +} diff --git a/Tun/Punchnet/DNS/DNSParser.swift b/Tun/Punchnet/DNS/DNSParser.swift new file mode 100644 index 0000000..0be12d1 --- /dev/null +++ b/Tun/Punchnet/DNS/DNSParser.swift @@ -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.. 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.. 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.. 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 + } +}