- Adaptive iPhone/iPad layout with NavigationSplitView sidebar - Auto-detect SSL-pinned domains, fall back to passthrough - Certificate install via local HTTP server (Safari profile flow) - App Group-backed CA, per-domain leaf cert LRU cache - DB-backed config repository, Darwin notification throttling - Rules engine, breakpoint rules, pinned domain tracking - os.Logger instrumentation across tunnel/proxy/mitm/capture/cert/rules/db/ipc/ui - Fix dyld framework embed, race conditions, thread safety
70 lines
2.0 KiB
Swift
70 lines
2.0 KiB
Swift
import Foundation
|
|
import NIOCore
|
|
|
|
/// Bidirectional TCP forwarder. Pairs two channels so bytes flow in both directions.
|
|
final class GlueHandler: ChannelInboundHandler, RemovableChannelHandler {
|
|
typealias InboundIn = ByteBuffer
|
|
typealias OutboundOut = ByteBuffer
|
|
|
|
var partner: GlueHandler?
|
|
private var context: ChannelHandlerContext?
|
|
private var pendingRead = false
|
|
|
|
func handlerAdded(context: ChannelHandlerContext) {
|
|
self.context = context
|
|
ProxyLogger.glue.debug("GlueHandler added to \(context.channel.localAddress?.description ?? "?")")
|
|
}
|
|
|
|
func handlerRemoved(context: ChannelHandlerContext) {
|
|
ProxyLogger.glue.debug("GlueHandler removed from \(context.channel.localAddress?.description ?? "?")")
|
|
self.context = nil
|
|
self.partner = nil
|
|
}
|
|
|
|
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
|
|
let buf = unwrapInboundIn(data)
|
|
ProxyLogger.glue.debug("GlueHandler read \(buf.readableBytes) bytes, forwarding to partner")
|
|
partner?.write(buf)
|
|
}
|
|
|
|
func channelReadComplete(context: ChannelHandlerContext) {
|
|
partner?.flush()
|
|
}
|
|
|
|
func channelInactive(context: ChannelHandlerContext) {
|
|
ProxyLogger.glue.debug("GlueHandler channelInactive — closing partner")
|
|
partner?.close()
|
|
}
|
|
|
|
func errorCaught(context: ChannelHandlerContext, error: Error) {
|
|
ProxyLogger.glue.error("GlueHandler error: \(error.localizedDescription)")
|
|
context.close(promise: nil)
|
|
}
|
|
|
|
func channelWritabilityChanged(context: ChannelHandlerContext) {
|
|
if context.channel.isWritable {
|
|
partner?.read()
|
|
}
|
|
}
|
|
|
|
private func write(_ buffer: ByteBuffer) {
|
|
context?.write(wrapOutboundOut(buffer), promise: nil)
|
|
}
|
|
|
|
private func flush() {
|
|
context?.flush()
|
|
}
|
|
|
|
private func read() {
|
|
if let context, !pendingRead {
|
|
pendingRead = true
|
|
context.read()
|
|
pendingRead = false
|
|
}
|
|
}
|
|
|
|
private func close() {
|
|
context?.close(promise: nil)
|
|
}
|
|
}
|