-
Notifications
You must be signed in to change notification settings - Fork 0
/
tls_fingerprint.go
134 lines (110 loc) · 3.19 KB
/
tls_fingerprint.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package clienthellod
import (
"errors"
"fmt"
"net"
"sync"
"sync/atomic"
"time"
"github.com/refraction-networking/clienthellod/internal/utils"
)
const DEFAULT_TLSFINGERPRINT_EXPIRY = 5 * time.Second
// TLSFingerprinter can be used to fingerprint TLS connections.
type TLSFingerprinter struct {
mapClientHellos *sync.Map
timeout time.Duration
closed atomic.Bool
}
// NewTLSFingerprinter creates a new TLSFingerprinter.
func NewTLSFingerprinter() *TLSFingerprinter {
return &TLSFingerprinter{
mapClientHellos: new(sync.Map),
closed: atomic.Bool{},
}
}
// NewTLSFingerprinterWithTimeout creates a new TLSFingerprinter with a timeout.
func NewTLSFingerprinterWithTimeout(timeout time.Duration) *TLSFingerprinter {
return &TLSFingerprinter{
mapClientHellos: new(sync.Map),
timeout: timeout,
closed: atomic.Bool{},
}
}
// SetTimeout sets the timeout for the TLSFingerprinter.
func (tfp *TLSFingerprinter) SetTimeout(timeout time.Duration) {
tfp.timeout = timeout
}
// HandleMessage handles a message.
func (tfp *TLSFingerprinter) HandleMessage(from string, p []byte) error {
if tfp.closed.Load() {
return errors.New("TLSFingerprinter closed")
}
ch, err := UnmarshalClientHello(p)
if err != nil {
return err
}
tfp.mapClientHellos.Store(from, ch)
go func(timeoutOverride time.Duration, key string, oldCh *ClientHello) {
if timeoutOverride == time.Duration(0) {
<-time.After(DEFAULT_TLSFINGERPRINT_EXPIRY)
} else {
<-time.After(timeoutOverride)
}
// tfp.mapClientHellos.Delete(key)
tfp.mapClientHellos.CompareAndDelete(key, oldCh)
}(tfp.timeout, from, ch)
return nil
}
// HandleTCPConn handles a TCP connection.
func (tfp *TLSFingerprinter) HandleTCPConn(conn net.Conn) (rewindConn net.Conn, err error) {
if tfp.closed.Load() {
return nil, errors.New("TLSFingerprinter closed")
}
ch, err := ReadClientHello(conn)
if err != nil {
return nil, fmt.Errorf("failed to read ClientHello from connection: %w", err)
}
if err = ch.ParseClientHello(); err != nil {
return nil, fmt.Errorf("failed to parse ClientHello: %w", err)
}
tfp.mapClientHellos.Store(conn.RemoteAddr().String(), ch)
go func(timeoutOverride time.Duration, key string, oldCh *ClientHello) {
if timeoutOverride == time.Duration(0) {
<-time.After(DEFAULT_TLSFINGERPRINT_EXPIRY)
} else {
<-time.After(timeoutOverride)
}
// tfp.mapClientHellos.Delete(key)
tfp.mapClientHellos.CompareAndDelete(key, oldCh)
}(tfp.timeout, conn.RemoteAddr().String(), ch)
return utils.RewindConn(conn, ch.Raw())
}
// Peek looks up a ClientHello for a given key.
func (tfp *TLSFingerprinter) Peek(from string) *ClientHello {
ch, ok := tfp.mapClientHellos.Load(from)
if !ok {
return nil
}
clientHello, ok := ch.(*ClientHello)
if !ok {
return nil
}
return clientHello
}
// Pop looks up a ClientHello for a given key and deletes it from the
// fingerprinter if found.
func (tfp *TLSFingerprinter) Pop(from string) *ClientHello {
ch, ok := tfp.mapClientHellos.LoadAndDelete(from)
if !ok {
return nil
}
clientHello, ok := ch.(*ClientHello)
if !ok {
return nil
}
return clientHello
}
// Close closes the TLSFingerprinter.
func (tfp *TLSFingerprinter) Close() {
tfp.closed.Store(true)
}