diff --git a/app/dispatcher/default.go b/app/dispatcher/default.go index 4365a2728..a233936dc 100644 --- a/app/dispatcher/default.go +++ b/app/dispatcher/default.go @@ -33,8 +33,8 @@ type cachedReader struct { cache buf.MultiBuffer } -func (r *cachedReader) Cache(b *buf.Buffer) error { - mb, err := r.reader.ReadMultiBufferTimeout(time.Millisecond * 100) +func (r *cachedReader) Cache(b *buf.Buffer, deadline time.Duration) error { + mb, err := r.reader.ReadMultiBufferTimeout(deadline) if err != nil { return err } @@ -42,14 +42,8 @@ func (r *cachedReader) Cache(b *buf.Buffer) error { if !mb.IsEmpty() { r.cache, _ = buf.MergeMulti(r.cache, mb) } - cacheLen := r.cache.Len() - if cacheLen <= b.Cap() { - b.Clear() - } else { - b.Release() - *b = *buf.NewWithSize(cacheLen) - } - rawBytes := b.Extend(cacheLen) + b.Clear() + rawBytes := b.Extend(b.Cap()) n := r.cache.Copy(rawBytes) b.Resize(0, int32(n)) r.Unlock() @@ -249,11 +243,9 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin } func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, network net.Network) (SniffResult, error) { - payload := buf.New() + payload := buf.NewWithSize(32767) - defer func() { - payload.Release() - }() + defer payload.Release() sniffer := NewSniffer(ctx) @@ -264,13 +256,17 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, netw } contentResult, contentErr := func() (SniffResult, error) { + cacheDeadline := 200 * time.Millisecond totalAttempt := 0 for { select { case <-ctx.Done(): return nil, ctx.Err() default: - cacheErr := cReader.Cache(payload) + cachingStartingTimeStamp := time.Now() + cacheErr := cReader.Cache(payload, cacheDeadline) + cachingTimeElapsed := time.Since(cachingStartingTimeStamp) + cacheDeadline -= cachingTimeElapsed if !payload.IsEmpty() { result, err := sniffer.Sniff(ctx, payload.Bytes(), network) @@ -286,7 +282,7 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, netw } } - if totalAttempt >= 2 { + if totalAttempt >= 2 || cacheDeadline <= 0 { return nil, errSniffingTimeout } } diff --git a/common/protocol/quic/sniff.go b/common/protocol/quic/sniff.go index 3d9af98f5..ca1a91de0 100644 --- a/common/protocol/quic/sniff.go +++ b/common/protocol/quic/sniff.go @@ -12,7 +12,6 @@ import ( "github.com/v2fly/v2ray-core/v5/common" "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" @@ -49,12 +48,14 @@ var ( ) func SniffQUIC(b []byte) (*SniffHeader, error) { + if len(b) == 0 { + return nil, common.ErrNoClue + } + // Crypto data separated across packets cryptoLen := 0 - cryptoData := bytespool.Alloc(int32(len(b))) - defer func() { - bytespool.Free(cryptoData) - }() + cryptoDataBuf := buf.NewWithSize(32767) + defer cryptoDataBuf.Release() cache := buf.New() defer cache.Release() @@ -230,14 +231,14 @@ func SniffQUIC(b []byte) (*SniffHeader, error) { } if cryptoLen < int(offset+length) { cryptoLen = int(offset + length) - if len(cryptoData) < cryptoLen { - newCryptoData := bytespool.Alloc(int32(cryptoLen)) - copy(newCryptoData, cryptoData) - bytespool.Free(cryptoData) - cryptoData = newCryptoData + if cryptoDataBuf.Cap() < int32(cryptoLen) { + return nil, io.ErrShortBuffer + } + if cryptoDataBuf.Len() != int32(cryptoLen) { + cryptoDataBuf.Extend(int32(cryptoLen) - cryptoDataBuf.Len()) } } - if _, err := buffer.Read(cryptoData[offset : offset+length]); err != nil { // Field: Crypto Data + if _, err := buffer.Read(cryptoDataBuf.BytesRange(int32(offset), int32(offset+length))); err != nil { // Field: Crypto Data return nil, io.ErrUnexpectedEOF } case 0x1c: // CONNECTION_CLOSE frame, only 0x1c is permitted in initial packet @@ -262,7 +263,7 @@ func SniffQUIC(b []byte) (*SniffHeader, error) { } tlsHdr := &ptls.SniffHeader{} - err = ptls.ReadClientHello(cryptoData[:cryptoLen], tlsHdr) + err = ptls.ReadClientHello(cryptoDataBuf.BytesRange(0, int32(cryptoLen)), tlsHdr) if err != nil { // The crypto data may have not been fully recovered in current packets, // So we continue to sniff rest packets. @@ -271,6 +272,7 @@ func SniffQUIC(b []byte) (*SniffHeader, error) { } return &SniffHeader{domain: tlsHdr.Domain()}, nil } + // All payload is parsed as valid QUIC packets, but we need more packets for crypto data to read client hello. return nil, protocol.ErrProtoNeedMoreData }