punchnet-macos/Tun/Context/SDLHoleControlPlane.swift
2026-05-28 01:26:45 +08:00

114 lines
4.2 KiB
Swift

//
// SDLHoleControlPlane.swift
// Tun
//
// Created by Codex on 2026/5/28.
//
import Foundation
import NIOCore
enum HoleDecision {
case updateNatType(SDLNATProberActor.NatType)
case sendPacket(type: SDLPacketType, data: Data, remoteAddress: SocketAddress)
case addSession(Session)
}
struct SDLHoleControlPlane {
private let networkAddress: SDLConfiguration.NetworkAddress
init(networkAddress: SDLConfiguration.NetworkAddress) {
self.networkAddress = networkAddress
}
func handle(_ event: SDLUDPHoleService.Event) -> [HoleDecision] {
switch event {
case .ready(let localAddress):
SDLLogger.log("[SDLContext] udpHole ready: \(localAddress)")
return []
case .natType(let natType):
SDLLogger.log("[SDLContext] nat_type is: \(natType)")
return [
.updateNatType(natType)
]
case .packet(let remoteAddress, let message):
return self.handlePacket(remoteAddress: remoteAddress, message: message)
case .closed(let error):
SDLLogger.log("[SDLContext] udpHole closed: \(error)", for: .debug)
return []
}
}
private func handlePacket(remoteAddress: SocketAddress, message: SDLHoleControlMessage) -> [HoleDecision] {
switch message {
case .stunReply, .stunProbeReply:
SDLLogger.log("[SDLContext] get a stun reply", for: .debug)
return []
case .register(let register):
return self.handleRegister(remoteAddress: remoteAddress, register: register)
case .registerAck(let registerAck):
return self.handleRegisterAck(remoteAddress: remoteAddress, registerAck: registerAck)
}
}
private func handleRegister(remoteAddress: SocketAddress, register: SDLRegister) -> [HoleDecision] {
SDLLogger.log("[SDLContext] register packet: \(register), network_address: \(self.networkAddress)")
var decisions: [HoleDecision] = []
guard register.dstMac == self.networkAddress.mac && register.networkID == self.networkAddress.networkId else {
SDLLogger.log("[SDLContext] didReadRegister get a invalid packet, because dst_ip not matched: \(register.dstMac)")
return decisions
}
var registerAck = SDLRegisterAck()
registerAck.networkID = self.networkAddress.networkId
registerAck.srcMac = self.networkAddress.mac
registerAck.dstMac = register.srcMac
if let data = try? registerAck.serializedData() {
decisions.append(.sendPacket(type: .registerAck, data: data, remoteAddress: remoteAddress))
}
if let session = self.makeSession(dstMac: register.srcMac, remoteAddress: remoteAddress) {
decisions.append(.addSession(session))
} else {
SDLLogger.log("[SDLContext] didReadRegister get unsupported remoteAddress: \(remoteAddress)", for: .debug)
}
return decisions
}
private func handleRegisterAck(remoteAddress: SocketAddress, registerAck: SDLRegisterAck) -> [HoleDecision] {
guard registerAck.dstMac == self.networkAddress.mac && registerAck.networkID == self.networkAddress.networkId else {
SDLLogger.log("[SDLContext] didReadRegisterAck get a invalid packet, because dst_mac not matched: \(registerAck.dstMac)", for: .debug)
return []
}
guard let session = self.makeSession(dstMac: registerAck.srcMac, remoteAddress: remoteAddress) else {
SDLLogger.log("[SDLContext] didReadRegisterAck get unsupported remoteAddress: \(remoteAddress)", for: .debug)
return []
}
return [.addSession(session)]
}
private func makeSession(dstMac: Data, remoteAddress: SocketAddress) -> Session? {
guard let addressType = Self.addressType(from: remoteAddress) else {
return nil
}
return Session(dstMac: dstMac, natAddress: remoteAddress, addressType: addressType)
}
private static func addressType(from remoteAddress: SocketAddress) -> Session.AddressType? {
switch remoteAddress {
case .v4:
return .v4
case .v6:
return .v6
default:
return nil
}
}
}