fix Context

This commit is contained in:
anlicheng 2025-07-14 23:24:12 +08:00
parent e19617961c
commit 7602831ecf
9 changed files with 388 additions and 299 deletions

View File

@ -0,0 +1,25 @@
//
// AESCipher.swift
// sdlan
//
// Created by on 2025/7/14.
//
import Foundation
struct AESCipher {
let aesKey: Data
let ivData: Data
init(aesKey: Data) {
self.aesKey = aesKey
self.ivData = Data(aesKey.prefix(16))
}
func decypt(data: Data) throws -> Data {
return try CC.crypt(.decrypt, blockMode: .cbc, algorithm: .aes, padding: .pkcs7Padding, data: data, key: aesKey, iv: ivData)
}
func encrypt(data: Data) throws -> Data {
return try CC.crypt(.encrypt, blockMode: .cbc, algorithm: .aes, padding: .pkcs7Padding, data: data, key: aesKey, iv: ivData)
}
}

View File

@ -0,0 +1,31 @@
//
// ArpServer.swift
// sdlan
//
// Created by on 2025/7/14.
//
import Foundation
actor ArpServer {
private var known_macs: [UInt32:Data] = [:]
init(known_macs: [UInt32:Data]) {
self.known_macs = known_macs
}
func query(ip: UInt32) -> Data? {
return self.known_macs[ip]
}
func append(ip: UInt32, mac: Data) {
self.known_macs[ip] = mac
}
func remove(ip: UInt32) {
self.known_macs.removeValue(forKey: ip)
}
func clear() {
self.known_macs = [:]
}
}

View File

@ -0,0 +1,31 @@
//
// HolerManager.swift
// sdlan
//
// Created by on 2025/7/14.
//
import Foundation
actor HolerManager {
private var holers: [Data:Task<(), Never>] = [:]
func addHoler(dstMac: Data, creator: @escaping () -> Task<(), Never>) {
if let task = self.holers[dstMac] {
if task.isCancelled {
self.holers[dstMac] = creator()
}
} else {
self.holers[dstMac] = creator()
}
}
func cleanup() {
for holer in holers.values {
holer.cancel()
}
self.holers.removeAll()
}
}

View File

@ -0,0 +1,58 @@
//
// NoticeMessage.swift
// sdlan
//
// Created by on 2025/7/14.
//
//
// NoticeMessage.swift
// sdlan
//
// Created by on 2024/6/3.
//
import Foundation
struct NoticeMessage {
//
enum NoticeType: UInt8 {
case upgrade = 1
case alert = 2
}
struct UpgradeMessage: Codable {
let prompt: String
let address: String
var binaryData: Data {
let json = try! JSONEncoder().encode(self)
var data = Data()
data.append(contentsOf: [NoticeType.upgrade.rawValue])
data.append(json)
return data
}
}
struct AlertMessage: Codable {
let alert: String
var binaryData: Data {
let json = try! JSONEncoder().encode(self)
var data = Data()
data.append(contentsOf: [NoticeType.alert.rawValue])
data.append(json)
return data
}
}
enum InboundMessage {
case none
case upgradeMessage(UpgradeMessage)
case alertMessage(AlertMessage)
}
}

View File

