punchnet-macos/Tun/Context/SDLSuperControlPlane.swift
2026-05-28 16:11:31 +08:00

215 lines
8.5 KiB
Swift

//
// SDLSuperControlPlane.swift
// Tun
//
// Created by Codex on 2026/5/28.
//
import Foundation
import NIOCore
enum SuperDecision {
case updateIPv6Assist(SDLV6Info?)
case completeRegistration(cipher: CCDataCipher)
case failTunnel(Error)
case publishTunnelEvent(code: Int?, message: String)
case sendSuper(type: SDLPacketType, data: Data)
case sendPacket(type: SDLPacketType, data: Data, remoteAddress: SocketAddress)
case resolvePeerInfo(SDLPeerInfo)
case removeSession(dstMac: Data)
case requestExposedService
case shutdown(message: String)
case applyPolicyResponse(SDLPolicyResponse)
case applyExposedServiceResponse(SDLExposedServiceResponse)
case handleARPResponse(SDLArpResponse)
}
actor SDLSuperControlPlane {
typealias DecisionHandler = @Sendable (SuperDecision) async -> Void
private let config: SDLConfiguration
private let rsaCipher: RSACipher
private var sessionToken: Data?
private var onDecision: DecisionHandler = { _ in }
init(config: SDLConfiguration, rsaCipher: RSACipher) {
self.config = config
self.rsaCipher = rsaCipher
}
func updateDecisionHandler(_ onDecision: @escaping DecisionHandler) {
self.onDecision = onDecision
}
func reset() {
self.sessionToken = nil
}
func handle(_ message: SDLSuperMessage) async {
switch message {
case .welcome(let welcome):
await self.handleWelcome(welcome)
case .pong:
()
case .registerSuperAck(let registerSuperAck):
await self.handleRegisterSuperAck(registerSuperAck)
case .registerSuperNak(let registerSuperNak):
await self.handleRegisterSuperNak(registerSuperNak)
case .peerInfo(let peerInfo):
SDLLogger.log("[SDLContext] peer message: \(peerInfo)")
await self.onDecision(.resolvePeerInfo(peerInfo))
case .event(let event):
await self.handleEvent(event)
case .policyReponse(let policyResponse):
await self.onDecision(.applyPolicyResponse(policyResponse))
case .exposedServiceResponse(let response):
await self.onDecision(.applyExposedServiceResponse(response))
case .arpResponse(let arpResponse):
SDLLogger.log("[SDLContext] get arp response: \(arpResponse)")
await self.onDecision(.handleARPResponse(arpResponse))
}
}
func sendStunRequest(natType: SDLNATProberActor.NatType, v6Info: SDLV6Info?) async {
guard let sessionToken else {
return
}
var stunRequest = SDLStunRequest()
stunRequest.clientID = self.config.clientId
stunRequest.networkID = self.config.networkAddress.networkId
stunRequest.ip = self.config.networkAddress.ip
stunRequest.mac = self.config.networkAddress.mac
stunRequest.natType = UInt32(natType.rawValue)
stunRequest.sessionToken = sessionToken
if let v6Info {
stunRequest.v6Info = v6Info
}
if let stunData = try? stunRequest.serializedData() {
await self.onDecision(.sendPacket(type: .stunRequest, data: stunData, remoteAddress: self.config.stunSocketAddress))
}
}
private func handleWelcome(_ welcome: SDLWelcome) async {
SDLLogger.log("[SDLContext] quic welcome: \(welcome)")
if welcome.hasIpv6Assist {
await self.onDecision(.updateIPv6Assist(welcome.ipv6Assist))
} else {
await self.onDecision(.updateIPv6Assist(nil))
}
await self.doRegisterSuper()
SDLLogger.log("[SDLContext] quic doRegisterSuper")
}
private func handleRegisterSuperAck(_ registerSuperAck: SDLRegisterSuperAck) async {
guard let key = try? self.rsaCipher.decode(data: Data(registerSuperAck.key)) else {
SDLLogger.fatal("[SDLSuperControlPlane] registerSuperAck invalid key, will fail tunnel")
SDLLogger.log("[SDLContext] registerSuperAck invalid key")
await self.onDecision(.failTunnel(SDLError.invalidKey))
return
}
let algorithm = registerSuperAck.algorithm.lowercased()
let regionId = registerSuperAck.regionID
self.sessionToken = registerSuperAck.sessionToken
let cipher: CCDataCipher
switch algorithm {
case "aes":
cipher = CCAESChiper(key: key)
case "chacha20":
cipher = CCChaCha20Cipher(regionId: regionId, keyData: key)
default:
SDLLogger.fatal("[SDLSuperControlPlane] unsupported cipher algorithm \(algorithm), will fail tunnel")
SDLLogger.log("[SDLContext] registerSuperAck invalid algorithm \(algorithm)")
await self.onDecision(.failTunnel(SDLError.unsupportedAlgorithm(algorithm: algorithm)))
return
}
SDLLogger.log("[SDLContext] registerSuperAck, use algorithm \(algorithm), key len: \(key.count)")
await self.onDecision(.completeRegistration(cipher: cipher))
}
private func handleRegisterSuperNak(_ nakPacket: SDLRegisterSuperNak) async {
let errorMessage = nakPacket.errorMessage
guard let errorCode = SDLNAKErrorCode(rawValue: UInt8(nakPacket.errorCode)) else {
return
}
switch errorCode {
case .invalidToken, .nodeDisabled:
SDLLogger.fatal("[SDLSuperControlPlane] SuperNak \(errorCode) will fail tunnel: \(errorMessage)")
await self.onDecision(.publishTunnelEvent(code: Int(errorCode.rawValue), message: errorMessage))
await self.onDecision(.failTunnel(NSError(domain: "com.jihe.punchnet.tun", code: -1)))
case .noIpAddress, .networkFault, .internalFault:
await self.onDecision(.publishTunnelEvent(code: Int(errorCode.rawValue), message: errorMessage))
}
SDLLogger.log("[SDLContext] Get a SuperNak message exit")
}
private func handleEvent(_ event: SDLEvent) async {
switch event.event {
case .natChanged(let natChangedEvent):
let dstMac = natChangedEvent.mac
SDLLogger.log("[SDLContext] natChangedEvent, dstMac: \(dstMac)")
await self.onDecision(.removeSession(dstMac: dstMac))
case .sendRegister(let sendRegisterEvent):
await self.handleSendRegisterEvent(sendRegisterEvent)
case .exposedServiceChanged:
SDLLogger.log("[SDLContext] exposedServiceChanged event")
await self.onDecision(.requestExposedService)
case .shutdown(let shutdownEvent):
SDLLogger.fatal("[SDLSuperControlPlane] shutdown event received: \(shutdownEvent.message)")
await self.onDecision(.shutdown(message: shutdownEvent.message))
case .none:
()
}
}
private func handleSendRegisterEvent(_ event: SDLEvent.SendRegister) async {
var register = SDLRegister()
register.networkID = self.config.networkAddress.networkId
register.srcMac = self.config.networkAddress.mac
register.dstMac = event.dstMac
guard let registerData = try? register.serializedData() else {
return
}
SDLLogger.log("[SDLContext] sendRegisterEvent, ip: \(event)")
if event.natIp > 0 && event.natPort > 0 {
let address = SDLUtil.int32ToIp(event.natIp)
if let remoteAddress = try? SocketAddress(ipAddress: address, port: Int(event.natPort)) {
await self.onDecision(.sendPacket(type: .register, data: registerData, remoteAddress: remoteAddress))
}
}
if event.hasV6Info, let remoteAddress = try? await event.v6Info.socketAddress() {
await self.onDecision(.sendPacket(type: .register, data: registerData, remoteAddress: remoteAddress))
}
}
private func doRegisterSuper() async {
var registerSuper = SDLRegisterSuper()
registerSuper.clientID = self.config.clientId
registerSuper.networkID = self.config.networkAddress.networkId
registerSuper.mac = self.config.networkAddress.mac
registerSuper.ip = self.config.networkAddress.ip
registerSuper.maskLen = UInt32(self.config.networkAddress.maskLen)
registerSuper.hostname = self.config.hostname
registerSuper.pubKey = self.rsaCipher.pubKey
registerSuper.accessToken = self.config.accessToken
if let registerSuperData = try? registerSuper.serializedData() {
SDLLogger.log("[SDLContext] will send register super")
await self.onDecision(.sendSuper(type: .registerSuper, data: registerSuperData))
}
}
}