解决dns的问题

This commit is contained in:
anlicheng 2026-04-10 15:23:12 +08:00
parent a1c42d8eef
commit f22e962bd7
7 changed files with 209 additions and 28 deletions

View File

@ -23,12 +23,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
SDLLogger.shared.log("NE read message: \(msg ?? "failed")") SDLLogger.shared.log("NE read message: \(msg ?? "failed")")
DarwinNotificationCenter.shared.post(.vpnStatusChanged) DarwinNotificationCenter.shared.post(.vpnStatusChanged)
// host: "192.168.0.101", port: 1265
guard let options, let config = SDLConfiguration.parse(options: options) else {
completionHandler(TunnelError.invalidConfiguration)
return
}
// //
guard self.contextActor == nil else { guard self.contextActor == nil else {
@ -39,6 +33,12 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
// //
let rsaCipher = try! CCRSACipher(keySize: 1024) let rsaCipher = try! CCRSACipher(keySize: 1024)
self.rootTask = Task { self.rootTask = Task {
// host: "192.168.0.101", port: 1265
guard let options, let config = await SDLConfiguration.parse(options: options) else {
completionHandler(TunnelError.invalidConfiguration)
return
}
self.contextActor = SDLContextActor(provider: self, config: config, rsaCipher: rsaCipher) self.contextActor = SDLContextActor(provider: self, config: config, rsaCipher: rsaCipher)
await self.contextActor?.start() await self.contextActor?.start()
completionHandler(nil) completionHandler(nil)

View File

@ -103,6 +103,7 @@ actor SDLContextActor {
} }
public func start() async { public func start() async {
self.startMonitor() self.startMonitor()
// arp // arp
@ -233,7 +234,7 @@ actor SDLContextActor {
self.dnsWorker = nil self.dnsWorker = nil
// dns // dns
let dnsClient = DNSCloudClient(host: self.config.serverHost, port: 15353, logger: SDLLogger.shared) let dnsClient = DNSCloudClient(host: self.config.serverIp, 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
@ -264,6 +265,8 @@ actor SDLContextActor {
if Task.isCancelled { if Task.isCancelled {
break break
} }
// Ip
let nePacket = NEPacket(data: packet, protocolFamily: 2) let nePacket = NEPacket(data: packet, protocolFamily: 2)
self.provider.packetFlow.writePacketObjects([nePacket]) self.provider.packetFlow.writePacketObjects([nePacket])
} }
@ -693,8 +696,31 @@ actor SDLContextActor {
let networkAddr = self.config.networkAddress let networkAddr = self.config.networkAddress
if DNSHelper.isDnsRequestPacket(ipPacket: packet) { if DNSHelper.isDnsRequestPacket(ipPacket: packet) {
// offset, dnsudp
if case .udp(let udpPacket) = packet.transportPacket {
let payloadOffset = udpPacket.payloadOffset
let dnsParser = DNSParser(data: packet.data, offset: payloadOffset)
if let dnsMessage = dnsParser.parse(), let name = dnsMessage.questions.first?.name {
// ip
if name.contains(self.config.networkAddress.networkDomain) {
SDLLogger.shared.log("[Tun] get cloud dns request: \(name)")
self.dnsClient?.forward(ipPacketData: packet.data)
}
// dnsudppayload
else if packet.data.count > payloadOffset {
//
let dnsPayload = Data(packet.data[payloadOffset..<packet.data.count])
SDLLogger.shared.log("[Tun] get local dns request: \(name)")
let tracker = DNSLocalClient.DNSTracker(transactionID: dnsMessage.transactionID, clientIP: packet.header.source, clientPort: udpPacket.srcPort, createdAt: Date())
self.dnsLocalClient?.query(tracker: tracker, dnsPayload: dnsPayload)
}
}
}
self.dnsClient?.forward(ipPacketData: packet.data) self.dnsClient?.forward(ipPacketData: packet.data)
return return
} else {
return
} }
let dstIp = packet.header.destination let dstIp = packet.header.destination
@ -812,8 +838,8 @@ actor SDLContextActor {
let dnsSettings = NEDNSSettings(servers: [dnsServer]) let dnsSettings = NEDNSSettings(servers: [dnsServer])
dnsSettings.searchDomains = [networkDomain] dnsSettings.searchDomains = [networkDomain]
dnsSettings.matchDomains = [networkDomain] dnsSettings.matchDomains = [networkDomain, ""]
// false // false Search Domain
dnsSettings.matchDomainsNoSearch = false dnsSettings.matchDomainsNoSearch = false
networkSettings.dnsSettings = dnsSettings networkSettings.dnsSettings = dnsSettings
@ -822,12 +848,6 @@ actor SDLContextActor {
// //
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()

View File

@ -2,6 +2,14 @@ import Foundation
import Network import Network
final class DNSLocalClient { final class DNSLocalClient {
// DNS
struct DNSTracker {
let transactionID: UInt16
let clientIP: UInt32 // IP ()
let clientPort: UInt16 // ()
let createdAt: Date //
}
private var connections: [NWConnection] = [] private var connections: [NWConnection] = []
// DNS // DNS
@ -9,6 +17,9 @@ final class DNSLocalClient {
public let packetFlow: AsyncStream<Data> public let packetFlow: AsyncStream<Data>
private let packetContinuation: AsyncStream<Data>.Continuation private let packetContinuation: AsyncStream<Data>.Continuation
private let locker = NSLock()
private var trackers: [UInt16: [DNSTracker]] = [:]
init() { init() {
let (stream, continuation) = AsyncStream.makeStream(of: Data.self, bufferingPolicy: .unbounded) let (stream, continuation) = AsyncStream.makeStream(of: Data.self, bufferingPolicy: .unbounded)
@ -29,6 +40,7 @@ final class DNSLocalClient {
case .ready: case .ready:
self?.receiveLoop(for: conn) self?.receiveLoop(for: conn)
case .failed(let error): case .failed(let error):
SDLLogger.shared.log("[DNSLocalClient] failed with error: \(error.localizedDescription)")
self?.stop() self?.stop()
case .cancelled: case .cancelled:
self?.packetContinuation.finish() self?.packetContinuation.finish()
@ -43,7 +55,11 @@ final class DNSLocalClient {
} }
/// 广 /// 广
func query(dnsPayload: Data) { func query(tracker: DNSTracker, dnsPayload: Data) {
locker.lock()
self.trackers[tracker.transactionID, default: []].append(tracker)
locker.unlock()
for conn in connections where conn.state == .ready { for conn in connections where conn.state == .ready {
conn.send(content: dnsPayload, completion: .contentProcessed({ _ in })) conn.send(content: dnsPayload, completion: .contentProcessed({ _ in }))
} }
@ -55,8 +71,7 @@ final class DNSLocalClient {
// AsyncStream // AsyncStream
// yield // yield
// //
// Transaction ID self?.handleResponse(data: data)
self?.packetContinuation.yield(data)
} }
if error == nil && conn.state == .ready { if error == nil && conn.state == .ready {
@ -65,6 +80,30 @@ final class DNSLocalClient {
} }
} }
private func handleResponse(data: Data) {
let dnsParser = DNSParser(data: data, offset: 0)
if let message = dnsParser.parse() {
let tranId = message.transactionID
locker.lock()
let items = self.trackers.removeValue(forKey: tranId)
locker.unlock()
if let items {
items.forEach { tracker in
let packet = Self.createDNSResponse(
payload: data,
srcIP: DNSHelper.dnsDestIpAddr,
srcPort: 53,
destIP: tracker.clientIP,
destPort: tracker.clientPort
)
self.packetContinuation.yield(packet)
}
}
}
}
func stop() { func stop() {
connections.forEach { conn in connections.forEach { conn in
conn.cancel() conn.cancel()
@ -73,3 +112,77 @@ final class DNSLocalClient {
} }
} }
extension DNSLocalClient {
/// TUN UDP/IPv4
static func createDNSResponse(payload: Data, srcIP: UInt32, srcPort: UInt16, destIP: UInt32, destPort: UInt16) -> Data {
let udpLen = 8 + payload.count
let ipLen = 20 + udpLen
// --- 1. IPv4 Header (20 ) ---
var ipHeader = Data(count: 20)
ipHeader[0] = 0x45 // Version 4, IHL 5
ipHeader[2...3] = withUnsafeBytes(of: UInt16(ipLen).bigEndian) { Data($0) }
ipHeader[8] = 64 // TTL
ipHeader[9] = 17 // Protocol UDP
// IP
ipHeader[12...15] = withUnsafeBytes(of: srcIP.bigEndian) { Data($0) }
ipHeader[16...19] = withUnsafeBytes(of: destIP.bigEndian) { Data($0) }
// IP Checksum
let ipChecksum = calculateChecksum(data: ipHeader)
ipHeader[10...11] = withUnsafeBytes(of: ipChecksum.bigEndian) { Data($0) }
// --- 2. UDP Header (8 ) ---
var udpHeader = Data(count: 8)
udpHeader[0...1] = withUnsafeBytes(of: srcPort.bigEndian) { Data($0) }
udpHeader[2...3] = withUnsafeBytes(of: destPort.bigEndian) { Data($0) }
udpHeader[4...5] = withUnsafeBytes(of: UInt16(udpLen).bigEndian) { Data($0) }
// UDP Checksum IPv4 0
udpHeader[6...7] = Data([0, 0])
// --- 3. ---
var packet = Data(capacity: ipLen)
packet.append(ipHeader)
packet.append(udpHeader)
packet.append(payload)
return packet
}
/// Internet Checksum
static func calculateChecksum(data: Data) -> UInt16 {
var sum: UInt32 = 0
let count = data.count
data.withUnsafeBytes { (ptr: UnsafeRawBufferPointer) in
guard let baseAddress = ptr.baseAddress else { return }
// 1. 16-bit
let wordCount = count / 2
let words = baseAddress.bindMemory(to: UInt16.self, capacity: wordCount)
for i in 0..<wordCount {
// 使 bigEndian: words[i]
sum += UInt32(UInt16(bigEndian: words[i]))
}
// 2.
if count % 2 != 0 {
// 8 16-bit
let lastByte = ptr[count - 1]
sum += UInt32(lastByte) << 8
}
}
// 3. 16
while (sum >> 16) != 0 {
sum = (sum & 0xffff) + (sum >> 16)
}
return UInt16(~sum & 0xffff)
}
}

View File

@ -29,7 +29,9 @@ struct DNSMessage {
var questions: [DNSQuestion] = [] var questions: [DNSQuestion] = []
var answers: [DNSResourceRecord] = [] var answers: [DNSResourceRecord] = []
var isResponse: Bool { (flags & 0x8000) != 0 } var isResponse: Bool {
(flags & 0x8000) != 0
}
} }
// MARK: - DNS // MARK: - DNS
@ -37,13 +39,15 @@ final class DNSParser {
private let data: Data private let data: Data
private var offset: Int = 0 private var offset: Int = 0
init(data: Data) { init(data: Data, offset: Int) {
self.data = data self.data = data
self.offset = offset
} }
func parse() -> DNSMessage? { func parse() -> DNSMessage? {
guard data.count >= 12 else { return nil } guard data.count >= 12 + self.offset else {
offset = 0 return nil
}
let id = readUInt16() let id = readUInt16()
let flags = readUInt16() let flags = readUInt16()
@ -55,11 +59,17 @@ final class DNSParser {
var message = DNSMessage(transactionID: id, flags: flags) var message = DNSMessage(transactionID: id, flags: flags)
for _ in 0..<qdCount { for _ in 0..<qdCount {
if let q = parseQuestion() { message.questions.append(q) } if let q = parseQuestion() {
message.questions.append(q)
}
} }
for _ in 0..<anCount { for _ in 0..<anCount {
if let rr = parseRR() { message.answers.append(rr) } if let rr = parseRR() {
message.answers.append(rr)
}
} }
return message return message
} }

View File

@ -201,17 +201,20 @@ struct UDPPacket {
let dstPort: UInt16 let dstPort: UInt16
let length: UInt16 let length: UInt16
let checksum: UInt16 let checksum: UInt16
let payloadOffset: Int
init?(_ data: Data, offset: Int) { init?(_ data: Data, offset: Int) {
guard data.count >= offset + 8 else { guard data.count >= offset + 8 else {
return nil return nil
} }
self.srcPort = UInt16(bytes: (data[offset], data[offset + 1])) self.srcPort = UInt16(bytes: (data[offset], data[offset + 1]))
self.dstPort = UInt16(bytes: (data[offset + 2], data[offset + 3])) self.dstPort = UInt16(bytes: (data[offset + 2], data[offset + 3]))
self.length = UInt16(bytes: (data[offset + 4], data[offset + 5])) self.length = UInt16(bytes: (data[offset + 4], data[offset + 5]))
self.checksum = UInt16(bytes: (data[offset + 6], data[offset + 7])) self.checksum = UInt16(bytes: (data[offset + 6], data[offset + 7]))
self.payloadOffset = offset + 8
} }
} }
// MARK: - ICMP Packet // MARK: - ICMP Packet

View File

@ -49,6 +49,7 @@ public class SDLConfiguration {
let version: Int let version: Int
let serverHost: String let serverHost: String
let serverIp: String
let stunServers: [String] let stunServers: [String]
let noticePort: Int let noticePort: Int
@ -78,6 +79,7 @@ public class SDLConfiguration {
public init(version: Int, public init(version: Int,
serverHost: String, serverHost: String,
serverIp: String,
stunServers: [String], stunServers: [String],
clientId: String, clientId: String,
networkAddress: NetworkAddress, networkAddress: NetworkAddress,
@ -86,9 +88,9 @@ public class SDLConfiguration {
accessToken: String, accessToken: String,
identityId: UInt32, identityId: UInt32,
exitNode: ExitNode?) { exitNode: ExitNode?) {
self.version = version self.version = version
self.serverHost = serverHost self.serverHost = serverHost
self.serverIp = serverIp
self.stunServers = stunServers self.stunServers = stunServers
self.clientId = clientId self.clientId = clientId
self.networkAddress = networkAddress self.networkAddress = networkAddress
@ -104,7 +106,7 @@ public class SDLConfiguration {
// //
extension SDLConfiguration { extension SDLConfiguration {
static func parse(options: [String: NSObject]) -> SDLConfiguration? { static func parse(options: [String: NSObject]) async -> SDLConfiguration? {
guard let version = options["version"] as? Int, guard let version = options["version"] as? Int,
let serverHost = options["server_host"] as? String, let serverHost = options["server_host"] as? String,
let stunAssistHost = options["stun_assist_host"] as? String, let stunAssistHost = options["stun_assist_host"] as? String,
@ -121,6 +123,11 @@ extension SDLConfiguration {
return nil return nil
} }
// dns
guard let serverIp = await SDLUtil.resolveHostname(host: serverHost) else {
return nil
}
// //
var exitNode: ExitNode? = nil var exitNode: ExitNode? = nil
if let exitNodeIpStr = options["exit_node_ip"] as? String, let exitNodeIp = SDLUtil.ipv4StrToInt32(exitNodeIpStr) { if let exitNodeIpStr = options["exit_node_ip"] as? String, let exitNodeIp = SDLUtil.ipv4StrToInt32(exitNodeIpStr) {
@ -129,6 +136,7 @@ extension SDLConfiguration {
return SDLConfiguration(version: version, return SDLConfiguration(version: version,
serverHost: serverHost, serverHost: serverHost,
serverIp: serverIp,
stunServers: [serverHost, stunAssistHost], stunServers: [serverHost, stunAssistHost],
clientId: clientId, clientId: clientId,
networkAddress: networkAddress, networkAddress: networkAddress,

View File

@ -7,6 +7,7 @@
import Foundation import Foundation
import SystemConfiguration import SystemConfiguration
import Network
struct SDLUtil { struct SDLUtil {
@ -76,5 +77,31 @@ struct SDLUtil {
} }
return results return results
} }
//
static func resolveHostname(host: String) async -> String? {
let endpoint = NWEndpoint.hostPort(host: NWEndpoint.Host(host), port: 53)
let parameters = NWParameters.udp
// 使 utun
parameters.prohibitedInterfaceTypes = [.other]
let connection = NWConnection(to: endpoint, using: parameters)
return await withCheckedContinuation { continuation in
connection.stateUpdateHandler = { state in
if case .ready = state {
if let path = connection.currentPath,
case .hostPort(let resolvedHost, _) = path.remoteEndpoint {
let ip = String(describing: resolvedHost)
continuation.resume(returning: ip)
connection.cancel()
}
} else if case .failed = state {
continuation.resume(returning: nil)
}
}
connection.start(queue: .global())
}
}
} }