This repository has been archived by the owner on Dec 7, 2023. It is now read-only.
/
utls.go
139 lines (120 loc) · 3.69 KB
/
utls.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
135
136
137
138
139
package tls
import (
"crypto/tls"
"net"
"github.com/MerlinKodo/clash-rev/log"
"github.com/mroth/weightedrand/v2"
utls "github.com/sagernet/utls"
)
type UConn struct {
*utls.UConn
}
type UClientHelloID struct {
*utls.ClientHelloID
}
var initRandomFingerprint UClientHelloID
var initUtlsClient string
func UClient(c net.Conn, config *tls.Config, fingerprint UClientHelloID) *UConn {
utlsConn := utls.UClient(c, copyConfig(config), utls.ClientHelloID{
Client: fingerprint.Client,
Version: fingerprint.Version,
Seed: fingerprint.Seed,
})
return &UConn{UConn: utlsConn}
}
func GetFingerprint(ClientFingerprint string) (UClientHelloID, bool) {
if ClientFingerprint == "none" {
return UClientHelloID{}, false
}
if initRandomFingerprint.ClientHelloID == nil {
initRandomFingerprint, _ = RollFingerprint()
}
if ClientFingerprint == "random" {
log.Debugln("use initial random HelloID:%s", initRandomFingerprint.Client)
return initRandomFingerprint, true
}
fingerprint, ok := Fingerprints[ClientFingerprint]
if ok {
log.Debugln("use specified fingerprint:%s", fingerprint.Client)
return fingerprint, ok
} else {
log.Warnln("wrong ClientFingerprint:%s", ClientFingerprint)
return UClientHelloID{}, false
}
}
func RollFingerprint() (UClientHelloID, bool) {
chooser, _ := weightedrand.NewChooser(
weightedrand.NewChoice("chrome", 6),
weightedrand.NewChoice("safari", 3),
weightedrand.NewChoice("ios", 2),
weightedrand.NewChoice("firefox", 1),
)
initClient := chooser.Pick()
log.Debugln("initial random HelloID:%s", initClient)
fingerprint, ok := Fingerprints[initClient]
return fingerprint, ok
}
var Fingerprints = map[string]UClientHelloID{
"chrome": {&utls.HelloChrome_Auto},
"firefox": {&utls.HelloFirefox_Auto},
"safari": {&utls.HelloSafari_Auto},
"ios": {&utls.HelloIOS_Auto},
"android": {&utls.HelloAndroid_11_OkHttp},
"edge": {&utls.HelloEdge_Auto},
"360": {&utls.Hello360_Auto},
"qq": {&utls.HelloQQ_Auto},
"random": {nil},
"randomized": {nil},
}
func init() {
weights := utls.DefaultWeights
weights.TLSVersMax_Set_VersionTLS13 = 1
weights.FirstKeyShare_Set_CurveP256 = 0
randomized := utls.HelloRandomized
randomized.Seed, _ = utls.NewPRNGSeed()
randomized.Weights = &weights
Fingerprints["randomized"] = UClientHelloID{&randomized}
}
func copyConfig(c *tls.Config) *utls.Config {
return &utls.Config{
RootCAs: c.RootCAs,
ServerName: c.ServerName,
InsecureSkipVerify: c.InsecureSkipVerify,
VerifyPeerCertificate: c.VerifyPeerCertificate,
}
}
// BuildWebsocketHandshakeState it will only send http/1.1 in its ALPN.
// Copy from https://github.com/XTLS/Xray-core/blob/main/transport/internet/tls/tls.go
func (c *UConn) BuildWebsocketHandshakeState() error {
// Build the handshake state. This will apply every variable of the TLS of the
// fingerprint in the UConn
if err := c.BuildHandshakeState(); err != nil {
return err
}
// Iterate over extensions and check for utls.ALPNExtension
hasALPNExtension := false
for _, extension := range c.Extensions {
if alpn, ok := extension.(*utls.ALPNExtension); ok {
hasALPNExtension = true
alpn.AlpnProtocols = []string{"http/1.1"}
break
}
}
if !hasALPNExtension { // Append extension if doesn't exists
c.Extensions = append(c.Extensions, &utls.ALPNExtension{AlpnProtocols: []string{"http/1.1"}})
}
// Rebuild the client hello
if err := c.BuildHandshakeState(); err != nil {
return err
}
return nil
}
func SetGlobalUtlsClient(Client string) {
initUtlsClient = Client
}
func HaveGlobalFingerprint() bool {
return len(initUtlsClient) != 0 && initUtlsClient != "none"
}
func GetGlobalFingerprint() string {
return initUtlsClient
}