swiftlib_sdlan/Sources/sdlan/SDLContext.swift
2025-07-15 00:17:20 +08:00

554 lines
22 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.

//
// SDLContext.swift
// Tun
//
// Created by on 2024/2/29.
//
import Foundation
import NetworkExtension
import NIOCore
import Combine
//
/*
1. rsa
*/
class SDLContext: @unchecked Sendable {
//
struct Route {
let dstAddress: String
let subnetMask: String
var debugInfo: String {
return "\(dstAddress):\(subnetMask)"
}
}
let config: SDLConfiguration
// tun
var devAddr: SDLDevAddr
// nat,
//var natAddress: SDLNatAddress?
// nat
var natType: SDLNatProber.NatType = .blocked
// AES
var aesCipher: AESCipher
// aes
var aesKey: Data = Data()
// rsa, public_key
let rsaCipher: RSACipher
//
var udpHole: SDLUDPHole?
private var udpCancel: AnyCancellable?
var superClient: SDLSuperClient?
private var superCancel: AnyCancellable?
//
private var readTask: Task<(), Never>?
let provider: NEPacketTunnelProvider
private var sessionManager: SessionManager
private var holerManager: HolerManager
private var arpServer: ArpServer
// stunRequestcookie
private var lastCookie: UInt32? = 0
//
private var stunCancel: AnyCancellable?
//
private var monitor = SDLNetworkMonitor()
private var monitorCancel: AnyCancellable?
// socket
private var noticeClient: SDLNoticeClient
//
private var flowTracer = SDLFlowTracerActor()
private var flowTracerCancel: AnyCancellable?
init(provider: NEPacketTunnelProvider, config: SDLConfiguration, rsaCipher: RSACipher, aesCipher: AESCipher) throws {
self.config = config
self.rsaCipher = rsaCipher
self.aesCipher = aesCipher
// mac
var devAddr = SDLDevAddr()
devAddr.mac = Self.getMacAddress()
self.devAddr = devAddr
self.provider = provider
self.sessionManager = SessionManager()
self.holerManager = HolerManager()
self.arpServer = ArpServer(known_macs: [:])
self.noticeClient = SDLNoticeClient()
}
func start() async throws {
try await self.startSuperClient()
try await self.startUDPHole()
self.noticeClient.start()
//
self.monitorCancel = self.monitor.eventFlow.sink { event in
switch event {
case .changed:
// nat
Task {
self.natType = await SDLNatProber.getNatType(udpHole: self.udpHole, config: self.config)
NSLog("didNetworkPathChanged, nat type is: \(self.natType)")
}
case .unreachable:
NSLog("didNetworkPathUnreachable")
}
}
self.monitor.start()
}
private func startSuperClient() async throws {
self.superClient = SDLSuperClient(host: config.superHost, port: config.superPort)
// super
self.superCancel?.cancel()
self.superCancel = self.superClient?.eventFlow.sink { event in
Task {
await self.handleSuperEvent(event: event)
}
}
try await self.superClient?.start()
}
private func handleSuperEvent(event: SDLSuperClient.SuperEvent) async {
switch event {
case .ready:
NSLog("[SDLContext] get registerSuper, mac address: \(Self.formatMacAddress(mac: self.devAddr.mac))")
guard let message = await self.superClient?.registerSuper(context: self) else {
return
}
switch message.packet {
case .registerSuperAck(let registerSuperAck):
// rsa
let aesKey = try! self.rsaCipher.decode(data: Data(registerSuperAck.aesKey))
let upgradeType = SDLUpgradeType(rawValue: registerSuperAck.upgradeType)
NSLog("[SDLContext] get registerSuperAck, aes_key len: \(aesKey.count)")
self.devAddr = registerSuperAck.devAddr
if upgradeType == .force {
let forceUpgrade = NoticeMessage.UpgradeMessage(prompt: registerSuperAck.upgradePrompt, address: registerSuperAck.upgradeAddress)
self.noticeClient.send(data: forceUpgrade.binaryData)
exit(-1)
}
// tun
await self.didNetworkConfigChanged(devAddr: self.devAddr)
self.aesKey = aesKey
if upgradeType == .normal {
let normalUpgrade = NoticeMessage.UpgradeMessage(prompt: registerSuperAck.upgradePrompt, address: registerSuperAck.upgradeAddress)
self.noticeClient.send(data: normalUpgrade.binaryData)
}
case .registerSuperNak(let nakPacket):
let errorMessage = nakPacket.errorMessage
guard let errorCode = SDLNAKErrorCode(rawValue: UInt8(nakPacket.errorCode)) else {
return
}
switch errorCode {
case .invalidToken, .nodeDisabled:
let alertNotice = NoticeMessage.AlertMessage(alert: errorMessage)
self.noticeClient.send(data: alertNotice.binaryData)
exit(-1)
case .noIpAddress, .networkFault, .internalFault:
let alertNotice = NoticeMessage.AlertMessage(alert: errorMessage)
self.noticeClient.send(data: alertNotice.binaryData)
}
SDLLogger.log("Get a SuperNak message exit", level: .error)
default:
()
}
case .closed:
SDLLogger.log("[SDLContext] super client closed", level: .debug)
await self.arpServer.clear()
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
Task {@MainActor in
try await self.startSuperClient()
}
}
case .event(let evt):
switch evt {
case .natChanged(let natChangedEvent):
let dstMac = natChangedEvent.mac
NSLog("natChangedEvent, dstMac: \(dstMac)")
await sessionManager.removeSession(dstMac: dstMac)
case .sendRegister(let sendRegisterEvent):
NSLog("sendRegisterEvent, ip: \(sendRegisterEvent)")
let address = SDLUtil.int32ToIp(sendRegisterEvent.natIp)
if let remoteAddress = try? SocketAddress.makeAddressResolvingHost(address, port: Int(sendRegisterEvent.natPort)) {
// register
self.udpHole?.sendRegister(context: self, remoteAddress: remoteAddress, dst_mac: sendRegisterEvent.dstMac)
}
case .networkShutdown(let shutdownEvent):
let alertNotice = NoticeMessage.AlertMessage(alert: shutdownEvent.message)
self.noticeClient.send(data: alertNotice.binaryData)
exit(-1)
}
case .command(let packetId, let command):
switch command {
case .changeNetwork(let changeNetworkCommand):
// rsa
let aesKey = try! self.rsaCipher.decode(data: Data(changeNetworkCommand.aesKey))
NSLog("[SDLContext] change network command get aes_key len: \(aesKey.count)")
self.devAddr = changeNetworkCommand.devAddr
// tun
await self.didNetworkConfigChanged(devAddr: self.devAddr)
self.aesKey = aesKey
var commandAck = SDLCommandAck()
commandAck.status = true
self.superClient?.commandAck(packetId: packetId, ack: commandAck)
}
}
}
private func startUDPHole() async throws {
self.udpHole = SDLUDPHole()
self.udpCancel?.cancel()
self.udpCancel = self.udpHole?.eventFlow.sink { event in
Task.detached {
await self.handleUDPEvent(event: event)
}
}
try await self.udpHole?.start()
}
private func handleUDPEvent(event: SDLUDPHole.UDPEvent) async {
switch event {
case .ready:
//
self.natType = await SDLNatProber.getNatType(udpHole: self.udpHole, config: self.config)
SDLLogger.log("[SDLContext] nat type is: \(self.natType)", level: .debug)
let timer = Timer.publish(every: 5.0, on: .main, in: .common).autoconnect()
self.stunCancel = Just(Date()).merge(with: timer).sink { _ in
self.lastCookie = self.udpHole?.stunRequest(context: self)
}
case .closed:
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
Task {
try await self.startUDPHole()
}
}
case .message(let remoteAddress, let message):
switch message {
case .register(let register):
NSLog("register packet: \(register), dev_addr: \(self.devAddr)")
// tun,
if register.dstMac == self.devAddr.mac && register.networkID == self.devAddr.networkID {
// ack
self.udpHole?.sendRegisterAck(context: self, remoteAddress: remoteAddress, dst_mac: register.srcMac)
// , super-nodenatudpnat
let session = Session(dstMac: register.srcMac, natAddress: remoteAddress)
await self.sessionManager.addSession(session: session)
} else {
SDLLogger.log("SDLContext didReadRegister get a invalid packet, because dst_ip not matched: \(register.dstMac)", level: .warning)
}
case .registerAck(let registerAck):
// tun,
if registerAck.dstMac == self.devAddr.mac && registerAck.networkID == self.devAddr.networkID {
let session = Session(dstMac: registerAck.srcMac, natAddress: remoteAddress)
await self.sessionManager.addSession(session: session)
} else {
SDLLogger.log("SDLContext didReadRegisterAck get a invalid packet, because dst_mac not matched: \(registerAck.dstMac)", level: .warning)
}
case .stunReply(let stunReply):
let cookie = stunReply.cookie
if cookie == self.lastCookie {
// nat
//self.natAddress = stunReply.natAddress
SDLLogger.log("[SDLContext] get a stunReply: \(try! stunReply.jsonString())")
}
default:
()
}
case .data(let data):
let mac = LayerPacket.MacAddress(data: data.dstMac)
guard (data.dstMac == self.devAddr.mac || mac.isBroadcast() || mac.isMulticast()) else {
NSLog("[SDLContext] didReadData 1")
return
}
guard let decyptedData = try? self.aesCipher.decypt(aesKey: self.aesKey, data: Data(data.data)) else {
NSLog("[SDLContext] didReadData 2")
return
}
do {
let layerPacket = try LayerPacket(layerData: decyptedData)
await self.flowTracer.inc(num: decyptedData.count, type: .inbound)
// arp
switch layerPacket.type {
case .arp:
// arp
if let arpPacket = ARPPacket(data: layerPacket.data) {
if arpPacket.targetIP == self.devAddr.netAddr {
switch arpPacket.opcode {
case .request:
NSLog("[SDLContext] get arp request packet")
let response = ARPPacket.arpResponse(for: arpPacket, mac: self.devAddr.mac, ip: self.devAddr.netAddr)
await self.routeLayerPacket(dstMac: arpPacket.senderMAC, type: .arp, data: response.marshal())
case .response:
NSLog("[SDLContext] get arp response packet")
await self.arpServer.append(ip: arpPacket.senderIP, mac: arpPacket.senderMAC)
}
} else {
NSLog("[SDLContext] get invalid arp packet, target_ip: \(arpPacket)")
}
} else {
NSLog("[SDLContext] get invalid arp packet")
}
case .ipv4:
NSLog("[SDLContext] get ipv4 packet")
guard let ipPacket = IPPacket(layerPacket.data), ipPacket.header.destination == self.devAddr.netAddr else {
return
}
let packet = NEPacket(data: ipPacket.data, protocolFamily: 2)
self.provider.packetFlow.writePacketObjects([packet])
default:
NSLog("[SDLContext] get invalid packet")
}
} catch let err {
NSLog("[SDLContext] didReadData err: \(err)")
}
}
}
//
func flowReportTask() {
Task {
//
self.flowTracerCancel = Timer.publish(every: 60.0, on: .main, in: .common).autoconnect()
.sink { _ in
Task {
let (forwardNum, p2pNum, inboundNum) = await self.flowTracer.reset()
self.superClient?.flowReport(forwardNum: forwardNum, p2pNum: p2pNum, inboundNum: inboundNum)
}
}
}
}
//
private func didNetworkConfigChanged(devAddr: SDLDevAddr, dnsServers: [String]? = nil) async {
let netAddress = SDLNetAddress(ip: devAddr.netAddr, maskLen: UInt8(devAddr.netBitLen))
let routes = [Route(dstAddress: netAddress.networkAddress, subnetMask: netAddress.maskAddress)]
// Add code here to start the process of connecting the tunnel.
let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "8.8.8.8")
networkSettings.mtu = 1460
// DNS
if let dnsServers {
networkSettings.dnsSettings = NEDNSSettings(servers: dnsServers)
} else {
networkSettings.dnsSettings = NEDNSSettings(servers: ["8.8.8.8", "114.114.114.114"])
}
let ipv4Settings = NEIPv4Settings(addresses: [netAddress.ipAddress], subnetMasks: [netAddress.maskAddress])
//
//NEIPv4Route.default()
ipv4Settings.includedRoutes = routes.map { route in
NEIPv4Route(destinationAddress: route.dstAddress, subnetMask: route.subnetMask)
}
networkSettings.ipv4Settings = ipv4Settings
//
do {
try await self.provider.setTunnelNetworkSettings(networkSettings)
await self.holerManager.cleanup()
self.startReader()
} catch let err {
SDLLogger.log("[SDLContext] setTunnelNetworkSettings get error: \(err)", level: .error)
exit(-1)
}
}
// , 线packetFlow
private func startReader() {
//
self.readTask?.cancel()
//
self.readTask = Task(priority: .high) {
repeat {
if Task.isCancelled {
break
}
let (packets, numbers) = await self.provider.packetFlow.readPackets()
for (data, number) in zip(packets, numbers) where number == 2 {
if let packet = IPPacket(data) {
Task.detached {
let dstIp = packet.header.destination
// , ip
if dstIp == self.devAddr.netAddr {
let nePacket = NEPacket(data: packet.data, protocolFamily: 2)
self.provider.packetFlow.writePacketObjects([nePacket])
return
}
// arpmac
if let dstMac = await self.arpServer.query(ip: dstIp) {
await self.routeLayerPacket(dstMac: dstMac, type: .ipv4, data: packet.data)
}
else {
// arp
let broadcastMac = Data([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])
let arpReqeust: ARPPacket = ARPPacket.arpRequest(senderIP: self.devAddr.netAddr, senderMAC: self.devAddr.mac, targetIP: dstIp)
await self.routeLayerPacket(dstMac: broadcastMac, type: .arp, data: arpReqeust.marshal())
NSLog("[SDLContext] dstIp: \(dstIp) arp query not found")
}
}
}
}
} while true
}
}
private func routeLayerPacket(dstMac: Data, type: LayerPacket.PacketType, data: Data) async {
// 2
let layerPacket = LayerPacket(dstMac: dstMac, srcMac: self.devAddr.mac, type: type, data: data)
guard let encodedPacket = try? self.aesCipher.encrypt(aesKey: self.aesKey, data: layerPacket.marshal()) else {
return
}
// session
if let session = await self.sessionManager.getSession(toAddress: dstMac) {
NSLog("[SDLContext] send packet by session: \(session)")
self.udpHole?.sendPacket(context: self, session: session, data: encodedPacket)
await self.flowTracer.inc(num: data.count, type: .p2p)
} else {
// super_node
self.udpHole?.forwardPacket(context: self, dst_mac: dstMac, data: encodedPacket)
//
await self.flowTracer.inc(num: data.count, type: .forward)
//
await self.holerManager.addHoler(dstMac: dstMac) {
self.holerTask(dstMac: dstMac)
}
}
}
func holerTask(dstMac: Data) -> Task<(), Never> {
return Task {
guard let message = try? await self.superClient?.queryInfo(context: self, dst_mac: dstMac) else {
return
}
switch message.packet {
case .empty:
SDLLogger.log("[SDLContext] hole query_info get empty: \(message)", level: .debug)
case .peerInfo(let peerInfo):
if let remoteAddress = peerInfo.v4Info.socketAddress() {
SDLLogger.log("[SDLContext] hole sock address: \(remoteAddress)", level: .warning)
// register
self.udpHole?.sendRegister(context: self, remoteAddress: remoteAddress, dst_mac: dstMac)
} else {
SDLLogger.log("[SDLContext] hole sock address is invalid: \(peerInfo.v4Info)", level: .warning)
}
default:
SDLLogger.log("[SDLContext] hole query_info is packet: \(message)", level: .warning)
}
}
}
deinit {
self.stunCancel?.cancel()
self.udpHole = nil
self.superClient = nil
}
}
//--MARK: UUID
extension SDLContext {
static func getUUID() -> String {
let userDefaults = UserDefaults.standard
if let uuid = userDefaults.value(forKey: "gClientId") as? String {
return uuid
} else {
let uuid = UUID().uuidString.replacingOccurrences(of: "-", with: "").lowercased()
userDefaults.setValue(uuid, forKey: "gClientId")
return uuid
}
}
// mac
static func getMacAddress() -> Data {
let key = "gMacAddress2"
let userDefaults = UserDefaults.standard
if let mac = userDefaults.value(forKey: key) as? Data {
return mac
}
else {
let mac = generateMacAddress()
userDefaults.setValue(mac, forKey: key)
return mac
}
}
// mac
private static func generateMacAddress() -> Data {
var macAddress = [UInt8](repeating: 0, count: 6)
for i in 0..<6 {
macAddress[i] = UInt8.random(in: 0...255)
}
return Data(macAddress)
}
// mac
private static func formatMacAddress(mac: Data) -> String {
let bytes = [UInt8](mac)
return bytes.map { String(format: "%02X", $0) }.joined(separator: ":").lowercased()
}
}