punchnet-macos/Tun/UDPHole/SDLUDPHoleV6.swift
2026-05-21 14:39:41 +08:00

134 lines
4.2 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.

//
// SDLUDPHoleV6.swift
// Tun
//
// Created by on 2026/4/15.
//
import Foundation
import NIOCore
import NIOPosix
import SwiftProtobuf
// sn-server
final class SDLUDPHoleV6: ChannelInboundHandler {
typealias InboundIn = AddressedEnvelope<ByteBuffer>
//
enum HoleEvent {
case ready
case closed
case errorCaught
}
private var isStopped: Bool = false
private let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
private var channel: Channel?
public let messageStream: AsyncStream<(SocketAddress, SDLHoleMessage)>
private let messageContinuation: AsyncStream<(SocketAddress, SDLHoleMessage)>.Continuation
//
public let eventStream: AsyncStream<HoleEvent>
private let eventContinuation: AsyncStream<HoleEvent>.Continuation
//
init() throws {
let (stream, continuation) = AsyncStream.makeStream(of: (SocketAddress, SDLHoleMessage).self, bufferingPolicy: .bufferingNewest(2048))
self.messageStream = stream
self.messageContinuation = continuation
let eventPair = AsyncStream.makeStream(of: HoleEvent.self)
self.eventStream = eventPair.stream
self.eventContinuation = eventPair.continuation
}
func start() throws -> SocketAddress? {
let bootstrap = DatagramBootstrap(group: group)
.channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.channelInitializer { channel in
channel.pipeline.addHandler(self)
}
// IPv6IPv6
let channel = try bootstrap.bind(host: "::", port: 0).wait()
self.channel = channel
return channel.localAddress
}
// --MARK: ChannelInboundHandler delegate
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
let envelope = unwrapInboundIn(data)
var buffer = envelope.data
let remoteAddress = envelope.remoteAddress
if let rawBytes = buffer.getBytes(at: buffer.readerIndex, length: buffer.readableBytes) {
SDLLogger.log("[SDLUDPHoleV6] get raw bytes: \(rawBytes.count), from: \(remoteAddress)", for: .debug)
}
do {
if let message = try SDLHoleMessage.decode(buffer: &buffer) {
self.messageContinuation.yield((remoteAddress, message))
} else {
SDLLogger.log("[SDLUDPHoleV6] decode message, get null", for: .debug)
}
} catch let err {
SDLLogger.log("[SDLUDPHoleV6] decode message, get error: \(err)", for: .debug)
}
}
func channelInactive(context: ChannelHandlerContext) {
SDLLogger.log("[SDLUDPHoleV6] channelInactive", for: .debug)
self.eventContinuation.yield(.closed)
}
func errorCaught(context: ChannelHandlerContext, error: any Error) {
SDLLogger.log("[SDLUDPHoleV6] channel error: \(error)", for: .debug)
context.close(promise: nil)
self.eventContinuation.yield(.errorCaught)
}
// MARK:
func send(type: SDLPacketType, data: Data, remoteAddress: SocketAddress) {
guard let channel = self.channel else {
return
}
var buffer = channel.allocator.buffer(capacity: data.count + 1)
buffer.writeBytes([type.rawValue])
buffer.writeBytes(data)
let envelope = AddressedEnvelope<ByteBuffer>(remoteAddress: remoteAddress, data: buffer)
_ = channel.eventLoop.submit {
channel.writeAndFlush(envelope, promise: nil)
}
}
func stop() {
guard !self.isStopped else {
return
}
self.isStopped = true
SDLLogger.log("[SDLUDPHoleV6] stop", for: .debug)
self.messageContinuation.finish()
self.eventContinuation.finish()
let channel = self.channel
self.channel = nil
try? channel?.close().wait()
try? self.group.syncShutdownGracefully()
}
deinit {
SDLLogger.log("[SDLUDPHoleV6] deinit", for: .debug)
}
}