This commit is contained in:
anlicheng 2026-04-16 16:16:29 +08:00
parent 720bf3549a
commit 8ebfdd1edf

View File

@ -8,19 +8,81 @@ import Foundation
import Network import Network
actor SDLIPV6AssistClient { actor SDLIPV6AssistClient {
struct Packet: Sendable {
enum IPVersion: UInt8, Sendable {
case ipv4 = 4
case ipv6 = 6
var protocolFamily: Int32 {
switch self {
case .ipv4:
return 2
case .ipv6:
return 30
}
}
}
let packetId: UInt32
let ipPacketData: Data
let ipVersion: IPVersion
var protocolFamily: Int32 {
return self.ipVersion.protocolFamily
}
}
private enum State { private enum State {
case idle case idle
case running case running
case stopped case stopped
} }
private struct PendingPacket: Sendable {
let packetId: UInt32
let ipVersion: Packet.IPVersion
}
private enum PacketParseError: Error {
case packetTooShort
case unmatchedPacketId(UInt32)
case invalidIPVersion(UInt8)
case unsupportedIPVersion(UInt8)
}
private var state: State = .idle private var state: State = .idle
private var connection: NWConnection? private var connection: NWConnection?
private var receiveTask: Task<Void, Never>? private var receiveTask: Task<Void, Never>?
private let assistServerAddress: NWEndpoint private let assistServerAddress: NWEndpoint
private var idGenerator: SDLIdGenerator
private var pendingPackets: [UInt32: PendingPacket] = [:]
init(host: String, port: UInt16 ) { // IP
self.assistServerAddress = .hostPort(host: NWEndpoint.Host(host), port: NWEndpoint.Port(integerLiteral: port)) let packetFlow: AsyncStream<Packet>
private let packetContinuation: AsyncStream<Packet>.Continuation
private var didFinishPacketFlow = false
//
private let closeStream: AsyncStream<Void>
private let closeContinuation: AsyncStream<Void>.Continuation
private var didFinishCloseStream = false
init?(assistServerInfo: SDLV6Info) {
guard assistServerInfo.port <= UInt32(UInt16.max), let host = SDLUtil.ipv6DataToString(assistServerInfo.v6) else {
return nil
}
let (packetStream, packetContinuation) = AsyncStream.makeStream(of: Packet.self, bufferingPolicy: .bufferingNewest(256))
self.packetFlow = packetStream
self.packetContinuation = packetContinuation
let (closeStream, closeContinuation) = AsyncStream.makeStream(of: Void.self, bufferingPolicy: .bufferingNewest(1))
self.closeStream = closeStream
self.closeContinuation = closeContinuation
self.assistServerAddress = .hostPort(host: NWEndpoint.Host(host), port: NWEndpoint.Port(integerLiteral: UInt16(assistServerInfo.port)))
self.idGenerator = SDLIdGenerator(seed: UInt32.random(in: 1..<UInt32.max))
} }
func start() { func start() {
@ -38,6 +100,11 @@ actor SDLIPV6AssistClient {
// 2. pathSelectionOptions // 2. pathSelectionOptions
parameters.multipathServiceType = .handover parameters.multipathServiceType = .handover
// IPv6 assist 退 IPv4
if let ipOptions = parameters.defaultProtocolStack.internetProtocol as? NWProtocolIP.Options {
ipOptions.version = .v6
}
// 2. // 2.
let connection = NWConnection(to: self.assistServerAddress, using: parameters) let connection = NWConnection(to: self.assistServerAddress, using: parameters)
self.connection = connection self.connection = connection
@ -52,6 +119,10 @@ actor SDLIPV6AssistClient {
connection.start(queue: .global()) connection.start(queue: .global())
} }
public func waitClose() async {
for await _ in self.closeStream { }
}
/// ///
private static func makeReceiveStream(for connection: NWConnection) -> AsyncStream<Data> { private static func makeReceiveStream(for connection: NWConnection) -> AsyncStream<Data> {
return AsyncStream(bufferingPolicy: .bufferingNewest(256)) { continuation in return AsyncStream(bufferingPolicy: .bufferingNewest(256)) { continuation in
@ -75,16 +146,32 @@ actor SDLIPV6AssistClient {
} }
/// DNS TUN IP /// DNS TUN IP
func forward(ipPacketData: Data) { /// IP 4 packetId
@discardableResult
func forward(ipPacketData: Data) -> UInt32? {
guard case .running = self.state, let connection = self.connection, connection.state == .ready else { guard case .running = self.state, let connection = self.connection, connection.state == .ready else {
return return nil
} }
connection.send(content: ipPacketData, completion: .contentProcessed { error in let ipVersion: Packet.IPVersion
if let error = error { do {
SDLLogger.log("[SDLIPV6AssistClient] Send error: \(error)", for: .debug) ipVersion = try self.parseIPVersion(packetData: ipPacketData)
} catch {
SDLLogger.log("[SDLIPV6AssistClient] Invalid outbound packet: \(error)", for: .debug)
return nil
}
let packetId = self.idGenerator.nextId()
self.pendingPackets[packetId] = .init(packetId: packetId, ipVersion: ipVersion)
let outboundPacket = Data(components: Data(uint32: packetId), ipPacketData)
connection.send(content: outboundPacket, completion: .contentProcessed { [weak self] error in
Task {
await self?.handleSendCompletion(packetId: packetId, error: error)
} }
}) })
return packetId
} }
func stop() { func stop() {
@ -95,8 +182,11 @@ actor SDLIPV6AssistClient {
self.state = .stopped self.state = .stopped
self.receiveTask?.cancel() self.receiveTask?.cancel()
self.receiveTask = nil self.receiveTask = nil
self.pendingPackets.removeAll()
self.connection?.cancel() self.connection?.cancel()
self.connection = nil self.connection = nil
self.finishPacketFlowIfNeeded()
self.finishCloseStreamIfNeeded()
} }
private func handleConnectionStateUpdate(_ state: NWConnection.State, for connection: NWConnection) { private func handleConnectionStateUpdate(_ state: NWConnection.State, for connection: NWConnection) {
@ -131,6 +221,8 @@ actor SDLIPV6AssistClient {
} }
await self.handleReceivedPacket(data) await self.handleReceivedPacket(data)
} }
await self?.didFinishReceiving(for: connection)
} }
} }
@ -139,7 +231,89 @@ actor SDLIPV6AssistClient {
return return
} }
NSLog("data: \(data)") do {
let packet = try self.parseInboundPacket(data)
self.packetContinuation.yield(packet)
} catch {
SDLLogger.log("[SDLIPV6AssistClient] Receive error: \(error)", for: .debug)
}
}
private func handleSendCompletion(packetId: UInt32, error: Error?) {
guard case .running = self.state else {
return
}
if let error {
self.pendingPackets.removeValue(forKey: packetId)
SDLLogger.log("[SDLIPV6AssistClient] Send error: \(error), packetId: \(packetId)", for: .debug)
}
}
private func didFinishReceiving(for connection: NWConnection) {
guard case .running = self.state else {
return
}
if self.connection === connection, connection.state != .ready {
self.stop()
} else {
self.receiveTask = nil
}
}
private func parseInboundPacket(_ data: Data) throws -> Packet {
guard data.count > 4 else {
throw PacketParseError.packetTooShort
}
let packetId = UInt32(data: Data(data.prefix(4)))
guard let pendingPacket = self.pendingPackets.removeValue(forKey: packetId) else {
throw PacketParseError.unmatchedPacketId(packetId)
}
let ipPacketData = Data(data.dropFirst(4))
let ipVersion = try self.parseIPVersion(packetData: ipPacketData)
if ipVersion != pendingPacket.ipVersion {
SDLLogger.log("[SDLIPV6AssistClient] packet version mismatch, packetId: \(packetId), request: \(pendingPacket.ipVersion.rawValue), response: \(ipVersion.rawValue)", for: .debug)
}
return .init(packetId: pendingPacket.packetId, ipPacketData: ipPacketData, ipVersion: ipVersion)
}
private func parseIPVersion(packetData: Data) throws -> Packet.IPVersion {
guard let firstByte = packetData.first else {
throw PacketParseError.packetTooShort
}
let rawVersion = firstByte >> 4
guard let ipVersion = Packet.IPVersion(rawValue: rawVersion) else {
throw PacketParseError.invalidIPVersion(rawVersion)
}
guard ipVersion == .ipv6 else {
throw PacketParseError.unsupportedIPVersion(rawVersion)
}
return ipVersion
}
private func finishPacketFlowIfNeeded() {
guard !self.didFinishPacketFlow else {
return
}
self.didFinishPacketFlow = true
self.packetContinuation.finish()
}
private func finishCloseStreamIfNeeded() {
guard !self.didFinishCloseStream else {
return
}
self.didFinishCloseStream = true
self.closeContinuation.finish()
} }
deinit { deinit {