fix Context
This commit is contained in:
parent
e19617961c
commit
7602831ecf
25
Sources/sdlan/AESCipher.swift
Normal file
25
Sources/sdlan/AESCipher.swift
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
31
Sources/sdlan/ArpServer.swift
Normal file
31
Sources/sdlan/ArpServer.swift
Normal 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 = [:]
|
||||||
|
}
|
||||||
|
}
|
||||||
31
Sources/sdlan/HolerManager.swift
Normal file
31
Sources/sdlan/HolerManager.swift
Normal 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
58
Sources/sdlan/NoticeMessage.swift
Normal file
58
Sources/sdlan/NoticeMessage.swift
Normal 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
42
Sources/sdlan/RSACipher.swift
Normal file
42
Sources/sdlan/RSACipher.swift
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
57
Sources/sdlan/SDLConfiguration.swift
Normal file
57
Sources/sdlan/SDLConfiguration.swift
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -27,56 +27,7 @@ class SDLContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 配置项目
|
let config: SDLConfiguration
|
||||||
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
|
|
||||||
|
|
||||||
// tun网络地址信息
|
// tun网络地址信息
|
||||||
var devAddr: SDLDevAddr
|
var devAddr: SDLDevAddr
|
||||||
@ -124,8 +75,7 @@ class SDLContext {
|
|||||||
private var flowTracer = SDLFlowTracerActor()
|
private var flowTracer = SDLFlowTracerActor()
|
||||||
private var flowTracerCancel: AnyCancellable?
|
private var flowTracerCancel: AnyCancellable?
|
||||||
|
|
||||||
init(provider: NEPacketTunnelProvider, config: Configuration) throws {
|
init(provider: NEPacketTunnelProvider, config: SDLConfiguration) throws {
|
||||||
|
|
||||||
self.config = config
|
self.config = config
|
||||||
self.rsaCipher = try RSACipher(keySize: 1024)
|
self.rsaCipher = try RSACipher(keySize: 1024)
|
||||||
|
|
||||||
@ -194,7 +144,6 @@ class SDLContext {
|
|||||||
if upgradeType == .force {
|
if upgradeType == .force {
|
||||||
let forceUpgrade = NoticeMessage.UpgradeMessage(prompt: registerSuperAck.upgradePrompt, address: registerSuperAck.upgradeAddress)
|
let forceUpgrade = NoticeMessage.UpgradeMessage(prompt: registerSuperAck.upgradePrompt, address: registerSuperAck.upgradeAddress)
|
||||||
self.noticeClient.send(data: forceUpgrade.binaryData)
|
self.noticeClient.send(data: forceUpgrade.binaryData)
|
||||||
|
|
||||||
exit(-1)
|
exit(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,8 +178,8 @@ class SDLContext {
|
|||||||
case .closed:
|
case .closed:
|
||||||
SDLLogger.log("[SDLContext] super client closed", level: .debug)
|
SDLLogger.log("[SDLContext] super client closed", level: .debug)
|
||||||
await self.arpServer.clear()
|
await self.arpServer.clear()
|
||||||
DispatchQueue.global().asyncAfter(deadline: .now() + 5) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
|
||||||
Task {
|
Task {@MainActor in
|
||||||
try await self.startSuperClient()
|
try await self.startSuperClient()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -291,7 +240,7 @@ class SDLContext {
|
|||||||
switch event {
|
switch event {
|
||||||
case .ready:
|
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)
|
SDLLogger.log("[SDLContext] nat type is: \(self.natType)", level: .debug)
|
||||||
|
|
||||||
let timer = Timer.publish(every: 5.0, on: .main, in: .common).autoconnect()
|
let timer = Timer.publish(every: 5.0, on: .main, in: .common).autoconnect()
|
||||||
@ -300,7 +249,7 @@ class SDLContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case .closed:
|
case .closed:
|
||||||
DispatchQueue.global().asyncAfter(deadline: .now() + 5) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
|
||||||
Task {
|
Task {
|
||||||
try await self.startUDPHole()
|
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管理, session的有效时间为10s,没次使用后更新最后使用时间
|
|
||||||
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> {
|
func holerTask(dstMac: Data) -> Task<(), Never> {
|
||||||
return Task {
|
return Task {
|
||||||
guard let message = try? await self.superClient?.queryInfo(context: self, dst_mac: dstMac) else {
|
guard let message = try? await self.superClient?.queryInfo(context: self, dst_mac: dstMac) else {
|
||||||
@ -716,72 +487,14 @@ extension SDLContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.stunCancel?.cancel()
|
||||||
|
self.udpHole = nil
|
||||||
|
self.superClient = nil
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//--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 的IP地址与上次回来的IP是不一样的,它就是对称型NAT; 这次的包也一定能发成功并收到
|
|
||||||
// 如果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 (ip地址和port都变的情况)
|
|
||||||
// 如果能收到的,说明是完全锥形 说明是IP地址限制锥型NAT,如果不能收到说明是端口限制锥型。
|
|
||||||
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改变情况)
|
|
||||||
// 如果能收到的说明是IP地址限制锥型NAT,如果不能收到说明是端口限制锥型。
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//--MARK: 获取设备的UUID
|
//--MARK: 获取设备的UUID
|
||||||
|
|
||||||
extension SDLContext {
|
extension SDLContext {
|
||||||
|
|||||||
75
Sources/sdlan/SDLNatProber.swift
Normal file
75
Sources/sdlan/SDLNatProber.swift
Normal 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 的IP地址与上次回来的IP是不一样的,它就是对称型NAT; 这次的包也一定能发成功并收到
|
||||||
|
// 如果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 (ip地址和port都变的情况)
|
||||||
|
// 如果能收到的,说明是完全锥形 说明是IP地址限制锥型NAT,如果不能收到说明是端口限制锥型。
|
||||||
|
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改变情况)
|
||||||
|
// 如果能收到的说明是IP地址限制锥型NAT,如果不能收到说明是端口限制锥型。
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
57
Sources/sdlan/SessionManager.swift
Normal file
57
Sources/sdlan/SessionManager.swift
Normal 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user