punchnet-macos/Tun/Punchnet/Actors/SDLPuncherActor.swift
2026-01-29 22:07:46 +08:00

102 lines
3.0 KiB
Swift

//
// SDLPuncherActor.swift
// Tun
//
// Created by on 2026/1/7.
//
import Foundation
import NIOCore
actor SDLPuncherActor {
// dstMac
private var coolingDown: Set<Data> = []
private let cooldown: Duration = .seconds(5)
private var udpHole: SDLUDPHole?
private var pktId: UInt32 = 1
//
private var pendingRequests: [UInt32:RegisterRequest] = [:]
// holer
private var logger: SDLLogger
private var querySocketAddress: SocketAddress
struct RegisterRequest {
let srcMac: Data
let dstMac: Data
let networkId: UInt32
}
init(querySocketAddress: SocketAddress, logger: SDLLogger) {
self.querySocketAddress = querySocketAddress
self.logger = logger
}
func setUDPHoleActor(udpHole: SDLUDPHole?) {
self.udpHole = udpHole
}
func submitRegisterRequest(request: RegisterRequest) {
let dstMac = request.dstMac
guard !coolingDown.contains(dstMac) else {
return
}
//
coolingDown.insert(dstMac)
let pktId = self.pktId
self.pktId &+= 1
if self.pktId == 0 {
self.pktId = 1
}
Task {
await self.tryHole(pktId: pktId, request: request)
//
try? await Task.sleep(for: .seconds(5))
self.endCooldown(for: dstMac)
self.removePendingRequest(for: pktId)
}
}
func handlePeerInfo(peerInfo: SDLPeerInfo) async {
if let request = pendingRequests.removeValue(forKey: peerInfo.pktID) {
if let remoteAddress = peerInfo.v4Info.socketAddress() {
self.logger.log("[SDLContext] hole sock address: \(remoteAddress)", level: .debug)
// register
var register = SDLRegister()
register.networkID = request.networkId
register.srcMac = request.srcMac
register.dstMac = request.dstMac
self.udpHole?.send(type: .register, data: try! register.serializedData(), remoteAddress: remoteAddress)
} else {
self.logger.log("[SDLContext] hole sock address is invalid: \(peerInfo.v4Info)", level: .warning)
}
}
}
private func endCooldown(for key: Data) {
self.coolingDown.remove(key)
}
private func removePendingRequest(for pktId: UInt32) {
self.pendingRequests.removeValue(forKey: pktId)
}
private func tryHole(pktId: UInt32, request: RegisterRequest) async {
var queryInfo = SDLQueryInfo()
queryInfo.pktID = pktId
queryInfo.dstMac = request.dstMac
self.pendingRequests[pktId] = request
if let queryData = try? queryInfo.serializedData() {
self.udpHole?.send(type: .queryInfo, data: queryData, remoteAddress: self.querySocketAddress)
}
}
}