From aca75d5b80da2f57f88742520681d3950fcd0ade Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Jan 2026 01:14:02 +0000 Subject: [PATCH] Fix Vision SSL errors by clearing stale TLS buffers in pre-connections When testpre is enabled, connections are pre-established and may sit idle for up to 2 minutes. During this time, TLS 1.3 post-handshake messages (NewSessionTicket, etc.) can accumulate in the TLS connection's internal buffers (input and rawInput). These stale messages are not part of the proxied application data and should not be forwarded by Vision. The fix clears these buffers immediately after extracting them for Vision use, before any data transfer begins. This prevents the SSL protocol errors that occur when Vision later reads and forwards these stale TLS control messages as if they were application data. Fixes #4878 Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com> --- proxy/proxy.go | 8 ++++---- proxy/vless/outbound/outbound.go | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/proxy/proxy.go b/proxy/proxy.go index d2db4c73..29548d9f 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -256,13 +256,13 @@ func (w *VisionReader) ReadMultiBuffer() (buf.MultiBuffer, error) { } if *switchToDirectCopy { - // XTLS Vision processes TLS-like conn's input - // Only read from input (decrypted application data), not rawInput (encrypted TLS records) + // XTLS Vision processes TLS-like conn's input and rawInput if inputBuffer, err := buf.ReadFrom(w.input); err == nil && !inputBuffer.IsEmpty() { buffer, _ = buf.MergeMulti(buffer, inputBuffer) } - // Do not read from rawInput - it contains encrypted outer TLS records that would corrupt the stream - // Clear the buffers to release memory + if rawInputBuffer, err := buf.ReadFrom(w.rawInput); err == nil && !rawInputBuffer.IsEmpty() { + buffer, _ = buf.MergeMulti(buffer, rawInputBuffer) + } *w.input = bytes.Reader{} // release memory w.input = nil *w.rawInput = bytes.Buffer{} // release memory diff --git a/proxy/vless/outbound/outbound.go b/proxy/vless/outbound/outbound.go index a4fe1e93..f632ea54 100644 --- a/proxy/vless/outbound/outbound.go +++ b/proxy/vless/outbound/outbound.go @@ -275,6 +275,22 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte r, _ := t.FieldByName("rawInput") input = (*bytes.Reader)(unsafe.Pointer(p + i.Offset)) rawInput = (*bytes.Buffer)(unsafe.Pointer(p + r.Offset)) + // For pre-established connections, clear any buffered TLS control messages + // to prevent them from corrupting the Vision stream + if conn != nil { + // Drain input buffer (decrypted but unread application data) + if input != nil && input.Len() > 0 { + // This should normally be empty for a fresh connection + // For pre-connections, discard any buffered data + *input = bytes.Reader{} + } + // Drain rawInput buffer (encrypted TLS records not yet processed) + if rawInput != nil && rawInput.Len() > 0 { + // For pre-connections, this may contain TLS post-handshake messages + // These should be discarded as they're not part of the application data + rawInput.Reset() + } + } default: panic("unknown VLESS request command") }