1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2026-05-04 03:29:08 -04:00

Correctly implement QUIC sniffer when handling multiple initial packets (#3310)

* Correctly implement QUIC sniffer when handling multiple initial packets

* Only parse token for initial packet

Signed-off-by: Vigilans <vigilans@foxmail.com>

* Update test case for QUIC sniffer

* Fix testcases
* Third packet in `Handshake[2]; packet 1-3` mistakenly copied UDP header into payload, making the payload length 1278 instead of 1250

* Introduce `protocol.ErrProtoNeedMoreData` to allow sniffer to fetch more packets until complete

---------

Signed-off-by: Vigilans <vigilans@foxmail.com>
Co-authored-by: Shelikhoo <xiaokangwang@outlook.com>
Co-authored-by: dyhkwong <50692134+dyhkwong@users.noreply.github.com>
This commit is contained in:
Vigilans
2025-02-16 22:11:27 +08:00
committed by GitHub
parent 807d4b200e
commit 8ceba34970
5 changed files with 459 additions and 190 deletions

View File

@@ -33,17 +33,27 @@ type cachedReader struct {
cache buf.MultiBuffer
}
func (r *cachedReader) Cache(b *buf.Buffer) {
mb, _ := r.reader.ReadMultiBufferTimeout(time.Millisecond * 100)
func (r *cachedReader) Cache(b *buf.Buffer) error {
mb, err := r.reader.ReadMultiBufferTimeout(time.Millisecond * 100)
if err != nil {
return err
}
r.Lock()
if !mb.IsEmpty() {
r.cache, _ = buf.MergeMulti(r.cache, mb)
}
b.Clear()
rawBytes := b.Extend(buf.Size)
cacheLen := r.cache.Len()
if cacheLen <= b.Cap() {
b.Clear()
} else {
b.Release()
*b = *buf.NewWithSize(cacheLen)
}
rawBytes := b.Extend(cacheLen)
n := r.cache.Copy(rawBytes)
b.Resize(0, int32(n))
r.Unlock()
return nil
}
func (r *cachedReader) readInternal() buf.MultiBuffer {
@@ -257,20 +267,24 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, netw
case <-ctx.Done():
return nil, ctx.Err()
default:
totalAttempt++
if totalAttempt > 2 {
return nil, errSniffingTimeout
}
cacheErr := cReader.Cache(payload)
cReader.Cache(payload)
if !payload.IsEmpty() {
result, err := sniffer.Sniff(ctx, payload.Bytes(), network)
if err != common.ErrNoClue {
switch err {
case common.ErrNoClue: // No Clue: protocol not matches, and sniffer cannot determine whether there will be a match or not
totalAttempt++
case protocol.ErrProtoNeedMoreData: // Protocol Need More Data: protocol matches, but need more data to complete sniffing
if cacheErr != nil { // Cache error (e.g. timeout) counts for failed attempt
totalAttempt++
}
default:
return result, err
}
}
if payload.IsFull() {
return nil, errUnknownContent
if totalAttempt >= 2 {
return nil, errSniffingTimeout
}
}
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/v2fly/v2ray-core/v5/common"
"github.com/v2fly/v2ray-core/v5/common/net"
"github.com/v2fly/v2ray-core/v5/common/protocol"
"github.com/v2fly/v2ray-core/v5/common/protocol/bittorrent"
"github.com/v2fly/v2ray-core/v5/common/protocol/http"
"github.com/v2fly/v2ray-core/v5/common/protocol/quic"
@@ -57,17 +58,20 @@ var errUnknownContent = newError("unknown content")
func (s *Sniffer) Sniff(c context.Context, payload []byte, network net.Network) (SniffResult, error) {
var pendingSniffer []protocolSnifferWithMetadata
for _, si := range s.sniffer {
s := si.protocolSniffer
sniffer := si.protocolSniffer
if si.metadataSniffer {
continue
}
if si.network != network {
continue
}
result, err := s(c, payload)
result, err := sniffer(c, payload)
if err == common.ErrNoClue {
pendingSniffer = append(pendingSniffer, si)
continue
} else if err == protocol.ErrProtoNeedMoreData { // Sniffer protocol matched, but need more data to complete sniffing
s.sniffer = []protocolSnifferWithMetadata{si}
return nil, protocol.ErrProtoNeedMoreData
}
if err == nil && result != nil {