Skip to content

Commit 566d698

Browse files
committed
Improve QUIC sniffer
1 parent 041932d commit 566d698

30 files changed

+1013
-237
lines changed

adapter/inbound.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,16 @@ type InboundContext struct {
3131
Network string
3232
Source M.Socksaddr
3333
Destination M.Socksaddr
34-
Domain string
35-
Protocol string
3634
User string
3735
Outbound string
3836

37+
// sniffer
38+
39+
Protocol string
40+
Domain string
41+
Client string
42+
SniffContext any
43+
3944
// cache
4045

4146
InboundDetour string

common/ja3/LICENSE

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
BSD 3-Clause License
2+
3+
Copyright (c) 2018, Open Systems AG
4+
All rights reserved.
5+
6+
Redistribution and use in source and binary forms, with or without
7+
modification, are permitted provided that the following conditions are met:
8+
9+
* Redistributions of source code must retain the above copyright notice, this
10+
list of conditions and the following disclaimer.
11+
12+
* Redistributions in binary form must reproduce the above copyright notice,
13+
this list of conditions and the following disclaimer in the documentation
14+
and/or other materials provided with the distribution.
15+
16+
* Neither the name of the copyright holder nor the names of its
17+
contributors may be used to endorse or promote products derived from
18+
this software without specific prior written permission.
19+
20+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

common/ja3/README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# JA3
2+
3+
mod from: https://github.com/open-ch/ja3

common/ja3/error.go

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) 2018, Open Systems AG. All rights reserved.
2+
//
3+
// Use of this source code is governed by a BSD-style license
4+
// that can be found in the LICENSE file in the root of the source
5+
// tree.
6+
7+
package ja3
8+
9+
import "fmt"
10+
11+
// Error types
12+
const (
13+
LengthErr string = "length check %v failed"
14+
ContentTypeErr string = "content type not matching"
15+
VersionErr string = "version check %v failed"
16+
HandshakeTypeErr string = "handshake type not matching"
17+
SNITypeErr string = "SNI type not supported"
18+
)
19+
20+
// ParseError can be encountered while parsing a segment
21+
type ParseError struct {
22+
errType string
23+
check int
24+
}
25+
26+
func (e *ParseError) Error() string {
27+
if e.errType == LengthErr || e.errType == VersionErr {
28+
return fmt.Sprintf(e.errType, e.check)
29+
}
30+
return fmt.Sprint(e.errType)
31+
}

common/ja3/ja3.go

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright (c) 2018, Open Systems AG. All rights reserved.
2+
//
3+
// Use of this source code is governed by a BSD-style license
4+
// that can be found in the LICENSE file in the root of the source
5+
// tree.
6+
7+
package ja3
8+
9+
import (
10+
"crypto/md5"
11+
"encoding/hex"
12+
13+
"golang.org/x/exp/slices"
14+
)
15+
16+
type ClientHello struct {
17+
Version uint16
18+
CipherSuites []uint16
19+
Extensions []uint16
20+
EllipticCurves []uint16
21+
EllipticCurvePF []uint8
22+
Versions []uint16
23+
SignatureAlgorithms []uint16
24+
ServerName string
25+
ja3ByteString []byte
26+
ja3Hash string
27+
}
28+
29+
func (j *ClientHello) Equals(another *ClientHello, ignoreExtensionsSequence bool) bool {
30+
if j.Version != another.Version {
31+
return false
32+
}
33+
if !slices.Equal(j.CipherSuites, another.CipherSuites) {
34+
return false
35+
}
36+
if !ignoreExtensionsSequence && !slices.Equal(j.Extensions, another.Extensions) {
37+
return false
38+
}
39+
if ignoreExtensionsSequence && !slices.Equal(j.Extensions, another.sortedExtensions()) {
40+
return false
41+
}
42+
if !slices.Equal(j.EllipticCurves, another.EllipticCurves) {
43+
return false
44+
}
45+
if !slices.Equal(j.EllipticCurvePF, another.EllipticCurvePF) {
46+
return false
47+
}
48+
if !slices.Equal(j.SignatureAlgorithms, another.SignatureAlgorithms) {
49+
return false
50+
}
51+
return true
52+
}
53+
54+
func (j *ClientHello) sortedExtensions() []uint16 {
55+
extensions := make([]uint16, len(j.Extensions))
56+
copy(extensions, j.Extensions)
57+
slices.Sort(extensions)
58+
return extensions
59+
}
60+
61+
func Compute(payload []byte) (*ClientHello, error) {
62+
ja3 := ClientHello{}
63+
err := ja3.parseSegment(payload)
64+
return &ja3, err
65+
}
66+
67+
func (j *ClientHello) String() string {
68+
if j.ja3ByteString == nil {
69+
j.marshalJA3()
70+
}
71+
return string(j.ja3ByteString)
72+
}
73+
74+
func (j *ClientHello) Hash() string {
75+
if j.ja3ByteString == nil {
76+
j.marshalJA3()
77+
}
78+
if j.ja3Hash == "" {
79+
h := md5.Sum(j.ja3ByteString)
80+
j.ja3Hash = hex.EncodeToString(h[:])
81+
}
82+
return j.ja3Hash
83+
}

0 commit comments

Comments
 (0)