Compare commits

..

15 Commits

Author SHA1 Message Date
copilot-swe-agent[bot] c1a47db0e6 Revert to original buffer processing order (input then rawInput)
Per RPRX's clarification, the original order was correct. Reverted the buffer processing order back to: input → rawInput. The issue is not an ordering problem as I misunderstood.

Awaiting clarification on the actual fix needed.

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-01-11 09:57:56 +00:00
copilot-swe-agent[bot] d8dfcbdd73 Fix Vision by processing rawInput before input when switching to direct mode
Per RPRX's feedback, the original code logic was correct, but the processing order matters. When switching to direct copy mode, rawInput (encrypted TLS records from the outer layer) should be processed and merged into the buffer before input (decrypted application data).

This ensures proper ordering of data when transitioning from Vision-padded mode to direct copy mode, preventing SSL errors especially with testpre connections.

Fixes #4878

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-01-11 09:49:31 +00:00
copilot-swe-agent[bot] 6ae3bcbb3f Remove trailing whitespace per code review
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-01-11 08:56:50 +00:00
copilot-swe-agent[bot] 8ca8e07fe0 Fix Vision timing issue: ensure complete TLS records before switching to direct copy
The issue was in the timing/conditions for switching to direct copy mode. When Vision receives CommandPaddingDirect from the server, it would immediately switch to direct mode even if the current buffer contains incomplete TLS records. This caused SSL protocol errors, especially with testpre where connections may have fragmented data.

The fix: Before actually performing the switch to direct copy mode, check if the current buffer contains complete TLS records using IsCompleteRecord(). If records are incomplete, return the buffer and delay the switch until the next read cycle when complete records are available.

This ensures Vision only switches to direct mode at safe TLS record boundaries, preventing data corruption.

Fixes #4878

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-01-11 08:54:18 +00:00
copilot-swe-agent[bot] 85976a73af Add nil checks for input/rawInput pointers in Vision direct copy mode
When switching to direct copy mode, add defensive nil checks before reading from input and rawInput pointers. This prevents potential issues if these pointers are not set for certain connection types.

While the TLS library handles these buffers internally, adding nil safety ensures robust operation across different connection configurations.

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-01-11 01:32:15 +00:00
copilot-swe-agent[bot] 8b65579719 Remove redundant nil check per code review
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-01-11 01:16:57 +00:00
copilot-swe-agent[bot] aca75d5b80 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>
2026-01-11 01:14:02 +00:00
copilot-swe-agent[bot] 7a71924ffe Improve comment clarity per code review
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-01-11 01:07:55 +00:00
copilot-swe-agent[bot] 7d70aefa5f Fix Vision SSL errors by not reading encrypted rawInput buffer
The issue occurs when switching to direct copy mode - Vision was incorrectly reading from rawInput buffer which contains ENCRYPTED outer TLS/Reality records and merging them with decrypted application data. This caused SSL protocol errors, especially with testpre where pre-established connections may have TLS session tickets or other post-handshake messages in rawInput.

The fix: Only read from input buffer (decrypted application data), skip rawInput (encrypted TLS records).

