From ff3c500a64e385051d351ed7976caa5f66c3e83f Mon Sep 17 00:00:00 2001 From: Page Fault Date: Mon, 18 May 2020 16:42:50 +0000 Subject: [PATCH] update docs, config --- README.md | 47 ++++++++++++++++++------------- api/server_test.go | 4 +-- common/common.go | 2 +- common/io.go | 4 +-- conf/conf.go | 2 -- conf/parse.go | 14 ++++----- docs/config.toml | 2 +- docs/content/_index.md | 40 ++------------------------ docs/content/basic/full-config.md | 14 ++++----- docs/content/basic/trojan.md | 7 ++--- example/client.json | 3 +- protocol/http/inbound.go | 2 +- protocol/trojan/websocket.go | 8 ++++-- proxy/client/client.go | 13 +++++---- proxy/client/forward.go | 2 +- proxy/client/nat.go | 4 +-- proxy/proxy.go | 7 +++-- proxy/relay/relay.go | 2 +- proxy/server/server.go | 8 +++--- shadow/shadow.go | 2 +- test/proxy_test.go | 1 - 21 files changed, 77 insertions(+), 111 deletions(-) diff --git a/README.md b/README.md index 887a068..b56cd6c 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ [![Commit](https://img.shields.io/github/last-commit/p4gefau1t/trojan-go)](https://img.shields.io/github/last-commit/p4gefau1t/trojan-go) [![Commit Activity](https://img.shields.io/github/commit-activity/m/p4gefau1t/trojan-go)](https://img.shields.io/github/commit-activity/m/p4gefau1t/trojan-go) [![Go Report Card](https://goreportcard.com/badge/github.com/p4gefau1t/trojan-go)](https://goreportcard.com/report/github.com/p4gefau1t/trojan-go) +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://github.com/p4gefau1t/trojan-go/pulls) 使用Go实现的完整Trojan代理,与Trojan协议以及Trojan-GFW版本的配置文件格式兼容。安全,高效,轻巧,易用。 @@ -21,11 +22,9 @@ 预编译的版本可在 [Release 页面](https://github.com/p4gefau1t/trojan-go/releases)下载。直接运行解压得到的执行文件即可,无其他组件依赖。 -跨平台客户端[Trojan-Qt5](https://github.com/Trojan-Qt5/Trojan-Qt5/)已使用Trojan-Go核心,支持目前所有的Trojan-Go扩展特性,界面友好,推荐作为客户端使用。 +如果你遇到配置和使用方面的问题,发现了软件Bug,或是有更好的想法,欢迎加入Trojan-Go的[Telegram交流反馈群](https://t.me/trojan_go_chat)。 -[Telegram交流反馈群](https://t.me/trojan_go_chat) - -### 下面的说明为简单介绍,完整配置教程和配置介绍参见[Trojan-Go文档](https://p4gefau1t.github.io/trojan-go)。 +## **下面的说明为简单介绍,完整配置教程和配置介绍参见[Trojan-Go文档](https://p4gefau1t.github.io/trojan-go)。** Trojan-Go支持并且兼容Trojan-GFW的绝大多数功能,包括但不限于: @@ -53,18 +52,30 @@ Trojan-Go支持并且兼容Trojan-GFW的绝大多数功能,包括但不限于 - 多平台和多操作系统支持,无特殊依赖 -- 多路复用,显著提升并发性能 +- 多路复用,降低延迟,提升并发性能 - 自定义路由模块,可实现国内直连/广告屏蔽等功能 -- Websocket,用于支持CDN流量中转(基于WebSocket over TLS/SSL)和对抗GFW中间人攻击 +- Websocket传输支持,用于实现CDN流量中转(基于WebSocket over TLS/SSL)和对抗GFW中间人攻击 -- 自动化HTTPS证书申请,使用ACME协议从Let's Encrypt自动申请和更新HTTPS证书 +- 自动化HTTPS证书申请,从Let's Encrypt自动申请和更新HTTPS证书 -- TLS指纹伪造,绕过针对TLS Client Hello的特征识别 +- TLS指纹伪造,绕过GFW针对TLS Client Hello的特征识别 - 基于gRPC的API支持,支持动态用户管理和流量速度限制 +- 服务端支持处理Trojan协议明文(TCP明文传输),以适应前置nginx等服务器的场景 + +## 图形界面客户端 + +Trojan-Go服务端可以兼容所有Trojan-GFW的客户端,如Igniter,ShadowRocket等。 + +下面是支持Trojan-Go扩展特性(Websocket/Mux等)的客户端。 + +[Trojan-Qt5](https://github.com/Trojan-Qt5/Trojan-Qt5/),跨平台客户端,支持Windows/MacOS/Linux,使用Trojan-Go核心,支持所有Trojan-Go扩展特性。 + +[Igniter-Go](https://github.com/p4gefau1t/trojan-go-android),Fork自Igniter,将Igniter核心替换为Trojan-Go并做了一定修改,支持所有Trojan-Go扩展特性。 + ## 使用方法 1. 快速证书配置 @@ -108,7 +119,7 @@ Trojan-Go支持并且兼容Trojan-GFW的绝大多数功能,包括但不限于 4. 使用Docker部署 ```shell - docker run\ + docker run \ --name trojan-go \ -d \ -v /etc/trojan-go/:/etc/trojan-go \ @@ -119,7 +130,7 @@ Trojan-Go支持并且兼容Trojan-GFW的绝大多数功能,包括但不限于 或者 ```shell - docker run\ + docker run \ --name trojan-go \ -d \ -v /path/to/host/config:/path/in/container \ @@ -171,7 +182,6 @@ server.json "key": "your_key.key" } } - ``` 客户端配置文件 @@ -201,13 +211,13 @@ client.json sudo ./trojan-go -autocert request ``` -向Let's Encrypt申请证书 +向Let's Encrypt申请证书。 申请过程中,按照ACME协议要求,trojan-go需要和letsencrypt服务器交互,因此需要暂时占用本地443和80端口,此时请暂时关闭nginx,apache,或者trojan等服务。 Linux下,绑定80和443端口需要root权限,因此你需要使用sudo执行trojan-go才能正常证书申请流程。 -你也可以指定自定义端口,然后使用nginx等web服务器进行443和80分流,将acme协议代理到自定义端口上。 +你也可以指定自定义端口,然后使用nginx等web服务器进行443和80分流,将ACME协议流量代理到自定义端口上。 如果申请成功,本目录下会得到 @@ -249,7 +259,7 @@ Trojan-Go支持使用TLS+Websocket承载Trojan协议,使得利用CDN进行流 可以省略```hostname```, 但是服务器和客户端的```path```必须一致。服务器开启Websocket支持后可以同时支持Websocket和一般Trojan流量,未配置Websocket选项的客户端依然可以正常使用。 -由于Trojan-GFW版本并不支持Websocket,因此,虽然开启了Websocket支持的服务端可以兼容所有客户端,但是如果要使用Websocket承载流量,请确保双方都使用Trojan-Go。 +由于Trojan-GFW版本并不支持Websocket,因此,虽然开启了Websocket支持的Trojan-Go服务端可以兼容所有客户端,但是如果要使用Websocket承载流量,请确保双方都使用Trojan-Go。 ### 多路复用 @@ -257,11 +267,11 @@ Trojan-Go支持使用TLS+Websocket承载Trojan协议,使得利用CDN进行流 在很差的网络条件下,一次TLS握手可能会花费很多时间。 -Trojan-Go支持多路复用(基于[smux](https://github.com/xtaci/smux))。通过使一个TLS隧道连接承载多个TCP连接的方式,减少TCP和TLS握手带来的延迟,以期提升高并发情景下的性能。 +Trojan-Go支持多路复用(基于[smux](https://github.com/xtaci/smux))。通过一个TLS隧道连接承载多个TCP连接的方式,减少TCP和TLS握手带来的延迟,以期提升高并发情景下的性能。 启用多路复用并不会增加你测速得到的链路速度,但会降低延迟,提升大量并发请求时的网络体验,例如浏览含有大量图片的网页等。 -注意,这个特性和Trojan-GFW**不兼容**,出于兼容性考虑,这个特性是默认关闭的。你可以通过设置mux选项中的"enabled"字段启用它。如下 +注意,这个特性和Trojan-GFW**不兼容**,出于兼容性考虑,这个特性是默认关闭的。你可以通过设置客户端的mux选项"enabled"字段启用它。如下 ```json "mux": { @@ -361,11 +371,8 @@ CGO_ENABLE=0 GOOS=linux GOARCH=arm go build -tags "full" [go-tproxy](https://github.com/LiamHaworth/go-tproxy) -[tcplisten](https://github.com/valyala/tcplisten) - [utls](https://github.com/refraction-networking/utls) - ## Stargazers over time -[![Stargazers over time](https://starchart.cc/p4gefau1t/trojan-go.svg)](https://starchart.cc/p4gefau1t/trojan-go) \ No newline at end of file +[![Stargazers over time](https://starchart.cc/p4gefau1t/trojan-go.svg)](https://starchart.cc/p4gefau1t/trojan-go) diff --git a/api/server_test.go b/api/server_test.go index c7e8d75..76e30f5 100644 --- a/api/server_test.go +++ b/api/server_test.go @@ -1,7 +1,7 @@ package api import ( - context "context" + "context" "fmt" "testing" "time" @@ -9,7 +9,7 @@ import ( "github.com/p4gefau1t/trojan-go/common" "github.com/p4gefau1t/trojan-go/conf" "github.com/p4gefau1t/trojan-go/stat/memory" - grpc "google.golang.org/grpc" + "google.golang.org/grpc" ) func TestServerAPI(t *testing.T) { diff --git a/common/common.go b/common/common.go index d7f93d2..78e309e 100644 --- a/common/common.go +++ b/common/common.go @@ -11,7 +11,7 @@ import ( ) const ( - Version = "v0.4.10" + Version = "v0.4.11" ) type Runnable interface { diff --git a/common/io.go b/common/io.go index c1a75bd..0fdac88 100644 --- a/common/io.go +++ b/common/io.go @@ -76,7 +76,7 @@ func (r *RewindReader) StopBuffering() { func (r *RewindReader) SetBufferSize(size int) { if size == 0 { //disable buffering if !r.buffered { - panic("already disabled") + panic("reader is already disabled") } r.buffered = false r.buf = nil @@ -84,7 +84,7 @@ func (r *RewindReader) SetBufferSize(size int) { r.bufferSize = 0 } else { if r.buffered { - panic("is already buffering") + panic("reader is already buffering") } r.buffered = true r.bufReadIdx = 0 diff --git a/conf/conf.go b/conf/conf.go index 4bba632..5896d3d 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -29,7 +29,6 @@ const ( type TLSConfig struct { Verify bool `json:"verify"` - VerifyHostname bool `json:"verify_hostname"` CertPath string `json:"cert"` KeyPath string `json:"key"` KeyPassword string `json:"key_password"` @@ -45,7 +44,6 @@ type TLSConfig struct { Curves string `json:"curves"` Fingerprint string `json:"fingerprint"` ServePlainText bool `json:"serve_plain_text"` - RedirectWithTLS bool `json:"redirect_with_tls"` ClientHelloID *utls.ClientHelloID FallbackAddress *common.Address diff --git a/conf/parse.go b/conf/parse.go index 87f6c05..514344b 100644 --- a/conf/parse.go +++ b/conf/parse.go @@ -356,10 +356,9 @@ func ParseJSON(data []byte) (*GlobalConfig, error) { KeepAlive: true, }, TLS: TLSConfig{ - Verify: true, - VerifyHostname: true, - SessionTicket: true, - ReuseSession: true, + Verify: true, + SessionTicket: true, + ReuseSession: true, ALPN: []string{ "http/1.1", }, @@ -372,10 +371,9 @@ func ParseJSON(data []byte) (*GlobalConfig, error) { Websocket: WebsocketConfig{ DoubleTLS: true, TLS: TLSConfig{ - Verify: true, - VerifyHostname: true, - SessionTicket: true, - ReuseSession: true, + Verify: true, + SessionTicket: true, + ReuseSession: true, }, }, MySQL: MySQLConfig{ diff --git a/docs/config.toml b/docs/config.toml index 7fe76f9..75c6c5c 100755 --- a/docs/config.toml +++ b/docs/config.toml @@ -16,7 +16,7 @@ enableMissingTranslationPlaceholders = false # Source Code repository section description = "An unidentifiable mechanism that helps you bypass GFW. " github_repository = "https://github.com/p4gefau1t/trojan-go" - version = "0.4.10" + version = "0.4.11" # Documentation repository section # documentation repository (set edit link to documentation repository) diff --git a/docs/content/_index.md b/docs/content/_index.md index c20656e..be02257 100644 --- a/docs/content/_index.md +++ b/docs/content/_index.md @@ -8,47 +8,11 @@ weight: 10 这里是Trojan-Go的文档,你可以在左侧的导航栏中找到一些使用技巧,以及完整的配置文件说明。 -Trojan-Go是使用Go语言实现的完整的Trojan代理,和Trojan协议以及原版的配置文件格式兼容。 +Trojan-Go是使用Go语言实现的完整的Trojan代理,和Trojan协议以及原版的配置文件格式兼容。支持并且兼容Trojan-GFW版本的绝大多数功能,并扩展了更多的实用功能。 Trojan-Go的的首要目标是保障传输安全性和隐蔽性。在此前提下,尽可能提升传输性能和易用性。 -Trojan-Go支持并且兼容原版Trojan的绝大多数功能,包括但不限于: - -- TLS/SSL隧道传输 - -- 透明代理 (NAT模式,iptables设置参见[这里](https://github.com/shadowsocks/shadowsocks-libev/tree/v3.3.1#transparent-proxy)) - -- UDP代理 - -- 对抗GFW被动/主动检测的机制 - -- MySQL数据库支持 - -- 流量统计,用户流量配额限制 - -- 从数据库中的用户列表进行认证 - -- TCP性能方面的选项,如TCP Fast Open,端口复用等 - -同时,Trojan-Go还有更多高效易用的功能特性: - -- 简易模式,快速部署使用 - -- Socks5/HTTP代理自动适配 - -- 多平台和多操作系统支持,无特殊依赖 - -- 多路复用,显著提升并发性能 - -- 自定义路由模块,可实现国内直连/广告屏蔽等功能 - -- Websocket,用于支持CDN流量中转(基于WebSocket over TLS/SSL)和对抗GFW中间人攻击 - -- 自动化证书申请,使用ACME协议从Let's Encrypt自动申请和更新HTTPS证书 - -- 基于gRPC的API支持,支持动态用户管理和流量速度限制 - -如果你遇到配置方面的问题,或是遇到了软件Bug,或是有更好的想法,欢迎加入[Telegram交流反馈群](https://t.me/trojan_go_chat) +如果你遇到配置和使用方面的问题,发现了软件Bug,或是有更好的想法,欢迎加入Trojan-Go的[Telegram交流反馈群](https://t.me/trojan_go_chat)。 ---- diff --git a/docs/content/basic/full-config.md b/docs/content/basic/full-config.md index 46a793e..cdea402 100644 --- a/docs/content/basic/full-config.md +++ b/docs/content/basic/full-config.md @@ -36,7 +36,6 @@ weight: 30 "dns": [], "ssl": { "verify": true, - "verify_hostname": true, "cert": *required*, "key": *required*, "key_password": "", @@ -86,7 +85,6 @@ weight: 30 "double_tls": true, "ssl": { "verify": true, - "verify_hostname": true, "cert": "", "key": "", "key_password": "", @@ -155,14 +153,12 @@ weight: 30 使用DOT可以防止DNS请求泄露,但由于TLS的握手耗费更多时间,查询速度也会有一定的下降,请自行斟酌性能和安全性的平衡。 -```buffer_size```为单个连接缓冲区大小,单位KiB,默认32KiB。提升这个数值可以提升网络吞吐量和效率,但是也会增加内存消耗。对于路由器等嵌入式系统,建议根据实际情况,适当减小该数值。 +```buffer_size```为单个连接缓冲区大小,单位KiB,默认32KiB。适当提升这个数值可以提升网络吞吐量和效率,但是也会增加内存消耗。对于路由器等嵌入式系统,建议根据实际情况,适当减小该数值。 ### ```ssl```选项 ```verify```表示客户端(client/nat/forward)是否校验服务端提供的证书合法性,默认开启。出于安全性考虑,这个选项不应该在实际场景中选择false,否则可能遭受中间人攻击。如果使用自签名或者自签发的证书,开启```verify```会导致校验失败。这种情况下,应当保持```verify```开启,然后在```cert```中填写服务端的证书,即可正常连接。 -```verify_hostname```表示客户端(client/nat/forward)是否校验服务端提供的证书的Common Name和本地提供的SNI字段的一致性。 - 服务端必须填入```cert```和```key```,对应服务器的证书和私钥文件,请注意证书是否有效/过期。如果使用权威CA签发的证书,客户端(client/nat/forward)可以不填写```cert```。如果使用自签名或者自签发的证书,应当在的```cert```处填入服务器证书文件,否则可能导致校验失败。 ```sni```指的是证书的Common Name,如果你使用letsencrypt等机构签名的证书,这里填入你的域名。如果这一项未填,将使用```remote_addr```填充。你应当指定一个有效的SNI(和远端证书CN一致),否则客户端可能无法验证远端证书有效性从而无法连接。 @@ -189,9 +185,9 @@ weight: 30 一旦指纹的值被设置,```cipher```,```curves```,```alpn```,```session_ticket```等有可能影响指纹的字段将使用该指纹的特定设置覆写。 -```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```进行验证。 +```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```进行验证。 ```serve_plain_text```服务端直接是否直接接受TCP连接并处理trojan协议明文。开启此选项后,```ssl```的其他选项将失效,trojan-go将直接处理连入的TCP连接而不使用TLS。此选项的意义在于支持nginx等Web服务器的分流。如果开启,请不要将trojan-go服务对外暴露。 @@ -261,11 +257,11 @@ Websocket传输是trojan-go的特性。在**正常的直接连接代理节点** ```ssl```如果```double_tls```启用,这个选项用于配置第二层TLS,如果没有填写则使用全局的```ssl```填充。各字段定义与全局```ssl```相同。 -```obfuscation_password```指定混淆密码。用于混淆内层连接以降低遭到国内无良CDN运营商识别的概率。如果需要使用混淆,服务端和客户端必须同时设置相同密码。这个选项对性能有一定影响,请自行斟酌安全性和性能的平衡。 +```obfuscation_password```指定混淆密码。用于混淆内层连接以避免遭到CDN运营商识别。如果需要使用混淆,服务端和客户端必须设置相同的密码。这个选项对性能有一定影响,请自行斟酌安全性和性能的平衡。 ### ```tcp```选项 -```no_delay```是否禁用纳格算法(Nagle’s algorithm),即TCP封包是否直接发出而不等待缓冲区填满。 +```no_delay```TCP封包是否直接发出而不等待缓冲区填满。 ```keep_alive```是否启用TCP心跳存活检测。 diff --git a/docs/content/basic/trojan.md b/docs/content/basic/trojan.md index 9353af8..5f3cbdf 100644 --- a/docs/content/basic/trojan.md +++ b/docs/content/basic/trojan.md @@ -4,12 +4,11 @@ draft: false weight: 21 --- - 这个页面将会简单讲述Trojan协议的基本工作原理。如果你对于GFW和Trojan的工作方式不感兴趣,可以跳过这一小节。但为了更好地保护你的通讯安全性和节点的隐蔽性,我还是建议你阅读。 ## 为什么Shadowsocks容易遭到封锁 -防火墙在早期仅仅只是对出境流量进行截获和审查,也即**被动检测**。Shadowsocks的加密协议设计使得传输的数据包本身几乎没有任何特征,看起来类似于完全随机的比特流,这在早一些年的确能有效绕过GFW。 +防火墙在早期仅仅只是对出境流量进行截获和审查,也即**被动检测**。Shadowsocks的加密协议设计使得传输的数据包本身几乎没有任何特征,看起来类似于完全随机的比特流,这在早期的确能有效绕过GFW。 目前的GFW已经开始采用**主动探测**的方式。具体来说,当GFW发现一个可疑的无法识别的连接时(大流量,随机字节流,高位端口等特征),将会**主动连接**这个服务器端口,重放之前捕获到的流量(或者经过一些精心修改后重放)。Shadowsocks服务器检测到不正常的连接,将连接断开。这种不正常的流量和断开连接的行为被视作可疑的Shadowsocks服务器的特征,于是该服务器被加入GFW的可疑名单中。这个名单不一定立即生效,而是在某些特殊的敏感时期,可疑名单中的服务器会遭到暂时或者永久的封锁。该可疑名单是否封锁,可能由人为因素决定。 @@ -27,8 +26,8 @@ weight: 21 - 前向安全(即使密钥泄露,GFW也无法解密先前的加密流量) -对于被动检测,Trojan协议与HTTPS协议流量的特征和行为完全一致,而HTTPS流量占据了目前互联网流量的一半以上,且HTTPS握手成功后流量是加密的,几乎不存在可行方法从其中分辨出Trojan协议流量。 +对于被动检测,Trojan协议的流量与HTTPS流量的特征和行为完全一致。而HTTPS流量占据了目前互联网流量的一半以上,且TLS握手成功后流量均为密文,几乎不存在可行方法从其中分辨出Trojan协议流量。 -对于主动检测,当防火墙主动连接Trojan服务器进行检测时,Trojan可以正确识别非Trojan协议的流量。与Shadowsocks等代理不同的是,此时Trojan不会断开连接,而是将这个连接代理到一个正常的Web服务器。在GFW看来,该服务器的行为和一个普通的HTTPS网站行为完全相同,无法判断是否是一个Trojan代理节点。这也是Trojan推荐使用合法的域名、使用CA签名的HTTPS证书的原因: 这让你的服务器完全无法被GFW使用主动检测判定是一个Trojan服务器。 +对于主动检测,当防火墙主动连接Trojan服务器进行检测时,Trojan可以正确识别非Trojan协议的流量。与Shadowsocks等代理不同的是,此时Trojan不会断开连接,而是将这个连接代理到一个正常的Web服务器。在GFW看来,该服务器的行为和一个普通的HTTPS网站行为完全相同,无法判断是否是一个Trojan代理节点。这也是Trojan推荐使用合法的域名、使用权威CA签名的HTTPS证书的原因: 这让你的服务器完全无法被GFW使用主动检测判定是一个Trojan服务器。 因此,就目前的情况来看,若要识别并阻断Trojan的连接,只能使用无差别封锁(封锁某个IP段,某一类证书,某一类域名,甚至阻断全国所有出境HTTPS连接)或发动大规模的中间人攻击(劫持所有TLS流量并劫持证书,审查内容)。对于中间人攻击,可以使用Websocket的双重TLS应对,高级配置中有详细讲解。 diff --git a/example/client.json b/example/client.json index 51dae75..9b0e0a7 100644 --- a/example/client.json +++ b/example/client.json @@ -23,6 +23,7 @@ "block": [ "geosite:category-ads" ], - "proxy": [] + "proxy": [], + "default_policy": "proxy" } } \ No newline at end of file diff --git a/protocol/http/inbound.go b/protocol/http/inbound.go index 3a6f85b..1570604 100644 --- a/protocol/http/inbound.go +++ b/protocol/http/inbound.go @@ -79,7 +79,7 @@ func (i *HTTPInboundTunnelConnSession) parseRequest() (bool, error) { return false, err } if httpRequest.Method != "CONNECT" { - return true, common.NewError("Not a connection") + return true, common.NewError("Not a CONNECT request") } i.bodyReader = httpRequest.Body i.httpRequest = httpRequest diff --git a/protocol/trojan/websocket.go b/protocol/trojan/websocket.go index 846c721..53ba2fd 100644 --- a/protocol/trojan/websocket.go +++ b/protocol/trojan/websocket.go @@ -201,12 +201,13 @@ func NewInboundWebsocket(ctx context.Context, conn net.Conn, config *conf.Global strings.ToLower(httpRequest.Header.Get("Upgrade")) != "websocket" { //check upgrade field //not a valid websocket conn rewindConn.R.Rewind() + err := common.NewError("Invalid websocket request from " + conn.RemoteAddr().String()) shadowMan.SubmitScapegoat(&shadow.Scapegoat{ Conn: rewindConn, ShadowAddress: config.RemoteAddress, - Info: "Invalid http upgrade request from " + conn.RemoteAddr().String(), + Info: err.Error(), }) - return nil, common.NewError("Invalid websocket request" + conn.RemoteAddr().String()) + return nil, err } //this is a websocket upgrade request @@ -303,8 +304,9 @@ func NewInboundWebsocket(ctx context.Context, conn net.Conn, config *conf.Global protocol.SetRandomizedTimeout(tlsConn) if tlsErr := tlsConn.Handshake(); tlsErr != nil { rewindConn.R.Rewind() + rewindConn.R.StopBuffering() //proxy this to our own ws server - tlsErr = common.NewError("Invalid double tls handshake from " + conn.RemoteAddr().String()).Base(tlsErr) + tlsErr = common.NewError("Invalid double TLS handshake from " + conn.RemoteAddr().String()).Base(tlsErr) goat, err := getWebsocketScapegoat( config, url, diff --git a/proxy/client/client.go b/proxy/client/client.go index 0bbb57b..17e6b5c 100644 --- a/proxy/client/client.go +++ b/proxy/client/client.go @@ -44,10 +44,10 @@ type Client struct { func (c *Client) handleSocksConn(conn io.ReadWriteCloser) { rwc := common.NewRewindReadWriteCloser(conn) + defer rwc.Close() inboundConn, req, err := socks.NewInboundConnSession(rwc) if err != nil { log.Error(common.NewError("Failed to handle socks requests").Base(err)) - rwc.Close() return } defer inboundConn.Close() @@ -101,7 +101,7 @@ func (c *Client) handleSocksConn(conn io.ReadWriteCloser) { return } log.Info("[Bypass] conn to", req) - proxy.ProxyConn(c.ctx, inboundConn, outboundConn, c.config.BufferSize) + proxy.RelayConn(c.ctx, inboundConn, outboundConn, c.config.BufferSize) return } else if policy == router.Block { log.Info("[Block] conn to", req) @@ -113,11 +113,12 @@ func (c *Client) handleSocksConn(conn io.ReadWriteCloser) { return } defer outboundConn.Close() - proxy.ProxyConn(c.ctx, inboundConn, outboundConn, c.config.BufferSize) + proxy.RelayConn(c.ctx, inboundConn, outboundConn, c.config.BufferSize) } func (c *Client) handleHTTPConn(conn io.ReadWriteCloser) { rwc := common.NewRewindReadWriteCloser(conn) + defer rwc.Close() inboundConn, req, inboundPacket, err := http.NewHTTPInbound(rwc) if err != nil { log.Error(common.NewError("Failed to handle HTTP requests").Base(err)) @@ -145,7 +146,7 @@ func (c *Client) handleHTTPConn(conn io.ReadWriteCloser) { return } log.Info("[Bypass] conn to", req) - proxy.ProxyConn(c.ctx, inboundConn, outboundConn, c.config.BufferSize) + proxy.RelayConn(c.ctx, inboundConn, outboundConn, c.config.BufferSize) return } else if policy == router.Block { log.Info("[Block] conn to", req) @@ -159,7 +160,7 @@ func (c *Client) handleHTTPConn(conn io.ReadWriteCloser) { } defer outboundConn.Close() log.Info("Conn tunneling to", req) - proxy.ProxyConn(c.ctx, inboundConn, outboundConn, c.config.BufferSize) + proxy.RelayConn(c.ctx, inboundConn, outboundConn, c.config.BufferSize) } else { //GET/POST requests defer inboundPacket.Close() packetChan := make(chan *packetInfo, 512) @@ -265,7 +266,7 @@ func (c *Client) listenUDP(errChan chan error) { router.Proxy: outboundPacket, router.Bypass: directOutboundPacket, } - proxy.ProxyPacketWithRouter(c.ctx, inboundPacket, table, c.router) + proxy.RelayPacketWithRouter(c.ctx, inboundPacket, table, c.router) outboundPacket.Close() directOutboundPacket.Close() case <-c.ctx.Done(): diff --git a/proxy/client/forward.go b/proxy/client/forward.go index 9335d46..6af479c 100644 --- a/proxy/client/forward.go +++ b/proxy/client/forward.go @@ -156,7 +156,7 @@ func (f *Forward) listenTCP(errChan chan error) { return } defer outboundConn.Close() - proxy.ProxyConn(f.ctx, inboundConn, outboundConn, f.config.BufferSize) + proxy.RelayConn(f.ctx, inboundConn, outboundConn, f.config.BufferSize) } go handle(inboundConn) } diff --git a/proxy/client/nat.go b/proxy/client/nat.go index 8c65d8e..de5363d 100644 --- a/proxy/client/nat.go +++ b/proxy/client/nat.go @@ -44,7 +44,7 @@ func (n *NAT) handleConn(conn net.Conn) { } defer outboundConn.Close() log.Info("[Tproxy] conn from", conn.RemoteAddr(), "tunneling to", req) - proxy.ProxyConn(n.ctx, inboundConn, outboundConn, n.config.BufferSize) + proxy.RelayConn(n.ctx, inboundConn, outboundConn, n.config.BufferSize) } func (n *NAT) listenUDP(errChan chan error) { @@ -71,7 +71,7 @@ func (n *NAT) listenUDP(errChan chan error) { } outboundPacket, err := trojan.NewPacketSession(outboundConn) common.Must(err) - proxy.ProxyPacket(n.ctx, inboundPacket, outboundPacket) + proxy.RelayPacket(n.ctx, inboundPacket, outboundPacket) outboundPacket.Close() } } diff --git a/proxy/proxy.go b/proxy/proxy.go index b326868..b0e7dcc 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -16,7 +16,7 @@ type Buildable interface { Build(config *conf.GlobalConfig) (common.Runnable, error) } -func ProxyConn(ctx context.Context, a, b io.ReadWriter, bufferSize int) { +func RelayConn(ctx context.Context, a, b io.ReadWriter, bufferSize int) { errChan := make(chan error, 2) copyConn := func(dst io.Writer, src io.Reader) { buf := make([]byte, bufferSize) @@ -31,10 +31,11 @@ func ProxyConn(ctx context.Context, a, b io.ReadWriter, bufferSize int) { log.Debug(common.NewError("conn proxy ends").Base(err)) } case <-ctx.Done(): + return } } -func ProxyPacket(ctx context.Context, a, b protocol.PacketReadWriter) { +func RelayPacket(ctx context.Context, a, b protocol.PacketReadWriter) { errChan := make(chan error, 2) copyPacket := func(dst protocol.PacketWriter, src protocol.PacketReader) { for { @@ -60,7 +61,7 @@ func ProxyPacket(ctx context.Context, a, b protocol.PacketReadWriter) { } } -func ProxyPacketWithRouter(ctx context.Context, from protocol.PacketReadWriter, table map[router.Policy]protocol.PacketReadWriter, router router.Router) { +func RelayPacketWithRouter(ctx context.Context, from protocol.PacketReadWriter, table map[router.Policy]protocol.PacketReadWriter, router router.Router) { errChan := make(chan error, 1+len(table)) copyPacket := func(dst protocol.PacketWriter, src protocol.PacketReader) { for { diff --git a/proxy/relay/relay.go b/proxy/relay/relay.go index 388719d..3a77dd3 100644 --- a/proxy/relay/relay.go +++ b/proxy/relay/relay.go @@ -26,7 +26,7 @@ func (f *Relay) handleConn(conn net.Conn) { return } defer newConn.Close() - proxy.ProxyConn(f.ctx, newConn, conn, f.config.BufferSize) + proxy.RelayConn(f.ctx, newConn, conn, f.config.BufferSize) } func (f *Relay) Run() error { diff --git a/proxy/server/server.go b/proxy/server/server.go index 92556a0..28ca4c7 100644 --- a/proxy/server/server.go +++ b/proxy/server/server.go @@ -50,13 +50,13 @@ func (s *Server) handleMuxConn(stream *smux.Stream) { } log.Info("Mux conn tunneling to", req.String()) defer outboundConn.Close() - proxy.ProxyConn(s.ctx, inboundConn, outboundConn, s.config.BufferSize) + proxy.RelayConn(s.ctx, inboundConn, outboundConn, s.config.BufferSize) case protocol.Associate: outboundPacket, err := direct.NewOutboundPacketSession(s.ctx) common.Must(err) inboundPacket, err := trojan.NewPacketSession(inboundConn) defer inboundPacket.Close() - proxy.ProxyPacket(s.ctx, inboundPacket, outboundPacket) + proxy.RelayPacket(s.ctx, inboundPacket, outboundPacket) default: log.Error(fmt.Sprintf("Invalid command %d", req.Command)) return @@ -100,7 +100,7 @@ func (s *Server) handleConn(conn net.Conn) { } defer outboundPacket.Close() log.Info("UDP tunnel established") - proxy.ProxyPacket(s.ctx, inboundPacket, outboundPacket) + proxy.RelayPacket(s.ctx, inboundPacket, outboundPacket) log.Debug("UDP tunnel closed") return } @@ -114,7 +114,7 @@ func (s *Server) handleConn(conn net.Conn) { defer outboundConn.Close() log.Info("Conn from", conn.RemoteAddr(), "tunneling to", req.String()) - proxy.ProxyConn(s.ctx, inboundConn, outboundConn, s.config.BufferSize) + proxy.RelayConn(s.ctx, inboundConn, outboundConn, s.config.BufferSize) } func (s *Server) ListenTCP(errChan chan error) { diff --git a/shadow/shadow.go b/shadow/shadow.go index 727e546..729be7d 100644 --- a/shadow/shadow.go +++ b/shadow/shadow.go @@ -61,7 +61,7 @@ func (m *ShadowManager) handleScapegoat() { if goat.Conn == nil || goat.ShadowConn == nil { panic(fmt.Sprintf("Empty conn: %v %v", goat.Conn, goat.ShadowConn)) } - proxy.ProxyConn(m.ctx, goat.Conn, goat.ShadowConn, m.config.BufferSize) + proxy.RelayConn(m.ctx, goat.Conn, goat.ShadowConn, m.config.BufferSize) goat.Conn.Close() goat.ShadowConn.Close() log.Info("Scapegoat relaying done: ", goat.Info) diff --git a/test/proxy_test.go b/test/proxy_test.go index 5e5310a..f64cb96 100644 --- a/test/proxy_test.go +++ b/test/proxy_test.go @@ -102,7 +102,6 @@ func getTLSConfig() conf.TLSConfig { CertPool: pool, KeyPair: KeyPair, Verify: true, - VerifyHostname: true, ReuseSession: true, SessionTicket: true, FallbackAddress: common.NewAddress("127.0.0.1", 10080, "tcp"),