解决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

@ -24,12 +24,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
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 {
completionHandler(TunnelError.invalidContext)
@ -39,6 +33,12 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
//
let rsaCipher = try! CCRSACipher(keySize: 1024)
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)
await self.contextActor?.start()
completionHandler(nil)

View File

@ -103,6 +103,7 @@ actor SDLContextActor {
}
public func start() async {
self.startMonitor()
// arp
@ -233,7 +234,7 @@ actor SDLContextActor {
self.dnsWorker = nil
// 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()
SDLLogger.shared.log("[SDLContext] dnsClient started")
self.dnsClient = dnsClient
@ -264,6 +265,8 @@ actor SDLContextActor {
if Task.isCancelled {
break
}
// Ip
let nePacket = NEPacket(data: packet, protocolFamily: 2)
self.provider.packetFlow.writePacketObjects([nePacket])
}
@ -693,8 +696,31 @@ actor SDLContextActor {
let networkAddr = self.config.networkAddress
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)
return
} else {
return
}
let dstIp = packet.header.destination
@ -812,8 +838,8 @@ actor SDLContextActor {
let dnsSettings = NEDNSSettings(servers: [dnsServer])
dnsSettings.searchDomains = [networkDomain]
dnsSettings.matchDomains = [networkDomain]
// false
dnsSettings.matchDomains = [networkDomain, ""]
// false Search Domain
dnsSettings.matchDomainsNoSearch = false
networkSettings.dnsSettings = dnsSettings
@ -822,12 +848,6 @@ actor SDLContextActor {
//
ipv4Settings.includedRoutes = routes
let rs = self.getIpv4ExcludeRoutes()
rs.forEach { x in
SDLLogger.shared.log("excludsL: \(x.destinationAddress)")
}
//
ipv4Settings.excludedRoutes = self.getIpv4ExcludeRoutes()

View File

@ -2,6 +2,14 @@ import Foundation
import Network
final class DNSLocalClient {
// DNS
struct DNSTracker {
let transactionID: UInt16
let clientIP: UInt32 // IP ()
let clientPort: UInt16 // ()
let createdAt: Date //
}
private var connections: [NWConnection] = []
// DNS
@ -10,6 +18,9 @@ final class DNSLocalClient {
public let packetFlow: AsyncStream<Data>
private let packetContinuation: AsyncStream<Data>.Continuation
private let locker = NSLock()
private var trackers: [UInt16: [DNSTracker]] = [:]
init() {
let (stream, continuation) = AsyncStream.makeStream(of: Data.self, bufferingPolicy: .unbounded)
self.packetFlow = stream
@ -29,6 +40,7 @@ final class DNSLocalClient {
case .ready:
self?.receiveLoop(for: conn)
case .failed(let error):
SDLLogger.shared.log("[DNSLocalClient] failed with error: \(error.localizedDescription)")
self?.stop()
case .cancelled:
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 {
conn.send(content: dnsPayload, completion: .contentProcessed({ _ in }))
}
@ -55,8 +71,7 @@ final class DNSLocalClient {
// AsyncStream
// yield
//
// Transaction ID
self?.packetContinuation.yield(data)
self?.handleResponse(data: data)
}
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() {
connections.forEach { conn in
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 answers: [DNSResourceRecord] = []
var isResponse: Bool { (flags & 0x8000) != 0 }
var isResponse: Bool {
(flags & 0x8000) != 0
}
}
// MARK: - DNS
@ -37,13 +39,15 @@ final class DNSParser {
private let data: Data
private var offset: Int = 0
init(data: Data) {
init(data: Data, offset: Int) {
self.data = data
self.offset = offset
}
func parse() -> DNSMessage? {
guard data.count >= 12 else { return nil }
offset = 0
guard data.count >= 12 + self.offset else {
return nil
}
let id = readUInt16()
let flags = readUInt16()
@ -55,11 +59,17 @@ final class DNSParser {
var message = DNSMessage(transactionID: id, flags: flags)
for _ in 0..<qdCount {
if let q = parseQuestion() { message.questions.append(q) }
if let q = parseQuestion() {
message.questions.append(q)
}
}
for _ in 0..<anCount {
if let rr = parseRR() { message.answers.append(rr) }
if let rr = parseRR() {
message.answers.append(rr)
}
}
return message
}

View File

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

View File

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

View File

@ -7,6 +7,7 @@
import Foundation
import SystemConfiguration
import Network
struct SDLUtil {
@ -77,4 +78,30 @@ struct SDLUtil {
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())
}
}
}