This commit is contained in:
anlicheng 2026-02-25 00:25:58 +08:00
parent 2d6d640a44
commit 703d4e191f
11 changed files with 186 additions and 185 deletions

View File

@ -10,8 +10,8 @@ import Darwin
actor ArpServerActor {
private var known_macs: [UInt32:Data] = [:]
init(known_macs: [UInt32:Data]) {
self.known_macs = known_macs
init() {
}
func query(ip: UInt32) -> Data? {

View File

@ -47,7 +47,7 @@ actor SDLContextActor {
private var dnsWorker: Task<Void, Never>?
private var quicClient: SDLQUICClient?
private var quicWorker: Task<Void, Never>?
private var quicWorkers: [Task<Void, Never>]?
nonisolated private let puncherActor: SDLPuncherActor
//
@ -78,6 +78,9 @@ actor SDLContextActor {
private let snapshotPublisher: SnapshotPublisher<IdentitySnapshot>
private let policyRequesterActor: PolicyRequesterActor
//
private var registerTask: Task<Void, Never>?
public init(provider: NEPacketTunnelProvider, config: SDLConfiguration, rsaCipher: RSACipher, aesCipher: AESCipher) {
self.provider = provider
self.config = config
@ -85,7 +88,7 @@ actor SDLContextActor {
self.aesCipher = aesCipher
self.sessionManager = SessionManager()
self.arpServer = ArpServerActor(known_macs: [:])
self.arpServer = ArpServerActor()
self.puncherActor = SDLPuncherActor(querySocketAddress: config.stunSocketAddress)
self.proberActor = SDLNATProberActor(addressArray: config.stunProbeSocketAddressArray)
@ -131,23 +134,28 @@ actor SDLContextActor {
}
private func startQUICClient() async throws -> SDLQUICClient {
self.quicWorker?.cancel()
self.quicWorkers?.forEach {$0.cancel()}
self.quicClient?.stop()
// monitor
let quicClient = SDLQUICClient(host: "118.178.229.213", port: 1365)
let quicClient = SDLQUICClient(host: self.config.serverIp, port: 443)
quicClient.start()
// quic
try await quicClient.waitReady()
// quic
try await Task.sleep(for: .seconds(0.2))
SDLLogger.shared.log("[SDLContext] start quic client ready")
self.quicWorker = Task.detached {
let messageTask = Task.detached {
for await message in quicClient.messageStream {
switch message {
case .welcome(let welcome):
SDLLogger.shared.log("[SDLContext] quic welcome: \(welcome)")
//
await self.startRegisterLoop()
case .pong:
SDLLogger.shared.log("[SDLContext] quic pong")
case .registerSuperAck(let registerSuperAck):
await self.handleRegisterSuperAck(registerSuperAck: registerSuperAck)
case .registerSuperNak(let registerSuperNak):
@ -162,17 +170,31 @@ actor SDLContextActor {
}
}
}
self.quicClient = quicClient
//
self.doRegisterSuper()
//
let pingTask = Task.detached {
let timerStream = SDLAsyncTimerStream()
timerStream.start(interval: .seconds(5))
for await _ in timerStream.stream {
if Task.isCancelled {
break
}
quicClient.send(type: .ping, data: Data())
}
SDLLogger.shared.log("[SDLContext] udp pingTask cancel")
}
self.quicClient = quicClient
self.quicWorkers = [messageTask, pingTask]
return quicClient
}
private func startNoticeClient() throws -> SDLNoticeClient {
// noticeClient
let noticeClient = try SDLNoticeClient(noticePort: self.config.noticePort, logger: SDLLogger.shared)
let noticeClient = try SDLNoticeClient(noticePort: self.config.noticePort)
noticeClient.start()
SDLLogger.shared.log("[SDLContext] noticeClient started")
@ -196,7 +218,7 @@ actor SDLContextActor {
switch event {
case .changed:
// nat
//self.natType = await self.getNatType()
await self.probeNatType()
SDLLogger.shared.log("didNetworkPathChanged, nat type is:", level: .info)
case .unreachable:
SDLLogger.shared.log("didNetworkPathUnreachable", level: .warning)
@ -210,7 +232,7 @@ actor SDLContextActor {
self.dnsWorker = nil
// dns
let dnsSocketAddress = try SocketAddress.makeAddressResolvingHost(self.config.remoteDnsServer, port: 15353)
let dnsSocketAddress = try SocketAddress.makeAddressResolvingHost(self.config.serverIp, port: 15353)
let dnsClient = try await SDLDNSClient(dnsServerAddress: dnsSocketAddress, logger: SDLLogger.shared)
try dnsClient.start()
SDLLogger.shared.log("[SDLContext] dnsClient started")
@ -246,11 +268,10 @@ actor SDLContextActor {
//
let pingTask = Task.detached {
let (stream, cont) = AsyncStream.makeStream(of: Void.self)
let timerStream = SDLAsyncTimerStream()
timerStream.start(cont)
timerStream.start(interval: .seconds(5))
for await _ in stream {
for await _ in timerStream.stream {
if Task.isCancelled {
break
}
@ -276,6 +297,8 @@ actor SDLContextActor {
await self.handleRegisterAck(remoteAddress: remoteAddress, registerAck: registerAck)
case .data(let data):
try? await self.handleData(data: data)
case .stunReply(let stunReply):
SDLLogger.shared.log("[SDLContext] get a stunReply: \(stunReply)")
}
}
@ -286,11 +309,7 @@ actor SDLContextActor {
self.udpHoleWorkers = [pingTask, messageTask]
// nat
Task {
let natType = await self.proberActor.probeNatType(using: udpHole)
self.setNatType(natType: natType)
SDLLogger.shared.log("[SDLContext] nat_type is: \(natType)")
}
self.probeNatType()
return udpHole
}
@ -303,6 +322,9 @@ actor SDLContextActor {
self.udpHoleWorkers?.forEach { $0.cancel() }
self.udpHoleWorkers = nil
self.quicWorkers?.forEach { $0.cancel() }
self.quicWorkers = nil
self.dnsWorker?.cancel()
self.dnsWorker = nil
@ -311,12 +333,34 @@ actor SDLContextActor {
self.readTask?.cancel()
self.readTask = nil
self.registerTask?.cancel()
self.registerTask = nil
}
private func setNatType(natType: SDLNATProberActor.NatType) {
self.natType = natType
}
//
private func startRegisterLoop() {
guard self.registerTask == nil else {
return
}
self.registerTask = Task {
while !Task.isCancelled {
self.doRegisterSuper()
try? await Task.sleep(for: .seconds(5))
if self.state == .registered {
break
}
SDLLogger.shared.log("[SDLContext] register super failed, retry")
}
self.registerTask = nil
}
}
private func sendStunRequest() {
guard let sessionToken = self.sessionToken else {
return
@ -423,20 +467,6 @@ actor SDLContextActor {
if let registerSuperData = try? registerSuper.serializedData() {
SDLLogger.shared.log("[SDLContext] will send register super")
self.quicClient?.send(type: .registerSuper, data: registerSuperData)
// 5
Task {
try await Task.sleep(for: .seconds(5))
self.checkRegisterState()
}
}
}
//
private func checkRegisterState() {
if self.state == .unregistered {
SDLLogger.shared.log("[SDLContext] register super failed, retry")
self.doRegisterSuper()
}
}
@ -535,13 +565,11 @@ actor SDLContextActor {
}
} else {
//
if let sessionToken = self.sessionToken {
var policyRequest = SDLPolicyRequest()
policyRequest.srcIdentityID = data.identityID
policyRequest.dstIdentityID = self.config.identityId
await self.policyRequesterActor.submitPolicyRequest(using: self.udpHole, request: &policyRequest)
}
await self.policyRequesterActor.submitPolicyRequest(using: self.quicClient, request: &policyRequest)
}
default:
SDLLogger.shared.log("[SDLContext] get invalid packet", level: .debug)
@ -588,11 +616,10 @@ actor SDLContextActor {
private func dealPacket(packet: IPPacket) async {
let networkAddr = self.config.networkAddress
// TODO
if SDLDNSClient.Helper.isDnsRequestPacket(ipPacket: packet) {
let destIp = packet.header.destination_ip
SDLLogger.shared.log("[DNSQuery] destIp: \(destIp), int: \(packet.header.destination.asIpAddress())", level: .debug)
//self.dnsClient?.forward(ipPacket: packet)
self.dnsClient?.forward(ipPacket: packet)
return
}
@ -654,7 +681,7 @@ actor SDLContextActor {
//
Task.detached {
await self.puncherActor.submitRegisterRequest(using: udpHole, request: .init(srcMac: networkAddr.mac, dstMac: dstMac, networkId: networkAddr.networkId))
await self.puncherActor.submitRegisterRequest(quicClient: self.quicClient, request: .init(srcMac: networkAddr.mac, dstMac: dstMac, networkId: networkAddr.networkId))
}
}
}
@ -696,6 +723,17 @@ actor SDLContextActor {
}
}
//
private func probeNatType() {
Task {
guard let udpHole = self.udpHole else {
return
}
// nat
self.natType = await self.proberActor.probeNatType(using: udpHole)
SDLLogger.shared.log("[SDLContext] nat_type is: \(natType)")
}
}
private func spawnLoop(_ body: @escaping () async throws -> Void) -> Task<Void, Never> {
return Task.detached {
@ -714,13 +752,11 @@ actor SDLContextActor {
// todo
private func triggerPolicy() async {
//
if let sessionToken = self.sessionToken {
var policyRequest = SDLPolicyRequest()
policyRequest.srcIdentityID = 1234
policyRequest.dstIdentityID = self.config.identityId
await self.policyRequesterActor.submitPolicyRequest(using: self.udpHole, request: &policyRequest)
}
await self.policyRequesterActor.submitPolicyRequest(using: self.quicClient, request: &policyRequest)
}
deinit {

View File

@ -30,9 +30,9 @@ actor SDLPuncherActor {
self.querySocketAddress = querySocketAddress
}
func submitRegisterRequest(using udpHole: SDLUDPHole?, request: RegisterRequest) {
func submitRegisterRequest(quicClient: SDLQUICClient?, request: RegisterRequest) {
let dstMac = request.dstMac
guard let udpHole, !coolingDown.contains(dstMac) else {
guard let quicClient, !coolingDown.contains(dstMac) else {
return
}
@ -44,7 +44,7 @@ actor SDLPuncherActor {
if self.pktId == 0 {
self.pktId = 1
}
//self.tryHole(using: udpHole, pktId: pktId, request: request)
self.tryHole(using: quicClient, pktId: pktId, request: request)
Task {
//
@ -54,22 +54,23 @@ actor SDLPuncherActor {
}
}
// func handlePeerInfo(using udpHole: SDLUDPHole, peerInfo: SDLPeerInfo) async {
// if let request = pendingRequests.removeValue(forKey: peerInfo.pktID) {
// if let remoteAddress = try? await peerInfo.v4Info.socketAddress() {
// SDLLogger.shared.log("[SDLContext] hole sock address: \(remoteAddress)", level: .debug)
// // register
// var register = SDLRegister()
// register.networkID = request.networkId
// register.srcMac = request.srcMac
// register.dstMac = request.dstMac
//
// udpHole.send(type: .register, data: try! register.serializedData(), remoteAddress: remoteAddress)
// } else {
// SDLLogger.shared.log("[SDLContext] hole sock address is invalid: \(peerInfo.v4Info)", level: .warning)
// }
// }
// }
func handlePeerInfo(using udpHole: SDLUDPHole, peerInfo: SDLPeerInfo) async {
guard let request = pendingRequests.removeValue(forKey: peerInfo.pktID),
let remoteAddress = try? await peerInfo.v4Info.socketAddress() else {
return
}
SDLLogger.shared.log("[SDLContext] hole sock address: \(remoteAddress)", level: .debug)
// register
var register = SDLRegister()
register.networkID = request.networkId
register.srcMac = request.srcMac
register.dstMac = request.dstMac
if let registerData = try? register.serializedData() {
udpHole.send(type: .register, data: registerData, remoteAddress: remoteAddress)
}
}
private func endCooldown(for key: Data) {
self.coolingDown.remove(key)
@ -79,14 +80,14 @@ actor SDLPuncherActor {
self.pendingRequests.removeValue(forKey: pktId)
}
// private func tryHole(using udpHole: SDLUDPHole, pktId: UInt32, request: RegisterRequest) {
// var queryInfo = SDLQueryInfo()
// queryInfo.pktID = pktId
// queryInfo.dstMac = request.dstMac
// self.pendingRequests[pktId] = request
//
// if let queryData = try? queryInfo.serializedData() {
// udpHole.send(type: .queryInfo, data: queryData, remoteAddress: self.querySocketAddress)
// }
// }
private func tryHole(using quicClient: SDLQUICClient, pktId: UInt32, request: RegisterRequest) {
var queryInfo = SDLQueryInfo()
queryInfo.pktID = pktId
queryInfo.dstMac = request.dstMac
self.pendingRequests[pktId] = request
if let queryData = try? queryInfo.serializedData() {
quicClient.send(type: .queryInfo, data: queryData)
}
}
}

View File

@ -102,6 +102,7 @@ final class SDLQUICClient {
self.messageCont.finish()
}
}
}
func send(type: SDLPacketType, data: Data) {
@ -240,6 +241,8 @@ final class SDLQUICClient {
}
return .event(.networkShutdown(networkShutdownEvent))
}
case .pong:
return .pong
default:
SDLLogger.shared.log("SDLUDPHole decode miss type: \(type)")

View File

@ -24,9 +24,9 @@ actor PolicyRequesterActor {
}
//
func submitPolicyRequest(using udpHole: SDLUDPHole?, request: inout SDLPolicyRequest) {
func submitPolicyRequest(using quicClient: SDLQUICClient?, request: inout SDLPolicyRequest) {
let identityId = request.srcIdentityID
guard let udpHole, !coolingDown.contains(identityId) else {
guard let quicClient, !coolingDown.contains(identityId) else {
return
}
@ -39,7 +39,7 @@ actor PolicyRequesterActor {
self.versions[identityId] = version + 1
//
if let queryData = try? request.serializedData() {
udpHole.send(type: .policyRequest, data: queryData, remoteAddress: self.querySocketAddress)
quicClient.send(type: .policyRequest, data: queryData)
}
Task {

View File

@ -9,15 +9,18 @@ import Foundation
class SDLAsyncTimerStream {
let timer: DispatchSourceTimer
let stream: AsyncStream<Void>
private let cont: AsyncStream<Void>.Continuation
init() {
self.timer = DispatchSource.makeTimerSource(queue: .global())
(stream, cont) = AsyncStream.makeStream(of: Void.self)
}
func start(_ cont: AsyncStream<Void>.Continuation) {
timer.schedule(deadline: .now(), repeating: .seconds(5))
func start(interval: DispatchTimeInterval) {
timer.schedule(deadline: .now(), repeating: interval)
timer.setEventHandler {
cont.yield()
self.cont.yield()
}
timer.resume()
}

View File

@ -9,16 +9,6 @@ import NIOCore
//
public class SDLConfiguration {
public struct StunServer {
public let host: String
public let ports: [Int]
public init(host: String, ports: [Int]) {
self.host = host
self.ports = ports
}
}
public struct NetworkAddress {
public let networkId: UInt32
public let ip: UInt32
@ -49,30 +39,24 @@ public class SDLConfiguration {
}
//
let version: UInt8
let version: Int
//
let installedChannel: String
let serverIp: String
let stunServers: [String]
let superHost: String
let superPort: Int
let stunServers: [StunServer]
let remoteDnsServer: String
let noticePort: Int
lazy var stunSocketAddress: SocketAddress = {
let stunServer = stunServers[0]
return try! SocketAddress.makeAddressResolvingHost(stunServer.host, port: stunServer.ports[0])
return try! SocketAddress.makeAddressResolvingHost(stunServer, port: 1365)
}()
//
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])
try! SocketAddress.makeAddressResolvingHost(stunServer, port: 1365),
try! SocketAddress.makeAddressResolvingHost(stunServer, port: 1366)
]
}
}()
@ -83,30 +67,24 @@ public class SDLConfiguration {
let accessToken: String
let identityId: UInt32
public init(version: UInt8,
installedChannel: String,
superHost: String,
superPort: Int,
stunServers: [StunServer],
public init(version: Int,
serverIp: String,
stunServers: [String],
clientId: String,
networkAddress: NetworkAddress,
hostname: String,
noticePort: Int,
accessToken: String,
identityId: UInt32,
remoteDnsServer: String) {
identityId: UInt32) {
self.version = version
self.installedChannel = installedChannel
self.superHost = superHost
self.superPort = superPort
self.serverIp = serverIp
self.stunServers = stunServers
self.clientId = clientId
self.networkAddress = networkAddress
self.noticePort = noticePort
self.accessToken = accessToken
self.identityId = identityId
self.remoteDnsServer = remoteDnsServer
self.hostname = hostname
}
}
@ -115,60 +93,36 @@ public class SDLConfiguration {
extension SDLConfiguration {
static func parse(options: [String: NSObject]) -> SDLConfiguration? {
guard let installed_channel = options["installed_channel"] as? String,
let superIp = options["super_ip"] as? String,
let superPort = options["super_port"] as? Int,
let stunServersStr = options["stun_servers"] as? String,
guard let version = options["version"] as? Int,
let serverIp = options["server_ip"] as? String,
let stunAssistIp = options["stun_assist_ip"] as? String,
let noticePort = options["notice_port"] as? Int,
let accessToken = options["access_token"] as? String,
let identityId = options["identity_id"] as? UInt32,
let clientId = options["client_id"] as? String,
let remoteDnsServer = options["remote_dns_server"] as? String,
let hostname = options["hostname"] as? String,
let networkAddressDict = options["network_address"] as? [String: NSObject] else {
return nil
}
guard let stunServers = parseStunServers(stunServersStr: stunServersStr), stunServers.count >= 2 else {
NSLog("stunServers配置错误")
return nil
}
guard let networkAddress = parseNetworkAddress(networkAddressDict) else {
return nil
}
return SDLConfiguration(version: 1,
installedChannel: installed_channel,
superHost: superIp,
superPort: superPort,
let stunServers: [String] = [
serverIp,
stunAssistIp
]
return SDLConfiguration(version: version,
serverIp: serverIp,
stunServers: stunServers,
clientId: clientId,
networkAddress: networkAddress,
hostname: hostname,
noticePort: noticePort,
accessToken: accessToken,
identityId: identityId,
remoteDnsServer: remoteDnsServer)
}
private static func parseStunServers(stunServersStr: String) -> [SDLConfiguration.StunServer]? {
let stunServers = stunServersStr.split(separator: ";").compactMap { server -> SDLConfiguration.StunServer? in
let parts = server.split(separator: ":", maxSplits: 2)
guard parts.count == 2 else {
return nil
}
let ports = parts[1].split(separator: ",", maxSplits: 2)
guard ports.count == 2, let port1 = Int(String(ports[0])), let port2 = Int(String(ports[1])) else {
return nil
}
return .init(host: String(parts[0]), ports: [port1, port2])
}
return stunServers.count >= 2 ? stunServers : nil
identityId: identityId)
}
private static func parseNetworkAddress(_ config: [String: NSObject]) -> SDLConfiguration.NetworkAddress? {

View File

@ -21,6 +21,10 @@ enum SDLPacketType: UInt8 {
case queryInfo = 0x06
case peerInfo = 0x07
//
case ping = 0x08
case pong = 0x09
//
case event = 0x10
@ -28,6 +32,7 @@ enum SDLPacketType: UInt8 {
case registerAck = 0x21
case stunRequest = 0x30
case stunReply = 0x31
case stunProbe = 0x32
case stunProbeReply = 0x33
@ -99,12 +104,15 @@ enum SDLHoleMessage {
case register(SDLRegister)
case registerAck(SDLRegisterAck)
case stunProbeReply(SDLStunProbeReply)
case stunReply(SDLStunReply)
}
enum SDLQUICInboundMessage {
//
case welcome(SDLWelcome)
case pong
//
case registerSuperAck(SDLRegisterSuperAck)
case registerSuperNak(SDLRegisterSuperNak)

View File

@ -25,12 +25,10 @@ final class SDLNoticeClient {
private var task: Task<Void, Never>?
private var channel: Channel
private let logger: SDLLogger
private let noticePort: Int
//
init(noticePort: Int, logger: SDLLogger) throws {
self.logger = logger
init(noticePort: Int) throws {
self.noticePort = noticePort
let bootstrap = DatagramBootstrap(group: self.group)
@ -40,7 +38,7 @@ final class SDLNoticeClient {
}
self.channel = try bootstrap.bind(host: "0.0.0.0", port: 0).wait()
self.logger.log("[SDLNoticeClient] started", level: .debug)
SDLLogger.shared.log("[SDLNoticeClient] started", level: .debug)
}
func start() {

View File

@ -84,6 +84,11 @@ final class SDLUDPHole: ChannelInboundHandler {
var buffer = envelope.data
let remoteAddress = envelope.remoteAddress
if let rawBytes = buffer.getBytes(at: buffer.readerIndex, length: buffer.readableBytes) {
SDLLogger.shared.log("[SDLUDPHole] get raw bytes: \(rawBytes), from: \(remoteAddress)")
}
do {
if let message = try decode(buffer: &buffer) {
self.messageContinuation.yield((remoteAddress, message))
@ -126,11 +131,9 @@ final class SDLUDPHole: ChannelInboundHandler {
// --MARK:
private func decode(buffer: inout ByteBuffer) throws -> SDLHoleMessage? {
let rawType = buffer.getInteger(at: 0, endianness: .big, as: UInt8.self)
guard let type = buffer.readInteger(as: UInt8.self),
let packetType = SDLPacketType(rawValue: type) else {
SDLLogger.shared.log("[SDLUDPHole] decode error 11: \(rawType)")
SDLLogger.shared.log("[SDLUDPHole] decode error 11")
return nil
}
@ -159,6 +162,12 @@ final class SDLUDPHole: ChannelInboundHandler {
return nil
}
return .stunProbeReply(stunProbeReply)
case .stunReply:
guard let bytes = buffer.readBytes(length: buffer.readableBytes),
let stunReply = try? SDLStunReply(serializedBytes: bytes) else {
return nil
}
return .stunReply(stunReply)
default:
SDLLogger.shared.log("SDLUDPHole decode miss type: \(type)")

View File

@ -9,25 +9,17 @@ import Foundation
struct SystemConfig {
//
static let version = 1
static let version: Int = 1
static let version_name = "1.1"
//
static let installedChannel = "MacAppStore"
static let serverHost = "punchnet.s5s8.com"
// super
//static let superHost = "118.178.229.213"
static let superHost = "punchnet.s5s8.com"
static let superPort = 18083
// stun
static let stunServers = "118.178.229.213:1365,1366;118.178.229.213:1365,1366"
//static let stunServers = "127.0.0.1:1265,1266;127.0.0.1:1265,1266"
// stunip
static let stunAssistHost = "punchnet.s5s8.com"
static func getOptions(networkId: UInt32, networkDomain: String, ip: String, maskLen: UInt8, accessToken: String, identityId: UInt32, hostname: String, noticePort: Int) -> [String: NSObject]? {
guard let superIp = DNSResolver.resolveAddrInfos(superHost).first else {
guard let serverIp = DNSResolver.resolveAddrInfos(serverHost).first else {
return nil
}
@ -35,15 +27,12 @@ struct SystemConfig {
let mac = getMacAddress()
let options = [
"version:": version as NSObject,
"installed_channel": installedChannel as NSObject,
"version": version as NSObject,
"client_id": clientId as NSObject,
"access_token": accessToken as NSObject,
"identity_id": identityId as NSObject,
"super_ip": superIp as NSObject,
"super_port": superPort as NSObject,
"stun_servers": stunServers as NSObject,
"remote_dns_server": superIp as NSObject,
"server_ip": "118.178.229.213" as NSObject,
"stun_assist_ip": "118.178.229.213" as NSObject,
"hostname": hostname as NSObject,
"notice_port": noticePort as NSObject,
"network_address": [