punchnet-macos/Tun/Punchnet/Actors/SDLQuicClient.swift
2026-02-20 00:20:11 +08:00

282 lines
9.4 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.

//
// SDLQuicClient.swift
// Tun
//
// Created by on 2026/2/13.
//
import Foundation
import NIOCore
import Network
// 便
enum SDLQUICError: Error {
case connectionFailed(Error)
case connectionCancelled
case timeout
case decodeError(String)
case packetTooLarge
}
final class SDLQUICClient {
private let connection: NWConnection
private let queue = DispatchQueue(label: "com.sdl.QUICClient.queue") // 线
private let (closeStream, closeCont) = AsyncStream.makeStream(of: Void.self)
private let (readyStream, readyCont) = AsyncStream.makeStream(of: Void.self)
enum Event {
case ready
case failed(Error)
case cancelled
}
init(host: String, port: UInt16) {
let options = NWProtocolQUIC.Options(alpn: ["punchnet/1.0"])
// TODO
sec_protocol_options_set_verify_block(
options.securityProtocolOptions,
{ metadata, trust, complete in
//
complete(true) // true =
},
DispatchQueue.global()
)
let params = NWParameters(quic: options)
self.connection = NWConnection(host: .init(host), port: .init(rawValue: port)!, using: params)
}
func start() {
SDLLogger.shared.log("[SDLQUICTransport] call start")
connection.stateUpdateHandler = { state in
SDLLogger.shared.log("[SDLQUICTransport] new state: \(state)")
switch state {
case .ready:
self.readyCont.yield()
self.readyCont.finish()
case .failed(_), .cancelled:
self.closeCont.yield()
self.closeCont.finish()
default:
()
}
}
connection.start(queue: self.queue)
}
func messageStream() async -> AsyncStream<SDLQUICInboundMessage> {
let reader = SDLQUICReader(connection: self.connection)
await reader.start()
return await reader.messageStream
}
func send(type: SDLPacketType, data: Data) {
var len = UInt16(data.count + 1).bigEndian
var packet = Data(Data(bytes: &len, count: 2))
packet.append(type.rawValue)
packet.append(data)
connection.send(content: packet, completion: .contentProcessed { _ in })
}
func waitReady() async throws {
for await _ in readyStream {}
}
func waitClose() async {
for await _ in closeStream {}
}
func stop() {
self.connection.cancel()
}
}
actor SDLQUICReader {
private let allocator = ByteBufferAllocator()
// 64K
private let maxPacketSize: Int
// 2M
private let maxBufferSize: Int
public var messageStream: AsyncStream<SDLQUICInboundMessage>
private let messageCont: AsyncStream<SDLQUICInboundMessage>.Continuation
private var readTask: Task<Void, Never>?
private let connection: NWConnection
init(connection: NWConnection, maxPacketSize: Int = 64 * 1024, maxBufferSize: Int = 2 * 1024 * 1024) {
self.connection = connection
self.maxBufferSize = maxBufferSize
self.maxPacketSize = maxPacketSize
(self.messageStream, self.messageCont) = AsyncStream.makeStream(of: SDLQUICInboundMessage.self)
}
func start() {
self.readTask = Task {
var buffer: ByteBuffer = allocator.buffer(capacity: self.maxBufferSize)
do {
while !Task.isCancelled {
let (isComplete, data) = try await self.readOnce()
if !data.isEmpty {
buffer.writeBytes(data)
let frames = try parseFrames(buffer: &buffer)
for frame in frames {
if let message = SDLQUICCodec.decode(frame: frame) {
self.messageCont.yield(message)
}
}
}
if isComplete {
break
}
}
self.messageCont.finish()
} catch {
self.messageCont.finish()
}
}
}
//
private func parseFrames(buffer: inout ByteBuffer) throws -> [ByteBuffer] {
guard buffer.readableBytes >= 2 else {
return []
}
var frames: [ByteBuffer] = []
while true {
guard let len = buffer.getInteger(at: buffer.readerIndex, endianness: .big, as: UInt16.self) else {
break
}
if len > self.maxPacketSize {
throw SDLQUICError.packetTooLarge
}
guard buffer.readableBytes >= len + 2 else {
break
}
buffer.moveReaderIndex(forwardBy: 2)
if let buf = buffer.readSlice(length: Int(len)) {
frames.append(buf)
}
}
if buffer.readerIndex > maxBufferSize / 10 * 6 {
buffer.discardReadBytes()
}
return frames
}
//
private func readOnce() async throws -> (Bool, Data) {
return try await withCheckedThrowingContinuation { cont in
connection.receive(minimumIncompleteLength: 1, maximumLength: maxPacketSize) { data, _, isComplete, error in
if let error {
cont.resume(throwing: error)
return
}
if let data, !data.isEmpty {
SDLLogger.shared.log("[SDLQUICTransport] read bytes: \(data.count)")
cont.resume(returning: (isComplete, data))
} else {
cont.resume(returning: (isComplete, Data()))
}
}
}
}
deinit {
self.readTask?.cancel()
self.messageCont.finish()
}
}
struct SDLQUICCodec {
// --MARK:
public static func decode(frame: ByteBuffer) -> SDLQUICInboundMessage? {
var buffer = frame
guard let type = buffer.readInteger(as: UInt8.self),
let packetType = SDLPacketType(rawValue: type) else {
return nil
}
switch packetType {
case .welcome:
guard let bytes = buffer.readBytes(length: buffer.readableBytes),
let welcome = try? SDLWelcome(serializedBytes: bytes) else {
return nil
}
return .welcome(welcome)
case .registerSuperAck:
guard let bytes = buffer.readBytes(length: buffer.readableBytes),
let registerSuperAck = try? SDLRegisterSuperAck(serializedBytes: bytes) else {
return nil
}
return .registerSuperAck(registerSuperAck)
case .registerSuperNak:
guard let bytes = buffer.readBytes(length: buffer.readableBytes),
let registerSuperNak = try? SDLRegisterSuperNak(serializedBytes: bytes) else {
return nil
}
return .registerSuperNak(registerSuperNak)
case .peerInfo:
guard let bytes = buffer.readBytes(length: buffer.readableBytes),
let peerInfo = try? SDLPeerInfo(serializedBytes: bytes) else {
return nil
}
return .peerInfo(peerInfo)
case .policyResponse:
guard let bytes = buffer.readBytes(length: buffer.readableBytes),
let policyResponse = try? SDLPolicyResponse(serializedBytes: bytes) else {
return nil
}
return .policyReponse(policyResponse)
case .event:
guard let eventVal = buffer.readInteger(as: UInt8.self),
let event = SDLEventType(rawValue: eventVal),
let bytes = buffer.readBytes(length: buffer.readableBytes) else {
SDLLogger.shared.log("[SDLUDPHole] decode error 15")
return nil
}
switch event {
case .natChanged:
guard let natChangedEvent = try? SDLNatChangedEvent(serializedBytes: bytes) else {
SDLLogger.shared.log("[SDLUDPHole] decode error 16")
return nil
}
return .event(.natChanged(natChangedEvent))
case .sendRegister:
guard let sendRegisterEvent = try? SDLSendRegisterEvent(serializedBytes: bytes) else {
SDLLogger.shared.log("[SDLUDPHole] decode error 17")
return nil
}
return .event(.sendRegister(sendRegisterEvent))
case .networkShutdown:
guard let networkShutdownEvent = try? SDLNetworkShutdownEvent(serializedBytes: bytes) else {
SDLLogger.shared.log("[SDLUDPHole] decode error 18")
return nil
}
return .event(.networkShutdown(networkShutdownEvent))
}
default:
SDLLogger.shared.log("SDLUDPHole decode miss type: \(type)")
return nil
}
}
}