Fixes #4878

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-01-11 01:05:15 +00:00
copilot-swe-agent[bot] ca12c4d909 Revert incorrect fix - restore original RemainingServerHello logic
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-01-10 22:43:48 +00:00
copilot-swe-agent[bot] c6cc048e05 Final verification: code review and security scan passed
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-01-10 22:27:50 +00:00
copilot-swe-agent[bot] 933088c1d4 Remove accidentally committed binary
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-01-10 22:25:36 +00:00
copilot-swe-agent[bot] 0a7aeb7cda Fix XTLS Vision incomplete TLS record handling - correct RemainingServerHello calculation
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-01-10 22:25:06 +00:00
copilot-swe-agent[bot] 249cb9facd Initial plan 2026-01-10 22:19:55 +00:00
风扇滑翔翼 07a0dafa41 DNS: Check err for UDP dns.PackMessage(req.msg) (#5512)
Fixes https://github.com/XTLS/Xray-core/issues/5506
2026-01-09 14:22:07 +00:00
3 changed files with 27 additions and 126 deletions
+9 -2
View File
@@ -160,7 +160,7 @@ func (s *ClassicNameServer) getCacheController() *CacheController {
}
// sendQuery implements CachedNameserver.
func (s *ClassicNameServer) sendQuery(ctx context.Context, _ chan<- error, fqdn string, option dns_feature.IPOption) {
func (s *ClassicNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {
errors.LogInfo(ctx, s.Name(), " querying DNS for: ", fqdn)
reqs := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
@@ -171,7 +171,14 @@ func (s *ClassicNameServer) sendQuery(ctx context.Context, _ chan<- error, fqdn
ctx: ctx,
}
s.addPendingRequest(udpReq)
b, _ := dns.PackMessage(req.msg)
b, err := dns.PackMessage(req.msg)
if err != nil {
errors.LogErrorInner(ctx, err, "failed to pack dns query")
if noResponseErrCh != nil {
noResponseErrCh <- err
}
return
}
copyDest := net.UDPDestination(s.address.Address, s.address.Port)
b.UDP = &copyDest
s.udpServer.Dispatch(toDnsContext(ctx, s.address.String()), *s.address, b)
+16 -116
View File
@@ -124,26 +124,6 @@ type netBindClient struct {
ctx context.Context
dialer internet.Dialer
reserved []byte
// Track all peer connections for unified reading
connMutex sync.RWMutex
conns map[*netEndpoint]net.Conn
dataChan chan *receivedData
closeChan chan struct{}
closeOnce sync.Once
}
const (
// Buffer size for dataChan - allows some buffering of received packets
// while dispatcher matches them with read requests
dataChannelBufferSize = 100
)
type receivedData struct {
data []byte
n int
endpoint *netEndpoint
err error
}
func (bind *netBindClient) connectTo(endpoint *netEndpoint) error {
@@ -153,114 +133,34 @@ func (bind *netBindClient) connectTo(endpoint *netEndpoint) error {
}
endpoint.conn = c
// Initialize channels on first connection
bind.connMutex.Lock()
if bind.conns == nil {
bind.conns = make(map[*netEndpoint]net.Conn)
bind.dataChan = make(chan *receivedData, dataChannelBufferSize)
bind.closeChan = make(chan struct{})
// Start unified reader dispatcher
go bind.unifiedReader()
}
bind.conns[endpoint] = c
bind.connMutex.Unlock()
// Start a reader goroutine for this specific connection
go func(conn net.Conn, endpoint *netEndpoint) {
const maxPacketSize = 1500
go func(readQueue <-chan *netReadInfo, endpoint *netEndpoint) {
for {
select {
case <-bind.closeChan:
return
default:
}
buf := make([]byte, maxPacketSize)
n, err := conn.Read(buf)
// Send only the valid data portion to dispatcher
dataToSend := buf
if n > 0 && n < len(buf) {
dataToSend = buf[:n]
}
// Send received data to dispatcher
select {
case bind.dataChan <- &receivedData{
data: dataToSend,
n: n,
endpoint: endpoint,
err: err,
}:
case <-bind.closeChan:
v, ok := <-readQueue
if !ok {
return
}
i, err := c.Read(v.buff)
if i > 3 {
v.buff[1] = 0
v.buff[2] = 0
v.buff[3] = 0
}
v.bytes = i
v.endpoint = endpoint
v.err = err
v.waiter.Done()
if err != nil {
bind.connMutex.Lock()
delete(bind.conns, endpoint)
endpoint.conn = nil
bind.connMutex.Unlock()
return
}
}
}(c, endpoint)
}(bind.readQueue, endpoint)
return nil
}
// unifiedReader dispatches received data to waiting read requests
func (bind *netBindClient) unifiedReader() {
for {
select {
case data := <-bind.dataChan:
// Bounds check to prevent panic
if data.n > len(data.data) {
data.n = len(data.data)
}
// Wait for a read request with timeout to prevent blocking forever
select {
case v := <-bind.readQueue:
// Copy data to request buffer
n := copy(v.buff, data.data[:data.n])
// Clear reserved bytes if needed
if n > 3 {
v.buff[1] = 0
v.buff[2] = 0
v.buff[3] = 0
}
v.bytes = n
v.endpoint = data.endpoint
v.err = data.err
v.waiter.Done()
case <-bind.closeChan:
return
}
case <-bind.closeChan:
return
}
}
}
// Close implements conn.Bind.Close for netBindClient
func (bind *netBindClient) Close() error {
// Use sync.Once to prevent double-close panic
bind.closeOnce.Do(func() {
bind.connMutex.Lock()
if bind.closeChan != nil {
close(bind.closeChan)
}
bind.connMutex.Unlock()
})
// Call parent Close
return bind.netBind.Close()
}
func (bind *netBindClient) Send(buff [][]byte, endpoint conn.Endpoint) error {
var err error
+2 -8
View File
@@ -114,12 +114,6 @@ func (h *Handler) processWireGuard(ctx context.Context, dialer internet.Dialer)
}
// bind := conn.NewStdNetBind() // TODO: conn.Bind wrapper for dialer
// Set workers to number of peers if not explicitly configured
// This allows concurrent packet reception from multiple peers
workers := int(h.conf.NumWorkers)
if workers <= 0 && len(h.conf.Peers) > 0 {
workers = len(h.conf.Peers)
}
h.bind = &netBindClient{
netBind: netBind{
dns: h.dns,
@@ -127,9 +121,9 @@ func (h *Handler) processWireGuard(ctx context.Context, dialer internet.Dialer)
IPv4Enable: h.hasIPv4,
IPv6Enable: h.hasIPv6,
},
workers: workers,
workers: int(h.conf.NumWorkers),
},
ctx: core.ToBackgroundDetachedContext(ctx),
ctx: ctx,
dialer: dialer,
reserved: h.conf.Reserved,
}