@ -0,0 +1,42 @@
//
// RSACipher.swift
// sdlan
//
// Created by on 2025/7/14.
//
import Foundation
struct RSACipher {
let pubKey: String
let privateKeyDER: Data
init(keySize: Int) throws {
let (privateKey, publicKey) = try Self.loadKeys(keySize: keySize)
let privKeyStr = SwKeyConvert.PrivateKey.derToPKCS1PEM(privateKey)
self.pubKey = SwKeyConvert.PublicKey.derToPKCS8PEM(publicKey)
self.privateKeyDER = try SwKeyConvert.PrivateKey.pemToPKCS1DER(privKeyStr)
}
public func decode(data: Data) throws -> Data {
let tag = Data()
let (decryptedData, _) = try CC.RSA.decrypt(data, derKey: self.privateKeyDER, tag: tag, padding: .pkcs1, digest: .none)
return decryptedData
}
private static func loadKeys(keySize: Int) throws -> (Data, Data) {
if let privateKey = UserDefaults.standard.data(forKey: "privateKey"),
let publicKey = UserDefaults.standard.data(forKey: "publicKey") {
return (privateKey, publicKey)
} else {
let (privateKey, publicKey) = try CC.RSA.generateKeyPair(keySize)
UserDefaults.standard.setValue(privateKey, forKey: "privateKey")
UserDefaults.standard.setValue(publicKey, forKey: "publicKey")
return (privateKey, publicKey)
}
}
}

View File

@ -0,0 +1,57 @@
//
// SDLConfiguration.swift
// sdlan
//
// Created by on 2025/7/14.
//
import Foundation
import NIOCore
//
final class SDLConfiguration {
struct StunServer {
let host: String
let ports: [Int]
}
//
let version: UInt8
//
let installedChannel: String
let superHost: String
let superPort: Int
let stunServers: [StunServer]
lazy var stunSocketAddress: SocketAddress = {
let stunServer = stunServers[0]
return try! SocketAddress.makeAddressResolvingHost(stunServer.host, port: stunServer.ports[0])
}()
//
lazy var stunProbeSocketAddressArray: [[SocketAddress]] = {
return stunServers.map { stunServer in
[
try! SocketAddress.makeAddressResolvingHost(stunServer.host, port: stunServer.ports[0]),
try! SocketAddress.makeAddressResolvingHost(stunServer.host, port: stunServer.ports[1])
]
}
}()
let clientId: String
let token: String
init(version: UInt8, installedChannel: String, superHost: String, superPort: Int, stunServers: [StunServer], clientId: String, token: String) {
self.version = version
self.installedChannel = installedChannel
self.superHost = superHost
self.superPort = superPort
self.stunServers = stunServers
self.clientId = clientId
self.token = token
}
}

View File

