forked from p4gefau1t/trojan-go
-
Notifications
You must be signed in to change notification settings - Fork 1
/
client.go
157 lines (141 loc) · 3.84 KB
/
client.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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package tls
import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"io"
"io/ioutil"
"strings"
utls "github.com/refraction-networking/utls"
"github.com/faireal/trojan-go/common"
"github.com/faireal/trojan-go/config"
"github.com/faireal/trojan-go/log"
"github.com/faireal/trojan-go/tunnel"
"github.com/faireal/trojan-go/tunnel/tls/fingerprint"
"github.com/faireal/trojan-go/tunnel/transport"
)
// Client is a tls client
type Client struct {
verify bool
sni string
ca *x509.CertPool
cipher []uint16
sessionTicket bool
reuseSession bool
fingerprint string
helloID utls.ClientHelloID
keyLogger io.WriteCloser
underlay tunnel.Client
}
func (c *Client) Close() error {
if c.keyLogger != nil {
c.keyLogger.Close()
}
return c.underlay.Close()
}
func (c *Client) DialPacket(tunnel.Tunnel) (tunnel.PacketConn, error) {
panic("not supported")
}
func (c *Client) DialConn(_ *tunnel.Address, overlay tunnel.Tunnel) (tunnel.Conn, error) {
conn, err := c.underlay.DialConn(nil, &Tunnel{})
if err != nil {
return nil, common.NewError("tls failed to dial conn").Base(err)
}
if c.fingerprint != "" {
// utls fingerprint
tlsConn := utls.UClient(conn, &utls.Config{
RootCAs: c.ca,
ServerName: c.sni,
InsecureSkipVerify: !c.verify,
KeyLogWriter: c.keyLogger,
}, c.helloID)
if err := tlsConn.Handshake(); err != nil {
return nil, common.NewError("tls failed to handshake with remote server").Base(err)
}
return &transport.Conn{
Conn: tlsConn,
}, nil
}
// golang default tls library
tlsConn := tls.Client(conn, &tls.Config{
InsecureSkipVerify: !c.verify,
ServerName: c.sni,
RootCAs: c.ca,
KeyLogWriter: c.keyLogger,
CipherSuites: c.cipher,
SessionTicketsDisabled: !c.sessionTicket,
})
err = tlsConn.Handshake()
if err != nil {
return nil, common.NewError("tls failed to handshake with remote server").Base(err)
}
return &transport.Conn{
Conn: tlsConn,
}, nil
}
// NewClient creates a tls client
func NewClient(ctx context.Context, underlay tunnel.Client) (*Client, error) {
cfg := config.FromContext(ctx, Name).(*Config)
helloID := utls.ClientHelloID{}
if cfg.TLS.Fingerprint != "" {
switch cfg.TLS.Fingerprint {
case "firefox":
helloID = utls.HelloFirefox_Auto
case "chrome":
helloID = utls.HelloChrome_Auto
case "ios":
helloID = utls.HelloIOS_Auto
default:
return nil, common.NewError("invalid fingerprint " + cfg.TLS.Fingerprint)
}
log.Info("tls fingerprint", cfg.TLS.Fingerprint, "applied")
}
if cfg.TLS.SNI == "" {
cfg.TLS.SNI = cfg.RemoteHost
log.Warn("tls sni is unspecified")
}
client := &Client{
underlay: underlay,
verify: cfg.TLS.Verify,
sni: cfg.TLS.SNI,
cipher: fingerprint.ParseCipher(strings.Split(cfg.TLS.Cipher, ":")),
sessionTicket: cfg.TLS.ReuseSession,
fingerprint: cfg.TLS.Fingerprint,
helloID: helloID,
}
if cfg.TLS.CertPath != "" {
caCertByte, err := ioutil.ReadFile(cfg.TLS.CertPath)
if err != nil {
return nil, common.NewError("failed to load cert file").Base(err)
}
client.ca = x509.NewCertPool()
ok := client.ca.AppendCertsFromPEM(caCertByte)
if !ok {
log.Warn("invalid cert list")
}
log.Info("using custom cert")
// print cert info
pemCerts := caCertByte
for len(pemCerts) > 0 {
var block *pem.Block
block, pemCerts = pem.Decode(pemCerts)
if block == nil {
break
}
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
continue
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
continue
}
log.Trace("issuer:", cert.Issuer, "subject:", cert.Subject)
}
}
if cfg.TLS.CertPath == "" {
log.Info("cert is unspecified, using default ca list")
}
log.Debug("tls client created")
return client, nil
}