punchnet-macos/Tun/Punchnet/SessionManager.swift
2026-04-16 10:42:44 +08:00

123 lines
3.4 KiB
Swift

//
// Session.swift
// sdlan
// Session
// Created by on 2025/7/14.
//
import Foundation
import NIOCore
import Darwin
struct Session: @unchecked Sendable {
enum AddressType: String, Hashable {
case v4
case v6
init?(socketAddress: SocketAddress) {
switch socketAddress {
case .v4:
self = .v4
case .v6:
self = .v6
default:
return nil
}
}
init?(packetType: LayerPacket.PacketType) {
switch packetType {
case .arp, .ipv4:
self = .v4
case .ipv6:
self = .v6
default:
return nil
}
}
}
// ip,
let dstMac: Data
// nat
let natAddress: SocketAddress
//
let addressType: AddressType
// 使
var lastTimestamp: Int32
init?(dstMac: Data, natAddress: SocketAddress) {
guard let addressType = AddressType(socketAddress: natAddress) else {
return nil
}
self.dstMac = dstMac
self.natAddress = natAddress
self.addressType = addressType
self.lastTimestamp = Int32(Date().timeIntervalSince1970)
}
mutating func updateLastTimestamp(_ lastTimestamp: Int32) {
self.lastTimestamp = lastTimestamp
}
}
actor SessionManager {
private var sessions: [Data: [Session.AddressType: Session]] = [:]
// session
private let ttl: Int32 = 10
func getSession(toAddress: Data, preferredType: Session.AddressType? = nil) -> Session? {
let timestamp = Int32(Date().timeIntervalSince1970)
guard var sessions = self.sessions[toAddress] else {
return nil
}
sessions = sessions.filter { $0.value.lastTimestamp + ttl >= timestamp }
guard !sessions.isEmpty else {
self.sessions.removeValue(forKey: toAddress)
return nil
}
guard var session = self.selectSession(in: sessions, preferredType: preferredType) else {
self.sessions[toAddress] = sessions
return nil
}
session.updateLastTimestamp(timestamp)
sessions[session.addressType] = session
self.sessions[toAddress] = sessions
return session
}
func addSession(session: Session) {
let timestamp = Int32(Date().timeIntervalSince1970)
var sessions = self.sessions[session.dstMac, default: [:]]
sessions = sessions.filter {
$0.value.lastTimestamp + ttl >= timestamp && $0.key != session.addressType
}
sessions[session.addressType] = session
self.sessions[session.dstMac] = sessions
}
func removeSession(dstMac: Data) {
self.sessions.removeValue(forKey: dstMac)
}
private func selectSession(in sessions: [Session.AddressType: Session], preferredType: Session.AddressType?) -> Session? {
if let preferredType {
if let preferred = sessions[preferredType] {
return preferred
}
}
return sessions.values.max(by: { $0.lastTimestamp < $1.lastTimestamp })
}
}