@ -27,56 +27,7 @@ class SDLContext {
}
}
//
final class Configuration {
struct StunServer {
let host: String
let ports: [Int]
}
//
let version: UInt8
//
let installedChannel: String
let superHost: String
let superPort: Int
let stunServers: [StunServer]
lazy var stunSocketAddress: SocketAddress = {
let stunServer = stunServers[0]
return try! SocketAddress.makeAddressResolvingHost(stunServer.host, port: stunServer.ports[0])
}()
//
lazy var stunProbeSocketAddressArray: [[SocketAddress]] = {
return stunServers.map { stunServer in
[
try! SocketAddress.makeAddressResolvingHost(stunServer.host, port: stunServer.ports[0]),
try! SocketAddress.makeAddressResolvingHost(stunServer.host, port: stunServer.ports[1])
]
}
}()
let clientId: String
let token: String
init(version: UInt8, installedChannel: String, superHost: String, superPort: Int, stunServers: [StunServer], clientId: String, token: String) {
self.version = version
self.installedChannel = installedChannel
self.superHost = superHost
self.superPort = superPort
self.stunServers = stunServers
self.clientId = clientId
self.token = token
}
}
let config: Configuration
let config: SDLConfiguration
// tun
var devAddr: SDLDevAddr
@ -124,8 +75,7 @@ class SDLContext {
private var flowTracer = SDLFlowTracerActor()
private var flowTracerCancel: AnyCancellable?
init(provider: NEPacketTunnelProvider, config: Configuration) throws {
init(provider: NEPacketTunnelProvider, config: SDLConfiguration) throws {
self.config = config
self.rsaCipher = try RSACipher(keySize: 1024)
@ -194,7 +144,6 @@ class SDLContext {
if upgradeType == .force {
let forceUpgrade = NoticeMessage.UpgradeMessage(prompt: registerSuperAck.upgradePrompt, address: registerSuperAck.upgradeAddress)
self.noticeClient.send(data: forceUpgrade.binaryData)
exit(-1)
}
@ -229,8 +178,8 @@ class SDLContext {
case .closed:
SDLLogger.log("[SDLContext] super client closed", level: .debug)
await self.arpServer.clear()
DispatchQueue.global().asyncAfter(deadline: .now() + 5) {
Task {
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
Task {@MainActor in
try await self.startSuperClient()
}
}
@ -291,7 +240,7 @@ class SDLContext {
switch event {
case .ready:
//
self.natType = await self.getNatType()
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()
@ -300,7 +249,7 @@ class SDLContext {
}
case .closed:
DispatchQueue.global().asyncAfter(deadline: .now() + 5) {
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
Task {
try await self.startUDPHole()
}
@ -515,184 +464,6 @@ class SDLContext {
}
}
deinit {
self.stunCancel?.cancel()
self.udpHole = nil
self.superClient = nil
}
}
//--MARK: RSA
extension SDLContext {
struct RSACipher {
let pubKey: String
let privateKeyDER: Data
init(keySize: Int) throws {
let (privateKey, publicKey) = try Self.loadKeys(keySize: keySize)
let privKeyStr = SwKeyConvert.PrivateKey.derToPKCS1PEM(privateKey)
self.pubKey = SwKeyConvert.PublicKey.derToPKCS8PEM(publicKey)
self.privateKeyDER = try SwKeyConvert.PrivateKey.pemToPKCS1DER(privKeyStr)
}
public func decode(data: Data) throws -> Data {
let tag = Data()
let (decryptedData, _) = try CC.RSA.decrypt(data, derKey: self.privateKeyDER, tag: tag, padding: .pkcs1, digest: .none)
return decryptedData
}
private static func loadKeys(keySize: Int) throws -> (Data, Data) {
if let privateKey = UserDefaults.standard.data(forKey: "privateKey"),
let publicKey = UserDefaults.standard.data(forKey: "publicKey") {
return (privateKey, publicKey)
} else {
let (privateKey, publicKey) = try CC.RSA.generateKeyPair(keySize)
UserDefaults.standard.setValue(privateKey, forKey: "privateKey")
UserDefaults.standard.setValue(publicKey, forKey: "publicKey")
return (privateKey, publicKey)
}
}
}
}
// --MARK: AES, AES256
extension SDLContext {
struct AESCipher {
let aesKey: Data
let ivData: Data
init(aesKey: Data) {
self.aesKey = aesKey
self.ivData = Data(aesKey.prefix(16))
}
func decypt(data: Data) throws -> Data {
return try CC.crypt(.decrypt, blockMode: .cbc, algorithm: .aes, padding: .pkcs7Padding, data: data, key: aesKey, iv: ivData)
}
func encrypt(data: Data) throws -> Data {
return try CC.crypt(.encrypt, blockMode: .cbc, algorithm: .aes, padding: .pkcs7Padding, data: data, key: aesKey, iv: ivData)
}
}
}
// --MARK: session, session10s使使
extension SDLContext {
struct Session {
// ip,
let dstMac: Data
// nat
let natAddress: SocketAddress
// 使
var lastTimestamp: Int32
init(dstMac: Data, natAddress: SocketAddress) {
self.dstMac = dstMac
self.natAddress = natAddress
self.lastTimestamp = Int32(Date().timeIntervalSince1970)
}
mutating func updateLastTimestamp(_ lastTimestamp: Int32) {
self.lastTimestamp = lastTimestamp
}
}
actor SessionManager {
private var sessions: [Data:Session] = [:]
// session
private let ttl: Int32 = 10
func getSession(toAddress: Data) -> Session? {
let timestamp = Int32(Date().timeIntervalSince1970)
if let session = self.sessions[toAddress] {
if session.lastTimestamp >= timestamp + ttl {
self.sessions[toAddress]?.updateLastTimestamp(timestamp)
return session
} else {
self.sessions.removeValue(forKey: toAddress)
}
}
return nil
}
func addSession(session: Session) {
self.sessions[session.dstMac] = session
}
func removeSession(dstMac: Data) {
self.sessions.removeValue(forKey: dstMac)
}
}
}
// --MARK: known_ips
extension SDLContext {
actor ArpServer {
private var known_macs: [UInt32:Data] = [:]
init(known_macs: [UInt32:Data]) {
self.known_macs = known_macs
}
func query(ip: UInt32) -> Data? {
return self.known_macs[ip]
}
func append(ip: UInt32, mac: Data) {
self.known_macs[ip] = mac
}
func remove(ip: UInt32) {
self.known_macs.removeValue(forKey: ip)
}
func clear() {
self.known_macs = [:]
}
}
}
// --MARK:
extension SDLContext {
actor HolerManager {
private var holers: [Data:Task<(), Never>] = [:]
func addHoler(dstMac: Data, creator: @escaping () -> Task<(), Never>) {
if let task = self.holers[dstMac] {
if task.isCancelled {
self.holers[dstMac] = creator()
}
} else {
self.holers[dstMac] = creator()
}
}
func cleanup() {
for holer in holers.values {
holer.cancel()
}
self.holers.removeAll()
}
}
func holerTask(dstMac: Data) -> Task<(), Never> {
return Task {
guard let message = try? await self.superClient?.queryInfo(context: self, dst_mac: dstMac) else {
@ -716,72 +487,14 @@ extension SDLContext {
}
}
}
//--MARK:
extension SDLContext {
// nat
enum NatType: UInt8, Encodable {
case blocked = 0
case noNat = 1
case fullCone = 2
case portRestricted = 3
case coneRestricted = 4
case symmetric = 5
}
private func getNatAddress(remoteAddress: SocketAddress, attr: SDLProbeAttr) async -> SocketAddress? {
let stunProbeReply = await self.udpHole?.stunProbe(remoteAddress: remoteAddress, attr: attr, timeout: 5)
return stunProbeReply?.socketAddress()
}
// nat
func getNatType() async -> NatType {
let addressArray = config.stunProbeSocketAddressArray
// step1: ip1:port1 <---- ip1:port1
guard let natAddress1 = await getNatAddress(remoteAddress: addressArray[0][0], attr: .none) else {
return .blocked
}
// nat
if natAddress1 == self.udpHole?.localAddress {
return .noNat
}
// step2: ip2:port2 <---- ip2:port2
guard let natAddress2 = await getNatAddress(remoteAddress: addressArray[1][1], attr: .none) else {
return .blocked
}
// natAddress2 IPIPNAT;
// ip{dstIp, dstPort, srcIp, srcPort}, ip
NSLog("nat_address1: \(natAddress1), nat_address2: \(natAddress2)")
if let ipAddress1 = natAddress1.ipAddress, let ipAddress2 = natAddress2.ipAddress, ipAddress1 != ipAddress2 {
return .symmetric
}
// step3: ip1:port1 <---- ip2:port2 (ipport)
// IPNAT
if let natAddress3 = await getNatAddress(remoteAddress: addressArray[0][0], attr: .peer) {
NSLog("nat_address1: \(natAddress1), nat_address2: \(natAddress2), nat_address3: \(natAddress3)")
return .fullCone
}
// step3: ip1:port1 <---- ip1:port2 (port)
// IPNAT
if let natAddress4 = await getNatAddress(remoteAddress: addressArray[0][0], attr: .port) {
NSLog("nat_address1: \(natAddress1), nat_address2: \(natAddress2), nat_address4: \(natAddress4)")
return .coneRestricted
} else {
return .portRestricted
}
deinit {
self.stunCancel?.cancel()
self.udpHole = nil
self.superClient = nil
}
}
//--MARK: UUID
extension SDLContext {

View File

@ -0,0 +1,75 @@
//
// File.swift
// sdlan
//
// Created by on 2025/7/14.
//
import Foundation
import NIOCore
//
struct SDLNatProber {
// nat
enum NatType: UInt8, Encodable {
case blocked = 0
case noNat = 1
case fullCone = 2
case portRestricted = 3
case coneRestricted = 4
case symmetric = 5
}
// nat
static func getNatType(udpHole: SDLUDPHole?, config: SDLConfiguration) async -> NatType {
guard let udpHole else {
return .blocked
}
let addressArray = config.stunProbeSocketAddressArray
// step1: ip1:port1 <---- ip1:port1
guard let natAddress1 = await getNatAddress(udpHole, remoteAddress: addressArray[0][0], attr: .none) else {
return .blocked
}
// nat
if natAddress1 == udpHole.localAddress {
return .noNat
}
// step2: ip2:port2 <---- ip2:port2
guard let natAddress2 = await getNatAddress(udpHole, remoteAddress: addressArray[1][1], attr: .none) else {
return .blocked
}
// natAddress2 IPIPNAT;
// ip{dstIp, dstPort, srcIp, srcPort}, ip
NSLog("nat_address1: \(natAddress1), nat_address2: \(natAddress2)")
if let ipAddress1 = natAddress1.ipAddress, let ipAddress2 = natAddress2.ipAddress, ipAddress1 != ipAddress2 {
return .symmetric
}
// step3: ip1:port1 <---- ip2:port2 (ipport)
// IPNAT
if let natAddress3 = await getNatAddress(udpHole, remoteAddress: addressArray[0][0], attr: .peer) {
NSLog("nat_address1: \(natAddress1), nat_address2: \(natAddress2), nat_address3: \(natAddress3)")
return .fullCone
}
// step3: ip1:port1 <---- ip1:port2 (port)
// IPNAT
if let natAddress4 = await getNatAddress(udpHole, remoteAddress: addressArray[0][0], attr: .port) {
NSLog("nat_address1: \(natAddress1), nat_address2: \(natAddress2), nat_address4: \(natAddress4)")
return .coneRestricted
} else {
return .portRestricted
}
}
private static func getNatAddress(_ udpHole: SDLUDPHole, remoteAddress: SocketAddress, attr: SDLProbeAttr) async -> SocketAddress? {
let stunProbeReply = await udpHole.stunProbe(remoteAddress: remoteAddress, attr: attr, timeout: 5)
return stunProbeReply?.socketAddress()
}
}

View File

@ -0,0 +1,57 @@
//
// Session.swift
// sdlan
//
// Created by on 2025/7/14.
//
import Foundation
import NIOCore
struct Session {
// ip,
let dstMac: Data
// nat
let natAddress: SocketAddress
// 使
var lastTimestamp: Int32
init(dstMac: Data, natAddress: SocketAddress) {
self.dstMac = dstMac
self.natAddress = natAddress
self.lastTimestamp = Int32(Date().timeIntervalSince1970)
}
mutating func updateLastTimestamp(_ lastTimestamp: Int32) {
self.lastTimestamp = lastTimestamp
}
}
actor SessionManager {
private var sessions: [Data:Session] = [:]
// session
private let ttl: Int32 = 10
func getSession(toAddress: Data) -> Session? {
let timestamp = Int32(Date().timeIntervalSince1970)
if let session = self.sessions[toAddress] {
if session.lastTimestamp >= timestamp + ttl {
self.sessions[toAddress]?.updateLastTimestamp(timestamp)
return session
} else {
self.sessions.removeValue(forKey: toAddress)
}
}
return nil
}
func addSession(session: Session) {
self.sessions[session.dstMac] = session
}
func removeSession(dstMac: Data) {
self.sessions.removeValue(forKey: dstMac)
}
}