punchnet-macos/Tun/Punchnet/Actors/SDLTunPacketRouter.swift
2026-04-14 20:07:17 +08:00

115 lines
3.9 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// SDLTunPacketRouter.swift
// Tun
//
// Created by on 2026/4/14.
//
import Foundation
struct SDLTunPacketRouter {
enum DropReason: String {
case invalidDNSRequest
case noRoute
}
enum ForwardKind {
case sameNetwork
case exitNode
case dnsExitNode
}
enum RouteDecision {
case loopback(ipPacketData: Data)
case cloudDNS(name: String, ipPacketData: Data)
case localDNS(name: String, payload: Data, tracker: DNSLocalClient.DNSTracker)
case forwardToNextHop(ip: UInt32, type: LayerPacket.PacketType, data: Data, kind: ForwardKind)
case drop(reason: DropReason)
}
let networkAddress: SDLConfiguration.NetworkAddress
let exitNode: SDLConfiguration.ExitNode?
func route(packet: IPPacket, now: Date = Date()) -> RouteDecision {
let dstIp = packet.header.destination
// , ip
if dstIp == self.networkAddress.ip {
return .loopback(ipPacketData: packet.data)
}
// dns
if let dnsDecision = self.routeDNS(packet: packet, now: now) {
return dnsDecision
}
//
// ip
if SDLUtil.inSameNetwork(ip: dstIp, compareIp: self.networkAddress.ip, maskLen: self.networkAddress.maskLen) {
return .forwardToNextHop(ip: dstIp, type: .ipv4, data: packet.data, kind: .sameNetwork)
}
// ,
if let exitNode = self.exitNode {
return .forwardToNextHop(ip: exitNode.exitNodeIp, type: .ipv4, data: packet.data, kind: .exitNode)
}
return .drop(reason: .noRoute)
}
private func routeDNS(packet: IPPacket, now: Date) -> RouteDecision? {
guard DNSHelper.isDnsRequestPacket(ipPacket: packet) else {
return nil
}
guard case .udp(let udpPacket) = packet.transportPacket else {
return .drop(reason: .invalidDNSRequest)
}
// offset, dnsudp
let payloadOffset = udpPacket.payloadOffset
let dnsParser = DNSParser(data: packet.data, offset: payloadOffset)
guard let dnsMessage = dnsParser.parse(), let name = dnsMessage.questions.first?.name else {
return .drop(reason: .invalidDNSRequest)
}
// ip
if name.contains(self.networkAddress.networkDomain) {
return .cloudDNS(name: name, ipPacketData: packet.data)
}
//
if let exitNode = self.exitNode {
return .forwardToNextHop(ip: exitNode.exitNodeIp, type: .ipv4, data: packet.data, kind: .dnsExitNode)
}
// dnsudppayload
let dnsPayload = Data(packet.data[payloadOffset..<packet.data.count])
let tracker = DNSLocalClient.DNSTracker(
transactionID: dnsMessage.transactionID,
clientIP: packet.header.source,
clientPort: udpPacket.srcPort,
createdAt: now
)
return .localDNS(name: name, payload: dnsPayload, tracker: tracker)
}
}
extension SDLTunPacketRouter.RouteDecision {
var shouldTrackFlow: Bool {
switch self {
case .forwardToNextHop(_, _, _, let kind):
switch kind {
case .sameNetwork, .exitNode:
return true
case .dnsExitNode:
return false
}
default:
return false
}
}
}