fix tls alpn problem, randomize timeout

This commit is contained in:
p4gefau1t
2020-04-29 00:11:16 -04:00
parent 7c881c2a2a
commit c27f163b9d
12 changed files with 171 additions and 186 deletions
+1
View File
@@ -2,6 +2,7 @@ on:
push:
tags:
- 'v*'
name: release-build
jobs:
build:
runs-on: ubuntu-latest
+9 -9
View File
@@ -1,10 +1,10 @@
# Trojan-Go
使用Golang实现的完整Trojan代理,和Trojan协议以及原版的配置文件格式兼容。安全,高效,轻巧,易用。
使用Go实现的完整Trojan代理,和Trojan协议以及Trojan-GFW版本的配置文件格式兼容。安全,高效,轻巧,易用。
支持使用[多路复用](#多路复用)提升并发性能,使用[路由模块](#路由模块)实现国内直连。
支持CDN[流量中转](#Websocket)(基于WebSocket over TLS/SSL)
支持[CDN流量中转](#Websocket)(基于WebSocket over TLS/SSL)
支持使用ACME协议从Let's Encrypt[自动申请和更新](#证书申请)HTTPS证书,只需提供域名和邮箱。
@@ -28,7 +28,7 @@ Trojan-Go支持并且兼容原版Trojan-GFW的绝大多数功能,包括但不
- TCP性能方面的选项,如TCP Fast Open,端口复用等
同时,Trojan-Go还有更多高效易用的功能特性:
同时,Trojan-Go还扩展了更多高效易用的功能特性:
- 简易模式,快速部署使用
@@ -166,7 +166,7 @@ Linux下,绑定80和443端口需要root权限,因此你需要使用sudo执
如果证书过期了,使用
```
sudo ./trojan-go -cert renew
sudo ./trojan-go -autocert renew
```
更新证书,确保上面提到的四个文件在trojan-go所在目录,运行后trojan-go将自动更新证书文件。
@@ -187,7 +187,7 @@ Trojan-Go支持使用TLS+Websocket承载Trojan协议,使得利用CDN进行流
}
```
完整的选项说明参见[Github Pages](https://p4gefau1t.github.io/trojan-go)。
完整的选项说明参见[Trojan-Go 文档](https://p4gefau1t.github.io/trojan-go)。
服务端可以省略```hostname```, 但是服务器和客户端的```path```必须相同。服务器开启Websocket支持后可以同时支持Websocket和一般Trojan流量,未配置Websocket选项的客户端依然可以正常使用。
@@ -211,7 +211,7 @@ Trojan-Go支持多路复用([smux](https://github.com/xtaci/smux))。通过使
}
```
完整的选项说明参见[Github Pages](https://p4gefau1t.github.io/trojan-go)。
完整的选项说明参见[Trojan-Go 文档](https://p4gefau1t.github.io/trojan-go)。
你只需要设置客户端的配置文件即可,服务端会自动检测是否启用多路复用并提供支持。
@@ -251,7 +251,7 @@ Trojan-Go的客户端内建一个简单实用的路由模块用以方便实现
其中```bypass```,```block```, ```proxy```字段中填入相应的列表文件或者geo数据库tag。列表文件每行是一个域名或者IP地址段(CIDR)。geo数据库geoip和geosite为IP数据库和域名数据库。一旦匹配,则执行相应策略。
完整的选项说明参见[Github Pages](https://p4gefau1t.github.io/trojan-go)。
完整的选项说明参见[Trojan-Go 文档](https://p4gefau1t.github.io/trojan-go)。
下面是一个实现国内直连的选项,它将绕过中国大陆IP地址,中国大陆域名,以及内网IP等保留的私有IP地址,直接连接远端而不通过隧道代理。
@@ -270,7 +270,7 @@ Trojan-Go的客户端内建一个简单实用的路由模块用以方便实现
## 构建
确保你的Golang版本 >= 1.14,推荐使用snap安装Golang保持与上游同步。
确保你的Go版本 >= 1.14,推荐使用snap安装Go保持与上游同步。
```
git clone https://github.com/p4gefau1t/trojan-go.git
@@ -278,7 +278,7 @@ cd trojan-go
go build
```
Golang支持通过设置环境变量进行交叉编译,例如
Go支持通过设置环境变量进行交叉编译,例如
```
CGO_ENABLE=0 GOOS=windows GOARCH=amd64 go build -o trojan-go.exe
-24
View File
@@ -15,7 +15,6 @@ import (
"github.com/p4gefau1t/trojan-go/common"
"github.com/p4gefau1t/trojan-go/log"
utls "github.com/refraction-networking/utls"
"golang.org/x/crypto/pbkdf2"
)
@@ -205,25 +204,6 @@ func loadClientConfig(config *GlobalConfig) error {
log.Warn(err)
}
//tls settings
if config.TLS.Fingerprint != "" && config.TLS.Fingerprint != "auto" {
table := map[string]*utls.ClientHelloID{
"chrome": &utls.HelloChrome_Auto,
"firefox": &utls.HelloFirefox_Auto,
"ios": &utls.HelloIOS_Auto,
"randomized": &utls.HelloRandomized,
"randomized_alpn": &utls.HelloRandomizedALPN,
"randomized_no_alpn": &utls.HelloRandomizedNoALPN,
}
id, found := table[config.TLS.Fingerprint]
if found {
log.Debug("tls fingerprint loaded:", id.Str())
config.TLS.ClientHelloID = id
} else {
log.Warn("invalid tls fingerprint:", config.TLS.Fingerprint, ", using default fingerprint")
}
}
if config.TLS.SNI == "" {
log.Warn("SNI is unspecified, using remote_addr as SNI")
config.TLS.SNI = config.RemoteHost
@@ -333,10 +313,6 @@ func ParseJSON(data []byte) (*GlobalConfig, error) {
config.TLS.VerifyHostname = true
config.TLS.SessionTicket = true
config.TLS.ReuseSession = true
config.TLS.ALPN = []string{
"http/1.1",
"h2",
}
config.Mux.IdleTimeout = 60
config.Mux.Concurrency = 8
config.MySQL.CheckRate = 60
+1 -1
View File
@@ -8,7 +8,7 @@ weight: 10
这里是Trojan-Go的文档,你可以在左侧的导航栏中找到一些使用技巧,以及完整的配置文件说明。
Trojan-Go是使用Golang实现的完整的Trojan代理,和Trojan协议以及原版的配置文件格式兼容。
Trojan-Go是使用Go语言实现的完整的Trojan代理,和Trojan协议以及原版的配置文件格式兼容。
Trojan-Go的的首要目标是保障传输安全性和隐蔽性。在此前提下,尽可能提升传输性能和易用性。
+5 -2
View File
@@ -95,9 +95,10 @@ sudo ./trojan-go -autocert renew
"remote_addr": "your_awesome_server",
"remote_port": 443,
"password": [
"your_awesome_password"
"your_awesome_password"
],
"ssl": {
"fingerprint": "firefox",
"sni": "your_domain_name"
}
}
@@ -107,4 +108,6 @@ sudo ./trojan-go -autocert renew
如果你在```remote_addr```中填写的是域名,```sni```可以省略。```sni```字段应当填写你申请证书的对应域名,或者你自己签发证书时证书的Common Name,而且必须一致。注意,```sni```字段目前的在TLS协议中是规定**明文传送**的(目的是使服务器提供相应证书),所以不要填写类似google.com等已经被封锁的域名,否则很有可能导致你的服务器也被封锁。
更多关于配置文件的信息,可以在左侧导航栏中找到相应介绍。
```fingerprint```将设置Trojan-Go伪造Firefox浏览器的TLS请求指纹,使得Trojan-Go的流量混杂在正常的HTTPS流量中无法被识别。还可以设置为```ios```,```chrome```等。
更多关于配置文件的信息,可以在左侧导航栏中找到相应介绍。
+5 -8
View File
@@ -47,10 +47,7 @@ weight: 30
"curves": "",
"prefer_server_cipher": false,
"sni": "",
"alpn": [
"http/1.1",
"h2"
]
"alpn": [],
"session_ticket": true,
"reuse_session": true,
"plain_http_response": "",
@@ -128,11 +125,11 @@ weight: 30
```prefer_server_cipher```客户端是否偏好选择服务端在协商中提供的密码学套件。
```cipher```和```cipher13```指TLS使用的密码学套件。只有在你明确知道自己在做什么的情况下,才应该去填写此项以修改trojan-go使用的TLS密码学套件。**正常情况下,你应该将其留空或者不填**,trojan-go会根据当前硬件平台以及远端的情况,自动选择最合适的加密算法以提升性能和安全性。如果需要填写,密码学套件名用分号(":")分隔。Golang的TLS库中弃用了TLS1.2中不安全的密码学套件,并完全支持TLS1.3。默认情况下,trojan-go将优先使用更安全的TLS1.3
```cipher```和```cipher13```指TLS使用的密码学套件。只有在你明确知道自己在做什么的情况下,才应该去填写此项以修改trojan-go使用的TLS密码学套件。**正常情况下,你应该将其留空或者不填**,trojan-go会根据当前硬件平台以及远端的情况,自动选择最合适的加密算法以提升性能和安全性。如果需要填写,密码学套件名用分号(":")分隔。Go的TLS库中弃用了TLS1.2中不安全的密码学套件,并完全支持TLS1.3。默认情况下,trojan-go将优先使用更安全的TLS1.3
```curves```指定TLS在ECDHE中偏好使用的椭圆曲线。只有你明确知道自己在做什么的情况下,才应该填写此项。曲线名称用分号(":")分隔。
```fingerprint```用于指定TLS Client Hello指纹伪造类型。trojan-go使用[utls](https://github.com/refraction-networking/utls)进行指纹伪造,避免遭到针对golang的TLS库的识别。合法的值有
```fingerprint```用于指定TLS Client Hello指纹伪造类型,以抵抗GFW对于TLS Client Hello指纹的特征识别和阻断。trojan-go使用[utls](https://github.com/refraction-networking/utls)进行指纹伪造。合法的值有
- ""(空),默认,不使用指纹伪造
@@ -146,9 +143,9 @@ weight: 30
- "randomized",随机指纹
一旦指纹的值被设置,```cipher```,```curves```,```alpn```等可能影响指纹的字段将使用该指纹的特定设置覆写。设置该选项有可能导致与服务器密钥协商失败,使用auto选项将自动尝试所有指纹并选出合适的一项。
一旦指纹的值被设置,```cipher```,```curves```,```alpn```,```session_ticket```等有可能影响指纹的字段将使用该指纹的特定设置覆写。设置该选项有可能导致与服务器密钥协商失败,使用auto选项将自动尝试所有指纹并选出合适的一项。
```plain_http_response```指定了当TLS握手失败时,明文发送的原始数据(原始TCP数据),这个字段填入该文件路径。推荐使用```fallback_port```而不是该字段。
```plain_http_response```指定了当TLS握手失败时,明文发送的原始数据(原始TCP数据)。这个字段填入该文件路径。推荐使用```fallback_port```而不是该字段。
```fallback_port```指TLS握手失败时,trojan-go将该连接代理到该端口上。这是trojan-go的特性,以便更好地隐蔽Trojan服务器,抵抗GFW的主动检测,使得服务器的443端口在遭遇非TLS协议的探测时,行为与正常服务器完全一致。当服务器接受了一个连接但无法进行TLS握手时,如果```fallback_port```不为空,则流量将会被代理至remote_addr:fallback_port。例如,你可以在本地使用nginx开启一个https服务,当你的服务器443端口被非TLS协议请求时(比如http请求),trojan-go将代理至本地https服务器,nginx将使用http协议明文返回一个400 Bad Request页面。你可以通过使用浏览器访问 http://your_domain_name.com:443 进行验证。
+1 -1
View File
@@ -4,7 +4,7 @@ draft: false
weight: 10
---
编译需要Golang版本号高于0.14.x,请在编译前确认你的编译器版本。推荐使用snap安装和更新go。
编译需要Go版本号高于0.14.x,请在编译前确认你的编译器版本。推荐使用snap安装和更新go。
编译过程非常简单
+7 -2
View File
@@ -176,8 +176,13 @@ func ParsePort(addr net.Addr) (uint16, error) {
var timeout time.Duration
func RandomizedTimeout(conn net.Conn) {
conn.SetDeadline(time.Now().Add(timeout))
func GetRandomTimeoutDuration() time.Duration {
offset := time.Duration(rand.Intn(3000)) * time.Millisecond
return timeout + offset
}
func SetRandomizedTimeout(conn net.Conn) {
conn.SetDeadline(time.Now().Add(GetRandomTimeoutDuration()))
}
func CancelTimeout(conn net.Conn) {
+15 -13
View File
@@ -190,9 +190,9 @@ func NewInboundWebsocket(ctx context.Context, conn net.Conn, config *conf.Global
defer rewindConn.R.StopBuffering()
bufrw := bufio.NewReadWriter(bufio.NewReader(rewindConn), bufio.NewWriter(rewindConn))
httpRequest, obfErr := http.ReadRequest(bufrw.Reader)
if obfErr != nil {
log.Debug(common.NewError("not a http request:").Base(obfErr))
httpRequest, err := http.ReadRequest(bufrw.Reader)
if err != nil {
log.Debug(common.NewError("not a http request:").Base(err))
return nil, nil
}
@@ -205,7 +205,7 @@ func NewInboundWebsocket(ctx context.Context, conn net.Conn, config *conf.Global
shadowMan.CommitScapegoat(&shadow.Scapegoat{
Conn: rewindConn,
ShadowAddress: config.RemoteAddress,
Info: "not a valid http upgrade request from " + conn.RemoteAddr().String(),
Info: "invalid http upgrade request from " + conn.RemoteAddr().String(),
})
return nil, common.NewError("invalid ws url or hostname")
}
@@ -215,7 +215,7 @@ func NewInboundWebsocket(ctx context.Context, conn net.Conn, config *conf.Global
rewindConn.R.SetBufferSize(0)
url := "wss://" + config.Websocket.HostName + config.Websocket.Path
origin := "https://" + config.Websocket.HostName
wsConfig, obfErr := websocket.NewConfig(url, origin)
wsConfig, err := websocket.NewConfig(url, origin)
handshaked := make(chan struct{})
@@ -253,7 +253,7 @@ func NewInboundWebsocket(ctx context.Context, conn net.Conn, config *conf.Global
return nil, common.NewError("failed to perform websocket handshake")
}
//use ws to transport
//use ws to transfer
var transport net.Conn
rewindConn = common.NewRewindConn(wsConn)
transport = rewindConn
@@ -266,19 +266,19 @@ func NewInboundWebsocket(ctx context.Context, conn net.Conn, config *conf.Global
log.Debug("ws obfs")
//deadline for sending the iv and hash
rewindConn.SetDeadline(time.Now().Add(protocol.TCPTimeout))
transport, obfErr = NewInboundObfReadWriteCloser(config.Websocket.ObfuscationKey, transport)
rewindConn.SetDeadline(time.Time{})
protocol.SetRandomizedTimeout(rewindConn)
transport, err = NewInboundObfReadWriteCloser(config.Websocket.ObfuscationKey, transport)
protocol.CancelTimeout(rewindConn)
if obfErr != nil {
if err != nil {
rewindConn.R.Rewind()
//proxy this to our own ws server
obfErr = common.NewError("remote websocket conn:" + conn.RemoteAddr().String() + "didn't send any valid iv/hash").Base(obfErr)
err = common.NewError("remote websocket " + conn.RemoteAddr().String() + "didn't send any valid iv").Base(err)
goat, err := getWebsocketScapegoat(
config,
url,
origin,
obfErr.Error(),
err.Error(),
rewindConn,
)
if err != nil {
@@ -287,7 +287,7 @@ func NewInboundWebsocket(ctx context.Context, conn net.Conn, config *conf.Global
} else {
shadowMan.CommitScapegoat(goat)
}
return nil, obfErr
return nil, err
}
}
if !config.Websocket.DoubleTLS {
@@ -301,6 +301,7 @@ func NewInboundWebsocket(ctx context.Context, conn net.Conn, config *conf.Global
SessionTicketsDisabled: !config.TLS.SessionTicket,
}
tlsConn := tls.Server(transport, tlsConfig)
protocol.SetRandomizedTimeout(tlsConn)
if tlsErr := tlsConn.Handshake(); tlsErr != nil {
rewindConn.R.Rewind()
//proxy this to our own ws server
@@ -320,6 +321,7 @@ func NewInboundWebsocket(ctx context.Context, conn net.Conn, config *conf.Global
}
return nil, tlsErr
}
protocol.CancelTimeout(tlsConn)
rewindConn.R.SetBufferSize(0)
return tlsConn, nil
}
+1
View File
@@ -55,6 +55,7 @@ func (m *MuxManager) newMuxClient() (*muxClientInfo, error) {
}
conn, err := trojan.NewOutboundConnSession(req, rwc, m.config)
if err != nil {
rwc.Close()
log.Error(common.NewError("failed to dial tls tunnel").Base(err))
return nil, err
}
+124 -124
View File
@@ -6,107 +6,25 @@ import (
"math/rand"
"net"
"sync"
"time"
"github.com/p4gefau1t/trojan-go/common"
"github.com/p4gefau1t/trojan-go/conf"
"github.com/p4gefau1t/trojan-go/log"
"github.com/p4gefau1t/trojan-go/protocol"
"github.com/p4gefau1t/trojan-go/protocol/trojan"
"github.com/p4gefau1t/trojan-go/sockopt"
utls "github.com/refraction-networking/utls"
)
type Roller struct {
HelloIDs []utls.ClientHelloID
HelloIDMu sync.Mutex
WorkingHelloID *utls.ClientHelloID
TCPDialTimeout time.Duration
TLSHandshakeTimeout time.Duration
TLSConfig *utls.Config
}
// NewRoller creates Roller object with default range of HelloIDs to cycle through until a
// working/unblocked one is found.
func NewRoller(config *utls.Config) *Roller {
tcpDialTimeoutInc := rand.Intn(14)
tcpDialTimeoutInc = 7 + tcpDialTimeoutInc
tlsHandshakeTimeoutInc := rand.Intn(20)
tlsHandshakeTimeoutInc = 11 + tlsHandshakeTimeoutInc
return &Roller{
HelloIDs: []utls.ClientHelloID{
utls.HelloChrome_Auto,
utls.HelloFirefox_Auto,
utls.HelloIOS_Auto,
utls.HelloRandomized,
},
TCPDialTimeout: time.Second * time.Duration(tcpDialTimeoutInc),
TLSHandshakeTimeout: time.Second * time.Duration(tlsHandshakeTimeoutInc),
TLSConfig: config,
}
}
func (c *Roller) Dial(network, addr, serverName string) (*utls.UConn, error) {
helloIDs := make([]utls.ClientHelloID, len(c.HelloIDs))
copy(helloIDs, c.HelloIDs)
rand.Shuffle(len(c.HelloIDs), func(i, j int) {
helloIDs[i], helloIDs[j] = helloIDs[j], helloIDs[i]
})
c.HelloIDMu.Lock()
workingHelloID := c.WorkingHelloID // keep using same helloID, if it works
c.HelloIDMu.Unlock()
if workingHelloID != nil {
helloIDFound := false
for i, ID := range helloIDs {
if ID == *workingHelloID {
helloIDs[i] = helloIDs[0]
helloIDs[0] = *workingHelloID // push working hello ID first
helloIDFound = true
break
}
}
if !helloIDFound {
helloIDs = append([]utls.ClientHelloID{*workingHelloID}, helloIDs...)
}
}
var tcpConn net.Conn
var err error
for _, helloID := range helloIDs {
tcpConn, err = net.DialTimeout(network, addr, c.TCPDialTimeout)
if err != nil {
return nil, err // on tcp Dial failure return with error right away
}
client := utls.UClient(tcpConn, c.TLSConfig, helloID)
client.SetSNI(serverName)
client.SetDeadline(time.Now().Add(c.TLSHandshakeTimeout))
err = client.Handshake()
client.SetDeadline(time.Time{}) // unset timeout
if err != nil {
log.Debug("hello id", helloID.Str(), "failed, err:", err)
continue // on tls Dial error keep trying HelloIDs
}
log.Debug("found avaliable hello id:", helloID.Str())
c.HelloIDMu.Lock()
c.WorkingHelloID = &client.ClientHelloID
c.HelloIDMu.Unlock()
return client, err
}
return nil, err
}
type TLSManager struct {
TransportManager
utlsConfig *utls.Config
tlsConfig *tls.Config
autoClientHelloID *utls.ClientHelloID
config *conf.GlobalConfig
roller *Roller
helloIDs []utls.ClientHelloID
helloIDLock sync.Mutex
workingHelloID *utls.ClientHelloID
utlsConfig *utls.Config
tlsConfig *tls.Config
config *conf.GlobalConfig
}
func (m *TLSManager) printConnInfo(conn net.Conn) {
@@ -139,57 +57,114 @@ func (m *TLSManager) printConnInfo(conn net.Conn) {
}
}
func (m *TLSManager) DialToServer() (io.ReadWriteCloser, error) {
func (m *TLSManager) dialTCP() (net.Conn, error) {
network := "tcp"
if m.config.TCP.PreferIPV4 {
network = "tcp4"
}
var tlsConn net.Conn
var err error
if m.config.TLS.Fingerprint == "auto" {
//use utls roller
tlsConn, err = m.roller.Dial(network, m.config.RemoteAddress.String(), m.config.TLS.SNI)
} else if m.config.TLS.ClientHelloID != nil {
//use utls fixed fingerprint
log.Debug("using fingerprint", m.config.TLS.ClientHelloID.Str())
var conn net.Conn
conn, err = net.Dial(network, m.config.RemoteAddress.String())
tlsConn = utls.UClient(conn, m.utlsConfig, *m.config.TLS.ClientHelloID)
} else {
//normal golang tls
conn, err := net.Dial(network, m.config.RemoteAddress.String())
conn, err := net.DialTimeout(network, m.config.RemoteAddress.String(), protocol.GetRandomTimeoutDuration())
if err != nil {
return nil, common.NewError("failed to dial to remote server").Base(err)
}
if err := sockopt.ApplyTCPConnOption(conn.(*net.TCPConn), &m.config.TCP); err != nil {
log.Warn(common.NewError("failed to apply tcp options").Base(err))
}
return conn, nil
}
func (m *TLSManager) dialTLSWithFakeFingerprint() (*utls.UConn, error) {
helloIDs := make([]utls.ClientHelloID, len(m.helloIDs))
copy(helloIDs, m.helloIDs)
rand.Shuffle(len(m.helloIDs), func(i, j int) {
helloIDs[i], helloIDs[j] = helloIDs[j], helloIDs[i]
})
m.helloIDLock.Lock()
workingHelloID := m.workingHelloID // keep using same helloID, if it works
m.helloIDLock.Unlock()
if workingHelloID != nil {
helloIDFound := false
for i, ID := range helloIDs {
if ID == *workingHelloID {
helloIDs[i] = helloIDs[0]
helloIDs[0] = *workingHelloID // push working hello ID first
helloIDFound = true
break
}
}
if !helloIDFound {
helloIDs = append([]utls.ClientHelloID{*workingHelloID}, helloIDs...)
helloIDs[0], helloIDs[len(helloIDs)-1] = helloIDs[len(helloIDs)-1], helloIDs[0]
}
}
for _, helloID := range helloIDs {
tcpConn, err := m.dialTCP()
if err != nil {
return nil, err // on tcp Dial failure return with error right away
}
client := utls.UClient(tcpConn, m.utlsConfig, helloID)
if m.config.Websocket.Enabled {
// HACK disable alpn (http/1.1, h2) to support websocket
client.HandshakeState.Hello.AlpnProtocols = []string{}
}
protocol.SetRandomizedTimeout(client)
err = client.Handshake()
protocol.CancelTimeout(client)
if err != nil {
log.Debug("hello id", helloID.Str(), "failed, err:", err)
continue // on tls Dial error keep trying HelloIDs
}
log.Debug("found avaliable hello id:", helloID.Str())
m.helloIDLock.Lock()
m.workingHelloID = &client.ClientHelloID
m.helloIDLock.Unlock()
return client, err
}
return nil, common.NewError("all client hello id tried but failed")
}
func (m *TLSManager) DialToServer() (io.ReadWriteCloser, error) {
var transport net.Conn
if m.config.TLS.Fingerprint != "" {
//use utls fingerprints
tlsConn, err := m.dialTLSWithFakeFingerprint()
if err != nil {
return nil, err
}
err = sockopt.ApplyTCPConnOption(conn.(*net.TCPConn), &m.config.TCP)
m.printConnInfo(tlsConn)
transport = tlsConn
} else {
//normal golang tls
tcpConn, err := m.dialTCP()
if err != nil {
return nil, common.NewError("failed to apply tcp option").Base(err)
return nil, err
}
tlsConn = tls.Client(conn, m.tlsConfig)
err = tlsConn.(*tls.Conn).Handshake()
}
if err != nil {
return nil, common.NewError("cannot dial to the remote server").Base(err)
}
m.printConnInfo(tlsConn)
var transport io.ReadWriteCloser = tlsConn
if m.config.Websocket.Enabled {
ws, err := trojan.NewOutboundWebosocket(tlsConn, m.config)
tlsConn := tls.Client(tcpConn, m.tlsConfig)
err = tlsConn.Handshake()
if err != nil {
return nil, err
}
transport = tlsConn
m.printConnInfo(tlsConn)
}
if m.config.Websocket.Enabled {
ws, err := trojan.NewOutboundWebosocket(transport, m.config)
if err != nil {
transport.Close()
return nil, common.NewError("failed to start websocket connection").Base(err)
}
transport = ws
return ws, nil
}
return transport, nil
}
func NewTLSManager(config *conf.GlobalConfig) *TLSManager {
utlsConfig := &utls.Config{
RootCAs: config.TLS.CertPool,
ServerName: config.TLS.SNI,
InsecureSkipVerify: !config.TLS.Verify,
SessionTicketsDisabled: !config.TLS.SessionTicket,
ClientSessionCache: utls.NewLRUClientSessionCache(-1),
RootCAs: config.TLS.CertPool,
ServerName: config.TLS.SNI,
InsecureSkipVerify: !config.TLS.Verify,
}
tlsConfig := &tls.Config{
CipherSuites: config.TLS.CipherSuites,
@@ -199,13 +174,38 @@ func NewTLSManager(config *conf.GlobalConfig) *TLSManager {
SessionTicketsDisabled: !config.TLS.SessionTicket,
CurvePreferences: config.TLS.CurvePreferences,
NextProtos: config.TLS.ALPN,
ClientSessionCache: tls.NewLRUClientSessionCache(-1),
ClientSessionCache: tls.NewLRUClientSessionCache(192),
}
m := &TLSManager{
config: config,
utlsConfig: utlsConfig,
tlsConfig: tlsConfig,
roller: NewRoller(utlsConfig),
}
if config.TLS.Fingerprint == "auto" {
m.helloIDs = []utls.ClientHelloID{
utls.HelloChrome_Auto,
utls.HelloFirefox_Auto,
utls.HelloIOS_Auto,
utls.HelloRandomizedNoALPN,
}
} else if config.TLS.Fingerprint != "" {
table := map[string]*utls.ClientHelloID{
"chrome": &utls.HelloChrome_Auto,
"firefox": &utls.HelloFirefox_Auto,
"ios": &utls.HelloIOS_Auto,
"randomized": &utls.HelloRandomizedNoALPN,
}
id, found := table[config.TLS.Fingerprint]
if found {
log.Debug("tls fingerprint loaded:", id.Str())
m.helloIDs = []utls.ClientHelloID{*id}
} else {
log.Warn("invalid tls fingerprint:", config.TLS.Fingerprint, ", using default fingerprint")
config.TLS.Fingerprint = ""
}
}
return m
}
+2 -2
View File
@@ -63,7 +63,7 @@ func (s *Server) handleMuxConn(stream *smux.Stream) {
}
func (s *Server) handleConn(conn *tls.Conn) {
protocol.RandomizedTimeout(conn)
protocol.SetRandomizedTimeout(conn)
inboundConn, req, err := trojan.NewInboundConnSession(s.ctx, conn, s.config, s.auth, s.shadow)
if err != nil {
//once the auth is failed, the conn will be took over by shadow manager. don't close it
@@ -171,7 +171,7 @@ func (s *Server) Run() error {
log.Info("conn accepted from", conn.RemoteAddr())
go func(conn net.Conn) {
//using randomized timeout
protocol.RandomizedTimeout(conn)
protocol.SetRandomizedTimeout(conn)
rewindConn := common.NewRewindConn(conn)
rewindConn.R.SetBufferSize(512)