mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-04-30 09:18:34 +00:00
QUIC sniffer: Full support for handling multiple initial packets (#4642)
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com> Co-authored-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:
parent
a608c5a1db
commit
58c48664e2
7 changed files with 371 additions and 95 deletions
|
@ -33,23 +33,21 @@ type cachedReader struct {
|
||||||
cache buf.MultiBuffer
|
cache buf.MultiBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *cachedReader) Cache(b *buf.Buffer) {
|
func (r *cachedReader) Cache(b *buf.Buffer, deadline time.Duration) error {
|
||||||
mb, _ := r.reader.ReadMultiBufferTimeout(time.Millisecond * 100)
|
mb, err := r.reader.ReadMultiBufferTimeout(deadline)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
r.Lock()
|
r.Lock()
|
||||||
if !mb.IsEmpty() {
|
if !mb.IsEmpty() {
|
||||||
r.cache, _ = buf.MergeMulti(r.cache, mb)
|
r.cache, _ = buf.MergeMulti(r.cache, mb)
|
||||||
}
|
}
|
||||||
cacheLen := r.cache.Len()
|
|
||||||
if cacheLen <= b.Cap() {
|
|
||||||
b.Clear()
|
b.Clear()
|
||||||
} else {
|
rawBytes := b.Extend(b.Cap())
|
||||||
b.Release()
|
|
||||||
*b = *buf.NewWithSize(cacheLen)
|
|
||||||
}
|
|
||||||
rawBytes := b.Extend(cacheLen)
|
|
||||||
n := r.cache.Copy(rawBytes)
|
n := r.cache.Copy(rawBytes)
|
||||||
b.Resize(0, int32(n))
|
b.Resize(0, int32(n))
|
||||||
r.Unlock()
|
r.Unlock()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *cachedReader) readInternal() buf.MultiBuffer {
|
func (r *cachedReader) readInternal() buf.MultiBuffer {
|
||||||
|
@ -355,7 +353,7 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
|
||||||
}
|
}
|
||||||
|
|
||||||
func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, network net.Network) (SniffResult, error) {
|
func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, network net.Network) (SniffResult, error) {
|
||||||
payload := buf.New()
|
payload := buf.NewWithSize(32767)
|
||||||
defer payload.Release()
|
defer payload.Release()
|
||||||
|
|
||||||
sniffer := NewSniffer(ctx)
|
sniffer := NewSniffer(ctx)
|
||||||
|
@ -367,26 +365,33 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, netw
|
||||||
}
|
}
|
||||||
|
|
||||||
contentResult, contentErr := func() (SniffResult, error) {
|
contentResult, contentErr := func() (SniffResult, error) {
|
||||||
|
cacheDeadline := 200 * time.Millisecond
|
||||||
totalAttempt := 0
|
totalAttempt := 0
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return nil, ctx.Err()
|
return nil, ctx.Err()
|
||||||
default:
|
default:
|
||||||
totalAttempt++
|
cachingStartingTimeStamp := time.Now()
|
||||||
if totalAttempt > 2 {
|
cacheErr := cReader.Cache(payload, cacheDeadline)
|
||||||
return nil, errSniffingTimeout
|
cachingTimeElapsed := time.Since(cachingStartingTimeStamp)
|
||||||
}
|
cacheDeadline -= cachingTimeElapsed
|
||||||
|
|
||||||
cReader.Cache(payload)
|
|
||||||
if !payload.IsEmpty() {
|
if !payload.IsEmpty() {
|
||||||
result, err := sniffer.Sniff(ctx, payload.Bytes(), network)
|
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
|
return result, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if payload.IsFull() {
|
if totalAttempt >= 2 || cacheDeadline <= 0 {
|
||||||
return nil, errUnknownContent
|
return nil, errSniffingTimeout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/common/protocol"
|
||||||
"github.com/xtls/xray-core/common/protocol/bittorrent"
|
"github.com/xtls/xray-core/common/protocol/bittorrent"
|
||||||
"github.com/xtls/xray-core/common/protocol/http"
|
"github.com/xtls/xray-core/common/protocol/http"
|
||||||
"github.com/xtls/xray-core/common/protocol/quic"
|
"github.com/xtls/xray-core/common/protocol/quic"
|
||||||
|
@ -58,14 +59,17 @@ var errUnknownContent = errors.New("unknown content")
|
||||||
func (s *Sniffer) Sniff(c context.Context, payload []byte, network net.Network) (SniffResult, error) {
|
func (s *Sniffer) Sniff(c context.Context, payload []byte, network net.Network) (SniffResult, error) {
|
||||||
var pendingSniffer []protocolSnifferWithMetadata
|
var pendingSniffer []protocolSnifferWithMetadata
|
||||||
for _, si := range s.sniffer {
|
for _, si := range s.sniffer {
|
||||||
s := si.protocolSniffer
|
protocolSniffer := si.protocolSniffer
|
||||||
if si.metadataSniffer || si.network != network {
|
if si.metadataSniffer || si.network != network {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
result, err := s(c, payload)
|
result, err := protocolSniffer(c, payload)
|
||||||
if err == common.ErrNoClue {
|
if err == common.ErrNoClue {
|
||||||
pendingSniffer = append(pendingSniffer, si)
|
pendingSniffer = append(pendingSniffer, si)
|
||||||
continue
|
continue
|
||||||
|
} else if err == protocol.ErrProtoNeedMoreData { // Sniffer protocol matched, but need more data to complete sniffing
|
||||||
|
s.sniffer = []protocolSnifferWithMetadata{si}
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil && result != nil {
|
if err == nil && result != nil {
|
||||||
|
|
|
@ -15,6 +15,15 @@ const (
|
||||||
|
|
||||||
var pool = bytespool.GetPool(Size)
|
var pool = bytespool.GetPool(Size)
|
||||||
|
|
||||||
|
// ownership represents the data owner of the buffer.
|
||||||
|
type ownership uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
managed ownership = iota
|
||||||
|
unmanaged
|
||||||
|
bytespools
|
||||||
|
)
|
||||||
|
|
||||||
// Buffer is a recyclable allocation of a byte array. Buffer.Release() recycles
|
// Buffer is a recyclable allocation of a byte array. Buffer.Release() recycles
|
||||||
// the buffer into an internal buffer pool, in order to recreate a buffer more
|
// the buffer into an internal buffer pool, in order to recreate a buffer more
|
||||||
// quickly.
|
// quickly.
|
||||||
|
@ -22,11 +31,11 @@ type Buffer struct {
|
||||||
v []byte
|
v []byte
|
||||||
start int32
|
start int32
|
||||||
end int32
|
end int32
|
||||||
unmanaged bool
|
ownership ownership
|
||||||
UDP *net.Destination
|
UDP *net.Destination
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a Buffer with 0 length and 8K capacity.
|
// New creates a Buffer with 0 length and 8K capacity, managed.
|
||||||
func New() *Buffer {
|
func New() *Buffer {
|
||||||
buf := pool.Get().([]byte)
|
buf := pool.Get().([]byte)
|
||||||
if cap(buf) >= Size {
|
if cap(buf) >= Size {
|
||||||
|
@ -40,7 +49,7 @@ func New() *Buffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewExisted creates a managed, standard size Buffer with an existed bytearray
|
// NewExisted creates a standard size Buffer with an existed bytearray, managed.
|
||||||
func NewExisted(b []byte) *Buffer {
|
func NewExisted(b []byte) *Buffer {
|
||||||
if cap(b) < Size {
|
if cap(b) < Size {
|
||||||
panic("Invalid buffer")
|
panic("Invalid buffer")
|
||||||
|
@ -57,16 +66,16 @@ func NewExisted(b []byte) *Buffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromBytes creates a Buffer with an existed bytearray
|
// FromBytes creates a Buffer with an existed bytearray, unmanaged.
|
||||||
func FromBytes(b []byte) *Buffer {
|
func FromBytes(b []byte) *Buffer {
|
||||||
return &Buffer{
|
return &Buffer{
|
||||||
v: b,
|
v: b,
|
||||||
end: int32(len(b)),
|
end: int32(len(b)),
|
||||||
unmanaged: true,
|
ownership: unmanaged,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// StackNew creates a new Buffer object on stack.
|
// StackNew creates a new Buffer object on stack, managed.
|
||||||
// This method is for buffers that is released in the same function.
|
// This method is for buffers that is released in the same function.
|
||||||
func StackNew() Buffer {
|
func StackNew() Buffer {
|
||||||
buf := pool.Get().([]byte)
|
buf := pool.Get().([]byte)
|
||||||
|
@ -81,9 +90,17 @@ func StackNew() Buffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewWithSize creates a Buffer with 0 length and capacity with at least the given size, bytespool's.
|
||||||
|
func NewWithSize(size int32) *Buffer {
|
||||||
|
return &Buffer{
|
||||||
|
v: bytespool.Alloc(size),
|
||||||
|
ownership: bytespools,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Release recycles the buffer into an internal buffer pool.
|
// Release recycles the buffer into an internal buffer pool.
|
||||||
func (b *Buffer) Release() {
|
func (b *Buffer) Release() {
|
||||||
if b == nil || b.v == nil || b.unmanaged {
|
if b == nil || b.v == nil || b.ownership == unmanaged {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,9 +108,14 @@ func (b *Buffer) Release() {
|
||||||
b.v = nil
|
b.v = nil
|
||||||
b.Clear()
|
b.Clear()
|
||||||
|
|
||||||
|
switch b.ownership {
|
||||||
|
case managed:
|
||||||
if cap(p) == Size {
|
if cap(p) == Size {
|
||||||
pool.Put(p)
|
pool.Put(p)
|
||||||
}
|
}
|
||||||
|
case bytespools:
|
||||||
|
bytespool.Free(p)
|
||||||
|
}
|
||||||
b.UDP = nil
|
b.UDP = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,13 +237,6 @@ func (b *Buffer) Cap() int32 {
|
||||||
return int32(len(b.v))
|
return int32(len(b.v))
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWithSize creates a Buffer with 0 length and capacity with at least the given size.
|
|
||||||
func NewWithSize(size int32) *Buffer {
|
|
||||||
return &Buffer{
|
|
||||||
v: bytespool.Alloc(size),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmpty returns true if the buffer is empty.
|
// IsEmpty returns true if the buffer is empty.
|
||||||
func (b *Buffer) IsEmpty() bool {
|
func (b *Buffer) IsEmpty() bool {
|
||||||
return b.Len() == 0
|
return b.Len() == 0
|
||||||
|
|
|
@ -1 +1,7 @@
|
||||||
package protocol // import "github.com/xtls/xray-core/common/protocol"
|
package protocol // import "github.com/xtls/xray-core/common/protocol"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrProtoNeedMoreData = errors.New("protocol matches, but need more data to complete sniffing")
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package quic
|
package quic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
@ -13,6 +12,7 @@ import (
|
||||||
"github.com/xtls/xray-core/common/buf"
|
"github.com/xtls/xray-core/common/buf"
|
||||||
"github.com/xtls/xray-core/common/bytespool"
|
"github.com/xtls/xray-core/common/bytespool"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
"github.com/xtls/xray-core/common/protocol"
|
||||||
ptls "github.com/xtls/xray-core/common/protocol/tls"
|
ptls "github.com/xtls/xray-core/common/protocol/tls"
|
||||||
"golang.org/x/crypto/hkdf"
|
"golang.org/x/crypto/hkdf"
|
||||||
)
|
)
|
||||||
|
@ -47,22 +47,17 @@ var (
|
||||||
errNotQuicInitial = errors.New("not initial packet")
|
errNotQuicInitial = errors.New("not initial packet")
|
||||||
)
|
)
|
||||||
|
|
||||||
func SniffQUIC(b []byte) (resultReturn *SniffHeader, errorReturn error) {
|
func SniffQUIC(b []byte) (*SniffHeader, error) {
|
||||||
// In extremely rare cases, this sniffer may cause slice error
|
if len(b) == 0 {
|
||||||
// and we set recover() here to prevent crash.
|
return nil, common.ErrNoClue
|
||||||
// TODO: Thoroughly fix this panic
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
errors.LogError(context.Background(), "Failed to sniff QUIC: ", r)
|
|
||||||
resultReturn = nil
|
|
||||||
errorReturn = common.ErrNoClue
|
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
|
|
||||||
// Crypto data separated across packets
|
// Crypto data separated across packets
|
||||||
cryptoLen := 0
|
cryptoLen := 0
|
||||||
cryptoData := bytespool.Alloc(int32(len(b)))
|
cryptoData := bytespool.Alloc(32767)
|
||||||
defer bytespool.Free(cryptoData)
|
defer bytespool.Free(cryptoData)
|
||||||
|
cache := buf.New()
|
||||||
|
defer cache.Release()
|
||||||
|
|
||||||
// Parse QUIC packets
|
// Parse QUIC packets
|
||||||
for len(b) > 0 {
|
for len(b) > 0 {
|
||||||
|
@ -105,6 +100,7 @@ func SniffQUIC(b []byte) (resultReturn *SniffHeader, errorReturn error) {
|
||||||
return nil, errNotQuic
|
return nil, errNotQuic
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isQuicInitial { // Only initial packets have token, see https://datatracker.ietf.org/doc/html/rfc9000#section-17.2.2
|
||||||
tokenLen, err := quicvarint.Read(buffer)
|
tokenLen, err := quicvarint.Read(buffer)
|
||||||
if err != nil || tokenLen > uint64(len(b)) {
|
if err != nil || tokenLen > uint64(len(b)) {
|
||||||
return nil, errNotQuic
|
return nil, errNotQuic
|
||||||
|
@ -113,6 +109,7 @@ func SniffQUIC(b []byte) (resultReturn *SniffHeader, errorReturn error) {
|
||||||
if _, err = buffer.ReadBytes(int32(tokenLen)); err != nil {
|
if _, err = buffer.ReadBytes(int32(tokenLen)); err != nil {
|
||||||
return nil, errNotQuic
|
return nil, errNotQuic
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
packetLen, err := quicvarint.Read(buffer)
|
packetLen, err := quicvarint.Read(buffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -130,9 +127,6 @@ func SniffQUIC(b []byte) (resultReturn *SniffHeader, errorReturn error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
origPNBytes := make([]byte, 4)
|
|
||||||
copy(origPNBytes, b[hdrLen:hdrLen+4])
|
|
||||||
|
|
||||||
var salt []byte
|
var salt []byte
|
||||||
if versionNumber == version1 {
|
if versionNumber == version1 {
|
||||||
salt = quicSalt
|
salt = quicSalt
|
||||||
|
@ -147,44 +141,34 @@ func SniffQUIC(b []byte) (resultReturn *SniffHeader, errorReturn error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cache := buf.New()
|
cache.Clear()
|
||||||
defer cache.Release()
|
|
||||||
|
|
||||||
mask := cache.Extend(int32(block.BlockSize()))
|
mask := cache.Extend(int32(block.BlockSize()))
|
||||||
block.Encrypt(mask, b[hdrLen+4:hdrLen+4+16])
|
block.Encrypt(mask, b[hdrLen+4:hdrLen+4+16])
|
||||||
b[0] ^= mask[0] & 0xf
|
b[0] ^= mask[0] & 0xf
|
||||||
for i := range b[hdrLen : hdrLen+4] {
|
packetNumberLength := int(b[0]&0x3 + 1)
|
||||||
|
for i := range packetNumberLength {
|
||||||
b[hdrLen+i] ^= mask[i+1]
|
b[hdrLen+i] ^= mask[i+1]
|
||||||
}
|
}
|
||||||
packetNumberLength := b[0]&0x3 + 1
|
|
||||||
if packetNumberLength != 1 {
|
|
||||||
return nil, errNotQuicInitial
|
|
||||||
}
|
|
||||||
var packetNumber uint32
|
|
||||||
{
|
|
||||||
n, err := buffer.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
packetNumber = uint32(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
extHdrLen := hdrLen + int(packetNumberLength)
|
|
||||||
copy(b[extHdrLen:hdrLen+4], origPNBytes[packetNumberLength:])
|
|
||||||
data := b[extHdrLen : int(packetLen)+hdrLen]
|
|
||||||
|
|
||||||
key := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic key", 16)
|
key := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic key", 16)
|
||||||
iv := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic iv", 12)
|
iv := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic iv", 12)
|
||||||
cipher := AEADAESGCMTLS13(key, iv)
|
cipher := AEADAESGCMTLS13(key, iv)
|
||||||
|
|
||||||
nonce := cache.Extend(int32(cipher.NonceSize()))
|
nonce := cache.Extend(int32(cipher.NonceSize()))
|
||||||
binary.BigEndian.PutUint64(nonce[len(nonce)-8:], uint64(packetNumber))
|
_, err = buffer.Read(nonce[len(nonce)-packetNumberLength:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
extHdrLen := hdrLen + packetNumberLength
|
||||||
|
data := b[extHdrLen : int(packetLen)+hdrLen]
|
||||||
decrypted, err := cipher.Open(b[extHdrLen:extHdrLen], nonce, data, b[:extHdrLen])
|
decrypted, err := cipher.Open(b[extHdrLen:extHdrLen], nonce, data, b[:extHdrLen])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
buffer = buf.FromBytes(decrypted)
|
buffer = buf.FromBytes(decrypted)
|
||||||
for i := 0; !buffer.IsEmpty(); i++ {
|
for !buffer.IsEmpty() {
|
||||||
frameType := byte(0x0) // Default to PADDING frame
|
frameType, _ := buffer.ReadByte()
|
||||||
for frameType == 0x0 && !buffer.IsEmpty() {
|
for frameType == 0x0 && !buffer.IsEmpty() {
|
||||||
frameType, _ = buffer.ReadByte()
|
frameType, _ = buffer.ReadByte()
|
||||||
}
|
}
|
||||||
|
@ -234,13 +218,12 @@ func SniffQUIC(b []byte) (resultReturn *SniffHeader, errorReturn error) {
|
||||||
return nil, io.ErrUnexpectedEOF
|
return nil, io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
if cryptoLen < int(offset+length) {
|
if cryptoLen < int(offset+length) {
|
||||||
cryptoLen = int(offset + length)
|
newCryptoLen := int(offset + length)
|
||||||
if len(cryptoData) < cryptoLen {
|
if len(cryptoData) < newCryptoLen {
|
||||||
newCryptoData := bytespool.Alloc(int32(cryptoLen))
|
return nil, io.ErrShortBuffer
|
||||||
copy(newCryptoData, cryptoData)
|
|
||||||
bytespool.Free(cryptoData)
|
|
||||||
cryptoData = newCryptoData
|
|
||||||
}
|
}
|
||||||
|
wipeBytes(cryptoData[cryptoLen:newCryptoLen])
|
||||||
|
cryptoLen = newCryptoLen
|
||||||
}
|
}
|
||||||
if _, err := buffer.Read(cryptoData[offset : offset+length]); err != nil { // Field: Crypto Data
|
if _, err := buffer.Read(cryptoData[offset : offset+length]); err != nil { // Field: Crypto Data
|
||||||
return nil, io.ErrUnexpectedEOF
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
@ -276,7 +259,14 @@ func SniffQUIC(b []byte) (resultReturn *SniffHeader, errorReturn error) {
|
||||||
}
|
}
|
||||||
return &SniffHeader{domain: tlsHdr.Domain()}, nil
|
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 wipeBytes(b []byte) {
|
||||||
|
for i := range len(b) {
|
||||||
|
b[i] = 0x0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func hkdfExpandLabel(hash crypto.Hash, secret, context []byte, label string, length int) []byte {
|
func hkdfExpandLabel(hash crypto.Hash, secret, context []byte, label string, length int) []byte {
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -3,9 +3,9 @@ package tls
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SniffHeader struct {
|
type SniffHeader struct {
|
||||||
|
@ -59,9 +59,6 @@ func ReadClientHello(data []byte, h *SniffHeader) error {
|
||||||
}
|
}
|
||||||
data = data[1+compressionMethodsLen:]
|
data = data[1+compressionMethodsLen:]
|
||||||
|
|
||||||
if len(data) == 0 {
|
|
||||||
return errNotClientHello
|
|
||||||
}
|
|
||||||
if len(data) < 2 {
|
if len(data) < 2 {
|
||||||
return errNotClientHello
|
return errNotClientHello
|
||||||
}
|
}
|
||||||
|
@ -104,13 +101,21 @@ func ReadClientHello(data []byte, h *SniffHeader) error {
|
||||||
return errNotClientHello
|
return errNotClientHello
|
||||||
}
|
}
|
||||||
if nameType == 0 {
|
if nameType == 0 {
|
||||||
serverName := string(d[:nameLen])
|
// QUIC separated across packets
|
||||||
|
// May cause the serverName to be incomplete
|
||||||
|
b := byte(0)
|
||||||
|
for _, b = range d[:nameLen] {
|
||||||
|
if b <= ' ' {
|
||||||
|
return protocol.ErrProtoNeedMoreData
|
||||||
|
}
|
||||||
|
}
|
||||||
// An SNI value may not include a
|
// An SNI value may not include a
|
||||||
// trailing dot. See
|
// trailing dot. See
|
||||||
// https://tools.ietf.org/html/rfc6066#section-3.
|
// https://tools.ietf.org/html/rfc6066#section-3.
|
||||||
if strings.HasSuffix(serverName, ".") {
|
if b == '.' {
|
||||||
return errNotClientHello
|
return errNotClientHello
|
||||||
}
|
}
|
||||||
|
serverName := string(d[:nameLen])
|
||||||
h.domain = serverName
|
h.domain = serverName
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue