1
0
Эх сурвалжийг харах

Correctly implement quic sniffer's frame parsing

Vigilans 2 жил өмнө
parent
commit
039e5f2078

+ 83 - 22
common/protocol/quic/sniff.go

@@ -10,6 +10,7 @@ import (
 	"github.com/quic-go/quic-go/quicvarint"
 	"github.com/xtls/xray-core/common"
 	"github.com/xtls/xray-core/common/buf"
+	"github.com/xtls/xray-core/common/bytespool"
 	"github.com/xtls/xray-core/common/errors"
 	ptls "github.com/xtls/xray-core/common/protocol/tls"
 	"golang.org/x/crypto/hkdf"
@@ -141,7 +142,7 @@ func SniffQUIC(b []byte) (*SniffHeader, error) {
 		packetNumber = uint32(n)
 	}
 
-	if packetNumber != 0 {
+	if packetNumber != 0 && packetNumber != 1 {
 		return nil, errNotQuicInitial
 	}
 
@@ -159,32 +160,92 @@ func SniffQUIC(b []byte) (*SniffHeader, error) {
 		return nil, err
 	}
 	buffer = buf.FromBytes(decrypted)
-	frameType, err := buffer.ReadByte()
-	if err != nil {
-		return nil, io.ErrUnexpectedEOF
-	}
-	if frameType != 0x6 {
-		// not crypto frame
-		return &SniffHeader{domain: ""}, nil
-	}
-	if common.Error2(quicvarint.Read(buffer)) != nil {
-		return nil, io.ErrUnexpectedEOF
-	}
-	dataLen, err := quicvarint.Read(buffer)
-	if err != nil {
-		return nil, io.ErrUnexpectedEOF
-	}
-	if dataLen > uint64(buffer.Len()) {
-		return nil, io.ErrUnexpectedEOF
+
+	cryptoLen := uint(0)
+	cryptoData := bytespool.Alloc(buffer.Len())
+	defer bytespool.Free(cryptoData)
+	for i := 0; !buffer.IsEmpty(); i++ {
+		frameType := byte(0x0) // Default to PADDING frame
+		for frameType == 0x0 && !buffer.IsEmpty() {
+			frameType, _ = buffer.ReadByte()
+		}
+		switch frameType {
+		case 0x00: // PADDING frame
+		case 0x01: // PING frame
+		case 0x02, 0x03: // ACK frame
+			if _, err = quicvarint.Read(buffer); err != nil { // Field: Largest Acknowledged
+				return nil, io.ErrUnexpectedEOF
+			}
+			if _, err = quicvarint.Read(buffer); err != nil { // Field: ACK Delay
+				return nil, io.ErrUnexpectedEOF
+			}
+			ackRangeCount, err := quicvarint.Read(buffer) // Field: ACK Range Count
+			if err != nil {
+				return nil, io.ErrUnexpectedEOF
+			}
+			if _, err = quicvarint.Read(buffer); err != nil { // Field: First ACK Range
+				return nil, io.ErrUnexpectedEOF
+			}
+			for i := 0; i < int(ackRangeCount); i++ { // Field: ACK Range
+				if _, err = quicvarint.Read(buffer); err != nil { // Field: ACK Range -> Gap
+					return nil, io.ErrUnexpectedEOF
+				}
+				if _, err = quicvarint.Read(buffer); err != nil { // Field: ACK Range -> ACK Range Length
+					return nil, io.ErrUnexpectedEOF
+				}
+			}
+			if frameType == 0x03 {
+				if _, err = quicvarint.Read(buffer); err != nil { // Field: ECN Counts -> ECT0 Count
+					return nil, io.ErrUnexpectedEOF
+				}
+				if _, err = quicvarint.Read(buffer); err != nil { // Field: ECN Counts -> ECT1 Count
+					return nil, io.ErrUnexpectedEOF
+				}
+				if _, err = quicvarint.Read(buffer); err != nil { //nolint:misspell // Field: ECN Counts -> ECT-CE Count
+					return nil, io.ErrUnexpectedEOF
+				}
+			}
+		case 0x06: // CRYPTO frame, we will use this frame
+			offset, err := quicvarint.Read(buffer) // Field: Offset
+			if err != nil {
+				return nil, io.ErrUnexpectedEOF
+			}
+			length, err := quicvarint.Read(buffer) // Field: Length
+			if err != nil || length > uint64(buffer.Len()) {
+				return nil, io.ErrUnexpectedEOF
+			}
+			if cryptoLen < uint(offset+length) {
+				cryptoLen = uint(offset + length)
+			}
+			if _, err := buffer.Read(cryptoData[offset : offset+length]); err != nil { // Field: Crypto Data
+				return nil, io.ErrUnexpectedEOF
+			}
+		case 0x1c: // CONNECTION_CLOSE frame, only 0x1c is permitted in initial packet
+			if _, err = quicvarint.Read(buffer); err != nil { // Field: Error Code
+				return nil, io.ErrUnexpectedEOF
+			}
+			if _, err = quicvarint.Read(buffer); err != nil { // Field: Frame Type
+				return nil, io.ErrUnexpectedEOF
+			}
+			length, err := quicvarint.Read(buffer) // Field: Reason Phrase Length
+			if err != nil {
+				return nil, io.ErrUnexpectedEOF
+			}
+			if _, err := buffer.ReadBytes(int32(length)); err != nil { // Field: Reason Phrase
+				return nil, io.ErrUnexpectedEOF
+			}
+		default:
+			// Only above frame types are permitted in initial packet.
+			// See https://www.rfc-editor.org/rfc/rfc9000.html#section-17.2.2-8
+			return nil, errNotQuicInitial
+		}
 	}
-	frameData, err := buffer.ReadBytes(int32(dataLen))
-	common.Must(err)
+
 	tlsHdr := &ptls.SniffHeader{}
-	err = ptls.ReadClientHello(frameData, tlsHdr)
+	err = ptls.ReadClientHello(cryptoData[:cryptoLen], tlsHdr)
 	if err != nil {
 		return nil, err
 	}
-
 	return &SniffHeader{domain: tlsHdr.Domain()}, nil
 }