Skip to content

Commit 801f3c3

Browse files
authored
feat: support sniff quic fragment data (MetaCubeX#1899)
1 parent 7ff046a commit 801f3c3

File tree

5 files changed

+558
-238
lines changed

5 files changed

+558
-238
lines changed

component/sniffer/dispatcher.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,18 +66,22 @@ func (sd *Dispatcher) forceSniff(metadata *C.Metadata) bool {
6666
func (sd *Dispatcher) UDPSniff(packet C.PacketAdapter, packetSender C.PacketSender) C.PacketSender {
6767
metadata := packet.Metadata()
6868
if sd.shouldOverride(metadata) {
69-
for sniffer, config := range sd.sniffers {
70-
if sniffer.SupportNetwork() == C.UDP || sniffer.SupportNetwork() == C.ALLNet {
71-
inWhitelist := sniffer.SupportPort(metadata.DstPort)
69+
for current, config := range sd.sniffers {
70+
if current.SupportNetwork() == C.UDP || current.SupportNetwork() == C.ALLNet {
71+
inWhitelist := current.SupportPort(metadata.DstPort)
7272
overrideDest := config.OverrideDest
7373

7474
if inWhitelist {
75-
host, err := sniffer.SniffData(packet.Data())
75+
if wrapable, ok := current.(sniffer.MultiPacketSniffer); ok {
76+
return wrapable.WrapperSender(packetSender, overrideDest)
77+
}
78+
79+
host, err := current.SniffData(packet.Data())
7680
if err != nil {
7781
continue
7882
}
7983

80-
sd.replaceDomain(metadata, host, overrideDest)
84+
replaceDomain(metadata, host, overrideDest)
8185
return packetSender
8286
}
8387
}
@@ -133,13 +137,13 @@ func (sd *Dispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) bool
133137

134138
sd.skipList.Delete(dst)
135139

136-
sd.replaceDomain(metadata, host, overrideDest)
140+
replaceDomain(metadata, host, overrideDest)
137141
return true
138142
}
139143
return false
140144
}
141145

142-
func (sd *Dispatcher) replaceDomain(metadata *C.Metadata, host string, overrideDest bool) {
146+
func replaceDomain(metadata *C.Metadata, host string, overrideDest bool) {
143147
metadata.SniffHost = host
144148
if overrideDest {
145149
log.Debugln("[Sniffer] Sniff %s [%s]-->[%s] success, replace domain [%s]-->[%s]",

component/sniffer/quic_sniffer.go

Lines changed: 21 additions & 220 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,14 @@ package sniffer
22

33
import (
44
"crypto"
5-
"crypto/aes"
6-
"crypto/cipher"
75
"encoding/binary"
86
"errors"
9-
"io"
7+
"time"
108

11-
"github.com/metacubex/mihomo/common/buf"
129
"github.com/metacubex/mihomo/common/utils"
10+
"github.com/metacubex/mihomo/constant"
1311
C "github.com/metacubex/mihomo/constant"
14-
15-
"github.com/metacubex/quic-go/quicvarint"
12+
"github.com/metacubex/mihomo/constant/sniffer"
1613
"golang.org/x/crypto/hkdf"
1714
)
1815

@@ -21,6 +18,10 @@ import (
2118
const (
2219
versionDraft29 uint32 = 0xff00001d
2320
version1 uint32 = 0x1
21+
// Timeout before quic sniffer all packets
22+
quicWaitConn = time.Second * 3
23+
quicPacketTypeInitial = 0x00
24+
quicPacketType0RTT = 0x01
2425
)
2526

2627
var (
@@ -30,6 +31,9 @@ var (
3031
errNotQuicInitial = errors.New("not QUIC initial packet")
3132
)
3233

34+
var _ sniffer.Sniffer = (*QuicSniffer)(nil)
35+
var _ sniffer.MultiPacketSniffer = (*QuicSniffer)(nil)
36+
3337
type QuicSniffer struct {
3438
*BaseSniffer
3539
}
@@ -44,228 +48,25 @@ func NewQuicSniffer(snifferConfig SnifferConfig) (*QuicSniffer, error) {
4448
}, nil
4549
}
4650

47-
func (quic QuicSniffer) Protocol() string {
51+
func (sniffer *QuicSniffer) Protocol() string {
4852
return "quic"
4953
}
5054

51-
func (quic QuicSniffer) SupportNetwork() C.NetWork {
55+
func (sniffer *QuicSniffer) SupportNetwork() C.NetWork {
5256
return C.UDP
5357
}
5458

55-
func (quic QuicSniffer) SniffData(b []byte) (string, error) {
56-
buffer := buf.As(b)
57-
typeByte, err := buffer.ReadByte()
58-
if err != nil {
59-
return "", errNotQuic
60-
}
61-
isLongHeader := typeByte&0x80 > 0
62-
if !isLongHeader || typeByte&0x40 == 0 {
63-
return "", errNotQuicInitial
64-
}
65-
66-
vb, err := buffer.ReadBytes(4)
67-
if err != nil {
68-
return "", errNotQuic
69-
}
70-
71-
versionNumber := binary.BigEndian.Uint32(vb)
72-
73-
if versionNumber != 0 && typeByte&0x40 == 0 {
74-
return "", errNotQuic
75-
} else if versionNumber != versionDraft29 && versionNumber != version1 {
76-
return "", errNotQuic
77-
}
78-
79-
if (typeByte&0x30)>>4 != 0x0 {
80-
return "", errNotQuicInitial
81-
}
82-
83-
var destConnID []byte
84-
if l, err := buffer.ReadByte(); err != nil {
85-
return "", errNotQuic
86-
} else if destConnID, err = buffer.ReadBytes(int(l)); err != nil {
87-
return "", errNotQuic
88-
}
89-
90-
if l, err := buffer.ReadByte(); err != nil {
91-
return "", errNotQuic
92-
} else if _, err := buffer.ReadBytes(int(l)); err != nil {
93-
return "", errNotQuic
94-
}
95-
96-
tokenLen, err := quicvarint.Read(buffer)
97-
if err != nil || tokenLen > uint64(len(b)) {
98-
return "", errNotQuic
99-
}
100-
101-
if _, err = buffer.ReadBytes(int(tokenLen)); err != nil {
102-
return "", errNotQuic
103-
}
104-
105-
packetLen, err := quicvarint.Read(buffer)
106-
if err != nil {
107-
return "", errNotQuic
108-
}
109-
110-
hdrLen := len(b) - buffer.Len()
111-
112-
var salt []byte
113-
if versionNumber == version1 {
114-
salt = quicSalt
115-
} else {
116-
salt = quicSaltOld
117-
}
118-
initialSecret := hkdf.Extract(crypto.SHA256.New, destConnID, salt)
119-
secret := hkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "client in", crypto.SHA256.Size())
120-
hpKey := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic hp", 16)
121-
block, err := aes.NewCipher(hpKey)
122-
if err != nil {
123-
return "", err
124-
}
125-
126-
cache := buf.NewPacket()
127-
defer cache.Release()
128-
129-
mask := cache.Extend(block.BlockSize())
130-
block.Encrypt(mask, b[hdrLen+4:hdrLen+4+16])
131-
firstByte := b[0]
132-
// Encrypt/decrypt first byte.
133-
if isLongHeader {
134-
// Long header: 4 bits masked
135-
// High 4 bits are not protected.
136-
firstByte ^= mask[0] & 0x0f
137-
} else {
138-
// Short header: 5 bits masked
139-
// High 3 bits are not protected.
140-
firstByte ^= mask[0] & 0x1f
141-
}
142-
packetNumberLength := int(firstByte&0x3 + 1) // max = 4 (64-bit sequence number)
143-
extHdrLen := hdrLen + packetNumberLength
144-
145-
// copy to avoid modify origin data
146-
extHdr := cache.Extend(extHdrLen)
147-
copy(extHdr, b)
148-
extHdr[0] = firstByte
149-
150-
packetNumber := extHdr[hdrLen:extHdrLen]
151-
// Encrypt/decrypt packet number.
152-
for i := range packetNumber {
153-
packetNumber[i] ^= mask[1+i]
154-
}
155-
156-
if packetNumber[0] != 0 && packetNumber[0] != 1 {
157-
return "", errNotQuicInitial
158-
}
159-
160-
data := b[extHdrLen : int(packetLen)+hdrLen]
161-
162-
key := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic key", 16)
163-
iv := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic iv", 12)
164-
aesCipher, err := aes.NewCipher(key)
165-
if err != nil {
166-
return "", err
167-
}
168-
aead, err := cipher.NewGCM(aesCipher)
169-
if err != nil {
170-
return "", err
171-
}
172-
// We only decrypt once, so we do not need to XOR it back.
173-
// https://github.com/quic-go/qtls-go1-20/blob/e132a0e6cb45e20ac0b705454849a11d09ba5a54/cipher_suites.go#L496
174-
for i, b := range packetNumber {
175-
iv[len(iv)-len(packetNumber)+i] ^= b
176-
}
177-
dst := cache.Extend(len(data))
178-
decrypted, err := aead.Open(dst[:0], iv, data, extHdr)
179-
if err != nil {
180-
return "", err
181-
}
182-
buffer = buf.As(decrypted)
183-
184-
cryptoLen := uint(0)
185-
cryptoData := cache.Extend(buffer.Len())
186-
for i := 0; !buffer.IsEmpty(); i++ {
187-
frameType := byte(0x0) // Default to PADDING frame
188-
for frameType == 0x0 && !buffer.IsEmpty() {
189-
frameType, _ = buffer.ReadByte()
190-
}
191-
switch frameType {
192-
case 0x00: // PADDING frame
193-
case 0x01: // PING frame
194-
case 0x02, 0x03: // ACK frame
195-
if _, err = quicvarint.Read(buffer); err != nil { // Field: Largest Acknowledged
196-
return "", io.ErrUnexpectedEOF
197-
}
198-
if _, err = quicvarint.Read(buffer); err != nil { // Field: ACK Delay
199-
return "", io.ErrUnexpectedEOF
200-
}
201-
ackRangeCount, err := quicvarint.Read(buffer) // Field: ACK Range Count
202-
if err != nil {
203-
return "", io.ErrUnexpectedEOF
204-
}
205-
if _, err = quicvarint.Read(buffer); err != nil { // Field: First ACK Range
206-
return "", io.ErrUnexpectedEOF
207-
}
208-
for i := 0; i < int(ackRangeCount); i++ { // Field: ACK Range
209-
if _, err = quicvarint.Read(buffer); err != nil { // Field: ACK Range -> Gap
210-
return "", io.ErrUnexpectedEOF
211-
}
212-
if _, err = quicvarint.Read(buffer); err != nil { // Field: ACK Range -> ACK Range Length
213-
return "", io.ErrUnexpectedEOF
214-
}
215-
}
216-
if frameType == 0x03 {
217-
if _, err = quicvarint.Read(buffer); err != nil { // Field: ECN Counts -> ECT0 Count
218-
return "", io.ErrUnexpectedEOF
219-
}
220-
if _, err = quicvarint.Read(buffer); err != nil { // Field: ECN Counts -> ECT1 Count
221-
return "", io.ErrUnexpectedEOF
222-
}
223-
if _, err = quicvarint.Read(buffer); err != nil { //nolint:misspell // Field: ECN Counts -> ECT-CE Count
224-
return "", io.ErrUnexpectedEOF
225-
}
226-
}
227-
case 0x06: // CRYPTO frame, we will use this frame
228-
offset, err := quicvarint.Read(buffer) // Field: Offset
229-
if err != nil {
230-
return "", io.ErrUnexpectedEOF
231-
}
232-
length, err := quicvarint.Read(buffer) // Field: Length
233-
if err != nil || length > uint64(buffer.Len()) {
234-
return "", io.ErrUnexpectedEOF
235-
}
236-
if cryptoLen < uint(offset+length) {
237-
cryptoLen = uint(offset + length)
238-
}
239-
if _, err := buffer.Read(cryptoData[offset : offset+length]); err != nil { // Field: Crypto Data
240-
return "", io.ErrUnexpectedEOF
241-
}
242-
case 0x1c: // CONNECTION_CLOSE frame, only 0x1c is permitted in initial packet
243-
if _, err = quicvarint.Read(buffer); err != nil { // Field: Error Code
244-
return "", io.ErrUnexpectedEOF
245-
}
246-
if _, err = quicvarint.Read(buffer); err != nil { // Field: Frame Type
247-
return "", io.ErrUnexpectedEOF
248-
}
249-
length, err := quicvarint.Read(buffer) // Field: Reason Phrase Length
250-
if err != nil {
251-
return "", io.ErrUnexpectedEOF
252-
}
253-
if _, err := buffer.ReadBytes(int(length)); err != nil { // Field: Reason Phrase
254-
return "", io.ErrUnexpectedEOF
255-
}
256-
default:
257-
// Only above frame types are permitted in initial packet.
258-
// See https://www.rfc-editor.org/rfc/rfc9000.html#section-17.2.2-8
259-
return "", errNotQuicInitial
260-
}
261-
}
262-
263-
domain, err := ReadClientHello(cryptoData[:cryptoLen])
264-
if err != nil {
265-
return "", err
59+
func (sniffer *QuicSniffer) WrapperSender(packetSender constant.PacketSender, override bool) constant.PacketSender {
60+
return &quicConnection{
61+
sender: packetSender,
62+
buffer: make([]quicDataBlock, 0),
63+
chClose: make(chan struct{}),
64+
override: override,
26665
}
66+
}
26767

268-
return *domain, nil
68+
func (sniffer *QuicSniffer) SniffData(b []byte) (string, error) {
69+
return "", ErrorUnsupportedSniffer
26970
}
27071

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

0 commit comments

Comments
 (0)