Skip to content

Commit

Permalink
Introduce protocol.ErrProtoNeedMoreData to allow sniffer to fetch m…
Browse files Browse the repository at this point in the history
…ore packets until complete
  • Loading branch information
dyhkwong authored and Vigilans committed Feb 15, 2025
1 parent d5c5f6d commit 04d482f
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 44 deletions.
31 changes: 20 additions & 11 deletions app/dispatcher/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,11 @@ 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)
Expand All @@ -50,6 +53,7 @@ func (r *cachedReader) Cache(b *buf.Buffer) {
n := r.cache.Copy(rawBytes)
b.Resize(0, int32(n))
r.Unlock()
return nil
}

func (r *cachedReader) readInternal() buf.MultiBuffer {
Expand Down Expand Up @@ -263,20 +267,25 @@ 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 {
return result, err
if err == nil {

Check failure on line 274 in app/dispatcher/default.go

View workflow job for this annotation

GitHub Actions / lint

ifElseChain: rewrite if-else to switch statement (gocritic)
return result, nil
} else if err == common.ErrNoClue { // No Clue: protocol not matches, and sniffer cannot determine whether there will be a match or not
totalAttempt++
} else if err == 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++
}
} else {
return nil, 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
8 changes: 8 additions & 0 deletions common/protocol/protocol.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
package protocol

import (
"errors"
)

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

var (

Check failure on line 9 in common/protocol/protocol.go

View workflow job for this annotation

GitHub Actions / lint

File is not properly formatted (gofumpt)
ErrProtoNeedMoreData = errors.New("protocol matches, but need more data to complete sniffing")
)
4 changes: 3 additions & 1 deletion common/protocol/quic/sniff.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/v2fly/v2ray-core/v5/common/buf"
"github.com/v2fly/v2ray-core/v5/common/bytespool"
"github.com/v2fly/v2ray-core/v5/common/errors"
"github.com/v2fly/v2ray-core/v5/common/protocol"
ptls "github.com/v2fly/v2ray-core/v5/common/protocol/tls"
)

Expand Down Expand Up @@ -267,7 +268,8 @@ func SniffQUIC(b []byte) (*SniffHeader, error) {
}
return &SniffHeader{domain: tlsHdr.Domain()}, nil
}
return nil, common.ErrNoClue
// All payload is parsed as valid QUIC packets, but we need more packets for crypto data to read client hello.
return nil, protocol.ErrProtoNeedMoreData
}

func hkdfExpandLabel(hash crypto.Hash, secret, context []byte, label string, length int) []byte {
Expand Down
Loading

0 comments on commit 04d482f

Please sign in to comment.