Skip to content

Commit

Permalink
Correctly implement QUIC sniffer when handling multiple initial packe…
Browse files Browse the repository at this point in the history
…ts (#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>
  • Loading branch information
3 people authored Feb 16, 2025
1 parent 807d4b2 commit 8ceba34
Show file tree
Hide file tree
Showing 5 changed files with 446 additions and 177 deletions.
38 changes: 26 additions & 12 deletions app/dispatcher/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
}
}
Expand Down
8 changes: 6 additions & 2 deletions app/dispatcher/sniffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
Expand Down
6 changes: 6 additions & 0 deletions common/protocol/protocol.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
package protocol

import (
"errors"
)

//go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen

var ErrProtoNeedMoreData = errors.New("protocol matches, but need more data to complete sniffing")
Loading

0 comments on commit 8ceba34

Please sign in to comment.