Эх сурвалжийг харах

Implement FullCone NAT for TUN inbound with configurable timeout

- Add cone flag to TUN Handler to track FullCone mode
- Use PacketReader for UDP to preserve packet boundaries
- Add NewDispatcherWithTimeout to support custom idle timeouts
- Enable 5-minute timeout through policy configuration
- UDP source addresses preserved through payload.UDP field

Co-authored-by: RPRX <[email protected]>
copilot-swe-agent[bot] 5 сар өмнө
parent
commit
a4c9d9338b

+ 15 - 3
proxy/tun/handler.go

@@ -24,6 +24,7 @@ type Handler struct {
 	stack         Stack
 	stack         Stack
 	policyManager policy.Manager
 	policyManager policy.Manager
 	dispatcher    routing.Dispatcher
 	dispatcher    routing.Dispatcher
+	cone          bool
 }
 }
 
 
 // ConnectionHandler interface with the only method that stack is going to push new connections to
 // ConnectionHandler interface with the only method that stack is going to push new connections to
@@ -46,6 +47,7 @@ func (t *Handler) Init(ctx context.Context, pm policy.Manager, dispatcher routin
 	t.ctx = core.ToBackgroundDetachedContext(ctx)
 	t.ctx = core.ToBackgroundDetachedContext(ctx)
 	t.policyManager = pm
 	t.policyManager = pm
 	t.dispatcher = dispatcher
 	t.dispatcher = dispatcher
+	t.cone = ctx.Value("cone").(bool)
 
 
 	tunName := t.config.Name
 	tunName := t.config.Name
 	tunOptions := TunOptions{
 	tunOptions := TunOptions{
@@ -106,10 +108,20 @@ func (t *Handler) HandleConnection(conn net.Conn, destination net.Destination) {
 	ctx = session.ContextWithInbound(ctx, &inbound)
 	ctx = session.ContextWithInbound(ctx, &inbound)
 	ctx = session.SubContextFromMuxInbound(ctx)
 	ctx = session.SubContextFromMuxInbound(ctx)
 
 
-	link := &transport.Link{
-		Reader: &buf.TimeoutWrapperReader{Reader: buf.NewReader(conn)},
-		Writer: buf.NewWriter(conn),
+	var link *transport.Link
+	if destination.Network == net.Network_UDP {
+		// For UDP, use PacketReader to preserve packet boundaries
+		link = &transport.Link{
+			Reader: buf.NewPacketReader(conn),
+			Writer: buf.NewWriter(conn),
+		}
+	} else {
+		link = &transport.Link{
+			Reader: &buf.TimeoutWrapperReader{Reader: buf.NewReader(conn)},
+			Writer: buf.NewWriter(conn),
+		}
 	}
 	}
+	
 	if err := t.dispatcher.DispatchLink(ctx, destination, link); err != nil {
 	if err := t.dispatcher.DispatchLink(ctx, destination, link); err != nil {
 		errors.LogError(ctx, errors.New("connection closed").Base(err))
 		errors.LogError(ctx, errors.New("connection closed").Base(err))
 		return
 		return

+ 11 - 1
transport/internet/udp/dispatcher.go

@@ -49,12 +49,22 @@ type Dispatcher struct {
 	callback   ResponseCallback
 	callback   ResponseCallback
 	callClose  func() error
 	callClose  func() error
 	closed     bool
 	closed     bool
+	timeout    time.Duration
 }
 }
 
 
 func NewDispatcher(dispatcher routing.Dispatcher, callback ResponseCallback) *Dispatcher {
 func NewDispatcher(dispatcher routing.Dispatcher, callback ResponseCallback) *Dispatcher {
 	return &Dispatcher{
 	return &Dispatcher{
 		dispatcher: dispatcher,
 		dispatcher: dispatcher,
 		callback:   callback,
 		callback:   callback,
+		timeout:    time.Minute,
+	}
+}
+
+func NewDispatcherWithTimeout(dispatcher routing.Dispatcher, callback ResponseCallback, timeout time.Duration) *Dispatcher {
+	return &Dispatcher{
+		dispatcher: dispatcher,
+		callback:   callback,
+		timeout:    timeout,
 	}
 	}
 }
 }
 
 
@@ -99,7 +109,7 @@ func (v *Dispatcher) getInboundRay(ctx context.Context, dest net.Destination) (*
 		cancel: cancel,
 		cancel: cancel,
 	}
 	}
 
 
-	entry.timer = signal.CancelAfterInactivity(ctx, entry.terminate, time.Minute)
+	entry.timer = signal.CancelAfterInactivity(ctx, entry.terminate, v.timeout)
 	v.conn = entry
 	v.conn = entry
 	go handleInput(ctx, entry, dest, v.callback, v.callClose)
 	go handleInput(ctx, entry, dest, v.callback, v.callClose)
 	return entry, nil
 	return entry, nil