merge 2.4 branch
This commit is contained in:
commit
7b509bc49d
2
.gitignore
vendored
2
.gitignore
vendored
@ -25,3 +25,5 @@ _testmain.go
|
||||
*.test
|
||||
|
||||
*.bak
|
||||
|
||||
cmd/gost
|
@ -1,5 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
1.6
|
||||
1.7
|
246
README.md
246
README.md
@ -7,17 +7,20 @@ gost - GO Simple Tunnel
|
||||
|
||||
特性
|
||||
------
|
||||
* 可同时监听多端口
|
||||
* 多端口监听
|
||||
* 可设置转发代理,支持多级转发(代理链)
|
||||
* 支持标准HTTP/HTTPS/SOCKS5代理协议
|
||||
* 支持标准HTTP/HTTPS/HTTP2/SOCKS4(A)/SOCKS5代理协议
|
||||
* SOCKS5代理支持TLS协商加密
|
||||
* Tunnel UDP over TCP
|
||||
* 支持Shadowsocks协议 (OTA: 2.2+,UDP: 2.4+)
|
||||
* 支持本地/远程端口转发 (2.1+)
|
||||
* 支持HTTP 2.0 (2.2+)
|
||||
* 实验性支持QUIC (2.3+)
|
||||
* 支持KCP协议 (2.3+)
|
||||
* 透明代理 (2.3+)
|
||||
* 权限控制 (2.4+)
|
||||
* 本地/远程TCP/UDP端口转发 (2.1+)
|
||||
* Shadowsocks协议 (UDP: 2.4+)
|
||||
* KCP协议 (2.3+)
|
||||
* TCP透明代理 (2.3+)
|
||||
* HTTP2通道 (2.4+)
|
||||
* SSH通道 (2.4+)
|
||||
* QUIC通道 (2.4+)
|
||||
* obfs4通道 (2.4+)
|
||||
|
||||
二进制文件下载:https://github.com/ginuerzh/gost/releases
|
||||
|
||||
@ -45,29 +48,40 @@ $ sudo snap install gost
|
||||
```
|
||||
scheme分为两部分: protocol+transport
|
||||
|
||||
protocol: 代理协议类型(http, socks5, shadowsocks), transport: 数据传输方式(ws, wss, tls, http2, quic, kcp), 二者可以任意组合,或单独使用:
|
||||
protocol: 代理协议类型(http, socks4(a), socks5, ss), transport: 数据传输方式(ws, wss, tls, quic, kcp, ssh, h2, h2c, obfs4), 二者可以任意组合,或单独使用:
|
||||
|
||||
> http - HTTP代理: http://:8080
|
||||
> http - 标准HTTP代理: http://:8080
|
||||
|
||||
> http+tls - HTTPS代理(可能需要提供受信任的证书): http+tls://:443或https://:443
|
||||
> https - 标准HTTPS代理(可能需要提供受信任的证书): http+tls://:443或https://:443
|
||||
|
||||
> http2 - HTTP2代理并向下兼容HTTPS代理: http2://:443
|
||||
> http2 - 标准HTTP2代理并向下兼容HTTPS: http2://:443
|
||||
|
||||
> socks - 标准SOCKS5代理(支持tls协商加密): socks://:1080
|
||||
> h2 - HTTP2 h2通道: h2://:443
|
||||
|
||||
> socks+wss - SOCKS5代理,使用websocket传输数据: socks+wss://:1080
|
||||
> h2c - HTTP2 h2c通道: h2c://:443
|
||||
|
||||
> tls - HTTPS/SOCKS5代理,使用tls传输数据: tls://:443
|
||||
> socks4(a) - 标准SOCKS4(A)代理: socks4://:1080或socks4a://:1080
|
||||
|
||||
> ss - Shadowsocks代理,ss://chacha20:123456@:8338
|
||||
> socks5 - 标准SOCKS5代理(支持TLS协商加密): socks5://:1080
|
||||
|
||||
> ssu - Shadowsocks UDP relay,ssu://chacha20:123456@:8338
|
||||
> socks5+wss - SOCKS5代理,使用websocket传输数据: socks5+wss://:1080
|
||||
|
||||
> quic - QUIC代理,quic://:6121
|
||||
> tls - HTTP/SOCKS4/SOCKS5代理,使用TLS传输数据: tls://:443
|
||||
|
||||
> kcp - KCP代理,kcp://:8388或kcp://aes:123456@:8388
|
||||
> ss - Shadowsocks代理: ss://chacha20:123456@:8338
|
||||
|
||||
> ssu - Shadowsocks UDP relay: ssu://chacha20:123456@:8338
|
||||
|
||||
> quic - QUIC通道: quic://:6121
|
||||
|
||||
> kcp - KCP通道: kcp://:8388或kcp://aes:123456@:8388
|
||||
|
||||
> redirect - 透明代理: redirect://:12345
|
||||
|
||||
> ssh - SSH代理通道: ssh://:2222,SSH转发通道: forward+ssh://:2222
|
||||
|
||||
> obfs4 - obfs4通道: obfs4://:8080
|
||||
|
||||
> redirect - 透明代理,redirect://:12345
|
||||
|
||||
#### 端口转发
|
||||
|
||||
@ -84,6 +98,8 @@ scheme://[bind_address]:port/[host]:hostport
|
||||
|
||||
#### 配置文件
|
||||
|
||||
此功能由[@septs](https://github.com/septs)贡献。
|
||||
|
||||
> -C : 指定配置文件路径
|
||||
|
||||
配置文件为标准json格式:
|
||||
@ -102,14 +118,6 @@ scheme://[bind_address]:port/[host]:hostport
|
||||
|
||||
ServeNodes等同于-L参数,ChainNodes等同于-F参数
|
||||
|
||||
#### 开启日志
|
||||
|
||||
> -logtostderr : 输出到控制台
|
||||
|
||||
> -v=3 : 日志级别(1-5),级别越高,日志越详细(级别5将开启http2 debug)
|
||||
|
||||
> -log_dir=/log/dir/path : 输出到目录/log/dir/path
|
||||
|
||||
|
||||
使用方法
|
||||
------
|
||||
@ -117,7 +125,7 @@ ServeNodes等同于-L参数,ChainNodes等同于-F参数
|
||||
|
||||
<img src="https://ginuerzh.github.io/images/gost_01.png" />
|
||||
|
||||
* 作为标准HTTP/SOCKS5代理
|
||||
* 作为标准HTTP/SOCKS4/SOCKS5代理
|
||||
```bash
|
||||
gost -L=:8080
|
||||
```
|
||||
@ -142,7 +150,7 @@ test002 12345678
|
||||
|
||||
* 多端口监听
|
||||
```bash
|
||||
gost -L=http2://:443 -L=socks://:1080 -L=ss://aes-128-cfb:123456@:8338
|
||||
gost -L=http2://:443 -L=socks5://:1080 -L=ss://aes-128-cfb:123456@:8338
|
||||
```
|
||||
|
||||
#### 设置转发代理
|
||||
@ -161,60 +169,76 @@ gost -L=:8080 -F=http://admin:123456@192.168.1.1:8081
|
||||
|
||||
<img src="https://ginuerzh.github.io/images/gost_03.png" />
|
||||
```bash
|
||||
gost -L=:8080 -F=http+tls://192.168.1.1:443 -F=socks+ws://192.168.1.2:1080 -F=ss://aes-128-cfb:123456@192.168.1.3:8338 -F=a.b.c.d:NNNN
|
||||
gost -L=:8080 -F=quic://192.168.1.1:6121 -F=socks5+wss://192.168.1.2:1080 -F=http2://192.168.1.3:443 ... -F=a.b.c.d:NNNN
|
||||
```
|
||||
gost按照-F设置的顺序通过代理链将请求最终转发给a.b.c.d:NNNN处理,每一个转发代理可以是任意HTTP/HTTPS/HTTP2/SOCKS5/Shadowsocks类型代理。
|
||||
gost按照-F设置的顺序通过代理链将请求最终转发给a.b.c.d:NNNN处理,每一个转发代理可以是任意HTTP/HTTPS/HTTP2/SOCKS4/SOCKS5/Shadowsocks类型代理。
|
||||
|
||||
#### 本地端口转发(TCP)
|
||||
|
||||
```bash
|
||||
gost -L=tcp://:2222/192.168.1.1:22 -F=...
|
||||
gost -L=tcp://:2222/192.168.1.1:22 [-F=...]
|
||||
```
|
||||
将本地TCP端口2222上的数据(通过代理链)转发到192.168.1.1:22上。当代理链末端(最后一个-F参数)为SSH转发通道类型时,gost会直接使用SSH的本地端口转发功能:
|
||||
|
||||
```bash
|
||||
gost -L=tcp://:2222/192.168.1.1:22 -F forward+ssh://:2222
|
||||
```
|
||||
将本地TCP端口2222上的数据(通过代理链)转发到192.168.1.1:22上。
|
||||
|
||||
#### 本地端口转发(UDP)
|
||||
|
||||
```bash
|
||||
gost -L=udp://:5353/192.168.1.1:53?ttl=60 -F=...
|
||||
gost -L=udp://:5353/192.168.1.1:53?ttl=60 [-F=...]
|
||||
```
|
||||
将本地UDP端口5353上的数据(通过代理链)转发到192.168.1.1:53上。
|
||||
每条转发通道都有超时时间,当超过此时间,且在此时间段内无任何数据交互,则此通道将关闭。可以通过`ttl`参数来设置超时时间,默认值为60秒。
|
||||
|
||||
**注:** 转发UDP数据时,如果有代理链,则代理链的末端(最后一个-F参数)必须是gost SOCKS5类型代理。
|
||||
**注:** 转发UDP数据时,如果有代理链,则代理链的末端(最后一个-F参数)必须是gost SOCKS5类型代理,gost会使用UDP over TCP方式进行转发。
|
||||
|
||||
#### 远程端口转发(TCP)
|
||||
|
||||
```bash
|
||||
gost -L=rtcp://:2222/192.168.1.1:22 -F=... -F=socks://172.24.10.1:1080
|
||||
gost -L=rtcp://:2222/192.168.1.1:22 [-F=...]
|
||||
```
|
||||
将172.24.10.1:2222上的数据(通过代理链)转发到192.168.1.1:22上。当代理链末端(最后一个-F参数)为SSH转发通道类型时,gost会直接使用SSH的远程端口转发功能:
|
||||
|
||||
```bash
|
||||
gost -L=rtcp://:2222/192.168.1.1:22 -F forward+ssh://:2222
|
||||
```
|
||||
将172.24.10.1:2222上的数据(通过代理链)转发到192.168.1.1:22上。
|
||||
|
||||
#### 远程端口转发(UDP)
|
||||
|
||||
```bash
|
||||
gost -L=rudp://:5353/192.168.1.1:53 -F=... -F=socks://172.24.10.1:1080
|
||||
gost -L=rudp://:5353/192.168.1.1:53 [-F=...]
|
||||
```
|
||||
将172.24.10.1:5353上的数据(通过代理链)转发到192.168.1.1:53上。
|
||||
|
||||
**注:** 若要使用远程端口转发功能,代理链不能为空(至少要设置一个-F参数),且代理链的末端(最后一个-F参数)必须是gost SOCKS5类型代理。
|
||||
**注:** 转发UDP数据时,如果有代理链,则代理链的末端(最后一个-F参数)必须是gost SOCKS5类型代理,gost会使用UDP over TCP方式进行转发。
|
||||
|
||||
#### HTTP2
|
||||
gost的HTTP2支持两种模式并自适应:
|
||||
* 作为标准的HTTP2代理,并向下兼容HTTPS代理。
|
||||
* 作为transport(类似于wss),传输其他协议。
|
||||
|
||||
gost的HTTP2支持两种模式:
|
||||
* 作为标准的HTTP2代理,并向下兼容HTTPS代理。
|
||||
* 作为通道传输其他协议。
|
||||
|
||||
##### 代理模式
|
||||
服务端:
|
||||
```bash
|
||||
gost -L=http2://:443
|
||||
```
|
||||
客户端:
|
||||
```bash
|
||||
gost -L=:8080 -F=http2://server_ip:443?ping=30
|
||||
gost -L=:8080 -F=http2://server_ip:443
|
||||
```
|
||||
|
||||
客户端支持`ping`参数开启心跳检测(默认不开启),参数值代表心跳间隔秒数。
|
||||
|
||||
**注:** gost的代理链仅支持一个HTTP2代理节点,采用就近原则,会将第一个遇到的HTTP2代理节点视为HTTP2代理,其他HTTP2代理节点则被视为HTTPS代理。
|
||||
##### 通道模式
|
||||
服务端:
|
||||
```bash
|
||||
gost -L=h2://:443
|
||||
```
|
||||
客户端:
|
||||
```bash
|
||||
gost -L=:8080 -F=h2://server_ip:443
|
||||
```
|
||||
|
||||
#### QUIC
|
||||
gost对QUIC的支持是基于[quic-go](https://github.com/lucas-clemente/quic-go)库。
|
||||
@ -224,12 +248,12 @@ gost对QUIC的支持是基于[quic-go](https://github.com/lucas-clemente/quic-go
|
||||
gost -L=quic://:6121
|
||||
```
|
||||
|
||||
客户端(Chrome):
|
||||
客户端:
|
||||
```bash
|
||||
chrome --enable-quic --proxy-server=quic://server_ip:6121
|
||||
gost -L=:8080 -F=quic://server_ip:6121
|
||||
```
|
||||
|
||||
**注:** 由于Chrome自身的限制,目前只能通过QUIC访问HTTP网站,无法访问HTTPS网站。
|
||||
**注:** QUIC模式只能作为代理链的第一个节点。
|
||||
|
||||
#### KCP
|
||||
gost对KCP的支持是基于[kcp-go](https://github.com/xtaci/kcp-go)和[kcptun](https://github.com/xtaci/kcptun)库。
|
||||
@ -244,24 +268,41 @@ gost -L=kcp://:8388
|
||||
gost -L=:8080 -F=kcp://server_ip:8388
|
||||
```
|
||||
|
||||
或者手动指定加密方法和密码(手动指定的加密方法和密码会覆盖配置文件中的相应值)
|
||||
|
||||
服务端:
|
||||
```bash
|
||||
gost -L=kcp://aes:123456@:8388
|
||||
```
|
||||
|
||||
客户端:
|
||||
```bash
|
||||
gost -L=:8080 -F=kcp://aes:123456@server_ip:8388
|
||||
```
|
||||
|
||||
gost会自动加载当前工作目录中的kcp.json(如果存在)配置文件,或者可以手动通过参数指定配置文件路径:
|
||||
```bash
|
||||
gost -L=kcp://:8388?c=/path/to/conf/file
|
||||
```
|
||||
|
||||
**注:** 客户端若要开启KCP转发,当且仅当代理链不为空且首个代理节点(第一个-F参数)为kcp类型。
|
||||
**注:** KCP模式只能作为代理链的第一个节点。
|
||||
|
||||
#### SSH
|
||||
|
||||
gost的SSH支持两种模式:
|
||||
* 作为转发通道,配合本地/远程TCP端口转发使用。
|
||||
* 作为通道传输其他协议。
|
||||
|
||||
##### 转发模式
|
||||
服务端:
|
||||
```bash
|
||||
gost -L=forward+ssh://:2222
|
||||
```
|
||||
客户端:
|
||||
```bash
|
||||
gost -L=rtcp://:1222/:22 -F=forward+ssh://server_ip:2222
|
||||
```
|
||||
|
||||
##### 通道模式
|
||||
服务端:
|
||||
```bash
|
||||
gost -L=ssh://:2222
|
||||
```
|
||||
客户端:
|
||||
```bash
|
||||
gost -L=:8080 -F=ssh://server_ip:2222?ping=60
|
||||
```
|
||||
|
||||
可以通过`ping`参数设置心跳包发送周期,单位为秒。默认不发送心跳包。
|
||||
|
||||
|
||||
#### 透明代理
|
||||
基于iptables的透明代理。
|
||||
@ -270,6 +311,24 @@ gost -L=kcp://:8388?c=/path/to/conf/file
|
||||
gost -L=redirect://:12345 -F=http2://server_ip:443
|
||||
```
|
||||
|
||||
#### obfs4
|
||||
此功能由[@isofew](https://github.com/isofew)贡献。
|
||||
|
||||
服务端:
|
||||
```bash
|
||||
gost -L=obfs4://:443
|
||||
```
|
||||
|
||||
当服务端运行后会在控制台打印出连接地址供客户端使用:
|
||||
```
|
||||
obfs4://:443/?cert=4UbQjIfjJEQHPOs8vs5sagrSXx1gfrDCGdVh2hpIPSKH0nklv1e4f29r7jb91VIrq4q5Jw&iat-mode=0
|
||||
```
|
||||
|
||||
客户端:
|
||||
```
|
||||
gost -L=:8888 -F='obfs4://server_ip:443?cert=4UbQjIfjJEQHPOs8vs5sagrSXx1gfrDCGdVh2hpIPSKH0nklv1e4f29r7jb91VIrq4q5Jw&iat-mode=0'
|
||||
```
|
||||
|
||||
加密机制
|
||||
------
|
||||
#### HTTP
|
||||
@ -277,7 +336,7 @@ gost -L=redirect://:12345 -F=http2://server_ip:443
|
||||
|
||||
服务端:
|
||||
```bash
|
||||
gost -L=http+tls://:443
|
||||
gost -L=https://:443
|
||||
```
|
||||
客户端:
|
||||
```bash
|
||||
@ -285,44 +344,43 @@ gost -L=:8080 -F=http+tls://server_ip:443
|
||||
```
|
||||
|
||||
#### HTTP2
|
||||
gost仅支持使用TLS加密的HTTP2协议,不支持明文HTTP2传输。
|
||||
gost的HTTP2代理模式仅支持使用TLS加密的HTTP2协议,不支持明文HTTP2传输。
|
||||
|
||||
gost的HTTP2通道模式支持加密(h2)和明文(h2c)两种模式。
|
||||
|
||||
#### SOCKS5
|
||||
gost支持标准SOCKS5协议的no-auth(0x00)和user/pass(0x02)方法,并在此基础上扩展了两个:tls(0x80)和tls-auth(0x82),用于数据加密。
|
||||
|
||||
服务端:
|
||||
```bash
|
||||
gost -L=socks://:1080
|
||||
gost -L=socks5://:1080
|
||||
```
|
||||
客户端:
|
||||
```bash
|
||||
gost -L=:8080 -F=socks://server_ip:1080
|
||||
gost -L=:8080 -F=socks5://server_ip:1080
|
||||
```
|
||||
|
||||
如果两端都是gost(如上)则数据传输会被加密(协商使用tls或tls-auth方法),否则使用标准SOCKS5进行通讯(no-auth或user/pass方法)。
|
||||
|
||||
**注:** 如果transport已经支持加密(wss, tls, http2, kcp),则SOCKS5不会再使用加密方法,防止不必要的双重加密。
|
||||
|
||||
#### Shadowsocks
|
||||
gost对shadowsocks的支持是基于[shadowsocks-go](https://github.com/shadowsocks/shadowsocks-go)库。
|
||||
|
||||
服务端(可以通过ota参数开启OTA强制模式,开启后客户端必须使用OTA模式):
|
||||
服务端:
|
||||
```bash
|
||||
gost -L=ss://aes-128-cfb:123456@:8338?ota=1
|
||||
gost -L=ss://chacha20:123456@:8338
|
||||
```
|
||||
客户端(可以通过ota参数开启OTA模式):
|
||||
客户端:
|
||||
```bash
|
||||
gost -L=:8080 -F=ss://aes-128-cfb:123456@server_ip:8338?ota=1
|
||||
gost -L=:8080 -F=ss://chacha20:123456@server_ip:8338
|
||||
```
|
||||
|
||||
##### Shadowsocks UDP relay
|
||||
|
||||
目前仅服务端支持UDP,且仅支持OTA模式。
|
||||
目前仅服务端支持UDP Relay。
|
||||
|
||||
服务端:
|
||||
```bash
|
||||
gost -L=ssu://aes-128-cfb:123456@:8338
|
||||
gost -L=ssu://chacha20:123456@:8338
|
||||
```
|
||||
|
||||
#### TLS
|
||||
@ -333,6 +391,12 @@ gost内置了TLS证书,如果需要使用其他TLS证书,有两种方法:
|
||||
gost -L="http2://:443?cert=/path/to/my/cert/file&key=/path/to/my/key/file"
|
||||
```
|
||||
|
||||
对于客户端可以指定CA证书进行[证书锁定](https://en.wikipedia.org/wiki/Transport_Layer_Security#Certificate_pinning)(Certificate Pinning):
|
||||
```bash
|
||||
gost -L=:8080 -F="http2://:443?ca=ca.pem"
|
||||
```
|
||||
证书锁定功能由[@sheerun](https://github.com/sheerun)贡献
|
||||
|
||||
SOCKS5 UDP数据处理
|
||||
------
|
||||
#### 不设置转发代理
|
||||
@ -351,6 +415,38 @@ gost作为标准SOCKS5代理处理UDP数据
|
||||
|
||||
当设置转发代理时,gost会使用UDP-over-TCP方式转发UDP数据。proxy1 - proxyN可以为任意HTTP/HTTPS/HTTP2/SOCKS5/Shadowsocks类型代理。
|
||||
|
||||
权限控制
|
||||
------
|
||||
此功能由[@sheerun](https://github.com/sheerun)贡献。
|
||||
|
||||
服务端可以通过白名单(`whitelist`参数)和黑名单(`blacklist`参数)来控制客户端的请求是否允许被处理。
|
||||
参数格式为: `[actions]:[hosts]:[ports]`
|
||||
|
||||
`[actions]`是一个由`,`分割的动作列表,可选值有: `tcp`(TCP转发), `udp`(UDP转发), `rtcp`(TCP远程转发), `rudp`(UDP远程转发), 或 `*`(所有动作)。
|
||||
|
||||
`[hosts]`是一个由`,`分割的Host列表,代表可以绑定到(rtcp,rudp)或转发到(tcp,udp)的目的主机,支持通配符(*.google.com)和`*`(所有主机)。
|
||||
|
||||
`[ports]`是一个由`,`分割的端口列表,代表可以绑定到(rtcp,rudp)或转发到(tcp,udp)的目的端口,可以是`*`(所有端口)。
|
||||
|
||||
多组权限可以通过`+`进行连接:
|
||||
|
||||
`whitelist=rtcp,rudp:localhost,127.0.0.1:2222,8000-9000+udp:8.8.8.8,8.8.4.4:53`(允许TCP/UDP远程端口转发绑定到localhost,127.0.0.1的2222端口和8000-9000端口范围,同时允许UDP转发到8.8.8.8:53和8.8.4.4:53)。
|
||||
|
||||
SSH远程端口转发只能绑定到127.0.0.1:8000
|
||||
```bash
|
||||
gost -L=forward+ssh://localhost:8389?whitelist=rtcp:127.0.0.1:8000
|
||||
```
|
||||
|
||||
SOCKS5的TCP/UDP远程端口转发只允许绑定到大于1000的端口
|
||||
```bash
|
||||
gost -L=socks://localhost:8389?blacklist=rtcp,rudp:*:0-1000
|
||||
```
|
||||
|
||||
SOCKS5的UDP转发只能转发到8.8.8.8:53
|
||||
```bash
|
||||
gost -L=socks://localhost:8389?whitelist=udp:8.8.8.8:53
|
||||
```
|
||||
|
||||
限制条件
|
||||
------
|
||||
代理链中的HTTP代理节点必须支持CONNECT方法。
|
||||
|
233
README_en.md
233
README_en.md
@ -7,15 +7,18 @@ Features
|
||||
------
|
||||
* Listening on multiple ports
|
||||
* Multi-level forward proxy - proxy chain
|
||||
* Standard HTTP/HTTPS/SOCKS5 proxy protocols support
|
||||
* Standard HTTP/HTTPS/HTTP2/SOCKS4(A)/SOCKS5 proxy protocols support
|
||||
* TLS encryption via negotiation support for SOCKS5 proxy
|
||||
* Tunnel UDP over TCP
|
||||
* Shadowsocks protocol support (OTA: 2.2+, UDP: 2.4+)
|
||||
* Local/remote port forwarding (2.1+)
|
||||
* HTTP 2.0 support (2.2+)
|
||||
* Experimental QUIC support (2.3+)
|
||||
* KCP protocol support (2.3+)
|
||||
* Transparent proxy (2.3+)
|
||||
* Permission control
|
||||
* Local/remote TCP/UDP port forwarding (2.1+)
|
||||
* Shadowsocks protocol (UDP: 2.4+)
|
||||
* KCP protocol (2.3+)
|
||||
* Transparent TCP proxy (2.3+)
|
||||
* HTTP2 tunnel (2.4+)
|
||||
* SSH tunnel (2.4+)
|
||||
* QUIC tunnel (2.4+)
|
||||
* obfs4 tunnel (2.4+)
|
||||
|
||||
Binary file download:https://github.com/ginuerzh/gost/releases
|
||||
|
||||
@ -45,30 +48,40 @@ Effective for the -L and -F parameters
|
||||
```
|
||||
scheme can be divided into two parts: protocol+transport
|
||||
|
||||
protocol: proxy protocol types (http, socks5, shadowsocks),
|
||||
transport: data transmission mode (ws, wss, tls, http2, quic, kcp), may be used in any combination or individually:
|
||||
protocol: proxy protocol types (http, socks4(a), socks5, ss),
|
||||
transport: data transmission mode (ws, wss, tls, quic, kcp, ssh, h2, h2c, obfs4), may be used in any combination or individually:
|
||||
|
||||
> http - standard HTTP proxy: http://:8080
|
||||
|
||||
> http+tls - standard HTTPS proxy (may need to provide a trusted certificate): http+tls://:443 or https://:443
|
||||
> https - standard HTTPS proxy (may need to provide a trusted certificate): http+tls://:443 or https://:443
|
||||
|
||||
> http2 - HTTP2 proxy and backwards-compatible with HTTPS proxy: http2://:443
|
||||
|
||||
> socks - standard SOCKS5 proxy: socks://:1080
|
||||
> h2 - HTTP2 h2 tunnel: h2://:443
|
||||
|
||||
> socks+wss - SOCKS5 over websocket: socks+wss://:1080
|
||||
> h2c - HTTP2 h2c tunnel: h2c://:443
|
||||
|
||||
> tls - HTTPS/SOCKS5 over tls: tls://:443
|
||||
> socks4(a) - standard SOCKS4(A) proxy: socks4://:1080 or socks4a://:1080
|
||||
|
||||
> ss - standard shadowsocks proxy, ss://chacha20:123456@:8338
|
||||
> socks5 - standard SOCKS5 proxy: socks5://:1080
|
||||
|
||||
> ssu - shadowsocks UDP relay,ssu://chacha20:123456@:8338
|
||||
> socks5+wss - SOCKS5 over websocket: socks5+wss://:1080
|
||||
|
||||
> quic - standard QUIC proxy, quic://:6121
|
||||
> tls - HTTPS/SOCKS4/SOCKS5 over TLS: tls://:443
|
||||
|
||||
> kcp - standard KCP tunnel,kcp://:8388 or kcp://aes:123456@:8388
|
||||
> ss - standard shadowsocks proxy: ss://chacha20:123456@:8338
|
||||
|
||||
> redirect - transparent proxy,redirect://:12345
|
||||
> ssu - shadowsocks UDP relay server: ssu://chacha20:123456@:8338
|
||||
|
||||
> quic - QUIC tunnel: quic://:6121
|
||||
|
||||
> kcp - KCP tunnel: kcp://:8388 or kcp://aes:123456@:8388
|
||||
|
||||
> redirect - transparent proxy: redirect://:12345
|
||||
|
||||
> ssh - SSH proxy tunnel: ssh://:2222, SSH forward tunnel: forward+ssh://:2222
|
||||
|
||||
> obfs4 - obfs4 tunnel: obfs4://:8080
|
||||
|
||||
#### Port forwarding
|
||||
|
||||
@ -85,6 +98,8 @@ scheme://[bind_address]:port/[host]:hostport
|
||||
|
||||
#### Configuration file
|
||||
|
||||
Contributed by [@septs](https://github.com/septs).
|
||||
|
||||
> -C : specifies the configuration file path
|
||||
|
||||
The configuration file is in standard JSON format:
|
||||
@ -103,21 +118,13 @@ The configuration file is in standard JSON format:
|
||||
|
||||
ServeNodes is equivalent to the -L parameter, ChainNodes is equivalent to the -F parameter.
|
||||
|
||||
#### Logging
|
||||
|
||||
> -logtostderr : log to console
|
||||
|
||||
> -v=3 : log level (1-5),The higher the level, the more detailed the log (level 5 will enable HTTP2 debug)
|
||||
|
||||
> -log_dir=/log/dir/path : log to directory /log/dir/path
|
||||
|
||||
Usage
|
||||
------
|
||||
#### No forward proxy
|
||||
|
||||
<img src="https://ginuerzh.github.io/images/gost_01.png" />
|
||||
|
||||
* Standard HTTP/SOCKS5 proxy
|
||||
* Standard HTTP/SOCKS4/SOCKS5 proxy
|
||||
```bash
|
||||
gost -L=:8080
|
||||
```
|
||||
@ -142,7 +149,7 @@ test002 12345678
|
||||
|
||||
* Listen on multiple ports
|
||||
```bash
|
||||
gost -L=http2://:443 -L=socks://:1080 -L=ss://aes-128-cfb:123456@:8338
|
||||
gost -L=http2://:443 -L=socks5://:1080 -L=ss://aes-128-cfb:123456@:8338
|
||||
```
|
||||
|
||||
#### Forward proxy
|
||||
@ -161,50 +168,58 @@ gost -L=:8080 -F=http://admin:123456@192.168.1.1:8081
|
||||
|
||||
<img src="https://ginuerzh.github.io/images/gost_03.png" />
|
||||
```bash
|
||||
gost -L=:8080 -F=http+tls://192.168.1.1:443 -F=socks+ws://192.168.1.2:1080 -F=ss://aes-128-cfb:123456@192.168.1.3:8338 -F=a.b.c.d:NNNN
|
||||
gost -L=:8080 -F=quic://192.168.1.1:6121 -F=socks5+wss://192.168.1.2:1080 -F=http2://192.168.1.3:443 ... -F=a.b.c.d:NNNN
|
||||
```
|
||||
Gost forwards the request to a.b.c.d:NNNN through the proxy chain in the order set by -F,
|
||||
each forward proxy can be any HTTP/HTTPS/HTTP2/SOCKS5/Shadowsocks type.
|
||||
each forward proxy can be any HTTP/HTTPS/HTTP2/SOCKS4/SOCKS5/Shadowsocks type.
|
||||
|
||||
#### Local TCP port forwarding
|
||||
|
||||
```bash
|
||||
gost -L=tcp://:2222/192.168.1.1:22 -F=...
|
||||
gost -L=tcp://:2222/192.168.1.1:22 [-F=...]
|
||||
```
|
||||
The data on the local TCP port 2222 is forwarded to 192.168.1.1:22 (through the proxy chain). If the last node of the chain (the last -F parameter) is a SSH forwad tunnel, then gost will use the local port forwarding function of SSH directly:
|
||||
|
||||
```bash
|
||||
gost -L=tcp://:2222/192.168.1.1:22 -F forward+ssh://:2222
|
||||
```
|
||||
The data on the local TCP port 2222 is forwarded to 192.168.1.1:22 (through the proxy chain).
|
||||
|
||||
#### Local UDP port forwarding
|
||||
|
||||
```bash
|
||||
gost -L=udp://:5353/192.168.1.1:53?ttl=60 -F=...
|
||||
gost -L=udp://:5353/192.168.1.1:53?ttl=60 [-F=...]
|
||||
```
|
||||
The data on the local UDP port 5353 is forwarded to 192.168.1.1:53 (through the proxy chain).
|
||||
Each forwarding channel has a timeout period. When this time is exceeded and there is no data interaction during this time period, the channel will be closed. The timeout value can be set by the `ttl` parameter. The default value is 60 seconds.
|
||||
|
||||
**NOTE:** When forwarding UDP data, if there is a proxy chain, the end of the chain (the last -F parameter) must be gost SOCKS5 proxy.
|
||||
**NOTE:** When forwarding UDP data, if there is a proxy chain, the end of the chain (the last -F parameter) must be gost SOCKS5 proxy, gost will use UDP-over-TCP to forward data.
|
||||
|
||||
#### Remote TCP port forwarding
|
||||
|
||||
```bash
|
||||
gost -L=rtcp://:2222/192.168.1.1:22 -F=... -F=socks://172.24.10.1:1080
|
||||
gost -L=rtcp://:2222/192.168.1.1:22 [-F=...]
|
||||
```
|
||||
The data on 172.24.10.1:2222 is forwarded to 192.168.1.1:22 (through the proxy chain). If the last node of the chain (the last -F parameter) is a SSH tunnel, then gost will use the remote port forwarding function of SSH directly:
|
||||
|
||||
```bash
|
||||
gost -L=rtcp://:2222/192.168.1.1:22 -F forward+ssh://:2222
|
||||
```
|
||||
The data on 172.24.10.1:2222 is forwarded to 192.168.1.1:22 (through the proxy chain).
|
||||
|
||||
#### Remote UDP port forwarding
|
||||
|
||||
```bash
|
||||
gost -L=rudp://:5353/192.168.1.1:53 -F=... -F=socks://172.24.10.1:1080
|
||||
gost -L=rudp://:5353/192.168.1.1:53 [-F=...]
|
||||
```
|
||||
The data on 172.24.10.1:5353 is forwarded to 192.168.1.1:53 (through the proxy chain).
|
||||
|
||||
**NOTE:** To use the remote port forwarding feature, the proxy chain can not be empty (at least one -F parameter is set)
|
||||
and the end of the chain (last -F parameter) must be gost SOCKS5 proxy.
|
||||
**NOTE:** When forwarding UDP data, if there is a proxy chain, the end of the chain (the last -F parameter) must be gost SOCKS5 proxy, gost will use UDP-over-TCP to forward data.
|
||||
|
||||
#### HTTP2
|
||||
Gost HTTP2 supports two modes and self-adapting:
|
||||
Gost HTTP2 supports two modes:
|
||||
* As a standard HTTP2 proxy, and backwards-compatible with the HTTPS proxy.
|
||||
* As transport (similar to wss), tunnel other protocol.
|
||||
* As a transport tunnel.
|
||||
|
||||
##### Standard proxy
|
||||
Server:
|
||||
```bash
|
||||
gost -L=http2://:443
|
||||
@ -214,11 +229,15 @@ Client:
|
||||
gost -L=:8080 -F=http2://server_ip:443?ping=30
|
||||
```
|
||||
|
||||
The client supports the `ping` parameter to enable heartbeat detection (which is disabled by default).
|
||||
Parameter value represents heartbeat interval seconds.
|
||||
|
||||
**NOTE:** The proxy chain of gost supports only one HTTP2 proxy node and the nearest rule applies,
|
||||
the first HTTP2 proxy node is treated as an HTTP2 proxy, and the other HTTP2 proxy nodes are treated as HTTPS proxies.
|
||||
##### Tunnel
|
||||
服务端:
|
||||
```bash
|
||||
gost -L=h2://:443
|
||||
```
|
||||
客户端:
|
||||
```bash
|
||||
gost -L=:8080 -F=h2://server_ip:443
|
||||
```
|
||||
|
||||
#### QUIC
|
||||
Support for QUIC is based on library [quic-go](https://github.com/lucas-clemente/quic-go).
|
||||
@ -227,12 +246,12 @@ Server:
|
||||
```bash
|
||||
gost -L=quic://:6121
|
||||
```
|
||||
Client(Chrome):
|
||||
Client:
|
||||
```bash
|
||||
chrome --enable-quic --proxy-server=quic://server_ip:6121
|
||||
gost -L=:8080 -F=quic://server_ip:6121
|
||||
```
|
||||
|
||||
**NOTE:** Due to Chrome's limitations, it is currently only possible to access the HTTP (but not HTTPS) site through QUIC.
|
||||
**NOTE:** QUIC node can only be used as the first node of the proxy chain.
|
||||
|
||||
#### KCP
|
||||
Support for KCP is based on libraries [kcp-go](https://github.com/xtaci/kcp-go) and [kcptun](https://github.com/xtaci/kcptun).
|
||||
@ -246,25 +265,41 @@ Client:
|
||||
gost -L=:8080 -F=kcp://server_ip:8388
|
||||
```
|
||||
|
||||
Or manually specify the encryption method and password (Manually specifying the encryption method and password overwrites the corresponding value in the configuration file)
|
||||
|
||||
Server:
|
||||
```bash
|
||||
gost -L=kcp://aes:123456@:8388
|
||||
```
|
||||
|
||||
Client:
|
||||
```bash
|
||||
gost -L=:8080 -F=kcp://aes:123456@server_ip:8388
|
||||
```
|
||||
|
||||
Gost will automatically load kcp.json configuration file from current working directory if exists,
|
||||
or you can use the parameter to specify the path to the file.
|
||||
```bash
|
||||
gost -L=kcp://:8388?c=/path/to/conf/file
|
||||
```
|
||||
|
||||
**NOTE:** KCP will be enabled if and only if the proxy chain is not empty and the first proxy node (the first -F parameter) is of type KCP.
|
||||
**NOTE:** KCP node can only be used as the first node of the proxy chain.
|
||||
|
||||
#### SSH
|
||||
Gost SSH supports two modes:
|
||||
* As a forward tunnel, used by local/remote TCP port forwarding.
|
||||
* As a transport tunnel.
|
||||
|
||||
|
||||
##### Forward tunnel
|
||||
Server:
|
||||
```bash
|
||||
gost -L=forward+ssh://:2222
|
||||
```
|
||||
Client:
|
||||
```bash
|
||||
gost -L=rtcp://:1222/:22 -F=forward+ssh://server_ip:2222
|
||||
```
|
||||
|
||||
##### Transport tunnel
|
||||
Server:
|
||||
```bash
|
||||
gost -L=ssh://:2222
|
||||
```
|
||||
Client:
|
||||
```bash
|
||||
gost -L=:8080 -F=ssh://server_ip:2222?ping=60
|
||||
```
|
||||
|
||||
The client supports the ping parameter to enable heartbeat detection (which is disabled by default). Parameter value represents heartbeat interval seconds.
|
||||
|
||||
#### Transparent proxy
|
||||
Iptables-based transparent proxy
|
||||
@ -273,6 +308,25 @@ Iptables-based transparent proxy
|
||||
gost -L=redirect://:12345 -F=http2://server_ip:443
|
||||
```
|
||||
|
||||
|
||||
#### obfs4
|
||||
Contributed by [@isofew](https://github.com/isofew).
|
||||
|
||||
Server:
|
||||
```bash
|
||||
gost -L=obfs4://:443
|
||||
```
|
||||
|
||||
When the server is running normally, the console prints out the connection address for the client to use:
|
||||
```
|
||||
obfs4://:443/?cert=4UbQjIfjJEQHPOs8vs5sagrSXx1gfrDCGdVh2hpIPSKH0nklv1e4f29r7jb91VIrq4q5Jw&iat-mode=0
|
||||
```
|
||||
|
||||
Client:
|
||||
```
|
||||
gost -L=:8888 -F='obfs4://server_ip:443?cert=4UbQjIfjJEQHPOs8vs5sagrSXx1gfrDCGdVh2hpIPSKH0nklv1e4f29r7jb91VIrq4q5Jw&iat-mode=0'
|
||||
```
|
||||
|
||||
Encryption Mechanism
|
||||
------
|
||||
#### HTTP
|
||||
@ -288,8 +342,9 @@ gost -L=:8080 -F=http+tls://server_ip:443
|
||||
```
|
||||
|
||||
#### HTTP2
|
||||
Gost supports only the HTTP2 protocol that uses TLS encryption (h2) and does not support plaintext HTTP2 (h2c) transport.
|
||||
Gost HTTP2 proxy mode only supports the use of TLS encrypted HTTP2 protocol, does not support plaintext HTTP2.
|
||||
|
||||
Gost HTTP2 tunnel mode supports both encryption (h2) and plaintext (h2c) modes.
|
||||
|
||||
#### SOCKS5
|
||||
Gost supports the standard SOCKS5 protocol methods: no-auth (0x00) and user/pass (0x02),
|
||||
@ -307,22 +362,20 @@ gost -L=:8080 -F=socks://server_ip:1080
|
||||
If both ends are gosts (as example above), the data transfer will be encrypted (using tls or tls-auth).
|
||||
Otherwise, use standard SOCKS5 for communication (no-auth or user/pass).
|
||||
|
||||
**NOTE:** If transport already supports encryption (wss, tls, http2, kcp), SOCKS5 will no longer use the encryption method to prevent unnecessary double encryption.
|
||||
|
||||
#### Shadowsocks
|
||||
Support for shadowsocks is based on library [shadowsocks-go](https://github.com/shadowsocks/shadowsocks-go).
|
||||
|
||||
Server (The OTA mode can be enabled by the ota parameter. When enabled, the client must use OTA mode):
|
||||
Server:
|
||||
```bash
|
||||
gost -L=ss://aes-128-cfb:123456@:8338?ota=1
|
||||
gost -L=ss://aes-128-cfb:123456@:8338
|
||||
```
|
||||
Client (The OTA mode can be enabled by the ota parameter):
|
||||
Client:
|
||||
```bash
|
||||
gost -L=:8080 -F=ss://aes-128-cfb:123456@server_ip:8338?ota=1
|
||||
gost -L=:8080 -F=ss://aes-128-cfb:123456@server_ip:8338
|
||||
```
|
||||
|
||||
##### Shadowsocks UDP relay
|
||||
Currently, only the server supports UDP, and only OTA mode is supported.
|
||||
Currently, only the server supports UDP Relay.
|
||||
|
||||
Server:
|
||||
```bash
|
||||
@ -337,6 +390,13 @@ There is built-in TLS certificate in gost, if you need to use other TLS certific
|
||||
gost -L="http2://:443?cert=/path/to/my/cert/file&key=/path/to/my/key/file"
|
||||
```
|
||||
|
||||
|
||||
For client, you can specify a CA certificate to allow for [Certificate Pinning](https://en.wikipedia.org/wiki/Transport_Layer_Security#Certificate_pinning):
|
||||
```bash
|
||||
gost -L=:8080 -F="http2://:443?ca=ca.pem"
|
||||
```
|
||||
Certificate Pinning is contributed by [@sheerun](https://github.com/sheerun).
|
||||
|
||||
SOCKS5 UDP Data Processing
|
||||
------
|
||||
#### No forward proxy
|
||||
@ -353,7 +413,38 @@ Gost acts as the standard SOCKS5 proxy for UDP relay.
|
||||
|
||||
<img src="https://ginuerzh.github.io/images/udp03.png" height=200 />
|
||||
|
||||
When forward proxies are set, gost uses UDP-over-TCP to forward UDP data, proxy1 to proxyN can be any HTTP/HTTPS/HTTP2/SOCKS5/Shadowsocks type.
|
||||
When forward proxies are set, gost uses UDP-over-TCP to forward UDP data, proxy1 to proxyN can be any HTTP/HTTPS/HTTP2/SOCKS4/SOCKS5/Shadowsocks type.
|
||||
|
||||
Permission control
|
||||
------
|
||||
Contributed by [@sheerun](https://github.com/sheerun).
|
||||
|
||||
One can pass available permissions with `whitelist` and `blacklist` values when starting a socks and ssh server. The format for each rule is as follows: `[actions]:[hosts]:[ports]`.
|
||||
|
||||
`[actions]` are comma-separted list of allowed actions: `rtcp`, `rudp`, `tcp`, `udp`. can be `*` to encompass all actions.
|
||||
|
||||
`[hosts]` are comma-separated list of allowed hosts that one can bind on (in case of `rtcp` and `rudp`), or forward to (incase of `tcp` and `udp`). hosts support globs, like `*.google.com`. can be `*` to encompass all hosts.
|
||||
|
||||
`[ports]` are comma-separated list of ports that one can bind to (in case of `rtcp` and `rudp`), or forward to (incase of `tcp` and `udp`), can be `*` to encompass all ports.
|
||||
|
||||
Multiple permissions can be passed if seperated with `+`:
|
||||
|
||||
`rtcp,rudp:localhost,127.0.0.1:2222,8000-9000+udp:8.8.8.8,8.8.4.4:53` (allow for reverse tcp and udp binding on localhost and 127.0.0.1 on ports 2222 and 8000-9000 port range, plus allow for udp forwarding to 8.8.8.8 and 8.8.4.4 on port 53)
|
||||
|
||||
SSH remote port forwarding can only bind on 127.0.0.1:8000
|
||||
```bash
|
||||
gost -L=forward+ssh://localhost:8389?whitelist=rtcp:127.0.0.1:8000
|
||||
```
|
||||
|
||||
SOCKS5 TCP/UDP remote port forwarding can only bind on ports greater than 1000
|
||||
```bash
|
||||
gost -L=socks://localhost:8389?blacklist=rtcp,rudp:*:0-1000
|
||||
```
|
||||
|
||||
SOCKS5 UDP forwading can only forward to 8.8.8.8:53
|
||||
```bash
|
||||
gost -L=socks://localhost:8389?whitelist=udp:8.8.8.8:53
|
||||
```
|
||||
|
||||
Limitation
|
||||
------
|
||||
|
409
chain.go
409
chain.go
@ -1,381 +1,110 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/http2"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Proxy chain holds a list of proxy nodes
|
||||
type ProxyChain struct {
|
||||
nodes []ProxyNode
|
||||
lastNode *ProxyNode
|
||||
http2NodeIndex int
|
||||
http2Enabled bool
|
||||
http2Client *http.Client
|
||||
kcpEnabled bool
|
||||
kcpConfig *KCPConfig
|
||||
kcpSession *KCPSession
|
||||
kcpMutex sync.Mutex
|
||||
var (
|
||||
// ErrEmptyChain is an error that implies the chain is empty.
|
||||
ErrEmptyChain = errors.New("empty chain")
|
||||
)
|
||||
|
||||
// Chain is a proxy chain that holds a list of proxy nodes.
|
||||
type Chain struct {
|
||||
nodes []Node
|
||||
}
|
||||
|
||||
func NewProxyChain(nodes ...ProxyNode) *ProxyChain {
|
||||
chain := &ProxyChain{nodes: nodes, http2NodeIndex: -1}
|
||||
return chain
|
||||
}
|
||||
|
||||
func (c *ProxyChain) AddProxyNode(node ...ProxyNode) {
|
||||
c.nodes = append(c.nodes, node...)
|
||||
}
|
||||
|
||||
func (c *ProxyChain) AddProxyNodeString(snode ...string) error {
|
||||
for _, sn := range snode {
|
||||
node, err := ParseProxyNode(sn, false) // isServeNode == false
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.AddProxyNode(node)
|
||||
// NewChain creates a proxy chain with proxy nodes nodes.
|
||||
func NewChain(nodes ...Node) *Chain {
|
||||
return &Chain{
|
||||
nodes: nodes,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ProxyChain) Nodes() []ProxyNode {
|
||||
// Nodes returns the proxy nodes that the chain holds.
|
||||
func (c *Chain) Nodes() []Node {
|
||||
return c.nodes
|
||||
}
|
||||
|
||||
func (c *ProxyChain) GetNode(index int) *ProxyNode {
|
||||
if index < len(c.nodes) {
|
||||
return &c.nodes[index]
|
||||
// LastNode returns the last node of the node list.
|
||||
// If the chain is empty, an empty node is returns.
|
||||
func (c *Chain) LastNode() Node {
|
||||
if c.IsEmpty() {
|
||||
return Node{}
|
||||
}
|
||||
return nil
|
||||
return c.nodes[len(c.nodes)-1]
|
||||
}
|
||||
|
||||
func (c *ProxyChain) SetNode(index int, node ProxyNode) {
|
||||
if index < len(c.nodes) {
|
||||
c.nodes[index] = node
|
||||
}
|
||||
}
|
||||
|
||||
// Init initialize the proxy chain.
|
||||
// KCP will be enabled if the first proxy node is KCP proxy (transport == kcp).
|
||||
// HTTP2 will be enabled when at least one HTTP2 proxy node (scheme == http2) is present.
|
||||
//
|
||||
// NOTE: Should be called immediately when proxy nodes are ready.
|
||||
func (c *ProxyChain) Init() {
|
||||
length := len(c.nodes)
|
||||
if length == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
c.lastNode = &c.nodes[length-1]
|
||||
|
||||
// HTTP2 restrict: HTTP2 will be enabled when at least one HTTP2 proxy node is present.
|
||||
for i, node := range c.nodes {
|
||||
if node.Transport == "http2" {
|
||||
glog.V(LINFO).Infoln("HTTP2 is enabled")
|
||||
cfg := &tls.Config{
|
||||
InsecureSkipVerify: node.insecureSkipVerify(),
|
||||
ServerName: node.serverName,
|
||||
}
|
||||
c.http2NodeIndex = i
|
||||
c.initHttp2Client(cfg, c.nodes[:i]...)
|
||||
break // shortest chain for HTTP2
|
||||
}
|
||||
}
|
||||
|
||||
for i, node := range c.nodes {
|
||||
if node.Transport == "kcp" && i > 0 {
|
||||
glog.Fatal("KCP must be the first node in the proxy chain")
|
||||
}
|
||||
}
|
||||
|
||||
if c.nodes[0].Transport == "kcp" {
|
||||
glog.V(LINFO).Infoln("KCP is enabled")
|
||||
c.kcpEnabled = true
|
||||
config, err := ParseKCPConfig(c.nodes[0].Get("c"))
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln("[kcp]", err)
|
||||
}
|
||||
if config == nil {
|
||||
config = DefaultKCPConfig
|
||||
}
|
||||
if c.nodes[0].Users != nil {
|
||||
config.Crypt = c.nodes[0].Users[0].Username()
|
||||
config.Key, _ = c.nodes[0].Users[0].Password()
|
||||
}
|
||||
c.kcpConfig = config
|
||||
// AddNode appends the node(s) to the chain.
|
||||
func (c *Chain) AddNode(nodes ...Node) {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
c.nodes = append(c.nodes, nodes...)
|
||||
}
|
||||
|
||||
func (c *ProxyChain) KCPEnabled() bool {
|
||||
return c.kcpEnabled
|
||||
// IsEmpty checks if the chain is empty.
|
||||
// An empty chain means that there is no proxy node in the chain.
|
||||
func (c *Chain) IsEmpty() bool {
|
||||
return c == nil || len(c.nodes) == 0
|
||||
}
|
||||
|
||||
func (c *ProxyChain) Http2Enabled() bool {
|
||||
return c.http2Enabled
|
||||
}
|
||||
|
||||
func (c *ProxyChain) initHttp2Client(config *tls.Config, nodes ...ProxyNode) {
|
||||
if c.http2NodeIndex < 0 || c.http2NodeIndex >= len(c.nodes) {
|
||||
return
|
||||
}
|
||||
http2Node := c.nodes[c.http2NodeIndex]
|
||||
|
||||
tr := http2.Transport{
|
||||
TLSClientConfig: config,
|
||||
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
|
||||
// replace the default dialer with our proxy chain.
|
||||
conn, err := c.dialWithNodes(false, http2Node.Addr, nodes...)
|
||||
if err != nil {
|
||||
return conn, err
|
||||
}
|
||||
conn = tls.Client(conn, cfg)
|
||||
|
||||
// enable HTTP2 ping-pong
|
||||
pingIntvl, _ := strconv.Atoi(http2Node.Get("ping"))
|
||||
if pingIntvl > 0 {
|
||||
enablePing(conn, time.Duration(pingIntvl)*time.Second)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
},
|
||||
}
|
||||
c.http2Client = &http.Client{Transport: &tr}
|
||||
c.http2Enabled = true
|
||||
|
||||
}
|
||||
|
||||
func enablePing(conn net.Conn, interval time.Duration) {
|
||||
if conn == nil || interval == 0 {
|
||||
return
|
||||
// Dial connects to the target address addr through the chain.
|
||||
// If the chain is empty, it will use the net.Dial directly.
|
||||
func (c *Chain) Dial(addr string) (net.Conn, error) {
|
||||
if c.IsEmpty() {
|
||||
return net.Dial("tcp", addr)
|
||||
}
|
||||
|
||||
glog.V(LINFO).Infoln("[http2] ping enabled, interval:", interval)
|
||||
go func() {
|
||||
t := time.NewTicker(interval)
|
||||
var framer *http2.Framer
|
||||
for {
|
||||
select {
|
||||
case <-t.C:
|
||||
if framer == nil {
|
||||
framer = http2.NewFramer(conn, conn)
|
||||
}
|
||||
|
||||
var p [8]byte
|
||||
rand.Read(p[:])
|
||||
err := framer.WritePing(false, p)
|
||||
if err != nil {
|
||||
t.Stop()
|
||||
framer = nil
|
||||
glog.V(LWARNING).Infoln("[http2] ping:", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Connect to addr through proxy chain
|
||||
func (c *ProxyChain) Dial(addr string) (net.Conn, error) {
|
||||
if !strings.Contains(addr, ":") {
|
||||
addr += ":80"
|
||||
conn, err := c.Conn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.dialWithNodes(true, addr, c.nodes...)
|
||||
|
||||
cc, err := c.LastNode().Client.Connect(conn, addr)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// GetConn initializes a proxy chain connection,
|
||||
// if no proxy nodes on this chain, it will return error
|
||||
func (c *ProxyChain) GetConn() (net.Conn, error) {
|
||||
nodes := c.nodes
|
||||
if len(nodes) == 0 {
|
||||
// Conn obtains a handshaked connection to the last node of the chain.
|
||||
// If the chain is empty, it returns an ErrEmptyChain error.
|
||||
func (c *Chain) Conn() (net.Conn, error) {
|
||||
if c.IsEmpty() {
|
||||
return nil, ErrEmptyChain
|
||||
}
|
||||
|
||||
if c.Http2Enabled() {
|
||||
nodes = nodes[c.http2NodeIndex+1:]
|
||||
if len(nodes) == 0 {
|
||||
header := make(http.Header)
|
||||
header.Set("Proxy-Switch", "gost") // Flag header to indicate server to switch to HTTP2 transport mode
|
||||
conn, err := c.getHttp2Conn(header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
http2Node := c.nodes[c.http2NodeIndex]
|
||||
if http2Node.Transport == "http2" {
|
||||
http2Node.Transport = "h2"
|
||||
}
|
||||
if http2Node.Protocol == "http2" {
|
||||
http2Node.Protocol = "socks5" // assume it as socks5 protocol, so we can do much more things.
|
||||
}
|
||||
pc := NewProxyConn(conn, http2Node)
|
||||
if err := pc.Handshake(); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return pc, nil
|
||||
}
|
||||
}
|
||||
return c.travelNodes(true, nodes...)
|
||||
}
|
||||
|
||||
func (c *ProxyChain) dialWithNodes(withHttp2 bool, addr string, nodes ...ProxyNode) (conn net.Conn, err error) {
|
||||
if len(nodes) == 0 {
|
||||
return net.DialTimeout("tcp", addr, DialTimeout)
|
||||
}
|
||||
|
||||
if withHttp2 && c.Http2Enabled() {
|
||||
nodes = nodes[c.http2NodeIndex+1:]
|
||||
if len(nodes) == 0 {
|
||||
return c.http2Connect(addr)
|
||||
}
|
||||
}
|
||||
pc, err := c.travelNodes(withHttp2, nodes...)
|
||||
nodes := c.nodes
|
||||
conn, err := nodes[0].Client.Dial(nodes[0].Addr, nodes[0].DialOptions...)
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
if err = pc.Connect(addr); err != nil {
|
||||
pc.Close()
|
||||
return
|
||||
}
|
||||
conn = pc
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ProxyChain) travelNodes(withHttp2 bool, nodes ...ProxyNode) (conn *ProxyConn, err error) {
|
||||
defer func() {
|
||||
if err != nil && conn != nil {
|
||||
conn, err = nodes[0].Client.Handshake(conn, nodes[0].HandshakeOptions...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, node := range nodes {
|
||||
if i == len(nodes)-1 {
|
||||
break
|
||||
}
|
||||
|
||||
next := nodes[i+1]
|
||||
cc, err := node.Client.Connect(conn, next.Addr)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
conn = nil
|
||||
return nil, err
|
||||
}
|
||||
}()
|
||||
|
||||
var cc net.Conn
|
||||
node := nodes[0]
|
||||
|
||||
if withHttp2 && c.Http2Enabled() {
|
||||
cc, err = c.http2Connect(node.Addr)
|
||||
} else if node.Transport == "kcp" {
|
||||
cc, err = c.getKCPConn()
|
||||
} else {
|
||||
cc, err = net.DialTimeout("tcp", node.Addr, DialTimeout)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
setKeepAlive(cc, KeepAliveTime)
|
||||
|
||||
pc := NewProxyConn(cc, node)
|
||||
conn = pc
|
||||
if err = pc.Handshake(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, node := range nodes[1:] {
|
||||
if err = conn.Connect(node.Addr); err != nil {
|
||||
return
|
||||
}
|
||||
pc := NewProxyConn(conn, node)
|
||||
conn = pc
|
||||
if err = pc.Handshake(); err != nil {
|
||||
return
|
||||
cc, err = next.Client.Handshake(cc, next.HandshakeOptions...)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
conn = cc
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ProxyChain) initKCPSession() (err error) {
|
||||
c.kcpMutex.Lock()
|
||||
defer c.kcpMutex.Unlock()
|
||||
|
||||
if c.kcpSession == nil || c.kcpSession.IsClosed() {
|
||||
glog.V(LINFO).Infoln("[kcp] new kcp session")
|
||||
c.kcpSession, err = DialKCP(c.nodes[0].Addr, c.kcpConfig)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ProxyChain) getKCPConn() (conn net.Conn, err error) {
|
||||
if !c.KCPEnabled() {
|
||||
return nil, errors.New("KCP is not enabled")
|
||||
}
|
||||
|
||||
if err = c.initKCPSession(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.kcpSession.GetConn()
|
||||
}
|
||||
|
||||
// Initialize an HTTP2 transport if HTTP2 is enabled.
|
||||
func (c *ProxyChain) getHttp2Conn(header http.Header) (net.Conn, error) {
|
||||
if !c.Http2Enabled() {
|
||||
return nil, errors.New("HTTP2 is not enabled")
|
||||
}
|
||||
http2Node := c.nodes[c.http2NodeIndex]
|
||||
pr, pw := io.Pipe()
|
||||
|
||||
if header == nil {
|
||||
header = make(http.Header)
|
||||
}
|
||||
|
||||
req := http.Request{
|
||||
Method: http.MethodConnect,
|
||||
URL: &url.URL{Scheme: "https", Host: http2Node.Addr},
|
||||
Header: header,
|
||||
Proto: "HTTP/2.0",
|
||||
ProtoMajor: 2,
|
||||
ProtoMinor: 0,
|
||||
Body: pr,
|
||||
Host: http2Node.Addr,
|
||||
ContentLength: -1,
|
||||
}
|
||||
if glog.V(LDEBUG) {
|
||||
dump, _ := httputil.DumpRequest(&req, false)
|
||||
glog.Infoln(string(dump))
|
||||
}
|
||||
resp, err := c.http2Client.Do(&req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if glog.V(LDEBUG) {
|
||||
dump, _ := httputil.DumpResponse(resp, false)
|
||||
glog.Infoln(string(dump))
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
resp.Body.Close()
|
||||
return nil, errors.New(resp.Status)
|
||||
}
|
||||
conn := &http2Conn{r: resp.Body, w: pw}
|
||||
conn.remoteAddr, _ = net.ResolveTCPAddr("tcp", http2Node.Addr)
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// Use HTTP2 as transport to connect target addr.
|
||||
//
|
||||
// BUG: SOCKS5 is ignored, only HTTP supported
|
||||
func (c *ProxyChain) http2Connect(addr string) (net.Conn, error) {
|
||||
if !c.Http2Enabled() {
|
||||
return nil, errors.New("HTTP2 is not enabled")
|
||||
}
|
||||
http2Node := c.nodes[c.http2NodeIndex]
|
||||
|
||||
header := make(http.Header)
|
||||
header.Set("Gost-Target", addr) // Flag header to indicate the address that server connected to
|
||||
if http2Node.Users != nil {
|
||||
header.Set("Proxy-Authorization",
|
||||
"Basic "+base64.StdEncoding.EncodeToString([]byte(http2Node.Users[0].String())))
|
||||
}
|
||||
return c.getHttp2Conn(header)
|
||||
}
|
||||
|
174
client.go
Normal file
174
client.go
Normal file
@ -0,0 +1,174 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Client is a proxy client.
|
||||
// A client is divided into two layers: connector and transporter.
|
||||
// Connector is responsible for connecting to the destination address through this proxy.
|
||||
// Transporter performs a handshake with this proxy.
|
||||
type Client struct {
|
||||
Connector Connector
|
||||
Transporter Transporter
|
||||
}
|
||||
|
||||
// Dial connects to the target address.
|
||||
func (c *Client) Dial(addr string, options ...DialOption) (net.Conn, error) {
|
||||
return c.Transporter.Dial(addr, options...)
|
||||
}
|
||||
|
||||
// Handshake performs a handshake with the proxy over connection conn.
|
||||
func (c *Client) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {
|
||||
return c.Transporter.Handshake(conn, options...)
|
||||
}
|
||||
|
||||
// Connect connects to the address addr via the proxy over connection conn.
|
||||
func (c *Client) Connect(conn net.Conn, addr string) (net.Conn, error) {
|
||||
return c.Connector.Connect(conn, addr)
|
||||
}
|
||||
|
||||
// DefaultClient is a standard HTTP proxy client.
|
||||
var DefaultClient = &Client{Connector: HTTPConnector(nil), Transporter: TCPTransporter()}
|
||||
|
||||
// Dial connects to the address addr via the DefaultClient.
|
||||
func Dial(addr string, options ...DialOption) (net.Conn, error) {
|
||||
return DefaultClient.Dial(addr, options...)
|
||||
}
|
||||
|
||||
// Handshake performs a handshake via the DefaultClient.
|
||||
func Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {
|
||||
return DefaultClient.Handshake(conn, options...)
|
||||
}
|
||||
|
||||
// Connect connects to the address addr via the DefaultClient.
|
||||
func Connect(conn net.Conn, addr string) (net.Conn, error) {
|
||||
return DefaultClient.Connect(conn, addr)
|
||||
}
|
||||
|
||||
// Connector is responsible for connecting to the destination address.
|
||||
type Connector interface {
|
||||
Connect(conn net.Conn, addr string) (net.Conn, error)
|
||||
}
|
||||
|
||||
// Transporter is responsible for handshaking with the proxy server.
|
||||
type Transporter interface {
|
||||
Dial(addr string, options ...DialOption) (net.Conn, error)
|
||||
Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error)
|
||||
// Indicate that the Transporter supports multiplex
|
||||
Multiplex() bool
|
||||
}
|
||||
|
||||
type tcpTransporter struct {
|
||||
}
|
||||
|
||||
// TCPTransporter creates a transporter for TCP proxy client.
|
||||
func TCPTransporter() Transporter {
|
||||
return &tcpTransporter{}
|
||||
}
|
||||
|
||||
func (tr *tcpTransporter) Dial(addr string, options ...DialOption) (net.Conn, error) {
|
||||
opts := &DialOptions{}
|
||||
for _, option := range options {
|
||||
option(opts)
|
||||
}
|
||||
if opts.Chain == nil {
|
||||
return net.DialTimeout("tcp", addr, opts.Timeout)
|
||||
}
|
||||
return opts.Chain.Dial(addr)
|
||||
}
|
||||
|
||||
func (tr *tcpTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (tr *tcpTransporter) Multiplex() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// DialOptions describes the options for dialing.
|
||||
type DialOptions struct {
|
||||
Timeout time.Duration
|
||||
Chain *Chain
|
||||
}
|
||||
|
||||
// DialOption allows a common way to set dial options.
|
||||
type DialOption func(opts *DialOptions)
|
||||
|
||||
func TimeoutDialOption(timeout time.Duration) DialOption {
|
||||
return func(opts *DialOptions) {
|
||||
opts.Timeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
func ChainDialOption(chain *Chain) DialOption {
|
||||
return func(opts *DialOptions) {
|
||||
opts.Chain = chain
|
||||
}
|
||||
}
|
||||
|
||||
// HandshakeOptions describes the options for handshake.
|
||||
type HandshakeOptions struct {
|
||||
Addr string
|
||||
User *url.Userinfo
|
||||
Timeout time.Duration
|
||||
Interval time.Duration
|
||||
TLSConfig *tls.Config
|
||||
WSOptions *WSOptions
|
||||
KCPConfig *KCPConfig
|
||||
QUICConfig *QUICConfig
|
||||
}
|
||||
|
||||
// HandshakeOption allows a common way to set handshake options.
|
||||
type HandshakeOption func(opts *HandshakeOptions)
|
||||
|
||||
func AddrHandshakeOption(addr string) HandshakeOption {
|
||||
return func(opts *HandshakeOptions) {
|
||||
opts.Addr = addr
|
||||
}
|
||||
}
|
||||
|
||||
func UserHandshakeOption(user *url.Userinfo) HandshakeOption {
|
||||
return func(opts *HandshakeOptions) {
|
||||
opts.User = user
|
||||
}
|
||||
}
|
||||
|
||||
func TimeoutHandshakeOption(timeout time.Duration) HandshakeOption {
|
||||
return func(opts *HandshakeOptions) {
|
||||
opts.Timeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
func IntervalHandshakeOption(interval time.Duration) HandshakeOption {
|
||||
return func(opts *HandshakeOptions) {
|
||||
opts.Interval = interval
|
||||
}
|
||||
}
|
||||
|
||||
func TLSConfigHandshakeOption(config *tls.Config) HandshakeOption {
|
||||
return func(opts *HandshakeOptions) {
|
||||
opts.TLSConfig = config
|
||||
}
|
||||
}
|
||||
|
||||
func WSOptionsHandshakeOption(options *WSOptions) HandshakeOption {
|
||||
return func(opts *HandshakeOptions) {
|
||||
opts.WSOptions = options
|
||||
}
|
||||
}
|
||||
|
||||
func KCPConfigHandshakeOption(config *KCPConfig) HandshakeOption {
|
||||
return func(opts *HandshakeOptions) {
|
||||
opts.KCPConfig = config
|
||||
}
|
||||
}
|
||||
|
||||
func QUICConfigHandshakeOption(config *QUICConfig) HandshakeOption {
|
||||
return func(opts *HandshakeOptions) {
|
||||
opts.QUICConfig = config
|
||||
}
|
||||
}
|
@ -15,5 +15,7 @@
|
||||
"resend": 0,
|
||||
"nc": 0,
|
||||
"sockbuf": 4194304,
|
||||
"keepalive": 10
|
||||
"keepalive": 10,
|
||||
"snmplog": "",
|
||||
"snmpperiod": 60
|
||||
}
|
451
cmd/gost/main.go
451
cmd/gost/main.go
@ -1,22 +1,30 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/ginuerzh/gost"
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/http2"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ginuerzh/gost"
|
||||
"github.com/go-log/log"
|
||||
)
|
||||
|
||||
var (
|
||||
options struct {
|
||||
ChainNodes, ServeNodes flagStringList
|
||||
chainNodes, serveNodes stringList
|
||||
debugMode bool
|
||||
}
|
||||
)
|
||||
|
||||
@ -26,64 +34,370 @@ func init() {
|
||||
printVersion bool
|
||||
)
|
||||
|
||||
flag.Var(&options.chainNodes, "F", "forward address, can make a forward chain")
|
||||
flag.Var(&options.serveNodes, "L", "listen address, can listen on multiple ports")
|
||||
flag.StringVar(&configureFile, "C", "", "configure file")
|
||||
flag.Var(&options.ChainNodes, "F", "forward address, can make a forward chain")
|
||||
flag.Var(&options.ServeNodes, "L", "listen address, can listen on multiple ports")
|
||||
flag.BoolVar(&options.debugMode, "D", false, "enable debug log")
|
||||
flag.BoolVar(&printVersion, "V", false, "print version")
|
||||
flag.Parse()
|
||||
|
||||
if err := loadConfigureFile(configureFile); err != nil {
|
||||
glog.Fatal(err)
|
||||
}
|
||||
|
||||
if glog.V(5) {
|
||||
http2.VerboseLogs = true
|
||||
log.Log(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if flag.NFlag() == 0 {
|
||||
flag.PrintDefaults()
|
||||
return
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if printVersion {
|
||||
fmt.Fprintf(os.Stderr, "gost %s (%s)\n", gost.Version, runtime.Version())
|
||||
return
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
gost.Debug = options.debugMode
|
||||
}
|
||||
|
||||
func main() {
|
||||
chain := gost.NewProxyChain()
|
||||
if err := chain.AddProxyNodeString(options.ChainNodes...); err != nil {
|
||||
glog.Fatal(err)
|
||||
chain, err := initChain()
|
||||
if err != nil {
|
||||
log.Log(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
chain.Init()
|
||||
if err := serve(chain); err != nil {
|
||||
log.Log(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
select {}
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for _, ns := range options.ServeNodes {
|
||||
serverNode, err := gost.ParseProxyNode(ns, true) // isServeNode == true
|
||||
func initChain() (*gost.Chain, error) {
|
||||
chain := gost.NewChain()
|
||||
for _, ns := range options.chainNodes {
|
||||
node, err := gost.ParseNode(ns)
|
||||
if err != nil {
|
||||
glog.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
go func(node gost.ProxyNode) {
|
||||
defer wg.Done()
|
||||
certFile, keyFile := node.Get("cert"), node.Get("key")
|
||||
if certFile == "" {
|
||||
certFile = gost.DefaultCertFile
|
||||
serverName, _, _ := net.SplitHostPort(node.Addr)
|
||||
if serverName == "" {
|
||||
serverName = "localhost" // default server name
|
||||
}
|
||||
|
||||
rootCAs, err := loadCA(node.Values.Get("ca"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsCfg := &tls.Config{
|
||||
ServerName: serverName,
|
||||
InsecureSkipVerify: !toBool(node.Values.Get("scure")),
|
||||
RootCAs: rootCAs,
|
||||
}
|
||||
var tr gost.Transporter
|
||||
switch node.Transport {
|
||||
case "tls":
|
||||
tr = gost.TLSTransporter()
|
||||
case "ws":
|
||||
wsOpts := &gost.WSOptions{}
|
||||
wsOpts.EnableCompression = toBool(node.Values.Get("compression"))
|
||||
wsOpts.ReadBufferSize, _ = strconv.Atoi(node.Values.Get("rbuf"))
|
||||
wsOpts.WriteBufferSize, _ = strconv.Atoi(node.Values.Get("wbuf"))
|
||||
node.HandshakeOptions = append(node.HandshakeOptions,
|
||||
gost.WSOptionsHandshakeOption(wsOpts),
|
||||
)
|
||||
tr = gost.WSTransporter(nil)
|
||||
case "wss":
|
||||
tr = gost.WSSTransporter(nil)
|
||||
case "kcp":
|
||||
if !chain.IsEmpty() {
|
||||
return nil, errors.New("KCP must be the first node in the proxy chain")
|
||||
}
|
||||
if keyFile == "" {
|
||||
keyFile = gost.DefaultKeyFile
|
||||
}
|
||||
cert, err := gost.LoadCertificate(certFile, keyFile)
|
||||
config, err := parseKCPConfig(node.Values.Get("c"))
|
||||
if err != nil {
|
||||
glog.Fatal(err)
|
||||
log.Log("[kcp]", err)
|
||||
}
|
||||
server := gost.NewProxyServer(node, chain, &tls.Config{Certificates: []tls.Certificate{cert}})
|
||||
glog.Fatal(server.Serve())
|
||||
}(serverNode)
|
||||
node.HandshakeOptions = append(node.HandshakeOptions,
|
||||
gost.KCPConfigHandshakeOption(config),
|
||||
)
|
||||
tr = gost.KCPTransporter(nil)
|
||||
case "ssh":
|
||||
if node.Protocol == "direct" || node.Protocol == "remote" || node.Protocol == "forward" {
|
||||
tr = gost.SSHForwardTransporter()
|
||||
} else {
|
||||
tr = gost.SSHTunnelTransporter()
|
||||
}
|
||||
|
||||
node.DialOptions = append(node.DialOptions,
|
||||
gost.ChainDialOption(chain),
|
||||
)
|
||||
chain = gost.NewChain() // cutoff the chain for multiplex
|
||||
case "quic":
|
||||
if !chain.IsEmpty() {
|
||||
return nil, errors.New("QUIC must be the first node in the proxy chain")
|
||||
}
|
||||
config := &gost.QUICConfig{
|
||||
TLSConfig: tlsCfg,
|
||||
KeepAlive: toBool(node.Values.Get("keepalive")),
|
||||
}
|
||||
node.HandshakeOptions = append(node.HandshakeOptions,
|
||||
gost.QUICConfigHandshakeOption(config),
|
||||
)
|
||||
tr = gost.QUICTransporter(nil)
|
||||
case "http2":
|
||||
tr = gost.HTTP2Transporter(nil)
|
||||
node.DialOptions = append(node.DialOptions,
|
||||
gost.ChainDialOption(chain),
|
||||
)
|
||||
chain = gost.NewChain() // cutoff the chain for multiplex
|
||||
case "h2":
|
||||
tr = gost.H2Transporter(nil)
|
||||
node.DialOptions = append(node.DialOptions,
|
||||
gost.ChainDialOption(chain),
|
||||
)
|
||||
chain = gost.NewChain() // cutoff the chain for multiplex
|
||||
case "h2c":
|
||||
tr = gost.H2CTransporter()
|
||||
node.DialOptions = append(node.DialOptions,
|
||||
gost.ChainDialOption(chain),
|
||||
)
|
||||
chain = gost.NewChain() // cutoff the chain for multiplex
|
||||
case "obfs4":
|
||||
if err := gost.Obfs4Init(node, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tr = gost.Obfs4Transporter()
|
||||
default:
|
||||
tr = gost.TCPTransporter()
|
||||
}
|
||||
|
||||
var connector gost.Connector
|
||||
switch node.Protocol {
|
||||
case "http2":
|
||||
connector = gost.HTTP2Connector(node.User)
|
||||
case "socks", "socks5":
|
||||
connector = gost.SOCKS5Connector(node.User)
|
||||
case "socks4":
|
||||
connector = gost.SOCKS4Connector()
|
||||
case "socks4a":
|
||||
connector = gost.SOCKS4AConnector()
|
||||
case "ss":
|
||||
connector = gost.ShadowConnector(node.User)
|
||||
case "direct", "forward":
|
||||
connector = gost.SSHDirectForwardConnector()
|
||||
case "remote":
|
||||
connector = gost.SSHRemoteForwardConnector()
|
||||
case "http":
|
||||
fallthrough
|
||||
default:
|
||||
node.Protocol = "http" // default protocol is HTTP
|
||||
connector = gost.HTTPConnector(node.User)
|
||||
}
|
||||
|
||||
node.DialOptions = append(node.DialOptions,
|
||||
gost.TimeoutDialOption(gost.DialTimeout),
|
||||
)
|
||||
|
||||
interval, _ := strconv.Atoi(node.Values.Get("ping"))
|
||||
node.HandshakeOptions = append(node.HandshakeOptions,
|
||||
gost.AddrHandshakeOption(node.Addr),
|
||||
gost.UserHandshakeOption(node.User),
|
||||
gost.TLSConfigHandshakeOption(tlsCfg),
|
||||
gost.IntervalHandshakeOption(time.Duration(interval)*time.Second),
|
||||
)
|
||||
node.Client = &gost.Client{
|
||||
Connector: connector,
|
||||
Transporter: tr,
|
||||
}
|
||||
chain.AddNode(node)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
return chain, nil
|
||||
}
|
||||
|
||||
func serve(chain *gost.Chain) error {
|
||||
for _, ns := range options.serveNodes {
|
||||
node, err := gost.ParseNode(ns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
users, err := parseUsers(node.Values.Get("secrets"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if node.User != nil {
|
||||
users = append(users, node.User)
|
||||
}
|
||||
tlsCfg, _ := tlsConfig(node.Values.Get("cert"), node.Values.Get("key"))
|
||||
|
||||
var ln gost.Listener
|
||||
switch node.Transport {
|
||||
case "tls":
|
||||
ln, err = gost.TLSListener(node.Addr, tlsCfg)
|
||||
case "ws":
|
||||
wsOpts := &gost.WSOptions{}
|
||||
wsOpts.EnableCompression = toBool(node.Values.Get("compression"))
|
||||
wsOpts.ReadBufferSize, _ = strconv.Atoi(node.Values.Get("rbuf"))
|
||||
wsOpts.WriteBufferSize, _ = strconv.Atoi(node.Values.Get("wbuf"))
|
||||
ln, err = gost.WSListener(node.Addr, wsOpts)
|
||||
case "wss":
|
||||
wsOpts := &gost.WSOptions{}
|
||||
wsOpts.EnableCompression = toBool(node.Values.Get("compression"))
|
||||
wsOpts.ReadBufferSize, _ = strconv.Atoi(node.Values.Get("rbuf"))
|
||||
wsOpts.WriteBufferSize, _ = strconv.Atoi(node.Values.Get("wbuf"))
|
||||
ln, err = gost.WSSListener(node.Addr, tlsCfg, wsOpts)
|
||||
case "kcp":
|
||||
config, err := parseKCPConfig(node.Values.Get("c"))
|
||||
if err != nil {
|
||||
log.Log("[kcp]", err)
|
||||
}
|
||||
ln, err = gost.KCPListener(node.Addr, config)
|
||||
case "ssh":
|
||||
config := &gost.SSHConfig{
|
||||
Users: users,
|
||||
TLSConfig: tlsCfg,
|
||||
}
|
||||
if node.Protocol == "forward" {
|
||||
ln, err = gost.TCPListener(node.Addr)
|
||||
} else {
|
||||
ln, err = gost.SSHTunnelListener(node.Addr, config)
|
||||
}
|
||||
case "quic":
|
||||
config := &gost.QUICConfig{
|
||||
TLSConfig: tlsCfg,
|
||||
KeepAlive: toBool(node.Values.Get("keepalive")),
|
||||
}
|
||||
timeout, _ := strconv.Atoi(node.Values.Get("timeout"))
|
||||
config.Timeout = time.Duration(timeout) * time.Second
|
||||
ln, err = gost.QUICListener(node.Addr, config)
|
||||
case "http2":
|
||||
ln, err = gost.HTTP2Listener(node.Addr, tlsCfg)
|
||||
case "h2":
|
||||
ln, err = gost.H2Listener(node.Addr, tlsCfg)
|
||||
case "h2c":
|
||||
ln, err = gost.H2CListener(node.Addr)
|
||||
case "obfs4":
|
||||
if err = gost.Obfs4Init(node, true); err != nil {
|
||||
return err
|
||||
}
|
||||
ln, err = gost.Obfs4Listener(node.Addr)
|
||||
case "tcp":
|
||||
ln, err = gost.TCPListener(node.Addr)
|
||||
case "rtcp":
|
||||
if chain.LastNode().Protocol == "forward" && chain.LastNode().Transport == "ssh" {
|
||||
chain.Nodes()[len(chain.Nodes())-1].Client.Connector = gost.SSHRemoteForwardConnector()
|
||||
}
|
||||
ln, err = gost.TCPRemoteForwardListener(node.Addr, chain)
|
||||
case "udp":
|
||||
ttl, _ := strconv.Atoi(node.Values.Get("ttl"))
|
||||
ln, err = gost.UDPDirectForwardListener(node.Addr, time.Duration(ttl)*time.Second)
|
||||
case "rudp":
|
||||
ttl, _ := strconv.Atoi(node.Values.Get("ttl"))
|
||||
ln, err = gost.UDPRemoteForwardListener(node.Addr, chain, time.Duration(ttl)*time.Second)
|
||||
case "redirect":
|
||||
ln, err = gost.TCPListener(node.Addr)
|
||||
case "ssu":
|
||||
ttl, _ := strconv.Atoi(node.Values.Get("ttl"))
|
||||
ln, err = gost.ShadowUDPListener(node.Addr, node.User, time.Duration(ttl)*time.Second)
|
||||
default:
|
||||
ln, err = gost.TCPListener(node.Addr)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var whitelist, blacklist *gost.Permissions
|
||||
if node.Values.Get("whitelist") != "" {
|
||||
if whitelist, err = gost.ParsePermissions(node.Values.Get("whitelist")); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// By default allow for everyting
|
||||
whitelist, _ = gost.ParsePermissions("*:*:*")
|
||||
}
|
||||
|
||||
if node.Values.Get("blacklist") != "" {
|
||||
if blacklist, err = gost.ParsePermissions(node.Values.Get("blacklist")); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// By default block nothing
|
||||
blacklist, _ = gost.ParsePermissions("")
|
||||
}
|
||||
|
||||
var handlerOptions []gost.HandlerOption
|
||||
|
||||
handlerOptions = append(handlerOptions,
|
||||
gost.AddrHandlerOption(node.Addr),
|
||||
gost.ChainHandlerOption(chain),
|
||||
gost.UsersHandlerOption(users...),
|
||||
gost.TLSConfigHandlerOption(tlsCfg),
|
||||
gost.WhitelistHandlerOption(whitelist),
|
||||
gost.BlacklistHandlerOption(blacklist),
|
||||
)
|
||||
var handler gost.Handler
|
||||
switch node.Protocol {
|
||||
case "http2":
|
||||
handler = gost.HTTP2Handler(handlerOptions...)
|
||||
case "socks", "socks5":
|
||||
handler = gost.SOCKS5Handler(handlerOptions...)
|
||||
case "socks4", "socks4a":
|
||||
handler = gost.SOCKS4Handler(handlerOptions...)
|
||||
case "ss":
|
||||
handler = gost.ShadowHandler(handlerOptions...)
|
||||
case "http":
|
||||
handler = gost.HTTPHandler(handlerOptions...)
|
||||
case "tcp":
|
||||
handler = gost.TCPDirectForwardHandler(node.Remote, handlerOptions...)
|
||||
case "rtcp":
|
||||
handler = gost.TCPRemoteForwardHandler(node.Remote, handlerOptions...)
|
||||
case "udp":
|
||||
handler = gost.UDPDirectForwardHandler(node.Remote, handlerOptions...)
|
||||
case "rudp":
|
||||
handler = gost.UDPRemoteForwardHandler(node.Remote, handlerOptions...)
|
||||
case "forward":
|
||||
handler = gost.SSHForwardHandler(handlerOptions...)
|
||||
case "redirect":
|
||||
handler = gost.TCPRedirectHandler(handlerOptions...)
|
||||
case "ssu":
|
||||
handler = gost.ShadowUDPdHandler(handlerOptions...)
|
||||
default:
|
||||
handler = gost.AutoHandler(handlerOptions...)
|
||||
}
|
||||
go new(gost.Server).Serve(ln, handler)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Load the certificate from cert and key files, will use the default certificate if the provided info are invalid.
|
||||
func tlsConfig(certFile, keyFile string) (*tls.Config, error) {
|
||||
if certFile == "" {
|
||||
certFile = "cert.pem"
|
||||
}
|
||||
if keyFile == "" {
|
||||
keyFile = "key.pem"
|
||||
}
|
||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tls.Config{Certificates: []tls.Certificate{cert}}, nil
|
||||
}
|
||||
|
||||
func loadCA(caFile string) (cp *x509.CertPool, err error) {
|
||||
if caFile == "" {
|
||||
return
|
||||
}
|
||||
cp = x509.NewCertPool()
|
||||
data, err := ioutil.ReadFile(caFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !cp.AppendCertsFromPEM(data) {
|
||||
return nil, errors.New("AppendCertsFromPEM failed")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func loadConfigureFile(configureFile string) error {
|
||||
@ -100,12 +414,65 @@ func loadConfigureFile(configureFile string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type flagStringList []string
|
||||
type stringList []string
|
||||
|
||||
func (this *flagStringList) String() string {
|
||||
return fmt.Sprintf("%s", *this)
|
||||
func (l *stringList) String() string {
|
||||
return fmt.Sprintf("%s", *l)
|
||||
}
|
||||
func (this *flagStringList) Set(value string) error {
|
||||
*this = append(*this, value)
|
||||
func (l *stringList) Set(value string) error {
|
||||
*l = append(*l, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func toBool(s string) bool {
|
||||
if b, _ := strconv.ParseBool(s); b {
|
||||
return b
|
||||
}
|
||||
n, _ := strconv.Atoi(s)
|
||||
return n > 0
|
||||
}
|
||||
|
||||
func parseKCPConfig(configFile string) (*gost.KCPConfig, error) {
|
||||
if configFile == "" {
|
||||
return nil, nil
|
||||
}
|
||||
file, err := os.Open(configFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
config := &gost.KCPConfig{}
|
||||
if err = json.NewDecoder(file).Decode(config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func parseUsers(authFile string) (users []*url.Userinfo, err error) {
|
||||
if authFile == "" {
|
||||
return
|
||||
}
|
||||
|
||||
file, err := os.Open(authFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
|
||||
s := strings.SplitN(line, " ", 2)
|
||||
if len(s) == 1 {
|
||||
users = append(users, url.User(strings.TrimSpace(s[0])))
|
||||
} else if len(s) == 2 {
|
||||
users = append(users, url.UserPassword(strings.TrimSpace(s[0]), strings.TrimSpace(s[1])))
|
||||
}
|
||||
}
|
||||
|
||||
err = scanner.Err()
|
||||
return
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
# username password
|
||||
|
||||
test\admin 123456
|
||||
$test 123456
|
||||
test001 123456
|
||||
test002 12345678
|
8
cmd/gost/vendor/github.com/codahale/chacha20/README.md
generated
vendored
8
cmd/gost/vendor/github.com/codahale/chacha20/README.md
generated
vendored
@ -1,8 +0,0 @@
|
||||
chacha20
|
||||
========
|
||||
|
||||
[](https://travis-ci.org/codahale/chacha20)
|
||||
|
||||
A pure Go implementation of the ChaCha20 stream cipher.
|
||||
|
||||
For documentation, check [godoc](http://godoc.org/github.com/codahale/chacha20).
|
235
cmd/gost/vendor/github.com/codahale/chacha20/chacha20.go
generated
vendored
235
cmd/gost/vendor/github.com/codahale/chacha20/chacha20.go
generated
vendored
@ -1,235 +0,0 @@
|
||||
// Package chacha20 provides a pure Go implementation of ChaCha20, a fast,
|
||||
// secure stream cipher.
|
||||
//
|
||||
// From Bernstein, Daniel J. "ChaCha, a variant of Salsa20." Workshop Record of
|
||||
// SASC. 2008. (http://cr.yp.to/chacha/chacha-20080128.pdf):
|
||||
//
|
||||
// ChaCha8 is a 256-bit stream cipher based on the 8-round cipher Salsa20/8.
|
||||
// The changes from Salsa20/8 to ChaCha8 are designed to improve diffusion per
|
||||
// round, conjecturally increasing resistance to cryptanalysis, while
|
||||
// preserving -- and often improving -- time per round. ChaCha12 and ChaCha20
|
||||
// are analogous modifications of the 12-round and 20-round ciphers Salsa20/12
|
||||
// and Salsa20/20. This paper presents the ChaCha family and explains the
|
||||
// differences between Salsa20 and ChaCha.
|
||||
//
|
||||
// For more information, see http://cr.yp.to/chacha.html
|
||||
package chacha20
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// KeySize is the length of ChaCha20 keys, in bytes.
|
||||
KeySize = 32
|
||||
// NonceSize is the length of ChaCha20 nonces, in bytes.
|
||||
NonceSize = 8
|
||||
// XNonceSize is the length of XChaCha20 nonces, in bytes.
|
||||
XNonceSize = 24
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidKey is returned when the provided key is not 256 bits long.
|
||||
ErrInvalidKey = errors.New("invalid key length (must be 256 bits)")
|
||||
// ErrInvalidNonce is returned when the provided nonce is not 64 bits long.
|
||||
ErrInvalidNonce = errors.New("invalid nonce length (must be 64 bits)")
|
||||
// ErrInvalidXNonce is returned when the provided nonce is not 192 bits
|
||||
// long.
|
||||
ErrInvalidXNonce = errors.New("invalid nonce length (must be 192 bits)")
|
||||
// ErrInvalidRounds is returned when the provided rounds is not
|
||||
// 8, 12, or 20.
|
||||
ErrInvalidRounds = errors.New("invalid rounds number (must be 8, 12, or 20)")
|
||||
)
|
||||
|
||||
// New creates and returns a new cipher.Stream. The key argument must be 256
|
||||
// bits long, and the nonce argument must be 64 bits long. The nonce must be
|
||||
// randomly generated or used only once. This Stream instance must not be used
|
||||
// to encrypt more than 2^70 bytes (~1 zettabyte).
|
||||
func New(key []byte, nonce []byte) (cipher.Stream, error) {
|
||||
return NewWithRounds(key, nonce, 20)
|
||||
}
|
||||
|
||||
// NewWithRounds creates and returns a new cipher.Stream just like New but
|
||||
// the rounds number of 8, 12, or 20 can be specified.
|
||||
func NewWithRounds(key []byte, nonce []byte, rounds uint8) (cipher.Stream, error) {
|
||||
if len(key) != KeySize {
|
||||
return nil, ErrInvalidKey
|
||||
}
|
||||
|
||||
if len(nonce) != NonceSize {
|
||||
return nil, ErrInvalidNonce
|
||||
}
|
||||
|
||||
if (rounds != 8) && (rounds != 12) && (rounds != 20) {
|
||||
return nil, ErrInvalidRounds
|
||||
}
|
||||
|
||||
s := new(stream)
|
||||
s.init(key, nonce, rounds)
|
||||
s.advance()
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// NewXChaCha creates and returns a new cipher.Stream. The key argument must be
|
||||
// 256 bits long, and the nonce argument must be 192 bits long. The nonce must
|
||||
// be randomly generated or only used once. This Stream instance must not be
|
||||
// used to encrypt more than 2^70 bytes (~1 zetta byte).
|
||||
func NewXChaCha(key []byte, nonce []byte) (cipher.Stream, error) {
|
||||
return NewXChaChaWithRounds(key, nonce, 20)
|
||||
}
|
||||
|
||||
// NewXChaChaWithRounds creates and returns a new cipher.Stream just like
|
||||
// NewXChaCha but the rounds number of 8, 12, or 20 can be specified.
|
||||
func NewXChaChaWithRounds(key []byte, nonce []byte, rounds uint8) (cipher.Stream, error) {
|
||||
if len(key) != KeySize {
|
||||
return nil, ErrInvalidKey
|
||||
}
|
||||
|
||||
if len(nonce) != XNonceSize {
|
||||
return nil, ErrInvalidXNonce
|
||||
}
|
||||
|
||||
if (rounds != 8) && (rounds != 12) && (rounds != 20) {
|
||||
return nil, ErrInvalidRounds
|
||||
}
|
||||
|
||||
s := new(stream)
|
||||
s.init(key, nonce, rounds)
|
||||
|
||||
// Call HChaCha to derive the subkey using the key and the first 16 bytes
|
||||
// of the nonce, and re-initialize the state using the subkey and the
|
||||
// remaining nonce.
|
||||
blockArr := (*[stateSize]uint32)(unsafe.Pointer(&s.block))
|
||||
core(&s.state, blockArr, s.rounds, true)
|
||||
copy(s.state[4:8], blockArr[0:4])
|
||||
copy(s.state[8:12], blockArr[12:16])
|
||||
s.state[12] = 0
|
||||
s.state[13] = 0
|
||||
s.state[14] = binary.LittleEndian.Uint32(nonce[16:])
|
||||
s.state[15] = binary.LittleEndian.Uint32(nonce[20:])
|
||||
|
||||
s.advance()
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
type stream struct {
|
||||
state [stateSize]uint32 // the state as an array of 16 32-bit words
|
||||
block [blockSize]byte // the keystream as an array of 64 bytes
|
||||
offset int // the offset of used bytes in block
|
||||
rounds uint8
|
||||
}
|
||||
|
||||
func (s *stream) XORKeyStream(dst, src []byte) {
|
||||
// Stride over the input in 64-byte blocks, minus the amount of keystream
|
||||
// previously used. This will produce best results when processing blocks
|
||||
// of a size evenly divisible by 64.
|
||||
i := 0
|
||||
max := len(src)
|
||||
for i < max {
|
||||
gap := blockSize - s.offset
|
||||
|
||||
limit := i + gap
|
||||
if limit > max {
|
||||
limit = max
|
||||
}
|
||||
|
||||
o := s.offset
|
||||
for j := i; j < limit; j++ {
|
||||
dst[j] = src[j] ^ s.block[o]
|
||||
o++
|
||||
}
|
||||
|
||||
i += gap
|
||||
s.offset = o
|
||||
|
||||
if o == blockSize {
|
||||
s.advance()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stream) init(key []byte, nonce []byte, rounds uint8) {
|
||||
// the magic constants for 256-bit keys
|
||||
s.state[0] = 0x61707865
|
||||
s.state[1] = 0x3320646e
|
||||
s.state[2] = 0x79622d32
|
||||
s.state[3] = 0x6b206574
|
||||
|
||||
s.state[4] = binary.LittleEndian.Uint32(key[0:])
|
||||
s.state[5] = binary.LittleEndian.Uint32(key[4:])
|
||||
s.state[6] = binary.LittleEndian.Uint32(key[8:])
|
||||
s.state[7] = binary.LittleEndian.Uint32(key[12:])
|
||||
s.state[8] = binary.LittleEndian.Uint32(key[16:])
|
||||
s.state[9] = binary.LittleEndian.Uint32(key[20:])
|
||||
s.state[10] = binary.LittleEndian.Uint32(key[24:])
|
||||
s.state[11] = binary.LittleEndian.Uint32(key[28:])
|
||||
|
||||
switch len(nonce) {
|
||||
case NonceSize:
|
||||
// ChaCha20 uses 8 byte nonces.
|
||||
s.state[12] = 0
|
||||
s.state[13] = 0
|
||||
s.state[14] = binary.LittleEndian.Uint32(nonce[0:])
|
||||
s.state[15] = binary.LittleEndian.Uint32(nonce[4:])
|
||||
case XNonceSize:
|
||||
// XChaCha20 derives the subkey via HChaCha initialized
|
||||
// with the first 16 bytes of the nonce.
|
||||
s.state[12] = binary.LittleEndian.Uint32(nonce[0:])
|
||||
s.state[13] = binary.LittleEndian.Uint32(nonce[4:])
|
||||
s.state[14] = binary.LittleEndian.Uint32(nonce[8:])
|
||||
s.state[15] = binary.LittleEndian.Uint32(nonce[12:])
|
||||
default:
|
||||
// Never happens, both ctors validate the nonce length.
|
||||
panic("invalid nonce size")
|
||||
}
|
||||
|
||||
s.rounds = rounds
|
||||
}
|
||||
|
||||
// BUG(codahale): Totally untested on big-endian CPUs. Would very much
|
||||
// appreciate someone with an ARM device giving this a swing.
|
||||
|
||||
// advances the keystream
|
||||
func (s *stream) advance() {
|
||||
core(&s.state, (*[stateSize]uint32)(unsafe.Pointer(&s.block)), s.rounds, false)
|
||||
|
||||
if bigEndian {
|
||||
j := blockSize - 1
|
||||
for i := 0; i < blockSize/2; i++ {
|
||||
s.block[j], s.block[i] = s.block[i], s.block[j]
|
||||
j--
|
||||
}
|
||||
}
|
||||
|
||||
s.offset = 0
|
||||
i := s.state[12] + 1
|
||||
s.state[12] = i
|
||||
if i == 0 {
|
||||
s.state[13]++
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
wordSize = 4 // the size of ChaCha20's words
|
||||
stateSize = 16 // the size of ChaCha20's state, in words
|
||||
blockSize = stateSize * wordSize // the size of ChaCha20's block, in bytes
|
||||
)
|
||||
|
||||
var (
|
||||
bigEndian bool // whether or not we're running on a bigEndian CPU
|
||||
)
|
||||
|
||||
// Do some up-front bookkeeping on what sort of CPU we're using. ChaCha20 treats
|
||||
// its state as a little-endian byte array when it comes to generating the
|
||||
// keystream, which allows for a zero-copy approach to the core transform. On
|
||||
// big-endian architectures, we have to take a hit to reverse the bytes.
|
||||
func init() {
|
||||
x := uint32(0x04030201)
|
||||
y := [4]byte{0x1, 0x2, 0x3, 0x4}
|
||||
bigEndian = *(*[4]byte)(unsafe.Pointer(&x)) != y
|
||||
}
|
166
cmd/gost/vendor/github.com/codahale/chacha20/core_ref.go
generated
vendored
166
cmd/gost/vendor/github.com/codahale/chacha20/core_ref.go
generated
vendored
@ -1,166 +0,0 @@
|
||||
// The ChaCha20 core transform.
|
||||
// An unrolled and inlined implementation in pure Go.
|
||||
|
||||
package chacha20
|
||||
|
||||
func core(input, output *[stateSize]uint32, rounds uint8, hchacha bool) {
|
||||
var (
|
||||
x00 = input[0]
|
||||
x01 = input[1]
|
||||
x02 = input[2]
|
||||
x03 = input[3]
|
||||
x04 = input[4]
|
||||
x05 = input[5]
|
||||
x06 = input[6]
|
||||
x07 = input[7]
|
||||
x08 = input[8]
|
||||
x09 = input[9]
|
||||
x10 = input[10]
|
||||
x11 = input[11]
|
||||
x12 = input[12]
|
||||
x13 = input[13]
|
||||
x14 = input[14]
|
||||
x15 = input[15]
|
||||
)
|
||||
|
||||
var x uint32
|
||||
|
||||
// Unrolling all 20 rounds kills performance on modern Intel processors
|
||||
// (Tested on a i5 Haswell, likely applies to Sandy Bridge+), due to uop
|
||||
// cache thrashing. The straight forward 2 rounds per loop implementation
|
||||
// of this has double the performance of the fully unrolled version.
|
||||
for i := uint8(0); i < rounds; i += 2 {
|
||||
x00 += x04
|
||||
x = x12 ^ x00
|
||||
x12 = (x << 16) | (x >> 16)
|
||||
x08 += x12
|
||||
x = x04 ^ x08
|
||||
x04 = (x << 12) | (x >> 20)
|
||||
x00 += x04
|
||||
x = x12 ^ x00
|
||||
x12 = (x << 8) | (x >> 24)
|
||||
x08 += x12
|
||||
x = x04 ^ x08
|
||||
x04 = (x << 7) | (x >> 25)
|
||||
x01 += x05
|
||||
x = x13 ^ x01
|
||||
x13 = (x << 16) | (x >> 16)
|
||||
x09 += x13
|
||||
x = x05 ^ x09
|
||||
x05 = (x << 12) | (x >> 20)
|
||||
x01 += x05
|
||||
x = x13 ^ x01
|
||||
x13 = (x << 8) | (x >> 24)
|
||||
x09 += x13
|
||||
x = x05 ^ x09
|
||||
x05 = (x << 7) | (x >> 25)
|
||||
x02 += x06
|
||||
x = x14 ^ x02
|
||||
x14 = (x << 16) | (x >> 16)
|
||||
x10 += x14
|
||||
x = x06 ^ x10
|
||||
x06 = (x << 12) | (x >> 20)
|
||||
x02 += x06
|
||||
x = x14 ^ x02
|
||||
x14 = (x << 8) | (x >> 24)
|
||||
x10 += x14
|
||||
x = x06 ^ x10
|
||||
x06 = (x << 7) | (x >> 25)
|
||||
x03 += x07
|
||||
x = x15 ^ x03
|
||||
x15 = (x << 16) | (x >> 16)
|
||||
x11 += x15
|
||||
x = x07 ^ x11
|
||||
x07 = (x << 12) | (x >> 20)
|
||||
x03 += x07
|
||||
x = x15 ^ x03
|
||||
x15 = (x << 8) | (x >> 24)
|
||||
x11 += x15
|
||||
x = x07 ^ x11
|
||||
x07 = (x << 7) | (x >> 25)
|
||||
x00 += x05
|
||||
x = x15 ^ x00
|
||||
x15 = (x << 16) | (x >> 16)
|
||||
x10 += x15
|
||||
x = x05 ^ x10
|
||||
x05 = (x << 12) | (x >> 20)
|
||||
x00 += x05
|
||||
x = x15 ^ x00
|
||||
x15 = (x << 8) | (x >> 24)
|
||||
x10 += x15
|
||||
x = x05 ^ x10
|
||||
x05 = (x << 7) | (x >> 25)
|
||||
x01 += x06
|
||||
x = x12 ^ x01
|
||||
x12 = (x << 16) | (x >> 16)
|
||||
x11 += x12
|
||||
x = x06 ^ x11
|
||||
x06 = (x << 12) | (x >> 20)
|
||||
x01 += x06
|
||||
x = x12 ^ x01
|
||||
x12 = (x << 8) | (x >> 24)
|
||||
x11 += x12
|
||||
x = x06 ^ x11
|
||||
x06 = (x << 7) | (x >> 25)
|
||||
x02 += x07
|
||||
x = x13 ^ x02
|
||||
x13 = (x << 16) | (x >> 16)
|
||||
x08 += x13
|
||||
x = x07 ^ x08
|
||||
x07 = (x << 12) | (x >> 20)
|
||||
x02 += x07
|
||||
x = x13 ^ x02
|
||||
x13 = (x << 8) | (x >> 24)
|
||||
x08 += x13
|
||||
x = x07 ^ x08
|
||||
x07 = (x << 7) | (x >> 25)
|
||||
x03 += x04
|
||||
x = x14 ^ x03
|
||||
x14 = (x << 16) | (x >> 16)
|
||||
x09 += x14
|
||||
x = x04 ^ x09
|
||||
x04 = (x << 12) | (x >> 20)
|
||||
x03 += x04
|
||||
x = x14 ^ x03
|
||||
x14 = (x << 8) | (x >> 24)
|
||||
x09 += x14
|
||||
x = x04 ^ x09
|
||||
x04 = (x << 7) | (x >> 25)
|
||||
}
|
||||
|
||||
if !hchacha {
|
||||
output[0] = x00 + input[0]
|
||||
output[1] = x01 + input[1]
|
||||
output[2] = x02 + input[2]
|
||||
output[3] = x03 + input[3]
|
||||
output[4] = x04 + input[4]
|
||||
output[5] = x05 + input[5]
|
||||
output[6] = x06 + input[6]
|
||||
output[7] = x07 + input[7]
|
||||
output[8] = x08 + input[8]
|
||||
output[9] = x09 + input[9]
|
||||
output[10] = x10 + input[10]
|
||||
output[11] = x11 + input[11]
|
||||
output[12] = x12 + input[12]
|
||||
output[13] = x13 + input[13]
|
||||
output[14] = x14 + input[14]
|
||||
output[15] = x15 + input[15]
|
||||
} else {
|
||||
output[0] = x00
|
||||
output[1] = x01
|
||||
output[2] = x02
|
||||
output[3] = x03
|
||||
output[4] = x04
|
||||
output[5] = x05
|
||||
output[6] = x06
|
||||
output[7] = x07
|
||||
output[8] = x08
|
||||
output[9] = x09
|
||||
output[10] = x10
|
||||
output[11] = x11
|
||||
output[12] = x12
|
||||
output[13] = x13
|
||||
output[14] = x14
|
||||
output[15] = x15
|
||||
}
|
||||
}
|
352
cmd/gost/vendor/github.com/ginuerzh/gost/README.md
generated
vendored
352
cmd/gost/vendor/github.com/ginuerzh/gost/README.md
generated
vendored
@ -1,352 +0,0 @@
|
||||
gost - GO Simple Tunnel
|
||||
======
|
||||
|
||||
### GO语言实现的安全隧道
|
||||
|
||||
[English README](README_en.md)
|
||||
|
||||
特性
|
||||
------
|
||||
* 可同时监听多端口
|
||||
* 可设置转发代理,支持多级转发(代理链)
|
||||
* 支持标准HTTP/HTTPS/SOCKS5代理协议
|
||||
* SOCKS5代理支持TLS协商加密
|
||||
* Tunnel UDP over TCP
|
||||
* 支持Shadowsocks协议 (OTA: 2.2+,UDP: 2.4+)
|
||||
* 支持本地/远程端口转发 (2.1+)
|
||||
* 支持HTTP 2.0 (2.2+)
|
||||
* 实验性支持QUIC (2.3+)
|
||||
* 支持KCP协议 (2.3+)
|
||||
* 透明代理 (2.3+)
|
||||
|
||||
二进制文件下载:https://github.com/ginuerzh/gost/releases
|
||||
|
||||
Google讨论组: https://groups.google.com/d/forum/go-gost
|
||||
|
||||
在gost中,gost与其他代理服务都被看作是代理节点,gost可以自己处理请求,或者将请求转发给任意一个或多个代理节点。
|
||||
|
||||
参数说明
|
||||
------
|
||||
#### 代理及代理链
|
||||
|
||||
适用于-L和-F参数
|
||||
|
||||
```bash
|
||||
[scheme://][user:pass@host]:port
|
||||
```
|
||||
scheme分为两部分: protocol+transport
|
||||
|
||||
protocol: 代理协议类型(http, socks5, shadowsocks), transport: 数据传输方式(ws, wss, tls, http2, quic, kcp), 二者可以任意组合,或单独使用:
|
||||
|
||||
> http - HTTP代理: http://:8080
|
||||
|
||||
> http+tls - HTTPS代理(可能需要提供受信任的证书): http+tls://:443或https://:443
|
||||
|
||||
> http2 - HTTP2代理并向下兼容HTTPS代理: http2://:443
|
||||
|
||||
> socks - 标准SOCKS5代理(支持tls协商加密): socks://:1080
|
||||
|
||||
> socks+wss - SOCKS5代理,使用websocket传输数据: socks+wss://:1080
|
||||
|
||||
> tls - HTTPS/SOCKS5代理,使用tls传输数据: tls://:443
|
||||
|
||||
> ss - Shadowsocks代理,ss://chacha20:123456@:8338
|
||||
|
||||
> ssu - Shadowsocks UDP relay,ssu://chacha20:123456@:8338
|
||||
|
||||
> quic - QUIC代理,quic://:6121
|
||||
|
||||
> kcp - KCP代理,kcp://:8388或kcp://aes:123456@:8388
|
||||
|
||||
> redirect - 透明代理,redirect://:12345
|
||||
|
||||
#### 端口转发
|
||||
|
||||
适用于-L参数
|
||||
|
||||
```bash
|
||||
scheme://[bind_address]:port/[host]:hostport
|
||||
```
|
||||
> scheme - 端口转发模式, 本地端口转发: tcp, udp; 远程端口转发: rtcp, rudp
|
||||
|
||||
> bind_address:port - 本地/远程绑定地址
|
||||
|
||||
> host:hostport - 目标访问地址
|
||||
|
||||
#### 配置文件
|
||||
|
||||
> -C : 指定配置文件路径
|
||||
|
||||
配置文件为标准json格式:
|
||||
```json
|
||||
{
|
||||
"ServeNodes": [
|
||||
":8080",
|
||||
"ss://chacha20:12345678@:8338"
|
||||
],
|
||||
"ChainNodes": [
|
||||
"http://192.168.1.1:8080",
|
||||
"https://10.0.2.1:443"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
ServeNodes等同于-L参数,ChainNodes等同于-F参数
|
||||
|
||||
#### 开启日志
|
||||
|
||||
> -logtostderr : 输出到控制台
|
||||
|
||||
> -v=3 : 日志级别(1-5),级别越高,日志越详细(级别5将开启http2 debug)
|
||||
|
||||
> -log_dir=/log/dir/path : 输出到目录/log/dir/path
|
||||
|
||||
|
||||
使用方法
|
||||
------
|
||||
#### 不设置转发代理
|
||||
|
||||
<img src="https://ginuerzh.github.io/images/gost_01.png" />
|
||||
|
||||
* 作为标准HTTP/SOCKS5代理
|
||||
```bash
|
||||
gost -L=:8080
|
||||
```
|
||||
|
||||
* 设置代理认证信息
|
||||
```bash
|
||||
gost -L=admin:123456@localhost:8080
|
||||
```
|
||||
|
||||
* 多组认证信息
|
||||
```bash
|
||||
gost -L=localhost:8080?secrets=secrets.txt
|
||||
```
|
||||
|
||||
通过secrets参数可以为HTTP/SOCKS5代理设置多组认证信息,格式为:
|
||||
```plain
|
||||
# username password
|
||||
|
||||
test001 123456
|
||||
test002 12345678
|
||||
```
|
||||
|
||||
* 多端口监听
|
||||
```bash
|
||||
gost -L=http2://:443 -L=socks://:1080 -L=ss://aes-128-cfb:123456@:8338
|
||||
```
|
||||
|
||||
#### 设置转发代理
|
||||
|
||||
<img src="https://ginuerzh.github.io/images/gost_02.png" />
|
||||
```bash
|
||||
gost -L=:8080 -F=192.168.1.1:8081
|
||||
```
|
||||
|
||||
* 转发代理认证
|
||||
```bash
|
||||
gost -L=:8080 -F=http://admin:123456@192.168.1.1:8081
|
||||
```
|
||||
|
||||
#### 设置多级转发代理(代理链)
|
||||
|
||||
<img src="https://ginuerzh.github.io/images/gost_03.png" />
|
||||
```bash
|
||||
gost -L=:8080 -F=http+tls://192.168.1.1:443 -F=socks+ws://192.168.1.2:1080 -F=ss://aes-128-cfb:123456@192.168.1.3:8338 -F=a.b.c.d:NNNN
|
||||
```
|
||||
gost按照-F设置的顺序通过代理链将请求最终转发给a.b.c.d:NNNN处理,每一个转发代理可以是任意HTTP/HTTPS/HTTP2/SOCKS5/Shadowsocks类型代理。
|
||||
|
||||
#### 本地端口转发(TCP)
|
||||
|
||||
```bash
|
||||
gost -L=tcp://:2222/192.168.1.1:22 -F=...
|
||||
```
|
||||
将本地TCP端口2222上的数据(通过代理链)转发到192.168.1.1:22上。
|
||||
|
||||
#### 本地端口转发(UDP)
|
||||
|
||||
```bash
|
||||
gost -L=udp://:5353/192.168.1.1:53?ttl=60 -F=...
|
||||
```
|
||||
将本地UDP端口5353上的数据(通过代理链)转发到192.168.1.1:53上。
|
||||
每条转发通道都有超时时间,当超过此时间,且在此时间段内无任何数据交互,则此通道将关闭。可以通过`ttl`参数来设置超时时间,默认值为60秒。
|
||||
|
||||
**注:** 转发UDP数据时,如果有代理链,则代理链的末端(最后一个-F参数)必须是gost SOCKS5类型代理。
|
||||
|
||||
#### 远程端口转发(TCP)
|
||||
|
||||
```bash
|
||||
gost -L=rtcp://:2222/192.168.1.1:22 -F=... -F=socks://172.24.10.1:1080
|
||||
```
|
||||
将172.24.10.1:2222上的数据(通过代理链)转发到192.168.1.1:22上。
|
||||
|
||||
#### 远程端口转发(UDP)
|
||||
|
||||
```bash
|
||||
gost -L=rudp://:5353/192.168.1.1:53 -F=... -F=socks://172.24.10.1:1080
|
||||
```
|
||||
将172.24.10.1:5353上的数据(通过代理链)转发到192.168.1.1:53上。
|
||||
|
||||
**注:** 若要使用远程端口转发功能,代理链不能为空(至少要设置一个-F参数),且代理链的末端(最后一个-F参数)必须是gost SOCKS5类型代理。
|
||||
|
||||
#### HTTP2
|
||||
gost的HTTP2支持两种模式并自适应:
|
||||
* 作为标准的HTTP2代理,并向下兼容HTTPS代理。
|
||||
* 作为transport(类似于wss),传输其他协议。
|
||||
|
||||
服务端:
|
||||
```bash
|
||||
gost -L=http2://:443
|
||||
```
|
||||
客户端:
|
||||
```bash
|
||||
gost -L=:8080 -F=http2://server_ip:443?ping=30
|
||||
```
|
||||
|
||||
客户端支持`ping`参数开启心跳检测(默认不开启),参数值代表心跳间隔秒数。
|
||||
|
||||
**注:** gost的代理链仅支持一个HTTP2代理节点,采用就近原则,会将第一个遇到的HTTP2代理节点视为HTTP2代理,其他HTTP2代理节点则被视为HTTPS代理。
|
||||
|
||||
#### QUIC
|
||||
gost对QUIC的支持是基于[quic-go](https://github.com/lucas-clemente/quic-go)库。
|
||||
|
||||
服务端:
|
||||
```bash
|
||||
gost -L=quic://:6121
|
||||
```
|
||||
|
||||
客户端(Chrome):
|
||||
```bash
|
||||
chrome --enable-quic --proxy-server=quic://server_ip:6121
|
||||
```
|
||||
|
||||
**注:** 由于Chrome自身的限制,目前只能通过QUIC访问HTTP网站,无法访问HTTPS网站。
|
||||
|
||||
#### KCP
|
||||
gost对KCP的支持是基于[kcp-go](https://github.com/xtaci/kcp-go)和[kcptun](https://github.com/xtaci/kcptun)库。
|
||||
|
||||
服务端:
|
||||
```bash
|
||||
gost -L=kcp://:8388
|
||||
```
|
||||
|
||||
客户端:
|
||||
```bash
|
||||
gost -L=:8080 -F=kcp://server_ip:8388
|
||||
```
|
||||
|
||||
或者手动指定加密方法和密码(手动指定的加密方法和密码会覆盖配置文件中的相应值)
|
||||
|
||||
服务端:
|
||||
```bash
|
||||
gost -L=kcp://aes:123456@:8388
|
||||
```
|
||||
|
||||
客户端:
|
||||
```bash
|
||||
gost -L=:8080 -F=kcp://aes:123456@server_ip:8388
|
||||
```
|
||||
|
||||
gost会自动加载当前工作目录中的kcp.json(如果存在)配置文件,或者可以手动通过参数指定配置文件路径:
|
||||
```bash
|
||||
gost -L=kcp://:8388?c=/path/to/conf/file
|
||||
```
|
||||
|
||||
**注:** 客户端若要开启KCP转发,当且仅当代理链不为空且首个代理节点(第一个-F参数)为kcp类型。
|
||||
|
||||
#### 透明代理
|
||||
基于iptables的透明代理。
|
||||
|
||||
```bash
|
||||
gost -L=redirect://:12345 -F=http2://server_ip:443
|
||||
```
|
||||
|
||||
加密机制
|
||||
------
|
||||
#### HTTP
|
||||
对于HTTP可以使用TLS加密整个通讯过程,即HTTPS代理:
|
||||
|
||||
服务端:
|
||||
```bash
|
||||
gost -L=http+tls://:443
|
||||
```
|
||||
客户端:
|
||||
```bash
|
||||
gost -L=:8080 -F=http+tls://server_ip:443
|
||||
```
|
||||
|
||||
#### HTTP2
|
||||
gost仅支持使用TLS加密的HTTP2协议,不支持明文HTTP2传输。
|
||||
|
||||
|
||||
#### SOCKS5
|
||||
gost支持标准SOCKS5协议的no-auth(0x00)和user/pass(0x02)方法,并在此基础上扩展了两个:tls(0x80)和tls-auth(0x82),用于数据加密。
|
||||
|
||||
服务端:
|
||||
```bash
|
||||
gost -L=socks://:1080
|
||||
```
|
||||
客户端:
|
||||
```bash
|
||||
gost -L=:8080 -F=socks://server_ip:1080
|
||||
```
|
||||
|
||||
如果两端都是gost(如上)则数据传输会被加密(协商使用tls或tls-auth方法),否则使用标准SOCKS5进行通讯(no-auth或user/pass方法)。
|
||||
|
||||
**注:** 如果transport已经支持加密(wss, tls, http2, kcp),则SOCKS5不会再使用加密方法,防止不必要的双重加密。
|
||||
|
||||
#### Shadowsocks
|
||||
gost对shadowsocks的支持是基于[shadowsocks-go](https://github.com/shadowsocks/shadowsocks-go)库。
|
||||
|
||||
服务端(可以通过ota参数开启OTA强制模式,开启后客户端必须使用OTA模式):
|
||||
```bash
|
||||
gost -L=ss://aes-128-cfb:123456@:8338?ota=1
|
||||
```
|
||||
客户端(可以通过ota参数开启OTA模式):
|
||||
```bash
|
||||
gost -L=:8080 -F=ss://aes-128-cfb:123456@server_ip:8338?ota=1
|
||||
```
|
||||
|
||||
##### Shadowsocks UDP relay
|
||||
|
||||
目前仅服务端支持UDP,且仅支持OTA模式。
|
||||
|
||||
服务端:
|
||||
```bash
|
||||
gost -L=ssu://aes-128-cfb:123456@:8338
|
||||
```
|
||||
|
||||
#### TLS
|
||||
gost内置了TLS证书,如果需要使用其他TLS证书,有两种方法:
|
||||
* 在gost运行目录放置cert.pem(公钥)和key.pem(私钥)两个文件即可,gost会自动加载运行目录下的cert.pem和key.pem文件。
|
||||
* 使用参数指定证书文件路径:
|
||||
```bash
|
||||
gost -L="http2://:443?cert=/path/to/my/cert/file&key=/path/to/my/key/file"
|
||||
```
|
||||
|
||||
SOCKS5 UDP数据处理
|
||||
------
|
||||
#### 不设置转发代理
|
||||
|
||||
<img src="https://ginuerzh.github.io/images/udp01.png" height=100 />
|
||||
|
||||
gost作为标准SOCKS5代理处理UDP数据
|
||||
|
||||
#### 设置转发代理
|
||||
|
||||
<img src="https://ginuerzh.github.io/images/udp02.png" height=100 />
|
||||
|
||||
#### 设置多个转发代理(代理链)
|
||||
|
||||
<img src="https://ginuerzh.github.io/images/udp03.png" height=200 />
|
||||
|
||||
当设置转发代理时,gost会使用UDP-over-TCP方式转发UDP数据。proxy1 - proxyN可以为任意HTTP/HTTPS/HTTP2/SOCKS5/Shadowsocks类型代理。
|
||||
|
||||
限制条件
|
||||
------
|
||||
代理链中的HTTP代理节点必须支持CONNECT方法。
|
||||
|
||||
如果要转发SOCKS5的BIND和UDP请求,代理链的末端(最后一个-F参数)必须支持gost SOCKS5类型代理。
|
||||
|
||||
|
||||
|
355
cmd/gost/vendor/github.com/ginuerzh/gost/README_en.md
generated
vendored
355
cmd/gost/vendor/github.com/ginuerzh/gost/README_en.md
generated
vendored
@ -1,355 +0,0 @@
|
||||
gost - GO Simple Tunnel
|
||||
======
|
||||
|
||||
### A simple security tunnel written in Golang
|
||||
|
||||
Features
|
||||
------
|
||||
* Listening on multiple ports
|
||||
* Multi-level forward proxy - proxy chain
|
||||
* Standard HTTP/HTTPS/SOCKS5 proxy protocols support
|
||||
* TLS encryption via negotiation support for SOCKS5 proxy
|
||||
* Tunnel UDP over TCP
|
||||
* Shadowsocks protocol support (OTA: 2.2+, UDP: 2.4+)
|
||||
* Local/remote port forwarding (2.1+)
|
||||
* HTTP 2.0 support (2.2+)
|
||||
* Experimental QUIC support (2.3+)
|
||||
* KCP protocol support (2.3+)
|
||||
* Transparent proxy (2.3+)
|
||||
|
||||
Binary file download:https://github.com/ginuerzh/gost/releases
|
||||
|
||||
Google group: https://groups.google.com/d/forum/go-gost
|
||||
|
||||
Gost and other proxy services are considered to be proxy nodes,
|
||||
gost can handle the request itself, or forward the request to any one or more proxy nodes.
|
||||
|
||||
Parameter Description
|
||||
------
|
||||
#### Proxy and proxy chain
|
||||
|
||||
Effective for the -L and -F parameters
|
||||
|
||||
```bash
|
||||
[scheme://][user:pass@host]:port
|
||||
```
|
||||
scheme can be divided into two parts: protocol+transport
|
||||
|
||||
protocol: proxy protocol types (http, socks5, shadowsocks),
|
||||
transport: data transmission mode (ws, wss, tls, http2, quic, kcp), may be used in any combination or individually:
|
||||
|
||||
> http - standard HTTP proxy: http://:8080
|
||||
|
||||
> http+tls - standard HTTPS proxy (may need to provide a trusted certificate): http+tls://:443 or https://:443
|
||||
|
||||
> http2 - HTTP2 proxy and backwards-compatible with HTTPS proxy: http2://:443
|
||||
|
||||
> socks - standard SOCKS5 proxy: socks://:1080
|
||||
|
||||
> socks+wss - SOCKS5 over websocket: socks+wss://:1080
|
||||
|
||||
> tls - HTTPS/SOCKS5 over tls: tls://:443
|
||||
|
||||
> ss - standard shadowsocks proxy, ss://chacha20:123456@:8338
|
||||
|
||||
> ssu - shadowsocks UDP relay,ssu://chacha20:123456@:8338
|
||||
|
||||
> quic - standard QUIC proxy, quic://:6121
|
||||
|
||||
> kcp - standard KCP tunnel,kcp://:8388 or kcp://aes:123456@:8388
|
||||
|
||||
> redirect - transparent proxy,redirect://:12345
|
||||
|
||||
#### Port forwarding
|
||||
|
||||
Effective for the -L parameter
|
||||
|
||||
```bash
|
||||
scheme://[bind_address]:port/[host]:hostport
|
||||
```
|
||||
> scheme - forward mode, local: tcp, udp; remote: rtcp, rudp
|
||||
|
||||
> bind_address:port - local/remote binding address
|
||||
|
||||
> host:hostport - target address
|
||||
|
||||
#### Configuration file
|
||||
|
||||
> -C : specifies the configuration file path
|
||||
|
||||
The configuration file is in standard JSON format:
|
||||
```json
|
||||
{
|
||||
"ServeNodes": [
|
||||
":8080",
|
||||
"ss://chacha20:12345678@:8338"
|
||||
],
|
||||
"ChainNodes": [
|
||||
"http://192.168.1.1:8080",
|
||||
"https://10.0.2.1:443"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
ServeNodes is equivalent to the -L parameter, ChainNodes is equivalent to the -F parameter.
|
||||
|
||||
#### Logging
|
||||
|
||||
> -logtostderr : log to console
|
||||
|
||||
> -v=3 : log level (1-5),The higher the level, the more detailed the log (level 5 will enable HTTP2 debug)
|
||||
|
||||
> -log_dir=/log/dir/path : log to directory /log/dir/path
|
||||
|
||||
Usage
|
||||
------
|
||||
#### No forward proxy
|
||||
|
||||
<img src="https://ginuerzh.github.io/images/gost_01.png" />
|
||||
|
||||
* Standard HTTP/SOCKS5 proxy
|
||||
```bash
|
||||
gost -L=:8080
|
||||
```
|
||||
|
||||
* Proxy authentication
|
||||
```bash
|
||||
gost -L=admin:123456@localhost:8080
|
||||
```
|
||||
|
||||
* Multiple sets of authentication information
|
||||
```bash
|
||||
gost -L=localhost:8080?secrets=secrets.txt
|
||||
```
|
||||
|
||||
The secrets parameter allows you to set multiple authentication information for HTTP/SOCKS5 proxies, the format is:
|
||||
```plain
|
||||
# username password
|
||||
|
||||
test001 123456
|
||||
test002 12345678
|
||||
```
|
||||
|
||||
* Listen on multiple ports
|
||||
```bash
|
||||
gost -L=http2://:443 -L=socks://:1080 -L=ss://aes-128-cfb:123456@:8338
|
||||
```
|
||||
|
||||
#### Forward proxy
|
||||
|
||||
<img src="https://ginuerzh.github.io/images/gost_02.png" />
|
||||
```bash
|
||||
gost -L=:8080 -F=192.168.1.1:8081
|
||||
```
|
||||
|
||||
* Forward proxy authentication
|
||||
```bash
|
||||
gost -L=:8080 -F=http://admin:123456@192.168.1.1:8081
|
||||
```
|
||||
|
||||
#### Multi-level forward proxy
|
||||
|
||||
<img src="https://ginuerzh.github.io/images/gost_03.png" />
|
||||
```bash
|
||||
gost -L=:8080 -F=http+tls://192.168.1.1:443 -F=socks+ws://192.168.1.2:1080 -F=ss://aes-128-cfb:123456@192.168.1.3:8338 -F=a.b.c.d:NNNN
|
||||
```
|
||||
Gost forwards the request to a.b.c.d:NNNN through the proxy chain in the order set by -F,
|
||||
each forward proxy can be any HTTP/HTTPS/HTTP2/SOCKS5/Shadowsocks type.
|
||||
|
||||
#### Local TCP port forwarding
|
||||
|
||||
```bash
|
||||
gost -L=tcp://:2222/192.168.1.1:22 -F=...
|
||||
```
|
||||
The data on the local TCP port 2222 is forwarded to 192.168.1.1:22 (through the proxy chain).
|
||||
|
||||
#### Local UDP port forwarding
|
||||
|
||||
```bash
|
||||
gost -L=udp://:5353/192.168.1.1:53?ttl=60 -F=...
|
||||
```
|
||||
The data on the local UDP port 5353 is forwarded to 192.168.1.1:53 (through the proxy chain).
|
||||
Each forwarding channel has a timeout period. When this time is exceeded and there is no data interaction during this time period, the channel will be closed. The timeout value can be set by the `ttl` parameter. The default value is 60 seconds.
|
||||
|
||||
**NOTE:** When forwarding UDP data, if there is a proxy chain, the end of the chain (the last -F parameter) must be gost SOCKS5 proxy.
|
||||
|
||||
#### Remote TCP port forwarding
|
||||
|
||||
```bash
|
||||
gost -L=rtcp://:2222/192.168.1.1:22 -F=... -F=socks://172.24.10.1:1080
|
||||
```
|
||||
The data on 172.24.10.1:2222 is forwarded to 192.168.1.1:22 (through the proxy chain).
|
||||
|
||||
#### Remote UDP port forwarding
|
||||
|
||||
```bash
|
||||
gost -L=rudp://:5353/192.168.1.1:53 -F=... -F=socks://172.24.10.1:1080
|
||||
```
|
||||
The data on 172.24.10.1:5353 is forwarded to 192.168.1.1:53 (through the proxy chain).
|
||||
|
||||
**NOTE:** To use the remote port forwarding feature, the proxy chain can not be empty (at least one -F parameter is set)
|
||||
and the end of the chain (last -F parameter) must be gost SOCKS5 proxy.
|
||||
|
||||
#### HTTP2
|
||||
Gost HTTP2 supports two modes and self-adapting:
|
||||
* As a standard HTTP2 proxy, and backwards-compatible with the HTTPS proxy.
|
||||
* As transport (similar to wss), tunnel other protocol.
|
||||
|
||||
Server:
|
||||
```bash
|
||||
gost -L=http2://:443
|
||||
```
|
||||
Client:
|
||||
```bash
|
||||
gost -L=:8080 -F=http2://server_ip:443?ping=30
|
||||
```
|
||||
|
||||
The client supports the `ping` parameter to enable heartbeat detection (which is disabled by default).
|
||||
Parameter value represents heartbeat interval seconds.
|
||||
|
||||
**NOTE:** The proxy chain of gost supports only one HTTP2 proxy node and the nearest rule applies,
|
||||
the first HTTP2 proxy node is treated as an HTTP2 proxy, and the other HTTP2 proxy nodes are treated as HTTPS proxies.
|
||||
|
||||
#### QUIC
|
||||
Support for QUIC is based on library [quic-go](https://github.com/lucas-clemente/quic-go).
|
||||
|
||||
Server:
|
||||
```bash
|
||||
gost -L=quic://:6121
|
||||
```
|
||||
Client(Chrome):
|
||||
```bash
|
||||
chrome --enable-quic --proxy-server=quic://server_ip:6121
|
||||
```
|
||||
|
||||
**NOTE:** Due to Chrome's limitations, it is currently only possible to access the HTTP (but not HTTPS) site through QUIC.
|
||||
|
||||
#### KCP
|
||||
Support for KCP is based on libraries [kcp-go](https://github.com/xtaci/kcp-go) and [kcptun](https://github.com/xtaci/kcptun).
|
||||
|
||||
Server:
|
||||
```bash
|
||||
gost -L=kcp://:8388
|
||||
```
|
||||
Client:
|
||||
```bash
|
||||
gost -L=:8080 -F=kcp://server_ip:8388
|
||||
```
|
||||
|
||||
Or manually specify the encryption method and password (Manually specifying the encryption method and password overwrites the corresponding value in the configuration file)
|
||||
|
||||
Server:
|
||||
```bash
|
||||
gost -L=kcp://aes:123456@:8388
|
||||
```
|
||||
|
||||
Client:
|
||||
```bash
|
||||
gost -L=:8080 -F=kcp://aes:123456@server_ip:8388
|
||||
```
|
||||
|
||||
Gost will automatically load kcp.json configuration file from current working directory if exists,
|
||||
or you can use the parameter to specify the path to the file.
|
||||
```bash
|
||||
gost -L=kcp://:8388?c=/path/to/conf/file
|
||||
```
|
||||
|
||||
**NOTE:** KCP will be enabled if and only if the proxy chain is not empty and the first proxy node (the first -F parameter) is of type KCP.
|
||||
|
||||
#### Transparent proxy
|
||||
Iptables-based transparent proxy
|
||||
|
||||
```bash
|
||||
gost -L=redirect://:12345 -F=http2://server_ip:443
|
||||
```
|
||||
|
||||
Encryption Mechanism
|
||||
------
|
||||
#### HTTP
|
||||
For HTTP, you can use TLS to encrypt the entire communication process, the HTTPS proxy:
|
||||
|
||||
Server:
|
||||
```bash
|
||||
gost -L=http+tls://:443
|
||||
```
|
||||
Client:
|
||||
```bash
|
||||
gost -L=:8080 -F=http+tls://server_ip:443
|
||||
```
|
||||
|
||||
#### HTTP2
|
||||
Gost supports only the HTTP2 protocol that uses TLS encryption (h2) and does not support plaintext HTTP2 (h2c) transport.
|
||||
|
||||
|
||||
#### SOCKS5
|
||||
Gost supports the standard SOCKS5 protocol methods: no-auth (0x00) and user/pass (0x02),
|
||||
and extends two methods for data encryption: tls(0x80) and tls-auth(0x82).
|
||||
|
||||
Server:
|
||||
```bash
|
||||
gost -L=socks://:1080
|
||||
```
|
||||
Client:
|
||||
```bash
|
||||
gost -L=:8080 -F=socks://server_ip:1080
|
||||
```
|
||||
|
||||
If both ends are gosts (as example above), the data transfer will be encrypted (using tls or tls-auth).
|
||||
Otherwise, use standard SOCKS5 for communication (no-auth or user/pass).
|
||||
|
||||
**NOTE:** If transport already supports encryption (wss, tls, http2, kcp), SOCKS5 will no longer use the encryption method to prevent unnecessary double encryption.
|
||||
|
||||
#### Shadowsocks
|
||||
Support for shadowsocks is based on library [shadowsocks-go](https://github.com/shadowsocks/shadowsocks-go).
|
||||
|
||||
Server (The OTA mode can be enabled by the ota parameter. When enabled, the client must use OTA mode):
|
||||
```bash
|
||||
gost -L=ss://aes-128-cfb:123456@:8338?ota=1
|
||||
```
|
||||
Client (The OTA mode can be enabled by the ota parameter):
|
||||
```bash
|
||||
gost -L=:8080 -F=ss://aes-128-cfb:123456@server_ip:8338?ota=1
|
||||
```
|
||||
|
||||
##### Shadowsocks UDP relay
|
||||
Currently, only the server supports UDP, and only OTA mode is supported.
|
||||
|
||||
Server:
|
||||
```bash
|
||||
gost -L=ssu://aes-128-cfb:123456@:8338
|
||||
```
|
||||
|
||||
#### TLS
|
||||
There is built-in TLS certificate in gost, if you need to use other TLS certificate, there are two ways:
|
||||
* Place two files cert.pem (public key) and key.pem (private key) in the current working directory, gost will automatically load them.
|
||||
* Use the parameter to specify the path to the certificate file:
|
||||
```bash
|
||||
gost -L="http2://:443?cert=/path/to/my/cert/file&key=/path/to/my/key/file"
|
||||
```
|
||||
|
||||
SOCKS5 UDP Data Processing
|
||||
------
|
||||
#### No forward proxy
|
||||
|
||||
<img src="https://ginuerzh.github.io/images/udp01.png" height=100 />
|
||||
|
||||
Gost acts as the standard SOCKS5 proxy for UDP relay.
|
||||
|
||||
#### Forward proxy
|
||||
|
||||
<img src="https://ginuerzh.github.io/images/udp02.png" height=100 />
|
||||
|
||||
#### Multi-level forward proxy
|
||||
|
||||
<img src="https://ginuerzh.github.io/images/udp03.png" height=200 />
|
||||
|
||||
When forward proxies are set, gost uses UDP-over-TCP to forward UDP data, proxy1 to proxyN can be any HTTP/HTTPS/HTTP2/SOCKS5/Shadowsocks type.
|
||||
|
||||
Limitation
|
||||
------
|
||||
The HTTP proxy node in the proxy chain must support the CONNECT method.
|
||||
|
||||
If the BIND and UDP requests for SOCKS5 are to be forwarded, the end of the chain (the last -F parameter) must be the gost SOCKS5 proxy.
|
||||
|
||||
|
||||
|
381
cmd/gost/vendor/github.com/ginuerzh/gost/chain.go
generated
vendored
381
cmd/gost/vendor/github.com/ginuerzh/gost/chain.go
generated
vendored
@ -1,381 +0,0 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/http2"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Proxy chain holds a list of proxy nodes
|
||||
type ProxyChain struct {
|
||||
nodes []ProxyNode
|
||||
lastNode *ProxyNode
|
||||
http2NodeIndex int
|
||||
http2Enabled bool
|
||||
http2Client *http.Client
|
||||
kcpEnabled bool
|
||||
kcpConfig *KCPConfig
|
||||
kcpSession *KCPSession
|
||||
kcpMutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewProxyChain(nodes ...ProxyNode) *ProxyChain {
|
||||
chain := &ProxyChain{nodes: nodes, http2NodeIndex: -1}
|
||||
return chain
|
||||
}
|
||||
|
||||
func (c *ProxyChain) AddProxyNode(node ...ProxyNode) {
|
||||
c.nodes = append(c.nodes, node...)
|
||||
}
|
||||
|
||||
func (c *ProxyChain) AddProxyNodeString(snode ...string) error {
|
||||
for _, sn := range snode {
|
||||
node, err := ParseProxyNode(sn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.AddProxyNode(node)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ProxyChain) Nodes() []ProxyNode {
|
||||
return c.nodes
|
||||
}
|
||||
|
||||
func (c *ProxyChain) GetNode(index int) *ProxyNode {
|
||||
if index < len(c.nodes) {
|
||||
return &c.nodes[index]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ProxyChain) SetNode(index int, node ProxyNode) {
|
||||
if index < len(c.nodes) {
|
||||
c.nodes[index] = node
|
||||
}
|
||||
}
|
||||
|
||||
// Init initialize the proxy chain.
|
||||
// KCP will be enabled if the first proxy node is KCP proxy (transport == kcp).
|
||||
// HTTP2 will be enabled when at least one HTTP2 proxy node (scheme == http2) is present.
|
||||
//
|
||||
// NOTE: Should be called immediately when proxy nodes are ready.
|
||||
func (c *ProxyChain) Init() {
|
||||
length := len(c.nodes)
|
||||
if length == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
c.lastNode = &c.nodes[length-1]
|
||||
|
||||
// HTTP2 restrict: HTTP2 will be enabled when at least one HTTP2 proxy node is present.
|
||||
for i, node := range c.nodes {
|
||||
if node.Transport == "http2" {
|
||||
glog.V(LINFO).Infoln("HTTP2 is enabled")
|
||||
cfg := &tls.Config{
|
||||
InsecureSkipVerify: node.insecureSkipVerify(),
|
||||
ServerName: node.serverName,
|
||||
}
|
||||
c.http2NodeIndex = i
|
||||
c.initHttp2Client(cfg, c.nodes[:i]...)
|
||||
break // shortest chain for HTTP2
|
||||
}
|
||||
}
|
||||
|
||||
for i, node := range c.nodes {
|
||||
if node.Transport == "kcp" && i > 0 {
|
||||
glog.Fatal("KCP must be the first node in the proxy chain")
|
||||
}
|
||||
}
|
||||
|
||||
if c.nodes[0].Transport == "kcp" {
|
||||
glog.V(LINFO).Infoln("KCP is enabled")
|
||||
c.kcpEnabled = true
|
||||
config, err := ParseKCPConfig(c.nodes[0].Get("c"))
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln("[kcp]", err)
|
||||
}
|
||||
if config == nil {
|
||||
config = DefaultKCPConfig
|
||||
}
|
||||
if c.nodes[0].Users != nil {
|
||||
config.Crypt = c.nodes[0].Users[0].Username()
|
||||
config.Key, _ = c.nodes[0].Users[0].Password()
|
||||
}
|
||||
c.kcpConfig = config
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ProxyChain) KCPEnabled() bool {
|
||||
return c.kcpEnabled
|
||||
}
|
||||
|
||||
func (c *ProxyChain) Http2Enabled() bool {
|
||||
return c.http2Enabled
|
||||
}
|
||||
|
||||
func (c *ProxyChain) initHttp2Client(config *tls.Config, nodes ...ProxyNode) {
|
||||
if c.http2NodeIndex < 0 || c.http2NodeIndex >= len(c.nodes) {
|
||||
return
|
||||
}
|
||||
http2Node := c.nodes[c.http2NodeIndex]
|
||||
|
||||
tr := http2.Transport{
|
||||
TLSClientConfig: config,
|
||||
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
|
||||
// replace the default dialer with our proxy chain.
|
||||
conn, err := c.dialWithNodes(false, http2Node.Addr, nodes...)
|
||||
if err != nil {
|
||||
return conn, err
|
||||
}
|
||||
conn = tls.Client(conn, cfg)
|
||||
|
||||
// enable HTTP2 ping-pong
|
||||
pingIntvl, _ := strconv.Atoi(http2Node.Get("ping"))
|
||||
if pingIntvl > 0 {
|
||||
enablePing(conn, time.Duration(pingIntvl)*time.Second)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
},
|
||||
}
|
||||
c.http2Client = &http.Client{Transport: &tr}
|
||||
c.http2Enabled = true
|
||||
|
||||
}
|
||||
|
||||
func enablePing(conn net.Conn, interval time.Duration) {
|
||||
if conn == nil || interval == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
glog.V(LINFO).Infoln("[http2] ping enabled, interval:", interval)
|
||||
go func() {
|
||||
t := time.NewTicker(interval)
|
||||
var framer *http2.Framer
|
||||
for {
|
||||
select {
|
||||
case <-t.C:
|
||||
if framer == nil {
|
||||
framer = http2.NewFramer(conn, conn)
|
||||
}
|
||||
|
||||
var p [8]byte
|
||||
rand.Read(p[:])
|
||||
err := framer.WritePing(false, p)
|
||||
if err != nil {
|
||||
t.Stop()
|
||||
framer = nil
|
||||
glog.V(LWARNING).Infoln("[http2] ping:", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Connect to addr through proxy chain
|
||||
func (c *ProxyChain) Dial(addr string) (net.Conn, error) {
|
||||
if !strings.Contains(addr, ":") {
|
||||
addr += ":80"
|
||||
}
|
||||
return c.dialWithNodes(true, addr, c.nodes...)
|
||||
}
|
||||
|
||||
// GetConn initializes a proxy chain connection,
|
||||
// if no proxy nodes on this chain, it will return error
|
||||
func (c *ProxyChain) GetConn() (net.Conn, error) {
|
||||
nodes := c.nodes
|
||||
if len(nodes) == 0 {
|
||||
return nil, ErrEmptyChain
|
||||
}
|
||||
|
||||
if c.Http2Enabled() {
|
||||
nodes = nodes[c.http2NodeIndex+1:]
|
||||
if len(nodes) == 0 {
|
||||
header := make(http.Header)
|
||||
header.Set("Proxy-Switch", "gost") // Flag header to indicate server to switch to HTTP2 transport mode
|
||||
conn, err := c.getHttp2Conn(header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
http2Node := c.nodes[c.http2NodeIndex]
|
||||
if http2Node.Transport == "http2" {
|
||||
http2Node.Transport = "h2"
|
||||
}
|
||||
if http2Node.Protocol == "http2" {
|
||||
http2Node.Protocol = "socks5" // assume it as socks5 protocol, so we can do much more things.
|
||||
}
|
||||
pc := NewProxyConn(conn, http2Node)
|
||||
if err := pc.Handshake(); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return pc, nil
|
||||
}
|
||||
}
|
||||
return c.travelNodes(true, nodes...)
|
||||
}
|
||||
|
||||
func (c *ProxyChain) dialWithNodes(withHttp2 bool, addr string, nodes ...ProxyNode) (conn net.Conn, err error) {
|
||||
if len(nodes) == 0 {
|
||||
return net.DialTimeout("tcp", addr, DialTimeout)
|
||||
}
|
||||
|
||||
if withHttp2 && c.Http2Enabled() {
|
||||
nodes = nodes[c.http2NodeIndex+1:]
|
||||
if len(nodes) == 0 {
|
||||
return c.http2Connect(addr)
|
||||
}
|
||||
}
|
||||
pc, err := c.travelNodes(withHttp2, nodes...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = pc.Connect(addr); err != nil {
|
||||
pc.Close()
|
||||
return
|
||||
}
|
||||
conn = pc
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ProxyChain) travelNodes(withHttp2 bool, nodes ...ProxyNode) (conn *ProxyConn, err error) {
|
||||
defer func() {
|
||||
if err != nil && conn != nil {
|
||||
conn.Close()
|
||||
conn = nil
|
||||
}
|
||||
}()
|
||||
|
||||
var cc net.Conn
|
||||
node := nodes[0]
|
||||
|
||||
if withHttp2 && c.Http2Enabled() {
|
||||
cc, err = c.http2Connect(node.Addr)
|
||||
} else if node.Transport == "kcp" {
|
||||
cc, err = c.getKCPConn()
|
||||
} else {
|
||||
cc, err = net.DialTimeout("tcp", node.Addr, DialTimeout)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
setKeepAlive(cc, KeepAliveTime)
|
||||
|
||||
pc := NewProxyConn(cc, node)
|
||||
conn = pc
|
||||
if err = pc.Handshake(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, node := range nodes[1:] {
|
||||
if err = conn.Connect(node.Addr); err != nil {
|
||||
return
|
||||
}
|
||||
pc := NewProxyConn(conn, node)
|
||||
conn = pc
|
||||
if err = pc.Handshake(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ProxyChain) initKCPSession() (err error) {
|
||||
c.kcpMutex.Lock()
|
||||
defer c.kcpMutex.Unlock()
|
||||
|
||||
if c.kcpSession == nil || c.kcpSession.IsClosed() {
|
||||
glog.V(LINFO).Infoln("[kcp] new kcp session")
|
||||
c.kcpSession, err = DialKCP(c.nodes[0].Addr, c.kcpConfig)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ProxyChain) getKCPConn() (conn net.Conn, err error) {
|
||||
if !c.KCPEnabled() {
|
||||
return nil, errors.New("KCP is not enabled")
|
||||
}
|
||||
|
||||
if err = c.initKCPSession(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.kcpSession.GetConn()
|
||||
}
|
||||
|
||||
// Initialize an HTTP2 transport if HTTP2 is enabled.
|
||||
func (c *ProxyChain) getHttp2Conn(header http.Header) (net.Conn, error) {
|
||||
if !c.Http2Enabled() {
|
||||
return nil, errors.New("HTTP2 is not enabled")
|
||||
}
|
||||
http2Node := c.nodes[c.http2NodeIndex]
|
||||
pr, pw := io.Pipe()
|
||||
|
||||
if header == nil {
|
||||
header = make(http.Header)
|
||||
}
|
||||
|
||||
req := http.Request{
|
||||
Method: http.MethodConnect,
|
||||
URL: &url.URL{Scheme: "https", Host: http2Node.Addr},
|
||||
Header: header,
|
||||
Proto: "HTTP/2.0",
|
||||
ProtoMajor: 2,
|
||||
ProtoMinor: 0,
|
||||
Body: pr,
|
||||
Host: http2Node.Addr,
|
||||
ContentLength: -1,
|
||||
}
|
||||
if glog.V(LDEBUG) {
|
||||
dump, _ := httputil.DumpRequest(&req, false)
|
||||
glog.Infoln(string(dump))
|
||||
}
|
||||
resp, err := c.http2Client.Do(&req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if glog.V(LDEBUG) {
|
||||
dump, _ := httputil.DumpResponse(resp, false)
|
||||
glog.Infoln(string(dump))
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
resp.Body.Close()
|
||||
return nil, errors.New(resp.Status)
|
||||
}
|
||||
conn := &http2Conn{r: resp.Body, w: pw}
|
||||
conn.remoteAddr, _ = net.ResolveTCPAddr("tcp", http2Node.Addr)
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// Use HTTP2 as transport to connect target addr.
|
||||
//
|
||||
// BUG: SOCKS5 is ignored, only HTTP supported
|
||||
func (c *ProxyChain) http2Connect(addr string) (net.Conn, error) {
|
||||
if !c.Http2Enabled() {
|
||||
return nil, errors.New("HTTP2 is not enabled")
|
||||
}
|
||||
http2Node := c.nodes[c.http2NodeIndex]
|
||||
|
||||
header := make(http.Header)
|
||||
header.Set("Gost-Target", addr) // Flag header to indicate the address that server connected to
|
||||
if http2Node.Users != nil {
|
||||
header.Set("Proxy-Authorization",
|
||||
"Basic "+base64.StdEncoding.EncodeToString([]byte(http2Node.Users[0].String())))
|
||||
}
|
||||
return c.getHttp2Conn(header)
|
||||
}
|
257
cmd/gost/vendor/github.com/ginuerzh/gost/conn.go
generated
vendored
257
cmd/gost/vendor/github.com/ginuerzh/gost/conn.go
generated
vendored
@ -1,257 +0,0 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
"github.com/golang/glog"
|
||||
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ProxyConn struct {
|
||||
conn net.Conn
|
||||
Node ProxyNode
|
||||
handshaked bool
|
||||
handshakeMutex sync.Mutex
|
||||
handshakeErr error
|
||||
}
|
||||
|
||||
func NewProxyConn(conn net.Conn, node ProxyNode) *ProxyConn {
|
||||
return &ProxyConn{
|
||||
conn: conn,
|
||||
Node: node,
|
||||
}
|
||||
}
|
||||
|
||||
// Handshake handshake with this proxy node based on the proxy node info: transport, protocol, authentication, etc.
|
||||
//
|
||||
// NOTE: any HTTP2 scheme will be treated as http (for protocol) or tls (for transport).
|
||||
func (c *ProxyConn) Handshake() error {
|
||||
c.handshakeMutex.Lock()
|
||||
defer c.handshakeMutex.Unlock()
|
||||
|
||||
if err := c.handshakeErr; err != nil {
|
||||
return err
|
||||
}
|
||||
if c.handshaked {
|
||||
return nil
|
||||
}
|
||||
c.handshakeErr = c.handshake()
|
||||
return c.handshakeErr
|
||||
}
|
||||
|
||||
func (c *ProxyConn) handshake() error {
|
||||
var tlsUsed bool
|
||||
|
||||
switch c.Node.Transport {
|
||||
case "ws": // websocket connection
|
||||
u := url.URL{Scheme: "ws", Host: c.Node.Addr, Path: "/ws"}
|
||||
conn, err := WebsocketClientConn(u.String(), c.conn, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.conn = conn
|
||||
case "wss": // websocket security
|
||||
tlsUsed = true
|
||||
u := url.URL{Scheme: "wss", Host: c.Node.Addr, Path: "/ws"}
|
||||
config := &tls.Config{
|
||||
InsecureSkipVerify: c.Node.insecureSkipVerify(),
|
||||
ServerName: c.Node.serverName,
|
||||
}
|
||||
conn, err := WebsocketClientConn(u.String(), c.conn, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.conn = conn
|
||||
case "tls", "http2": // tls connection
|
||||
tlsUsed = true
|
||||
cfg := &tls.Config{
|
||||
InsecureSkipVerify: c.Node.insecureSkipVerify(),
|
||||
ServerName: c.Node.serverName,
|
||||
}
|
||||
c.conn = tls.Client(c.conn, cfg)
|
||||
case "h2": // same as http2, but just set a flag for later using.
|
||||
tlsUsed = true
|
||||
case "kcp": // kcp connection
|
||||
tlsUsed = true
|
||||
default:
|
||||
}
|
||||
|
||||
switch c.Node.Protocol {
|
||||
case "socks", "socks5": // socks5 handshake with auth and tls supported
|
||||
selector := &clientSelector{
|
||||
methods: []uint8{
|
||||
gosocks5.MethodNoAuth,
|
||||
gosocks5.MethodUserPass,
|
||||
//MethodTLS,
|
||||
},
|
||||
}
|
||||
|
||||
if len(c.Node.Users) > 0 {
|
||||
selector.user = c.Node.Users[0]
|
||||
}
|
||||
|
||||
if !tlsUsed { // if transport is not security, enable security socks5
|
||||
selector.methods = append(selector.methods, MethodTLS)
|
||||
selector.tlsConfig = &tls.Config{
|
||||
InsecureSkipVerify: c.Node.insecureSkipVerify(),
|
||||
ServerName: c.Node.serverName,
|
||||
}
|
||||
}
|
||||
|
||||
conn := gosocks5.ClientConn(c.conn, selector)
|
||||
if err := conn.Handleshake(); err != nil {
|
||||
return err
|
||||
}
|
||||
c.conn = conn
|
||||
case "ss": // shadowsocks
|
||||
// nothing to do
|
||||
case "http", "http2":
|
||||
fallthrough
|
||||
default:
|
||||
}
|
||||
|
||||
c.handshaked = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Connect connect to addr through this proxy node
|
||||
func (c *ProxyConn) Connect(addr string) error {
|
||||
switch c.Node.Protocol {
|
||||
case "ss": // shadowsocks
|
||||
rawaddr, err := ss.RawAddr(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var method, password string
|
||||
if len(c.Node.Users) > 0 {
|
||||
method = c.Node.Users[0].Username()
|
||||
password, _ = c.Node.Users[0].Password()
|
||||
}
|
||||
if c.Node.getBool("ota") && !strings.HasSuffix(method, "-auth") {
|
||||
method += "-auth"
|
||||
}
|
||||
|
||||
cipher, err := ss.NewCipher(method, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ssc, err := ss.DialWithRawAddrConn(rawaddr, c.conn, cipher)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.conn = &shadowConn{conn: ssc}
|
||||
return nil
|
||||
case "socks", "socks5":
|
||||
host, port, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p, _ := strconv.Atoi(port)
|
||||
req := gosocks5.NewRequest(gosocks5.CmdConnect, &gosocks5.Addr{
|
||||
Type: gosocks5.AddrDomain,
|
||||
Host: host,
|
||||
Port: uint16(p),
|
||||
})
|
||||
if err := req.Write(c); err != nil {
|
||||
return err
|
||||
}
|
||||
glog.V(LDEBUG).Infoln("[socks5]", req)
|
||||
|
||||
reply, err := gosocks5.ReadReply(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
glog.V(LDEBUG).Infoln("[socks5]", reply)
|
||||
if reply.Rep != gosocks5.Succeeded {
|
||||
return errors.New("Service unavailable")
|
||||
}
|
||||
case "http":
|
||||
fallthrough
|
||||
default:
|
||||
req := &http.Request{
|
||||
Method: http.MethodConnect,
|
||||
URL: &url.URL{Host: addr},
|
||||
Host: addr,
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Header: make(http.Header),
|
||||
}
|
||||
req.Header.Set("Proxy-Connection", "keep-alive")
|
||||
if len(c.Node.Users) > 0 {
|
||||
user := c.Node.Users[0]
|
||||
s := user.String()
|
||||
if _, set := user.Password(); !set {
|
||||
s += ":"
|
||||
}
|
||||
req.Header.Set("Proxy-Authorization",
|
||||
"Basic "+base64.StdEncoding.EncodeToString([]byte(s)))
|
||||
}
|
||||
if err := req.Write(c); err != nil {
|
||||
return err
|
||||
}
|
||||
if glog.V(LDEBUG) {
|
||||
dump, _ := httputil.DumpRequest(req, false)
|
||||
glog.Infoln(string(dump))
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(c), req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if glog.V(LDEBUG) {
|
||||
dump, _ := httputil.DumpResponse(resp, false)
|
||||
glog.Infoln(string(dump))
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return errors.New(resp.Status)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ProxyConn) Read(b []byte) (n int, err error) {
|
||||
return c.conn.Read(b)
|
||||
}
|
||||
|
||||
func (c *ProxyConn) Write(b []byte) (n int, err error) {
|
||||
return c.conn.Write(b)
|
||||
}
|
||||
|
||||
func (c *ProxyConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *ProxyConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *ProxyConn) RemoteAddr() net.Addr {
|
||||
return c.conn.RemoteAddr()
|
||||
}
|
||||
|
||||
func (c *ProxyConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *ProxyConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *ProxyConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
521
cmd/gost/vendor/github.com/ginuerzh/gost/forward.go
generated
vendored
521
cmd/gost/vendor/github.com/ginuerzh/gost/forward.go
generated
vendored
@ -1,521 +0,0 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
"github.com/golang/glog"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TcpForwardServer struct {
|
||||
Base *ProxyServer
|
||||
Handler func(conn net.Conn, raddr net.Addr)
|
||||
}
|
||||
|
||||
func NewTcpForwardServer(base *ProxyServer) *TcpForwardServer {
|
||||
return &TcpForwardServer{Base: base}
|
||||
}
|
||||
|
||||
func (s *TcpForwardServer) ListenAndServe() error {
|
||||
raddr, err := net.ResolveTCPAddr("tcp", s.Base.Node.Remote)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ln, err := net.Listen("tcp", s.Base.Node.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
if s.Handler == nil {
|
||||
s.Handler = s.handleTcpForward
|
||||
}
|
||||
|
||||
for {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln(err)
|
||||
continue
|
||||
}
|
||||
setKeepAlive(conn, KeepAliveTime)
|
||||
|
||||
go s.Handler(conn, raddr)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TcpForwardServer) handleTcpForward(conn net.Conn, raddr net.Addr) {
|
||||
defer conn.Close()
|
||||
|
||||
glog.V(LINFO).Infof("[tcp] %s - %s", conn.RemoteAddr(), raddr)
|
||||
cc, err := s.Base.Chain.Dial(raddr.String())
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[tcp] %s -> %s : %s", conn.RemoteAddr(), raddr, err)
|
||||
return
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
glog.V(LINFO).Infof("[tcp] %s <-> %s", conn.RemoteAddr(), raddr)
|
||||
s.Base.transport(conn, cc)
|
||||
glog.V(LINFO).Infof("[tcp] %s >-< %s", conn.RemoteAddr(), raddr)
|
||||
}
|
||||
|
||||
type packet struct {
|
||||
srcAddr string // src address
|
||||
dstAddr string // dest address
|
||||
data []byte
|
||||
}
|
||||
|
||||
type cnode struct {
|
||||
chain *ProxyChain
|
||||
conn net.Conn
|
||||
srcAddr, dstAddr string
|
||||
rChan, wChan chan *packet
|
||||
err error
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
func (node *cnode) getUDPTunnel() (net.Conn, error) {
|
||||
conn, err := node.chain.GetConn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn.SetWriteDeadline(time.Now().Add(WriteTimeout))
|
||||
if err = gosocks5.NewRequest(CmdUdpTun, nil).Write(conn); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
conn.SetWriteDeadline(time.Time{})
|
||||
|
||||
conn.SetReadDeadline(time.Now().Add(ReadTimeout))
|
||||
reply, err := gosocks5.ReadReply(conn)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
conn.SetReadDeadline(time.Time{})
|
||||
|
||||
if reply.Rep != gosocks5.Succeeded {
|
||||
conn.Close()
|
||||
return nil, errors.New("UDP tunnel failure")
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (node *cnode) run() {
|
||||
if len(node.chain.Nodes()) == 0 {
|
||||
lconn, err := net.ListenUDP("udp", nil)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", node.srcAddr, node.dstAddr, err)
|
||||
node.err = err
|
||||
return
|
||||
}
|
||||
node.conn = lconn
|
||||
} else {
|
||||
tc, err := node.getUDPTunnel()
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[udp-tun] %s -> %s : %s", node.srcAddr, node.dstAddr, err)
|
||||
node.err = err
|
||||
return
|
||||
}
|
||||
node.conn = tc
|
||||
}
|
||||
|
||||
defer node.conn.Close()
|
||||
|
||||
timer := time.NewTimer(node.ttl)
|
||||
errChan := make(chan error, 2)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
switch c := node.conn.(type) {
|
||||
case *net.UDPConn:
|
||||
b := make([]byte, MediumBufferSize)
|
||||
n, addr, err := c.ReadFromUDP(b)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[udp] %s <- %s : %s", node.srcAddr, node.dstAddr, err)
|
||||
node.err = err
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
timer.Reset(node.ttl)
|
||||
glog.V(LDEBUG).Infof("[udp] %s <<< %s : length %d", node.srcAddr, addr, n)
|
||||
|
||||
select {
|
||||
// swap srcAddr with dstAddr
|
||||
case node.rChan <- &packet{srcAddr: addr.String(), dstAddr: node.srcAddr, data: b[:n]}:
|
||||
case <-time.After(time.Second * 3):
|
||||
glog.V(LWARNING).Infof("[udp] %s <- %s : %s", node.srcAddr, node.dstAddr, "recv queue is full, discard")
|
||||
}
|
||||
|
||||
default:
|
||||
dgram, err := gosocks5.ReadUDPDatagram(c)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[udp-tun] %s <- %s : %s", node.srcAddr, node.dstAddr, err)
|
||||
node.err = err
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
timer.Reset(node.ttl)
|
||||
glog.V(LDEBUG).Infof("[udp-tun] %s <<< %s : length %d", node.srcAddr, dgram.Header.Addr.String(), len(dgram.Data))
|
||||
|
||||
select {
|
||||
// swap srcAddr with dstAddr
|
||||
case node.rChan <- &packet{srcAddr: dgram.Header.Addr.String(), dstAddr: node.srcAddr, data: dgram.Data}:
|
||||
case <-time.After(time.Second * 3):
|
||||
glog.V(LWARNING).Infof("[udp-tun] %s <- %s : %s", node.srcAddr, node.dstAddr, "recv queue is full, discard")
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for pkt := range node.wChan {
|
||||
timer.Reset(node.ttl)
|
||||
|
||||
dstAddr, err := net.ResolveUDPAddr("udp", pkt.dstAddr)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", pkt.srcAddr, pkt.dstAddr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
switch c := node.conn.(type) {
|
||||
case *net.UDPConn:
|
||||
if _, err := c.WriteToUDP(pkt.data, dstAddr); err != nil {
|
||||
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", pkt.srcAddr, pkt.dstAddr, err)
|
||||
node.err = err
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
glog.V(LDEBUG).Infof("[udp] %s >>> %s : length %d", pkt.srcAddr, pkt.dstAddr, len(pkt.data))
|
||||
|
||||
default:
|
||||
dgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(uint16(len(pkt.data)), 0, ToSocksAddr(dstAddr)), pkt.data)
|
||||
if err := dgram.Write(c); err != nil {
|
||||
glog.V(LWARNING).Infof("[udp-tun] %s -> %s : %s", pkt.srcAddr, pkt.dstAddr, err)
|
||||
node.err = err
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
glog.V(LDEBUG).Infof("[udp-tun] %s >>> %s : length %d", pkt.srcAddr, pkt.dstAddr, len(pkt.data))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-errChan:
|
||||
case <-timer.C:
|
||||
}
|
||||
}
|
||||
|
||||
type UdpForwardServer struct {
|
||||
Base *ProxyServer
|
||||
TTL int
|
||||
}
|
||||
|
||||
func NewUdpForwardServer(base *ProxyServer, ttl int) *UdpForwardServer {
|
||||
return &UdpForwardServer{Base: base, TTL: ttl}
|
||||
}
|
||||
|
||||
func (s *UdpForwardServer) ListenAndServe() error {
|
||||
laddr, err := net.ResolveUDPAddr("udp", s.Base.Node.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
raddr, err := net.ResolveUDPAddr("udp", s.Base.Node.Remote)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conn, err := net.ListenUDP("udp", laddr)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err)
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
rChan, wChan := make(chan *packet, 128), make(chan *packet, 128)
|
||||
// start send queue
|
||||
go func(ch chan<- *packet) {
|
||||
for {
|
||||
b := make([]byte, MediumBufferSize)
|
||||
n, addr, err := conn.ReadFromUDP(b)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
select {
|
||||
case ch <- &packet{srcAddr: addr.String(), dstAddr: raddr.String(), data: b[:n]}:
|
||||
case <-time.After(time.Second * 3):
|
||||
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", addr, raddr, "send queue is full, discard")
|
||||
}
|
||||
}
|
||||
}(wChan)
|
||||
// start recv queue
|
||||
go func(ch <-chan *packet) {
|
||||
for pkt := range ch {
|
||||
dstAddr, err := net.ResolveUDPAddr("udp", pkt.dstAddr)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[udp] %s <- %s : %s", pkt.dstAddr, pkt.srcAddr, err)
|
||||
continue
|
||||
}
|
||||
if _, err := conn.WriteToUDP(pkt.data, dstAddr); err != nil {
|
||||
glog.V(LWARNING).Infof("[udp] %s <- %s : %s", pkt.dstAddr, pkt.srcAddr, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}(rChan)
|
||||
|
||||
// mapping client to node
|
||||
m := make(map[string]*cnode)
|
||||
|
||||
// start dispatcher
|
||||
for pkt := range wChan {
|
||||
// clear obsolete nodes
|
||||
for k, node := range m {
|
||||
if node != nil && node.err != nil {
|
||||
close(node.wChan)
|
||||
delete(m, k)
|
||||
glog.V(LINFO).Infof("[udp] clear node %s", k)
|
||||
}
|
||||
}
|
||||
|
||||
node, ok := m[pkt.srcAddr]
|
||||
if !ok {
|
||||
node = &cnode{
|
||||
chain: s.Base.Chain,
|
||||
srcAddr: pkt.srcAddr,
|
||||
dstAddr: pkt.dstAddr,
|
||||
rChan: rChan,
|
||||
wChan: make(chan *packet, 32),
|
||||
ttl: time.Duration(s.TTL) * time.Second,
|
||||
}
|
||||
m[pkt.srcAddr] = node
|
||||
go node.run()
|
||||
glog.V(LINFO).Infof("[udp] %s -> %s : new client (%d)", pkt.srcAddr, pkt.dstAddr, len(m))
|
||||
}
|
||||
|
||||
select {
|
||||
case node.wChan <- pkt:
|
||||
case <-time.After(time.Second * 3):
|
||||
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", pkt.srcAddr, pkt.dstAddr, "node send queue is full, discard")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type RTcpForwardServer struct {
|
||||
Base *ProxyServer
|
||||
}
|
||||
|
||||
func NewRTcpForwardServer(base *ProxyServer) *RTcpForwardServer {
|
||||
return &RTcpForwardServer{Base: base}
|
||||
}
|
||||
|
||||
func (s *RTcpForwardServer) Serve() error {
|
||||
if len(s.Base.Chain.nodes) == 0 {
|
||||
return errors.New("rtcp: at least one -F must be assigned")
|
||||
}
|
||||
|
||||
laddr, err := net.ResolveTCPAddr("tcp", s.Base.Node.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
raddr, err := net.ResolveTCPAddr("tcp", s.Base.Node.Remote)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
retry := 0
|
||||
for {
|
||||
conn, err := s.Base.Chain.GetConn()
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[rtcp] %s - %s : %s", laddr, raddr, err)
|
||||
time.Sleep((1 << uint(retry)) * time.Second)
|
||||
if retry < 5 {
|
||||
retry++
|
||||
}
|
||||
continue
|
||||
}
|
||||
retry = 0
|
||||
|
||||
if err := s.connectRTcpForward(conn, laddr, raddr); err != nil {
|
||||
conn.Close()
|
||||
time.Sleep(6 * time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RTcpForwardServer) connectRTcpForward(conn net.Conn, laddr, raddr net.Addr) error {
|
||||
glog.V(LINFO).Infof("[rtcp] %s - %s", laddr, raddr)
|
||||
|
||||
req := gosocks5.NewRequest(gosocks5.CmdBind, ToSocksAddr(laddr))
|
||||
if err := req.Write(conn); err != nil {
|
||||
glog.V(LWARNING).Infof("[rtcp] %s -> %s : %s", laddr, raddr, err)
|
||||
return err
|
||||
}
|
||||
|
||||
// first reply, bind status
|
||||
conn.SetReadDeadline(time.Now().Add(ReadTimeout))
|
||||
rep, err := gosocks5.ReadReply(conn)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[rtcp] %s -> %s : %s", laddr, raddr, err)
|
||||
return err
|
||||
}
|
||||
conn.SetReadDeadline(time.Time{})
|
||||
if rep.Rep != gosocks5.Succeeded {
|
||||
glog.V(LWARNING).Infof("[rtcp] %s -> %s : bind on %s failure", laddr, raddr, laddr)
|
||||
return errors.New("Bind on " + laddr.String() + " failure")
|
||||
}
|
||||
glog.V(LINFO).Infof("[rtcp] %s - %s BIND ON %s OK", laddr, raddr, rep.Addr)
|
||||
|
||||
// second reply, peer connection
|
||||
rep, err = gosocks5.ReadReply(conn)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[rtcp] %s -> %s : %s", laddr, raddr, err)
|
||||
return err
|
||||
}
|
||||
if rep.Rep != gosocks5.Succeeded {
|
||||
glog.V(LWARNING).Infof("[rtcp] %s -> %s : peer connect failure", laddr, raddr)
|
||||
return errors.New("peer connect failure")
|
||||
}
|
||||
|
||||
glog.V(LINFO).Infof("[rtcp] %s -> %s PEER %s CONNECTED", laddr, raddr, rep.Addr)
|
||||
|
||||
go func() {
|
||||
defer conn.Close()
|
||||
|
||||
lconn, err := net.DialTimeout("tcp", raddr.String(), time.Second*180)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[rtcp] %s -> %s : %s", rep.Addr, raddr, err)
|
||||
return
|
||||
}
|
||||
defer lconn.Close()
|
||||
|
||||
glog.V(LINFO).Infof("[rtcp] %s <-> %s", rep.Addr, lconn.RemoteAddr())
|
||||
s.Base.transport(lconn, conn)
|
||||
glog.V(LINFO).Infof("[rtcp] %s >-< %s", rep.Addr, lconn.RemoteAddr())
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type RUdpForwardServer struct {
|
||||
Base *ProxyServer
|
||||
}
|
||||
|
||||
func NewRUdpForwardServer(base *ProxyServer) *RUdpForwardServer {
|
||||
return &RUdpForwardServer{Base: base}
|
||||
}
|
||||
|
||||
func (s *RUdpForwardServer) Serve() error {
|
||||
if len(s.Base.Chain.nodes) == 0 {
|
||||
return errors.New("rudp: at least one -F must be assigned")
|
||||
}
|
||||
|
||||
laddr, err := net.ResolveUDPAddr("udp", s.Base.Node.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
raddr, err := net.ResolveUDPAddr("udp", s.Base.Node.Remote)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
retry := 0
|
||||
for {
|
||||
conn, err := s.Base.Chain.GetConn()
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[rudp] %s - %s : %s", laddr, raddr, err)
|
||||
time.Sleep((1 << uint(retry)) * time.Second)
|
||||
if retry < 5 {
|
||||
retry++
|
||||
}
|
||||
continue
|
||||
}
|
||||
retry = 0
|
||||
|
||||
if err := s.connectRUdpForward(conn, laddr, raddr); err != nil {
|
||||
conn.Close()
|
||||
time.Sleep(6 * time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RUdpForwardServer) connectRUdpForward(conn net.Conn, laddr, raddr *net.UDPAddr) error {
|
||||
glog.V(LINFO).Infof("[rudp] %s - %s", laddr, raddr)
|
||||
|
||||
req := gosocks5.NewRequest(CmdUdpTun, ToSocksAddr(laddr))
|
||||
conn.SetWriteDeadline(time.Now().Add(WriteTimeout))
|
||||
if err := req.Write(conn); err != nil {
|
||||
glog.V(LWARNING).Infof("[rudp] %s -> %s : %s", laddr, raddr, err)
|
||||
return err
|
||||
}
|
||||
conn.SetWriteDeadline(time.Time{})
|
||||
|
||||
conn.SetReadDeadline(time.Now().Add(ReadTimeout))
|
||||
rep, err := gosocks5.ReadReply(conn)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[rudp] %s <- %s : %s", laddr, raddr, err)
|
||||
return err
|
||||
}
|
||||
conn.SetReadDeadline(time.Time{})
|
||||
|
||||
if rep.Rep != gosocks5.Succeeded {
|
||||
glog.V(LWARNING).Infof("[rudp] %s <- %s : bind on %s failure", laddr, raddr, laddr)
|
||||
return errors.New(fmt.Sprintf("bind on %s failure", laddr))
|
||||
}
|
||||
|
||||
glog.V(LINFO).Infof("[rudp] %s - %s BIND ON %s OK", laddr, raddr, rep.Addr)
|
||||
|
||||
for {
|
||||
dgram, err := gosocks5.ReadUDPDatagram(conn)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[rudp] %s <- %s : %s", laddr, raddr, err)
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
b := make([]byte, MediumBufferSize)
|
||||
|
||||
relay, err := net.DialUDP("udp", nil, raddr)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[rudp] %s -> %s : %s", laddr, raddr, err)
|
||||
return
|
||||
}
|
||||
defer relay.Close()
|
||||
|
||||
if _, err := relay.Write(dgram.Data); err != nil {
|
||||
glog.V(LWARNING).Infof("[rudp] %s -> %s : %s", laddr, raddr, err)
|
||||
return
|
||||
}
|
||||
glog.V(LDEBUG).Infof("[rudp] %s >>> %s length: %d", laddr, raddr, len(dgram.Data))
|
||||
|
||||
relay.SetReadDeadline(time.Now().Add(ReadTimeout))
|
||||
n, err := relay.Read(b)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[rudp] %s <- %s : %s", laddr, raddr, err)
|
||||
return
|
||||
}
|
||||
relay.SetReadDeadline(time.Time{})
|
||||
|
||||
glog.V(LDEBUG).Infof("[rudp] %s <<< %s length: %d", laddr, raddr, n)
|
||||
|
||||
conn.SetWriteDeadline(time.Now().Add(WriteTimeout))
|
||||
if err := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(uint16(n), 0, dgram.Header.Addr), b[:n]).Write(conn); err != nil {
|
||||
glog.V(LWARNING).Infof("[rudp] %s <- %s : %s", laddr, raddr, err)
|
||||
return
|
||||
}
|
||||
conn.SetWriteDeadline(time.Time{})
|
||||
}()
|
||||
}
|
||||
}
|
146
cmd/gost/vendor/github.com/ginuerzh/gost/gost.go
generated
vendored
146
cmd/gost/vendor/github.com/ginuerzh/gost/gost.go
generated
vendored
@ -1,146 +0,0 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"github.com/golang/glog"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
Version = "2.4-dev"
|
||||
)
|
||||
|
||||
// Log level for glog
|
||||
const (
|
||||
LFATAL = iota
|
||||
LERROR
|
||||
LWARNING
|
||||
LINFO
|
||||
LDEBUG
|
||||
)
|
||||
|
||||
var (
|
||||
KeepAliveTime = 180 * time.Second
|
||||
DialTimeout = 30 * time.Second
|
||||
ReadTimeout = 90 * time.Second
|
||||
WriteTimeout = 90 * time.Second
|
||||
|
||||
DefaultTTL = 60 // default udp node TTL in second for udp port forwarding
|
||||
)
|
||||
|
||||
var (
|
||||
SmallBufferSize = 1 * 1024 // 1KB small buffer
|
||||
MediumBufferSize = 8 * 1024 // 8KB medium buffer
|
||||
LargeBufferSize = 32 * 1024 // 32KB large buffer
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultCertFile = "cert.pem"
|
||||
DefaultKeyFile = "key.pem"
|
||||
|
||||
// This is the default cert and key data for convenience, providing your own cert is recommended.
|
||||
defaultRawCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIC5jCCAdCgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD
|
||||
bzAeFw0xNDAzMTcwNjIwNTFaFw0xNTAzMTcwNjIwNTFaMBIxEDAOBgNVBAoTB0Fj
|
||||
bWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDccNO1xmd4lWSf
|
||||
d/0/QS3E93cYIWHw831i/IKxigdRD/XMZonLdEHywW6lOiXazaP8e6CqPGSmnl0x
|
||||
5k/3dvGCMj2JCVxM6+z7NpL+AiwvXmvkj/TOciCgwqssCwYS2CiVwjfazRjx1ZUJ
|
||||
VDC5qiyRsfktQ2fVHrpnJGVSRagmiQgwGWBilVG9B8QvRtpQKN/GQGq17oIQm8aK
|
||||
kOdPt93g93ojMIg7YJpgDgOirvVz/hDn7YD4ryrtPos9CMafFkJprymKpRHyvz7P
|
||||
8a3+OkuPjFjPnwOHQ5u1U3+8vC44vfb1ExWzDLoT8Xp8Gndx39k0f7MVOol3GnYu
|
||||
MN/dvNUdAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIAoDATBgNVHSUEDDAKBggrBgEF
|
||||
BQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDALBgkqhkiG
|
||||
9w0BAQUDggEBAIG8CJqvTIgJnNOK+i5/IUc/3yF/mSCWuG8qP+Fmo2t6T0PVOtc0
|
||||
8wiWH5iWtCAhjn0MRY9l/hIjWm6gUZGHCGuEgsOPpJDYGoNLjH9Xwokm4y3LFNRK
|
||||
UBrrrDbKRNibApBHCapPf6gC5sXcjOwx7P2/kiHDgY7YH47jfcRhtAPNsM4gjsEO
|
||||
RmwENY+hRUFHIRfQTyalqND+x6PWhRo3K6hpHs4DQEYPq4P2kFPqUqSBymH+Ny5/
|
||||
BcQ3wdMNmC6Bm/oiL1QV0M+/InOsAgQk/EDd0kmoU1ZT2lYHQduGmP099bOlHNpS
|
||||
uqO3vXF3q8SPPr/A9TqSs7BKkBQbe0+cdsA=
|
||||
-----END CERTIFICATE-----`)
|
||||
defaultRawKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA3HDTtcZneJVkn3f9P0EtxPd3GCFh8PN9YvyCsYoHUQ/1zGaJ
|
||||
y3RB8sFupTol2s2j/Hugqjxkpp5dMeZP93bxgjI9iQlcTOvs+zaS/gIsL15r5I/0
|
||||
znIgoMKrLAsGEtgolcI32s0Y8dWVCVQwuaoskbH5LUNn1R66ZyRlUkWoJokIMBlg
|
||||
YpVRvQfEL0baUCjfxkBqte6CEJvGipDnT7fd4Pd6IzCIO2CaYA4Doq71c/4Q5+2A
|
||||
+K8q7T6LPQjGnxZCaa8piqUR8r8+z/Gt/jpLj4xYz58Dh0ObtVN/vLwuOL329RMV
|
||||
swy6E/F6fBp3cd/ZNH+zFTqJdxp2LjDf3bzVHQIDAQABAoIBAHal26147nQ+pHwY
|
||||
jxwers3XDCjWvup7g79lfcqlKi79UiUEA6KYHm7UogMYewt7p4nb2KwH+XycvDiB
|
||||
aAUf5flXpTs+6IkWauUDiLZi4PlV7uiEexUq5FjirlL0U/6MjbudX4bK4WQ4uxDc
|
||||
WaV07Kw2iJFOOHLDKT0en9JaX5jtJNc4ZnE9efFoQ5jfypPWtRw65G1rULEg6nvc
|
||||
GDh+1ce+4foCkpLRC9c24xAwJONZG6x3UqrSS9qfAsb73nWRQrTfUcO3nhoN8VvL
|
||||
kL9skn1+S06NyUN0KoEtyRBp+RcpXSsBWAo6qZmo/WqhB/gjzWrxVwn20+yJSm35
|
||||
ZsMc6QECgYEA8GS+Mp9xfB2szWHz6YTOO1Uu4lHM1ccZMwS1G+dL0KO3uGAiPdvp
|
||||
woVot6v6w88t7onXsLo5pgz7SYug0CpkF3K/MRd1Ar4lH7PK7IBQ6rFr9ppVxDbx
|
||||
AEWRswUoPbKCr7W6HU8LbQHDavsDlEIwc6+DiwnL4BzlKjb7RpgQEz0CgYEA6sB5
|
||||
uHvx3Y5FDcGk1n73leQSAcq14l3ZLNpjrs8msoREDil/j5WmuSN58/7PGMiMgHEi
|
||||
1vLm3H796JmvGr9OBvspOjHyk07ui2/We/j9Hoxm1VWhyi8HkLNDj70HKalTTFMz
|
||||
RHO4O+0xCva+h9mKZrRMVktXr2jjdFn/0MYIZ2ECgYAIIsC1IeRLWQ3CHbCNlKsO
|
||||
IwHlMvOFwKk/qsceXKOaOhA7szU1dr3gkXdL0Aw6mEZrrkqYdpUA46uVf54/rU+Z
|
||||
445I8QxKvXiwK/uQKX+TkdGflPWWIG3jnnch4ejMvb/ihnn4B/bRB6A/fKNQXzUY
|
||||
lTYUfI5j1VaEKTwz1W2l2QKBgByFCcSp+jZqhGUpc3dDsZyaOr3Q/Mvlju7uEVI5
|
||||
hIAHpaT60a6GBd1UPAqymEJwivFHzW3D0NxU6VAK68UaHMaoWNfjHY9b9YsnKS2i
|
||||
kE3XzN56Ks+/avHfdYPO+UHMenw5V28nh+hv5pdoZrlmanQTz3pkaOC8o3WNQZEB
|
||||
nh/BAoGBAMY5z2f1pmMhrvtPDSlEVjgjELbaInxFaxPLR4Pdyzn83gtIIU14+R8X
|
||||
2LPs6PPwrNjWnIgrUSVXncIFL3pa45B+Mx1pYCpOAB1+nCZjIBQmpeo4Y0dwA/XH
|
||||
85EthKPvoszm+OPbyI16OcePV5ocX7lupRYuAo0pek7bomhmHWHz
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
)
|
||||
|
||||
var (
|
||||
ErrEmptyChain = errors.New("empty chain")
|
||||
)
|
||||
|
||||
func setKeepAlive(conn net.Conn, d time.Duration) error {
|
||||
c, ok := conn.(*net.TCPConn)
|
||||
if !ok {
|
||||
return errors.New("Not a TCP connection")
|
||||
}
|
||||
if err := c.SetKeepAlive(true); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.SetKeepAlivePeriod(d); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Load the certificate from cert and key files, will use the default certificate if the provided info are invalid.
|
||||
func LoadCertificate(certFile, keyFile string) (tls.Certificate, error) {
|
||||
tlsCert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err == nil {
|
||||
return tlsCert, nil
|
||||
}
|
||||
glog.V(LWARNING).Infoln(err)
|
||||
return tls.X509KeyPair(defaultRawCert, defaultRawKey)
|
||||
}
|
||||
|
||||
// Replace the default certificate by your own
|
||||
func SetDefaultCertificate(rawCert, rawKey []byte) {
|
||||
defaultRawCert = rawCert
|
||||
defaultRawKey = rawKey
|
||||
}
|
||||
|
||||
func basicProxyAuth(proxyAuth string) (username, password string, ok bool) {
|
||||
if proxyAuth == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(proxyAuth, "Basic ") {
|
||||
return
|
||||
}
|
||||
c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(proxyAuth, "Basic "))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cs := string(c)
|
||||
s := strings.IndexByte(cs, ':')
|
||||
if s < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
return cs[:s], cs[s+1:], true
|
||||
}
|
385
cmd/gost/vendor/github.com/ginuerzh/gost/http.go
generated
vendored
385
cmd/gost/vendor/github.com/ginuerzh/gost/http.go
generated
vendored
@ -1,385 +0,0 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/http2"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
//"strings"
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
type HttpServer struct {
|
||||
conn net.Conn
|
||||
Base *ProxyServer
|
||||
}
|
||||
|
||||
func NewHttpServer(conn net.Conn, base *ProxyServer) *HttpServer {
|
||||
return &HttpServer{
|
||||
conn: conn,
|
||||
Base: base,
|
||||
}
|
||||
}
|
||||
|
||||
// Default HTTP server handler
|
||||
func (s *HttpServer) HandleRequest(req *http.Request) {
|
||||
glog.V(LINFO).Infof("[http] %s %s - %s %s", req.Method, s.conn.RemoteAddr(), req.Host, req.Proto)
|
||||
|
||||
if glog.V(LDEBUG) {
|
||||
dump, _ := httputil.DumpRequest(req, false)
|
||||
glog.Infoln(string(dump))
|
||||
}
|
||||
|
||||
if req.Method == "PRI" && req.ProtoMajor == 2 {
|
||||
glog.V(LWARNING).Infof("[http] %s <- %s : Not an HTTP2 server", s.conn.RemoteAddr(), req.Host)
|
||||
resp := "HTTP/1.1 400 Bad Request\r\n" +
|
||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n"
|
||||
s.conn.Write([]byte(resp))
|
||||
return
|
||||
}
|
||||
|
||||
valid := false
|
||||
u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
|
||||
glog.V(LINFO).Infoln(u, p)
|
||||
for _, user := range s.Base.Node.Users {
|
||||
username := user.Username()
|
||||
password, _ := user.Password()
|
||||
if (u == username && p == password) ||
|
||||
(u == username && password == "") ||
|
||||
(username == "" && p == password) {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(s.Base.Node.Users) > 0 && !valid {
|
||||
glog.V(LWARNING).Infof("[http] %s <- %s : proxy authentication required", s.conn.RemoteAddr(), req.Host)
|
||||
resp := "HTTP/1.1 407 Proxy Authentication Required\r\n" +
|
||||
"Proxy-Authenticate: Basic realm=\"gost\"\r\n" +
|
||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n"
|
||||
s.conn.Write([]byte(resp))
|
||||
return
|
||||
}
|
||||
|
||||
req.Header.Del("Proxy-Authorization")
|
||||
|
||||
// forward http request
|
||||
lastNode := s.Base.Chain.lastNode
|
||||
if lastNode != nil && (lastNode.Protocol == "http" || lastNode.Protocol == "") {
|
||||
s.forwardRequest(req)
|
||||
return
|
||||
}
|
||||
|
||||
c, err := s.Base.Chain.Dial(req.Host)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[http] %s -> %s : %s", s.conn.RemoteAddr(), req.Host, err)
|
||||
|
||||
b := []byte("HTTP/1.1 503 Service unavailable\r\n" +
|
||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
||||
glog.V(LDEBUG).Infof("[http] %s <- %s\n%s", s.conn.RemoteAddr(), req.Host, string(b))
|
||||
s.conn.Write(b)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
if req.Method == http.MethodConnect {
|
||||
b := []byte("HTTP/1.1 200 Connection established\r\n" +
|
||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
||||
glog.V(LDEBUG).Infof("[http] %s <- %s\n%s", s.conn.RemoteAddr(), req.Host, string(b))
|
||||
s.conn.Write(b)
|
||||
} else {
|
||||
req.Header.Del("Proxy-Connection")
|
||||
req.Header.Set("Connection", "Keep-Alive")
|
||||
|
||||
if err = req.Write(c); err != nil {
|
||||
glog.V(LWARNING).Infof("[http] %s -> %s : %s", s.conn.RemoteAddr(), req.Host, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
glog.V(LINFO).Infof("[http] %s <-> %s", s.conn.RemoteAddr(), req.Host)
|
||||
s.Base.transport(s.conn, c)
|
||||
glog.V(LINFO).Infof("[http] %s >-< %s", s.conn.RemoteAddr(), req.Host)
|
||||
}
|
||||
|
||||
func (s *HttpServer) forwardRequest(req *http.Request) {
|
||||
last := s.Base.Chain.lastNode
|
||||
if last == nil {
|
||||
return
|
||||
}
|
||||
cc, err := s.Base.Chain.GetConn()
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[http] %s -> %s : %s", s.conn.RemoteAddr(), last.Addr, err)
|
||||
|
||||
b := []byte("HTTP/1.1 503 Service unavailable\r\n" +
|
||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
||||
glog.V(LDEBUG).Infof("[http] %s <- %s\n%s", s.conn.RemoteAddr(), last.Addr, string(b))
|
||||
s.conn.Write(b)
|
||||
return
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
if len(last.Users) > 0 {
|
||||
user := last.Users[0]
|
||||
s := user.String()
|
||||
if _, set := user.Password(); !set {
|
||||
s += ":"
|
||||
}
|
||||
req.Header.Set("Proxy-Authorization",
|
||||
"Basic "+base64.StdEncoding.EncodeToString([]byte(s)))
|
||||
}
|
||||
|
||||
cc.SetWriteDeadline(time.Now().Add(WriteTimeout))
|
||||
if err = req.WriteProxy(cc); err != nil {
|
||||
glog.V(LWARNING).Infof("[http] %s -> %s : %s", s.conn.RemoteAddr(), req.Host, err)
|
||||
return
|
||||
}
|
||||
cc.SetWriteDeadline(time.Time{})
|
||||
|
||||
glog.V(LINFO).Infof("[http] %s <-> %s", s.conn.RemoteAddr(), req.Host)
|
||||
s.Base.transport(s.conn, cc)
|
||||
glog.V(LINFO).Infof("[http] %s >-< %s", s.conn.RemoteAddr(), req.Host)
|
||||
return
|
||||
}
|
||||
|
||||
type Http2Server struct {
|
||||
Base *ProxyServer
|
||||
Handler http.Handler
|
||||
TLSConfig *tls.Config
|
||||
}
|
||||
|
||||
func NewHttp2Server(base *ProxyServer) *Http2Server {
|
||||
return &Http2Server{Base: base}
|
||||
}
|
||||
|
||||
func (s *Http2Server) ListenAndServeTLS(config *tls.Config) error {
|
||||
srv := http.Server{
|
||||
Addr: s.Base.Node.Addr,
|
||||
Handler: s.Handler,
|
||||
TLSConfig: config,
|
||||
}
|
||||
if srv.Handler == nil {
|
||||
srv.Handler = http.HandlerFunc(s.HandleRequest)
|
||||
}
|
||||
http2.ConfigureServer(&srv, nil)
|
||||
return srv.ListenAndServeTLS("", "")
|
||||
}
|
||||
|
||||
// Default HTTP2 server handler
|
||||
func (s *Http2Server) HandleRequest(w http.ResponseWriter, req *http.Request) {
|
||||
target := req.Header.Get("Gost-Target")
|
||||
if target == "" {
|
||||
target = req.Host
|
||||
}
|
||||
glog.V(LINFO).Infof("[http2] %s %s - %s %s", req.Method, req.RemoteAddr, target, req.Proto)
|
||||
if glog.V(LDEBUG) {
|
||||
dump, _ := httputil.DumpRequest(req, false)
|
||||
glog.Infoln(string(dump))
|
||||
}
|
||||
|
||||
w.Header().Set("Proxy-Agent", "gost/"+Version)
|
||||
|
||||
// HTTP2 as transport
|
||||
if req.Header.Get("Proxy-Switch") == "gost" {
|
||||
conn, err := s.Upgrade(w, req)
|
||||
if err != nil {
|
||||
glog.V(LINFO).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err)
|
||||
return
|
||||
}
|
||||
glog.V(LINFO).Infof("[http2] %s - %s : switch to HTTP2 transport mode OK", req.RemoteAddr, target)
|
||||
s.Base.handleConn(conn)
|
||||
return
|
||||
}
|
||||
|
||||
valid := false
|
||||
u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
|
||||
for _, user := range s.Base.Node.Users {
|
||||
username := user.Username()
|
||||
password, _ := user.Password()
|
||||
if (u == username && p == password) ||
|
||||
(u == username && password == "") ||
|
||||
(username == "" && p == password) {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(s.Base.Node.Users) > 0 && !valid {
|
||||
glog.V(LWARNING).Infof("[http2] %s <- %s : proxy authentication required", req.RemoteAddr, target)
|
||||
w.WriteHeader(http.StatusProxyAuthRequired)
|
||||
return
|
||||
}
|
||||
|
||||
req.Header.Del("Proxy-Authorization")
|
||||
|
||||
c, err := s.Base.Chain.Dial(target)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err)
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
glog.V(LINFO).Infof("[http2] %s <-> %s", req.RemoteAddr, target)
|
||||
|
||||
if req.Method == http.MethodConnect {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if fw, ok := w.(http.Flusher); ok {
|
||||
fw.Flush()
|
||||
}
|
||||
|
||||
// compatible with HTTP1.x
|
||||
if hj, ok := w.(http.Hijacker); ok && req.ProtoMajor == 1 {
|
||||
// we take over the underly connection
|
||||
conn, _, err := hj.Hijack()
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
s.Base.transport(conn, c)
|
||||
return
|
||||
}
|
||||
|
||||
errc := make(chan error, 2)
|
||||
|
||||
go func() {
|
||||
_, err := io.Copy(c, req.Body)
|
||||
errc <- err
|
||||
}()
|
||||
go func() {
|
||||
_, err := io.Copy(flushWriter{w}, c)
|
||||
errc <- err
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-errc:
|
||||
// glog.V(LWARNING).Infoln("exit", err)
|
||||
}
|
||||
glog.V(LINFO).Infof("[http2] %s >-< %s", req.RemoteAddr, target)
|
||||
return
|
||||
}
|
||||
|
||||
req.Header.Set("Connection", "Keep-Alive")
|
||||
if err = req.Write(c); err != nil {
|
||||
glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(c), req)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln(err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
for k, v := range resp.Header {
|
||||
for _, vv := range v {
|
||||
w.Header().Add(k, vv)
|
||||
}
|
||||
}
|
||||
w.WriteHeader(resp.StatusCode)
|
||||
if _, err := io.Copy(flushWriter{w}, resp.Body); err != nil {
|
||||
glog.V(LWARNING).Infof("[http2] %s <- %s : %s", req.RemoteAddr, target, err)
|
||||
}
|
||||
|
||||
glog.V(LINFO).Infof("[http2] %s >-< %s", req.RemoteAddr, target)
|
||||
}
|
||||
|
||||
// Upgrade upgrade an HTTP2 request to a bidirectional connection that preparing for tunneling other protocol, just like a websocket connection.
|
||||
func (s *Http2Server) Upgrade(w http.ResponseWriter, r *http.Request) (net.Conn, error) {
|
||||
if r.Method != http.MethodConnect {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return nil, errors.New("Method not allowed")
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
if fw, ok := w.(http.Flusher); ok {
|
||||
fw.Flush()
|
||||
}
|
||||
|
||||
conn := &http2Conn{r: r.Body, w: flushWriter{w}}
|
||||
conn.remoteAddr, _ = net.ResolveTCPAddr("tcp", r.RemoteAddr)
|
||||
conn.localAddr, _ = net.ResolveTCPAddr("tcp", r.Host)
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// HTTP2 client connection, wrapped up just like a net.Conn
|
||||
type http2Conn struct {
|
||||
r io.Reader
|
||||
w io.Writer
|
||||
remoteAddr net.Addr
|
||||
localAddr net.Addr
|
||||
}
|
||||
|
||||
func (c *http2Conn) Read(b []byte) (n int, err error) {
|
||||
return c.r.Read(b)
|
||||
}
|
||||
|
||||
func (c *http2Conn) Write(b []byte) (n int, err error) {
|
||||
return c.w.Write(b)
|
||||
}
|
||||
|
||||
func (c *http2Conn) Close() (err error) {
|
||||
if rc, ok := c.r.(io.Closer); ok {
|
||||
err = rc.Close()
|
||||
}
|
||||
if w, ok := c.w.(io.Closer); ok {
|
||||
err = w.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *http2Conn) LocalAddr() net.Addr {
|
||||
return c.localAddr
|
||||
}
|
||||
|
||||
func (c *http2Conn) RemoteAddr() net.Addr {
|
||||
return c.remoteAddr
|
||||
}
|
||||
|
||||
func (c *http2Conn) SetDeadline(t time.Time) error {
|
||||
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||
}
|
||||
|
||||
func (c *http2Conn) SetReadDeadline(t time.Time) error {
|
||||
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||
}
|
||||
|
||||
func (c *http2Conn) SetWriteDeadline(t time.Time) error {
|
||||
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||
}
|
||||
|
||||
type flushWriter struct {
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func (fw flushWriter) Write(p []byte) (n int, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if s, ok := r.(string); ok {
|
||||
err = errors.New(s)
|
||||
return
|
||||
}
|
||||
err = r.(error)
|
||||
}
|
||||
}()
|
||||
|
||||
n, err = fw.w.Write(p)
|
||||
if err != nil {
|
||||
// glog.V(LWARNING).Infoln("flush writer:", err)
|
||||
return
|
||||
}
|
||||
if f, ok := fw.w.(http.Flusher); ok {
|
||||
f.Flush()
|
||||
}
|
||||
return
|
||||
}
|
369
cmd/gost/vendor/github.com/ginuerzh/gost/kcp.go
generated
vendored
369
cmd/gost/vendor/github.com/ginuerzh/gost/kcp.go
generated
vendored
@ -1,369 +0,0 @@
|
||||
// KCP feature is based on https://github.com/xtaci/kcptun
|
||||
|
||||
package gost
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/json"
|
||||
"github.com/golang/glog"
|
||||
"github.com/klauspost/compress/snappy"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"gopkg.in/xtaci/kcp-go.v2"
|
||||
"gopkg.in/xtaci/smux.v1"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultKCPConfigFile = "kcp.json"
|
||||
)
|
||||
|
||||
var (
|
||||
SALT = "kcp-go"
|
||||
)
|
||||
|
||||
type KCPConfig struct {
|
||||
Key string `json:"key"`
|
||||
Crypt string `json:"crypt"`
|
||||
Mode string `json:"mode"`
|
||||
MTU int `json:"mtu"`
|
||||
SndWnd int `json:"sndwnd"`
|
||||
RcvWnd int `json:"rcvwnd"`
|
||||
DataShard int `json:"datashard"`
|
||||
ParityShard int `json:"parityshard"`
|
||||
DSCP int `json:"dscp"`
|
||||
NoComp bool `json:"nocomp"`
|
||||
AckNodelay bool `json:"acknodelay"`
|
||||
NoDelay int `json:"nodelay"`
|
||||
Interval int `json:"interval"`
|
||||
Resend int `json:"resend"`
|
||||
NoCongestion int `json:"nc"`
|
||||
SockBuf int `json:"sockbuf"`
|
||||
KeepAlive int `json:"keepalive"`
|
||||
}
|
||||
|
||||
func ParseKCPConfig(configFile string) (*KCPConfig, error) {
|
||||
if configFile == "" {
|
||||
configFile = DefaultKCPConfigFile
|
||||
}
|
||||
file, err := os.Open(configFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
config := &KCPConfig{}
|
||||
if err = json.NewDecoder(file).Decode(config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func (c *KCPConfig) Init() {
|
||||
switch c.Mode {
|
||||
case "normal":
|
||||
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 0, 30, 2, 1
|
||||
case "fast2":
|
||||
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 1, 20, 2, 1
|
||||
case "fast3":
|
||||
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 1, 10, 2, 1
|
||||
case "fast":
|
||||
fallthrough
|
||||
default:
|
||||
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 0, 20, 2, 1
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultKCPConfig = &KCPConfig{
|
||||
Key: "it's a secrect",
|
||||
Crypt: "aes",
|
||||
Mode: "fast",
|
||||
MTU: 1350,
|
||||
SndWnd: 1024,
|
||||
RcvWnd: 1024,
|
||||
DataShard: 10,
|
||||
ParityShard: 3,
|
||||
DSCP: 0,
|
||||
NoComp: false,
|
||||
AckNodelay: false,
|
||||
NoDelay: 0,
|
||||
Interval: 40,
|
||||
Resend: 0,
|
||||
NoCongestion: 0,
|
||||
SockBuf: 4194304,
|
||||
KeepAlive: 10,
|
||||
}
|
||||
)
|
||||
|
||||
type KCPServer struct {
|
||||
Base *ProxyServer
|
||||
Config *KCPConfig
|
||||
}
|
||||
|
||||
func NewKCPServer(base *ProxyServer, config *KCPConfig) *KCPServer {
|
||||
return &KCPServer{Base: base, Config: config}
|
||||
}
|
||||
|
||||
func (s *KCPServer) ListenAndServe() (err error) {
|
||||
if s.Config == nil {
|
||||
s.Config = DefaultKCPConfig
|
||||
}
|
||||
s.Config.Init()
|
||||
|
||||
ln, err := kcp.ListenWithOptions(s.Base.Node.Addr,
|
||||
blockCrypt(s.Config.Key, s.Config.Crypt, SALT), s.Config.DataShard, s.Config.ParityShard)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ln.SetDSCP(s.Config.DSCP); err != nil {
|
||||
glog.V(LWARNING).Infoln("[kcp]", err)
|
||||
}
|
||||
if err = ln.SetReadBuffer(s.Config.SockBuf); err != nil {
|
||||
glog.V(LWARNING).Infoln("[kcp]", err)
|
||||
}
|
||||
if err = ln.SetWriteBuffer(s.Config.SockBuf); err != nil {
|
||||
glog.V(LWARNING).Infoln("[kcp]", err)
|
||||
}
|
||||
|
||||
for {
|
||||
conn, err := ln.AcceptKCP()
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln(err)
|
||||
continue
|
||||
}
|
||||
|
||||
conn.SetStreamMode(true)
|
||||
conn.SetNoDelay(s.Config.NoDelay, s.Config.Interval, s.Config.Resend, s.Config.NoCongestion)
|
||||
conn.SetMtu(s.Config.MTU)
|
||||
conn.SetWindowSize(s.Config.SndWnd, s.Config.RcvWnd)
|
||||
conn.SetACKNoDelay(s.Config.AckNodelay)
|
||||
conn.SetKeepAlive(s.Config.KeepAlive)
|
||||
|
||||
go s.handleMux(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *KCPServer) handleMux(conn net.Conn) {
|
||||
smuxConfig := smux.DefaultConfig()
|
||||
smuxConfig.MaxReceiveBuffer = s.Config.SockBuf
|
||||
|
||||
glog.V(LINFO).Infof("[kcp] %s - %s", conn.RemoteAddr(), s.Base.Node.Addr)
|
||||
|
||||
if !s.Config.NoComp {
|
||||
conn = newCompStreamConn(conn)
|
||||
}
|
||||
|
||||
mux, err := smux.Server(conn, smuxConfig)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln("[kcp]", err)
|
||||
return
|
||||
}
|
||||
defer mux.Close()
|
||||
|
||||
glog.V(LINFO).Infof("[kcp] %s <-> %s", conn.RemoteAddr(), s.Base.Node.Addr)
|
||||
defer glog.V(LINFO).Infof("[kcp] %s >-< %s", conn.RemoteAddr(), s.Base.Node.Addr)
|
||||
|
||||
for {
|
||||
stream, err := mux.AcceptStream()
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln("[kcp]", err)
|
||||
return
|
||||
}
|
||||
go s.Base.handleConn(NewKCPConn(conn, stream))
|
||||
}
|
||||
}
|
||||
|
||||
func blockCrypt(key, crypt, salt string) (block kcp.BlockCrypt) {
|
||||
pass := pbkdf2.Key([]byte(key), []byte(salt), 4096, 32, sha1.New)
|
||||
|
||||
switch crypt {
|
||||
case "tea":
|
||||
block, _ = kcp.NewTEABlockCrypt(pass[:16])
|
||||
case "xor":
|
||||
block, _ = kcp.NewSimpleXORBlockCrypt(pass)
|
||||
case "none":
|
||||
block, _ = kcp.NewNoneBlockCrypt(pass)
|
||||
case "aes-128":
|
||||
block, _ = kcp.NewAESBlockCrypt(pass[:16])
|
||||
case "aes-192":
|
||||
block, _ = kcp.NewAESBlockCrypt(pass[:24])
|
||||
case "blowfish":
|
||||
block, _ = kcp.NewBlowfishBlockCrypt(pass)
|
||||
case "twofish":
|
||||
block, _ = kcp.NewTwofishBlockCrypt(pass)
|
||||
case "cast5":
|
||||
block, _ = kcp.NewCast5BlockCrypt(pass[:16])
|
||||
case "3des":
|
||||
block, _ = kcp.NewTripleDESBlockCrypt(pass[:24])
|
||||
case "xtea":
|
||||
block, _ = kcp.NewXTEABlockCrypt(pass[:16])
|
||||
case "salsa20":
|
||||
block, _ = kcp.NewSalsa20BlockCrypt(pass)
|
||||
case "aes":
|
||||
fallthrough
|
||||
default: // aes
|
||||
block, _ = kcp.NewAESBlockCrypt(pass)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type KCPSession struct {
|
||||
conn net.Conn
|
||||
session *smux.Session
|
||||
}
|
||||
|
||||
func DialKCP(addr string, config *KCPConfig) (*KCPSession, error) {
|
||||
if config == nil {
|
||||
config = DefaultKCPConfig
|
||||
}
|
||||
config.Init()
|
||||
|
||||
kcpconn, err := kcp.DialWithOptions(addr,
|
||||
blockCrypt(config.Key, config.Crypt, SALT), config.DataShard, config.ParityShard)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kcpconn.SetStreamMode(true)
|
||||
kcpconn.SetNoDelay(config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
|
||||
kcpconn.SetWindowSize(config.SndWnd, config.RcvWnd)
|
||||
kcpconn.SetMtu(config.MTU)
|
||||
kcpconn.SetACKNoDelay(config.AckNodelay)
|
||||
kcpconn.SetKeepAlive(config.KeepAlive)
|
||||
|
||||
if err := kcpconn.SetDSCP(config.DSCP); err != nil {
|
||||
glog.V(LWARNING).Infoln("[kcp]", err)
|
||||
}
|
||||
if err := kcpconn.SetReadBuffer(config.SockBuf); err != nil {
|
||||
glog.V(LWARNING).Infoln("[kcp]", err)
|
||||
}
|
||||
if err := kcpconn.SetWriteBuffer(config.SockBuf); err != nil {
|
||||
glog.V(LWARNING).Infoln("[kcp]", err)
|
||||
}
|
||||
|
||||
// stream multiplex
|
||||
smuxConfig := smux.DefaultConfig()
|
||||
smuxConfig.MaxReceiveBuffer = config.SockBuf
|
||||
var conn net.Conn = kcpconn
|
||||
if !config.NoComp {
|
||||
conn = newCompStreamConn(kcpconn)
|
||||
}
|
||||
session, err := smux.Client(conn, smuxConfig)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return &KCPSession{conn: conn, session: session}, nil
|
||||
}
|
||||
|
||||
func (session *KCPSession) GetConn() (*KCPConn, error) {
|
||||
stream, err := session.session.OpenStream()
|
||||
if err != nil {
|
||||
session.Close()
|
||||
return nil, err
|
||||
}
|
||||
return NewKCPConn(session.conn, stream), nil
|
||||
}
|
||||
|
||||
func (session *KCPSession) Close() error {
|
||||
return session.session.Close()
|
||||
}
|
||||
|
||||
func (session *KCPSession) IsClosed() bool {
|
||||
return session.session.IsClosed()
|
||||
}
|
||||
|
||||
func (session *KCPSession) NumStreams() int {
|
||||
return session.session.NumStreams()
|
||||
}
|
||||
|
||||
type KCPConn struct {
|
||||
conn net.Conn
|
||||
stream *smux.Stream
|
||||
}
|
||||
|
||||
func NewKCPConn(conn net.Conn, stream *smux.Stream) *KCPConn {
|
||||
return &KCPConn{conn: conn, stream: stream}
|
||||
}
|
||||
|
||||
func (c *KCPConn) Read(b []byte) (n int, err error) {
|
||||
return c.stream.Read(b)
|
||||
}
|
||||
|
||||
func (c *KCPConn) Write(b []byte) (n int, err error) {
|
||||
return c.stream.Write(b)
|
||||
}
|
||||
|
||||
func (c *KCPConn) Close() error {
|
||||
return c.stream.Close()
|
||||
}
|
||||
|
||||
func (c *KCPConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *KCPConn) RemoteAddr() net.Addr {
|
||||
return c.conn.RemoteAddr()
|
||||
}
|
||||
|
||||
func (c *KCPConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *KCPConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *KCPConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
type compStreamConn struct {
|
||||
conn net.Conn
|
||||
w *snappy.Writer
|
||||
r *snappy.Reader
|
||||
}
|
||||
|
||||
func newCompStreamConn(conn net.Conn) *compStreamConn {
|
||||
c := new(compStreamConn)
|
||||
c.conn = conn
|
||||
c.w = snappy.NewBufferedWriter(conn)
|
||||
c.r = snappy.NewReader(conn)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *compStreamConn) Read(b []byte) (n int, err error) {
|
||||
return c.r.Read(b)
|
||||
}
|
||||
|
||||
func (c *compStreamConn) Write(b []byte) (n int, err error) {
|
||||
n, err = c.w.Write(b)
|
||||
err = c.w.Flush()
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *compStreamConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *compStreamConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *compStreamConn) RemoteAddr() net.Addr {
|
||||
return c.conn.RemoteAddr()
|
||||
}
|
||||
|
||||
func (c *compStreamConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *compStreamConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *compStreamConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
161
cmd/gost/vendor/github.com/ginuerzh/gost/node.go
generated
vendored
161
cmd/gost/vendor/github.com/ginuerzh/gost/node.go
generated
vendored
@ -1,161 +0,0 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/golang/glog"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Proxy node represent a proxy
|
||||
type ProxyNode struct {
|
||||
Addr string // [host]:port
|
||||
Protocol string // protocol: http/socks5/ss
|
||||
Transport string // transport: ws/wss/tls/http2/tcp/udp/rtcp/rudp
|
||||
Remote string // remote address, used by tcp/udp port forwarding
|
||||
Users []*url.Userinfo // authentication for proxy
|
||||
values url.Values
|
||||
serverName string
|
||||
conn net.Conn
|
||||
}
|
||||
|
||||
// The proxy node string pattern is [scheme://][user:pass@host]:port.
|
||||
//
|
||||
// Scheme can be devided into two parts by character '+', such as: http+tls.
|
||||
func ParseProxyNode(s string) (node ProxyNode, err error) {
|
||||
if !strings.Contains(s, "://") {
|
||||
s = "gost://" + s
|
||||
}
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
node = ProxyNode{
|
||||
Addr: u.Host,
|
||||
values: u.Query(),
|
||||
serverName: u.Host,
|
||||
}
|
||||
|
||||
if u.User != nil {
|
||||
node.Users = append(node.Users, u.User)
|
||||
}
|
||||
|
||||
users, er := parseUsers(node.Get("secrets"))
|
||||
if users != nil {
|
||||
node.Users = append(node.Users, users...)
|
||||
}
|
||||
if er != nil {
|
||||
glog.V(LWARNING).Infoln("secrets:", er)
|
||||
}
|
||||
|
||||
if strings.Contains(u.Host, ":") {
|
||||
node.serverName, _, _ = net.SplitHostPort(u.Host)
|
||||
if node.serverName == "" {
|
||||
node.serverName = "localhost" // default server name
|
||||
}
|
||||
}
|
||||
|
||||
schemes := strings.Split(u.Scheme, "+")
|
||||
if len(schemes) == 1 {
|
||||
node.Protocol = schemes[0]
|
||||
node.Transport = schemes[0]
|
||||
}
|
||||
if len(schemes) == 2 {
|
||||
node.Protocol = schemes[0]
|
||||
node.Transport = schemes[1]
|
||||
}
|
||||
|
||||
switch node.Transport {
|
||||
case "ws", "wss", "tls", "http2", "quic", "kcp", "redirect", "ssu":
|
||||
case "https":
|
||||
node.Protocol = "http"
|
||||
node.Transport = "tls"
|
||||
case "tcp", "udp": // started from v2.1, tcp and udp are for local port forwarding
|
||||
node.Remote = strings.Trim(u.EscapedPath(), "/")
|
||||
case "rtcp", "rudp": // started from v2.1, rtcp and rudp are for remote port forwarding
|
||||
node.Remote = strings.Trim(u.EscapedPath(), "/")
|
||||
default:
|
||||
node.Transport = ""
|
||||
}
|
||||
|
||||
switch node.Protocol {
|
||||
case "http", "http2", "socks", "socks5", "ss":
|
||||
default:
|
||||
node.Protocol = ""
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func parseUsers(authFile string) (users []*url.Userinfo, err error) {
|
||||
if authFile == "" {
|
||||
return
|
||||
}
|
||||
|
||||
file, err := os.Open(authFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
|
||||
s := strings.SplitN(line, " ", 2)
|
||||
if len(s) == 1 {
|
||||
users = append(users, url.User(strings.TrimSpace(s[0])))
|
||||
} else if len(s) == 2 {
|
||||
users = append(users, url.UserPassword(strings.TrimSpace(s[0]), strings.TrimSpace(s[1])))
|
||||
}
|
||||
}
|
||||
|
||||
err = scanner.Err()
|
||||
return
|
||||
}
|
||||
|
||||
// Get get node parameter by key
|
||||
func (node *ProxyNode) Get(key string) string {
|
||||
return node.values.Get(key)
|
||||
}
|
||||
|
||||
func (node *ProxyNode) getBool(key string) bool {
|
||||
s := node.Get(key)
|
||||
if b, _ := strconv.ParseBool(s); b {
|
||||
return b
|
||||
}
|
||||
n, _ := strconv.Atoi(s)
|
||||
return n > 0
|
||||
}
|
||||
|
||||
func (node *ProxyNode) Set(key, value string) {
|
||||
node.values.Set(key, value)
|
||||
}
|
||||
|
||||
func (node *ProxyNode) insecureSkipVerify() bool {
|
||||
return !node.getBool("secure")
|
||||
}
|
||||
|
||||
func (node *ProxyNode) certFile() string {
|
||||
if cert := node.Get("cert"); cert != "" {
|
||||
return cert
|
||||
}
|
||||
return DefaultCertFile
|
||||
}
|
||||
|
||||
func (node *ProxyNode) keyFile() string {
|
||||
if key := node.Get("key"); key != "" {
|
||||
return key
|
||||
}
|
||||
return DefaultKeyFile
|
||||
}
|
||||
|
||||
func (node ProxyNode) String() string {
|
||||
return fmt.Sprintf("transport: %s, protocol: %s, addr: %s", node.Transport, node.Protocol, node.Addr)
|
||||
}
|
80
cmd/gost/vendor/github.com/ginuerzh/gost/quic.go
generated
vendored
80
cmd/gost/vendor/github.com/ginuerzh/gost/quic.go
generated
vendored
@ -1,80 +0,0 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"github.com/golang/glog"
|
||||
"github.com/lucas-clemente/quic-go/h2quic"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
)
|
||||
|
||||
type QuicServer struct {
|
||||
Base *ProxyServer
|
||||
Handler http.Handler
|
||||
TLSConfig *tls.Config
|
||||
}
|
||||
|
||||
func NewQuicServer(base *ProxyServer) *QuicServer {
|
||||
return &QuicServer{Base: base}
|
||||
}
|
||||
|
||||
func (s *QuicServer) ListenAndServeTLS(config *tls.Config) error {
|
||||
server := &h2quic.Server{
|
||||
Server: &http.Server{
|
||||
Addr: s.Base.Node.Addr,
|
||||
Handler: s.Handler,
|
||||
TLSConfig: config,
|
||||
},
|
||||
}
|
||||
if server.Handler == nil {
|
||||
server.Handler = http.HandlerFunc(s.HandleRequest)
|
||||
}
|
||||
return server.ListenAndServe()
|
||||
}
|
||||
|
||||
func (s *QuicServer) HandleRequest(w http.ResponseWriter, req *http.Request) {
|
||||
target := req.Host
|
||||
glog.V(LINFO).Infof("[quic] %s %s - %s %s", req.Method, req.RemoteAddr, target, req.Proto)
|
||||
|
||||
if glog.V(LDEBUG) {
|
||||
dump, _ := httputil.DumpRequest(req, false)
|
||||
glog.Infoln(string(dump))
|
||||
}
|
||||
|
||||
c, err := s.Base.Chain.Dial(target)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[quic] %s -> %s : %s", req.RemoteAddr, target, err)
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
glog.V(LINFO).Infof("[quic] %s <-> %s", req.RemoteAddr, target)
|
||||
|
||||
req.Header.Set("Connection", "Keep-Alive")
|
||||
if err = req.Write(c); err != nil {
|
||||
glog.V(LWARNING).Infof("[quic] %s -> %s : %s", req.RemoteAddr, target, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(c), req)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln(err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
for k, v := range resp.Header {
|
||||
for _, vv := range v {
|
||||
w.Header().Add(k, vv)
|
||||
}
|
||||
}
|
||||
w.WriteHeader(resp.StatusCode)
|
||||
if _, err := io.Copy(flushWriter{w}, resp.Body); err != nil {
|
||||
glog.V(LWARNING).Infof("[quic] %s <- %s : %s", req.RemoteAddr, target, err)
|
||||
}
|
||||
|
||||
glog.V(LINFO).Infof("[quic] %s >-< %s", req.RemoteAddr, target)
|
||||
}
|
103
cmd/gost/vendor/github.com/ginuerzh/gost/redirect.go
generated
vendored
103
cmd/gost/vendor/github.com/ginuerzh/gost/redirect.go
generated
vendored
@ -1,103 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package gost
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/golang/glog"
|
||||
"net"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
SO_ORIGINAL_DST = 80
|
||||
)
|
||||
|
||||
type RedsocksTCPServer struct {
|
||||
Base *ProxyServer
|
||||
}
|
||||
|
||||
func NewRedsocksTCPServer(base *ProxyServer) *RedsocksTCPServer {
|
||||
return &RedsocksTCPServer{
|
||||
Base: base,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RedsocksTCPServer) ListenAndServe() error {
|
||||
laddr, err := net.ResolveTCPAddr("tcp", s.Base.Node.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ln, err := net.ListenTCP("tcp", laddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer ln.Close()
|
||||
for {
|
||||
conn, err := ln.AcceptTCP()
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln(err)
|
||||
continue
|
||||
}
|
||||
go s.handleRedirectTCP(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RedsocksTCPServer) handleRedirectTCP(conn *net.TCPConn) {
|
||||
srcAddr := conn.RemoteAddr()
|
||||
dstAddr, conn, err := getOriginalDstAddr(conn)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[red-tcp] %s -> %s : %s", srcAddr, dstAddr, err)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
glog.V(LINFO).Infof("[red-tcp] %s -> %s", srcAddr, dstAddr)
|
||||
|
||||
cc, err := s.Base.Chain.Dial(dstAddr.String())
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[red-tcp] %s -> %s : %s", srcAddr, dstAddr, err)
|
||||
return
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
glog.V(LINFO).Infof("[red-tcp] %s <-> %s", srcAddr, dstAddr)
|
||||
s.Base.transport(conn, cc)
|
||||
glog.V(LINFO).Infof("[red-tcp] %s >-< %s", srcAddr, dstAddr)
|
||||
}
|
||||
|
||||
func getOriginalDstAddr(conn *net.TCPConn) (addr net.Addr, c *net.TCPConn, err error) {
|
||||
defer conn.Close()
|
||||
|
||||
fc, err := conn.File()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer fc.Close()
|
||||
|
||||
mreq, err := syscall.GetsockoptIPv6Mreq(int(fc.Fd()), syscall.IPPROTO_IP, SO_ORIGINAL_DST)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// only ipv4 support
|
||||
ip := net.IPv4(mreq.Multiaddr[4], mreq.Multiaddr[5], mreq.Multiaddr[6], mreq.Multiaddr[7])
|
||||
port := uint16(mreq.Multiaddr[2])<<8 + uint16(mreq.Multiaddr[3])
|
||||
addr, err = net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", ip.String(), port))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
cc, err := net.FileConn(fc)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
c, ok := cc.(*net.TCPConn)
|
||||
if !ok {
|
||||
err = errors.New("not a TCP connection")
|
||||
}
|
||||
return
|
||||
}
|
17
cmd/gost/vendor/github.com/ginuerzh/gost/redirect_win.go
generated
vendored
17
cmd/gost/vendor/github.com/ginuerzh/gost/redirect_win.go
generated
vendored
@ -1,17 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package gost
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
type RedsocksTCPServer struct{}
|
||||
|
||||
func NewRedsocksTCPServer(base *ProxyServer) *RedsocksTCPServer {
|
||||
return &RedsocksTCPServer{}
|
||||
}
|
||||
|
||||
func (s *RedsocksTCPServer) ListenAndServe() error {
|
||||
return errors.New("Not supported")
|
||||
}
|
260
cmd/gost/vendor/github.com/ginuerzh/gost/server.go
generated
vendored
260
cmd/gost/vendor/github.com/ginuerzh/gost/server.go
generated
vendored
@ -1,260 +0,0 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
"github.com/golang/glog"
|
||||
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ProxyServer struct {
|
||||
Node ProxyNode
|
||||
Chain *ProxyChain
|
||||
TLSConfig *tls.Config
|
||||
selector *serverSelector
|
||||
cipher *ss.Cipher
|
||||
ota bool
|
||||
}
|
||||
|
||||
func NewProxyServer(node ProxyNode, chain *ProxyChain, config *tls.Config) *ProxyServer {
|
||||
if chain == nil {
|
||||
chain = NewProxyChain()
|
||||
}
|
||||
if config == nil {
|
||||
config = &tls.Config{}
|
||||
}
|
||||
|
||||
var cipher *ss.Cipher
|
||||
var ota bool
|
||||
if node.Protocol == "ss" || node.Transport == "ssu" {
|
||||
var err error
|
||||
var method, password string
|
||||
|
||||
if len(node.Users) > 0 {
|
||||
method = node.Users[0].Username()
|
||||
password, _ = node.Users[0].Password()
|
||||
}
|
||||
ota = node.getBool("ota")
|
||||
if strings.HasSuffix(method, "-auth") {
|
||||
ota = true
|
||||
method = strings.TrimSuffix(method, "-auth")
|
||||
}
|
||||
cipher, err = ss.NewCipher(method, password)
|
||||
if err != nil {
|
||||
glog.Fatal(err)
|
||||
}
|
||||
}
|
||||
return &ProxyServer{
|
||||
Node: node,
|
||||
Chain: chain,
|
||||
TLSConfig: config,
|
||||
selector: &serverSelector{ // socks5 server selector
|
||||
// methods that socks5 server supported
|
||||
methods: []uint8{
|
||||
gosocks5.MethodNoAuth,
|
||||
gosocks5.MethodUserPass,
|
||||
MethodTLS,
|
||||
MethodTLSAuth,
|
||||
},
|
||||
users: node.Users,
|
||||
tlsConfig: config,
|
||||
},
|
||||
cipher: cipher,
|
||||
ota: ota,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ProxyServer) Serve() error {
|
||||
var ln net.Listener
|
||||
var err error
|
||||
node := s.Node
|
||||
|
||||
switch node.Transport {
|
||||
case "ws": // websocket connection
|
||||
return NewWebsocketServer(s).ListenAndServe()
|
||||
case "wss": // websocket security connection
|
||||
return NewWebsocketServer(s).ListenAndServeTLS(s.TLSConfig)
|
||||
case "tls": // tls connection
|
||||
ln, err = tls.Listen("tcp", node.Addr, s.TLSConfig)
|
||||
case "http2": // Standard HTTP2 proxy server, compatible with HTTP1.x.
|
||||
server := NewHttp2Server(s)
|
||||
server.Handler = http.HandlerFunc(server.HandleRequest)
|
||||
return server.ListenAndServeTLS(s.TLSConfig)
|
||||
case "tcp": // Local TCP port forwarding
|
||||
return NewTcpForwardServer(s).ListenAndServe()
|
||||
case "udp": // Local UDP port forwarding
|
||||
ttl, _ := strconv.Atoi(s.Node.Get("ttl"))
|
||||
if ttl <= 0 {
|
||||
ttl = DefaultTTL
|
||||
}
|
||||
return NewUdpForwardServer(s, ttl).ListenAndServe()
|
||||
case "rtcp": // Remote TCP port forwarding
|
||||
return NewRTcpForwardServer(s).Serve()
|
||||
case "rudp": // Remote UDP port forwarding
|
||||
return NewRUdpForwardServer(s).Serve()
|
||||
case "quic":
|
||||
return NewQuicServer(s).ListenAndServeTLS(s.TLSConfig)
|
||||
case "kcp":
|
||||
config, err := ParseKCPConfig(s.Node.Get("c"))
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln("[kcp]", err)
|
||||
}
|
||||
if config == nil {
|
||||
config = DefaultKCPConfig
|
||||
}
|
||||
// override crypt and key if specified explicitly
|
||||
if s.Node.Users != nil {
|
||||
config.Crypt = s.Node.Users[0].Username()
|
||||
config.Key, _ = s.Node.Users[0].Password()
|
||||
}
|
||||
return NewKCPServer(s, config).ListenAndServe()
|
||||
case "redirect":
|
||||
return NewRedsocksTCPServer(s).ListenAndServe()
|
||||
case "ssu": // shadowsocks udp relay
|
||||
ttl, _ := strconv.Atoi(s.Node.Get("ttl"))
|
||||
if ttl <= 0 {
|
||||
ttl = DefaultTTL
|
||||
}
|
||||
return NewShadowUdpServer(s, ttl).ListenAndServe()
|
||||
default:
|
||||
ln, err = net.Listen("tcp", node.Addr)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer ln.Close()
|
||||
|
||||
for {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln(err)
|
||||
continue
|
||||
}
|
||||
|
||||
setKeepAlive(conn, KeepAliveTime)
|
||||
|
||||
go s.handleConn(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ProxyServer) handleConn(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
switch s.Node.Protocol {
|
||||
case "ss": // shadowsocks
|
||||
server := NewShadowServer(ss.NewConn(conn, s.cipher.Copy()), s)
|
||||
server.OTA = s.ota
|
||||
server.Serve()
|
||||
return
|
||||
case "http":
|
||||
req, err := http.ReadRequest(bufio.NewReader(conn))
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln("[http]", err)
|
||||
return
|
||||
}
|
||||
NewHttpServer(conn, s).HandleRequest(req)
|
||||
return
|
||||
case "socks", "socks5":
|
||||
conn = gosocks5.ServerConn(conn, s.selector)
|
||||
req, err := gosocks5.ReadRequest(conn)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln("[socks5]", err)
|
||||
return
|
||||
}
|
||||
NewSocks5Server(conn, s).HandleRequest(req)
|
||||
return
|
||||
}
|
||||
|
||||
// http or socks5
|
||||
b := make([]byte, MediumBufferSize)
|
||||
|
||||
n, err := io.ReadAtLeast(conn, b, 2)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln(err)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: use bufio.Reader
|
||||
if b[0] == gosocks5.Ver5 {
|
||||
mn := int(b[1]) // methods count
|
||||
length := 2 + mn
|
||||
if n < length {
|
||||
if _, err := io.ReadFull(conn, b[n:length]); err != nil {
|
||||
glog.V(LWARNING).Infoln("[socks5]", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
// TODO: use gosocks5.ServerConn
|
||||
methods := b[2 : 2+mn]
|
||||
method := s.selector.Select(methods...)
|
||||
if _, err := conn.Write([]byte{gosocks5.Ver5, method}); err != nil {
|
||||
glog.V(LWARNING).Infoln("[socks5] select:", err)
|
||||
return
|
||||
}
|
||||
c, err := s.selector.OnSelected(method, conn)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln("[socks5] onselected:", err)
|
||||
return
|
||||
}
|
||||
conn = c
|
||||
|
||||
req, err := gosocks5.ReadRequest(conn)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln("[socks5] request:", err)
|
||||
return
|
||||
}
|
||||
NewSocks5Server(conn, s).HandleRequest(req)
|
||||
return
|
||||
}
|
||||
|
||||
req, err := http.ReadRequest(bufio.NewReader(&reqReader{b: b[:n], r: conn}))
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln("[http]", err)
|
||||
return
|
||||
}
|
||||
NewHttpServer(conn, s).HandleRequest(req)
|
||||
}
|
||||
|
||||
func (_ *ProxyServer) transport(conn1, conn2 net.Conn) (err error) {
|
||||
errc := make(chan error, 2)
|
||||
|
||||
go func() {
|
||||
_, err := io.Copy(conn1, conn2)
|
||||
errc <- err
|
||||
}()
|
||||
|
||||
go func() {
|
||||
_, err := io.Copy(conn2, conn1)
|
||||
errc <- err
|
||||
}()
|
||||
|
||||
select {
|
||||
case err = <-errc:
|
||||
//glog.V(LWARNING).Infoln("transport exit", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type reqReader struct {
|
||||
b []byte
|
||||
r io.Reader
|
||||
}
|
||||
|
||||
func (r *reqReader) Read(p []byte) (n int, err error) {
|
||||
if len(r.b) == 0 {
|
||||
return r.r.Read(p)
|
||||
}
|
||||
n = copy(p, r.b)
|
||||
r.b = r.b[n:]
|
||||
|
||||
return
|
||||
}
|
674
cmd/gost/vendor/github.com/ginuerzh/gost/socks.go
generated
vendored
674
cmd/gost/vendor/github.com/ginuerzh/gost/socks.go
generated
vendored
@ -1,674 +0,0 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
//"errors"
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
"github.com/golang/glog"
|
||||
//"os/exec"
|
||||
//"io"
|
||||
//"io/ioutil"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
MethodTLS uint8 = 0x80 // extended method for tls
|
||||
MethodTLSAuth uint8 = 0x82 // extended method for tls+auth
|
||||
)
|
||||
|
||||
const (
|
||||
CmdUdpTun uint8 = 0xF3 // extended method for udp over tcp
|
||||
)
|
||||
|
||||
type clientSelector struct {
|
||||
methods []uint8
|
||||
user *url.Userinfo
|
||||
tlsConfig *tls.Config
|
||||
}
|
||||
|
||||
func (selector *clientSelector) Methods() []uint8 {
|
||||
return selector.methods
|
||||
}
|
||||
|
||||
func (selector *clientSelector) Select(methods ...uint8) (method uint8) {
|
||||
return
|
||||
}
|
||||
|
||||
func (selector *clientSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) {
|
||||
switch method {
|
||||
case MethodTLS:
|
||||
conn = tls.Client(conn, selector.tlsConfig)
|
||||
|
||||
case gosocks5.MethodUserPass, MethodTLSAuth:
|
||||
if method == MethodTLSAuth {
|
||||
conn = tls.Client(conn, selector.tlsConfig)
|
||||
}
|
||||
|
||||
var username, password string
|
||||
if selector.user != nil {
|
||||
username = selector.user.Username()
|
||||
password, _ = selector.user.Password()
|
||||
}
|
||||
|
||||
req := gosocks5.NewUserPassRequest(gosocks5.UserPassVer, username, password)
|
||||
if err := req.Write(conn); err != nil {
|
||||
glog.V(LWARNING).Infoln("socks5 auth:", err)
|
||||
return nil, err
|
||||
}
|
||||
glog.V(LDEBUG).Infoln(req)
|
||||
|
||||
resp, err := gosocks5.ReadUserPassResponse(conn)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln("socks5 auth:", err)
|
||||
return nil, err
|
||||
}
|
||||
glog.V(LDEBUG).Infoln(resp)
|
||||
|
||||
if resp.Status != gosocks5.Succeeded {
|
||||
return nil, gosocks5.ErrAuthFailure
|
||||
}
|
||||
case gosocks5.MethodNoAcceptable:
|
||||
return nil, gosocks5.ErrBadMethod
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
type serverSelector struct {
|
||||
methods []uint8
|
||||
users []*url.Userinfo
|
||||
tlsConfig *tls.Config
|
||||
}
|
||||
|
||||
func (selector *serverSelector) Methods() []uint8 {
|
||||
return selector.methods
|
||||
}
|
||||
|
||||
func (selector *serverSelector) Select(methods ...uint8) (method uint8) {
|
||||
glog.V(LDEBUG).Infof("%d %d %v", gosocks5.Ver5, len(methods), methods)
|
||||
|
||||
method = gosocks5.MethodNoAuth
|
||||
for _, m := range methods {
|
||||
if m == MethodTLS {
|
||||
method = m
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// when user/pass is set, auth is mandatory
|
||||
if selector.users != nil {
|
||||
if method == gosocks5.MethodNoAuth {
|
||||
method = gosocks5.MethodUserPass
|
||||
}
|
||||
if method == MethodTLS {
|
||||
method = MethodTLSAuth
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) {
|
||||
glog.V(LDEBUG).Infof("%d %d", gosocks5.Ver5, method)
|
||||
|
||||
switch method {
|
||||
case MethodTLS:
|
||||
conn = tls.Server(conn, selector.tlsConfig)
|
||||
|
||||
case gosocks5.MethodUserPass, MethodTLSAuth:
|
||||
if method == MethodTLSAuth {
|
||||
conn = tls.Server(conn, selector.tlsConfig)
|
||||
}
|
||||
|
||||
req, err := gosocks5.ReadUserPassRequest(conn)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln("[socks5-auth]", err)
|
||||
return nil, err
|
||||
}
|
||||
glog.V(LDEBUG).Infoln("[socks5]", req.String())
|
||||
|
||||
valid := false
|
||||
for _, user := range selector.users {
|
||||
username := user.Username()
|
||||
password, _ := user.Password()
|
||||
if (req.Username == username && req.Password == password) ||
|
||||
(req.Username == username && password == "") ||
|
||||
(username == "" && req.Password == password) {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(selector.users) > 0 && !valid {
|
||||
resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Failure)
|
||||
if err := resp.Write(conn); err != nil {
|
||||
glog.V(LWARNING).Infoln("[socks5-auth]", err)
|
||||
return nil, err
|
||||
}
|
||||
glog.V(LDEBUG).Infoln("[socks5]", resp)
|
||||
glog.V(LWARNING).Infoln("[socks5-auth] proxy authentication required")
|
||||
|
||||
return nil, gosocks5.ErrAuthFailure
|
||||
}
|
||||
|
||||
resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Succeeded)
|
||||
if err := resp.Write(conn); err != nil {
|
||||
glog.V(LWARNING).Infoln("[socks5-auth]", err)
|
||||
return nil, err
|
||||
}
|
||||
glog.V(LDEBUG).Infoln(resp)
|
||||
|
||||
case gosocks5.MethodNoAcceptable:
|
||||
return nil, gosocks5.ErrBadMethod
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
type Socks5Server struct {
|
||||
conn net.Conn
|
||||
Base *ProxyServer
|
||||
}
|
||||
|
||||
func NewSocks5Server(conn net.Conn, base *ProxyServer) *Socks5Server {
|
||||
return &Socks5Server{conn: conn, Base: base}
|
||||
}
|
||||
|
||||
func (s *Socks5Server) HandleRequest(req *gosocks5.Request) {
|
||||
glog.V(LDEBUG).Infof("[socks5] %s -> %s\n%s", s.conn.RemoteAddr(), req.Addr, req)
|
||||
|
||||
switch req.Cmd {
|
||||
case gosocks5.CmdConnect:
|
||||
glog.V(LINFO).Infof("[socks5-connect] %s -> %s", s.conn.RemoteAddr(), req.Addr)
|
||||
s.handleConnect(req)
|
||||
|
||||
case gosocks5.CmdBind:
|
||||
glog.V(LINFO).Infof("[socks5-bind] %s - %s", s.conn.RemoteAddr(), req.Addr)
|
||||
s.handleBind(req)
|
||||
|
||||
case gosocks5.CmdUdp:
|
||||
glog.V(LINFO).Infof("[socks5-udp] %s - %s", s.conn.RemoteAddr(), req.Addr)
|
||||
s.handleUDPRelay(req)
|
||||
|
||||
case CmdUdpTun:
|
||||
glog.V(LINFO).Infof("[socks5-udp] %s - %s", s.conn.RemoteAddr(), req.Addr)
|
||||
s.handleUDPTunnel(req)
|
||||
|
||||
default:
|
||||
glog.V(LWARNING).Infoln("[socks5] Unrecognized request:", req.Cmd)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Socks5Server) handleConnect(req *gosocks5.Request) {
|
||||
cc, err := s.Base.Chain.Dial(req.Addr.String())
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[socks5-connect] %s -> %s : %s", s.conn.RemoteAddr(), req.Addr, err)
|
||||
rep := gosocks5.NewReply(gosocks5.HostUnreachable, nil)
|
||||
rep.Write(s.conn)
|
||||
glog.V(LDEBUG).Infof("[socks5-connect] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, rep)
|
||||
return
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
rep := gosocks5.NewReply(gosocks5.Succeeded, nil)
|
||||
if err := rep.Write(s.conn); err != nil {
|
||||
glog.V(LWARNING).Infof("[socks5-connect] %s <- %s : %s", s.conn.RemoteAddr(), req.Addr, err)
|
||||
return
|
||||
}
|
||||
glog.V(LDEBUG).Infof("[socks5-connect] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, rep)
|
||||
|
||||
glog.V(LINFO).Infof("[socks5-connect] %s <-> %s", s.conn.RemoteAddr(), req.Addr)
|
||||
//Transport(conn, cc)
|
||||
s.Base.transport(s.conn, cc)
|
||||
glog.V(LINFO).Infof("[socks5-connect] %s >-< %s", s.conn.RemoteAddr(), req.Addr)
|
||||
}
|
||||
|
||||
func (s *Socks5Server) handleBind(req *gosocks5.Request) {
|
||||
cc, err := s.Base.Chain.GetConn()
|
||||
|
||||
// connection error
|
||||
if err != nil && err != ErrEmptyChain {
|
||||
glog.V(LWARNING).Infof("[socks5-bind] %s <- %s : %s", s.conn.RemoteAddr(), req.Addr, err)
|
||||
reply := gosocks5.NewReply(gosocks5.Failure, nil)
|
||||
reply.Write(s.conn)
|
||||
glog.V(LDEBUG).Infof("[socks5-bind] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, reply)
|
||||
return
|
||||
}
|
||||
// serve socks5 bind
|
||||
if err == ErrEmptyChain {
|
||||
s.bindOn(req.Addr.String())
|
||||
return
|
||||
}
|
||||
|
||||
defer cc.Close()
|
||||
// forward request
|
||||
req.Write(cc)
|
||||
|
||||
glog.V(LINFO).Infof("[socks5-bind] %s <-> %s", s.conn.RemoteAddr(), cc.RemoteAddr())
|
||||
s.Base.transport(s.conn, cc)
|
||||
glog.V(LINFO).Infof("[socks5-bind] %s >-< %s", s.conn.RemoteAddr(), cc.RemoteAddr())
|
||||
}
|
||||
|
||||
func (s *Socks5Server) handleUDPRelay(req *gosocks5.Request) {
|
||||
bindAddr, _ := net.ResolveUDPAddr("udp", req.Addr.String())
|
||||
relay, err := net.ListenUDP("udp", bindAddr) // udp associate, strict mode: if the port already in use, it will return error
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", s.conn.RemoteAddr(), req.Addr, err)
|
||||
reply := gosocks5.NewReply(gosocks5.Failure, nil)
|
||||
reply.Write(s.conn)
|
||||
glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, reply)
|
||||
return
|
||||
}
|
||||
defer relay.Close()
|
||||
|
||||
socksAddr := ToSocksAddr(relay.LocalAddr())
|
||||
socksAddr.Host, _, _ = net.SplitHostPort(s.conn.LocalAddr().String())
|
||||
reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr)
|
||||
if err := reply.Write(s.conn); err != nil {
|
||||
glog.V(LWARNING).Infof("[socks5-udp] %s <- %s : %s", s.conn.RemoteAddr(), req.Addr, err)
|
||||
return
|
||||
}
|
||||
glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", s.conn.RemoteAddr(), reply.Addr, reply)
|
||||
glog.V(LINFO).Infof("[socks5-udp] %s - %s BIND ON %s OK", s.conn.RemoteAddr(), req.Addr, socksAddr)
|
||||
|
||||
cc, err := s.Base.Chain.GetConn()
|
||||
// connection error
|
||||
if err != nil && err != ErrEmptyChain {
|
||||
glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", s.conn.RemoteAddr(), socksAddr, err)
|
||||
return
|
||||
}
|
||||
|
||||
// serve as standard socks5 udp relay local <-> remote
|
||||
if err == ErrEmptyChain {
|
||||
peer, er := net.ListenUDP("udp", nil)
|
||||
if er != nil {
|
||||
glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", s.conn.RemoteAddr(), socksAddr, er)
|
||||
return
|
||||
}
|
||||
defer peer.Close()
|
||||
|
||||
go s.transportUDP(relay, peer)
|
||||
}
|
||||
|
||||
// forward udp local <-> tunnel
|
||||
if err == nil {
|
||||
defer cc.Close()
|
||||
|
||||
cc.SetWriteDeadline(time.Now().Add(WriteTimeout))
|
||||
req := gosocks5.NewRequest(CmdUdpTun, nil)
|
||||
if err := req.Write(cc); err != nil {
|
||||
glog.V(LWARNING).Infoln("[socks5-udp] %s -> %s : %s", s.conn.RemoteAddr(), cc.RemoteAddr(), err)
|
||||
return
|
||||
}
|
||||
cc.SetWriteDeadline(time.Time{})
|
||||
glog.V(LDEBUG).Infof("[socks5-udp] %s -> %s\n%s", s.conn.RemoteAddr(), cc.RemoteAddr(), req)
|
||||
|
||||
cc.SetReadDeadline(time.Now().Add(ReadTimeout))
|
||||
reply, err = gosocks5.ReadReply(cc)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln("[socks5-udp] %s -> %s : %s", s.conn.RemoteAddr(), cc.RemoteAddr(), err)
|
||||
return
|
||||
}
|
||||
glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", s.conn.RemoteAddr(), cc.RemoteAddr(), reply)
|
||||
|
||||
if reply.Rep != gosocks5.Succeeded {
|
||||
glog.V(LWARNING).Infoln("[socks5-udp] %s <- %s : udp associate failed", s.conn.RemoteAddr(), cc.RemoteAddr())
|
||||
return
|
||||
}
|
||||
cc.SetReadDeadline(time.Time{})
|
||||
glog.V(LINFO).Infof("[socks5-udp] %s <-> %s [tun: %s]", s.conn.RemoteAddr(), socksAddr, reply.Addr)
|
||||
|
||||
go s.tunnelClientUDP(relay, cc)
|
||||
}
|
||||
|
||||
glog.V(LINFO).Infof("[socks5-udp] %s <-> %s", s.conn.RemoteAddr(), socksAddr)
|
||||
b := make([]byte, SmallBufferSize)
|
||||
for {
|
||||
_, err := s.conn.Read(b) // discard any data from tcp connection
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[socks5-udp] %s - %s : %s", s.conn.RemoteAddr(), socksAddr, err)
|
||||
break // client disconnected
|
||||
}
|
||||
}
|
||||
glog.V(LINFO).Infof("[socks5-udp] %s >-< %s", s.conn.RemoteAddr(), socksAddr)
|
||||
}
|
||||
|
||||
func (s *Socks5Server) handleUDPTunnel(req *gosocks5.Request) {
|
||||
cc, err := s.Base.Chain.GetConn()
|
||||
|
||||
// connection error
|
||||
if err != nil && err != ErrEmptyChain {
|
||||
glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", s.conn.RemoteAddr(), req.Addr, err)
|
||||
reply := gosocks5.NewReply(gosocks5.Failure, nil)
|
||||
reply.Write(s.conn)
|
||||
glog.V(LDEBUG).Infof("[socks5-udp] %s -> %s\n%s", s.conn.RemoteAddr(), req.Addr, reply)
|
||||
return
|
||||
}
|
||||
|
||||
// serve tunnel udp, tunnel <-> remote, handle tunnel udp request
|
||||
if err == ErrEmptyChain {
|
||||
bindAddr, _ := net.ResolveUDPAddr("udp", req.Addr.String())
|
||||
uc, err := net.ListenUDP("udp", bindAddr)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", s.conn.RemoteAddr(), req.Addr, err)
|
||||
return
|
||||
}
|
||||
defer uc.Close()
|
||||
|
||||
socksAddr := ToSocksAddr(uc.LocalAddr())
|
||||
socksAddr.Host, _, _ = net.SplitHostPort(s.conn.LocalAddr().String())
|
||||
reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr)
|
||||
if err := reply.Write(s.conn); err != nil {
|
||||
glog.V(LWARNING).Infof("[socks5-udp] %s <- %s : %s", s.conn.RemoteAddr(), socksAddr, err)
|
||||
return
|
||||
}
|
||||
glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", s.conn.RemoteAddr(), socksAddr, reply)
|
||||
|
||||
glog.V(LINFO).Infof("[socks5-udp] %s <-> %s", s.conn.RemoteAddr(), socksAddr)
|
||||
s.tunnelServerUDP(s.conn, uc)
|
||||
glog.V(LINFO).Infof("[socks5-udp] %s >-< %s", s.conn.RemoteAddr(), socksAddr)
|
||||
return
|
||||
}
|
||||
|
||||
defer cc.Close()
|
||||
|
||||
// tunnel <-> tunnel, direct forwarding
|
||||
req.Write(cc)
|
||||
|
||||
glog.V(LINFO).Infof("[socks5-udp] %s <-> %s [tun]", s.conn.RemoteAddr(), cc.RemoteAddr())
|
||||
s.Base.transport(s.conn, cc)
|
||||
glog.V(LINFO).Infof("[socks5-udp] %s >-< %s [tun]", s.conn.RemoteAddr(), cc.RemoteAddr())
|
||||
}
|
||||
|
||||
func (s *Socks5Server) bindOn(addr string) {
|
||||
bindAddr, _ := net.ResolveTCPAddr("tcp", addr)
|
||||
ln, err := net.ListenTCP("tcp", bindAddr) // strict mode: if the port already in use, it will return error
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[socks5-bind] %s -> %s : %s", s.conn.RemoteAddr(), addr, err)
|
||||
gosocks5.NewReply(gosocks5.Failure, nil).Write(s.conn)
|
||||
return
|
||||
}
|
||||
|
||||
socksAddr := ToSocksAddr(ln.Addr())
|
||||
// Issue: may not reachable when host has multi-interface
|
||||
socksAddr.Host, _, _ = net.SplitHostPort(s.conn.LocalAddr().String())
|
||||
reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr)
|
||||
if err := reply.Write(s.conn); err != nil {
|
||||
glog.V(LWARNING).Infof("[socks5-bind] %s <- %s : %s", s.conn.RemoteAddr(), addr, err)
|
||||
ln.Close()
|
||||
return
|
||||
}
|
||||
glog.V(LDEBUG).Infof("[socks5-bind] %s <- %s\n%s", s.conn.RemoteAddr(), addr, reply)
|
||||
glog.V(LINFO).Infof("[socks5-bind] %s - %s BIND ON %s OK", s.conn.RemoteAddr(), addr, socksAddr)
|
||||
|
||||
var pconn net.Conn
|
||||
accept := func() <-chan error {
|
||||
errc := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
defer close(errc)
|
||||
defer ln.Close()
|
||||
|
||||
c, err := ln.AcceptTCP()
|
||||
if err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
pconn = c
|
||||
}()
|
||||
|
||||
return errc
|
||||
}
|
||||
|
||||
pc1, pc2 := net.Pipe()
|
||||
pipe := func() <-chan error {
|
||||
errc := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
defer close(errc)
|
||||
defer pc1.Close()
|
||||
|
||||
errc <- s.Base.transport(s.conn, pc1)
|
||||
}()
|
||||
|
||||
return errc
|
||||
}
|
||||
|
||||
defer pc2.Close()
|
||||
|
||||
for {
|
||||
select {
|
||||
case err := <-accept():
|
||||
if err != nil || pconn == nil {
|
||||
glog.V(LWARNING).Infof("[socks5-bind] %s <- %s : %s", s.conn.RemoteAddr(), addr, err)
|
||||
return
|
||||
}
|
||||
defer pconn.Close()
|
||||
|
||||
reply := gosocks5.NewReply(gosocks5.Succeeded, ToSocksAddr(pconn.RemoteAddr()))
|
||||
if err := reply.Write(pc2); err != nil {
|
||||
glog.V(LWARNING).Infof("[socks5-bind] %s <- %s : %s", s.conn.RemoteAddr(), addr, err)
|
||||
}
|
||||
glog.V(LDEBUG).Infof("[socks5-bind] %s <- %s\n%s", s.conn.RemoteAddr(), addr, reply)
|
||||
glog.V(LINFO).Infof("[socks5-bind] %s <- %s PEER %s ACCEPTED", s.conn.RemoteAddr(), socksAddr, pconn.RemoteAddr())
|
||||
|
||||
glog.V(LINFO).Infof("[socks5-bind] %s <-> %s", s.conn.RemoteAddr(), pconn.RemoteAddr())
|
||||
if err = s.Base.transport(pc2, pconn); err != nil {
|
||||
glog.V(LWARNING).Infoln(err)
|
||||
}
|
||||
glog.V(LINFO).Infof("[socks5-bind] %s >-< %s", s.conn.RemoteAddr(), pconn.RemoteAddr())
|
||||
return
|
||||
case err := <-pipe():
|
||||
glog.V(LWARNING).Infof("[socks5-bind] %s -> %s : %v", s.conn.RemoteAddr(), addr, err)
|
||||
ln.Close()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Socks5Server) transportUDP(relay, peer *net.UDPConn) (err error) {
|
||||
errc := make(chan error, 2)
|
||||
|
||||
var clientAddr *net.UDPAddr
|
||||
|
||||
go func() {
|
||||
b := make([]byte, LargeBufferSize)
|
||||
|
||||
for {
|
||||
n, laddr, err := relay.ReadFromUDP(b)
|
||||
if err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
if clientAddr == nil {
|
||||
clientAddr = laddr
|
||||
}
|
||||
dgram, err := gosocks5.ReadUDPDatagram(bytes.NewReader(b[:n]))
|
||||
if err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
|
||||
raddr, err := net.ResolveUDPAddr("udp", dgram.Header.Addr.String())
|
||||
if err != nil {
|
||||
continue // drop silently
|
||||
}
|
||||
if _, err := peer.WriteToUDP(dgram.Data, raddr); err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
glog.V(LDEBUG).Infof("[socks5-udp] %s >>> %s length: %d", relay.LocalAddr(), raddr, len(dgram.Data))
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
b := make([]byte, LargeBufferSize)
|
||||
|
||||
for {
|
||||
n, raddr, err := peer.ReadFromUDP(b)
|
||||
if err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
if clientAddr == nil {
|
||||
continue
|
||||
}
|
||||
buf := bytes.Buffer{}
|
||||
dgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(0, 0, ToSocksAddr(raddr)), b[:n])
|
||||
dgram.Write(&buf)
|
||||
if _, err := relay.WriteToUDP(buf.Bytes(), clientAddr); err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
glog.V(LDEBUG).Infof("[socks5-udp] %s <<< %s length: %d", relay.LocalAddr(), raddr, len(dgram.Data))
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case err = <-errc:
|
||||
//log.Println("w exit", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Socks5Server) tunnelClientUDP(uc *net.UDPConn, cc net.Conn) (err error) {
|
||||
errc := make(chan error, 2)
|
||||
|
||||
var clientAddr *net.UDPAddr
|
||||
|
||||
go func() {
|
||||
b := make([]byte, LargeBufferSize)
|
||||
|
||||
for {
|
||||
n, addr, err := uc.ReadFromUDP(b)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[udp-tun] %s <- %s : %s", cc.RemoteAddr(), addr, err)
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
|
||||
// glog.V(LDEBUG).Infof("read udp %d, % #x", n, b[:n])
|
||||
// pipe from relay to tunnel
|
||||
dgram, err := gosocks5.ReadUDPDatagram(bytes.NewReader(b[:n]))
|
||||
if err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
if clientAddr == nil {
|
||||
clientAddr = addr
|
||||
}
|
||||
dgram.Header.Rsv = uint16(len(dgram.Data))
|
||||
if err := dgram.Write(cc); err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
glog.V(LDEBUG).Infof("[udp-tun] %s >>> %s length: %d", uc.LocalAddr(), dgram.Header.Addr, len(dgram.Data))
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
dgram, err := gosocks5.ReadUDPDatagram(cc)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[udp-tun] %s -> 0 : %s", cc.RemoteAddr(), err)
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
|
||||
// pipe from tunnel to relay
|
||||
if clientAddr == nil {
|
||||
continue
|
||||
}
|
||||
dgram.Header.Rsv = 0
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
dgram.Write(&buf)
|
||||
if _, err := uc.WriteToUDP(buf.Bytes(), clientAddr); err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
glog.V(LDEBUG).Infof("[udp-tun] %s <<< %s length: %d", uc.LocalAddr(), dgram.Header.Addr, len(dgram.Data))
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case err = <-errc:
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Socks5Server) tunnelServerUDP(cc net.Conn, uc *net.UDPConn) (err error) {
|
||||
errc := make(chan error, 2)
|
||||
|
||||
go func() {
|
||||
b := make([]byte, LargeBufferSize)
|
||||
|
||||
for {
|
||||
n, addr, err := uc.ReadFromUDP(b)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[udp-tun] %s <- %s : %s", cc.RemoteAddr(), addr, err)
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
|
||||
// pipe from peer to tunnel
|
||||
dgram := gosocks5.NewUDPDatagram(
|
||||
gosocks5.NewUDPHeader(uint16(n), 0, ToSocksAddr(addr)), b[:n])
|
||||
if err := dgram.Write(cc); err != nil {
|
||||
glog.V(LWARNING).Infof("[udp-tun] %s <- %s : %s", cc.RemoteAddr(), dgram.Header.Addr, err)
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
glog.V(LDEBUG).Infof("[udp-tun] %s <<< %s length: %d", cc.RemoteAddr(), dgram.Header.Addr, len(dgram.Data))
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
dgram, err := gosocks5.ReadUDPDatagram(cc)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[udp-tun] %s -> 0 : %s", cc.RemoteAddr(), err)
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
|
||||
// pipe from tunnel to peer
|
||||
addr, err := net.ResolveUDPAddr("udp", dgram.Header.Addr.String())
|
||||
if err != nil {
|
||||
continue // drop silently
|
||||
}
|
||||
if _, err := uc.WriteToUDP(dgram.Data, addr); err != nil {
|
||||
glog.V(LWARNING).Infof("[udp-tun] %s -> %s : %s", cc.RemoteAddr(), addr, err)
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
glog.V(LDEBUG).Infof("[udp-tun] %s >>> %s length: %d", cc.RemoteAddr(), addr, len(dgram.Data))
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case err = <-errc:
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func ToSocksAddr(addr net.Addr) *gosocks5.Addr {
|
||||
host := "0.0.0.0"
|
||||
port := 0
|
||||
if addr != nil {
|
||||
h, p, _ := net.SplitHostPort(addr.String())
|
||||
host = h
|
||||
port, _ = strconv.Atoi(p)
|
||||
}
|
||||
return &gosocks5.Addr{
|
||||
Type: gosocks5.AddrIPv4,
|
||||
Host: host,
|
||||
Port: uint16(port),
|
||||
}
|
||||
}
|
353
cmd/gost/vendor/github.com/ginuerzh/gost/ss.go
generated
vendored
353
cmd/gost/vendor/github.com/ginuerzh/gost/ss.go
generated
vendored
@ -1,353 +0,0 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
"github.com/golang/glog"
|
||||
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
idType = 0 // address type index
|
||||
idIP0 = 1 // ip addres start index
|
||||
idDmLen = 1 // domain address length index
|
||||
idDm0 = 2 // domain address start index
|
||||
|
||||
typeIPv4 = 1 // type is ipv4 address
|
||||
typeDm = 3 // type is domain address
|
||||
typeIPv6 = 4 // type is ipv6 address
|
||||
|
||||
lenIPv4 = net.IPv4len + 2 // ipv4 + 2port
|
||||
lenIPv6 = net.IPv6len + 2 // ipv6 + 2port
|
||||
lenDmBase = 2 // 1addrLen + 2port, plus addrLen
|
||||
lenHmacSha1 = 10
|
||||
)
|
||||
|
||||
type ShadowServer struct {
|
||||
conn *ss.Conn
|
||||
Base *ProxyServer
|
||||
OTA bool // one time auth
|
||||
}
|
||||
|
||||
func NewShadowServer(conn *ss.Conn, base *ProxyServer) *ShadowServer {
|
||||
return &ShadowServer{conn: conn, Base: base}
|
||||
}
|
||||
|
||||
func (s *ShadowServer) Serve() {
|
||||
glog.V(LINFO).Infof("[ss] %s - %s", s.conn.RemoteAddr(), s.conn.LocalAddr())
|
||||
|
||||
addr, ota, err := s.getRequest()
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[ss] %s - %s : %s", s.conn.RemoteAddr(), s.conn.LocalAddr(), err)
|
||||
return
|
||||
}
|
||||
glog.V(LINFO).Infof("[ss] %s -> %s, ota: %v", s.conn.RemoteAddr(), addr, ota)
|
||||
|
||||
cc, err := s.Base.Chain.Dial(addr)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[ss] %s -> %s : %s", s.conn.RemoteAddr(), addr, err)
|
||||
return
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
glog.V(LINFO).Infof("[ss] %s <-> %s", s.conn.RemoteAddr(), addr)
|
||||
if ota {
|
||||
s.transportOTA(s.conn, cc)
|
||||
} else {
|
||||
s.Base.transport(&shadowConn{conn: s.conn}, cc)
|
||||
}
|
||||
glog.V(LINFO).Infof("[ss] %s >-< %s", s.conn.RemoteAddr(), addr)
|
||||
}
|
||||
|
||||
// This function is copied from shadowsocks library with some modification.
|
||||
func (s *ShadowServer) getRequest() (host string, ota bool, err error) {
|
||||
// buf size should at least have the same size with the largest possible
|
||||
// request size (when addrType is 3, domain name has at most 256 bytes)
|
||||
// 1(addrType) + 1(lenByte) + 256(max length address) + 2(port)
|
||||
buf := make([]byte, SmallBufferSize)
|
||||
|
||||
// read till we get possible domain length field
|
||||
s.conn.SetReadDeadline(time.Now().Add(ReadTimeout))
|
||||
if _, err = io.ReadFull(s.conn, buf[:idType+1]); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var reqStart, reqEnd int
|
||||
addrType := buf[idType]
|
||||
switch addrType & ss.AddrMask {
|
||||
case typeIPv4:
|
||||
reqStart, reqEnd = idIP0, idIP0+lenIPv4
|
||||
case typeIPv6:
|
||||
reqStart, reqEnd = idIP0, idIP0+lenIPv6
|
||||
case typeDm:
|
||||
if _, err = io.ReadFull(s.conn, buf[idType+1:idDmLen+1]); err != nil {
|
||||
return
|
||||
}
|
||||
reqStart, reqEnd = idDm0, int(idDm0+buf[idDmLen]+lenDmBase)
|
||||
default:
|
||||
err = fmt.Errorf("addr type %d not supported", addrType&ss.AddrMask)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = io.ReadFull(s.conn, buf[reqStart:reqEnd]); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Return string for typeIP is not most efficient, but browsers (Chrome,
|
||||
// Safari, Firefox) all seems using typeDm exclusively. So this is not a
|
||||
// big problem.
|
||||
switch addrType & ss.AddrMask {
|
||||
case typeIPv4:
|
||||
host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String()
|
||||
case typeIPv6:
|
||||
host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String()
|
||||
case typeDm:
|
||||
host = string(buf[idDm0 : idDm0+buf[idDmLen]])
|
||||
}
|
||||
// parse port
|
||||
port := binary.BigEndian.Uint16(buf[reqEnd-2 : reqEnd])
|
||||
host = net.JoinHostPort(host, strconv.Itoa(int(port)))
|
||||
// if specified one time auth enabled, we should verify this
|
||||
if s.OTA || addrType&ss.OneTimeAuthMask > 0 {
|
||||
ota = true
|
||||
if _, err = io.ReadFull(s.conn, buf[reqEnd:reqEnd+lenHmacSha1]); err != nil {
|
||||
return
|
||||
}
|
||||
iv := s.conn.GetIv()
|
||||
key := s.conn.GetKey()
|
||||
actualHmacSha1Buf := ss.HmacSha1(append(iv, key...), buf[:reqEnd])
|
||||
if !bytes.Equal(buf[reqEnd:reqEnd+lenHmacSha1], actualHmacSha1Buf) {
|
||||
err = fmt.Errorf("verify one time auth failed, iv=%v key=%v data=%v", iv, key, buf[:reqEnd])
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const (
|
||||
dataLenLen = 2
|
||||
hmacSha1Len = 10
|
||||
idxData0 = dataLenLen + hmacSha1Len
|
||||
)
|
||||
|
||||
// copyOta copies data from src to dst with ota verification.
|
||||
//
|
||||
// This function is copied from shadowsocks library with some modification.
|
||||
func (s *ShadowServer) copyOta(dst net.Conn, src *ss.Conn) (int64, error) {
|
||||
// sometimes it have to fill large block
|
||||
buf := make([]byte, LargeBufferSize)
|
||||
for {
|
||||
src.SetReadDeadline(time.Now().Add(ReadTimeout))
|
||||
if n, err := io.ReadFull(src, buf[:dataLenLen+hmacSha1Len]); err != nil {
|
||||
return int64(n), err
|
||||
}
|
||||
src.SetReadDeadline(time.Time{})
|
||||
|
||||
dataLen := binary.BigEndian.Uint16(buf[:dataLenLen])
|
||||
expectedHmacSha1 := buf[dataLenLen:idxData0]
|
||||
|
||||
var dataBuf []byte
|
||||
if len(buf) < int(idxData0+dataLen) {
|
||||
dataBuf = make([]byte, dataLen)
|
||||
} else {
|
||||
dataBuf = buf[idxData0 : idxData0+dataLen]
|
||||
}
|
||||
if n, err := io.ReadFull(src, dataBuf); err != nil {
|
||||
return int64(n), err
|
||||
}
|
||||
chunkIdBytes := make([]byte, 4)
|
||||
chunkId := src.GetAndIncrChunkId()
|
||||
binary.BigEndian.PutUint32(chunkIdBytes, chunkId)
|
||||
actualHmacSha1 := ss.HmacSha1(append(src.GetIv(), chunkIdBytes...), dataBuf)
|
||||
if !bytes.Equal(expectedHmacSha1, actualHmacSha1) {
|
||||
return 0, errors.New("ota error: mismatch")
|
||||
}
|
||||
|
||||
if n, err := dst.Write(dataBuf); err != nil {
|
||||
return int64(n), err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ShadowServer) transportOTA(sc *ss.Conn, cc net.Conn) (err error) {
|
||||
errc := make(chan error, 2)
|
||||
|
||||
go func() {
|
||||
_, err := io.Copy(&shadowConn{conn: sc}, cc)
|
||||
errc <- err
|
||||
}()
|
||||
|
||||
go func() {
|
||||
_, err := s.copyOta(cc, sc)
|
||||
errc <- err
|
||||
}()
|
||||
|
||||
select {
|
||||
case err = <-errc:
|
||||
//glog.V(LWARNING).Infoln("transport exit", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Due to in/out byte length is inconsistent of the shadowsocks.Conn.Write,
|
||||
// we wrap around it to make io.Copy happy
|
||||
type shadowConn struct {
|
||||
conn *ss.Conn
|
||||
}
|
||||
|
||||
func (c *shadowConn) Read(b []byte) (n int, err error) {
|
||||
return c.conn.Read(b)
|
||||
}
|
||||
|
||||
func (c *shadowConn) Write(b []byte) (n int, err error) {
|
||||
n = len(b) // force byte length consistent
|
||||
_, err = c.conn.Write(b)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *shadowConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *shadowConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *shadowConn) RemoteAddr() net.Addr {
|
||||
return c.conn.RemoteAddr()
|
||||
}
|
||||
|
||||
func (c *shadowConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *shadowConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *shadowConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
type ShadowUdpServer struct {
|
||||
Base *ProxyServer
|
||||
TTL int
|
||||
}
|
||||
|
||||
func NewShadowUdpServer(base *ProxyServer, ttl int) *ShadowUdpServer {
|
||||
return &ShadowUdpServer{Base: base, TTL: ttl}
|
||||
}
|
||||
|
||||
func (s *ShadowUdpServer) ListenAndServe() error {
|
||||
laddr, err := net.ResolveUDPAddr("udp", s.Base.Node.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lconn, err := net.ListenUDP("udp", laddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer lconn.Close()
|
||||
|
||||
conn := ss.NewSecurePacketConn(lconn, s.Base.cipher.Copy(), true) // force OTA on
|
||||
|
||||
rChan, wChan := make(chan *packet, 128), make(chan *packet, 128)
|
||||
// start send queue
|
||||
go func(ch chan<- *packet) {
|
||||
for {
|
||||
b := make([]byte, MediumBufferSize)
|
||||
n, addr, err := conn.ReadFrom(b[3:]) // add rsv and frag fields to make it the standard SOCKS5 UDP datagram
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[ssu] %s -> %s : %s", addr, laddr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
b[3] &= ss.AddrMask // remove OTA flag
|
||||
dgram, err := gosocks5.ReadUDPDatagram(bytes.NewReader(b[:n+3]))
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[ssu] %s -> %s : %s", addr, laddr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
select {
|
||||
case ch <- &packet{srcAddr: addr.String(), dstAddr: dgram.Header.Addr.String(), data: dgram.Data}:
|
||||
case <-time.After(time.Second * 3):
|
||||
glog.V(LWARNING).Infof("[ssu] %s -> %s : %s", addr, dgram.Header.Addr.String(), "send queue is full, discard")
|
||||
}
|
||||
}
|
||||
}(wChan)
|
||||
// start recv queue
|
||||
go func(ch <-chan *packet) {
|
||||
for pkt := range ch {
|
||||
srcAddr, err := net.ResolveUDPAddr("udp", pkt.srcAddr)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[ssu] %s <- %s : %s", pkt.dstAddr, pkt.srcAddr, err)
|
||||
continue
|
||||
}
|
||||
dstAddr, err := net.ResolveUDPAddr("udp", pkt.dstAddr)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[ssu] %s <- %s : %s", pkt.dstAddr, pkt.srcAddr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
dgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(0, 0, ToSocksAddr(srcAddr)), pkt.data)
|
||||
b := bytes.Buffer{}
|
||||
dgram.Write(&b)
|
||||
if b.Len() < 10 {
|
||||
glog.V(LWARNING).Infof("[ssu] %s <- %s : invalid udp datagram", pkt.dstAddr, pkt.srcAddr)
|
||||
continue
|
||||
}
|
||||
|
||||
if _, err := conn.WriteTo(b.Bytes()[3:], dstAddr); err != nil { // remove rsv and frag fields to make it standard shadowsocks UDP datagram
|
||||
glog.V(LWARNING).Infof("[ssu] %s <- %s : %s", pkt.dstAddr, pkt.srcAddr, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}(rChan)
|
||||
|
||||
// mapping client to node
|
||||
m := make(map[string]*cnode)
|
||||
|
||||
// start dispatcher
|
||||
for pkt := range wChan {
|
||||
// clear obsolete nodes
|
||||
for k, node := range m {
|
||||
if node != nil && node.err != nil {
|
||||
close(node.wChan)
|
||||
delete(m, k)
|
||||
glog.V(LINFO).Infof("[ssu] clear node %s", k)
|
||||
}
|
||||
}
|
||||
|
||||
node, ok := m[pkt.srcAddr]
|
||||
if !ok {
|
||||
node = &cnode{
|
||||
chain: s.Base.Chain,
|
||||
srcAddr: pkt.srcAddr,
|
||||
dstAddr: pkt.dstAddr,
|
||||
rChan: rChan,
|
||||
wChan: make(chan *packet, 32),
|
||||
ttl: time.Duration(s.TTL) * time.Second,
|
||||
}
|
||||
m[pkt.srcAddr] = node
|
||||
go node.run()
|
||||
glog.V(LINFO).Infof("[ssu] %s -> %s : new client (%d)", pkt.srcAddr, pkt.dstAddr, len(m))
|
||||
}
|
||||
|
||||
select {
|
||||
case node.wChan <- pkt:
|
||||
case <-time.After(time.Second * 3):
|
||||
glog.V(LWARNING).Infof("[ssu] %s -> %s : %s", pkt.srcAddr, pkt.dstAddr, "node send queue is full, discard")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
142
cmd/gost/vendor/github.com/ginuerzh/gost/ws.go
generated
vendored
142
cmd/gost/vendor/github.com/ginuerzh/gost/ws.go
generated
vendored
@ -1,142 +0,0 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"github.com/golang/glog"
|
||||
"gopkg.in/gorilla/websocket.v1"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"time"
|
||||
)
|
||||
|
||||
type WebsocketServer struct {
|
||||
Addr string
|
||||
Base *ProxyServer
|
||||
Handler http.Handler
|
||||
upgrader websocket.Upgrader
|
||||
}
|
||||
|
||||
func NewWebsocketServer(base *ProxyServer) *WebsocketServer {
|
||||
return &WebsocketServer{
|
||||
Addr: base.Node.Addr,
|
||||
Base: base,
|
||||
upgrader: websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
CheckOrigin: func(r *http.Request) bool { return true },
|
||||
EnableCompression: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Default websocket server handler
|
||||
func (s *WebsocketServer) HandleRequest(w http.ResponseWriter, r *http.Request) {
|
||||
glog.V(LINFO).Infof("[ws] %s - %s", r.RemoteAddr, s.Addr)
|
||||
if glog.V(LDEBUG) {
|
||||
dump, _ := httputil.DumpRequest(r, false)
|
||||
glog.V(LDEBUG).Infof("[ws] %s - %s\n%s", r.RemoteAddr, s.Addr, string(dump))
|
||||
}
|
||||
conn, err := s.upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
glog.V(LERROR).Infof("[ws] %s - %s : %s", r.RemoteAddr, s.Addr, err)
|
||||
return
|
||||
}
|
||||
s.Base.handleConn(WebsocketServerConn(conn))
|
||||
}
|
||||
|
||||
func (s *WebsocketServer) ListenAndServe() error {
|
||||
mux := http.NewServeMux()
|
||||
if s.Handler == nil {
|
||||
s.Handler = http.HandlerFunc(s.HandleRequest)
|
||||
}
|
||||
mux.Handle("/ws", s.Handler)
|
||||
return http.ListenAndServe(s.Addr, mux)
|
||||
}
|
||||
|
||||
func (s *WebsocketServer) ListenAndServeTLS(config *tls.Config) error {
|
||||
mux := http.NewServeMux()
|
||||
if s.Handler == nil {
|
||||
s.Handler = http.HandlerFunc(s.HandleRequest)
|
||||
}
|
||||
mux.Handle("/ws", s.Handler)
|
||||
server := &http.Server{
|
||||
Addr: s.Addr,
|
||||
Handler: mux,
|
||||
TLSConfig: config,
|
||||
}
|
||||
return server.ListenAndServeTLS("", "")
|
||||
}
|
||||
|
||||
type WebsocketConn struct {
|
||||
conn *websocket.Conn
|
||||
rb []byte
|
||||
}
|
||||
|
||||
func WebsocketClientConn(url string, conn net.Conn, config *tls.Config) (*WebsocketConn, error) {
|
||||
dialer := websocket.Dialer{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
TLSClientConfig: config,
|
||||
HandshakeTimeout: DialTimeout,
|
||||
EnableCompression: true,
|
||||
NetDial: func(net, addr string) (net.Conn, error) {
|
||||
return conn, nil
|
||||
},
|
||||
}
|
||||
|
||||
c, resp, err := dialer.Dial(url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return &WebsocketConn{conn: c}, nil
|
||||
}
|
||||
|
||||
func WebsocketServerConn(conn *websocket.Conn) *WebsocketConn {
|
||||
conn.EnableWriteCompression(true)
|
||||
return &WebsocketConn{
|
||||
conn: conn,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *WebsocketConn) Read(b []byte) (n int, err error) {
|
||||
if len(c.rb) == 0 {
|
||||
_, c.rb, err = c.conn.ReadMessage()
|
||||
}
|
||||
n = copy(b, c.rb)
|
||||
c.rb = c.rb[n:]
|
||||
return
|
||||
}
|
||||
|
||||
func (c *WebsocketConn) Write(b []byte) (n int, err error) {
|
||||
err = c.conn.WriteMessage(websocket.BinaryMessage, b)
|
||||
n = len(b)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *WebsocketConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *WebsocketConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *WebsocketConn) RemoteAddr() net.Addr {
|
||||
return c.conn.RemoteAddr()
|
||||
}
|
||||
|
||||
func (conn *WebsocketConn) SetDeadline(t time.Time) error {
|
||||
if err := conn.SetReadDeadline(t); err != nil {
|
||||
return err
|
||||
}
|
||||
return conn.SetWriteDeadline(t)
|
||||
}
|
||||
func (c *WebsocketConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *WebsocketConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
87
cmd/gost/vendor/github.com/klauspost/crc32/README.md
generated
vendored
87
cmd/gost/vendor/github.com/klauspost/crc32/README.md
generated
vendored
@ -1,87 +0,0 @@
|
||||
# crc32
|
||||
CRC32 hash with x64 optimizations
|
||||
|
||||
This package is a drop-in replacement for the standard library `hash/crc32` package, that features SSE 4.2 optimizations on x64 platforms, for a 10x speedup.
|
||||
|
||||
[](https://travis-ci.org/klauspost/crc32)
|
||||
|
||||
# usage
|
||||
|
||||
Install using `go get github.com/klauspost/crc32`. This library is based on Go 1.5 code and requires Go 1.3 or newer.
|
||||
|
||||
Replace `import "hash/crc32"` with `import "github.com/klauspost/crc32"` and you are good to go.
|
||||
|
||||
# changes
|
||||
* Oct 20, 2016: Changes have been merged to upstream Go. Package updated to match.
|
||||
* Dec 4, 2015: Uses the "slice-by-8" trick more extensively, which gives a 1.5 to 2.5x speedup if assembler is unavailable.
|
||||
|
||||
|
||||
# performance
|
||||
|
||||
For *Go 1.7* performance is equivalent to the standard library. So if you use this package for Go 1.7 you can switch back.
|
||||
|
||||
|
||||
For IEEE tables (the most common), there is approximately a factor 10 speedup with "CLMUL" (Carryless multiplication) instruction:
|
||||
```
|
||||
benchmark old ns/op new ns/op delta
|
||||
BenchmarkCrc32KB 99955 10258 -89.74%
|
||||
|
||||
benchmark old MB/s new MB/s speedup
|
||||
BenchmarkCrc32KB 327.83 3194.20 9.74x
|
||||
```
|
||||
|
||||
For other tables and "CLMUL" capable machines the performance is the same as the standard library.
|
||||
|
||||
Here are some detailed benchmarks, comparing to go 1.5 standard library with and without assembler enabled.
|
||||
|
||||
```
|
||||
Std: Standard Go 1.5 library
|
||||
Crc: Indicates IEEE type CRC.
|
||||
40B: Size of each slice encoded.
|
||||
NoAsm: Assembler was disabled (ie. not an AMD64 or SSE 4.2+ capable machine).
|
||||
Castagnoli: Castagnoli CRC type.
|
||||
|
||||
BenchmarkStdCrc40B-4 10000000 158 ns/op 252.88 MB/s
|
||||
BenchmarkCrc40BNoAsm-4 20000000 105 ns/op 377.38 MB/s (slice8)
|
||||
BenchmarkCrc40B-4 20000000 105 ns/op 378.77 MB/s (slice8)
|
||||
|
||||
BenchmarkStdCrc1KB-4 500000 3604 ns/op 284.10 MB/s
|
||||
BenchmarkCrc1KBNoAsm-4 1000000 1463 ns/op 699.79 MB/s (slice8)
|
||||
BenchmarkCrc1KB-4 3000000 396 ns/op 2583.69 MB/s (asm)
|
||||
|
||||
BenchmarkStdCrc8KB-4 200000 11417 ns/op 717.48 MB/s (slice8)
|
||||
BenchmarkCrc8KBNoAsm-4 200000 11317 ns/op 723.85 MB/s (slice8)
|
||||
BenchmarkCrc8KB-4 500000 2919 ns/op 2805.73 MB/s (asm)
|
||||
|
||||
BenchmarkStdCrc32KB-4 30000 45749 ns/op 716.24 MB/s (slice8)
|
||||
BenchmarkCrc32KBNoAsm-4 30000 45109 ns/op 726.42 MB/s (slice8)
|
||||
BenchmarkCrc32KB-4 100000 11497 ns/op 2850.09 MB/s (asm)
|
||||
|
||||
BenchmarkStdNoAsmCastagnol40B-4 10000000 161 ns/op 246.94 MB/s
|
||||
BenchmarkStdCastagnoli40B-4 50000000 28.4 ns/op 1410.69 MB/s (asm)
|
||||
BenchmarkCastagnoli40BNoAsm-4 20000000 100 ns/op 398.01 MB/s (slice8)
|
||||
BenchmarkCastagnoli40B-4 50000000 28.2 ns/op 1419.54 MB/s (asm)
|
||||
|
||||
BenchmarkStdNoAsmCastagnoli1KB-4 500000 3622 ns/op 282.67 MB/s
|
||||
BenchmarkStdCastagnoli1KB-4 10000000 144 ns/op 7099.78 MB/s (asm)
|
||||
BenchmarkCastagnoli1KBNoAsm-4 1000000 1475 ns/op 694.14 MB/s (slice8)
|
||||
BenchmarkCastagnoli1KB-4 10000000 146 ns/op 6993.35 MB/s (asm)
|
||||
|
||||
BenchmarkStdNoAsmCastagnoli8KB-4 50000 28781 ns/op 284.63 MB/s
|
||||
BenchmarkStdCastagnoli8KB-4 1000000 1029 ns/op 7957.89 MB/s (asm)
|
||||
BenchmarkCastagnoli8KBNoAsm-4 200000 11410 ns/op 717.94 MB/s (slice8)
|
||||
BenchmarkCastagnoli8KB-4 1000000 1000 ns/op 8188.71 MB/s (asm)
|
||||
|
||||
BenchmarkStdNoAsmCastagnoli32KB-4 10000 115426 ns/op 283.89 MB/s
|
||||
BenchmarkStdCastagnoli32KB-4 300000 4065 ns/op 8059.13 MB/s (asm)
|
||||
BenchmarkCastagnoli32KBNoAsm-4 30000 45171 ns/op 725.41 MB/s (slice8)
|
||||
BenchmarkCastagnoli32KB-4 500000 4077 ns/op 8035.89 MB/s (asm)
|
||||
```
|
||||
|
||||
The IEEE assembler optimizations has been submitted and will be part of the Go 1.6 standard library.
|
||||
|
||||
However, the improved use of slice-by-8 has not, but will probably be submitted for Go 1.7.
|
||||
|
||||
# license
|
||||
|
||||
Standard Go license. Changes are Copyright (c) 2015 Klaus Post under same conditions.
|
207
cmd/gost/vendor/github.com/klauspost/crc32/crc32.go
generated
vendored
207
cmd/gost/vendor/github.com/klauspost/crc32/crc32.go
generated
vendored
@ -1,207 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package crc32 implements the 32-bit cyclic redundancy check, or CRC-32,
|
||||
// checksum. See http://en.wikipedia.org/wiki/Cyclic_redundancy_check for
|
||||
// information.
|
||||
//
|
||||
// Polynomials are represented in LSB-first form also known as reversed representation.
|
||||
//
|
||||
// See http://en.wikipedia.org/wiki/Mathematics_of_cyclic_redundancy_checks#Reversed_representations_and_reciprocal_polynomials
|
||||
// for information.
|
||||
package crc32
|
||||
|
||||
import (
|
||||
"hash"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// The size of a CRC-32 checksum in bytes.
|
||||
const Size = 4
|
||||
|
||||
// Predefined polynomials.
|
||||
const (
|
||||
// IEEE is by far and away the most common CRC-32 polynomial.
|
||||
// Used by ethernet (IEEE 802.3), v.42, fddi, gzip, zip, png, ...
|
||||
IEEE = 0xedb88320
|
||||
|
||||
// Castagnoli's polynomial, used in iSCSI.
|
||||
// Has better error detection characteristics than IEEE.
|
||||
// http://dx.doi.org/10.1109/26.231911
|
||||
Castagnoli = 0x82f63b78
|
||||
|
||||
// Koopman's polynomial.
|
||||
// Also has better error detection characteristics than IEEE.
|
||||
// http://dx.doi.org/10.1109/DSN.2002.1028931
|
||||
Koopman = 0xeb31d82e
|
||||
)
|
||||
|
||||
// Table is a 256-word table representing the polynomial for efficient processing.
|
||||
type Table [256]uint32
|
||||
|
||||
// This file makes use of functions implemented in architecture-specific files.
|
||||
// The interface that they implement is as follows:
|
||||
//
|
||||
// // archAvailableIEEE reports whether an architecture-specific CRC32-IEEE
|
||||
// // algorithm is available.
|
||||
// archAvailableIEEE() bool
|
||||
//
|
||||
// // archInitIEEE initializes the architecture-specific CRC3-IEEE algorithm.
|
||||
// // It can only be called if archAvailableIEEE() returns true.
|
||||
// archInitIEEE()
|
||||
//
|
||||
// // archUpdateIEEE updates the given CRC32-IEEE. It can only be called if
|
||||
// // archInitIEEE() was previously called.
|
||||
// archUpdateIEEE(crc uint32, p []byte) uint32
|
||||
//
|
||||
// // archAvailableCastagnoli reports whether an architecture-specific
|
||||
// // CRC32-C algorithm is available.
|
||||
// archAvailableCastagnoli() bool
|
||||
//
|
||||
// // archInitCastagnoli initializes the architecture-specific CRC32-C
|
||||
// // algorithm. It can only be called if archAvailableCastagnoli() returns
|
||||
// // true.
|
||||
// archInitCastagnoli()
|
||||
//
|
||||
// // archUpdateCastagnoli updates the given CRC32-C. It can only be called
|
||||
// // if archInitCastagnoli() was previously called.
|
||||
// archUpdateCastagnoli(crc uint32, p []byte) uint32
|
||||
|
||||
// castagnoliTable points to a lazily initialized Table for the Castagnoli
|
||||
// polynomial. MakeTable will always return this value when asked to make a
|
||||
// Castagnoli table so we can compare against it to find when the caller is
|
||||
// using this polynomial.
|
||||
var castagnoliTable *Table
|
||||
var castagnoliTable8 *slicing8Table
|
||||
var castagnoliArchImpl bool
|
||||
var updateCastagnoli func(crc uint32, p []byte) uint32
|
||||
var castagnoliOnce sync.Once
|
||||
|
||||
func castagnoliInit() {
|
||||
castagnoliTable = simpleMakeTable(Castagnoli)
|
||||
castagnoliArchImpl = archAvailableCastagnoli()
|
||||
|
||||
if castagnoliArchImpl {
|
||||
archInitCastagnoli()
|
||||
updateCastagnoli = archUpdateCastagnoli
|
||||
} else {
|
||||
// Initialize the slicing-by-8 table.
|
||||
castagnoliTable8 = slicingMakeTable(Castagnoli)
|
||||
updateCastagnoli = func(crc uint32, p []byte) uint32 {
|
||||
return slicingUpdate(crc, castagnoliTable8, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IEEETable is the table for the IEEE polynomial.
|
||||
var IEEETable = simpleMakeTable(IEEE)
|
||||
|
||||
// ieeeTable8 is the slicing8Table for IEEE
|
||||
var ieeeTable8 *slicing8Table
|
||||
var ieeeArchImpl bool
|
||||
var updateIEEE func(crc uint32, p []byte) uint32
|
||||
var ieeeOnce sync.Once
|
||||
|
||||
func ieeeInit() {
|
||||
ieeeArchImpl = archAvailableIEEE()
|
||||
|
||||
if ieeeArchImpl {
|
||||
archInitIEEE()
|
||||
updateIEEE = archUpdateIEEE
|
||||
} else {
|
||||
// Initialize the slicing-by-8 table.
|
||||
ieeeTable8 = slicingMakeTable(IEEE)
|
||||
updateIEEE = func(crc uint32, p []byte) uint32 {
|
||||
return slicingUpdate(crc, ieeeTable8, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MakeTable returns a Table constructed from the specified polynomial.
|
||||
// The contents of this Table must not be modified.
|
||||
func MakeTable(poly uint32) *Table {
|
||||
switch poly {
|
||||
case IEEE:
|
||||
ieeeOnce.Do(ieeeInit)
|
||||
return IEEETable
|
||||
case Castagnoli:
|
||||
castagnoliOnce.Do(castagnoliInit)
|
||||
return castagnoliTable
|
||||
}
|
||||
return simpleMakeTable(poly)
|
||||
}
|
||||
|
||||
// digest represents the partial evaluation of a checksum.
|
||||
type digest struct {
|
||||
crc uint32
|
||||
tab *Table
|
||||
}
|
||||
|
||||
// New creates a new hash.Hash32 computing the CRC-32 checksum
|
||||
// using the polynomial represented by the Table.
|
||||
// Its Sum method will lay the value out in big-endian byte order.
|
||||
func New(tab *Table) hash.Hash32 {
|
||||
if tab == IEEETable {
|
||||
ieeeOnce.Do(ieeeInit)
|
||||
}
|
||||
return &digest{0, tab}
|
||||
}
|
||||
|
||||
// NewIEEE creates a new hash.Hash32 computing the CRC-32 checksum
|
||||
// using the IEEE polynomial.
|
||||
// Its Sum method will lay the value out in big-endian byte order.
|
||||
func NewIEEE() hash.Hash32 { return New(IEEETable) }
|
||||
|
||||
func (d *digest) Size() int { return Size }
|
||||
|
||||
func (d *digest) BlockSize() int { return 1 }
|
||||
|
||||
func (d *digest) Reset() { d.crc = 0 }
|
||||
|
||||
// Update returns the result of adding the bytes in p to the crc.
|
||||
func Update(crc uint32, tab *Table, p []byte) uint32 {
|
||||
switch tab {
|
||||
case castagnoliTable:
|
||||
return updateCastagnoli(crc, p)
|
||||
case IEEETable:
|
||||
// Unfortunately, because IEEETable is exported, IEEE may be used without a
|
||||
// call to MakeTable. We have to make sure it gets initialized in that case.
|
||||
ieeeOnce.Do(ieeeInit)
|
||||
return updateIEEE(crc, p)
|
||||
default:
|
||||
return simpleUpdate(crc, tab, p)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *digest) Write(p []byte) (n int, err error) {
|
||||
switch d.tab {
|
||||
case castagnoliTable:
|
||||
d.crc = updateCastagnoli(d.crc, p)
|
||||
case IEEETable:
|
||||
// We only create digest objects through New() which takes care of
|
||||
// initialization in this case.
|
||||
d.crc = updateIEEE(d.crc, p)
|
||||
default:
|
||||
d.crc = simpleUpdate(d.crc, d.tab, p)
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (d *digest) Sum32() uint32 { return d.crc }
|
||||
|
||||
func (d *digest) Sum(in []byte) []byte {
|
||||
s := d.Sum32()
|
||||
return append(in, byte(s>>24), byte(s>>16), byte(s>>8), byte(s))
|
||||
}
|
||||
|
||||
// Checksum returns the CRC-32 checksum of data
|
||||
// using the polynomial represented by the Table.
|
||||
func Checksum(data []byte, tab *Table) uint32 { return Update(0, tab, data) }
|
||||
|
||||
// ChecksumIEEE returns the CRC-32 checksum of data
|
||||
// using the IEEE polynomial.
|
||||
func ChecksumIEEE(data []byte) uint32 {
|
||||
ieeeOnce.Do(ieeeInit)
|
||||
return updateIEEE(0, data)
|
||||
}
|
230
cmd/gost/vendor/github.com/klauspost/crc32/crc32_amd64.go
generated
vendored
230
cmd/gost/vendor/github.com/klauspost/crc32/crc32_amd64.go
generated
vendored
@ -1,230 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine,!gccgo
|
||||
|
||||
// AMD64-specific hardware-assisted CRC32 algorithms. See crc32.go for a
|
||||
// description of the interface that each architecture-specific file
|
||||
// implements.
|
||||
|
||||
package crc32
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// This file contains the code to call the SSE 4.2 version of the Castagnoli
|
||||
// and IEEE CRC.
|
||||
|
||||
// haveSSE41/haveSSE42/haveCLMUL are defined in crc_amd64.s and use
|
||||
// CPUID to test for SSE 4.1, 4.2 and CLMUL support.
|
||||
func haveSSE41() bool
|
||||
func haveSSE42() bool
|
||||
func haveCLMUL() bool
|
||||
|
||||
// castagnoliSSE42 is defined in crc32_amd64.s and uses the SSE4.2 CRC32
|
||||
// instruction.
|
||||
//go:noescape
|
||||
func castagnoliSSE42(crc uint32, p []byte) uint32
|
||||
|
||||
// castagnoliSSE42Triple is defined in crc32_amd64.s and uses the SSE4.2 CRC32
|
||||
// instruction.
|
||||
//go:noescape
|
||||
func castagnoliSSE42Triple(
|
||||
crcA, crcB, crcC uint32,
|
||||
a, b, c []byte,
|
||||
rounds uint32,
|
||||
) (retA uint32, retB uint32, retC uint32)
|
||||
|
||||
// ieeeCLMUL is defined in crc_amd64.s and uses the PCLMULQDQ
|
||||
// instruction as well as SSE 4.1.
|
||||
//go:noescape
|
||||
func ieeeCLMUL(crc uint32, p []byte) uint32
|
||||
|
||||
var sse42 = haveSSE42()
|
||||
var useFastIEEE = haveCLMUL() && haveSSE41()
|
||||
|
||||
const castagnoliK1 = 168
|
||||
const castagnoliK2 = 1344
|
||||
|
||||
type sse42Table [4]Table
|
||||
|
||||
var castagnoliSSE42TableK1 *sse42Table
|
||||
var castagnoliSSE42TableK2 *sse42Table
|
||||
|
||||
func archAvailableCastagnoli() bool {
|
||||
return sse42
|
||||
}
|
||||
|
||||
func archInitCastagnoli() {
|
||||
if !sse42 {
|
||||
panic("arch-specific Castagnoli not available")
|
||||
}
|
||||
castagnoliSSE42TableK1 = new(sse42Table)
|
||||
castagnoliSSE42TableK2 = new(sse42Table)
|
||||
// See description in updateCastagnoli.
|
||||
// t[0][i] = CRC(i000, O)
|
||||
// t[1][i] = CRC(0i00, O)
|
||||
// t[2][i] = CRC(00i0, O)
|
||||
// t[3][i] = CRC(000i, O)
|
||||
// where O is a sequence of K zeros.
|
||||
var tmp [castagnoliK2]byte
|
||||
for b := 0; b < 4; b++ {
|
||||
for i := 0; i < 256; i++ {
|
||||
val := uint32(i) << uint32(b*8)
|
||||
castagnoliSSE42TableK1[b][i] = castagnoliSSE42(val, tmp[:castagnoliK1])
|
||||
castagnoliSSE42TableK2[b][i] = castagnoliSSE42(val, tmp[:])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// castagnoliShift computes the CRC32-C of K1 or K2 zeroes (depending on the
|
||||
// table given) with the given initial crc value. This corresponds to
|
||||
// CRC(crc, O) in the description in updateCastagnoli.
|
||||
func castagnoliShift(table *sse42Table, crc uint32) uint32 {
|
||||
return table[3][crc>>24] ^
|
||||
table[2][(crc>>16)&0xFF] ^
|
||||
table[1][(crc>>8)&0xFF] ^
|
||||
table[0][crc&0xFF]
|
||||
}
|
||||
|
||||
func archUpdateCastagnoli(crc uint32, p []byte) uint32 {
|
||||
if !sse42 {
|
||||
panic("not available")
|
||||
}
|
||||
|
||||
// This method is inspired from the algorithm in Intel's white paper:
|
||||
// "Fast CRC Computation for iSCSI Polynomial Using CRC32 Instruction"
|
||||
// The same strategy of splitting the buffer in three is used but the
|
||||
// combining calculation is different; the complete derivation is explained
|
||||
// below.
|
||||
//
|
||||
// -- The basic idea --
|
||||
//
|
||||
// The CRC32 instruction (available in SSE4.2) can process 8 bytes at a
|
||||
// time. In recent Intel architectures the instruction takes 3 cycles;
|
||||
// however the processor can pipeline up to three instructions if they
|
||||
// don't depend on each other.
|
||||
//
|
||||
// Roughly this means that we can process three buffers in about the same
|
||||
// time we can process one buffer.
|
||||
//
|
||||
// The idea is then to split the buffer in three, CRC the three pieces
|
||||
// separately and then combine the results.
|
||||
//
|
||||
// Combining the results requires precomputed tables, so we must choose a
|
||||
// fixed buffer length to optimize. The longer the length, the faster; but
|
||||
// only buffers longer than this length will use the optimization. We choose
|
||||
// two cutoffs and compute tables for both:
|
||||
// - one around 512: 168*3=504
|
||||
// - one around 4KB: 1344*3=4032
|
||||
//
|
||||
// -- The nitty gritty --
|
||||
//
|
||||
// Let CRC(I, X) be the non-inverted CRC32-C of the sequence X (with
|
||||
// initial non-inverted CRC I). This function has the following properties:
|
||||
// (a) CRC(I, AB) = CRC(CRC(I, A), B)
|
||||
// (b) CRC(I, A xor B) = CRC(I, A) xor CRC(0, B)
|
||||
//
|
||||
// Say we want to compute CRC(I, ABC) where A, B, C are three sequences of
|
||||
// K bytes each, where K is a fixed constant. Let O be the sequence of K zero
|
||||
// bytes.
|
||||
//
|
||||
// CRC(I, ABC) = CRC(I, ABO xor C)
|
||||
// = CRC(I, ABO) xor CRC(0, C)
|
||||
// = CRC(CRC(I, AB), O) xor CRC(0, C)
|
||||
// = CRC(CRC(I, AO xor B), O) xor CRC(0, C)
|
||||
// = CRC(CRC(I, AO) xor CRC(0, B), O) xor CRC(0, C)
|
||||
// = CRC(CRC(CRC(I, A), O) xor CRC(0, B), O) xor CRC(0, C)
|
||||
//
|
||||
// The castagnoliSSE42Triple function can compute CRC(I, A), CRC(0, B),
|
||||
// and CRC(0, C) efficiently. We just need to find a way to quickly compute
|
||||
// CRC(uvwx, O) given a 4-byte initial value uvwx. We can precompute these
|
||||
// values; since we can't have a 32-bit table, we break it up into four
|
||||
// 8-bit tables:
|
||||
//
|
||||
// CRC(uvwx, O) = CRC(u000, O) xor
|
||||
// CRC(0v00, O) xor
|
||||
// CRC(00w0, O) xor
|
||||
// CRC(000x, O)
|
||||
//
|
||||
// We can compute tables corresponding to the four terms for all 8-bit
|
||||
// values.
|
||||
|
||||
crc = ^crc
|
||||
|
||||
// If a buffer is long enough to use the optimization, process the first few
|
||||
// bytes to align the buffer to an 8 byte boundary (if necessary).
|
||||
if len(p) >= castagnoliK1*3 {
|
||||
delta := int(uintptr(unsafe.Pointer(&p[0])) & 7)
|
||||
if delta != 0 {
|
||||
delta = 8 - delta
|
||||
crc = castagnoliSSE42(crc, p[:delta])
|
||||
p = p[delta:]
|
||||
}
|
||||
}
|
||||
|
||||
// Process 3*K2 at a time.
|
||||
for len(p) >= castagnoliK2*3 {
|
||||
// Compute CRC(I, A), CRC(0, B), and CRC(0, C).
|
||||
crcA, crcB, crcC := castagnoliSSE42Triple(
|
||||
crc, 0, 0,
|
||||
p, p[castagnoliK2:], p[castagnoliK2*2:],
|
||||
castagnoliK2/24)
|
||||
|
||||
// CRC(I, AB) = CRC(CRC(I, A), O) xor CRC(0, B)
|
||||
crcAB := castagnoliShift(castagnoliSSE42TableK2, crcA) ^ crcB
|
||||
// CRC(I, ABC) = CRC(CRC(I, AB), O) xor CRC(0, C)
|
||||
crc = castagnoliShift(castagnoliSSE42TableK2, crcAB) ^ crcC
|
||||
p = p[castagnoliK2*3:]
|
||||
}
|
||||
|
||||
// Process 3*K1 at a time.
|
||||
for len(p) >= castagnoliK1*3 {
|
||||
// Compute CRC(I, A), CRC(0, B), and CRC(0, C).
|
||||
crcA, crcB, crcC := castagnoliSSE42Triple(
|
||||
crc, 0, 0,
|
||||
p, p[castagnoliK1:], p[castagnoliK1*2:],
|
||||
castagnoliK1/24)
|
||||
|
||||
// CRC(I, AB) = CRC(CRC(I, A), O) xor CRC(0, B)
|
||||
crcAB := castagnoliShift(castagnoliSSE42TableK1, crcA) ^ crcB
|
||||
// CRC(I, ABC) = CRC(CRC(I, AB), O) xor CRC(0, C)
|
||||
crc = castagnoliShift(castagnoliSSE42TableK1, crcAB) ^ crcC
|
||||
p = p[castagnoliK1*3:]
|
||||
}
|
||||
|
||||
// Use the simple implementation for what's left.
|
||||
crc = castagnoliSSE42(crc, p)
|
||||
return ^crc
|
||||
}
|
||||
|
||||
func archAvailableIEEE() bool {
|
||||
return useFastIEEE
|
||||
}
|
||||
|
||||
var archIeeeTable8 *slicing8Table
|
||||
|
||||
func archInitIEEE() {
|
||||
if !useFastIEEE {
|
||||
panic("not available")
|
||||
}
|
||||
// We still use slicing-by-8 for small buffers.
|
||||
archIeeeTable8 = slicingMakeTable(IEEE)
|
||||
}
|
||||
|
||||
func archUpdateIEEE(crc uint32, p []byte) uint32 {
|
||||
if !useFastIEEE {
|
||||
panic("not available")
|
||||
}
|
||||
|
||||
if len(p) >= 64 {
|
||||
left := len(p) & 15
|
||||
do := len(p) - left
|
||||
crc = ^ieeeCLMUL(^crc, p[:do])
|
||||
p = p[do:]
|
||||
}
|
||||
if len(p) == 0 {
|
||||
return crc
|
||||
}
|
||||
return slicingUpdate(crc, archIeeeTable8, p)
|
||||
}
|
319
cmd/gost/vendor/github.com/klauspost/crc32/crc32_amd64.s
generated
vendored
319
cmd/gost/vendor/github.com/klauspost/crc32/crc32_amd64.s
generated
vendored
@ -1,319 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build gc
|
||||
|
||||
#define NOSPLIT 4
|
||||
#define RODATA 8
|
||||
|
||||
// castagnoliSSE42 updates the (non-inverted) crc with the given buffer.
|
||||
//
|
||||
// func castagnoliSSE42(crc uint32, p []byte) uint32
|
||||
TEXT ·castagnoliSSE42(SB), NOSPLIT, $0
|
||||
MOVL crc+0(FP), AX // CRC value
|
||||
MOVQ p+8(FP), SI // data pointer
|
||||
MOVQ p_len+16(FP), CX // len(p)
|
||||
|
||||
// If there are fewer than 8 bytes to process, skip alignment.
|
||||
CMPQ CX, $8
|
||||
JL less_than_8
|
||||
|
||||
MOVQ SI, BX
|
||||
ANDQ $7, BX
|
||||
JZ aligned
|
||||
|
||||
// Process the first few bytes to 8-byte align the input.
|
||||
|
||||
// BX = 8 - BX. We need to process this many bytes to align.
|
||||
SUBQ $1, BX
|
||||
XORQ $7, BX
|
||||
|
||||
BTQ $0, BX
|
||||
JNC align_2
|
||||
|
||||
CRC32B (SI), AX
|
||||
DECQ CX
|
||||
INCQ SI
|
||||
|
||||
align_2:
|
||||
BTQ $1, BX
|
||||
JNC align_4
|
||||
|
||||
// CRC32W (SI), AX
|
||||
BYTE $0x66; BYTE $0xf2; BYTE $0x0f; BYTE $0x38; BYTE $0xf1; BYTE $0x06
|
||||
|
||||
SUBQ $2, CX
|
||||
ADDQ $2, SI
|
||||
|
||||
align_4:
|
||||
BTQ $2, BX
|
||||
JNC aligned
|
||||
|
||||
// CRC32L (SI), AX
|
||||
BYTE $0xf2; BYTE $0x0f; BYTE $0x38; BYTE $0xf1; BYTE $0x06
|
||||
|
||||
SUBQ $4, CX
|
||||
ADDQ $4, SI
|
||||
|
||||
aligned:
|
||||
// The input is now 8-byte aligned and we can process 8-byte chunks.
|
||||
CMPQ CX, $8
|
||||
JL less_than_8
|
||||
|
||||
CRC32Q (SI), AX
|
||||
ADDQ $8, SI
|
||||
SUBQ $8, CX
|
||||
JMP aligned
|
||||
|
||||
less_than_8:
|
||||
// We may have some bytes left over; process 4 bytes, then 2, then 1.
|
||||
BTQ $2, CX
|
||||
JNC less_than_4
|
||||
|
||||
// CRC32L (SI), AX
|
||||
BYTE $0xf2; BYTE $0x0f; BYTE $0x38; BYTE $0xf1; BYTE $0x06
|
||||
ADDQ $4, SI
|
||||
|
||||
less_than_4:
|
||||
BTQ $1, CX
|
||||
JNC less_than_2
|
||||
|
||||
// CRC32W (SI), AX
|
||||
BYTE $0x66; BYTE $0xf2; BYTE $0x0f; BYTE $0x38; BYTE $0xf1; BYTE $0x06
|
||||
ADDQ $2, SI
|
||||
|
||||
less_than_2:
|
||||
BTQ $0, CX
|
||||
JNC done
|
||||
|
||||
CRC32B (SI), AX
|
||||
|
||||
done:
|
||||
MOVL AX, ret+32(FP)
|
||||
RET
|
||||
|
||||
// castagnoliSSE42Triple updates three (non-inverted) crcs with (24*rounds)
|
||||
// bytes from each buffer.
|
||||
//
|
||||
// func castagnoliSSE42Triple(
|
||||
// crc1, crc2, crc3 uint32,
|
||||
// a, b, c []byte,
|
||||
// rounds uint32,
|
||||
// ) (retA uint32, retB uint32, retC uint32)
|
||||
TEXT ·castagnoliSSE42Triple(SB), NOSPLIT, $0
|
||||
MOVL crcA+0(FP), AX
|
||||
MOVL crcB+4(FP), CX
|
||||
MOVL crcC+8(FP), DX
|
||||
|
||||
MOVQ a+16(FP), R8 // data pointer
|
||||
MOVQ b+40(FP), R9 // data pointer
|
||||
MOVQ c+64(FP), R10 // data pointer
|
||||
|
||||
MOVL rounds+88(FP), R11
|
||||
|
||||
loop:
|
||||
CRC32Q (R8), AX
|
||||
CRC32Q (R9), CX
|
||||
CRC32Q (R10), DX
|
||||
|
||||
CRC32Q 8(R8), AX
|
||||
CRC32Q 8(R9), CX
|
||||
CRC32Q 8(R10), DX
|
||||
|
||||
CRC32Q 16(R8), AX
|
||||
CRC32Q 16(R9), CX
|
||||
CRC32Q 16(R10), DX
|
||||
|
||||
ADDQ $24, R8
|
||||
ADDQ $24, R9
|
||||
ADDQ $24, R10
|
||||
|
||||
DECQ R11
|
||||
JNZ loop
|
||||
|
||||
MOVL AX, retA+96(FP)
|
||||
MOVL CX, retB+100(FP)
|
||||
MOVL DX, retC+104(FP)
|
||||
RET
|
||||
|
||||
// func haveSSE42() bool
|
||||
TEXT ·haveSSE42(SB), NOSPLIT, $0
|
||||
XORQ AX, AX
|
||||
INCL AX
|
||||
CPUID
|
||||
SHRQ $20, CX
|
||||
ANDQ $1, CX
|
||||
MOVB CX, ret+0(FP)
|
||||
RET
|
||||
|
||||
// func haveCLMUL() bool
|
||||
TEXT ·haveCLMUL(SB), NOSPLIT, $0
|
||||
XORQ AX, AX
|
||||
INCL AX
|
||||
CPUID
|
||||
SHRQ $1, CX
|
||||
ANDQ $1, CX
|
||||
MOVB CX, ret+0(FP)
|
||||
RET
|
||||
|
||||
// func haveSSE41() bool
|
||||
TEXT ·haveSSE41(SB), NOSPLIT, $0
|
||||
XORQ AX, AX
|
||||
INCL AX
|
||||
CPUID
|
||||
SHRQ $19, CX
|
||||
ANDQ $1, CX
|
||||
MOVB CX, ret+0(FP)
|
||||
RET
|
||||
|
||||
// CRC32 polynomial data
|
||||
//
|
||||
// These constants are lifted from the
|
||||
// Linux kernel, since they avoid the costly
|
||||
// PSHUFB 16 byte reversal proposed in the
|
||||
// original Intel paper.
|
||||
DATA r2r1kp<>+0(SB)/8, $0x154442bd4
|
||||
DATA r2r1kp<>+8(SB)/8, $0x1c6e41596
|
||||
DATA r4r3kp<>+0(SB)/8, $0x1751997d0
|
||||
DATA r4r3kp<>+8(SB)/8, $0x0ccaa009e
|
||||
DATA rupolykp<>+0(SB)/8, $0x1db710641
|
||||
DATA rupolykp<>+8(SB)/8, $0x1f7011641
|
||||
DATA r5kp<>+0(SB)/8, $0x163cd6124
|
||||
|
||||
GLOBL r2r1kp<>(SB), RODATA, $16
|
||||
GLOBL r4r3kp<>(SB), RODATA, $16
|
||||
GLOBL rupolykp<>(SB), RODATA, $16
|
||||
GLOBL r5kp<>(SB), RODATA, $8
|
||||
|
||||
// Based on http://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf
|
||||
// len(p) must be at least 64, and must be a multiple of 16.
|
||||
|
||||
// func ieeeCLMUL(crc uint32, p []byte) uint32
|
||||
TEXT ·ieeeCLMUL(SB), NOSPLIT, $0
|
||||
MOVL crc+0(FP), X0 // Initial CRC value
|
||||
MOVQ p+8(FP), SI // data pointer
|
||||
MOVQ p_len+16(FP), CX // len(p)
|
||||
|
||||
MOVOU (SI), X1
|
||||
MOVOU 16(SI), X2
|
||||
MOVOU 32(SI), X3
|
||||
MOVOU 48(SI), X4
|
||||
PXOR X0, X1
|
||||
ADDQ $64, SI // buf+=64
|
||||
SUBQ $64, CX // len-=64
|
||||
CMPQ CX, $64 // Less than 64 bytes left
|
||||
JB remain64
|
||||
|
||||
MOVOA r2r1kp<>+0(SB), X0
|
||||
|
||||
loopback64:
|
||||
MOVOA X1, X5
|
||||
MOVOA X2, X6
|
||||
MOVOA X3, X7
|
||||
MOVOA X4, X8
|
||||
|
||||
PCLMULQDQ $0, X0, X1
|
||||
PCLMULQDQ $0, X0, X2
|
||||
PCLMULQDQ $0, X0, X3
|
||||
PCLMULQDQ $0, X0, X4
|
||||
|
||||
// Load next early
|
||||
MOVOU (SI), X11
|
||||
MOVOU 16(SI), X12
|
||||
MOVOU 32(SI), X13
|
||||
MOVOU 48(SI), X14
|
||||
|
||||
PCLMULQDQ $0x11, X0, X5
|
||||
PCLMULQDQ $0x11, X0, X6
|
||||
PCLMULQDQ $0x11, X0, X7
|
||||
PCLMULQDQ $0x11, X0, X8
|
||||
|
||||
PXOR X5, X1
|
||||
PXOR X6, X2
|
||||
PXOR X7, X3
|
||||
PXOR X8, X4
|
||||
|
||||
PXOR X11, X1
|
||||
PXOR X12, X2
|
||||
PXOR X13, X3
|
||||
PXOR X14, X4
|
||||
|
||||
ADDQ $0x40, DI
|
||||
ADDQ $64, SI // buf+=64
|
||||
SUBQ $64, CX // len-=64
|
||||
CMPQ CX, $64 // Less than 64 bytes left?
|
||||
JGE loopback64
|
||||
|
||||
// Fold result into a single register (X1)
|
||||
remain64:
|
||||
MOVOA r4r3kp<>+0(SB), X0
|
||||
|
||||
MOVOA X1, X5
|
||||
PCLMULQDQ $0, X0, X1
|
||||
PCLMULQDQ $0x11, X0, X5
|
||||
PXOR X5, X1
|
||||
PXOR X2, X1
|
||||
|
||||
MOVOA X1, X5
|
||||
PCLMULQDQ $0, X0, X1
|
||||
PCLMULQDQ $0x11, X0, X5
|
||||
PXOR X5, X1
|
||||
PXOR X3, X1
|
||||
|
||||
MOVOA X1, X5
|
||||
PCLMULQDQ $0, X0, X1
|
||||
PCLMULQDQ $0x11, X0, X5
|
||||
PXOR X5, X1
|
||||
PXOR X4, X1
|
||||
|
||||
// If there is less than 16 bytes left we are done
|
||||
CMPQ CX, $16
|
||||
JB finish
|
||||
|
||||
// Encode 16 bytes
|
||||
remain16:
|
||||
MOVOU (SI), X10
|
||||
MOVOA X1, X5
|
||||
PCLMULQDQ $0, X0, X1
|
||||
PCLMULQDQ $0x11, X0, X5
|
||||
PXOR X5, X1
|
||||
PXOR X10, X1
|
||||
SUBQ $16, CX
|
||||
ADDQ $16, SI
|
||||
CMPQ CX, $16
|
||||
JGE remain16
|
||||
|
||||
finish:
|
||||
// Fold final result into 32 bits and return it
|
||||
PCMPEQB X3, X3
|
||||
PCLMULQDQ $1, X1, X0
|
||||
PSRLDQ $8, X1
|
||||
PXOR X0, X1
|
||||
|
||||
MOVOA X1, X2
|
||||
MOVQ r5kp<>+0(SB), X0
|
||||
|
||||
// Creates 32 bit mask. Note that we don't care about upper half.
|
||||
PSRLQ $32, X3
|
||||
|
||||
PSRLDQ $4, X2
|
||||
PAND X3, X1
|
||||
PCLMULQDQ $0, X0, X1
|
||||
PXOR X2, X1
|
||||
|
||||
MOVOA rupolykp<>+0(SB), X0
|
||||
|
||||
MOVOA X1, X2
|
||||
PAND X3, X1
|
||||
PCLMULQDQ $0x10, X0, X1
|
||||
PAND X3, X1
|
||||
PCLMULQDQ $0, X0, X1
|
||||
PXOR X2, X1
|
||||
|
||||
// PEXTRD $1, X1, AX (SSE 4.1)
|
||||
BYTE $0x66; BYTE $0x0f; BYTE $0x3a
|
||||
BYTE $0x16; BYTE $0xc8; BYTE $0x01
|
||||
MOVL AX, ret+32(FP)
|
||||
|
||||
RET
|
43
cmd/gost/vendor/github.com/klauspost/crc32/crc32_amd64p32.go
generated
vendored
43
cmd/gost/vendor/github.com/klauspost/crc32/crc32_amd64p32.go
generated
vendored
@ -1,43 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine,!gccgo
|
||||
|
||||
package crc32
|
||||
|
||||
// This file contains the code to call the SSE 4.2 version of the Castagnoli
|
||||
// CRC.
|
||||
|
||||
// haveSSE42 is defined in crc32_amd64p32.s and uses CPUID to test for SSE 4.2
|
||||
// support.
|
||||
func haveSSE42() bool
|
||||
|
||||
// castagnoliSSE42 is defined in crc32_amd64p32.s and uses the SSE4.2 CRC32
|
||||
// instruction.
|
||||
//go:noescape
|
||||
func castagnoliSSE42(crc uint32, p []byte) uint32
|
||||
|
||||
var sse42 = haveSSE42()
|
||||
|
||||
func archAvailableCastagnoli() bool {
|
||||
return sse42
|
||||
}
|
||||
|
||||
func archInitCastagnoli() {
|
||||
if !sse42 {
|
||||
panic("not available")
|
||||
}
|
||||
// No initialization necessary.
|
||||
}
|
||||
|
||||
func archUpdateCastagnoli(crc uint32, p []byte) uint32 {
|
||||
if !sse42 {
|
||||
panic("not available")
|
||||
}
|
||||
return castagnoliSSE42(crc, p)
|
||||
}
|
||||
|
||||
func archAvailableIEEE() bool { return false }
|
||||
func archInitIEEE() { panic("not available") }
|
||||
func archUpdateIEEE(crc uint32, p []byte) uint32 { panic("not available") }
|
67
cmd/gost/vendor/github.com/klauspost/crc32/crc32_amd64p32.s
generated
vendored
67
cmd/gost/vendor/github.com/klauspost/crc32/crc32_amd64p32.s
generated
vendored
@ -1,67 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build gc
|
||||
|
||||
#define NOSPLIT 4
|
||||
#define RODATA 8
|
||||
|
||||
// func castagnoliSSE42(crc uint32, p []byte) uint32
|
||||
TEXT ·castagnoliSSE42(SB), NOSPLIT, $0
|
||||
MOVL crc+0(FP), AX // CRC value
|
||||
MOVL p+4(FP), SI // data pointer
|
||||
MOVL p_len+8(FP), CX // len(p)
|
||||
|
||||
NOTL AX
|
||||
|
||||
// If there's less than 8 bytes to process, we do it byte-by-byte.
|
||||
CMPQ CX, $8
|
||||
JL cleanup
|
||||
|
||||
// Process individual bytes until the input is 8-byte aligned.
|
||||
startup:
|
||||
MOVQ SI, BX
|
||||
ANDQ $7, BX
|
||||
JZ aligned
|
||||
|
||||
CRC32B (SI), AX
|
||||
DECQ CX
|
||||
INCQ SI
|
||||
JMP startup
|
||||
|
||||
aligned:
|
||||
// The input is now 8-byte aligned and we can process 8-byte chunks.
|
||||
CMPQ CX, $8
|
||||
JL cleanup
|
||||
|
||||
CRC32Q (SI), AX
|
||||
ADDQ $8, SI
|
||||
SUBQ $8, CX
|
||||
JMP aligned
|
||||
|
||||
cleanup:
|
||||
// We may have some bytes left over that we process one at a time.
|
||||
CMPQ CX, $0
|
||||
JE done
|
||||
|
||||
CRC32B (SI), AX
|
||||
INCQ SI
|
||||
DECQ CX
|
||||
JMP cleanup
|
||||
|
||||
done:
|
||||
NOTL AX
|
||||
MOVL AX, ret+16(FP)
|
||||
RET
|
||||
|
||||
// func haveSSE42() bool
|
||||
TEXT ·haveSSE42(SB), NOSPLIT, $0
|
||||
XORQ AX, AX
|
||||
INCL AX
|
||||
CPUID
|
||||
SHRQ $20, CX
|
||||
ANDQ $1, CX
|
||||
MOVB CX, ret+0(FP)
|
||||
RET
|
||||
|
89
cmd/gost/vendor/github.com/klauspost/crc32/crc32_generic.go
generated
vendored
89
cmd/gost/vendor/github.com/klauspost/crc32/crc32_generic.go
generated
vendored
@ -1,89 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file contains CRC32 algorithms that are not specific to any architecture
|
||||
// and don't use hardware acceleration.
|
||||
//
|
||||
// The simple (and slow) CRC32 implementation only uses a 256*4 bytes table.
|
||||
//
|
||||
// The slicing-by-8 algorithm is a faster implementation that uses a bigger
|
||||
// table (8*256*4 bytes).
|
||||
|
||||
package crc32
|
||||
|
||||
// simpleMakeTable allocates and constructs a Table for the specified
|
||||
// polynomial. The table is suitable for use with the simple algorithm
|
||||
// (simpleUpdate).
|
||||
func simpleMakeTable(poly uint32) *Table {
|
||||
t := new(Table)
|
||||
simplePopulateTable(poly, t)
|
||||
return t
|
||||
}
|
||||
|
||||
// simplePopulateTable constructs a Table for the specified polynomial, suitable
|
||||
// for use with simpleUpdate.
|
||||
func simplePopulateTable(poly uint32, t *Table) {
|
||||
for i := 0; i < 256; i++ {
|
||||
crc := uint32(i)
|
||||
for j := 0; j < 8; j++ {
|
||||
if crc&1 == 1 {
|
||||
crc = (crc >> 1) ^ poly
|
||||
} else {
|
||||
crc >>= 1
|
||||
}
|
||||
}
|
||||
t[i] = crc
|
||||
}
|
||||
}
|
||||
|
||||
// simpleUpdate uses the simple algorithm to update the CRC, given a table that
|
||||
// was previously computed using simpleMakeTable.
|
||||
func simpleUpdate(crc uint32, tab *Table, p []byte) uint32 {
|
||||
crc = ^crc
|
||||
for _, v := range p {
|
||||
crc = tab[byte(crc)^v] ^ (crc >> 8)
|
||||
}
|
||||
return ^crc
|
||||
}
|
||||
|
||||
// Use slicing-by-8 when payload >= this value.
|
||||
const slicing8Cutoff = 16
|
||||
|
||||
// slicing8Table is array of 8 Tables, used by the slicing-by-8 algorithm.
|
||||
type slicing8Table [8]Table
|
||||
|
||||
// slicingMakeTable constructs a slicing8Table for the specified polynomial. The
|
||||
// table is suitable for use with the slicing-by-8 algorithm (slicingUpdate).
|
||||
func slicingMakeTable(poly uint32) *slicing8Table {
|
||||
t := new(slicing8Table)
|
||||
simplePopulateTable(poly, &t[0])
|
||||
for i := 0; i < 256; i++ {
|
||||
crc := t[0][i]
|
||||
for j := 1; j < 8; j++ {
|
||||
crc = t[0][crc&0xFF] ^ (crc >> 8)
|
||||
t[j][i] = crc
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// slicingUpdate uses the slicing-by-8 algorithm to update the CRC, given a
|
||||
// table that was previously computed using slicingMakeTable.
|
||||
func slicingUpdate(crc uint32, tab *slicing8Table, p []byte) uint32 {
|
||||
if len(p) >= slicing8Cutoff {
|
||||
crc = ^crc
|
||||
for len(p) > 8 {
|
||||
crc ^= uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24
|
||||
crc = tab[0][p[7]] ^ tab[1][p[6]] ^ tab[2][p[5]] ^ tab[3][p[4]] ^
|
||||
tab[4][crc>>24] ^ tab[5][(crc>>16)&0xFF] ^
|
||||
tab[6][(crc>>8)&0xFF] ^ tab[7][crc&0xFF]
|
||||
p = p[8:]
|
||||
}
|
||||
crc = ^crc
|
||||
}
|
||||
if len(p) == 0 {
|
||||
return crc
|
||||
}
|
||||
return simpleUpdate(crc, &tab[0], p)
|
||||
}
|
15
cmd/gost/vendor/github.com/klauspost/crc32/crc32_otherarch.go
generated
vendored
15
cmd/gost/vendor/github.com/klauspost/crc32/crc32_otherarch.go
generated
vendored
@ -1,15 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !amd64,!amd64p32,!s390x
|
||||
|
||||
package crc32
|
||||
|
||||
func archAvailableIEEE() bool { return false }
|
||||
func archInitIEEE() { panic("not available") }
|
||||
func archUpdateIEEE(crc uint32, p []byte) uint32 { panic("not available") }
|
||||
|
||||
func archAvailableCastagnoli() bool { return false }
|
||||
func archInitCastagnoli() { panic("not available") }
|
||||
func archUpdateCastagnoli(crc uint32, p []byte) uint32 { panic("not available") }
|
91
cmd/gost/vendor/github.com/klauspost/crc32/crc32_s390x.go
generated
vendored
91
cmd/gost/vendor/github.com/klauspost/crc32/crc32_s390x.go
generated
vendored
@ -1,91 +0,0 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build s390x
|
||||
|
||||
package crc32
|
||||
|
||||
const (
|
||||
vxMinLen = 64
|
||||
vxAlignMask = 15 // align to 16 bytes
|
||||
)
|
||||
|
||||
// hasVectorFacility reports whether the machine has the z/Architecture
|
||||
// vector facility installed and enabled.
|
||||
func hasVectorFacility() bool
|
||||
|
||||
var hasVX = hasVectorFacility()
|
||||
|
||||
// vectorizedCastagnoli implements CRC32 using vector instructions.
|
||||
// It is defined in crc32_s390x.s.
|
||||
//go:noescape
|
||||
func vectorizedCastagnoli(crc uint32, p []byte) uint32
|
||||
|
||||
// vectorizedIEEE implements CRC32 using vector instructions.
|
||||
// It is defined in crc32_s390x.s.
|
||||
//go:noescape
|
||||
func vectorizedIEEE(crc uint32, p []byte) uint32
|
||||
|
||||
func archAvailableCastagnoli() bool {
|
||||
return hasVX
|
||||
}
|
||||
|
||||
var archCastagnoliTable8 *slicing8Table
|
||||
|
||||
func archInitCastagnoli() {
|
||||
if !hasVX {
|
||||
panic("not available")
|
||||
}
|
||||
// We still use slicing-by-8 for small buffers.
|
||||
archCastagnoliTable8 = slicingMakeTable(Castagnoli)
|
||||
}
|
||||
|
||||
// archUpdateCastagnoli calculates the checksum of p using
|
||||
// vectorizedCastagnoli.
|
||||
func archUpdateCastagnoli(crc uint32, p []byte) uint32 {
|
||||
if !hasVX {
|
||||
panic("not available")
|
||||
}
|
||||
// Use vectorized function if data length is above threshold.
|
||||
if len(p) >= vxMinLen {
|
||||
aligned := len(p) & ^vxAlignMask
|
||||
crc = vectorizedCastagnoli(crc, p[:aligned])
|
||||
p = p[aligned:]
|
||||
}
|
||||
if len(p) == 0 {
|
||||
return crc
|
||||
}
|
||||
return slicingUpdate(crc, archCastagnoliTable8, p)
|
||||
}
|
||||
|
||||
func archAvailableIEEE() bool {
|
||||
return hasVX
|
||||
}
|
||||
|
||||
var archIeeeTable8 *slicing8Table
|
||||
|
||||
func archInitIEEE() {
|
||||
if !hasVX {
|
||||
panic("not available")
|
||||
}
|
||||
// We still use slicing-by-8 for small buffers.
|
||||
archIeeeTable8 = slicingMakeTable(IEEE)
|
||||
}
|
||||
|
||||
// archUpdateIEEE calculates the checksum of p using vectorizedIEEE.
|
||||
func archUpdateIEEE(crc uint32, p []byte) uint32 {
|
||||
if !hasVX {
|
||||
panic("not available")
|
||||
}
|
||||
// Use vectorized function if data length is above threshold.
|
||||
if len(p) >= vxMinLen {
|
||||
aligned := len(p) & ^vxAlignMask
|
||||
crc = vectorizedIEEE(crc, p[:aligned])
|
||||
p = p[aligned:]
|
||||
}
|
||||
if len(p) == 0 {
|
||||
return crc
|
||||
}
|
||||
return slicingUpdate(crc, archIeeeTable8, p)
|
||||
}
|
249
cmd/gost/vendor/github.com/klauspost/crc32/crc32_s390x.s
generated
vendored
249
cmd/gost/vendor/github.com/klauspost/crc32/crc32_s390x.s
generated
vendored
@ -1,249 +0,0 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build s390x
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// Vector register range containing CRC-32 constants
|
||||
|
||||
#define CONST_PERM_LE2BE V9
|
||||
#define CONST_R2R1 V10
|
||||
#define CONST_R4R3 V11
|
||||
#define CONST_R5 V12
|
||||
#define CONST_RU_POLY V13
|
||||
#define CONST_CRC_POLY V14
|
||||
|
||||
// The CRC-32 constant block contains reduction constants to fold and
|
||||
// process particular chunks of the input data stream in parallel.
|
||||
//
|
||||
// Note that the constant definitions below are extended in order to compute
|
||||
// intermediate results with a single VECTOR GALOIS FIELD MULTIPLY instruction.
|
||||
// The rightmost doubleword can be 0 to prevent contribution to the result or
|
||||
// can be multiplied by 1 to perform an XOR without the need for a separate
|
||||
// VECTOR EXCLUSIVE OR instruction.
|
||||
//
|
||||
// The polynomials used are bit-reflected:
|
||||
//
|
||||
// IEEE: P'(x) = 0x0edb88320
|
||||
// Castagnoli: P'(x) = 0x082f63b78
|
||||
|
||||
// IEEE polynomial constants
|
||||
DATA ·crcleconskp+0(SB)/8, $0x0F0E0D0C0B0A0908 // LE-to-BE mask
|
||||
DATA ·crcleconskp+8(SB)/8, $0x0706050403020100
|
||||
DATA ·crcleconskp+16(SB)/8, $0x00000001c6e41596 // R2
|
||||
DATA ·crcleconskp+24(SB)/8, $0x0000000154442bd4 // R1
|
||||
DATA ·crcleconskp+32(SB)/8, $0x00000000ccaa009e // R4
|
||||
DATA ·crcleconskp+40(SB)/8, $0x00000001751997d0 // R3
|
||||
DATA ·crcleconskp+48(SB)/8, $0x0000000000000000
|
||||
DATA ·crcleconskp+56(SB)/8, $0x0000000163cd6124 // R5
|
||||
DATA ·crcleconskp+64(SB)/8, $0x0000000000000000
|
||||
DATA ·crcleconskp+72(SB)/8, $0x00000001F7011641 // u'
|
||||
DATA ·crcleconskp+80(SB)/8, $0x0000000000000000
|
||||
DATA ·crcleconskp+88(SB)/8, $0x00000001DB710641 // P'(x) << 1
|
||||
|
||||
GLOBL ·crcleconskp(SB), RODATA, $144
|
||||
|
||||
// Castagonli Polynomial constants
|
||||
DATA ·crccleconskp+0(SB)/8, $0x0F0E0D0C0B0A0908 // LE-to-BE mask
|
||||
DATA ·crccleconskp+8(SB)/8, $0x0706050403020100
|
||||
DATA ·crccleconskp+16(SB)/8, $0x000000009e4addf8 // R2
|
||||
DATA ·crccleconskp+24(SB)/8, $0x00000000740eef02 // R1
|
||||
DATA ·crccleconskp+32(SB)/8, $0x000000014cd00bd6 // R4
|
||||
DATA ·crccleconskp+40(SB)/8, $0x00000000f20c0dfe // R3
|
||||
DATA ·crccleconskp+48(SB)/8, $0x0000000000000000
|
||||
DATA ·crccleconskp+56(SB)/8, $0x00000000dd45aab8 // R5
|
||||
DATA ·crccleconskp+64(SB)/8, $0x0000000000000000
|
||||
DATA ·crccleconskp+72(SB)/8, $0x00000000dea713f1 // u'
|
||||
DATA ·crccleconskp+80(SB)/8, $0x0000000000000000
|
||||
DATA ·crccleconskp+88(SB)/8, $0x0000000105ec76f0 // P'(x) << 1
|
||||
|
||||
GLOBL ·crccleconskp(SB), RODATA, $144
|
||||
|
||||
// func hasVectorFacility() bool
|
||||
TEXT ·hasVectorFacility(SB), NOSPLIT, $24-1
|
||||
MOVD $x-24(SP), R1
|
||||
XC $24, 0(R1), 0(R1) // clear the storage
|
||||
MOVD $2, R0 // R0 is the number of double words stored -1
|
||||
WORD $0xB2B01000 // STFLE 0(R1)
|
||||
XOR R0, R0 // reset the value of R0
|
||||
MOVBZ z-8(SP), R1
|
||||
AND $0x40, R1
|
||||
BEQ novector
|
||||
|
||||
vectorinstalled:
|
||||
// check if the vector instruction has been enabled
|
||||
VLEIB $0, $0xF, V16
|
||||
VLGVB $0, V16, R1
|
||||
CMPBNE R1, $0xF, novector
|
||||
MOVB $1, ret+0(FP) // have vx
|
||||
RET
|
||||
|
||||
novector:
|
||||
MOVB $0, ret+0(FP) // no vx
|
||||
RET
|
||||
|
||||
// The CRC-32 function(s) use these calling conventions:
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// R2: Initial CRC value, typically ~0; and final CRC (return) value.
|
||||
// R3: Input buffer pointer, performance might be improved if the
|
||||
// buffer is on a doubleword boundary.
|
||||
// R4: Length of the buffer, must be 64 bytes or greater.
|
||||
//
|
||||
// Register usage:
|
||||
//
|
||||
// R5: CRC-32 constant pool base pointer.
|
||||
// V0: Initial CRC value and intermediate constants and results.
|
||||
// V1..V4: Data for CRC computation.
|
||||
// V5..V8: Next data chunks that are fetched from the input buffer.
|
||||
//
|
||||
// V9..V14: CRC-32 constants.
|
||||
|
||||
// func vectorizedIEEE(crc uint32, p []byte) uint32
|
||||
TEXT ·vectorizedIEEE(SB), NOSPLIT, $0
|
||||
MOVWZ crc+0(FP), R2 // R2 stores the CRC value
|
||||
MOVD p+8(FP), R3 // data pointer
|
||||
MOVD p_len+16(FP), R4 // len(p)
|
||||
|
||||
MOVD $·crcleconskp(SB), R5
|
||||
BR vectorizedBody<>(SB)
|
||||
|
||||
// func vectorizedCastagnoli(crc uint32, p []byte) uint32
|
||||
TEXT ·vectorizedCastagnoli(SB), NOSPLIT, $0
|
||||
MOVWZ crc+0(FP), R2 // R2 stores the CRC value
|
||||
MOVD p+8(FP), R3 // data pointer
|
||||
MOVD p_len+16(FP), R4 // len(p)
|
||||
|
||||
// R5: crc-32 constant pool base pointer, constant is used to reduce crc
|
||||
MOVD $·crccleconskp(SB), R5
|
||||
BR vectorizedBody<>(SB)
|
||||
|
||||
TEXT vectorizedBody<>(SB), NOSPLIT, $0
|
||||
XOR $0xffffffff, R2 // NOTW R2
|
||||
VLM 0(R5), CONST_PERM_LE2BE, CONST_CRC_POLY
|
||||
|
||||
// Load the initial CRC value into the rightmost word of V0
|
||||
VZERO V0
|
||||
VLVGF $3, R2, V0
|
||||
|
||||
// Crash if the input size is less than 64-bytes.
|
||||
CMP R4, $64
|
||||
BLT crash
|
||||
|
||||
// Load a 64-byte data chunk and XOR with CRC
|
||||
VLM 0(R3), V1, V4 // 64-bytes into V1..V4
|
||||
|
||||
// Reflect the data if the CRC operation is in the bit-reflected domain
|
||||
VPERM V1, V1, CONST_PERM_LE2BE, V1
|
||||
VPERM V2, V2, CONST_PERM_LE2BE, V2
|
||||
VPERM V3, V3, CONST_PERM_LE2BE, V3
|
||||
VPERM V4, V4, CONST_PERM_LE2BE, V4
|
||||
|
||||
VX V0, V1, V1 // V1 ^= CRC
|
||||
ADD $64, R3 // BUF = BUF + 64
|
||||
ADD $(-64), R4
|
||||
|
||||
// Check remaining buffer size and jump to proper folding method
|
||||
CMP R4, $64
|
||||
BLT less_than_64bytes
|
||||
|
||||
fold_64bytes_loop:
|
||||
// Load the next 64-byte data chunk into V5 to V8
|
||||
VLM 0(R3), V5, V8
|
||||
VPERM V5, V5, CONST_PERM_LE2BE, V5
|
||||
VPERM V6, V6, CONST_PERM_LE2BE, V6
|
||||
VPERM V7, V7, CONST_PERM_LE2BE, V7
|
||||
VPERM V8, V8, CONST_PERM_LE2BE, V8
|
||||
|
||||
// Perform a GF(2) multiplication of the doublewords in V1 with
|
||||
// the reduction constants in V0. The intermediate result is
|
||||
// then folded (accumulated) with the next data chunk in V5 and
|
||||
// stored in V1. Repeat this step for the register contents
|
||||
// in V2, V3, and V4 respectively.
|
||||
|
||||
VGFMAG CONST_R2R1, V1, V5, V1
|
||||
VGFMAG CONST_R2R1, V2, V6, V2
|
||||
VGFMAG CONST_R2R1, V3, V7, V3
|
||||
VGFMAG CONST_R2R1, V4, V8, V4
|
||||
|
||||
// Adjust buffer pointer and length for next loop
|
||||
ADD $64, R3 // BUF = BUF + 64
|
||||
ADD $(-64), R4 // LEN = LEN - 64
|
||||
|
||||
CMP R4, $64
|
||||
BGE fold_64bytes_loop
|
||||
|
||||
less_than_64bytes:
|
||||
// Fold V1 to V4 into a single 128-bit value in V1
|
||||
VGFMAG CONST_R4R3, V1, V2, V1
|
||||
VGFMAG CONST_R4R3, V1, V3, V1
|
||||
VGFMAG CONST_R4R3, V1, V4, V1
|
||||
|
||||
// Check whether to continue with 64-bit folding
|
||||
CMP R4, $16
|
||||
BLT final_fold
|
||||
|
||||
fold_16bytes_loop:
|
||||
VL 0(R3), V2 // Load next data chunk
|
||||
VPERM V2, V2, CONST_PERM_LE2BE, V2
|
||||
|
||||
VGFMAG CONST_R4R3, V1, V2, V1 // Fold next data chunk
|
||||
|
||||
// Adjust buffer pointer and size for folding next data chunk
|
||||
ADD $16, R3
|
||||
ADD $-16, R4
|
||||
|
||||
// Process remaining data chunks
|
||||
CMP R4, $16
|
||||
BGE fold_16bytes_loop
|
||||
|
||||
final_fold:
|
||||
VLEIB $7, $0x40, V9
|
||||
VSRLB V9, CONST_R4R3, V0
|
||||
VLEIG $0, $1, V0
|
||||
|
||||
VGFMG V0, V1, V1
|
||||
|
||||
VLEIB $7, $0x20, V9 // Shift by words
|
||||
VSRLB V9, V1, V2 // Store remaining bits in V2
|
||||
VUPLLF V1, V1 // Split rightmost doubleword
|
||||
VGFMAG CONST_R5, V1, V2, V1 // V1 = (V1 * R5) XOR V2
|
||||
|
||||
// The input values to the Barret reduction are the degree-63 polynomial
|
||||
// in V1 (R(x)), degree-32 generator polynomial, and the reduction
|
||||
// constant u. The Barret reduction result is the CRC value of R(x) mod
|
||||
// P(x).
|
||||
//
|
||||
// The Barret reduction algorithm is defined as:
|
||||
//
|
||||
// 1. T1(x) = floor( R(x) / x^32 ) GF2MUL u
|
||||
// 2. T2(x) = floor( T1(x) / x^32 ) GF2MUL P(x)
|
||||
// 3. C(x) = R(x) XOR T2(x) mod x^32
|
||||
//
|
||||
// Note: To compensate the division by x^32, use the vector unpack
|
||||
// instruction to move the leftmost word into the leftmost doubleword
|
||||
// of the vector register. The rightmost doubleword is multiplied
|
||||
// with zero to not contribute to the intermedate results.
|
||||
|
||||
// T1(x) = floor( R(x) / x^32 ) GF2MUL u
|
||||
VUPLLF V1, V2
|
||||
VGFMG CONST_RU_POLY, V2, V2
|
||||
|
||||
// Compute the GF(2) product of the CRC polynomial in VO with T1(x) in
|
||||
// V2 and XOR the intermediate result, T2(x), with the value in V1.
|
||||
// The final result is in the rightmost word of V2.
|
||||
|
||||
VUPLLF V2, V2
|
||||
VGFMAG CONST_CRC_POLY, V2, V1, V2
|
||||
|
||||
done:
|
||||
VLGVF $2, V2, R2
|
||||
XOR $0xffffffff, R2 // NOTW R2
|
||||
MOVWZ R2, ret + 32(FP)
|
||||
RET
|
||||
|
||||
crash:
|
||||
MOVD $0, (R0) // input size is less than 64-bytes
|
49
cmd/gost/vendor/github.com/lucas-clemente/quic-go/ackhandler/packet.go
generated
vendored
49
cmd/gost/vendor/github.com/lucas-clemente/quic-go/ackhandler/packet.go
generated
vendored
@ -1,49 +0,0 @@
|
||||
package ackhandler
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/frames"
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
)
|
||||
|
||||
// A Packet is a packet
|
||||
// +gen linkedlist
|
||||
type Packet struct {
|
||||
PacketNumber protocol.PacketNumber
|
||||
Frames []frames.Frame
|
||||
Length protocol.ByteCount
|
||||
|
||||
MissingReports uint8
|
||||
|
||||
SendTime time.Time
|
||||
}
|
||||
|
||||
// GetStreamFramesForRetransmission gets all the streamframes for retransmission
|
||||
func (p *Packet) GetStreamFramesForRetransmission() []*frames.StreamFrame {
|
||||
var streamFrames []*frames.StreamFrame
|
||||
for _, frame := range p.Frames {
|
||||
if streamFrame, isStreamFrame := frame.(*frames.StreamFrame); isStreamFrame {
|
||||
streamFrames = append(streamFrames, streamFrame)
|
||||
}
|
||||
}
|
||||
return streamFrames
|
||||
}
|
||||
|
||||
// GetControlFramesForRetransmission gets all the control frames for retransmission
|
||||
func (p *Packet) GetControlFramesForRetransmission() []frames.Frame {
|
||||
var controlFrames []frames.Frame
|
||||
for _, frame := range p.Frames {
|
||||
// omit ACKs
|
||||
if _, isStreamFrame := frame.(*frames.StreamFrame); isStreamFrame {
|
||||
continue
|
||||
}
|
||||
|
||||
_, isAck := frame.(*frames.AckFrame)
|
||||
_, isStopWaiting := frame.(*frames.StopWaitingFrame)
|
||||
if !isAck && !isStopWaiting {
|
||||
controlFrames = append(controlFrames, frame)
|
||||
}
|
||||
}
|
||||
return controlFrames
|
||||
}
|
143
cmd/gost/vendor/github.com/lucas-clemente/quic-go/ackhandler/received_packet_handler.go
generated
vendored
143
cmd/gost/vendor/github.com/lucas-clemente/quic-go/ackhandler/received_packet_handler.go
generated
vendored
@ -1,143 +0,0 @@
|
||||
package ackhandler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/frames"
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
"github.com/lucas-clemente/quic-go/qerr"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrDuplicatePacket occurres when a duplicate packet is received
|
||||
ErrDuplicatePacket = errors.New("ReceivedPacketHandler: Duplicate Packet")
|
||||
// ErrMapAccess occurs when a NACK contains invalid NACK ranges
|
||||
ErrMapAccess = qerr.Error(qerr.InvalidAckData, "Packet does not exist in PacketHistory")
|
||||
// ErrPacketSmallerThanLastStopWaiting occurs when a packet arrives with a packet number smaller than the largest LeastUnacked of a StopWaitingFrame. If this error occurs, the packet should be ignored
|
||||
ErrPacketSmallerThanLastStopWaiting = errors.New("ReceivedPacketHandler: Packet number smaller than highest StopWaiting")
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidPacketNumber = errors.New("ReceivedPacketHandler: Invalid packet number")
|
||||
errTooManyOutstandingReceivedPackets = qerr.Error(qerr.TooManyOutstandingReceivedPackets, "")
|
||||
)
|
||||
|
||||
type receivedPacketHandler struct {
|
||||
largestInOrderObserved protocol.PacketNumber
|
||||
largestObserved protocol.PacketNumber
|
||||
ignorePacketsBelow protocol.PacketNumber
|
||||
currentAckFrame *frames.AckFrame
|
||||
stateChanged bool // has an ACK for this state already been sent? Will be set to false every time a new packet arrives, and to false every time an ACK is sent
|
||||
|
||||
packetHistory *receivedPacketHistory
|
||||
|
||||
receivedTimes map[protocol.PacketNumber]time.Time
|
||||
lowestInReceivedTimes protocol.PacketNumber
|
||||
}
|
||||
|
||||
// NewReceivedPacketHandler creates a new receivedPacketHandler
|
||||
func NewReceivedPacketHandler() ReceivedPacketHandler {
|
||||
return &receivedPacketHandler{
|
||||
receivedTimes: make(map[protocol.PacketNumber]time.Time),
|
||||
packetHistory: newReceivedPacketHistory(),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *receivedPacketHandler) ReceivedPacket(packetNumber protocol.PacketNumber) error {
|
||||
if packetNumber == 0 {
|
||||
return errInvalidPacketNumber
|
||||
}
|
||||
|
||||
// if the packet number is smaller than the largest LeastUnacked value of a StopWaiting we received, we cannot detect if this packet has a duplicate number
|
||||
// the packet has to be ignored anyway
|
||||
if packetNumber <= h.ignorePacketsBelow {
|
||||
return ErrPacketSmallerThanLastStopWaiting
|
||||
}
|
||||
|
||||
_, ok := h.receivedTimes[packetNumber]
|
||||
if packetNumber <= h.largestInOrderObserved || ok {
|
||||
return ErrDuplicatePacket
|
||||
}
|
||||
|
||||
h.packetHistory.ReceivedPacket(packetNumber)
|
||||
|
||||
h.stateChanged = true
|
||||
h.currentAckFrame = nil
|
||||
|
||||
if packetNumber > h.largestObserved {
|
||||
h.largestObserved = packetNumber
|
||||
}
|
||||
|
||||
if packetNumber == h.largestInOrderObserved+1 {
|
||||
h.largestInOrderObserved = packetNumber
|
||||
}
|
||||
|
||||
h.receivedTimes[packetNumber] = time.Now()
|
||||
|
||||
if protocol.PacketNumber(len(h.receivedTimes)) > protocol.MaxTrackedReceivedPackets {
|
||||
return errTooManyOutstandingReceivedPackets
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *receivedPacketHandler) ReceivedStopWaiting(f *frames.StopWaitingFrame) error {
|
||||
// ignore if StopWaiting is unneeded, because we already received a StopWaiting with a higher LeastUnacked
|
||||
if h.ignorePacketsBelow >= f.LeastUnacked {
|
||||
return nil
|
||||
}
|
||||
|
||||
h.ignorePacketsBelow = f.LeastUnacked - 1
|
||||
h.garbageCollectReceivedTimes()
|
||||
|
||||
// the LeastUnacked is the smallest packet number of any packet for which the sender is still awaiting an ack. So the largestInOrderObserved is one less than that
|
||||
if f.LeastUnacked > h.largestInOrderObserved {
|
||||
h.largestInOrderObserved = f.LeastUnacked - 1
|
||||
}
|
||||
|
||||
h.packetHistory.DeleteBelow(f.LeastUnacked)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *receivedPacketHandler) GetAckFrame(dequeue bool) (*frames.AckFrame, error) {
|
||||
if !h.stateChanged {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if dequeue {
|
||||
h.stateChanged = false
|
||||
}
|
||||
|
||||
if h.currentAckFrame != nil {
|
||||
return h.currentAckFrame, nil
|
||||
}
|
||||
|
||||
packetReceivedTime, ok := h.receivedTimes[h.largestObserved]
|
||||
if !ok {
|
||||
return nil, ErrMapAccess
|
||||
}
|
||||
|
||||
ackRanges := h.packetHistory.GetAckRanges()
|
||||
h.currentAckFrame = &frames.AckFrame{
|
||||
LargestAcked: h.largestObserved,
|
||||
LowestAcked: ackRanges[len(ackRanges)-1].FirstPacketNumber,
|
||||
PacketReceivedTime: packetReceivedTime,
|
||||
}
|
||||
|
||||
if len(ackRanges) > 1 {
|
||||
h.currentAckFrame.AckRanges = ackRanges
|
||||
}
|
||||
|
||||
return h.currentAckFrame, nil
|
||||
}
|
||||
|
||||
func (h *receivedPacketHandler) garbageCollectReceivedTimes() {
|
||||
for i := h.lowestInReceivedTimes; i <= h.ignorePacketsBelow; i++ {
|
||||
delete(h.receivedTimes, i)
|
||||
}
|
||||
if h.ignorePacketsBelow > h.lowestInReceivedTimes {
|
||||
h.lowestInReceivedTimes = h.ignorePacketsBelow + 1
|
||||
}
|
||||
}
|
350
cmd/gost/vendor/github.com/lucas-clemente/quic-go/ackhandler/sent_packet_handler.go
generated
vendored
350
cmd/gost/vendor/github.com/lucas-clemente/quic-go/ackhandler/sent_packet_handler.go
generated
vendored
@ -1,350 +0,0 @@
|
||||
package ackhandler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/congestion"
|
||||
"github.com/lucas-clemente/quic-go/frames"
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
"github.com/lucas-clemente/quic-go/qerr"
|
||||
"github.com/lucas-clemente/quic-go/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrDuplicateOrOutOfOrderAck occurs when a duplicate or an out-of-order ACK is received
|
||||
ErrDuplicateOrOutOfOrderAck = errors.New("SentPacketHandler: Duplicate or out-of-order ACK")
|
||||
// ErrTooManyTrackedSentPackets occurs when the sentPacketHandler has to keep track of too many packets
|
||||
ErrTooManyTrackedSentPackets = errors.New("Too many outstanding non-acked and non-retransmitted packets")
|
||||
// ErrAckForSkippedPacket occurs when the client sent an ACK for a packet number that we intentionally skipped
|
||||
ErrAckForSkippedPacket = qerr.Error(qerr.InvalidAckData, "Received an ACK for a skipped packet number")
|
||||
errAckForUnsentPacket = qerr.Error(qerr.InvalidAckData, "Received ACK for an unsent package")
|
||||
)
|
||||
|
||||
var errPacketNumberNotIncreasing = errors.New("Already sent a packet with a higher packet number.")
|
||||
|
||||
type sentPacketHandler struct {
|
||||
lastSentPacketNumber protocol.PacketNumber
|
||||
lastSentPacketTime time.Time
|
||||
skippedPackets []protocol.PacketNumber
|
||||
|
||||
LargestAcked protocol.PacketNumber
|
||||
|
||||
largestReceivedPacketWithAck protocol.PacketNumber
|
||||
|
||||
packetHistory *PacketList
|
||||
stopWaitingManager stopWaitingManager
|
||||
|
||||
retransmissionQueue []*Packet
|
||||
|
||||
bytesInFlight protocol.ByteCount
|
||||
|
||||
rttStats *congestion.RTTStats
|
||||
congestion congestion.SendAlgorithm
|
||||
|
||||
consecutiveRTOCount uint32
|
||||
}
|
||||
|
||||
// NewSentPacketHandler creates a new sentPacketHandler
|
||||
func NewSentPacketHandler() SentPacketHandler {
|
||||
rttStats := &congestion.RTTStats{}
|
||||
|
||||
congestion := congestion.NewCubicSender(
|
||||
congestion.DefaultClock{},
|
||||
rttStats,
|
||||
false, /* don't use reno since chromium doesn't (why?) */
|
||||
protocol.InitialCongestionWindow,
|
||||
protocol.DefaultMaxCongestionWindow,
|
||||
)
|
||||
|
||||
return &sentPacketHandler{
|
||||
packetHistory: NewPacketList(),
|
||||
stopWaitingManager: stopWaitingManager{},
|
||||
rttStats: rttStats,
|
||||
congestion: congestion,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) ackPacket(packetElement *PacketElement) {
|
||||
packet := &packetElement.Value
|
||||
h.bytesInFlight -= packet.Length
|
||||
h.packetHistory.Remove(packetElement)
|
||||
}
|
||||
|
||||
// nackPacket NACKs a packet
|
||||
// it returns true if a FastRetransmissions was triggered
|
||||
func (h *sentPacketHandler) nackPacket(packetElement *PacketElement) bool {
|
||||
packet := &packetElement.Value
|
||||
|
||||
packet.MissingReports++
|
||||
|
||||
if packet.MissingReports > protocol.RetransmissionThreshold {
|
||||
utils.Debugf("\tQueueing packet 0x%x for retransmission (fast)", packet.PacketNumber)
|
||||
h.queuePacketForRetransmission(packetElement)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// does NOT set packet.Retransmitted. This variable is not needed anymore
|
||||
func (h *sentPacketHandler) queuePacketForRetransmission(packetElement *PacketElement) {
|
||||
packet := &packetElement.Value
|
||||
h.bytesInFlight -= packet.Length
|
||||
h.retransmissionQueue = append(h.retransmissionQueue, packet)
|
||||
|
||||
h.packetHistory.Remove(packetElement)
|
||||
|
||||
// strictly speaking, this is only necessary for RTO retransmissions
|
||||
// this is because FastRetransmissions are triggered by missing ranges in ACKs, and then the LargestAcked will already be higher than the packet number of the retransmitted packet
|
||||
h.stopWaitingManager.QueuedRetransmissionForPacketNumber(packet.PacketNumber)
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) largestInOrderAcked() protocol.PacketNumber {
|
||||
if f := h.packetHistory.Front(); f != nil {
|
||||
return f.Value.PacketNumber - 1
|
||||
}
|
||||
return h.LargestAcked
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) SentPacket(packet *Packet) error {
|
||||
if packet.PacketNumber <= h.lastSentPacketNumber {
|
||||
return errPacketNumberNotIncreasing
|
||||
}
|
||||
|
||||
for p := h.lastSentPacketNumber + 1; p < packet.PacketNumber; p++ {
|
||||
h.skippedPackets = append(h.skippedPackets, p)
|
||||
|
||||
if len(h.skippedPackets) > protocol.MaxTrackedSkippedPackets {
|
||||
h.skippedPackets = h.skippedPackets[1:]
|
||||
}
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
h.lastSentPacketTime = now
|
||||
packet.SendTime = now
|
||||
if packet.Length == 0 {
|
||||
return errors.New("SentPacketHandler: packet cannot be empty")
|
||||
}
|
||||
h.bytesInFlight += packet.Length
|
||||
|
||||
h.lastSentPacketNumber = packet.PacketNumber
|
||||
h.packetHistory.PushBack(*packet)
|
||||
|
||||
h.congestion.OnPacketSent(
|
||||
now,
|
||||
h.BytesInFlight(),
|
||||
packet.PacketNumber,
|
||||
packet.Length,
|
||||
true, /* TODO: is retransmittable */
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) ReceivedAck(ackFrame *frames.AckFrame, withPacketNumber protocol.PacketNumber, rcvTime time.Time) error {
|
||||
if ackFrame.LargestAcked > h.lastSentPacketNumber {
|
||||
return errAckForUnsentPacket
|
||||
}
|
||||
|
||||
// duplicate or out-of-order ACK
|
||||
if withPacketNumber <= h.largestReceivedPacketWithAck {
|
||||
return ErrDuplicateOrOutOfOrderAck
|
||||
}
|
||||
|
||||
h.largestReceivedPacketWithAck = withPacketNumber
|
||||
|
||||
// ignore repeated ACK (ACKs that don't have a higher LargestAcked than the last ACK)
|
||||
if ackFrame.LargestAcked <= h.largestInOrderAcked() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// check if it acks any packets that were skipped
|
||||
for _, p := range h.skippedPackets {
|
||||
if ackFrame.AcksPacket(p) {
|
||||
return ErrAckForSkippedPacket
|
||||
}
|
||||
}
|
||||
|
||||
h.LargestAcked = ackFrame.LargestAcked
|
||||
|
||||
var ackedPackets congestion.PacketVector
|
||||
var lostPackets congestion.PacketVector
|
||||
ackRangeIndex := 0
|
||||
rttUpdated := false
|
||||
|
||||
var el, elNext *PacketElement
|
||||
for el = h.packetHistory.Front(); el != nil; el = elNext {
|
||||
// determine the next list element right at the beginning, because el.Next() is not avaible anymore, when the list element is deleted (i.e. when the packet is ACKed)
|
||||
elNext = el.Next()
|
||||
packet := el.Value
|
||||
packetNumber := packet.PacketNumber
|
||||
|
||||
// NACK packets below the LowestAcked
|
||||
if packetNumber < ackFrame.LowestAcked {
|
||||
retransmitted := h.nackPacket(el)
|
||||
if retransmitted {
|
||||
lostPackets = append(lostPackets, congestion.PacketInfo{Number: packetNumber, Length: packet.Length})
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Update the RTT
|
||||
if packetNumber == h.LargestAcked {
|
||||
rttUpdated = true
|
||||
timeDelta := rcvTime.Sub(packet.SendTime)
|
||||
h.rttStats.UpdateRTT(timeDelta, ackFrame.DelayTime, rcvTime)
|
||||
if utils.Debug() {
|
||||
utils.Debugf("\tEstimated RTT: %dms", h.rttStats.SmoothedRTT()/time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
if packetNumber > ackFrame.LargestAcked {
|
||||
break
|
||||
}
|
||||
|
||||
if ackFrame.HasMissingRanges() {
|
||||
ackRange := ackFrame.AckRanges[len(ackFrame.AckRanges)-1-ackRangeIndex]
|
||||
|
||||
for packetNumber > ackRange.LastPacketNumber && ackRangeIndex < len(ackFrame.AckRanges)-1 {
|
||||
ackRangeIndex++
|
||||
ackRange = ackFrame.AckRanges[len(ackFrame.AckRanges)-1-ackRangeIndex]
|
||||
}
|
||||
|
||||
if packetNumber >= ackRange.FirstPacketNumber { // packet i contained in ACK range
|
||||
if packetNumber > ackRange.LastPacketNumber {
|
||||
return fmt.Errorf("BUG: ackhandler would have acked wrong packet 0x%x, while evaluating range 0x%x -> 0x%x", packetNumber, ackRange.FirstPacketNumber, ackRange.LastPacketNumber)
|
||||
}
|
||||
h.ackPacket(el)
|
||||
ackedPackets = append(ackedPackets, congestion.PacketInfo{Number: packetNumber, Length: packet.Length})
|
||||
} else {
|
||||
retransmitted := h.nackPacket(el)
|
||||
if retransmitted {
|
||||
lostPackets = append(lostPackets, congestion.PacketInfo{Number: packetNumber, Length: packet.Length})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
h.ackPacket(el)
|
||||
ackedPackets = append(ackedPackets, congestion.PacketInfo{Number: packetNumber, Length: packet.Length})
|
||||
}
|
||||
}
|
||||
|
||||
if rttUpdated {
|
||||
// Reset counter if a new packet was acked
|
||||
h.consecutiveRTOCount = 0
|
||||
}
|
||||
|
||||
h.garbageCollectSkippedPackets()
|
||||
|
||||
h.stopWaitingManager.ReceivedAck(ackFrame)
|
||||
|
||||
h.congestion.OnCongestionEvent(
|
||||
rttUpdated,
|
||||
h.BytesInFlight(),
|
||||
ackedPackets,
|
||||
lostPackets,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) DequeuePacketForRetransmission() *Packet {
|
||||
if len(h.retransmissionQueue) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(h.retransmissionQueue) > 0 {
|
||||
queueLen := len(h.retransmissionQueue)
|
||||
// packets are usually NACKed in descending order. So use the slice as a stack
|
||||
packet := h.retransmissionQueue[queueLen-1]
|
||||
h.retransmissionQueue = h.retransmissionQueue[:queueLen-1]
|
||||
return packet
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) BytesInFlight() protocol.ByteCount {
|
||||
return h.bytesInFlight
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) GetLeastUnacked() protocol.PacketNumber {
|
||||
return h.largestInOrderAcked() + 1
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) GetStopWaitingFrame(force bool) *frames.StopWaitingFrame {
|
||||
return h.stopWaitingManager.GetStopWaitingFrame(force)
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) SendingAllowed() bool {
|
||||
congestionLimited := h.BytesInFlight() > h.congestion.GetCongestionWindow()
|
||||
maxTrackedLimited := protocol.PacketNumber(len(h.retransmissionQueue)+h.packetHistory.Len()) >= protocol.MaxTrackedSentPackets
|
||||
return !(congestionLimited || maxTrackedLimited)
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) CheckForError() error {
|
||||
length := len(h.retransmissionQueue) + h.packetHistory.Len()
|
||||
if protocol.PacketNumber(length) > protocol.MaxTrackedSentPackets {
|
||||
return ErrTooManyTrackedSentPackets
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) MaybeQueueRTOs() {
|
||||
if time.Now().Before(h.TimeOfFirstRTO()) {
|
||||
return
|
||||
}
|
||||
|
||||
// Always queue the two oldest packets
|
||||
if h.packetHistory.Front() != nil {
|
||||
h.queueRTO(h.packetHistory.Front())
|
||||
}
|
||||
if h.packetHistory.Front() != nil {
|
||||
h.queueRTO(h.packetHistory.Front())
|
||||
}
|
||||
|
||||
// Reset the RTO timer here, since it's not clear that this packet contained any retransmittable frames
|
||||
h.lastSentPacketTime = time.Now()
|
||||
h.consecutiveRTOCount++
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) queueRTO(el *PacketElement) {
|
||||
packet := &el.Value
|
||||
packetsLost := congestion.PacketVector{congestion.PacketInfo{
|
||||
Number: packet.PacketNumber,
|
||||
Length: packet.Length,
|
||||
}}
|
||||
h.congestion.OnCongestionEvent(false, h.BytesInFlight(), nil, packetsLost)
|
||||
h.congestion.OnRetransmissionTimeout(true)
|
||||
utils.Debugf("\tQueueing packet 0x%x for retransmission (RTO)", packet.PacketNumber)
|
||||
h.queuePacketForRetransmission(el)
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) getRTO() time.Duration {
|
||||
rto := h.congestion.RetransmissionDelay()
|
||||
if rto == 0 {
|
||||
rto = protocol.DefaultRetransmissionTime
|
||||
}
|
||||
rto = utils.MaxDuration(rto, protocol.MinRetransmissionTime)
|
||||
// Exponential backoff
|
||||
rto *= 1 << h.consecutiveRTOCount
|
||||
return utils.MinDuration(rto, protocol.MaxRetransmissionTime)
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) TimeOfFirstRTO() time.Time {
|
||||
if h.lastSentPacketTime.IsZero() {
|
||||
return time.Time{}
|
||||
}
|
||||
return h.lastSentPacketTime.Add(h.getRTO())
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) garbageCollectSkippedPackets() {
|
||||
lioa := h.largestInOrderAcked()
|
||||
deleteIndex := 0
|
||||
for i, p := range h.skippedPackets {
|
||||
if p <= lioa {
|
||||
deleteIndex = i + 1
|
||||
}
|
||||
}
|
||||
h.skippedPackets = h.skippedPackets[deleteIndex:]
|
||||
}
|
12
cmd/gost/vendor/github.com/lucas-clemente/quic-go/congestion/congestion_vector.go
generated
vendored
12
cmd/gost/vendor/github.com/lucas-clemente/quic-go/congestion/congestion_vector.go
generated
vendored
@ -1,12 +0,0 @@
|
||||
package congestion
|
||||
|
||||
import "github.com/lucas-clemente/quic-go/protocol"
|
||||
|
||||
// PacketInfo combines packet number and length of a packet for congestion calculation
|
||||
type PacketInfo struct {
|
||||
Number protocol.PacketNumber
|
||||
Length protocol.ByteCount
|
||||
}
|
||||
|
||||
// PacketVector is passed to the congestion algorithm
|
||||
type PacketVector []PacketInfo
|
92
cmd/gost/vendor/github.com/lucas-clemente/quic-go/crypto/proof_source.go
generated
vendored
92
cmd/gost/vendor/github.com/lucas-clemente/quic-go/crypto/proof_source.go
generated
vendored
@ -1,92 +0,0 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// proofSource stores a key and a certificate for the server proof
|
||||
type proofSource struct {
|
||||
config *tls.Config
|
||||
}
|
||||
|
||||
// NewProofSource loads the key and cert from files
|
||||
func NewProofSource(tlsConfig *tls.Config) (Signer, error) {
|
||||
return &proofSource{config: tlsConfig}, nil
|
||||
}
|
||||
|
||||
// SignServerProof signs CHLO and server config for use in the server proof
|
||||
func (ps *proofSource) SignServerProof(sni string, chlo []byte, serverConfigData []byte) ([]byte, error) {
|
||||
cert, err := ps.getCertForSNI(sni)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hash := sha256.New()
|
||||
hash.Write([]byte("QUIC CHLO and server config signature\x00"))
|
||||
chloHash := sha256.Sum256(chlo)
|
||||
hash.Write([]byte{32, 0, 0, 0})
|
||||
hash.Write(chloHash[:])
|
||||
hash.Write(serverConfigData)
|
||||
|
||||
key, ok := cert.PrivateKey.(crypto.Signer)
|
||||
if !ok {
|
||||
return nil, errors.New("expected PrivateKey to implement crypto.Signer")
|
||||
}
|
||||
|
||||
opts := crypto.SignerOpts(crypto.SHA256)
|
||||
|
||||
if _, ok = key.(*rsa.PrivateKey); ok {
|
||||
opts = &rsa.PSSOptions{SaltLength: 32, Hash: crypto.SHA256}
|
||||
}
|
||||
|
||||
return key.Sign(rand.Reader, hash.Sum(nil), opts)
|
||||
}
|
||||
|
||||
// GetCertsCompressed gets the certificate in the format described by the QUIC crypto doc
|
||||
func (ps *proofSource) GetCertsCompressed(sni string, pCommonSetHashes, pCachedHashes []byte) ([]byte, error) {
|
||||
cert, err := ps.getCertForSNI(sni)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getCompressedCert(cert.Certificate, pCommonSetHashes, pCachedHashes)
|
||||
}
|
||||
|
||||
// GetLeafCert gets the leaf certificate
|
||||
func (ps *proofSource) GetLeafCert(sni string) ([]byte, error) {
|
||||
cert, err := ps.getCertForSNI(sni)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cert.Certificate[0], nil
|
||||
}
|
||||
|
||||
func (ps *proofSource) getCertForSNI(sni string) (*tls.Certificate, error) {
|
||||
if ps.config.GetCertificate != nil {
|
||||
cert, err := ps.config.GetCertificate(&tls.ClientHelloInfo{ServerName: sni})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cert != nil {
|
||||
return cert, nil
|
||||
}
|
||||
}
|
||||
if len(ps.config.NameToCertificate) != 0 {
|
||||
if cert, ok := ps.config.NameToCertificate[sni]; ok {
|
||||
return cert, nil
|
||||
}
|
||||
wildcardSNI := "*" + strings.TrimLeftFunc(sni, func(r rune) bool { return r != '.' })
|
||||
if cert, ok := ps.config.NameToCertificate[wildcardSNI]; ok {
|
||||
return cert, nil
|
||||
}
|
||||
}
|
||||
if len(ps.config.Certificates) != 0 {
|
||||
return &ps.config.Certificates[0], nil
|
||||
}
|
||||
return nil, errors.New("no matching certificate found")
|
||||
}
|
8
cmd/gost/vendor/github.com/lucas-clemente/quic-go/crypto/signer.go
generated
vendored
8
cmd/gost/vendor/github.com/lucas-clemente/quic-go/crypto/signer.go
generated
vendored
@ -1,8 +0,0 @@
|
||||
package crypto
|
||||
|
||||
// A Signer holds a certificate and a private key
|
||||
type Signer interface {
|
||||
SignServerProof(sni string, chlo []byte, serverConfigData []byte) ([]byte, error)
|
||||
GetCertsCompressed(sni string, commonSetHashes, cachedHashes []byte) ([]byte, error)
|
||||
GetLeafCert(sni string) ([]byte, error)
|
||||
}
|
128
cmd/gost/vendor/github.com/lucas-clemente/quic-go/crypto/source_address_token.go
generated
vendored
128
cmd/gost/vendor/github.com/lucas-clemente/quic-go/crypto/source_address_token.go
generated
vendored
@ -1,128 +0,0 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
// StkSource is used to create and verify source address tokens
|
||||
type StkSource interface {
|
||||
// NewToken creates a new token for a given IP address
|
||||
NewToken(ip net.IP) ([]byte, error)
|
||||
// VerifyToken verifies if a token matches a given IP address and is not outdated
|
||||
VerifyToken(ip net.IP, data []byte) error
|
||||
}
|
||||
|
||||
type sourceAddressToken struct {
|
||||
ip net.IP
|
||||
// unix timestamp in seconds
|
||||
timestamp uint64
|
||||
}
|
||||
|
||||
func (t *sourceAddressToken) serialize() []byte {
|
||||
res := make([]byte, 8+len(t.ip))
|
||||
binary.LittleEndian.PutUint64(res, t.timestamp)
|
||||
copy(res[8:], t.ip)
|
||||
return res
|
||||
}
|
||||
|
||||
func parseToken(data []byte) (*sourceAddressToken, error) {
|
||||
if len(data) != 8+4 && len(data) != 8+16 {
|
||||
return nil, fmt.Errorf("invalid STK length: %d", len(data))
|
||||
}
|
||||
return &sourceAddressToken{
|
||||
ip: data[8:],
|
||||
timestamp: binary.LittleEndian.Uint64(data),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type stkSource struct {
|
||||
aead cipher.AEAD
|
||||
}
|
||||
|
||||
const stkKeySize = 16
|
||||
|
||||
// Chrome currently sets this to 12, but discusses changing it to 16. We start
|
||||
// at 16 :)
|
||||
const stkNonceSize = 16
|
||||
|
||||
// NewStkSource creates a source for source address tokens
|
||||
func NewStkSource(secret []byte) (StkSource, error) {
|
||||
key, err := deriveKey(secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aead, err := cipher.NewGCMWithNonceSize(c, stkNonceSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &stkSource{aead: aead}, nil
|
||||
}
|
||||
|
||||
func (s *stkSource) NewToken(ip net.IP) ([]byte, error) {
|
||||
return encryptToken(s.aead, &sourceAddressToken{
|
||||
ip: ip,
|
||||
timestamp: uint64(time.Now().Unix()),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *stkSource) VerifyToken(ip net.IP, data []byte) error {
|
||||
if len(data) < stkNonceSize {
|
||||
return errors.New("STK too short")
|
||||
}
|
||||
nonce := data[:stkNonceSize]
|
||||
|
||||
res, err := s.aead.Open(nil, nonce, data[stkNonceSize:], nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
token, err := parseToken(res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if subtle.ConstantTimeCompare(token.ip, ip) != 1 {
|
||||
return errors.New("invalid ip in STK")
|
||||
}
|
||||
|
||||
if time.Now().Unix() > int64(token.timestamp)+protocol.STKExpiryTimeSec {
|
||||
return errors.New("STK expired")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func deriveKey(secret []byte) ([]byte, error) {
|
||||
r := hkdf.New(sha256.New, secret, nil, []byte("QUIC source address token key"))
|
||||
key := make([]byte, stkKeySize)
|
||||
if _, err := io.ReadFull(r, key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func encryptToken(aead cipher.AEAD, token *sourceAddressToken) ([]byte, error) {
|
||||
nonce := make([]byte, stkNonceSize)
|
||||
if _, err := rand.Read(nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return aead.Seal(nonce, nonce, token.serialize(), nil), nil
|
||||
}
|
188
cmd/gost/vendor/github.com/lucas-clemente/quic-go/flowcontrol/flow_control_manager.go
generated
vendored
188
cmd/gost/vendor/github.com/lucas-clemente/quic-go/flowcontrol/flow_control_manager.go
generated
vendored
@ -1,188 +0,0 @@
|
||||
package flowcontrol
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/handshake"
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
"github.com/lucas-clemente/quic-go/utils"
|
||||
)
|
||||
|
||||
type flowControlManager struct {
|
||||
connectionParametersManager *handshake.ConnectionParametersManager
|
||||
streamFlowController map[protocol.StreamID]*flowController
|
||||
contributesToConnectionFlowControl map[protocol.StreamID]bool
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrStreamFlowControlViolation is a stream flow control violation
|
||||
ErrStreamFlowControlViolation = errors.New("Stream level flow control violation")
|
||||
// ErrConnectionFlowControlViolation is a connection level flow control violation
|
||||
ErrConnectionFlowControlViolation = errors.New("Connection level flow control violation")
|
||||
)
|
||||
|
||||
var errMapAccess = errors.New("Error accessing the flowController map.")
|
||||
|
||||
// NewFlowControlManager creates a new flow control manager
|
||||
func NewFlowControlManager(connectionParametersManager *handshake.ConnectionParametersManager) FlowControlManager {
|
||||
fcm := flowControlManager{
|
||||
connectionParametersManager: connectionParametersManager,
|
||||
streamFlowController: make(map[protocol.StreamID]*flowController),
|
||||
contributesToConnectionFlowControl: make(map[protocol.StreamID]bool),
|
||||
}
|
||||
// initialize connection level flow controller
|
||||
fcm.streamFlowController[0] = newFlowController(0, connectionParametersManager)
|
||||
fcm.contributesToConnectionFlowControl[0] = false
|
||||
return &fcm
|
||||
}
|
||||
|
||||
// NewStream creates new flow controllers for a stream
|
||||
func (f *flowControlManager) NewStream(streamID protocol.StreamID, contributesToConnectionFlow bool) {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
if _, ok := f.streamFlowController[streamID]; ok {
|
||||
return
|
||||
}
|
||||
|
||||
f.streamFlowController[streamID] = newFlowController(streamID, f.connectionParametersManager)
|
||||
f.contributesToConnectionFlowControl[streamID] = contributesToConnectionFlow
|
||||
}
|
||||
|
||||
// RemoveStream removes a closed stream from flow control
|
||||
func (f *flowControlManager) RemoveStream(streamID protocol.StreamID) {
|
||||
f.mutex.Lock()
|
||||
delete(f.streamFlowController, streamID)
|
||||
delete(f.contributesToConnectionFlowControl, streamID)
|
||||
f.mutex.Unlock()
|
||||
}
|
||||
|
||||
// UpdateHighestReceived updates the highest received byte offset for a stream
|
||||
// it adds the number of additional bytes to connection level flow control
|
||||
// streamID must not be 0 here
|
||||
func (f *flowControlManager) UpdateHighestReceived(streamID protocol.StreamID, byteOffset protocol.ByteCount) error {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
streamFlowController, err := f.getFlowController(streamID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
increment := streamFlowController.UpdateHighestReceived(byteOffset)
|
||||
|
||||
if streamFlowController.CheckFlowControlViolation() {
|
||||
return ErrStreamFlowControlViolation
|
||||
}
|
||||
|
||||
if f.contributesToConnectionFlowControl[streamID] {
|
||||
connectionFlowController := f.streamFlowController[0]
|
||||
connectionFlowController.IncrementHighestReceived(increment)
|
||||
if connectionFlowController.CheckFlowControlViolation() {
|
||||
return ErrConnectionFlowControlViolation
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// streamID must not be 0 here
|
||||
func (f *flowControlManager) AddBytesRead(streamID protocol.StreamID, n protocol.ByteCount) error {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
streamFlowController, err := f.getFlowController(streamID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
streamFlowController.AddBytesRead(n)
|
||||
|
||||
if f.contributesToConnectionFlowControl[streamID] {
|
||||
f.streamFlowController[0].AddBytesRead(n)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *flowControlManager) GetWindowUpdates() (res []WindowUpdate) {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
for id, fc := range f.streamFlowController {
|
||||
if necessary, offset := fc.MaybeTriggerWindowUpdate(); necessary {
|
||||
res = append(res, WindowUpdate{StreamID: id, Offset: offset})
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// streamID must not be 0 here
|
||||
func (f *flowControlManager) AddBytesSent(streamID protocol.StreamID, n protocol.ByteCount) error {
|
||||
// Only lock the part reading from the map, since send-windows are only accessed from the session goroutine.
|
||||
f.mutex.Lock()
|
||||
streamFlowController, err := f.getFlowController(streamID)
|
||||
f.mutex.Unlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
streamFlowController.AddBytesSent(n)
|
||||
|
||||
if f.contributesToConnectionFlowControl[streamID] {
|
||||
f.streamFlowController[0].AddBytesSent(n)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// must not be called with StreamID 0
|
||||
func (f *flowControlManager) SendWindowSize(streamID protocol.StreamID) (protocol.ByteCount, error) {
|
||||
// Only lock the part reading from the map, since send-windows are only accessed from the session goroutine.
|
||||
f.mutex.RLock()
|
||||
streamFlowController, err := f.getFlowController(streamID)
|
||||
f.mutex.RUnlock()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
res := streamFlowController.SendWindowSize()
|
||||
|
||||
contributes, ok := f.contributesToConnectionFlowControl[streamID]
|
||||
if !ok {
|
||||
return 0, errMapAccess
|
||||
}
|
||||
if contributes {
|
||||
res = utils.MinByteCount(res, f.streamFlowController[0].SendWindowSize())
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (f *flowControlManager) RemainingConnectionWindowSize() protocol.ByteCount {
|
||||
// Only lock the part reading from the map, since send-windows are only accessed from the session goroutine.
|
||||
f.mutex.RLock()
|
||||
res := f.streamFlowController[0].SendWindowSize()
|
||||
f.mutex.RUnlock()
|
||||
return res
|
||||
}
|
||||
|
||||
// streamID may be 0 here
|
||||
func (f *flowControlManager) UpdateWindow(streamID protocol.StreamID, offset protocol.ByteCount) (bool, error) {
|
||||
// Only lock the part reading from the map, since send-windows are only accessed from the session goroutine.
|
||||
f.mutex.Lock()
|
||||
streamFlowController, err := f.getFlowController(streamID)
|
||||
f.mutex.Unlock()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return streamFlowController.UpdateSendWindow(offset), nil
|
||||
}
|
||||
|
||||
func (f *flowControlManager) getFlowController(streamID protocol.StreamID) (*flowController, error) {
|
||||
streamFlowController, ok := f.streamFlowController[streamID]
|
||||
if !ok {
|
||||
return nil, errMapAccess
|
||||
}
|
||||
return streamFlowController, nil
|
||||
}
|
115
cmd/gost/vendor/github.com/lucas-clemente/quic-go/flowcontrol/flow_controller.go
generated
vendored
115
cmd/gost/vendor/github.com/lucas-clemente/quic-go/flowcontrol/flow_controller.go
generated
vendored
@ -1,115 +0,0 @@
|
||||
package flowcontrol
|
||||
|
||||
import (
|
||||
"github.com/lucas-clemente/quic-go/handshake"
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
)
|
||||
|
||||
type flowController struct {
|
||||
streamID protocol.StreamID
|
||||
|
||||
connectionParametersManager *handshake.ConnectionParametersManager
|
||||
|
||||
bytesSent protocol.ByteCount
|
||||
sendFlowControlWindow protocol.ByteCount
|
||||
|
||||
bytesRead protocol.ByteCount
|
||||
highestReceived protocol.ByteCount
|
||||
receiveFlowControlWindow protocol.ByteCount
|
||||
receiveFlowControlWindowIncrement protocol.ByteCount
|
||||
}
|
||||
|
||||
// newFlowController gets a new flow controller
|
||||
func newFlowController(streamID protocol.StreamID, connectionParametersManager *handshake.ConnectionParametersManager) *flowController {
|
||||
fc := flowController{
|
||||
streamID: streamID,
|
||||
connectionParametersManager: connectionParametersManager,
|
||||
}
|
||||
|
||||
if streamID == 0 {
|
||||
fc.receiveFlowControlWindow = connectionParametersManager.GetReceiveConnectionFlowControlWindow()
|
||||
fc.receiveFlowControlWindowIncrement = fc.receiveFlowControlWindow
|
||||
} else {
|
||||
fc.receiveFlowControlWindow = connectionParametersManager.GetReceiveStreamFlowControlWindow()
|
||||
fc.receiveFlowControlWindowIncrement = fc.receiveFlowControlWindow
|
||||
}
|
||||
|
||||
return &fc
|
||||
}
|
||||
|
||||
func (c *flowController) getSendFlowControlWindow() protocol.ByteCount {
|
||||
if c.sendFlowControlWindow == 0 {
|
||||
if c.streamID == 0 {
|
||||
return c.connectionParametersManager.GetSendConnectionFlowControlWindow()
|
||||
}
|
||||
return c.connectionParametersManager.GetSendStreamFlowControlWindow()
|
||||
}
|
||||
return c.sendFlowControlWindow
|
||||
}
|
||||
|
||||
func (c *flowController) AddBytesSent(n protocol.ByteCount) {
|
||||
c.bytesSent += n
|
||||
}
|
||||
|
||||
// UpdateSendWindow should be called after receiving a WindowUpdateFrame
|
||||
// it returns true if the window was actually updated
|
||||
func (c *flowController) UpdateSendWindow(newOffset protocol.ByteCount) bool {
|
||||
if newOffset > c.sendFlowControlWindow {
|
||||
c.sendFlowControlWindow = newOffset
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *flowController) SendWindowSize() protocol.ByteCount {
|
||||
sendFlowControlWindow := c.getSendFlowControlWindow()
|
||||
|
||||
if c.bytesSent > sendFlowControlWindow { // should never happen, but make sure we don't do an underflow here
|
||||
return 0
|
||||
}
|
||||
return sendFlowControlWindow - c.bytesSent
|
||||
}
|
||||
|
||||
func (c *flowController) SendWindowOffset() protocol.ByteCount {
|
||||
return c.getSendFlowControlWindow()
|
||||
}
|
||||
|
||||
// UpdateHighestReceived updates the highestReceived value, if the byteOffset is higher
|
||||
// Should **only** be used for the stream-level FlowController
|
||||
func (c *flowController) UpdateHighestReceived(byteOffset protocol.ByteCount) protocol.ByteCount {
|
||||
if byteOffset > c.highestReceived {
|
||||
increment := byteOffset - c.highestReceived
|
||||
c.highestReceived = byteOffset
|
||||
return increment
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// IncrementHighestReceived adds an increment to the highestReceived value
|
||||
// Should **only** be used for the connection-level FlowController
|
||||
func (c *flowController) IncrementHighestReceived(increment protocol.ByteCount) {
|
||||
c.highestReceived += increment
|
||||
}
|
||||
|
||||
func (c *flowController) AddBytesRead(n protocol.ByteCount) {
|
||||
c.bytesRead += n
|
||||
}
|
||||
|
||||
// MaybeTriggerWindowUpdate determines if it is necessary to send a WindowUpdate
|
||||
// if so, it returns true and the offset of the window
|
||||
func (c *flowController) MaybeTriggerWindowUpdate() (bool, protocol.ByteCount) {
|
||||
diff := c.receiveFlowControlWindow - c.bytesRead
|
||||
// Chromium implements the same threshold
|
||||
if diff < (c.receiveFlowControlWindowIncrement / 2) {
|
||||
c.receiveFlowControlWindow = c.bytesRead + c.receiveFlowControlWindowIncrement
|
||||
return true, c.receiveFlowControlWindow
|
||||
}
|
||||
return false, 0
|
||||
}
|
||||
|
||||
func (c *flowController) CheckFlowControlViolation() bool {
|
||||
if c.highestReceived > c.receiveFlowControlWindow {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
19
cmd/gost/vendor/github.com/lucas-clemente/quic-go/frames/log.go
generated
vendored
19
cmd/gost/vendor/github.com/lucas-clemente/quic-go/frames/log.go
generated
vendored
@ -1,19 +0,0 @@
|
||||
package frames
|
||||
|
||||
import "github.com/lucas-clemente/quic-go/utils"
|
||||
|
||||
// LogFrame logs a frame, either sent or received
|
||||
func LogFrame(frame Frame, sent bool) {
|
||||
if !utils.Debug() {
|
||||
return
|
||||
}
|
||||
dir := "<-"
|
||||
if sent {
|
||||
dir = "->"
|
||||
}
|
||||
if sf, ok := frame.(*StreamFrame); ok {
|
||||
utils.Debugf("\t%s &frames.StreamFrame{StreamID: %d, FinBit: %t, Offset: 0x%x, Data length: 0x%x, Offset + Data length: 0x%x}", dir, sf.StreamID, sf.FinBit, sf.Offset, sf.DataLen(), sf.Offset+sf.DataLen())
|
||||
return
|
||||
}
|
||||
utils.Debugf("\t%s %#v", dir, frame)
|
||||
}
|
51
cmd/gost/vendor/github.com/lucas-clemente/quic-go/h2quic/request.go
generated
vendored
51
cmd/gost/vendor/github.com/lucas-clemente/quic-go/h2quic/request.go
generated
vendored
@ -1,51 +0,0 @@
|
||||
package h2quic
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"golang.org/x/net/http2/hpack"
|
||||
)
|
||||
|
||||
func requestFromHeaders(headers []hpack.HeaderField) (*http.Request, error) {
|
||||
var path, authority, method string
|
||||
httpHeaders := http.Header{}
|
||||
|
||||
for _, h := range headers {
|
||||
switch h.Name {
|
||||
case ":path":
|
||||
path = h.Value
|
||||
case ":method":
|
||||
method = h.Value
|
||||
case ":authority":
|
||||
authority = h.Value
|
||||
default:
|
||||
if !h.IsPseudo() {
|
||||
httpHeaders.Add(h.Name, h.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(path) == 0 || len(authority) == 0 || len(method) == 0 {
|
||||
return nil, errors.New(":path, :authority and :method must not be empty")
|
||||
}
|
||||
|
||||
u, err := url.Parse(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &http.Request{
|
||||
Method: method,
|
||||
URL: u,
|
||||
Proto: "HTTP/2.0",
|
||||
ProtoMajor: 2,
|
||||
ProtoMinor: 0,
|
||||
Header: httpHeaders,
|
||||
Body: nil,
|
||||
// ContentLength: -1,
|
||||
Host: authority,
|
||||
RequestURI: path,
|
||||
}, nil
|
||||
}
|
202
cmd/gost/vendor/github.com/lucas-clemente/quic-go/handshake/connection_parameters_manager.go
generated
vendored
202
cmd/gost/vendor/github.com/lucas-clemente/quic-go/handshake/connection_parameters_manager.go
generated
vendored
@ -1,202 +0,0 @@
|
||||
package handshake
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
"github.com/lucas-clemente/quic-go/qerr"
|
||||
"github.com/lucas-clemente/quic-go/utils"
|
||||
)
|
||||
|
||||
// ConnectionParametersManager stores the connection parameters
|
||||
// Warning: Writes may only be done from the crypto stream, see the comment
|
||||
// in GetSHLOMap().
|
||||
type ConnectionParametersManager struct {
|
||||
params map[Tag][]byte
|
||||
mutex sync.RWMutex
|
||||
|
||||
flowControlNegotiated bool // have the flow control parameters for sending already been negotiated
|
||||
|
||||
maxStreamsPerConnection uint32
|
||||
idleConnectionStateLifetime time.Duration
|
||||
sendStreamFlowControlWindow protocol.ByteCount
|
||||
sendConnectionFlowControlWindow protocol.ByteCount
|
||||
receiveStreamFlowControlWindow protocol.ByteCount
|
||||
receiveConnectionFlowControlWindow protocol.ByteCount
|
||||
}
|
||||
|
||||
var errTagNotInConnectionParameterMap = errors.New("ConnectionParametersManager: Tag not found in ConnectionsParameter map")
|
||||
|
||||
// ErrMalformedTag is returned when the tag value cannot be read
|
||||
var (
|
||||
ErrMalformedTag = qerr.Error(qerr.InvalidCryptoMessageParameter, "malformed Tag value")
|
||||
ErrFlowControlRenegotiationNotSupported = qerr.Error(qerr.InvalidCryptoMessageParameter, "renegotiation of flow control parameters not supported")
|
||||
)
|
||||
|
||||
// NewConnectionParamatersManager creates a new connection parameters manager
|
||||
func NewConnectionParamatersManager() *ConnectionParametersManager {
|
||||
return &ConnectionParametersManager{
|
||||
params: make(map[Tag][]byte),
|
||||
idleConnectionStateLifetime: protocol.DefaultIdleTimeout,
|
||||
sendStreamFlowControlWindow: protocol.InitialStreamFlowControlWindow, // can only be changed by the client
|
||||
sendConnectionFlowControlWindow: protocol.InitialConnectionFlowControlWindow, // can only be changed by the client
|
||||
receiveStreamFlowControlWindow: protocol.ReceiveStreamFlowControlWindow,
|
||||
receiveConnectionFlowControlWindow: protocol.ReceiveConnectionFlowControlWindow,
|
||||
maxStreamsPerConnection: protocol.MaxStreamsPerConnection,
|
||||
}
|
||||
}
|
||||
|
||||
// SetFromMap reads all params
|
||||
func (h *ConnectionParametersManager) SetFromMap(params map[Tag][]byte) error {
|
||||
h.mutex.Lock()
|
||||
defer h.mutex.Unlock()
|
||||
|
||||
for key, value := range params {
|
||||
switch key {
|
||||
case TagTCID:
|
||||
h.params[key] = value
|
||||
case TagMSPC:
|
||||
clientValue, err := utils.ReadUint32(bytes.NewBuffer(value))
|
||||
if err != nil {
|
||||
return ErrMalformedTag
|
||||
}
|
||||
h.maxStreamsPerConnection = h.negotiateMaxStreamsPerConnection(clientValue)
|
||||
case TagICSL:
|
||||
clientValue, err := utils.ReadUint32(bytes.NewBuffer(value))
|
||||
if err != nil {
|
||||
return ErrMalformedTag
|
||||
}
|
||||
h.idleConnectionStateLifetime = h.negotiateIdleConnectionStateLifetime(time.Duration(clientValue) * time.Second)
|
||||
case TagSFCW:
|
||||
if h.flowControlNegotiated {
|
||||
return ErrFlowControlRenegotiationNotSupported
|
||||
}
|
||||
sendStreamFlowControlWindow, err := utils.ReadUint32(bytes.NewBuffer(value))
|
||||
if err != nil {
|
||||
return ErrMalformedTag
|
||||
}
|
||||
h.sendStreamFlowControlWindow = protocol.ByteCount(sendStreamFlowControlWindow)
|
||||
case TagCFCW:
|
||||
if h.flowControlNegotiated {
|
||||
return ErrFlowControlRenegotiationNotSupported
|
||||
}
|
||||
sendConnectionFlowControlWindow, err := utils.ReadUint32(bytes.NewBuffer(value))
|
||||
if err != nil {
|
||||
return ErrMalformedTag
|
||||
}
|
||||
h.sendConnectionFlowControlWindow = protocol.ByteCount(sendConnectionFlowControlWindow)
|
||||
}
|
||||
}
|
||||
|
||||
_, containsSFCW := params[TagSFCW]
|
||||
_, containsCFCW := params[TagCFCW]
|
||||
if containsCFCW || containsSFCW {
|
||||
h.flowControlNegotiated = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *ConnectionParametersManager) negotiateMaxStreamsPerConnection(clientValue uint32) uint32 {
|
||||
return utils.MinUint32(clientValue, protocol.MaxStreamsPerConnection)
|
||||
}
|
||||
|
||||
func (h *ConnectionParametersManager) negotiateIdleConnectionStateLifetime(clientValue time.Duration) time.Duration {
|
||||
return utils.MinDuration(clientValue, protocol.MaxIdleTimeout)
|
||||
}
|
||||
|
||||
// getRawValue gets the byte-slice for a tag
|
||||
func (h *ConnectionParametersManager) getRawValue(tag Tag) ([]byte, error) {
|
||||
h.mutex.RLock()
|
||||
rawValue, ok := h.params[tag]
|
||||
h.mutex.RUnlock()
|
||||
|
||||
if !ok {
|
||||
return nil, errTagNotInConnectionParameterMap
|
||||
}
|
||||
return rawValue, nil
|
||||
}
|
||||
|
||||
// GetSHLOMap gets all values (except crypto values) needed for the SHLO
|
||||
func (h *ConnectionParametersManager) GetSHLOMap() map[Tag][]byte {
|
||||
sfcw := bytes.NewBuffer([]byte{})
|
||||
utils.WriteUint32(sfcw, uint32(h.GetReceiveStreamFlowControlWindow()))
|
||||
cfcw := bytes.NewBuffer([]byte{})
|
||||
utils.WriteUint32(cfcw, uint32(h.GetReceiveConnectionFlowControlWindow()))
|
||||
mspc := bytes.NewBuffer([]byte{})
|
||||
utils.WriteUint32(mspc, h.GetMaxStreamsPerConnection())
|
||||
mids := bytes.NewBuffer([]byte{})
|
||||
utils.WriteUint32(mids, protocol.MaxIncomingDynamicStreams)
|
||||
icsl := bytes.NewBuffer([]byte{})
|
||||
utils.WriteUint32(icsl, uint32(h.GetIdleConnectionStateLifetime()/time.Second))
|
||||
|
||||
return map[Tag][]byte{
|
||||
TagICSL: icsl.Bytes(),
|
||||
TagMSPC: mspc.Bytes(),
|
||||
TagMIDS: mids.Bytes(),
|
||||
TagCFCW: cfcw.Bytes(),
|
||||
TagSFCW: sfcw.Bytes(),
|
||||
}
|
||||
}
|
||||
|
||||
// GetSendStreamFlowControlWindow gets the size of the stream-level flow control window for sending data
|
||||
func (h *ConnectionParametersManager) GetSendStreamFlowControlWindow() protocol.ByteCount {
|
||||
h.mutex.RLock()
|
||||
defer h.mutex.RUnlock()
|
||||
return h.sendStreamFlowControlWindow
|
||||
}
|
||||
|
||||
// GetSendConnectionFlowControlWindow gets the size of the stream-level flow control window for sending data
|
||||
func (h *ConnectionParametersManager) GetSendConnectionFlowControlWindow() protocol.ByteCount {
|
||||
h.mutex.RLock()
|
||||
defer h.mutex.RUnlock()
|
||||
return h.sendConnectionFlowControlWindow
|
||||
}
|
||||
|
||||
// GetReceiveStreamFlowControlWindow gets the size of the stream-level flow control window for receiving data
|
||||
func (h *ConnectionParametersManager) GetReceiveStreamFlowControlWindow() protocol.ByteCount {
|
||||
h.mutex.RLock()
|
||||
defer h.mutex.RUnlock()
|
||||
return h.receiveStreamFlowControlWindow
|
||||
}
|
||||
|
||||
// GetReceiveConnectionFlowControlWindow gets the size of the stream-level flow control window for receiving data
|
||||
func (h *ConnectionParametersManager) GetReceiveConnectionFlowControlWindow() protocol.ByteCount {
|
||||
h.mutex.RLock()
|
||||
defer h.mutex.RUnlock()
|
||||
return h.receiveConnectionFlowControlWindow
|
||||
}
|
||||
|
||||
// GetMaxStreamsPerConnection gets the maximum number of streams per connection
|
||||
func (h *ConnectionParametersManager) GetMaxStreamsPerConnection() uint32 {
|
||||
h.mutex.RLock()
|
||||
defer h.mutex.RUnlock()
|
||||
return h.maxStreamsPerConnection
|
||||
}
|
||||
|
||||
// GetIdleConnectionStateLifetime gets the idle timeout
|
||||
func (h *ConnectionParametersManager) GetIdleConnectionStateLifetime() time.Duration {
|
||||
h.mutex.RLock()
|
||||
defer h.mutex.RUnlock()
|
||||
return h.idleConnectionStateLifetime
|
||||
}
|
||||
|
||||
// TruncateConnectionID determines if the client requests truncated ConnectionIDs
|
||||
func (h *ConnectionParametersManager) TruncateConnectionID() bool {
|
||||
rawValue, err := h.getRawValue(TagTCID)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if len(rawValue) != 4 {
|
||||
return false
|
||||
}
|
||||
value := binary.LittleEndian.Uint32(rawValue)
|
||||
if value == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
327
cmd/gost/vendor/github.com/lucas-clemente/quic-go/handshake/crypto_setup.go
generated
vendored
327
cmd/gost/vendor/github.com/lucas-clemente/quic-go/handshake/crypto_setup.go
generated
vendored
@ -1,327 +0,0 @@
|
||||
package handshake
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/crypto"
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
"github.com/lucas-clemente/quic-go/qerr"
|
||||
"github.com/lucas-clemente/quic-go/utils"
|
||||
)
|
||||
|
||||
// KeyDerivationFunction is used for key derivation
|
||||
type KeyDerivationFunction func(forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo []byte, scfg []byte, cert []byte, divNonce []byte) (crypto.AEAD, error)
|
||||
|
||||
// KeyExchangeFunction is used to make a new KEX
|
||||
type KeyExchangeFunction func() crypto.KeyExchange
|
||||
|
||||
// The CryptoSetup handles all things crypto for the Session
|
||||
type CryptoSetup struct {
|
||||
connID protocol.ConnectionID
|
||||
ip net.IP
|
||||
version protocol.VersionNumber
|
||||
scfg *ServerConfig
|
||||
diversificationNonce []byte
|
||||
|
||||
secureAEAD crypto.AEAD
|
||||
forwardSecureAEAD crypto.AEAD
|
||||
receivedForwardSecurePacket bool
|
||||
receivedSecurePacket bool
|
||||
aeadChanged chan struct{}
|
||||
|
||||
keyDerivation KeyDerivationFunction
|
||||
keyExchange KeyExchangeFunction
|
||||
|
||||
cryptoStream utils.Stream
|
||||
|
||||
connectionParametersManager *ConnectionParametersManager
|
||||
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
var _ crypto.AEAD = &CryptoSetup{}
|
||||
|
||||
// NewCryptoSetup creates a new CryptoSetup instance
|
||||
func NewCryptoSetup(
|
||||
connID protocol.ConnectionID,
|
||||
ip net.IP,
|
||||
version protocol.VersionNumber,
|
||||
scfg *ServerConfig,
|
||||
cryptoStream utils.Stream,
|
||||
connectionParametersManager *ConnectionParametersManager,
|
||||
aeadChanged chan struct{},
|
||||
) (*CryptoSetup, error) {
|
||||
return &CryptoSetup{
|
||||
connID: connID,
|
||||
ip: ip,
|
||||
version: version,
|
||||
scfg: scfg,
|
||||
keyDerivation: crypto.DeriveKeysAESGCM,
|
||||
keyExchange: getEphermalKEX,
|
||||
cryptoStream: cryptoStream,
|
||||
connectionParametersManager: connectionParametersManager,
|
||||
aeadChanged: aeadChanged,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// HandleCryptoStream reads and writes messages on the crypto stream
|
||||
func (h *CryptoSetup) HandleCryptoStream() error {
|
||||
for {
|
||||
var chloData bytes.Buffer
|
||||
messageTag, cryptoData, err := ParseHandshakeMessage(io.TeeReader(h.cryptoStream, &chloData))
|
||||
if err != nil {
|
||||
return qerr.HandshakeFailed
|
||||
}
|
||||
if messageTag != TagCHLO {
|
||||
return qerr.InvalidCryptoMessageType
|
||||
}
|
||||
|
||||
utils.Debugf("Got CHLO:\n%s", printHandshakeMessage(cryptoData))
|
||||
|
||||
done, err := h.handleMessage(chloData.Bytes(), cryptoData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if done {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *CryptoSetup) handleMessage(chloData []byte, cryptoData map[Tag][]byte) (bool, error) {
|
||||
sniSlice, ok := cryptoData[TagSNI]
|
||||
if !ok {
|
||||
return false, qerr.Error(qerr.CryptoMessageParameterNotFound, "SNI required")
|
||||
}
|
||||
sni := string(sniSlice)
|
||||
if sni == "" {
|
||||
return false, qerr.Error(qerr.CryptoMessageParameterNotFound, "SNI required")
|
||||
}
|
||||
|
||||
var reply []byte
|
||||
var err error
|
||||
if !h.isInchoateCHLO(cryptoData) {
|
||||
// We have a CHLO with a proper server config ID, do a 0-RTT handshake
|
||||
reply, err = h.handleCHLO(sni, chloData, cryptoData)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
_, err = h.cryptoStream.Write(reply)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// We have an inchoate or non-matching CHLO, we now send a rejection
|
||||
reply, err = h.handleInchoateCHLO(sni, chloData, cryptoData)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
_, err = h.cryptoStream.Write(reply)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Open a message
|
||||
func (h *CryptoSetup) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) {
|
||||
h.mutex.RLock()
|
||||
defer h.mutex.RUnlock()
|
||||
|
||||
if h.forwardSecureAEAD != nil {
|
||||
res, err := h.forwardSecureAEAD.Open(dst, src, packetNumber, associatedData)
|
||||
if err == nil {
|
||||
h.receivedForwardSecurePacket = true
|
||||
return res, nil
|
||||
}
|
||||
if h.receivedForwardSecurePacket {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if h.secureAEAD != nil {
|
||||
res, err := h.secureAEAD.Open(dst, src, packetNumber, associatedData)
|
||||
if err == nil {
|
||||
h.receivedSecurePacket = true
|
||||
return res, nil
|
||||
}
|
||||
if h.receivedSecurePacket {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return (&crypto.NullAEAD{}).Open(dst, src, packetNumber, associatedData)
|
||||
}
|
||||
|
||||
// Seal a message, call LockForSealing() before!
|
||||
func (h *CryptoSetup) Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte {
|
||||
if h.receivedForwardSecurePacket {
|
||||
return h.forwardSecureAEAD.Seal(dst, src, packetNumber, associatedData)
|
||||
} else if h.secureAEAD != nil {
|
||||
return h.secureAEAD.Seal(dst, src, packetNumber, associatedData)
|
||||
} else {
|
||||
return (&crypto.NullAEAD{}).Seal(dst, src, packetNumber, associatedData)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *CryptoSetup) isInchoateCHLO(cryptoData map[Tag][]byte) bool {
|
||||
scid, ok := cryptoData[TagSCID]
|
||||
if !ok || !bytes.Equal(h.scfg.ID, scid) {
|
||||
return true
|
||||
}
|
||||
if _, ok := cryptoData[TagPUBS]; !ok {
|
||||
return true
|
||||
}
|
||||
if err := h.scfg.stkSource.VerifyToken(h.ip, cryptoData[TagSTK]); err != nil {
|
||||
utils.Infof("STK invalid: %s", err.Error())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *CryptoSetup) handleInchoateCHLO(sni string, chlo []byte, cryptoData map[Tag][]byte) ([]byte, error) {
|
||||
if len(chlo) < protocol.ClientHelloMinimumSize {
|
||||
return nil, qerr.Error(qerr.CryptoInvalidValueLength, "CHLO too small")
|
||||
}
|
||||
|
||||
token, err := h.scfg.stkSource.NewToken(h.ip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
replyMap := map[Tag][]byte{
|
||||
TagSCFG: h.scfg.Get(),
|
||||
TagSTK: token,
|
||||
TagSVID: []byte("quic-go"),
|
||||
}
|
||||
|
||||
if h.scfg.stkSource.VerifyToken(h.ip, cryptoData[TagSTK]) == nil {
|
||||
proof, err := h.scfg.Sign(sni, chlo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
commonSetHashes := cryptoData[TagCCS]
|
||||
cachedCertsHashes := cryptoData[TagCCRT]
|
||||
|
||||
certCompressed, err := h.scfg.GetCertsCompressed(sni, commonSetHashes, cachedCertsHashes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Token was valid, send more details
|
||||
replyMap[TagPROF] = proof
|
||||
replyMap[TagCERT] = certCompressed
|
||||
}
|
||||
|
||||
var serverReply bytes.Buffer
|
||||
WriteHandshakeMessage(&serverReply, TagREJ, replyMap)
|
||||
return serverReply.Bytes(), nil
|
||||
}
|
||||
|
||||
func (h *CryptoSetup) handleCHLO(sni string, data []byte, cryptoData map[Tag][]byte) ([]byte, error) {
|
||||
// We have a CHLO matching our server config, we can continue with the 0-RTT handshake
|
||||
sharedSecret, err := h.scfg.kex.CalculateSharedKey(cryptoData[TagPUBS])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
h.mutex.Lock()
|
||||
defer h.mutex.Unlock()
|
||||
|
||||
certUncompressed, err := h.scfg.signer.GetLeafCert(sni)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nonce := make([]byte, 32)
|
||||
if _, err = rand.Read(nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
h.diversificationNonce = make([]byte, 32)
|
||||
if _, err = rand.Read(h.diversificationNonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
h.secureAEAD, err = h.keyDerivation(
|
||||
false,
|
||||
sharedSecret,
|
||||
cryptoData[TagNONC],
|
||||
h.connID,
|
||||
data,
|
||||
h.scfg.Get(),
|
||||
certUncompressed,
|
||||
h.diversificationNonce,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Generate a new curve instance to derive the forward secure key
|
||||
var fsNonce bytes.Buffer
|
||||
fsNonce.Write(cryptoData[TagNONC])
|
||||
fsNonce.Write(nonce)
|
||||
ephermalKex := h.keyExchange()
|
||||
ephermalSharedSecret, err := ephermalKex.CalculateSharedKey(cryptoData[TagPUBS])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h.forwardSecureAEAD, err = h.keyDerivation(
|
||||
true,
|
||||
ephermalSharedSecret,
|
||||
fsNonce.Bytes(),
|
||||
h.connID,
|
||||
data,
|
||||
h.scfg.Get(),
|
||||
certUncompressed,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = h.connectionParametersManager.SetFromMap(cryptoData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
replyMap := h.connectionParametersManager.GetSHLOMap()
|
||||
// add crypto parameters
|
||||
replyMap[TagPUBS] = ephermalKex.PublicKey()
|
||||
replyMap[TagSNO] = nonce
|
||||
replyMap[TagVER] = protocol.SupportedVersionsAsTags
|
||||
|
||||
var reply bytes.Buffer
|
||||
WriteHandshakeMessage(&reply, TagSHLO, replyMap)
|
||||
|
||||
h.aeadChanged <- struct{}{}
|
||||
|
||||
return reply.Bytes(), nil
|
||||
}
|
||||
|
||||
// DiversificationNonce returns a diversification nonce if required in the next packet to be Seal'ed. See LockForSealing()!
|
||||
func (h *CryptoSetup) DiversificationNonce() []byte {
|
||||
if h.receivedForwardSecurePacket || h.secureAEAD == nil {
|
||||
return nil
|
||||
}
|
||||
return h.diversificationNonce
|
||||
}
|
||||
|
||||
// LockForSealing should be called before Seal(). It is needed so that diversification nonces can be obtained before packets are sealed, and the AEADs are not changed in the meantime.
|
||||
func (h *CryptoSetup) LockForSealing() {
|
||||
h.mutex.RLock()
|
||||
}
|
||||
|
||||
// UnlockForSealing should be called after Seal() is complete, see LockForSealing().
|
||||
func (h *CryptoSetup) UnlockForSealing() {
|
||||
h.mutex.RUnlock()
|
||||
}
|
||||
|
||||
// HandshakeComplete returns true after the first forward secure packet was received form the client.
|
||||
func (h *CryptoSetup) HandshakeComplete() bool {
|
||||
return h.receivedForwardSecurePacket
|
||||
}
|
203
cmd/gost/vendor/github.com/lucas-clemente/quic-go/packet_packer.go
generated
vendored
203
cmd/gost/vendor/github.com/lucas-clemente/quic-go/packet_packer.go
generated
vendored
@ -1,203 +0,0 @@
|
||||
package quic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/frames"
|
||||
"github.com/lucas-clemente/quic-go/handshake"
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
)
|
||||
|
||||
type packedPacket struct {
|
||||
number protocol.PacketNumber
|
||||
raw []byte
|
||||
frames []frames.Frame
|
||||
}
|
||||
|
||||
type packetPacker struct {
|
||||
connectionID protocol.ConnectionID
|
||||
version protocol.VersionNumber
|
||||
cryptoSetup *handshake.CryptoSetup
|
||||
|
||||
packetNumberGenerator *packetNumberGenerator
|
||||
|
||||
connectionParametersManager *handshake.ConnectionParametersManager
|
||||
|
||||
streamFramer *streamFramer
|
||||
controlFrames []frames.Frame
|
||||
}
|
||||
|
||||
func newPacketPacker(connectionID protocol.ConnectionID, cryptoSetup *handshake.CryptoSetup, connectionParametersHandler *handshake.ConnectionParametersManager, streamFramer *streamFramer, version protocol.VersionNumber) *packetPacker {
|
||||
return &packetPacker{
|
||||
cryptoSetup: cryptoSetup,
|
||||
connectionID: connectionID,
|
||||
connectionParametersManager: connectionParametersHandler,
|
||||
version: version,
|
||||
streamFramer: streamFramer,
|
||||
packetNumberGenerator: newPacketNumberGenerator(protocol.SkipPacketAveragePeriodLength),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *packetPacker) PackConnectionClose(frame *frames.ConnectionCloseFrame, leastUnacked protocol.PacketNumber) (*packedPacket, error) {
|
||||
return p.packPacket(nil, []frames.Frame{frame}, leastUnacked, true, false)
|
||||
}
|
||||
|
||||
func (p *packetPacker) PackPacket(stopWaitingFrame *frames.StopWaitingFrame, controlFrames []frames.Frame, leastUnacked protocol.PacketNumber, maySendOnlyAck bool) (*packedPacket, error) {
|
||||
return p.packPacket(stopWaitingFrame, controlFrames, leastUnacked, false, maySendOnlyAck)
|
||||
}
|
||||
|
||||
func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, controlFrames []frames.Frame, leastUnacked protocol.PacketNumber, onlySendOneControlFrame, maySendOnlyAck bool) (*packedPacket, error) {
|
||||
if len(controlFrames) > 0 {
|
||||
p.controlFrames = append(p.controlFrames, controlFrames...)
|
||||
}
|
||||
|
||||
currentPacketNumber := p.packetNumberGenerator.Peek()
|
||||
|
||||
// cryptoSetup needs to be locked here, so that the AEADs are not changed between
|
||||
// calling DiversificationNonce() and Seal().
|
||||
p.cryptoSetup.LockForSealing()
|
||||
defer p.cryptoSetup.UnlockForSealing()
|
||||
|
||||
packetNumberLen := protocol.GetPacketNumberLengthForPublicHeader(currentPacketNumber, leastUnacked)
|
||||
responsePublicHeader := &PublicHeader{
|
||||
ConnectionID: p.connectionID,
|
||||
PacketNumber: currentPacketNumber,
|
||||
PacketNumberLen: packetNumberLen,
|
||||
TruncateConnectionID: p.connectionParametersManager.TruncateConnectionID(),
|
||||
DiversificationNonce: p.cryptoSetup.DiversificationNonce(),
|
||||
}
|
||||
|
||||
publicHeaderLength, err := responsePublicHeader.GetLength()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if stopWaitingFrame != nil {
|
||||
stopWaitingFrame.PacketNumber = currentPacketNumber
|
||||
stopWaitingFrame.PacketNumberLen = packetNumberLen
|
||||
}
|
||||
|
||||
var payloadFrames []frames.Frame
|
||||
if onlySendOneControlFrame {
|
||||
payloadFrames = []frames.Frame{controlFrames[0]}
|
||||
} else {
|
||||
payloadFrames, err = p.composeNextPacket(stopWaitingFrame, publicHeaderLength)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we have enough frames to send
|
||||
if len(payloadFrames) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
// Don't send out packets that only contain a StopWaitingFrame
|
||||
if !onlySendOneControlFrame && len(payloadFrames) == 1 && stopWaitingFrame != nil {
|
||||
return nil, nil
|
||||
}
|
||||
// Don't send out packets that only contain an ACK (plus optional STOP_WAITING), if requested
|
||||
if !maySendOnlyAck {
|
||||
if len(payloadFrames) == 1 {
|
||||
if _, ok := payloadFrames[0].(*frames.AckFrame); ok {
|
||||
return nil, nil
|
||||
}
|
||||
} else if len(payloadFrames) == 2 && stopWaitingFrame != nil {
|
||||
if _, ok := payloadFrames[1].(*frames.AckFrame); ok {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
raw := getPacketBuffer()
|
||||
buffer := bytes.NewBuffer(raw)
|
||||
|
||||
if err = responsePublicHeader.WritePublicHeader(buffer, p.version); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
payloadStartIndex := buffer.Len()
|
||||
|
||||
for _, frame := range payloadFrames {
|
||||
err := frame.Write(buffer, p.version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if protocol.ByteCount(buffer.Len()+12) > protocol.MaxPacketSize {
|
||||
return nil, errors.New("PacketPacker BUG: packet too large")
|
||||
}
|
||||
|
||||
raw = raw[0:buffer.Len()]
|
||||
p.cryptoSetup.Seal(raw[payloadStartIndex:payloadStartIndex], raw[payloadStartIndex:], currentPacketNumber, raw[:payloadStartIndex])
|
||||
raw = raw[0 : buffer.Len()+12]
|
||||
|
||||
num := p.packetNumberGenerator.Pop()
|
||||
if num != currentPacketNumber {
|
||||
return nil, errors.New("PacketPacker BUG: Peeked and Popped packet numbers do not match.")
|
||||
}
|
||||
|
||||
return &packedPacket{
|
||||
number: currentPacketNumber,
|
||||
raw: raw,
|
||||
frames: payloadFrames,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *packetPacker) composeNextPacket(stopWaitingFrame *frames.StopWaitingFrame, publicHeaderLength protocol.ByteCount) ([]frames.Frame, error) {
|
||||
var payloadLength protocol.ByteCount
|
||||
var payloadFrames []frames.Frame
|
||||
|
||||
maxFrameSize := protocol.MaxFrameAndPublicHeaderSize - publicHeaderLength
|
||||
|
||||
if stopWaitingFrame != nil {
|
||||
payloadFrames = append(payloadFrames, stopWaitingFrame)
|
||||
minLength, err := stopWaitingFrame.MinLength(p.version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payloadLength += minLength
|
||||
}
|
||||
|
||||
for len(p.controlFrames) > 0 {
|
||||
frame := p.controlFrames[len(p.controlFrames)-1]
|
||||
minLength, _ := frame.MinLength(p.version) // controlFrames does not contain any StopWaitingFrames. So it will *never* return an error
|
||||
if payloadLength+minLength > maxFrameSize {
|
||||
break
|
||||
}
|
||||
payloadFrames = append(payloadFrames, frame)
|
||||
payloadLength += minLength
|
||||
p.controlFrames = p.controlFrames[:len(p.controlFrames)-1]
|
||||
}
|
||||
|
||||
if payloadLength > maxFrameSize {
|
||||
return nil, fmt.Errorf("Packet Packer BUG: packet payload (%d) too large (%d)", payloadLength, maxFrameSize)
|
||||
}
|
||||
|
||||
// temporarily increase the maxFrameSize by 2 bytes
|
||||
// this leads to a properly sized packet in all cases, since we do all the packet length calculations with StreamFrames that have the DataLen set
|
||||
// however, for the last StreamFrame in the packet, we can omit the DataLen, thus saving 2 bytes and yielding a packet of exactly the correct size
|
||||
maxFrameSize += 2
|
||||
|
||||
fs := p.streamFramer.PopStreamFrames(maxFrameSize - payloadLength)
|
||||
if len(fs) != 0 {
|
||||
fs[len(fs)-1].DataLenPresent = false
|
||||
}
|
||||
|
||||
// TODO: Simplify
|
||||
for _, f := range fs {
|
||||
payloadFrames = append(payloadFrames, f)
|
||||
}
|
||||
|
||||
for b := p.streamFramer.PopBlockedFrame(); b != nil; b = p.streamFramer.PopBlockedFrame() {
|
||||
p.controlFrames = append(p.controlFrames, b)
|
||||
}
|
||||
|
||||
return payloadFrames, nil
|
||||
}
|
||||
|
||||
func (p *packetPacker) QueueControlFrameForNextPacket(f frames.Frame) {
|
||||
p.controlFrames = append(p.controlFrames, f)
|
||||
}
|
90
cmd/gost/vendor/github.com/lucas-clemente/quic-go/protocol/server_parameters.go
generated
vendored
90
cmd/gost/vendor/github.com/lucas-clemente/quic-go/protocol/server_parameters.go
generated
vendored
@ -1,90 +0,0 @@
|
||||
package protocol
|
||||
|
||||
import "time"
|
||||
|
||||
// DefaultMaxCongestionWindow is the default for the max congestion window
|
||||
const DefaultMaxCongestionWindow PacketNumber = 1000
|
||||
|
||||
// InitialCongestionWindow is the initial congestion window in QUIC packets
|
||||
const InitialCongestionWindow PacketNumber = 32
|
||||
|
||||
// MaxUndecryptablePackets limits the number of undecryptable packets that a
|
||||
// session queues for later until it sends a public reset.
|
||||
const MaxUndecryptablePackets = 10
|
||||
|
||||
// AckSendDelay is the maximal time delay applied to packets containing only ACKs
|
||||
const AckSendDelay = 5 * time.Millisecond
|
||||
|
||||
// ReceiveStreamFlowControlWindow is the stream-level flow control window for receiving data
|
||||
// This is the value that Google servers are using
|
||||
const ReceiveStreamFlowControlWindow ByteCount = (1 << 20) // 1 MB
|
||||
|
||||
// ReceiveConnectionFlowControlWindow is the stream-level flow control window for receiving data
|
||||
// This is the value that Google servers are using
|
||||
const ReceiveConnectionFlowControlWindow ByteCount = (1 << 20) * 1.5 // 1.5 MB
|
||||
|
||||
// MaxStreamsPerConnection is the maximum value accepted for the number of streams per connection
|
||||
const MaxStreamsPerConnection = 100
|
||||
|
||||
// MaxIncomingDynamicStreams is the maximum value accepted for the incoming number of dynamic streams per connection
|
||||
const MaxIncomingDynamicStreams = 100
|
||||
|
||||
// MaxStreamsMultiplier is the slack the client is allowed for the maximum number of streams per connection, needed e.g. when packets are out of order or dropped. The minimum of this procentual increase and the absolute increment specified by MaxStreamsMinimumIncrement is used.
|
||||
const MaxStreamsMultiplier = 1.1
|
||||
|
||||
// MaxStreamsMinimumIncrement is the slack the client is allowed for the maximum number of streams per connection, needed e.g. when packets are out of order or dropped. The minimum of this absolute increment and the procentual increase specified by MaxStreamsMultiplier is used.
|
||||
const MaxStreamsMinimumIncrement = 10
|
||||
|
||||
// MaxNewStreamIDDelta is the maximum difference between and a newly opened Stream and the highest StreamID that a client has ever opened
|
||||
// note that the number of streams is half this value, since the client can only open streams with open StreamID
|
||||
const MaxNewStreamIDDelta = 4 * MaxStreamsPerConnection
|
||||
|
||||
// MaxSessionUnprocessedPackets is the max number of packets stored in each session that are not yet processed.
|
||||
const MaxSessionUnprocessedPackets = DefaultMaxCongestionWindow
|
||||
|
||||
// RetransmissionThreshold + 1 is the number of times a packet has to be NACKed so that it gets retransmitted
|
||||
const RetransmissionThreshold = 3
|
||||
|
||||
// SkipPacketAveragePeriodLength is the average period length in which one packet number is skipped to prevent an Optimistic ACK attack
|
||||
const SkipPacketAveragePeriodLength PacketNumber = 500
|
||||
|
||||
// MaxTrackedSkippedPackets is the maximum number of skipped packet numbers the SentPacketHandler keep track of for Optimistic ACK attack mitigation
|
||||
const MaxTrackedSkippedPackets = 10
|
||||
|
||||
// STKExpiryTimeSec is the valid time of a source address token in seconds
|
||||
const STKExpiryTimeSec = 24 * 60 * 60
|
||||
|
||||
// MaxTrackedSentPackets is maximum number of sent packets saved for either later retransmission or entropy calculation
|
||||
const MaxTrackedSentPackets = 2 * DefaultMaxCongestionWindow
|
||||
|
||||
// MaxTrackedReceivedPackets is the maximum number of received packets saved for doing the entropy calculations
|
||||
const MaxTrackedReceivedPackets = 2 * DefaultMaxCongestionWindow
|
||||
|
||||
// MaxStreamFrameSorterGaps is the maximum number of gaps between received StreamFrames
|
||||
// prevents DOS attacks against the streamFrameSorter
|
||||
const MaxStreamFrameSorterGaps = 1000
|
||||
|
||||
// CryptoMaxParams is the upper limit for the number of parameters in a crypto message.
|
||||
// Value taken from Chrome.
|
||||
const CryptoMaxParams = 128
|
||||
|
||||
// CryptoParameterMaxLength is the upper limit for the length of a parameter in a crypto message.
|
||||
const CryptoParameterMaxLength = 2000
|
||||
|
||||
// EphermalKeyLifetime is the lifetime of the ephermal key during the handshake, see handshake.getEphermalKEX.
|
||||
const EphermalKeyLifetime = time.Minute
|
||||
|
||||
// InitialIdleTimeout is the timeout before the handshake succeeds.
|
||||
const InitialIdleTimeout = 5 * time.Second
|
||||
|
||||
// DefaultIdleTimeout is the default idle timeout.
|
||||
const DefaultIdleTimeout = 30 * time.Second
|
||||
|
||||
// MaxIdleTimeout is the maximum idle timeout that can be negotiated.
|
||||
const MaxIdleTimeout = 1 * time.Minute
|
||||
|
||||
// MaxTimeForCryptoHandshake is the default timeout for a connection until the crypto handshake succeeds.
|
||||
const MaxTimeForCryptoHandshake = 10 * time.Second
|
||||
|
||||
// NumCachedCertificates is the number of cached compressed certificate chains, each taking ~1K space
|
||||
const NumCachedCertificates = 128
|
67
cmd/gost/vendor/github.com/lucas-clemente/quic-go/protocol/version.go
generated
vendored
67
cmd/gost/vendor/github.com/lucas-clemente/quic-go/protocol/version.go
generated
vendored
@ -1,67 +0,0 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// VersionNumber is a version number as int
|
||||
type VersionNumber int
|
||||
|
||||
// The version numbers, making grepping easier
|
||||
const (
|
||||
Version34 VersionNumber = 34 + iota
|
||||
Version35
|
||||
Version36
|
||||
VersionWhatever = 0 // for when the version doesn't matter
|
||||
)
|
||||
|
||||
// SupportedVersions lists the versions that the server supports
|
||||
var SupportedVersions = []VersionNumber{
|
||||
Version34, Version35, Version36,
|
||||
}
|
||||
|
||||
// SupportedVersionsAsTags is needed for the SHLO crypto message
|
||||
var SupportedVersionsAsTags []byte
|
||||
|
||||
// SupportedVersionsAsString is needed for the Alt-Scv HTTP header
|
||||
var SupportedVersionsAsString string
|
||||
|
||||
// VersionNumberToTag maps version numbers ('32') to tags ('Q032')
|
||||
func VersionNumberToTag(vn VersionNumber) uint32 {
|
||||
v := uint32(vn)
|
||||
return 'Q' + ((v/100%10)+'0')<<8 + ((v/10%10)+'0')<<16 + ((v%10)+'0')<<24
|
||||
}
|
||||
|
||||
// VersionTagToNumber is built from VersionNumberToTag in init()
|
||||
func VersionTagToNumber(v uint32) VersionNumber {
|
||||
return VersionNumber(((v>>8)&0xff-'0')*100 + ((v>>16)&0xff-'0')*10 + ((v>>24)&0xff - '0'))
|
||||
}
|
||||
|
||||
// IsSupportedVersion returns true if the server supports this version
|
||||
func IsSupportedVersion(v VersionNumber) bool {
|
||||
for _, t := range SupportedVersions {
|
||||
if t == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func init() {
|
||||
var b bytes.Buffer
|
||||
for _, v := range SupportedVersions {
|
||||
s := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(s, VersionNumberToTag(v))
|
||||
b.Write(s)
|
||||
}
|
||||
SupportedVersionsAsTags = b.Bytes()
|
||||
|
||||
for i := len(SupportedVersions) - 1; i >= 0; i-- {
|
||||
SupportedVersionsAsString += strconv.Itoa(int(SupportedVersions[i]))
|
||||
if i != 0 {
|
||||
SupportedVersionsAsString += ","
|
||||
}
|
||||
}
|
||||
}
|
178
cmd/gost/vendor/github.com/lucas-clemente/quic-go/public_header.go
generated
vendored
178
cmd/gost/vendor/github.com/lucas-clemente/quic-go/public_header.go
generated
vendored
@ -1,178 +0,0 @@
|
||||
package quic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
"github.com/lucas-clemente/quic-go/qerr"
|
||||
"github.com/lucas-clemente/quic-go/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
errPacketNumberLenNotSet = errors.New("PublicHeader: PacketNumberLen not set")
|
||||
errResetAndVersionFlagSet = errors.New("PublicHeader: Reset Flag and Version Flag should not be set at the same time")
|
||||
errReceivedTruncatedConnectionID = qerr.Error(qerr.InvalidPacketHeader, "receiving packets with truncated ConnectionID is not supported")
|
||||
errInvalidConnectionID = qerr.Error(qerr.InvalidPacketHeader, "connection ID cannot be 0")
|
||||
errGetLengthOnlyForRegularPackets = errors.New("PublicHeader: GetLength can only be called for regular packets")
|
||||
)
|
||||
|
||||
// The PublicHeader of a QUIC packet
|
||||
type PublicHeader struct {
|
||||
Raw []byte
|
||||
ConnectionID protocol.ConnectionID
|
||||
VersionFlag bool
|
||||
ResetFlag bool
|
||||
TruncateConnectionID bool
|
||||
PacketNumberLen protocol.PacketNumberLen
|
||||
PacketNumber protocol.PacketNumber
|
||||
VersionNumber protocol.VersionNumber
|
||||
DiversificationNonce []byte
|
||||
}
|
||||
|
||||
// WritePublicHeader writes a public header
|
||||
func (h *PublicHeader) WritePublicHeader(b *bytes.Buffer, version protocol.VersionNumber) error {
|
||||
publicFlagByte := uint8(0x00)
|
||||
if h.VersionFlag && h.ResetFlag {
|
||||
return errResetAndVersionFlagSet
|
||||
}
|
||||
if h.VersionFlag {
|
||||
publicFlagByte |= 0x01
|
||||
}
|
||||
if h.ResetFlag {
|
||||
publicFlagByte |= 0x02
|
||||
}
|
||||
if !h.TruncateConnectionID {
|
||||
publicFlagByte |= 0x08
|
||||
}
|
||||
|
||||
if len(h.DiversificationNonce) > 0 {
|
||||
if len(h.DiversificationNonce) != 32 {
|
||||
return errors.New("invalid diversification nonce length")
|
||||
}
|
||||
publicFlagByte |= 0x04
|
||||
}
|
||||
|
||||
if !h.ResetFlag && !h.VersionFlag {
|
||||
switch h.PacketNumberLen {
|
||||
case protocol.PacketNumberLen1:
|
||||
publicFlagByte |= 0x00
|
||||
case protocol.PacketNumberLen2:
|
||||
publicFlagByte |= 0x10
|
||||
case protocol.PacketNumberLen4:
|
||||
publicFlagByte |= 0x20
|
||||
case protocol.PacketNumberLen6:
|
||||
publicFlagByte |= 0x30
|
||||
}
|
||||
}
|
||||
|
||||
b.WriteByte(publicFlagByte)
|
||||
|
||||
if !h.TruncateConnectionID {
|
||||
utils.WriteUint64(b, uint64(h.ConnectionID))
|
||||
}
|
||||
|
||||
if len(h.DiversificationNonce) > 0 {
|
||||
b.Write(h.DiversificationNonce)
|
||||
}
|
||||
|
||||
if !h.ResetFlag && !h.VersionFlag {
|
||||
switch h.PacketNumberLen {
|
||||
case protocol.PacketNumberLen1:
|
||||
b.WriteByte(uint8(h.PacketNumber))
|
||||
case protocol.PacketNumberLen2:
|
||||
utils.WriteUint16(b, uint16(h.PacketNumber))
|
||||
case protocol.PacketNumberLen4:
|
||||
utils.WriteUint32(b, uint32(h.PacketNumber))
|
||||
case protocol.PacketNumberLen6:
|
||||
utils.WriteUint48(b, uint64(h.PacketNumber))
|
||||
default:
|
||||
return errPacketNumberLenNotSet
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParsePublicHeader parses a QUIC packet's public header
|
||||
func ParsePublicHeader(b io.ByteReader) (*PublicHeader, error) {
|
||||
header := &PublicHeader{}
|
||||
|
||||
// First byte
|
||||
publicFlagByte, err := b.ReadByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
header.VersionFlag = publicFlagByte&0x01 > 0
|
||||
header.ResetFlag = publicFlagByte&0x02 > 0
|
||||
|
||||
// TODO: activate this check once Chrome sends the correct value
|
||||
// see https://github.com/lucas-clemente/quic-go/issues/232
|
||||
// if publicFlagByte&0x04 > 0 {
|
||||
// return nil, errors.New("diversification nonces should only be sent by servers")
|
||||
// }
|
||||
|
||||
if publicFlagByte&0x08 == 0 {
|
||||
return nil, errReceivedTruncatedConnectionID
|
||||
}
|
||||
|
||||
switch publicFlagByte & 0x30 {
|
||||
case 0x30:
|
||||
header.PacketNumberLen = protocol.PacketNumberLen6
|
||||
case 0x20:
|
||||
header.PacketNumberLen = protocol.PacketNumberLen4
|
||||
case 0x10:
|
||||
header.PacketNumberLen = protocol.PacketNumberLen2
|
||||
case 0x00:
|
||||
header.PacketNumberLen = protocol.PacketNumberLen1
|
||||
}
|
||||
|
||||
// Connection ID
|
||||
connID, err := utils.ReadUint64(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
header.ConnectionID = protocol.ConnectionID(connID)
|
||||
if header.ConnectionID == 0 {
|
||||
return nil, errInvalidConnectionID
|
||||
}
|
||||
|
||||
// Version (optional)
|
||||
if header.VersionFlag {
|
||||
var versionTag uint32
|
||||
versionTag, err = utils.ReadUint32(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
header.VersionNumber = protocol.VersionTagToNumber(versionTag)
|
||||
}
|
||||
|
||||
// Packet number
|
||||
packetNumber, err := utils.ReadUintN(b, uint8(header.PacketNumberLen))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
header.PacketNumber = protocol.PacketNumber(packetNumber)
|
||||
|
||||
return header, nil
|
||||
}
|
||||
|
||||
// GetLength gets the length of the publicHeader in bytes
|
||||
// can only be called for regular packets
|
||||
func (h *PublicHeader) GetLength() (protocol.ByteCount, error) {
|
||||
if h.VersionFlag || h.ResetFlag {
|
||||
return 0, errGetLengthOnlyForRegularPackets
|
||||
}
|
||||
|
||||
length := protocol.ByteCount(1) // 1 byte for public flags
|
||||
if h.PacketNumberLen != protocol.PacketNumberLen1 && h.PacketNumberLen != protocol.PacketNumberLen2 && h.PacketNumberLen != protocol.PacketNumberLen4 && h.PacketNumberLen != protocol.PacketNumberLen6 {
|
||||
return 0, errPacketNumberLenNotSet
|
||||
}
|
||||
if !h.TruncateConnectionID {
|
||||
length += 8 // 8 bytes for the connection ID
|
||||
}
|
||||
length += protocol.ByteCount(len(h.DiversificationNonce))
|
||||
length += protocol.ByteCount(h.PacketNumberLen)
|
||||
return length, nil
|
||||
}
|
24
cmd/gost/vendor/github.com/lucas-clemente/quic-go/public_reset.go
generated
vendored
24
cmd/gost/vendor/github.com/lucas-clemente/quic-go/public_reset.go
generated
vendored
@ -1,24 +0,0 @@
|
||||
package quic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/handshake"
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
"github.com/lucas-clemente/quic-go/utils"
|
||||
)
|
||||
|
||||
func writePublicReset(connectionID protocol.ConnectionID, rejectedPacketNumber protocol.PacketNumber, nonceProof uint64) []byte {
|
||||
b := &bytes.Buffer{}
|
||||
b.WriteByte(0x0a)
|
||||
utils.WriteUint64(b, uint64(connectionID))
|
||||
utils.WriteUint32(b, uint32(handshake.TagPRST))
|
||||
utils.WriteUint32(b, 2)
|
||||
utils.WriteUint32(b, uint32(handshake.TagRNON))
|
||||
utils.WriteUint32(b, 8)
|
||||
utils.WriteUint32(b, uint32(handshake.TagRSEQ))
|
||||
utils.WriteUint32(b, 16)
|
||||
utils.WriteUint64(b, nonceProof)
|
||||
utils.WriteUint64(b, uint64(rejectedPacketNumber))
|
||||
return b.Bytes()
|
||||
}
|
205
cmd/gost/vendor/github.com/lucas-clemente/quic-go/server.go
generated
vendored
205
cmd/gost/vendor/github.com/lucas-clemente/quic-go/server.go
generated
vendored
@ -1,205 +0,0 @@
|
||||
package quic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/crypto"
|
||||
"github.com/lucas-clemente/quic-go/handshake"
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
"github.com/lucas-clemente/quic-go/qerr"
|
||||
"github.com/lucas-clemente/quic-go/utils"
|
||||
)
|
||||
|
||||
// packetHandler handles packets
|
||||
type packetHandler interface {
|
||||
handlePacket(*receivedPacket)
|
||||
run()
|
||||
Close(error) error
|
||||
}
|
||||
|
||||
// A Server of QUIC
|
||||
type Server struct {
|
||||
addr *net.UDPAddr
|
||||
|
||||
conn *net.UDPConn
|
||||
connMutex sync.Mutex
|
||||
|
||||
signer crypto.Signer
|
||||
scfg *handshake.ServerConfig
|
||||
|
||||
sessions map[protocol.ConnectionID]packetHandler
|
||||
sessionsMutex sync.RWMutex
|
||||
|
||||
streamCallback StreamCallback
|
||||
|
||||
newSession func(conn connection, v protocol.VersionNumber, connectionID protocol.ConnectionID, sCfg *handshake.ServerConfig, streamCallback StreamCallback, closeCallback closeCallback) (packetHandler, error)
|
||||
}
|
||||
|
||||
// NewServer makes a new server
|
||||
func NewServer(addr string, tlsConfig *tls.Config, cb StreamCallback) (*Server, error) {
|
||||
signer, err := crypto.NewProofSource(tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kex, err := crypto.NewCurve25519KEX()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scfg, err := handshake.NewServerConfig(kex, signer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Server{
|
||||
addr: udpAddr,
|
||||
signer: signer,
|
||||
scfg: scfg,
|
||||
streamCallback: cb,
|
||||
sessions: map[protocol.ConnectionID]packetHandler{},
|
||||
newSession: newSession,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListenAndServe listens and serves a connection
|
||||
func (s *Server) ListenAndServe() error {
|
||||
conn, err := net.ListenUDP("udp", s.addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.Serve(conn)
|
||||
}
|
||||
|
||||
// Serve on an existing UDP connection.
|
||||
func (s *Server) Serve(conn *net.UDPConn) error {
|
||||
s.connMutex.Lock()
|
||||
s.conn = conn
|
||||
s.connMutex.Unlock()
|
||||
|
||||
for {
|
||||
data := getPacketBuffer()
|
||||
data = data[:protocol.MaxPacketSize]
|
||||
n, remoteAddr, err := conn.ReadFromUDP(data)
|
||||
if err != nil {
|
||||
if strings.HasSuffix(err.Error(), "use of closed network connection") {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
data = data[:n]
|
||||
if err := s.handlePacket(conn, remoteAddr, data); err != nil {
|
||||
utils.Errorf("error handling packet: %s", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close the server
|
||||
func (s *Server) Close() error {
|
||||
s.sessionsMutex.Lock()
|
||||
for _, session := range s.sessions {
|
||||
if session != nil {
|
||||
s.sessionsMutex.Unlock()
|
||||
_ = session.Close(nil)
|
||||
s.sessionsMutex.Lock()
|
||||
}
|
||||
}
|
||||
s.sessionsMutex.Unlock()
|
||||
|
||||
s.connMutex.Lock()
|
||||
conn := s.conn
|
||||
s.conn = nil
|
||||
s.connMutex.Unlock()
|
||||
|
||||
if conn == nil {
|
||||
return nil
|
||||
}
|
||||
return conn.Close()
|
||||
}
|
||||
|
||||
func (s *Server) handlePacket(conn *net.UDPConn, remoteAddr *net.UDPAddr, packet []byte) error {
|
||||
if protocol.ByteCount(len(packet)) > protocol.MaxPacketSize {
|
||||
return qerr.PacketTooLarge
|
||||
}
|
||||
|
||||
rcvTime := time.Now()
|
||||
|
||||
r := bytes.NewReader(packet)
|
||||
|
||||
hdr, err := ParsePublicHeader(r)
|
||||
if err != nil {
|
||||
return qerr.Error(qerr.InvalidPacketHeader, err.Error())
|
||||
}
|
||||
hdr.Raw = packet[:len(packet)-r.Len()]
|
||||
|
||||
// Send Version Negotiation Packet if the client is speaking a different protocol version
|
||||
if hdr.VersionFlag && !protocol.IsSupportedVersion(hdr.VersionNumber) {
|
||||
utils.Infof("Client offered version %d, sending VersionNegotiationPacket", hdr.VersionNumber)
|
||||
_, err = conn.WriteToUDP(composeVersionNegotiation(hdr.ConnectionID), remoteAddr)
|
||||
return err
|
||||
}
|
||||
|
||||
s.sessionsMutex.RLock()
|
||||
session, ok := s.sessions[hdr.ConnectionID]
|
||||
s.sessionsMutex.RUnlock()
|
||||
|
||||
if !ok {
|
||||
utils.Infof("Serving new connection: %x, version %d from %v", hdr.ConnectionID, hdr.VersionNumber, remoteAddr)
|
||||
session, err = s.newSession(
|
||||
&udpConn{conn: conn, currentAddr: remoteAddr},
|
||||
hdr.VersionNumber,
|
||||
hdr.ConnectionID,
|
||||
s.scfg,
|
||||
s.streamCallback,
|
||||
s.closeCallback,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go session.run()
|
||||
s.sessionsMutex.Lock()
|
||||
s.sessions[hdr.ConnectionID] = session
|
||||
s.sessionsMutex.Unlock()
|
||||
}
|
||||
if session == nil {
|
||||
// Late packet for closed session
|
||||
return nil
|
||||
}
|
||||
session.handlePacket(&receivedPacket{
|
||||
remoteAddr: remoteAddr,
|
||||
publicHeader: hdr,
|
||||
data: packet[len(packet)-r.Len():],
|
||||
rcvTime: rcvTime,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) closeCallback(id protocol.ConnectionID) {
|
||||
s.sessionsMutex.Lock()
|
||||
s.sessions[id] = nil
|
||||
s.sessionsMutex.Unlock()
|
||||
}
|
||||
|
||||
func composeVersionNegotiation(connectionID protocol.ConnectionID) []byte {
|
||||
fullReply := &bytes.Buffer{}
|
||||
responsePublicHeader := PublicHeader{
|
||||
ConnectionID: connectionID,
|
||||
PacketNumber: 1,
|
||||
VersionFlag: true,
|
||||
}
|
||||
err := responsePublicHeader.WritePublicHeader(fullReply, protocol.Version35)
|
||||
if err != nil {
|
||||
utils.Errorf("error composing version negotiation packet: %s", err.Error())
|
||||
}
|
||||
fullReply.Write(protocol.SupportedVersionsAsTags)
|
||||
return fullReply.Bytes()
|
||||
}
|
673
cmd/gost/vendor/github.com/lucas-clemente/quic-go/session.go
generated
vendored
673
cmd/gost/vendor/github.com/lucas-clemente/quic-go/session.go
generated
vendored
@ -1,673 +0,0 @@
|
||||
package quic
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/ackhandler"
|
||||
"github.com/lucas-clemente/quic-go/flowcontrol"
|
||||
"github.com/lucas-clemente/quic-go/frames"
|
||||
"github.com/lucas-clemente/quic-go/handshake"
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
"github.com/lucas-clemente/quic-go/qerr"
|
||||
"github.com/lucas-clemente/quic-go/utils"
|
||||
)
|
||||
|
||||
type unpacker interface {
|
||||
Unpack(publicHeaderBinary []byte, hdr *PublicHeader, data []byte) (*unpackedPacket, error)
|
||||
}
|
||||
|
||||
type receivedPacket struct {
|
||||
remoteAddr interface{}
|
||||
publicHeader *PublicHeader
|
||||
data []byte
|
||||
rcvTime time.Time
|
||||
}
|
||||
|
||||
var (
|
||||
errRstStreamOnInvalidStream = errors.New("RST_STREAM received for unknown stream")
|
||||
errWindowUpdateOnClosedStream = errors.New("WINDOW_UPDATE received for an already closed stream")
|
||||
)
|
||||
|
||||
// StreamCallback gets a stream frame and returns a reply frame
|
||||
type StreamCallback func(*Session, utils.Stream)
|
||||
|
||||
// closeCallback is called when a session is closed
|
||||
type closeCallback func(id protocol.ConnectionID)
|
||||
|
||||
// A Session is a QUIC session
|
||||
type Session struct {
|
||||
connectionID protocol.ConnectionID
|
||||
version protocol.VersionNumber
|
||||
|
||||
streamCallback StreamCallback
|
||||
closeCallback closeCallback
|
||||
|
||||
conn connection
|
||||
|
||||
streamsMap *streamsMap
|
||||
|
||||
sentPacketHandler ackhandler.SentPacketHandler
|
||||
receivedPacketHandler ackhandler.ReceivedPacketHandler
|
||||
streamFramer *streamFramer
|
||||
|
||||
flowControlManager flowcontrol.FlowControlManager
|
||||
|
||||
unpacker unpacker
|
||||
packer *packetPacker
|
||||
|
||||
cryptoSetup *handshake.CryptoSetup
|
||||
|
||||
receivedPackets chan *receivedPacket
|
||||
sendingScheduled chan struct{}
|
||||
// closeChan is used to notify the run loop that it should terminate.
|
||||
// If the value is not nil, the error is sent as a CONNECTION_CLOSE.
|
||||
closeChan chan *qerr.QuicError
|
||||
closed uint32 // atomic bool
|
||||
|
||||
undecryptablePackets []*receivedPacket
|
||||
aeadChanged chan struct{}
|
||||
|
||||
delayedAckOriginTime time.Time
|
||||
|
||||
connectionParametersManager *handshake.ConnectionParametersManager
|
||||
|
||||
lastRcvdPacketNumber protocol.PacketNumber
|
||||
// Used to calculate the next packet number from the truncated wire
|
||||
// representation, and sent back in public reset packets
|
||||
largestRcvdPacketNumber protocol.PacketNumber
|
||||
|
||||
sessionCreationTime time.Time
|
||||
lastNetworkActivityTime time.Time
|
||||
|
||||
timer *time.Timer
|
||||
currentDeadline time.Time
|
||||
timerRead bool
|
||||
}
|
||||
|
||||
// newSession makes a new session
|
||||
func newSession(conn connection, v protocol.VersionNumber, connectionID protocol.ConnectionID, sCfg *handshake.ServerConfig, streamCallback StreamCallback, closeCallback closeCallback) (packetHandler, error) {
|
||||
connectionParametersManager := handshake.NewConnectionParamatersManager()
|
||||
flowControlManager := flowcontrol.NewFlowControlManager(connectionParametersManager)
|
||||
|
||||
var sentPacketHandler ackhandler.SentPacketHandler
|
||||
var receivedPacketHandler ackhandler.ReceivedPacketHandler
|
||||
|
||||
sentPacketHandler = ackhandler.NewSentPacketHandler()
|
||||
receivedPacketHandler = ackhandler.NewReceivedPacketHandler()
|
||||
|
||||
now := time.Now()
|
||||
session := &Session{
|
||||
conn: conn,
|
||||
connectionID: connectionID,
|
||||
version: v,
|
||||
|
||||
streamCallback: streamCallback,
|
||||
closeCallback: closeCallback,
|
||||
|
||||
connectionParametersManager: connectionParametersManager,
|
||||
sentPacketHandler: sentPacketHandler,
|
||||
receivedPacketHandler: receivedPacketHandler,
|
||||
flowControlManager: flowControlManager,
|
||||
|
||||
receivedPackets: make(chan *receivedPacket, protocol.MaxSessionUnprocessedPackets),
|
||||
closeChan: make(chan *qerr.QuicError, 1),
|
||||
sendingScheduled: make(chan struct{}, 1),
|
||||
undecryptablePackets: make([]*receivedPacket, 0, protocol.MaxUndecryptablePackets),
|
||||
aeadChanged: make(chan struct{}, 1),
|
||||
|
||||
timer: time.NewTimer(0),
|
||||
lastNetworkActivityTime: now,
|
||||
sessionCreationTime: now,
|
||||
}
|
||||
|
||||
session.streamsMap = newStreamsMap(session.newStream)
|
||||
|
||||
cryptoStream, _ := session.GetOrOpenStream(1)
|
||||
var err error
|
||||
session.cryptoSetup, err = handshake.NewCryptoSetup(connectionID, conn.RemoteAddr().IP, v, sCfg, cryptoStream, session.connectionParametersManager, session.aeadChanged)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
session.streamFramer = newStreamFramer(session.streamsMap, flowControlManager)
|
||||
session.packer = newPacketPacker(connectionID, session.cryptoSetup, session.connectionParametersManager, session.streamFramer, v)
|
||||
session.unpacker = &packetUnpacker{aead: session.cryptoSetup, version: v}
|
||||
|
||||
return session, err
|
||||
}
|
||||
|
||||
// run the session main loop
|
||||
func (s *Session) run() {
|
||||
// Start the crypto stream handler
|
||||
go func() {
|
||||
if err := s.cryptoSetup.HandleCryptoStream(); err != nil {
|
||||
s.Close(err)
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
// Close immediately if requested
|
||||
select {
|
||||
case errForConnClose := <-s.closeChan:
|
||||
if errForConnClose != nil {
|
||||
s.sendConnectionClose(errForConnClose)
|
||||
}
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
s.maybeResetTimer()
|
||||
|
||||
var err error
|
||||
select {
|
||||
case errForConnClose := <-s.closeChan:
|
||||
if errForConnClose != nil {
|
||||
s.sendConnectionClose(errForConnClose)
|
||||
}
|
||||
return
|
||||
case <-s.timer.C:
|
||||
s.timerRead = true
|
||||
// We do all the interesting stuff after the switch statement, so
|
||||
// nothing to see here.
|
||||
case <-s.sendingScheduled:
|
||||
// We do all the interesting stuff after the switch statement, so
|
||||
// nothing to see here.
|
||||
case p := <-s.receivedPackets:
|
||||
err = s.handlePacketImpl(p)
|
||||
if qErr, ok := err.(*qerr.QuicError); ok && qErr.ErrorCode == qerr.DecryptionFailure {
|
||||
s.tryQueueingUndecryptablePacket(p)
|
||||
continue
|
||||
}
|
||||
// This is a bit unclean, but works properly, since the packet always
|
||||
// begins with the public header and we never copy it.
|
||||
putPacketBuffer(p.publicHeader.Raw)
|
||||
if s.delayedAckOriginTime.IsZero() {
|
||||
s.delayedAckOriginTime = p.rcvTime
|
||||
}
|
||||
case <-s.aeadChanged:
|
||||
s.tryDecryptingQueuedPackets()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
s.Close(err)
|
||||
}
|
||||
|
||||
if err := s.sendPacket(); err != nil {
|
||||
s.Close(err)
|
||||
}
|
||||
if time.Now().Sub(s.lastNetworkActivityTime) >= s.idleTimeout() {
|
||||
s.Close(qerr.Error(qerr.NetworkIdleTimeout, "No recent network activity."))
|
||||
}
|
||||
if !s.cryptoSetup.HandshakeComplete() && time.Now().Sub(s.sessionCreationTime) >= protocol.MaxTimeForCryptoHandshake {
|
||||
s.Close(qerr.Error(qerr.NetworkIdleTimeout, "Crypto handshake did not complete in time."))
|
||||
}
|
||||
s.garbageCollectStreams()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) maybeResetTimer() {
|
||||
nextDeadline := s.lastNetworkActivityTime.Add(s.idleTimeout())
|
||||
|
||||
if !s.delayedAckOriginTime.IsZero() {
|
||||
nextDeadline = utils.MinTime(nextDeadline, s.delayedAckOriginTime.Add(protocol.AckSendDelay))
|
||||
}
|
||||
if rtoTime := s.sentPacketHandler.TimeOfFirstRTO(); !rtoTime.IsZero() {
|
||||
nextDeadline = utils.MinTime(nextDeadline, rtoTime)
|
||||
}
|
||||
if !s.cryptoSetup.HandshakeComplete() {
|
||||
handshakeDeadline := s.sessionCreationTime.Add(protocol.MaxTimeForCryptoHandshake)
|
||||
nextDeadline = utils.MinTime(nextDeadline, handshakeDeadline)
|
||||
}
|
||||
|
||||
if nextDeadline.Equal(s.currentDeadline) {
|
||||
// No need to reset the timer
|
||||
return
|
||||
}
|
||||
|
||||
// We need to drain the timer if the value from its channel was not read yet.
|
||||
// See https://groups.google.com/forum/#!topic/golang-dev/c9UUfASVPoU
|
||||
if !s.timer.Stop() && !s.timerRead {
|
||||
<-s.timer.C
|
||||
}
|
||||
s.timer.Reset(nextDeadline.Sub(time.Now()))
|
||||
|
||||
s.timerRead = false
|
||||
s.currentDeadline = nextDeadline
|
||||
}
|
||||
|
||||
func (s *Session) idleTimeout() time.Duration {
|
||||
if s.cryptoSetup.HandshakeComplete() {
|
||||
return s.connectionParametersManager.GetIdleConnectionStateLifetime()
|
||||
}
|
||||
return protocol.InitialIdleTimeout
|
||||
}
|
||||
|
||||
func (s *Session) handlePacketImpl(p *receivedPacket) error {
|
||||
if p.rcvTime.IsZero() {
|
||||
// To simplify testing
|
||||
p.rcvTime = time.Now()
|
||||
}
|
||||
|
||||
s.lastNetworkActivityTime = p.rcvTime
|
||||
hdr := p.publicHeader
|
||||
data := p.data
|
||||
|
||||
// Calculate packet number
|
||||
hdr.PacketNumber = protocol.InferPacketNumber(
|
||||
hdr.PacketNumberLen,
|
||||
s.largestRcvdPacketNumber,
|
||||
hdr.PacketNumber,
|
||||
)
|
||||
if utils.Debug() {
|
||||
utils.Debugf("<- Reading packet 0x%x (%d bytes) for connection %x", hdr.PacketNumber, len(data)+len(hdr.Raw), hdr.ConnectionID)
|
||||
}
|
||||
|
||||
// TODO: Only do this after authenticating
|
||||
s.conn.setCurrentRemoteAddr(p.remoteAddr)
|
||||
|
||||
packet, err := s.unpacker.Unpack(hdr.Raw, hdr, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.lastRcvdPacketNumber = hdr.PacketNumber
|
||||
// Only do this after decrypting, so we are sure the packet is not attacker-controlled
|
||||
s.largestRcvdPacketNumber = utils.MaxPacketNumber(s.largestRcvdPacketNumber, hdr.PacketNumber)
|
||||
|
||||
err = s.receivedPacketHandler.ReceivedPacket(hdr.PacketNumber)
|
||||
// ignore duplicate packets
|
||||
if err == ackhandler.ErrDuplicatePacket {
|
||||
utils.Infof("Ignoring packet 0x%x due to ErrDuplicatePacket", hdr.PacketNumber)
|
||||
return nil
|
||||
}
|
||||
// ignore packets with packet numbers smaller than the LeastUnacked of a StopWaiting
|
||||
if err == ackhandler.ErrPacketSmallerThanLastStopWaiting {
|
||||
utils.Infof("Ignoring packet 0x%x due to ErrPacketSmallerThanLastStopWaiting", hdr.PacketNumber)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.handleFrames(packet.frames)
|
||||
}
|
||||
|
||||
func (s *Session) handleFrames(fs []frames.Frame) error {
|
||||
for _, ff := range fs {
|
||||
var err error
|
||||
frames.LogFrame(ff, false)
|
||||
switch frame := ff.(type) {
|
||||
case *frames.StreamFrame:
|
||||
err = s.handleStreamFrame(frame)
|
||||
// TODO: send RstStreamFrame
|
||||
case *frames.AckFrame:
|
||||
err = s.handleAckFrame(frame)
|
||||
case *frames.ConnectionCloseFrame:
|
||||
s.closeImpl(qerr.Error(frame.ErrorCode, frame.ReasonPhrase), true)
|
||||
case *frames.GoawayFrame:
|
||||
err = errors.New("unimplemented: handling GOAWAY frames")
|
||||
case *frames.StopWaitingFrame:
|
||||
err = s.receivedPacketHandler.ReceivedStopWaiting(frame)
|
||||
case *frames.RstStreamFrame:
|
||||
err = s.handleRstStreamFrame(frame)
|
||||
case *frames.WindowUpdateFrame:
|
||||
err = s.handleWindowUpdateFrame(frame)
|
||||
case *frames.BlockedFrame:
|
||||
case *frames.PingFrame:
|
||||
default:
|
||||
return errors.New("Session BUG: unexpected frame type")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
switch err {
|
||||
case ackhandler.ErrDuplicateOrOutOfOrderAck:
|
||||
// Can happen e.g. when packets thought missing arrive late
|
||||
case errRstStreamOnInvalidStream:
|
||||
// Can happen when RST_STREAMs arrive early or late (?)
|
||||
utils.Errorf("Ignoring error in session: %s", err.Error())
|
||||
case errWindowUpdateOnClosedStream:
|
||||
// Can happen when we already sent the last StreamFrame with the FinBit, but the client already sent a WindowUpdate for this Stream
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handlePacket is called by the server with a new packet
|
||||
func (s *Session) handlePacket(p *receivedPacket) {
|
||||
// Discard packets once the amount of queued packets is larger than
|
||||
// the channel size, protocol.MaxSessionUnprocessedPackets
|
||||
select {
|
||||
case s.receivedPackets <- p:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) handleStreamFrame(frame *frames.StreamFrame) error {
|
||||
str, err := s.streamsMap.GetOrOpenStream(frame.StreamID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if str == nil {
|
||||
// Stream is closed, ignore
|
||||
return nil
|
||||
}
|
||||
err = str.AddStreamFrame(frame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Session) handleWindowUpdateFrame(frame *frames.WindowUpdateFrame) error {
|
||||
if frame.StreamID != 0 {
|
||||
str, err := s.streamsMap.GetOrOpenStream(frame.StreamID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if str == nil {
|
||||
return errWindowUpdateOnClosedStream
|
||||
}
|
||||
}
|
||||
_, err := s.flowControlManager.UpdateWindow(frame.StreamID, frame.ByteOffset)
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Handle frame.byteOffset
|
||||
func (s *Session) handleRstStreamFrame(frame *frames.RstStreamFrame) error {
|
||||
str, err := s.streamsMap.GetOrOpenStream(frame.StreamID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if str == nil {
|
||||
return errRstStreamOnInvalidStream
|
||||
}
|
||||
s.closeStreamWithError(str, fmt.Errorf("RST_STREAM received with code %d", frame.ErrorCode))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Session) handleAckFrame(frame *frames.AckFrame) error {
|
||||
if err := s.sentPacketHandler.ReceivedAck(frame, s.lastRcvdPacketNumber, s.lastNetworkActivityTime); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close the connection. If err is nil it will be set to qerr.PeerGoingAway.
|
||||
func (s *Session) Close(e error) error {
|
||||
return s.closeImpl(e, false)
|
||||
}
|
||||
|
||||
func (s *Session) closeImpl(e error, remoteClose bool) error {
|
||||
// Only close once
|
||||
if !atomic.CompareAndSwapUint32(&s.closed, 0, 1) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if e == nil {
|
||||
e = qerr.PeerGoingAway
|
||||
}
|
||||
|
||||
quicErr := qerr.ToQuicError(e)
|
||||
|
||||
// Don't log 'normal' reasons
|
||||
if quicErr.ErrorCode == qerr.PeerGoingAway || quicErr.ErrorCode == qerr.NetworkIdleTimeout {
|
||||
utils.Infof("Closing connection %x", s.connectionID)
|
||||
} else {
|
||||
utils.Errorf("Closing session with error: %s", e.Error())
|
||||
}
|
||||
|
||||
s.closeStreamsWithError(quicErr)
|
||||
s.closeCallback(s.connectionID)
|
||||
|
||||
if remoteClose {
|
||||
// If this is a remote close we don't need to send a CONNECTION_CLOSE
|
||||
s.closeChan <- nil
|
||||
return nil
|
||||
}
|
||||
|
||||
if quicErr.ErrorCode == qerr.DecryptionFailure {
|
||||
// If we send a public reset, don't send a CONNECTION_CLOSE
|
||||
s.closeChan <- nil
|
||||
return s.sendPublicReset(s.lastRcvdPacketNumber)
|
||||
}
|
||||
s.closeChan <- quicErr
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Session) closeStreamsWithError(err error) {
|
||||
s.streamsMap.Iterate(func(str *stream) (bool, error) {
|
||||
s.closeStreamWithError(str, err)
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Session) closeStreamWithError(str *stream, err error) {
|
||||
str.RegisterError(err)
|
||||
}
|
||||
|
||||
func (s *Session) sendPacket() error {
|
||||
// Repeatedly try sending until we don't have any more data, or run out of the congestion window
|
||||
for {
|
||||
err := s.sentPacketHandler.CheckForError()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Do this before checking the congestion, since we might de-congestionize here :)
|
||||
s.sentPacketHandler.MaybeQueueRTOs()
|
||||
|
||||
if !s.sentPacketHandler.SendingAllowed() {
|
||||
return nil
|
||||
}
|
||||
|
||||
var controlFrames []frames.Frame
|
||||
|
||||
// check for retransmissions first
|
||||
for {
|
||||
retransmitPacket := s.sentPacketHandler.DequeuePacketForRetransmission()
|
||||
if retransmitPacket == nil {
|
||||
break
|
||||
}
|
||||
utils.Debugf("\tDequeueing retransmission for packet 0x%x", retransmitPacket.PacketNumber)
|
||||
|
||||
// resend the frames that were in the packet
|
||||
controlFrames = append(controlFrames, retransmitPacket.GetControlFramesForRetransmission()...)
|
||||
for _, streamFrame := range retransmitPacket.GetStreamFramesForRetransmission() {
|
||||
s.streamFramer.AddFrameForRetransmission(streamFrame)
|
||||
}
|
||||
}
|
||||
|
||||
windowUpdateFrames, err := s.getWindowUpdateFrames()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, wuf := range windowUpdateFrames {
|
||||
controlFrames = append(controlFrames, wuf)
|
||||
}
|
||||
|
||||
ack, err := s.receivedPacketHandler.GetAckFrame(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ack != nil {
|
||||
controlFrames = append(controlFrames, ack)
|
||||
}
|
||||
|
||||
// Check whether we are allowed to send a packet containing only an ACK
|
||||
maySendOnlyAck := time.Now().Sub(s.delayedAckOriginTime) > protocol.AckSendDelay
|
||||
if runtime.GOOS == "windows" {
|
||||
maySendOnlyAck = true
|
||||
}
|
||||
|
||||
hasRetransmission := s.streamFramer.HasFramesForRetransmission()
|
||||
|
||||
var stopWaitingFrame *frames.StopWaitingFrame
|
||||
if ack != nil || hasRetransmission {
|
||||
stopWaitingFrame = s.sentPacketHandler.GetStopWaitingFrame(hasRetransmission)
|
||||
}
|
||||
packet, err := s.packer.PackPacket(stopWaitingFrame, controlFrames, s.sentPacketHandler.GetLeastUnacked(), maySendOnlyAck)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if packet == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pop the ACK frame now that we are sure we're gonna send it
|
||||
_, err = s.receivedPacketHandler.GetAckFrame(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, f := range windowUpdateFrames {
|
||||
s.packer.QueueControlFrameForNextPacket(f)
|
||||
}
|
||||
|
||||
err = s.sentPacketHandler.SentPacket(&ackhandler.Packet{
|
||||
PacketNumber: packet.number,
|
||||
Frames: packet.frames,
|
||||
Length: protocol.ByteCount(len(packet.raw)),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.logPacket(packet)
|
||||
s.delayedAckOriginTime = time.Time{}
|
||||
|
||||
err = s.conn.write(packet.raw)
|
||||
putPacketBuffer(packet.raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) sendConnectionClose(quicErr *qerr.QuicError) error {
|
||||
packet, err := s.packer.PackConnectionClose(&frames.ConnectionCloseFrame{ErrorCode: quicErr.ErrorCode, ReasonPhrase: quicErr.ErrorMessage}, s.sentPacketHandler.GetLeastUnacked())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if packet == nil {
|
||||
return errors.New("Session BUG: expected packet not to be nil")
|
||||
}
|
||||
s.logPacket(packet)
|
||||
return s.conn.write(packet.raw)
|
||||
}
|
||||
|
||||
func (s *Session) logPacket(packet *packedPacket) {
|
||||
if !utils.Debug() {
|
||||
// We don't need to allocate the slices for calling the format functions
|
||||
return
|
||||
}
|
||||
if utils.Debug() {
|
||||
utils.Debugf("-> Sending packet 0x%x (%d bytes)", packet.number, len(packet.raw))
|
||||
for _, frame := range packet.frames {
|
||||
frames.LogFrame(frame, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrOpenStream either returns an existing stream, a newly opened stream, or nil if a stream with the provided ID is already closed.
|
||||
// Newly opened streams should only originate from the client. To open a stream from the server, OpenStream should be used.
|
||||
func (s *Session) GetOrOpenStream(id protocol.StreamID) (utils.Stream, error) {
|
||||
return s.streamsMap.GetOrOpenStream(id)
|
||||
}
|
||||
|
||||
// OpenStream opens a stream from the server's side
|
||||
func (s *Session) OpenStream(id protocol.StreamID) (utils.Stream, error) {
|
||||
return s.streamsMap.OpenStream(id)
|
||||
}
|
||||
|
||||
func (s *Session) newStreamImpl(id protocol.StreamID) (*stream, error) {
|
||||
return s.streamsMap.GetOrOpenStream(id)
|
||||
}
|
||||
|
||||
func (s *Session) newStream(id protocol.StreamID) (*stream, error) {
|
||||
stream, err := newStream(id, s.scheduleSending, s.flowControlManager)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: find a better solution for determining which streams contribute to connection level flow control
|
||||
if id == 1 || id == 3 {
|
||||
s.flowControlManager.NewStream(id, false)
|
||||
} else {
|
||||
s.flowControlManager.NewStream(id, true)
|
||||
}
|
||||
|
||||
s.streamCallback(s, stream)
|
||||
|
||||
return stream, nil
|
||||
}
|
||||
|
||||
// garbageCollectStreams goes through all streams and removes EOF'ed streams
|
||||
// from the streams map.
|
||||
func (s *Session) garbageCollectStreams() {
|
||||
s.streamsMap.Iterate(func(str *stream) (bool, error) {
|
||||
id := str.StreamID()
|
||||
if str.finished() {
|
||||
err := s.streamsMap.RemoveStream(id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
s.flowControlManager.RemoveStream(id)
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Session) sendPublicReset(rejectedPacketNumber protocol.PacketNumber) error {
|
||||
utils.Infof("Sending public reset for connection %x, packet number %d", s.connectionID, rejectedPacketNumber)
|
||||
return s.conn.write(writePublicReset(s.connectionID, rejectedPacketNumber, 0))
|
||||
}
|
||||
|
||||
// scheduleSending signals that we have data for sending
|
||||
func (s *Session) scheduleSending() {
|
||||
select {
|
||||
case s.sendingScheduled <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) tryQueueingUndecryptablePacket(p *receivedPacket) {
|
||||
if s.cryptoSetup.HandshakeComplete() {
|
||||
return
|
||||
}
|
||||
utils.Infof("Queueing packet 0x%x for later decryption", p.publicHeader.PacketNumber)
|
||||
if len(s.undecryptablePackets)+1 >= protocol.MaxUndecryptablePackets {
|
||||
s.Close(qerr.Error(qerr.DecryptionFailure, "too many undecryptable packets received"))
|
||||
}
|
||||
s.undecryptablePackets = append(s.undecryptablePackets, p)
|
||||
}
|
||||
|
||||
func (s *Session) tryDecryptingQueuedPackets() {
|
||||
for _, p := range s.undecryptablePackets {
|
||||
s.handlePacket(p)
|
||||
}
|
||||
s.undecryptablePackets = s.undecryptablePackets[:0]
|
||||
}
|
||||
|
||||
func (s *Session) getWindowUpdateFrames() ([]*frames.WindowUpdateFrame, error) {
|
||||
updates := s.flowControlManager.GetWindowUpdates()
|
||||
res := make([]*frames.WindowUpdateFrame, len(updates))
|
||||
for i, u := range updates {
|
||||
res[i] = &frames.WindowUpdateFrame{StreamID: u.StreamID, ByteOffset: u.Offset}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// RemoteAddr returns the net.UDPAddr of the client
|
||||
func (s *Session) RemoteAddr() *net.UDPAddr {
|
||||
return s.conn.RemoteAddr()
|
||||
}
|
267
cmd/gost/vendor/github.com/lucas-clemente/quic-go/stream.go
generated
vendored
267
cmd/gost/vendor/github.com/lucas-clemente/quic-go/stream.go
generated
vendored
@ -1,267 +0,0 @@
|
||||
package quic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/flowcontrol"
|
||||
"github.com/lucas-clemente/quic-go/frames"
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
"github.com/lucas-clemente/quic-go/qerr"
|
||||
"github.com/lucas-clemente/quic-go/utils"
|
||||
)
|
||||
|
||||
// A Stream assembles the data from StreamFrames and provides a super-convenient Read-Interface
|
||||
//
|
||||
// Read() and Write() may be called concurrently, but multiple calls to Read() or Write() individually must be synchronized manually.
|
||||
type stream struct {
|
||||
streamID protocol.StreamID
|
||||
onData func()
|
||||
|
||||
readPosInFrame int
|
||||
writeOffset protocol.ByteCount
|
||||
readOffset protocol.ByteCount
|
||||
|
||||
// Once set, err must not be changed!
|
||||
err error
|
||||
mutex sync.Mutex
|
||||
|
||||
// eof is set if we are finished reading
|
||||
eof int32 // really a bool
|
||||
// closed is set when we are finished writing
|
||||
closed int32 // really a bool
|
||||
|
||||
frameQueue *streamFrameSorter
|
||||
newFrameOrErrCond sync.Cond
|
||||
|
||||
dataForWriting []byte
|
||||
finSent bool
|
||||
doneWritingOrErrCond sync.Cond
|
||||
|
||||
flowControlManager flowcontrol.FlowControlManager
|
||||
}
|
||||
|
||||
// newStream creates a new Stream
|
||||
func newStream(StreamID protocol.StreamID, onData func(), flowControlManager flowcontrol.FlowControlManager) (*stream, error) {
|
||||
s := &stream{
|
||||
onData: onData,
|
||||
streamID: StreamID,
|
||||
flowControlManager: flowControlManager,
|
||||
frameQueue: newStreamFrameSorter(),
|
||||
}
|
||||
|
||||
s.newFrameOrErrCond.L = &s.mutex
|
||||
s.doneWritingOrErrCond.L = &s.mutex
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Read implements io.Reader. It is not thread safe!
|
||||
func (s *stream) Read(p []byte) (int, error) {
|
||||
if atomic.LoadInt32(&s.eof) != 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
bytesRead := 0
|
||||
for bytesRead < len(p) {
|
||||
s.mutex.Lock()
|
||||
frame := s.frameQueue.Head()
|
||||
|
||||
if frame == nil && bytesRead > 0 {
|
||||
s.mutex.Unlock()
|
||||
return bytesRead, s.err
|
||||
}
|
||||
|
||||
var err error
|
||||
for {
|
||||
// Stop waiting on errors
|
||||
if s.err != nil {
|
||||
err = s.err
|
||||
break
|
||||
}
|
||||
if frame != nil {
|
||||
s.readPosInFrame = int(s.readOffset - frame.Offset)
|
||||
break
|
||||
}
|
||||
s.newFrameOrErrCond.Wait()
|
||||
frame = s.frameQueue.Head()
|
||||
}
|
||||
s.mutex.Unlock()
|
||||
// Here, either frame != nil xor err != nil
|
||||
|
||||
if frame == nil {
|
||||
atomic.StoreInt32(&s.eof, 1)
|
||||
// We have an err and no data, return the error
|
||||
return bytesRead, err
|
||||
}
|
||||
|
||||
m := utils.Min(len(p)-bytesRead, int(frame.DataLen())-s.readPosInFrame)
|
||||
|
||||
if bytesRead > len(p) {
|
||||
return bytesRead, fmt.Errorf("BUG: bytesRead (%d) > len(p) (%d) in stream.Read", bytesRead, len(p))
|
||||
}
|
||||
if s.readPosInFrame > int(frame.DataLen()) {
|
||||
return bytesRead, fmt.Errorf("BUG: readPosInFrame (%d) > frame.DataLen (%d) in stream.Read", s.readPosInFrame, frame.DataLen())
|
||||
}
|
||||
copy(p[bytesRead:], frame.Data[s.readPosInFrame:])
|
||||
|
||||
s.readPosInFrame += m
|
||||
bytesRead += m
|
||||
s.readOffset += protocol.ByteCount(m)
|
||||
|
||||
s.flowControlManager.AddBytesRead(s.streamID, protocol.ByteCount(m))
|
||||
s.onData() // so that a possible WINDOW_UPDATE is sent
|
||||
|
||||
if s.readPosInFrame >= int(frame.DataLen()) {
|
||||
fin := frame.FinBit
|
||||
s.mutex.Lock()
|
||||
s.frameQueue.Pop()
|
||||
s.mutex.Unlock()
|
||||
if fin {
|
||||
atomic.StoreInt32(&s.eof, 1)
|
||||
return bytesRead, io.EOF
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bytesRead, nil
|
||||
}
|
||||
|
||||
func (s *stream) Write(p []byte) (int, error) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
if s.err != nil {
|
||||
return 0, s.err
|
||||
}
|
||||
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
s.dataForWriting = make([]byte, len(p))
|
||||
copy(s.dataForWriting, p)
|
||||
|
||||
s.onData()
|
||||
|
||||
for s.dataForWriting != nil && s.err == nil {
|
||||
s.doneWritingOrErrCond.Wait()
|
||||
}
|
||||
|
||||
if s.err != nil {
|
||||
return 0, s.err
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (s *stream) lenOfDataForWriting() protocol.ByteCount {
|
||||
s.mutex.Lock()
|
||||
l := protocol.ByteCount(len(s.dataForWriting))
|
||||
s.mutex.Unlock()
|
||||
return l
|
||||
}
|
||||
|
||||
func (s *stream) getDataForWriting(maxBytes protocol.ByteCount) []byte {
|
||||
s.mutex.Lock()
|
||||
if s.dataForWriting == nil {
|
||||
s.mutex.Unlock()
|
||||
return nil
|
||||
}
|
||||
var ret []byte
|
||||
if protocol.ByteCount(len(s.dataForWriting)) > maxBytes {
|
||||
ret = s.dataForWriting[:maxBytes]
|
||||
s.dataForWriting = s.dataForWriting[maxBytes:]
|
||||
} else {
|
||||
ret = s.dataForWriting
|
||||
s.dataForWriting = nil
|
||||
s.doneWritingOrErrCond.Signal()
|
||||
}
|
||||
s.writeOffset += protocol.ByteCount(len(ret))
|
||||
s.mutex.Unlock()
|
||||
return ret
|
||||
}
|
||||
|
||||
// Close implements io.Closer
|
||||
func (s *stream) Close() error {
|
||||
atomic.StoreInt32(&s.closed, 1)
|
||||
s.onData()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stream) shouldSendFin() bool {
|
||||
s.mutex.Lock()
|
||||
res := atomic.LoadInt32(&s.closed) != 0 && !s.finSent && s.err == nil && s.dataForWriting == nil
|
||||
s.mutex.Unlock()
|
||||
return res
|
||||
}
|
||||
|
||||
func (s *stream) sentFin() {
|
||||
s.mutex.Lock()
|
||||
s.finSent = true
|
||||
s.mutex.Unlock()
|
||||
}
|
||||
|
||||
// AddStreamFrame adds a new stream frame
|
||||
func (s *stream) AddStreamFrame(frame *frames.StreamFrame) error {
|
||||
maxOffset := frame.Offset + frame.DataLen()
|
||||
err := s.flowControlManager.UpdateHighestReceived(s.streamID, maxOffset)
|
||||
|
||||
if err == flowcontrol.ErrStreamFlowControlViolation {
|
||||
return qerr.FlowControlReceivedTooMuchData
|
||||
}
|
||||
if err == flowcontrol.ErrConnectionFlowControlViolation {
|
||||
return qerr.FlowControlReceivedTooMuchData
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
err = s.frameQueue.Push(frame)
|
||||
if err != nil && err != errDuplicateStreamData {
|
||||
return err
|
||||
}
|
||||
s.newFrameOrErrCond.Signal()
|
||||
return nil
|
||||
}
|
||||
|
||||
// CloseRemote makes the stream receive a "virtual" FIN stream frame at a given offset
|
||||
func (s *stream) CloseRemote(offset protocol.ByteCount) {
|
||||
s.AddStreamFrame(&frames.StreamFrame{FinBit: true, Offset: offset})
|
||||
}
|
||||
|
||||
// RegisterError is called by session to indicate that an error occurred and the
|
||||
// stream should be closed.
|
||||
func (s *stream) RegisterError(err error) {
|
||||
atomic.StoreInt32(&s.closed, 1)
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
if s.err != nil { // s.err must not be changed!
|
||||
return
|
||||
}
|
||||
s.err = err
|
||||
s.doneWritingOrErrCond.Signal()
|
||||
s.newFrameOrErrCond.Signal()
|
||||
}
|
||||
|
||||
func (s *stream) finishedReading() bool {
|
||||
return atomic.LoadInt32(&s.eof) != 0
|
||||
}
|
||||
|
||||
func (s *stream) finishedWriting() bool {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
return s.err != nil || (atomic.LoadInt32(&s.closed) != 0 && s.finSent)
|
||||
}
|
||||
|
||||
func (s *stream) finished() bool {
|
||||
return s.finishedReading() && s.finishedWriting()
|
||||
}
|
||||
|
||||
func (s *stream) StreamID() protocol.StreamID {
|
||||
return s.streamID
|
||||
}
|
119
cmd/gost/vendor/github.com/lucas-clemente/quic-go/stream_frame_sorter.go
generated
vendored
119
cmd/gost/vendor/github.com/lucas-clemente/quic-go/stream_frame_sorter.go
generated
vendored
@ -1,119 +0,0 @@
|
||||
package quic
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/frames"
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
"github.com/lucas-clemente/quic-go/qerr"
|
||||
"github.com/lucas-clemente/quic-go/utils"
|
||||
)
|
||||
|
||||
type streamFrameSorter struct {
|
||||
queuedFrames map[protocol.ByteCount]*frames.StreamFrame
|
||||
readPosition protocol.ByteCount
|
||||
gaps *utils.ByteIntervalList
|
||||
}
|
||||
|
||||
var (
|
||||
errTooManyGapsInReceivedStreamData = errors.New("Too many gaps in received StreamFrame data")
|
||||
errDuplicateStreamData = errors.New("Overlapping Stream Data")
|
||||
errEmptyStreamData = errors.New("Stream Data empty")
|
||||
)
|
||||
|
||||
func newStreamFrameSorter() *streamFrameSorter {
|
||||
s := streamFrameSorter{
|
||||
gaps: utils.NewByteIntervalList(),
|
||||
queuedFrames: make(map[protocol.ByteCount]*frames.StreamFrame),
|
||||
}
|
||||
s.gaps.PushFront(utils.ByteInterval{Start: 0, End: protocol.MaxByteCount})
|
||||
return &s
|
||||
}
|
||||
|
||||
func (s *streamFrameSorter) Push(frame *frames.StreamFrame) error {
|
||||
_, ok := s.queuedFrames[frame.Offset]
|
||||
if ok {
|
||||
return errDuplicateStreamData
|
||||
}
|
||||
|
||||
start := frame.Offset
|
||||
end := frame.Offset + frame.DataLen()
|
||||
|
||||
if start == end {
|
||||
if frame.FinBit {
|
||||
s.queuedFrames[frame.Offset] = frame
|
||||
return nil
|
||||
}
|
||||
return errEmptyStreamData
|
||||
}
|
||||
|
||||
var foundInGap bool
|
||||
|
||||
for gap := s.gaps.Front(); gap != nil; gap = gap.Next() {
|
||||
// the complete frame lies before or after the gap
|
||||
if end <= gap.Value.Start || start > gap.Value.End {
|
||||
continue
|
||||
}
|
||||
|
||||
if start < gap.Value.Start {
|
||||
return qerr.Error(qerr.OverlappingStreamData, "start of gap in stream chunk")
|
||||
}
|
||||
|
||||
if start < gap.Value.End && end > gap.Value.End {
|
||||
return qerr.Error(qerr.OverlappingStreamData, "end of gap in stream chunk")
|
||||
}
|
||||
|
||||
foundInGap = true
|
||||
|
||||
if start == gap.Value.Start {
|
||||
if end == gap.Value.End {
|
||||
s.gaps.Remove(gap)
|
||||
break
|
||||
}
|
||||
if end < gap.Value.End {
|
||||
gap.Value.Start = end
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if end == gap.Value.End {
|
||||
gap.Value.End = start
|
||||
break
|
||||
}
|
||||
|
||||
if end < gap.Value.End {
|
||||
intv := utils.ByteInterval{Start: end, End: gap.Value.End}
|
||||
s.gaps.InsertAfter(intv, gap)
|
||||
gap.Value.End = start
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !foundInGap {
|
||||
return errDuplicateStreamData
|
||||
}
|
||||
|
||||
if s.gaps.Len() > protocol.MaxStreamFrameSorterGaps {
|
||||
return errTooManyGapsInReceivedStreamData
|
||||
}
|
||||
|
||||
s.queuedFrames[frame.Offset] = frame
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *streamFrameSorter) Pop() *frames.StreamFrame {
|
||||
frame := s.Head()
|
||||
if frame != nil {
|
||||
s.readPosition += frame.DataLen()
|
||||
delete(s.queuedFrames, frame.Offset)
|
||||
}
|
||||
return frame
|
||||
}
|
||||
|
||||
func (s *streamFrameSorter) Head() *frames.StreamFrame {
|
||||
frame, ok := s.queuedFrames[s.readPosition]
|
||||
if ok {
|
||||
return frame
|
||||
}
|
||||
return nil
|
||||
}
|
219
cmd/gost/vendor/github.com/lucas-clemente/quic-go/streams_map.go
generated
vendored
219
cmd/gost/vendor/github.com/lucas-clemente/quic-go/streams_map.go
generated
vendored
@ -1,219 +0,0 @@
|
||||
package quic
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
"github.com/lucas-clemente/quic-go/qerr"
|
||||
"github.com/lucas-clemente/quic-go/utils"
|
||||
)
|
||||
|
||||
type streamsMap struct {
|
||||
mutex sync.RWMutex
|
||||
|
||||
streams map[protocol.StreamID]*stream
|
||||
openStreams []protocol.StreamID
|
||||
|
||||
highestStreamOpenedByClient protocol.StreamID
|
||||
streamsOpenedAfterLastGarbageCollect int
|
||||
|
||||
newStream newStreamLambda
|
||||
maxNumStreams int
|
||||
|
||||
roundRobinIndex int
|
||||
}
|
||||
|
||||
type streamLambda func(*stream) (bool, error)
|
||||
type newStreamLambda func(protocol.StreamID) (*stream, error)
|
||||
|
||||
var (
|
||||
errMapAccess = errors.New("streamsMap: Error accessing the streams map")
|
||||
)
|
||||
|
||||
func newStreamsMap(newStream newStreamLambda) *streamsMap {
|
||||
maxNumStreams := utils.Max(int(float32(protocol.MaxIncomingDynamicStreams)*protocol.MaxStreamsMultiplier), int(protocol.MaxIncomingDynamicStreams))
|
||||
|
||||
return &streamsMap{
|
||||
streams: map[protocol.StreamID]*stream{},
|
||||
openStreams: make([]protocol.StreamID, 0, maxNumStreams),
|
||||
newStream: newStream,
|
||||
maxNumStreams: maxNumStreams,
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrOpenStream either returns an existing stream, a newly opened stream, or nil if a stream with the provided ID is already closed.
|
||||
// Newly opened streams should only originate from the client. To open a stream from the server, OpenStream should be used.
|
||||
func (m *streamsMap) GetOrOpenStream(id protocol.StreamID) (*stream, error) {
|
||||
m.mutex.RLock()
|
||||
s, ok := m.streams[id]
|
||||
m.mutex.RUnlock()
|
||||
if ok {
|
||||
return s, nil // s may be nil
|
||||
}
|
||||
|
||||
// ... we don't have an existing stream, try opening a new one
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
// We need to check whether another invocation has already created a stream (between RUnlock() and Lock()).
|
||||
s, ok = m.streams[id]
|
||||
if ok {
|
||||
return s, nil
|
||||
}
|
||||
if len(m.openStreams) == m.maxNumStreams {
|
||||
return nil, qerr.TooManyOpenStreams
|
||||
}
|
||||
if id%2 == 0 {
|
||||
return nil, qerr.Error(qerr.InvalidStreamID, fmt.Sprintf("attempted to open stream %d from client-side", id))
|
||||
}
|
||||
if id+protocol.MaxNewStreamIDDelta < m.highestStreamOpenedByClient {
|
||||
return nil, qerr.Error(qerr.InvalidStreamID, fmt.Sprintf("attempted to open stream %d, which is a lot smaller than the highest opened stream, %d", id, m.highestStreamOpenedByClient))
|
||||
}
|
||||
|
||||
s, err := m.newStream(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if id > m.highestStreamOpenedByClient {
|
||||
m.highestStreamOpenedByClient = id
|
||||
}
|
||||
|
||||
m.streamsOpenedAfterLastGarbageCollect++
|
||||
if m.streamsOpenedAfterLastGarbageCollect%protocol.MaxNewStreamIDDelta == 0 {
|
||||
m.garbageCollectClosedStreams()
|
||||
}
|
||||
|
||||
m.putStream(s)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// OpenStream opens a stream from the server's side
|
||||
func (m *streamsMap) OpenStream(id protocol.StreamID) (*stream, error) {
|
||||
panic("OpenStream: not implemented")
|
||||
}
|
||||
|
||||
func (m *streamsMap) Iterate(fn streamLambda) error {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
for _, streamID := range m.openStreams {
|
||||
cont, err := m.iterateFunc(streamID, fn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !cont {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RoundRobinIterate executes the streamLambda for every open stream, until the streamLambda returns false
|
||||
// It uses a round-robin-like scheduling to ensure that every stream is considered fairly
|
||||
// It prioritizes the crypto- and the header-stream (StreamIDs 1 and 3)
|
||||
func (m *streamsMap) RoundRobinIterate(fn streamLambda) error {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
numStreams := len(m.openStreams)
|
||||
startIndex := m.roundRobinIndex
|
||||
|
||||
for _, i := range []protocol.StreamID{1, 3} {
|
||||
cont, err := m.iterateFunc(i, fn)
|
||||
if err != nil && err != errMapAccess {
|
||||
return err
|
||||
}
|
||||
if !cont {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < numStreams; i++ {
|
||||
streamID := m.openStreams[(i+startIndex)%numStreams]
|
||||
|
||||
if streamID == 1 || streamID == 3 {
|
||||
continue
|
||||
}
|
||||
|
||||
cont, err := m.iterateFunc(streamID, fn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.roundRobinIndex = (m.roundRobinIndex + 1) % numStreams
|
||||
if !cont {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *streamsMap) iterateFunc(streamID protocol.StreamID, fn streamLambda) (bool, error) {
|
||||
str, ok := m.streams[streamID]
|
||||
if !ok {
|
||||
return true, errMapAccess
|
||||
}
|
||||
if str == nil {
|
||||
return false, fmt.Errorf("BUG: Stream %d is closed, but still in openStreams map", streamID)
|
||||
}
|
||||
return fn(str)
|
||||
}
|
||||
|
||||
func (m *streamsMap) putStream(s *stream) error {
|
||||
id := s.StreamID()
|
||||
if _, ok := m.streams[id]; ok {
|
||||
return fmt.Errorf("a stream with ID %d already exists", id)
|
||||
}
|
||||
|
||||
m.streams[id] = s
|
||||
m.openStreams = append(m.openStreams, id)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Attention: this function must only be called if a mutex has been acquired previously
|
||||
func (m *streamsMap) RemoveStream(id protocol.StreamID) error {
|
||||
s, ok := m.streams[id]
|
||||
if !ok || s == nil {
|
||||
return fmt.Errorf("attempted to remove non-existing stream: %d", id)
|
||||
}
|
||||
|
||||
m.streams[id] = nil
|
||||
|
||||
for i, s := range m.openStreams {
|
||||
if s == id {
|
||||
// delete the streamID from the openStreams slice
|
||||
m.openStreams = m.openStreams[:i+copy(m.openStreams[i:], m.openStreams[i+1:])]
|
||||
// adjust round-robin index, if necessary
|
||||
if i < m.roundRobinIndex {
|
||||
m.roundRobinIndex--
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NumberOfStreams gets the number of open streams
|
||||
func (m *streamsMap) NumberOfStreams() int {
|
||||
m.mutex.RLock()
|
||||
n := len(m.openStreams)
|
||||
m.mutex.RUnlock()
|
||||
return n
|
||||
}
|
||||
|
||||
// garbageCollectClosedStreams deletes nil values in the streams if they are smaller than protocol.MaxNewStreamIDDelta than the highest stream opened by the client
|
||||
// note that this garbage collection is relatively expensive, since it iterates over the whole streams map. It should not be called every time a stream is openend or closed
|
||||
func (m *streamsMap) garbageCollectClosedStreams() {
|
||||
for id, str := range m.streams {
|
||||
if str != nil {
|
||||
continue
|
||||
}
|
||||
if id+protocol.MaxNewStreamIDDelta <= m.highestStreamOpenedByClient {
|
||||
delete(m.streams, id)
|
||||
}
|
||||
}
|
||||
m.streamsOpenedAfterLastGarbageCollect = 0
|
||||
}
|
29
cmd/gost/vendor/github.com/lucas-clemente/quic-go/udp_conn.go
generated
vendored
29
cmd/gost/vendor/github.com/lucas-clemente/quic-go/udp_conn.go
generated
vendored
@ -1,29 +0,0 @@
|
||||
package quic
|
||||
|
||||
import "net"
|
||||
|
||||
type connection interface {
|
||||
write([]byte) error
|
||||
setCurrentRemoteAddr(interface{})
|
||||
RemoteAddr() *net.UDPAddr
|
||||
}
|
||||
|
||||
type udpConn struct {
|
||||
conn *net.UDPConn
|
||||
currentAddr *net.UDPAddr
|
||||
}
|
||||
|
||||
var _ connection = &udpConn{}
|
||||
|
||||
func (c *udpConn) write(p []byte) error {
|
||||
_, err := c.conn.WriteToUDP(p, c.currentAddr)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *udpConn) setCurrentRemoteAddr(addr interface{}) {
|
||||
c.currentAddr = addr.(*net.UDPAddr)
|
||||
}
|
||||
|
||||
func (c *udpConn) RemoteAddr() *net.UDPAddr {
|
||||
return c.currentAddr
|
||||
}
|
70
cmd/gost/vendor/github.com/lucas-clemente/quic-go/utils/log.go
generated
vendored
70
cmd/gost/vendor/github.com/lucas-clemente/quic-go/utils/log.go
generated
vendored
@ -1,70 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var out io.Writer = os.Stdout
|
||||
|
||||
// LogLevel of quic-go
|
||||
type LogLevel uint8
|
||||
|
||||
const (
|
||||
// LogLevelDebug enables debug logs (e.g. packet contents)
|
||||
LogLevelDebug LogLevel = iota
|
||||
// LogLevelInfo enables info logs (e.g. packets)
|
||||
LogLevelInfo
|
||||
// LogLevelError enables err logs
|
||||
LogLevelError
|
||||
// LogLevelNothing disables
|
||||
LogLevelNothing
|
||||
)
|
||||
|
||||
var logLevel = LogLevelNothing
|
||||
|
||||
var mutex sync.Mutex
|
||||
|
||||
// SetLogWriter sets the log writer.
|
||||
func SetLogWriter(w io.Writer) {
|
||||
out = w
|
||||
}
|
||||
|
||||
// SetLogLevel sets the log level
|
||||
func SetLogLevel(level LogLevel) {
|
||||
logLevel = level
|
||||
}
|
||||
|
||||
// Debugf logs something
|
||||
func Debugf(format string, args ...interface{}) {
|
||||
if logLevel == LogLevelDebug {
|
||||
mutex.Lock()
|
||||
fmt.Fprintf(out, format+"\n", args...)
|
||||
mutex.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Infof logs something
|
||||
func Infof(format string, args ...interface{}) {
|
||||
if logLevel <= LogLevelInfo {
|
||||
mutex.Lock()
|
||||
fmt.Fprintf(out, format+"\n", args...)
|
||||
mutex.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Errorf logs something
|
||||
func Errorf(format string, args ...interface{}) {
|
||||
if logLevel <= LogLevelError {
|
||||
mutex.Lock()
|
||||
fmt.Fprintf(out, format+"\n", args...)
|
||||
mutex.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Debug returns true if the log level is LogLevelDebug
|
||||
func Debug() bool {
|
||||
return logLevel == LogLevelDebug
|
||||
}
|
88
cmd/gost/vendor/golang.org/x/crypto/curve25519/cswap_amd64.s
generated
vendored
88
cmd/gost/vendor/golang.org/x/crypto/curve25519/cswap_amd64.s
generated
vendored
@ -1,88 +0,0 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This code was translated into a form compatible with 6a from the public
|
||||
// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
|
||||
|
||||
// +build amd64,!gccgo,!appengine
|
||||
|
||||
// func cswap(inout *[5]uint64, v uint64)
|
||||
TEXT ·cswap(SB),7,$0
|
||||
MOVQ inout+0(FP),DI
|
||||
MOVQ v+8(FP),SI
|
||||
|
||||
CMPQ SI,$1
|
||||
MOVQ 0(DI),SI
|
||||
MOVQ 80(DI),DX
|
||||
MOVQ 8(DI),CX
|
||||
MOVQ 88(DI),R8
|
||||
MOVQ SI,R9
|
||||
CMOVQEQ DX,SI
|
||||
CMOVQEQ R9,DX
|
||||
MOVQ CX,R9
|
||||
CMOVQEQ R8,CX
|
||||
CMOVQEQ R9,R8
|
||||
MOVQ SI,0(DI)
|
||||
MOVQ DX,80(DI)
|
||||
MOVQ CX,8(DI)
|
||||
MOVQ R8,88(DI)
|
||||
MOVQ 16(DI),SI
|
||||
MOVQ 96(DI),DX
|
||||
MOVQ 24(DI),CX
|
||||
MOVQ 104(DI),R8
|
||||
MOVQ SI,R9
|
||||
CMOVQEQ DX,SI
|
||||
CMOVQEQ R9,DX
|
||||
MOVQ CX,R9
|
||||
CMOVQEQ R8,CX
|
||||
CMOVQEQ R9,R8
|
||||
MOVQ SI,16(DI)
|
||||
MOVQ DX,96(DI)
|
||||
MOVQ CX,24(DI)
|
||||
MOVQ R8,104(DI)
|
||||
MOVQ 32(DI),SI
|
||||
MOVQ 112(DI),DX
|
||||
MOVQ 40(DI),CX
|
||||
MOVQ 120(DI),R8
|
||||
MOVQ SI,R9
|
||||
CMOVQEQ DX,SI
|
||||
CMOVQEQ R9,DX
|
||||
MOVQ CX,R9
|
||||
CMOVQEQ R8,CX
|
||||
CMOVQEQ R9,R8
|
||||
MOVQ SI,32(DI)
|
||||
MOVQ DX,112(DI)
|
||||
MOVQ CX,40(DI)
|
||||
MOVQ R8,120(DI)
|
||||
MOVQ 48(DI),SI
|
||||
MOVQ 128(DI),DX
|
||||
MOVQ 56(DI),CX
|
||||
MOVQ 136(DI),R8
|
||||
MOVQ SI,R9
|
||||
CMOVQEQ DX,SI
|
||||
CMOVQEQ R9,DX
|
||||
MOVQ CX,R9
|
||||
CMOVQEQ R8,CX
|
||||
CMOVQEQ R9,R8
|
||||
MOVQ SI,48(DI)
|
||||
MOVQ DX,128(DI)
|
||||
MOVQ CX,56(DI)
|
||||
MOVQ R8,136(DI)
|
||||
MOVQ 64(DI),SI
|
||||
MOVQ 144(DI),DX
|
||||
MOVQ 72(DI),CX
|
||||
MOVQ 152(DI),R8
|
||||
MOVQ SI,R9
|
||||
CMOVQEQ DX,SI
|
||||
CMOVQEQ R9,DX
|
||||
MOVQ CX,R9
|
||||
CMOVQEQ R8,CX
|
||||
CMOVQEQ R9,R8
|
||||
MOVQ SI,64(DI)
|
||||
MOVQ DX,144(DI)
|
||||
MOVQ CX,72(DI)
|
||||
MOVQ R8,152(DI)
|
||||
MOVQ DI,AX
|
||||
MOVQ SI,DX
|
||||
RET
|
60
cmd/gost/vendor/golang.org/x/net/http2/fixed_buffer.go
generated
vendored
60
cmd/gost/vendor/golang.org/x/net/http2/fixed_buffer.go
generated
vendored
@ -1,60 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// fixedBuffer is an io.ReadWriter backed by a fixed size buffer.
|
||||
// It never allocates, but moves old data as new data is written.
|
||||
type fixedBuffer struct {
|
||||
buf []byte
|
||||
r, w int
|
||||
}
|
||||
|
||||
var (
|
||||
errReadEmpty = errors.New("read from empty fixedBuffer")
|
||||
errWriteFull = errors.New("write on full fixedBuffer")
|
||||
)
|
||||
|
||||
// Read copies bytes from the buffer into p.
|
||||
// It is an error to read when no data is available.
|
||||
func (b *fixedBuffer) Read(p []byte) (n int, err error) {
|
||||
if b.r == b.w {
|
||||
return 0, errReadEmpty
|
||||
}
|
||||
n = copy(p, b.buf[b.r:b.w])
|
||||
b.r += n
|
||||
if b.r == b.w {
|
||||
b.r = 0
|
||||
b.w = 0
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Len returns the number of bytes of the unread portion of the buffer.
|
||||
func (b *fixedBuffer) Len() int {
|
||||
return b.w - b.r
|
||||
}
|
||||
|
||||
// Write copies bytes from p into the buffer.
|
||||
// It is an error to write more data than the buffer can hold.
|
||||
func (b *fixedBuffer) Write(p []byte) (n int, err error) {
|
||||
// Slide existing data to beginning.
|
||||
if b.r > 0 && len(p) > len(b.buf)-b.w {
|
||||
copy(b.buf, b.buf[b.r:b.w])
|
||||
b.w -= b.r
|
||||
b.r = 0
|
||||
}
|
||||
|
||||
// Write new data.
|
||||
n = copy(b.buf[b.w:], p)
|
||||
b.w += n
|
||||
if n < len(p) {
|
||||
err = errWriteFull
|
||||
}
|
||||
return n, err
|
||||
}
|
43
cmd/gost/vendor/golang.org/x/net/http2/go16.go
generated
vendored
43
cmd/gost/vendor/golang.org/x/net/http2/go16.go
generated
vendored
@ -1,43 +0,0 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.6
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func transportExpectContinueTimeout(t1 *http.Transport) time.Duration {
|
||||
return t1.ExpectContinueTimeout
|
||||
}
|
||||
|
||||
// isBadCipher reports whether the cipher is blacklisted by the HTTP/2 spec.
|
||||
func isBadCipher(cipher uint16) bool {
|
||||
switch cipher {
|
||||
case tls.TLS_RSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
|
||||
// Reject cipher suites from Appendix A.
|
||||
// "This list includes those cipher suites that do not
|
||||
// offer an ephemeral key exchange and those that are
|
||||
// based on the TLS null, stream or block cipher type"
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
46
cmd/gost/vendor/golang.org/x/net/http2/not_go16.go
generated
vendored
46
cmd/gost/vendor/golang.org/x/net/http2/not_go16.go
generated
vendored
@ -1,46 +0,0 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !go1.6
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func configureTransport(t1 *http.Transport) (*Transport, error) {
|
||||
return nil, errTransportVersion
|
||||
}
|
||||
|
||||
func transportExpectContinueTimeout(t1 *http.Transport) time.Duration {
|
||||
return 0
|
||||
|
||||
}
|
||||
|
||||
// isBadCipher reports whether the cipher is blacklisted by the HTTP/2 spec.
|
||||
func isBadCipher(cipher uint16) bool {
|
||||
switch cipher {
|
||||
case tls.TLS_RSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
|
||||
// Reject cipher suites from Appendix A.
|
||||
// "This list includes those cipher suites that do not
|
||||
// offer an ephemeral key exchange and those that are
|
||||
// based on the TLS null, stream or block cipher type"
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
68
cmd/gost/vendor/golang.org/x/net/idna/idna.go
generated
vendored
68
cmd/gost/vendor/golang.org/x/net/idna/idna.go
generated
vendored
@ -1,68 +0,0 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package idna implements IDNA2008 (Internationalized Domain Names for
|
||||
// Applications), defined in RFC 5890, RFC 5891, RFC 5892, RFC 5893 and
|
||||
// RFC 5894.
|
||||
package idna // import "golang.org/x/net/idna"
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// TODO(nigeltao): specify when errors occur. For example, is ToASCII(".") or
|
||||
// ToASCII("foo\x00") an error? See also http://www.unicode.org/faq/idn.html#11
|
||||
|
||||
// acePrefix is the ASCII Compatible Encoding prefix.
|
||||
const acePrefix = "xn--"
|
||||
|
||||
// ToASCII converts a domain or domain label to its ASCII form. For example,
|
||||
// ToASCII("bücher.example.com") is "xn--bcher-kva.example.com", and
|
||||
// ToASCII("golang") is "golang".
|
||||
func ToASCII(s string) (string, error) {
|
||||
if ascii(s) {
|
||||
return s, nil
|
||||
}
|
||||
labels := strings.Split(s, ".")
|
||||
for i, label := range labels {
|
||||
if !ascii(label) {
|
||||
a, err := encode(acePrefix, label)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
labels[i] = a
|
||||
}
|
||||
}
|
||||
return strings.Join(labels, "."), nil
|
||||
}
|
||||
|
||||
// ToUnicode converts a domain or domain label to its Unicode form. For example,
|
||||
// ToUnicode("xn--bcher-kva.example.com") is "bücher.example.com", and
|
||||
// ToUnicode("golang") is "golang".
|
||||
func ToUnicode(s string) (string, error) {
|
||||
if !strings.Contains(s, acePrefix) {
|
||||
return s, nil
|
||||
}
|
||||
labels := strings.Split(s, ".")
|
||||
for i, label := range labels {
|
||||
if strings.HasPrefix(label, acePrefix) {
|
||||
u, err := decode(label[len(acePrefix):])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
labels[i] = u
|
||||
}
|
||||
}
|
||||
return strings.Join(labels, "."), nil
|
||||
}
|
||||
|
||||
func ascii(s string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] >= utf8.RuneSelf {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
BIN
cmd/gost/vendor/gopkg.in/xtaci/kcp-go.v2/frame.png
generated
vendored
BIN
cmd/gost/vendor/gopkg.in/xtaci/kcp-go.v2/frame.png
generated
vendored
Binary file not shown.
Before Width: | Height: | Size: 8.0 KiB |
62
cmd/gost/vendor/gopkg.in/xtaci/kcp-go.v2/snmp.go
generated
vendored
62
cmd/gost/vendor/gopkg.in/xtaci/kcp-go.v2/snmp.go
generated
vendored
@ -1,62 +0,0 @@
|
||||
package kcp
|
||||
|
||||
import "sync/atomic"
|
||||
|
||||
// Snmp defines network statistics indicator
|
||||
type Snmp struct {
|
||||
BytesSent uint64 // payload bytes sent
|
||||
BytesReceived uint64
|
||||
MaxConn uint64
|
||||
ActiveOpens uint64
|
||||
PassiveOpens uint64
|
||||
CurrEstab uint64
|
||||
InErrs uint64
|
||||
InCsumErrors uint64 // checksum errors
|
||||
InSegs uint64
|
||||
OutSegs uint64
|
||||
OutBytes uint64 // udp bytes sent
|
||||
RetransSegs uint64
|
||||
FastRetransSegs uint64
|
||||
EarlyRetransSegs uint64
|
||||
LostSegs uint64
|
||||
RepeatSegs uint64
|
||||
FECRecovered uint64
|
||||
FECErrs uint64
|
||||
FECSegs uint64 // fec segments received
|
||||
}
|
||||
|
||||
func newSnmp() *Snmp {
|
||||
return new(Snmp)
|
||||
}
|
||||
|
||||
// Copy make a copy of current snmp snapshot
|
||||
func (s *Snmp) Copy() *Snmp {
|
||||
d := newSnmp()
|
||||
d.BytesSent = atomic.LoadUint64(&s.BytesSent)
|
||||
d.BytesReceived = atomic.LoadUint64(&s.BytesReceived)
|
||||
d.MaxConn = atomic.LoadUint64(&s.MaxConn)
|
||||
d.ActiveOpens = atomic.LoadUint64(&s.ActiveOpens)
|
||||
d.PassiveOpens = atomic.LoadUint64(&s.PassiveOpens)
|
||||
d.CurrEstab = atomic.LoadUint64(&s.CurrEstab)
|
||||
d.InErrs = atomic.LoadUint64(&s.InErrs)
|
||||
d.InCsumErrors = atomic.LoadUint64(&s.InCsumErrors)
|
||||
d.InSegs = atomic.LoadUint64(&s.InSegs)
|
||||
d.OutSegs = atomic.LoadUint64(&s.OutSegs)
|
||||
d.OutBytes = atomic.LoadUint64(&s.OutBytes)
|
||||
d.RetransSegs = atomic.LoadUint64(&s.RetransSegs)
|
||||
d.FastRetransSegs = atomic.LoadUint64(&s.FastRetransSegs)
|
||||
d.EarlyRetransSegs = atomic.LoadUint64(&s.EarlyRetransSegs)
|
||||
d.LostSegs = atomic.LoadUint64(&s.LostSegs)
|
||||
d.RepeatSegs = atomic.LoadUint64(&s.RepeatSegs)
|
||||
d.FECSegs = atomic.LoadUint64(&s.FECSegs)
|
||||
d.FECErrs = atomic.LoadUint64(&s.FECErrs)
|
||||
d.FECRecovered = atomic.LoadUint64(&s.FECRecovered)
|
||||
return d
|
||||
}
|
||||
|
||||
// DefaultSnmp is the global KCP connection statistics collector
|
||||
var DefaultSnmp *Snmp
|
||||
|
||||
func init() {
|
||||
DefaultSnmp = newSnmp()
|
||||
}
|
166
cmd/gost/vendor/gopkg.in/xtaci/smux.v1/stream.go
generated
vendored
166
cmd/gost/vendor/gopkg.in/xtaci/smux.v1/stream.go
generated
vendored
@ -1,166 +0,0 @@
|
||||
package smux
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Stream implements io.ReadWriteCloser
|
||||
type Stream struct {
|
||||
id uint32
|
||||
rstflag int32
|
||||
sess *Session
|
||||
buffer bytes.Buffer
|
||||
bufferLock sync.Mutex
|
||||
frameSize int
|
||||
chReadEvent chan struct{} // notify a read event
|
||||
die chan struct{} // flag the stream has closed
|
||||
dieLock sync.Mutex
|
||||
}
|
||||
|
||||
// newStream initiates a Stream struct
|
||||
func newStream(id uint32, frameSize int, sess *Session) *Stream {
|
||||
s := new(Stream)
|
||||
s.id = id
|
||||
s.chReadEvent = make(chan struct{}, 1)
|
||||
s.frameSize = frameSize
|
||||
s.sess = sess
|
||||
s.die = make(chan struct{})
|
||||
return s
|
||||
}
|
||||
|
||||
// Read implements io.ReadWriteCloser
|
||||
func (s *Stream) Read(b []byte) (n int, err error) {
|
||||
READ:
|
||||
select {
|
||||
case <-s.die:
|
||||
return 0, errors.New(errBrokenPipe)
|
||||
default:
|
||||
}
|
||||
|
||||
s.bufferLock.Lock()
|
||||
n, err = s.buffer.Read(b)
|
||||
s.bufferLock.Unlock()
|
||||
|
||||
if n > 0 {
|
||||
s.sess.returnTokens(n)
|
||||
return n, nil
|
||||
} else if atomic.LoadInt32(&s.rstflag) == 1 {
|
||||
_ = s.Close()
|
||||
return 0, errors.New(errConnReset)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-s.chReadEvent:
|
||||
goto READ
|
||||
case <-s.die:
|
||||
return 0, errors.New(errBrokenPipe)
|
||||
}
|
||||
}
|
||||
|
||||
// Write implements io.ReadWriteCloser
|
||||
func (s *Stream) Write(b []byte) (n int, err error) {
|
||||
select {
|
||||
case <-s.die:
|
||||
return 0, errors.New(errBrokenPipe)
|
||||
default:
|
||||
}
|
||||
|
||||
frames := s.split(b, cmdPSH, s.id)
|
||||
// preallocate buffer
|
||||
buffer := make([]byte, len(frames)*headerSize+len(b))
|
||||
bts := buffer
|
||||
|
||||
// combine frames into a large blob
|
||||
for k := range frames {
|
||||
bts[0] = version
|
||||
bts[1] = frames[k].cmd
|
||||
binary.LittleEndian.PutUint16(bts[2:], uint16(len(frames[k].data)))
|
||||
binary.LittleEndian.PutUint32(bts[4:], frames[k].sid)
|
||||
copy(bts[headerSize:], frames[k].data)
|
||||
bts = bts[len(frames[k].data)+headerSize:]
|
||||
}
|
||||
|
||||
if _, err = s.sess.writeBinary(buffer); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// Close implements io.ReadWriteCloser
|
||||
func (s *Stream) Close() error {
|
||||
s.dieLock.Lock()
|
||||
defer s.dieLock.Unlock()
|
||||
|
||||
select {
|
||||
case <-s.die:
|
||||
return errors.New(errBrokenPipe)
|
||||
default:
|
||||
close(s.die)
|
||||
s.sess.streamClosed(s.id)
|
||||
_, err := s.sess.writeFrame(newFrame(cmdRST, s.id))
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// session closes the stream
|
||||
func (s *Stream) sessionClose() {
|
||||
s.dieLock.Lock()
|
||||
defer s.dieLock.Unlock()
|
||||
|
||||
select {
|
||||
case <-s.die:
|
||||
default:
|
||||
close(s.die)
|
||||
}
|
||||
}
|
||||
|
||||
// pushBytes a slice into buffer
|
||||
func (s *Stream) pushBytes(p []byte) {
|
||||
s.bufferLock.Lock()
|
||||
s.buffer.Write(p)
|
||||
s.bufferLock.Unlock()
|
||||
}
|
||||
|
||||
// recycleTokens transform remaining bytes to tokens(will truncate buffer)
|
||||
func (s *Stream) recycleTokens() (n int) {
|
||||
s.bufferLock.Lock()
|
||||
n = s.buffer.Len()
|
||||
s.buffer.Reset()
|
||||
s.bufferLock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// split large byte buffer into smaller frames, reference only
|
||||
func (s *Stream) split(bts []byte, cmd byte, sid uint32) []Frame {
|
||||
var frames []Frame
|
||||
for len(bts) > s.frameSize {
|
||||
frame := newFrame(cmd, sid)
|
||||
frame.data = bts[:s.frameSize]
|
||||
bts = bts[s.frameSize:]
|
||||
frames = append(frames, frame)
|
||||
}
|
||||
if len(bts) > 0 {
|
||||
frame := newFrame(cmd, sid)
|
||||
frame.data = bts
|
||||
frames = append(frames, frame)
|
||||
}
|
||||
return frames
|
||||
}
|
||||
|
||||
// notify read event
|
||||
func (s *Stream) notifyReadEvent() {
|
||||
select {
|
||||
case s.chReadEvent <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// mark this stream has been reset
|
||||
func (s *Stream) markRST() {
|
||||
atomic.StoreInt32(&s.rstflag, 1)
|
||||
}
|
295
cmd/gost/vendor/vendor.json
vendored
295
cmd/gost/vendor/vendor.json
vendored
@ -1,295 +0,0 @@
|
||||
{
|
||||
"comment": "",
|
||||
"ignore": "test",
|
||||
"package": [
|
||||
{
|
||||
"checksumSHA1": "IFJyJgPCjumDG37lEb0lyRBBGZE=",
|
||||
"path": "github.com/Yawning/chacha20",
|
||||
"revision": "c91e78db502ff629614837aacb7aa4efa61c651a",
|
||||
"revisionTime": "2016-04-30T09:49:23Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "QPs3L3mjPoi+a9GJCjW8HhyJczM=",
|
||||
"path": "github.com/codahale/chacha20",
|
||||
"revision": "ec07b4f69a3f70b1dd2a8ad77230deb1ba5d6953",
|
||||
"revisionTime": "2015-11-07T02:50:05Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "5TwW96Afcvo+zm0tAn+DSNIQreQ=",
|
||||
"path": "github.com/ginuerzh/gosocks5",
|
||||
"revision": "0f737bddba2abd1496dc03c8b39b817cc5f33fa7",
|
||||
"revisionTime": "2017-01-19T05:34:58Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "VXFPGZtx+GKexkXeL3YljqJLHFk=",
|
||||
"path": "github.com/ginuerzh/gost",
|
||||
"revision": "dc75931858cf96d95525faa22b7ffa0cea3e7d68",
|
||||
"revisionTime": "2017-01-25T04:21:04Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "URsJa4y/sUUw/STmbeYx9EKqaYE=",
|
||||
"path": "github.com/golang/glog",
|
||||
"revision": "44145f04b68cf362d9c4df2182967c2275eaefed",
|
||||
"revisionTime": "2014-09-23T20:47:00Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "d9PxF1XQGLMJZRct2R8qVM/eYlE=",
|
||||
"path": "github.com/hashicorp/golang-lru",
|
||||
"revision": "0a025b7e63adc15a622f29b0b2c4c3848243bbf6",
|
||||
"revisionTime": "2016-08-13T22:13:03Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "9hffs0bAIU6CquiRhKQdzjHnKt0=",
|
||||
"path": "github.com/hashicorp/golang-lru/simplelru",
|
||||
"revision": "0a025b7e63adc15a622f29b0b2c4c3848243bbf6",
|
||||
"revisionTime": "2016-08-13T22:13:03Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "/EgCTbjJkJh2yi9lqEgzmau8O4I=",
|
||||
"path": "github.com/klauspost/compress/snappy",
|
||||
"revision": "1e658061989f47658e69492cf63a839630a25eba",
|
||||
"revisionTime": "2016-10-20T15:14:30Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "iKPMvbAueGfdyHcWCgzwKzm8WVo=",
|
||||
"path": "github.com/klauspost/cpuid",
|
||||
"revision": "09cded8978dc9e80714c4d85b0322337b0a1e5e0",
|
||||
"revisionTime": "2016-03-02T07:53:16Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "BM6ZlNJmtKy3GBoWwg2X55gnZ4A=",
|
||||
"path": "github.com/klauspost/crc32",
|
||||
"revision": "cb6bfca970f6908083f26f39a79009d608efd5cd",
|
||||
"revisionTime": "2016-10-16T15:41:25Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "dwSGkUfh3A2h0VkXndzBX/27hVc=",
|
||||
"path": "github.com/klauspost/reedsolomon",
|
||||
"revision": "c54154da9e35cab25232314cf69ab9d78447f9a5",
|
||||
"revisionTime": "2016-09-12T19:31:07Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "fbae4URna3lp8RtTOutiXIO1JS0=",
|
||||
"path": "github.com/lucas-clemente/aes12",
|
||||
"revision": "8ee5b5610baca43b60ecfad586b3c40d92a96e0c",
|
||||
"revisionTime": "2016-08-23T09:51:02Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "ne1X+frkx5fJcpz9FaZPuUZ7amM=",
|
||||
"path": "github.com/lucas-clemente/fnv128a",
|
||||
"revision": "393af48d391698c6ae4219566bfbdfef67269997",
|
||||
"revisionTime": "2016-05-04T15:23:51Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "xbX/mARowOKpW3S1G8hmaDlWdp8=",
|
||||
"path": "github.com/lucas-clemente/quic-go",
|
||||
"revision": "ef977ee0591f72543f8323cd12a585f5406ff971",
|
||||
"revisionTime": "2016-10-14T09:35:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "OA9E+y7g05x/mWJJHmA7oPxWKQo=",
|
||||
"path": "github.com/lucas-clemente/quic-go-certificates",
|
||||
"revision": "d2f86524cced5186554df90d92529757d22c1cb6",
|
||||
"revisionTime": "2016-08-23T09:51:56Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "1+iOTf/w8VXGqQao0FMNEE2RFFg=",
|
||||
"path": "github.com/lucas-clemente/quic-go/ackhandler",
|
||||
"revision": "ef977ee0591f72543f8323cd12a585f5406ff971",
|
||||
"revisionTime": "2016-10-14T09:35:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "8zoU6uLKP2Czs96VgmNMubNcWKk=",
|
||||
"path": "github.com/lucas-clemente/quic-go/congestion",
|
||||
"revision": "ef977ee0591f72543f8323cd12a585f5406ff971",
|
||||
"revisionTime": "2016-10-14T09:35:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "vzBxE7JViWonmrSndgmRuye8ntA=",
|
||||
"path": "github.com/lucas-clemente/quic-go/crypto",
|
||||
"revision": "ef977ee0591f72543f8323cd12a585f5406ff971",
|
||||
"revisionTime": "2016-10-14T09:35:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "CaOt7EZEuyWQ073FITB8qQfFswA=",
|
||||
"path": "github.com/lucas-clemente/quic-go/flowcontrol",
|
||||
"revision": "ef977ee0591f72543f8323cd12a585f5406ff971",
|
||||
"revisionTime": "2016-10-14T09:35:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "VvhIOTKtMkPZ7pdrCPHlDQI2wIw=",
|
||||
"path": "github.com/lucas-clemente/quic-go/frames",
|
||||
"revision": "ef977ee0591f72543f8323cd12a585f5406ff971",
|
||||
"revisionTime": "2016-10-14T09:35:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "IJDUjsrJP5dtHvxVNT32x4SQQVk=",
|
||||
"path": "github.com/lucas-clemente/quic-go/h2quic",
|
||||
"revision": "ef977ee0591f72543f8323cd12a585f5406ff971",
|
||||
"revisionTime": "2016-10-14T09:35:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "1DQTvvwcUUrmMKkN4ASjX5+iGqs=",
|
||||
"path": "github.com/lucas-clemente/quic-go/handshake",
|
||||
"revision": "ef977ee0591f72543f8323cd12a585f5406ff971",
|
||||
"revisionTime": "2016-10-14T09:35:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "qp5LXpuvIAgW3BffRzHVZQk1WfE=",
|
||||
"path": "github.com/lucas-clemente/quic-go/protocol",
|
||||
"revision": "ef977ee0591f72543f8323cd12a585f5406ff971",
|
||||
"revisionTime": "2016-10-14T09:35:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "ss57bkTclCnmt9fVosYie/ehkoo=",
|
||||
"path": "github.com/lucas-clemente/quic-go/qerr",
|
||||
"revision": "ef977ee0591f72543f8323cd12a585f5406ff971",
|
||||
"revisionTime": "2016-10-14T09:35:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "j/A6Rfz4BWpt5rsR2j5kR0H2ZTI=",
|
||||
"path": "github.com/lucas-clemente/quic-go/utils",
|
||||
"revision": "ef977ee0591f72543f8323cd12a585f5406ff971",
|
||||
"revisionTime": "2016-10-14T09:35:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "ynJSWoF6v+3zMnh9R0QmmG6iGV8=",
|
||||
"path": "github.com/pkg/errors",
|
||||
"revision": "839d9e913e063e28dfd0e6c7b7512793e0a48be9",
|
||||
"revisionTime": "2016-10-02T05:25:12Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "MRsfMrdZwnnCTfIzT3czcj0lb0s=",
|
||||
"path": "github.com/shadowsocks/shadowsocks-go/shadowsocks",
|
||||
"revision": "97a5c71f80ba5f5b3e549f14a619fe557ff4f3c9",
|
||||
"revisionTime": "2017-01-21T20:35:16Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "JsJdKXhz87gWenMwBeejTOeNE7k=",
|
||||
"path": "golang.org/x/crypto/blowfish",
|
||||
"revision": "1150b8bd09e53aea1d415621adae9bad665061a1",
|
||||
"revisionTime": "2016-10-21T22:59:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "TT1rac6kpQp2vz24m5yDGUNQ/QQ=",
|
||||
"path": "golang.org/x/crypto/cast5",
|
||||
"revision": "1150b8bd09e53aea1d415621adae9bad665061a1",
|
||||
"revisionTime": "2016-10-21T22:59:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "dwOedwBJ1EIK9+S3t108Bx054Y8=",
|
||||
"path": "golang.org/x/crypto/curve25519",
|
||||
"revision": "1150b8bd09e53aea1d415621adae9bad665061a1",
|
||||
"revisionTime": "2016-10-21T22:59:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "4D8hxMIaSDEW5pCQk22Xj4DcDh4=",
|
||||
"path": "golang.org/x/crypto/hkdf",
|
||||
"revision": "1150b8bd09e53aea1d415621adae9bad665061a1",
|
||||
"revisionTime": "2016-10-21T22:59:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "1MGpGDQqnUoRpv7VEcQrXOBydXE=",
|
||||
"path": "golang.org/x/crypto/pbkdf2",
|
||||
"revision": "1150b8bd09e53aea1d415621adae9bad665061a1",
|
||||
"revisionTime": "2016-10-21T22:59:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "o5QDZYQhyiEt2jg1Fot34mR1rBg=",
|
||||
"path": "golang.org/x/crypto/salsa20",
|
||||
"revision": "1150b8bd09e53aea1d415621adae9bad665061a1",
|
||||
"revisionTime": "2016-10-21T22:59:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "w8PESKJweP+BCAxCYPU1QC08qUU=",
|
||||
"path": "golang.org/x/crypto/salsa20/salsa",
|
||||
"revision": "1150b8bd09e53aea1d415621adae9bad665061a1",
|
||||
"revisionTime": "2016-10-21T22:59:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Iwv89z1aXYKaB936lnsmE2NcfqA=",
|
||||
"path": "golang.org/x/crypto/tea",
|
||||
"revision": "1150b8bd09e53aea1d415621adae9bad665061a1",
|
||||
"revisionTime": "2016-10-21T22:59:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "/qXTeb4Dp6W5qlttwaN+Yur36+A=",
|
||||
"path": "golang.org/x/crypto/twofish",
|
||||
"revision": "1150b8bd09e53aea1d415621adae9bad665061a1",
|
||||
"revisionTime": "2016-10-21T22:59:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "d37eTFvbbuPQcKt5vkgsx+6C4c0=",
|
||||
"path": "golang.org/x/crypto/xtea",
|
||||
"revision": "1150b8bd09e53aea1d415621adae9bad665061a1",
|
||||
"revisionTime": "2016-10-21T22:59:10Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "tK8eFmQ0JeKpR3P0TjiGobzlIh0=",
|
||||
"path": "golang.org/x/net/bpf",
|
||||
"revision": "65dfc08770ce66f74becfdff5f8ab01caef4e946",
|
||||
"revisionTime": "2016-10-24T22:38:16Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "N1akwAdrHVfPPrsFOhG2ouP21VA=",
|
||||
"path": "golang.org/x/net/http2",
|
||||
"revision": "60c41d1de8da134c05b7b40154a9a82bf5b7edb9",
|
||||
"revisionTime": "2017-01-10T03:16:11Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "HzuGD7AwgC0p1az1WAQnEFnEk98=",
|
||||
"path": "golang.org/x/net/http2/hpack",
|
||||
"revision": "65dfc08770ce66f74becfdff5f8ab01caef4e946",
|
||||
"revisionTime": "2016-10-24T22:38:16Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "GIGmSrYACByf5JDIP9ByBZksY80=",
|
||||
"path": "golang.org/x/net/idna",
|
||||
"revision": "65dfc08770ce66f74becfdff5f8ab01caef4e946",
|
||||
"revisionTime": "2016-10-24T22:38:16Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "yRuyntx9a59ugMi5NlN4ST0XRcI=",
|
||||
"path": "golang.org/x/net/internal/iana",
|
||||
"revision": "65dfc08770ce66f74becfdff5f8ab01caef4e946",
|
||||
"revisionTime": "2016-10-24T22:38:16Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "LVY0kRV5iwxDS3XQaBWxdzPemyc=",
|
||||
"path": "golang.org/x/net/internal/netreflect",
|
||||
"revision": "65dfc08770ce66f74becfdff5f8ab01caef4e946",
|
||||
"revisionTime": "2016-10-24T22:38:16Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "yzxKtJnG6O34n0gsbnoceGhASdQ=",
|
||||
"path": "golang.org/x/net/ipv4",
|
||||
"revision": "65dfc08770ce66f74becfdff5f8ab01caef4e946",
|
||||
"revisionTime": "2016-10-24T22:38:16Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "3xyuaSNmClqG4YWC7g0isQIbUTc=",
|
||||
"path": "golang.org/x/net/lex/httplex",
|
||||
"revision": "65dfc08770ce66f74becfdff5f8ab01caef4e946",
|
||||
"revisionTime": "2016-10-24T22:38:16Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "G/v7O8ukxRnqPUrKYmf+niQ+JNU=",
|
||||
"path": "gopkg.in/gorilla/websocket.v1",
|
||||
"revision": "3ab3a8b8831546bd18fd182c20687ca853b2bb13",
|
||||
"revisionTime": "2016-12-15T22:53:35Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "nkIlj9QTxHQ78Vb+VgjhXZ4rZ3E=",
|
||||
"path": "gopkg.in/xtaci/kcp-go.v2",
|
||||
"revision": "6610d527ea5c4890cf593796ff8ff1f10486bb68",
|
||||
"revisionTime": "2016-09-08T14:44:41Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "aIqXwA82JxLOXcgmuVSgcRqdJvU=",
|
||||
"path": "gopkg.in/xtaci/smux.v1",
|
||||
"revision": "9f2b528a60917e6446273926f4c676cac759d2b0",
|
||||
"revisionTime": "2016-09-22T10:26:45Z"
|
||||
}
|
||||
],
|
||||
"rootPath": "github.com/ginuerzh/gost/cmd/gost"
|
||||
}
|
263
conn.go
263
conn.go
@ -1,263 +0,0 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
"github.com/golang/glog"
|
||||
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ProxyConn struct {
|
||||
conn net.Conn
|
||||
Node ProxyNode
|
||||
handshaked bool
|
||||
handshakeMutex sync.Mutex
|
||||
handshakeErr error
|
||||
}
|
||||
|
||||
func NewProxyConn(conn net.Conn, node ProxyNode) *ProxyConn {
|
||||
return &ProxyConn{
|
||||
conn: conn,
|
||||
Node: node,
|
||||
}
|
||||
}
|
||||
|
||||
// Handshake handshake with this proxy node based on the proxy node info: transport, protocol, authentication, etc.
|
||||
//
|
||||
// NOTE: any HTTP2 scheme will be treated as http (for protocol) or tls (for transport).
|
||||
func (c *ProxyConn) Handshake() error {
|
||||
c.handshakeMutex.Lock()
|
||||
defer c.handshakeMutex.Unlock()
|
||||
|
||||
if err := c.handshakeErr; err != nil {
|
||||
return err
|
||||
}
|
||||
if c.handshaked {
|
||||
return nil
|
||||
}
|
||||
c.handshakeErr = c.handshake()
|
||||
return c.handshakeErr
|
||||
}
|
||||
|
||||
func (c *ProxyConn) handshake() error {
|
||||
var tlsUsed bool
|
||||
|
||||
switch c.Node.Transport {
|
||||
case "ws": // websocket connection
|
||||
u := url.URL{Scheme: "ws", Host: c.Node.Addr, Path: "/ws"}
|
||||
conn, err := WebsocketClientConn(u.String(), c.conn, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.conn = conn
|
||||
case "wss": // websocket security
|
||||
tlsUsed = true
|
||||
u := url.URL{Scheme: "wss", Host: c.Node.Addr, Path: "/ws"}
|
||||
config := &tls.Config{
|
||||
InsecureSkipVerify: c.Node.insecureSkipVerify(),
|
||||
ServerName: c.Node.serverName,
|
||||
}
|
||||
conn, err := WebsocketClientConn(u.String(), c.conn, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.conn = conn
|
||||
case "tls", "http2": // tls connection
|
||||
tlsUsed = true
|
||||
cfg := &tls.Config{
|
||||
InsecureSkipVerify: c.Node.insecureSkipVerify(),
|
||||
ServerName: c.Node.serverName,
|
||||
}
|
||||
c.conn = tls.Client(c.conn, cfg)
|
||||
case "h2": // same as http2, but just set a flag for later using.
|
||||
tlsUsed = true
|
||||
case "kcp": // kcp connection
|
||||
tlsUsed = true
|
||||
case "obfs4":
|
||||
conn, err := c.Node.Obfs4ClientConn(c.conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.conn = conn
|
||||
default:
|
||||
}
|
||||
|
||||
switch c.Node.Protocol {
|
||||
case "socks", "socks5": // socks5 handshake with auth and tls supported
|
||||
selector := &clientSelector{
|
||||
methods: []uint8{
|
||||
gosocks5.MethodNoAuth,
|
||||
gosocks5.MethodUserPass,
|
||||
//MethodTLS,
|
||||
},
|
||||
}
|
||||
|
||||
if len(c.Node.Users) > 0 {
|
||||
selector.user = c.Node.Users[0]
|
||||
}
|
||||
|
||||
if !tlsUsed { // if transport is not security, enable security socks5
|
||||
selector.methods = append(selector.methods, MethodTLS)
|
||||
selector.tlsConfig = &tls.Config{
|
||||
InsecureSkipVerify: c.Node.insecureSkipVerify(),
|
||||
ServerName: c.Node.serverName,
|
||||
}
|
||||
}
|
||||
|
||||
conn := gosocks5.ClientConn(c.conn, selector)
|
||||
if err := conn.Handleshake(); err != nil {
|
||||
return err
|
||||
}
|
||||
c.conn = conn
|
||||
case "ss": // shadowsocks
|
||||
// nothing to do
|
||||
case "http", "http2":
|
||||
fallthrough
|
||||
default:
|
||||
}
|
||||
|
||||
c.handshaked = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Connect connect to addr through this proxy node
|
||||
func (c *ProxyConn) Connect(addr string) error {
|
||||
switch c.Node.Protocol {
|
||||
case "ss": // shadowsocks
|
||||
rawaddr, err := ss.RawAddr(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var method, password string
|
||||
if len(c.Node.Users) > 0 {
|
||||
method = c.Node.Users[0].Username()
|
||||
password, _ = c.Node.Users[0].Password()
|
||||
}
|
||||
if c.Node.getBool("ota") && !strings.HasSuffix(method, "-auth") {
|
||||
method += "-auth"
|
||||
}
|
||||
|
||||
cipher, err := ss.NewCipher(method, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ssc, err := ss.DialWithRawAddrConn(rawaddr, c.conn, cipher)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.conn = &shadowConn{conn: ssc}
|
||||
return nil
|
||||
case "socks", "socks5":
|
||||
host, port, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p, _ := strconv.Atoi(port)
|
||||
req := gosocks5.NewRequest(gosocks5.CmdConnect, &gosocks5.Addr{
|
||||
Type: gosocks5.AddrDomain,
|
||||
Host: host,
|
||||
Port: uint16(p),
|
||||
})
|
||||
if err := req.Write(c); err != nil {
|
||||
return err
|
||||
}
|
||||
glog.V(LDEBUG).Infoln("[socks5]", req)
|
||||
|
||||
reply, err := gosocks5.ReadReply(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
glog.V(LDEBUG).Infoln("[socks5]", reply)
|
||||
if reply.Rep != gosocks5.Succeeded {
|
||||
return errors.New("Service unavailable")
|
||||
}
|
||||
case "http":
|
||||
fallthrough
|
||||
default:
|
||||
req := &http.Request{
|
||||
Method: http.MethodConnect,
|
||||
URL: &url.URL{Host: addr},
|
||||
Host: addr,
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Header: make(http.Header),
|
||||
}
|
||||
req.Header.Set("Proxy-Connection", "keep-alive")
|
||||
if len(c.Node.Users) > 0 {
|
||||
user := c.Node.Users[0]
|
||||
s := user.String()
|
||||
if _, set := user.Password(); !set {
|
||||
s += ":"
|
||||
}
|
||||
req.Header.Set("Proxy-Authorization",
|
||||
"Basic "+base64.StdEncoding.EncodeToString([]byte(s)))
|
||||
}
|
||||
if err := req.Write(c); err != nil {
|
||||
return err
|
||||
}
|
||||
if glog.V(LDEBUG) {
|
||||
dump, _ := httputil.DumpRequest(req, false)
|
||||
glog.Infoln(string(dump))
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(c), req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if glog.V(LDEBUG) {
|
||||
dump, _ := httputil.DumpResponse(resp, false)
|
||||
glog.Infoln(string(dump))
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return errors.New(resp.Status)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ProxyConn) Read(b []byte) (n int, err error) {
|
||||
return c.conn.Read(b)
|
||||
}
|
||||
|
||||
func (c *ProxyConn) Write(b []byte) (n int, err error) {
|
||||
return c.conn.Write(b)
|
||||
}
|
||||
|
||||
func (c *ProxyConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *ProxyConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *ProxyConn) RemoteAddr() net.Addr {
|
||||
return c.conn.RemoteAddr()
|
||||
}
|
||||
|
||||
func (c *ProxyConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *ProxyConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *ProxyConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
220
examples/bench/cli.go
Normal file
220
examples/bench/cli.go
Normal file
@ -0,0 +1,220 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ginuerzh/gost"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
var (
|
||||
requests, concurrency int
|
||||
quiet bool
|
||||
swg, ewg sync.WaitGroup
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
|
||||
flag.IntVar(&requests, "n", 1, "Number of requests to perform")
|
||||
flag.IntVar(&concurrency, "c", 1, "Number of multiple requests to make at a time")
|
||||
flag.BoolVar(&quiet, "q", false, "quiet mode")
|
||||
flag.BoolVar(&http2.VerboseLogs, "v", false, "HTTP2 verbose logs")
|
||||
flag.BoolVar(&gost.Debug, "d", false, "debug mode")
|
||||
flag.Parse()
|
||||
|
||||
if quiet {
|
||||
gost.SetLogger(&gost.NopLogger{})
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
chain := gost.NewChain(
|
||||
|
||||
/*
|
||||
// http+tcp
|
||||
gost.Node{
|
||||
Addr: "127.0.0.1:18080",
|
||||
Client: gost.NewClient(
|
||||
gost.HTTPConnector(url.UserPassword("admin", "123456")),
|
||||
gost.TCPTransporter(),
|
||||
),
|
||||
},
|
||||
*/
|
||||
|
||||
/*
|
||||
// socks5+tcp
|
||||
gost.Node{
|
||||
Addr: "127.0.0.1:11080",
|
||||
Client: gost.NewClient(
|
||||
gost.SOCKS5Connector(url.UserPassword("admin", "123456")),
|
||||
gost.TCPTransporter(),
|
||||
),
|
||||
},
|
||||
*/
|
||||
|
||||
/*
|
||||
// ss+tcp
|
||||
gost.Node{
|
||||
Addr: "127.0.0.1:18338",
|
||||
Client: gost.NewClient(
|
||||
gost.ShadowConnector(url.UserPassword("chacha20", "123456")),
|
||||
gost.TCPTransporter(),
|
||||
),
|
||||
},
|
||||
*/
|
||||
|
||||
/*
|
||||
// http+ws
|
||||
gost.Node{
|
||||
Addr: "127.0.0.1:18000",
|
||||
Client: gost.NewClient(
|
||||
gost.HTTPConnector(url.UserPassword("admin", "123456")),
|
||||
gost.WSTransporter(nil),
|
||||
),
|
||||
},
|
||||
*/
|
||||
|
||||
/*
|
||||
// http+wss
|
||||
gost.Node{
|
||||
Addr: "127.0.0.1:18443",
|
||||
Client: gost.NewClient(
|
||||
gost.HTTPConnector(url.UserPassword("admin", "123456")),
|
||||
gost.WSSTransporter(nil),
|
||||
),
|
||||
},
|
||||
*/
|
||||
|
||||
/*
|
||||
// http+tls
|
||||
gost.Node{
|
||||
Addr: "127.0.0.1:11443",
|
||||
Client: gost.NewClient(
|
||||
gost.HTTPConnector(url.UserPassword("admin", "123456")),
|
||||
gost.TLSTransporter(),
|
||||
),
|
||||
},
|
||||
*/
|
||||
|
||||
/*
|
||||
// http2
|
||||
gost.Node{
|
||||
Addr: "127.0.0.1:1443",
|
||||
Client: &gost.Client{
|
||||
Connector: gost.HTTP2Connector(url.UserPassword("admin", "123456")),
|
||||
Transporter: gost.HTTP2Transporter(nil),
|
||||
},
|
||||
},
|
||||
*/
|
||||
|
||||
/*
|
||||
// http+kcp
|
||||
gost.Node{
|
||||
Addr: "127.0.0.1:18388",
|
||||
Client: gost.NewClient(
|
||||
gost.HTTPConnector(nil),
|
||||
gost.KCPTransporter(nil),
|
||||
),
|
||||
},
|
||||
*/
|
||||
|
||||
/*
|
||||
// http+ssh
|
||||
gost.Node{
|
||||
Addr: "127.0.0.1:12222",
|
||||
Client: gost.NewClient(
|
||||
gost.HTTPConnector(url.UserPassword("admin", "123456")),
|
||||
gost.SSHTunnelTransporter(),
|
||||
),
|
||||
},
|
||||
*/
|
||||
|
||||
/*
|
||||
// http+quic
|
||||
gost.Node{
|
||||
Addr: "localhost:6121",
|
||||
Client: &gost.Client{
|
||||
Connector: gost.HTTPConnector(url.UserPassword("admin", "123456")),
|
||||
Transporter: gost.QUICTransporter(nil),
|
||||
},
|
||||
},
|
||||
*/
|
||||
// socks5+h2
|
||||
gost.Node{
|
||||
Addr: "localhost:8443",
|
||||
Client: &gost.Client{
|
||||
// Connector: gost.HTTPConnector(url.UserPassword("admin", "123456")),
|
||||
Connector: gost.SOCKS5Connector(url.UserPassword("admin", "123456")),
|
||||
// Transporter: gost.H2CTransporter(), // HTTP2 h2c mode
|
||||
Transporter: gost.H2Transporter(nil), // HTTP2 h2
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
total := 0
|
||||
for total < requests {
|
||||
if total+concurrency > requests {
|
||||
concurrency = requests - total
|
||||
}
|
||||
startChan := make(chan struct{})
|
||||
for i := 0; i < concurrency; i++ {
|
||||
swg.Add(1)
|
||||
ewg.Add(1)
|
||||
go request(chain, startChan)
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
swg.Wait() // wait for workers ready
|
||||
close(startChan) // start signal
|
||||
ewg.Wait() // wait for workers done
|
||||
|
||||
duration := time.Since(start)
|
||||
total += concurrency
|
||||
log.Printf("%d/%d/%d requests done (%v/%v)", total, requests, concurrency, duration, duration/time.Duration(concurrency))
|
||||
}
|
||||
}
|
||||
|
||||
func request(chain *gost.Chain, start <-chan struct{}) {
|
||||
defer ewg.Done()
|
||||
|
||||
swg.Done()
|
||||
<-start
|
||||
|
||||
conn, err := chain.Dial("localhost:18888")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
//conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
|
||||
req, err := http.NewRequest(http.MethodGet, "http://localhost:18888", nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if err := req.Write(conn); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
resp, err := http.ReadResponse(bufio.NewReader(conn), req)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if gost.Debug {
|
||||
rb, _ := httputil.DumpRequest(req, true)
|
||||
log.Println(string(rb))
|
||||
rb, _ = httputil.DumpResponse(resp, true)
|
||||
log.Println(string(rb))
|
||||
}
|
||||
}
|
359
examples/bench/srv.go
Normal file
359
examples/bench/srv.go
Normal file
@ -0,0 +1,359 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/ginuerzh/gost"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
var (
|
||||
quiet bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
|
||||
flag.BoolVar(&quiet, "q", false, "quiet mode")
|
||||
flag.BoolVar(&gost.Debug, "d", false, "debug mode")
|
||||
flag.BoolVar(&http2.VerboseLogs, "v", false, "HTTP2 verbose logs")
|
||||
flag.Parse()
|
||||
|
||||
if quiet {
|
||||
gost.SetLogger(&gost.NopLogger{})
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
go httpServer()
|
||||
go socks5Server()
|
||||
go tlsServer()
|
||||
go shadowServer()
|
||||
go wsServer()
|
||||
go wssServer()
|
||||
go kcpServer()
|
||||
go tcpForwardServer()
|
||||
go tcpRemoteForwardServer()
|
||||
// go rudpForwardServer()
|
||||
// go tcpRedirectServer()
|
||||
go sshTunnelServer()
|
||||
go http2Server()
|
||||
go http2TunnelServer()
|
||||
go quicServer()
|
||||
go shadowUDPServer()
|
||||
go testServer()
|
||||
select {}
|
||||
}
|
||||
|
||||
func httpServer() {
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.TCPListener(":18080")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.HTTPHandler(
|
||||
gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
|
||||
)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
func socks5Server() {
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.TCPListener(":11080")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.SOCKS5Handler(
|
||||
gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
|
||||
gost.TLSConfigHandlerOption(tlsConfig()),
|
||||
)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
func shadowServer() {
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.TCPListener(":18338")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.ShadowHandler(
|
||||
gost.UsersHandlerOption(url.UserPassword("chacha20", "123456")),
|
||||
)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
func tlsServer() {
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.TLSListener(":11443", tlsConfig())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.HTTPHandler(
|
||||
gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
|
||||
)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
func wsServer() {
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.WSListener(":18000", nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.HTTPHandler(
|
||||
gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
|
||||
)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
func wssServer() {
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.WSSListener(":18443", tlsConfig(), nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.HTTPHandler(
|
||||
gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
|
||||
)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
func kcpServer() {
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.KCPListener(":18388", nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.HTTPHandler()
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
func tcpForwardServer() {
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.TCPListener(":2222")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.TCPDirectForwardHandler("localhost:22")
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
func tcpRemoteForwardServer() {
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.TCPRemoteForwardListener(
|
||||
":1222",
|
||||
/*
|
||||
gost.NewChain(
|
||||
gost.Node{
|
||||
Protocol: "socks5",
|
||||
Transport: "tcp",
|
||||
Addr: "localhost:12345",
|
||||
User: url.UserPassword("admin", "123456"),
|
||||
Client: &gost.Client{
|
||||
Connector: gost.SOCKS5Connector(url.UserPassword("admin", "123456")),
|
||||
Transporter: gost.TCPTransporter(),
|
||||
},
|
||||
},
|
||||
),
|
||||
*/
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal()
|
||||
}
|
||||
h := gost.TCPRemoteForwardHandler(
|
||||
":22",
|
||||
//gost.AddrHandlerOption("127.0.0.1:22"),
|
||||
)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
func rudpForwardServer() {
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.UDPRemoteForwardListener(
|
||||
":10053",
|
||||
gost.NewChain(
|
||||
gost.Node{
|
||||
Protocol: "socks5",
|
||||
Transport: "tcp",
|
||||
Addr: "localhost:12345",
|
||||
User: url.UserPassword("admin", "123456"),
|
||||
Client: &gost.Client{
|
||||
Connector: gost.SOCKS5Connector(url.UserPassword("admin", "123456")),
|
||||
Transporter: gost.TCPTransporter(),
|
||||
},
|
||||
},
|
||||
),
|
||||
30*time.Second,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal()
|
||||
}
|
||||
h := gost.UDPRemoteForwardHandler("localhost:53")
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
func tcpRedirectServer() {
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.TCPListener(":8008")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.TCPRedirectHandler()
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
func sshTunnelServer() {
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.SSHTunnelListener(":12222", &gost.SSHConfig{TLSConfig: tlsConfig()})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.HTTPHandler(
|
||||
gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
|
||||
)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
func http2Server() {
|
||||
// http2.VerboseLogs = true
|
||||
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.HTTP2Listener(":1443", tlsConfig())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.HTTP2Handler(
|
||||
gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
|
||||
)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
func http2TunnelServer() {
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.H2Listener(":8443", tlsConfig()) // HTTP2 h2 mode
|
||||
// ln, err := gost.H2CListener(":8443") // HTTP2 h2c mode
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// h := gost.HTTPHandler(
|
||||
// gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
|
||||
// )
|
||||
h := gost.SOCKS5Handler(
|
||||
gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
|
||||
gost.TLSConfigHandlerOption(tlsConfig()),
|
||||
)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
func quicServer() {
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.QUICListener("localhost:6121", &gost.QUICConfig{TLSConfig: tlsConfig()})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.HTTPHandler(
|
||||
gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
|
||||
)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
func shadowUDPServer() {
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.ShadowUDPListener(":18338", url.UserPassword("chacha20", "123456"), 30*time.Second)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.ShadowUDPdHandler(
|
||||
/*
|
||||
gost.ChainHandlerOption(gost.NewChain(
|
||||
gost.Node{
|
||||
Protocol: "socks5",
|
||||
Transport: "tcp",
|
||||
Addr: "localhost:11080",
|
||||
User: url.UserPassword("admin", "123456"),
|
||||
Client: &gost.Client{
|
||||
Connector: gost.SOCKS5Connector(url.UserPassword("admin", "123456")),
|
||||
Transporter: gost.TCPTransporter(),
|
||||
},
|
||||
},
|
||||
)),
|
||||
*/
|
||||
)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
var (
|
||||
rawCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIC+jCCAeKgAwIBAgIRAMlREhz8Miu1FQozsxbeqyMwDQYJKoZIhvcNAQELBQAw
|
||||
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNzA1MTkwNTM5MDJaFw0xODA1MTkwNTM5
|
||||
MDJaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
|
||||
ggEKAoIBAQCyfqvv0kDriciEAVIW6JaWYFCL9a19jj1wmAGmVGxV3kNsr01kpa6N
|
||||
0EBqnrcy7WknhCt1d43CqhKtTcXgJ/J9phZVxlizb8sUB85hm+MvP0N3HCg3f0Jw
|
||||
hLuMrPijS6xjyw0fKCK/p6OUYMIfo5cdqeZid2WV4Ozts5uRd6Dmy2kyBe8Zg1F4
|
||||
8YJGuTWZmL2L7uZUiPY4T3q9+1iucq3vUpxymVRi1BTXnTpx+C0GS8NNgeEmevHv
|
||||
482vHM5DNflAQ+mvGZvBVduq/AfirCDnt2DIZm1DcZXLrY9F3EPrlRZexmAhCDGR
|
||||
LIKnMmoGicBM11Aw1fDIfJAHynk43tjPAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF
|
||||
oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC
|
||||
CWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAx8Lna8DcQv0bRB3L9i2+KRN
|
||||
l/UhPCoFagxk1cZore4p0w+1m7OgigOoTpg5jh78DzVDhScZlgJ0bBVYp5rojeJS
|
||||
cBDC9lCDcaXQfFmT5LykCAwIgw/gs+rw5Aq0y3D0m8CcqKosyZa9wnZ2cVy/+45w
|
||||
emcSdboc65ueZScv38/W7aTUoVRcjyRUv0jv0zW0EPnnDlluVkeZo9spBhiTTwoj
|
||||
b3zGODs6alTNIJwZIHNxxyOmfJPpVVp8BzGbMk7YBixSlZ/vbrrYV34TcSiy7J57
|
||||
lNNoVWM+OwiVk1+AEZfQDwaQfef5tsIkAZBUyITkkDKRhygtwM2110dejbEsgg==
|
||||
-----END CERTIFICATE-----`)
|
||||
rawKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAsn6r79JA64nIhAFSFuiWlmBQi/WtfY49cJgBplRsVd5DbK9N
|
||||
ZKWujdBAap63Mu1pJ4QrdXeNwqoSrU3F4CfyfaYWVcZYs2/LFAfOYZvjLz9Ddxwo
|
||||
N39CcIS7jKz4o0usY8sNHygiv6ejlGDCH6OXHanmYndlleDs7bObkXeg5stpMgXv
|
||||
GYNRePGCRrk1mZi9i+7mVIj2OE96vftYrnKt71KccplUYtQU1506cfgtBkvDTYHh
|
||||
Jnrx7+PNrxzOQzX5QEPprxmbwVXbqvwH4qwg57dgyGZtQ3GVy62PRdxD65UWXsZg
|
||||
IQgxkSyCpzJqBonATNdQMNXwyHyQB8p5ON7YzwIDAQABAoIBAQCG4doj3Apa8z+n
|
||||
IShbT1+cOyQi34A+xOIA151Hh7xmFxN0afRd/iWt3JUQ/OcLgQRZbDM7DSD+3W5H
|
||||
r+G7xfQkpwFxx/T3g58+f7ehYx+GcJQWyhxJ88zNIkBnyb4KCAE5WBOOW9IGajPe
|
||||
yE9pgUGMlPsXpYoKfHIOHg+NGY1pWUGBfBNR2kGrbkpZMmyy5bGa8dyrwAFBFRru
|
||||
kcmmKvate8UlbRspFtd4nR/GQLTBrcDJ1k1i1Su/4BpDuDeK6LPI8ZRePGqbdcxk
|
||||
TS30lsdYozuGfjZ5Zu8lSIJ//+7RjfDg8r684dpWjpalq8Quen60ZrIs01CSbfyU
|
||||
k8gOzTHhAoGBAOKhp41wXveegq+WylSXFyngm4bzF4dVdTRsSbJVk7NaOx1vCU6o
|
||||
/xIHoGEQyLI6wF+EaHmY89/Qu6tSV97XyBbiKeskopv5iXS/BsWTHJ1VbCA1ZLmK
|
||||
HgGllEkS0xfc9AdB7b6/K7LxAAQVKP3DtN6+6pSDZh9Sv2M1j0DbhkNbAoGBAMmg
|
||||
HcMfExaaeskjHqyLudtKX+znwaIoumleOGuavohR4R+Fpk8Yv8Xhb5U7Yr4gk0vY
|
||||
CFmhp1WAi6QMZ/8jePlKKXl3Ney827luoKiMczp2DoYE0t0u2Kw3LfkNKfjADZ7d
|
||||
JI6xPJV9/X1erwjq+4UdKqrpOf05SY4nkBMcvr6dAoGAXzisvbDJNiFTp5Mj0Abr
|
||||
pJzKvBjHegVeCXi2PkfWlzUCQYu1zWcURO8PY7k5mik1SuzHONAbJ578Oy+N3AOt
|
||||
/m9oTXRHHmHqbzMUFU+KZlDN7XqBp7NwiCCZ/Vn7d7tOjP4Wdl68baL07sI1RupD
|
||||
xJNS3LOY5PBPmc+XMRkLgKECgYEAgBNDlJSCrZMHeAjlDTncn53I/VXiPD2e3BvL
|
||||
vx6W9UT9ueZN1GSmPO6M0MDeYmOS7VSXSUhUYQ28pkJzNTC1QbWITu4YxP7anBnX
|
||||
1/kPoQ0pAJzDzVharlqGy3M/PBHTFRzogfO3xkY35ZFlokaR6uayGcr42Q+w16nt
|
||||
7RYPXEkCgYEA3GQYirHnGZuQ952jMvduqnpgkJiSnr0fa+94Rwa1pAhxHLFMo5s4
|
||||
fqZOtqKPj2s5X1JR0VCey1ilCcaAhWeb3tXCpbYLZSbMtjtqwA6LUeGY+Xdupsjw
|
||||
cfWIcOfHsIm2kP+RCxEnZf1XwiN9wyJeiUKlE0dqmx9j7F0Bm+7YDhI=
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
)
|
||||
|
||||
func tlsConfig() *tls.Config {
|
||||
cert, err := tls.X509KeyPair(rawCert, rawKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
PreferServerCipherSuites: true,
|
||||
}
|
||||
}
|
||||
|
||||
func testServer() {
|
||||
s := &http.Server{
|
||||
Addr: ":18888",
|
||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, "abcdefghijklmnopqrstuvwxyz")
|
||||
}),
|
||||
}
|
||||
log.Fatal(s.ListenAndServe())
|
||||
}
|
34
examples/forward/direct/client.go
Normal file
34
examples/forward/direct/client.go
Normal file
@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/ginuerzh/gost"
|
||||
)
|
||||
|
||||
func main() {
|
||||
tcpForward()
|
||||
}
|
||||
|
||||
func tcpForward() {
|
||||
chain := gost.NewChain(
|
||||
gost.Node{
|
||||
Addr: "localhost:11222",
|
||||
Client: &gost.Client{
|
||||
Connector: gost.SSHDirectForwardConnector(),
|
||||
Transporter: gost.SSHForwardTransporter(),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.TCPListener(":11800")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.TCPDirectForwardHandler(
|
||||
"localhost:22",
|
||||
gost.ChainHandlerOption(chain),
|
||||
)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
82
examples/forward/direct/server.go
Normal file
82
examples/forward/direct/server.go
Normal file
@ -0,0 +1,82 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"log"
|
||||
|
||||
"github.com/ginuerzh/gost"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sshForwardServer()
|
||||
}
|
||||
|
||||
func sshForwardServer() {
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.TCPListener(":11222")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.SSHForwardHandler(
|
||||
gost.AddrHandlerOption(":11222"),
|
||||
// gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
|
||||
gost.TLSConfigHandlerOption(tlsConfig()),
|
||||
)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
var (
|
||||
rawCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIC+jCCAeKgAwIBAgIRAMlREhz8Miu1FQozsxbeqyMwDQYJKoZIhvcNAQELBQAw
|
||||
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNzA1MTkwNTM5MDJaFw0xODA1MTkwNTM5
|
||||
MDJaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
|
||||
ggEKAoIBAQCyfqvv0kDriciEAVIW6JaWYFCL9a19jj1wmAGmVGxV3kNsr01kpa6N
|
||||
0EBqnrcy7WknhCt1d43CqhKtTcXgJ/J9phZVxlizb8sUB85hm+MvP0N3HCg3f0Jw
|
||||
hLuMrPijS6xjyw0fKCK/p6OUYMIfo5cdqeZid2WV4Ozts5uRd6Dmy2kyBe8Zg1F4
|
||||
8YJGuTWZmL2L7uZUiPY4T3q9+1iucq3vUpxymVRi1BTXnTpx+C0GS8NNgeEmevHv
|
||||
482vHM5DNflAQ+mvGZvBVduq/AfirCDnt2DIZm1DcZXLrY9F3EPrlRZexmAhCDGR
|
||||
LIKnMmoGicBM11Aw1fDIfJAHynk43tjPAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF
|
||||
oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC
|
||||
CWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAx8Lna8DcQv0bRB3L9i2+KRN
|
||||
l/UhPCoFagxk1cZore4p0w+1m7OgigOoTpg5jh78DzVDhScZlgJ0bBVYp5rojeJS
|
||||
cBDC9lCDcaXQfFmT5LykCAwIgw/gs+rw5Aq0y3D0m8CcqKosyZa9wnZ2cVy/+45w
|
||||
emcSdboc65ueZScv38/W7aTUoVRcjyRUv0jv0zW0EPnnDlluVkeZo9spBhiTTwoj
|
||||
b3zGODs6alTNIJwZIHNxxyOmfJPpVVp8BzGbMk7YBixSlZ/vbrrYV34TcSiy7J57
|
||||
lNNoVWM+OwiVk1+AEZfQDwaQfef5tsIkAZBUyITkkDKRhygtwM2110dejbEsgg==
|
||||
-----END CERTIFICATE-----`)
|
||||
rawKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAsn6r79JA64nIhAFSFuiWlmBQi/WtfY49cJgBplRsVd5DbK9N
|
||||
ZKWujdBAap63Mu1pJ4QrdXeNwqoSrU3F4CfyfaYWVcZYs2/LFAfOYZvjLz9Ddxwo
|
||||
N39CcIS7jKz4o0usY8sNHygiv6ejlGDCH6OXHanmYndlleDs7bObkXeg5stpMgXv
|
||||
GYNRePGCRrk1mZi9i+7mVIj2OE96vftYrnKt71KccplUYtQU1506cfgtBkvDTYHh
|
||||
Jnrx7+PNrxzOQzX5QEPprxmbwVXbqvwH4qwg57dgyGZtQ3GVy62PRdxD65UWXsZg
|
||||
IQgxkSyCpzJqBonATNdQMNXwyHyQB8p5ON7YzwIDAQABAoIBAQCG4doj3Apa8z+n
|
||||
IShbT1+cOyQi34A+xOIA151Hh7xmFxN0afRd/iWt3JUQ/OcLgQRZbDM7DSD+3W5H
|
||||
r+G7xfQkpwFxx/T3g58+f7ehYx+GcJQWyhxJ88zNIkBnyb4KCAE5WBOOW9IGajPe
|
||||
yE9pgUGMlPsXpYoKfHIOHg+NGY1pWUGBfBNR2kGrbkpZMmyy5bGa8dyrwAFBFRru
|
||||
kcmmKvate8UlbRspFtd4nR/GQLTBrcDJ1k1i1Su/4BpDuDeK6LPI8ZRePGqbdcxk
|
||||
TS30lsdYozuGfjZ5Zu8lSIJ//+7RjfDg8r684dpWjpalq8Quen60ZrIs01CSbfyU
|
||||
k8gOzTHhAoGBAOKhp41wXveegq+WylSXFyngm4bzF4dVdTRsSbJVk7NaOx1vCU6o
|
||||
/xIHoGEQyLI6wF+EaHmY89/Qu6tSV97XyBbiKeskopv5iXS/BsWTHJ1VbCA1ZLmK
|
||||
HgGllEkS0xfc9AdB7b6/K7LxAAQVKP3DtN6+6pSDZh9Sv2M1j0DbhkNbAoGBAMmg
|
||||
HcMfExaaeskjHqyLudtKX+znwaIoumleOGuavohR4R+Fpk8Yv8Xhb5U7Yr4gk0vY
|
||||
CFmhp1WAi6QMZ/8jePlKKXl3Ney827luoKiMczp2DoYE0t0u2Kw3LfkNKfjADZ7d
|
||||
JI6xPJV9/X1erwjq+4UdKqrpOf05SY4nkBMcvr6dAoGAXzisvbDJNiFTp5Mj0Abr
|
||||
pJzKvBjHegVeCXi2PkfWlzUCQYu1zWcURO8PY7k5mik1SuzHONAbJ578Oy+N3AOt
|
||||
/m9oTXRHHmHqbzMUFU+KZlDN7XqBp7NwiCCZ/Vn7d7tOjP4Wdl68baL07sI1RupD
|
||||
xJNS3LOY5PBPmc+XMRkLgKECgYEAgBNDlJSCrZMHeAjlDTncn53I/VXiPD2e3BvL
|
||||
vx6W9UT9ueZN1GSmPO6M0MDeYmOS7VSXSUhUYQ28pkJzNTC1QbWITu4YxP7anBnX
|
||||
1/kPoQ0pAJzDzVharlqGy3M/PBHTFRzogfO3xkY35ZFlokaR6uayGcr42Q+w16nt
|
||||
7RYPXEkCgYEA3GQYirHnGZuQ952jMvduqnpgkJiSnr0fa+94Rwa1pAhxHLFMo5s4
|
||||
fqZOtqKPj2s5X1JR0VCey1ilCcaAhWeb3tXCpbYLZSbMtjtqwA6LUeGY+Xdupsjw
|
||||
cfWIcOfHsIm2kP+RCxEnZf1XwiN9wyJeiUKlE0dqmx9j7F0Bm+7YDhI=
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
)
|
||||
|
||||
func tlsConfig() *tls.Config {
|
||||
cert, err := tls.X509KeyPair(rawCert, rawKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||
}
|
35
examples/forward/remote/client.go
Normal file
35
examples/forward/remote/client.go
Normal file
@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/ginuerzh/gost"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sshRemoteForward()
|
||||
}
|
||||
|
||||
func sshRemoteForward() {
|
||||
chain := gost.NewChain(
|
||||
gost.Node{
|
||||
Protocol: "forward",
|
||||
Transport: "ssh",
|
||||
Addr: "localhost:11222",
|
||||
Client: &gost.Client{
|
||||
Connector: gost.SSHRemoteForwardConnector(),
|
||||
Transporter: gost.SSHForwardTransporter(),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.TCPRemoteForwardListener(":11800", chain)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.TCPRemoteForwardHandler(
|
||||
"localhost:10000",
|
||||
)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
82
examples/forward/remote/server.go
Normal file
82
examples/forward/remote/server.go
Normal file
@ -0,0 +1,82 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"log"
|
||||
|
||||
"github.com/ginuerzh/gost"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sshRemoteForwardServer()
|
||||
}
|
||||
|
||||
func sshRemoteForwardServer() {
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.TCPListener(":11222")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.SSHForwardHandler(
|
||||
gost.AddrHandlerOption(":11222"),
|
||||
// gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
|
||||
gost.TLSConfigHandlerOption(tlsConfig()),
|
||||
)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
var (
|
||||
rawCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIC+jCCAeKgAwIBAgIRAMlREhz8Miu1FQozsxbeqyMwDQYJKoZIhvcNAQELBQAw
|
||||
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNzA1MTkwNTM5MDJaFw0xODA1MTkwNTM5
|
||||
MDJaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
|
||||
ggEKAoIBAQCyfqvv0kDriciEAVIW6JaWYFCL9a19jj1wmAGmVGxV3kNsr01kpa6N
|
||||
0EBqnrcy7WknhCt1d43CqhKtTcXgJ/J9phZVxlizb8sUB85hm+MvP0N3HCg3f0Jw
|
||||
hLuMrPijS6xjyw0fKCK/p6OUYMIfo5cdqeZid2WV4Ozts5uRd6Dmy2kyBe8Zg1F4
|
||||
8YJGuTWZmL2L7uZUiPY4T3q9+1iucq3vUpxymVRi1BTXnTpx+C0GS8NNgeEmevHv
|
||||
482vHM5DNflAQ+mvGZvBVduq/AfirCDnt2DIZm1DcZXLrY9F3EPrlRZexmAhCDGR
|
||||
LIKnMmoGicBM11Aw1fDIfJAHynk43tjPAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF
|
||||
oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC
|
||||
CWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAx8Lna8DcQv0bRB3L9i2+KRN
|
||||
l/UhPCoFagxk1cZore4p0w+1m7OgigOoTpg5jh78DzVDhScZlgJ0bBVYp5rojeJS
|
||||
cBDC9lCDcaXQfFmT5LykCAwIgw/gs+rw5Aq0y3D0m8CcqKosyZa9wnZ2cVy/+45w
|
||||
emcSdboc65ueZScv38/W7aTUoVRcjyRUv0jv0zW0EPnnDlluVkeZo9spBhiTTwoj
|
||||
b3zGODs6alTNIJwZIHNxxyOmfJPpVVp8BzGbMk7YBixSlZ/vbrrYV34TcSiy7J57
|
||||
lNNoVWM+OwiVk1+AEZfQDwaQfef5tsIkAZBUyITkkDKRhygtwM2110dejbEsgg==
|
||||
-----END CERTIFICATE-----`)
|
||||
rawKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAsn6r79JA64nIhAFSFuiWlmBQi/WtfY49cJgBplRsVd5DbK9N
|
||||
ZKWujdBAap63Mu1pJ4QrdXeNwqoSrU3F4CfyfaYWVcZYs2/LFAfOYZvjLz9Ddxwo
|
||||
N39CcIS7jKz4o0usY8sNHygiv6ejlGDCH6OXHanmYndlleDs7bObkXeg5stpMgXv
|
||||
GYNRePGCRrk1mZi9i+7mVIj2OE96vftYrnKt71KccplUYtQU1506cfgtBkvDTYHh
|
||||
Jnrx7+PNrxzOQzX5QEPprxmbwVXbqvwH4qwg57dgyGZtQ3GVy62PRdxD65UWXsZg
|
||||
IQgxkSyCpzJqBonATNdQMNXwyHyQB8p5ON7YzwIDAQABAoIBAQCG4doj3Apa8z+n
|
||||
IShbT1+cOyQi34A+xOIA151Hh7xmFxN0afRd/iWt3JUQ/OcLgQRZbDM7DSD+3W5H
|
||||
r+G7xfQkpwFxx/T3g58+f7ehYx+GcJQWyhxJ88zNIkBnyb4KCAE5WBOOW9IGajPe
|
||||
yE9pgUGMlPsXpYoKfHIOHg+NGY1pWUGBfBNR2kGrbkpZMmyy5bGa8dyrwAFBFRru
|
||||
kcmmKvate8UlbRspFtd4nR/GQLTBrcDJ1k1i1Su/4BpDuDeK6LPI8ZRePGqbdcxk
|
||||
TS30lsdYozuGfjZ5Zu8lSIJ//+7RjfDg8r684dpWjpalq8Quen60ZrIs01CSbfyU
|
||||
k8gOzTHhAoGBAOKhp41wXveegq+WylSXFyngm4bzF4dVdTRsSbJVk7NaOx1vCU6o
|
||||
/xIHoGEQyLI6wF+EaHmY89/Qu6tSV97XyBbiKeskopv5iXS/BsWTHJ1VbCA1ZLmK
|
||||
HgGllEkS0xfc9AdB7b6/K7LxAAQVKP3DtN6+6pSDZh9Sv2M1j0DbhkNbAoGBAMmg
|
||||
HcMfExaaeskjHqyLudtKX+znwaIoumleOGuavohR4R+Fpk8Yv8Xhb5U7Yr4gk0vY
|
||||
CFmhp1WAi6QMZ/8jePlKKXl3Ney827luoKiMczp2DoYE0t0u2Kw3LfkNKfjADZ7d
|
||||
JI6xPJV9/X1erwjq+4UdKqrpOf05SY4nkBMcvr6dAoGAXzisvbDJNiFTp5Mj0Abr
|
||||
pJzKvBjHegVeCXi2PkfWlzUCQYu1zWcURO8PY7k5mik1SuzHONAbJ578Oy+N3AOt
|
||||
/m9oTXRHHmHqbzMUFU+KZlDN7XqBp7NwiCCZ/Vn7d7tOjP4Wdl68baL07sI1RupD
|
||||
xJNS3LOY5PBPmc+XMRkLgKECgYEAgBNDlJSCrZMHeAjlDTncn53I/VXiPD2e3BvL
|
||||
vx6W9UT9ueZN1GSmPO6M0MDeYmOS7VSXSUhUYQ28pkJzNTC1QbWITu4YxP7anBnX
|
||||
1/kPoQ0pAJzDzVharlqGy3M/PBHTFRzogfO3xkY35ZFlokaR6uayGcr42Q+w16nt
|
||||
7RYPXEkCgYEA3GQYirHnGZuQ952jMvduqnpgkJiSnr0fa+94Rwa1pAhxHLFMo5s4
|
||||
fqZOtqKPj2s5X1JR0VCey1ilCcaAhWeb3tXCpbYLZSbMtjtqwA6LUeGY+Xdupsjw
|
||||
cfWIcOfHsIm2kP+RCxEnZf1XwiN9wyJeiUKlE0dqmx9j7F0Bm+7YDhI=
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
)
|
||||
|
||||
func tlsConfig() *tls.Config {
|
||||
cert, err := tls.X509KeyPair(rawCert, rawKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||
}
|
53
examples/forward/udp/cli.go
Normal file
53
examples/forward/udp/cli.go
Normal file
@ -0,0 +1,53 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
concurrency int
|
||||
saddr string
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
|
||||
flag.StringVar(&saddr, "S", ":18080", "server address")
|
||||
flag.IntVar(&concurrency, "c", 1, "Number of multiple echo to make at a time")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
func main() {
|
||||
for i := 0; i < concurrency; i++ {
|
||||
go udpEchoLoop()
|
||||
}
|
||||
select{}
|
||||
}
|
||||
|
||||
func udpEchoLoop() {
|
||||
addr, err := net.ResolveUDPAddr("udp", saddr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
conn, err := net.DialUDP("udp", nil, addr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
msg := []byte(`abcdefghijklmnopqrstuvwxyz`)
|
||||
for {
|
||||
if _, err := conn.Write(msg); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
b := make([]byte, 1024)
|
||||
_, err := conn.Read(b)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// log.Println(string(b[:n]))
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
57
examples/forward/udp/direct.go
Normal file
57
examples/forward/udp/direct.go
Normal file
@ -0,0 +1,57 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/ginuerzh/gost"
|
||||
)
|
||||
|
||||
var (
|
||||
laddr, faddr string
|
||||
quiet bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
|
||||
flag.StringVar(&laddr, "L", ":18080", "listen address")
|
||||
flag.StringVar(&faddr, "F", ":8080", "forward address")
|
||||
flag.BoolVar(&quiet, "q", false, "quiet mode")
|
||||
flag.BoolVar(&gost.Debug, "d", false, "debug mode")
|
||||
flag.Parse()
|
||||
|
||||
if quiet {
|
||||
gost.SetLogger(&gost.NopLogger{})
|
||||
}
|
||||
}
|
||||
func main() {
|
||||
udpDirectForwardServer()
|
||||
}
|
||||
|
||||
func udpDirectForwardServer() {
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.UDPDirectForwardListener(laddr, time.Second*30)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.UDPDirectForwardHandler(
|
||||
faddr,
|
||||
/*
|
||||
gost.ChainHandlerOption(gost.NewChain(gost.Node{
|
||||
Protocol: "socks5",
|
||||
Transport: "tcp",
|
||||
Addr: ":11080",
|
||||
User: url.UserPassword("admin", "123456"),
|
||||
Client: &gost.Client{
|
||||
Connector: gost.SOCKS5Connector(
|
||||
url.UserPassword("admin", "123456"),
|
||||
),
|
||||
Transporter: gost.TCPTransporter(),
|
||||
},
|
||||
})),
|
||||
*/
|
||||
)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
60
examples/forward/udp/remote.go
Normal file
60
examples/forward/udp/remote.go
Normal file
@ -0,0 +1,60 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/ginuerzh/gost"
|
||||
)
|
||||
|
||||
var (
|
||||
laddr, faddr string
|
||||
quiet bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
|
||||
flag.StringVar(&laddr, "L", ":18080", "listen address")
|
||||
flag.StringVar(&faddr, "F", ":8080", "forward address")
|
||||
flag.BoolVar(&quiet, "q", false, "quiet mode")
|
||||
flag.BoolVar(&gost.Debug, "d", false, "debug mode")
|
||||
flag.Parse()
|
||||
|
||||
if quiet {
|
||||
gost.SetLogger(&gost.NopLogger{})
|
||||
}
|
||||
}
|
||||
func main() {
|
||||
udpRemoteForwardServer()
|
||||
}
|
||||
|
||||
func udpRemoteForwardServer() {
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.UDPRemoteForwardListener(
|
||||
laddr,
|
||||
/*
|
||||
gost.NewChain(gost.Node{
|
||||
Protocol: "socks5",
|
||||
Transport: "tcp",
|
||||
Addr: ":11080",
|
||||
User: url.UserPassword("admin", "123456"),
|
||||
Client: &gost.Client{
|
||||
Connector: gost.SOCKS5Connector(
|
||||
url.UserPassword("admin", "123456"),
|
||||
),
|
||||
Transporter: gost.TCPTransporter(),
|
||||
},
|
||||
}),
|
||||
*/
|
||||
nil,
|
||||
time.Second*30)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.UDPRemoteForwardHandler(
|
||||
faddr,
|
||||
)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
44
examples/forward/udp/srv.go
Normal file
44
examples/forward/udp/srv.go
Normal file
@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"net"
|
||||
)
|
||||
|
||||
var (
|
||||
laddr string
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
|
||||
flag.StringVar(&laddr, "L", ":8080", "listen address")
|
||||
flag.Parse()
|
||||
}
|
||||
func main() {
|
||||
udpEchoServer()
|
||||
}
|
||||
|
||||
func udpEchoServer() {
|
||||
addr, err := net.ResolveUDPAddr("udp", laddr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
conn, err := net.ListenUDP("udp", addr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for {
|
||||
b := make([]byte, 1024)
|
||||
n, raddr, err := conn.ReadFromUDP(b)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if _, err = conn.WriteToUDP(b[:n], raddr); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
125
examples/http2/http2.go
Normal file
125
examples/http2/http2.go
Normal file
@ -0,0 +1,125 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
"log"
|
||||
"net/url"
|
||||
|
||||
"golang.org/x/net/http2"
|
||||
|
||||
"github.com/ginuerzh/gost"
|
||||
)
|
||||
|
||||
var (
|
||||
quiet bool
|
||||
keyFile, certFile string
|
||||
laddr string
|
||||
user, passwd string
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
|
||||
flag.StringVar(&laddr, "L", ":1443", "listen address")
|
||||
flag.StringVar(&user, "u", "", "username")
|
||||
flag.StringVar(&passwd, "p", "", "password")
|
||||
flag.BoolVar(&quiet, "q", false, "quiet mode")
|
||||
flag.BoolVar(&gost.Debug, "d", false, "debug mode")
|
||||
flag.BoolVar(&http2.VerboseLogs, "v", false, "HTTP2 verbose log")
|
||||
flag.StringVar(&keyFile, "key", "key.pem", "TLS key file")
|
||||
flag.StringVar(&certFile, "cert", "cert.pem", "TLS cert file")
|
||||
flag.Parse()
|
||||
|
||||
if quiet {
|
||||
gost.SetLogger(&gost.NopLogger{})
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
http2Server()
|
||||
}
|
||||
|
||||
func http2Server() {
|
||||
cert, er := tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if er != nil {
|
||||
log.Println(er)
|
||||
cert, er = tls.X509KeyPair(rawCert, rawKey)
|
||||
if er != nil {
|
||||
panic(er)
|
||||
}
|
||||
}
|
||||
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.HTTP2Listener(laddr, &tls.Config{Certificates: []tls.Certificate{cert}})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var users []*url.Userinfo
|
||||
if user != "" || passwd != "" {
|
||||
users = append(users, url.UserPassword(user, passwd))
|
||||
}
|
||||
|
||||
h := gost.HTTP2Handler(
|
||||
gost.UsersHandlerOption(users...),
|
||||
gost.AddrHandlerOption(laddr),
|
||||
)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
var (
|
||||
rawCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIC+jCCAeKgAwIBAgIRAMlREhz8Miu1FQozsxbeqyMwDQYJKoZIhvcNAQELBQAw
|
||||
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNzA1MTkwNTM5MDJaFw0xODA1MTkwNTM5
|
||||
MDJaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
|
||||
ggEKAoIBAQCyfqvv0kDriciEAVIW6JaWYFCL9a19jj1wmAGmVGxV3kNsr01kpa6N
|
||||
0EBqnrcy7WknhCt1d43CqhKtTcXgJ/J9phZVxlizb8sUB85hm+MvP0N3HCg3f0Jw
|
||||
hLuMrPijS6xjyw0fKCK/p6OUYMIfo5cdqeZid2WV4Ozts5uRd6Dmy2kyBe8Zg1F4
|
||||
8YJGuTWZmL2L7uZUiPY4T3q9+1iucq3vUpxymVRi1BTXnTpx+C0GS8NNgeEmevHv
|
||||
482vHM5DNflAQ+mvGZvBVduq/AfirCDnt2DIZm1DcZXLrY9F3EPrlRZexmAhCDGR
|
||||
LIKnMmoGicBM11Aw1fDIfJAHynk43tjPAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF
|
||||
oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC
|
||||
CWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAx8Lna8DcQv0bRB3L9i2+KRN
|
||||
l/UhPCoFagxk1cZore4p0w+1m7OgigOoTpg5jh78DzVDhScZlgJ0bBVYp5rojeJS
|
||||
cBDC9lCDcaXQfFmT5LykCAwIgw/gs+rw5Aq0y3D0m8CcqKosyZa9wnZ2cVy/+45w
|
||||
emcSdboc65ueZScv38/W7aTUoVRcjyRUv0jv0zW0EPnnDlluVkeZo9spBhiTTwoj
|
||||
b3zGODs6alTNIJwZIHNxxyOmfJPpVVp8BzGbMk7YBixSlZ/vbrrYV34TcSiy7J57
|
||||
lNNoVWM+OwiVk1+AEZfQDwaQfef5tsIkAZBUyITkkDKRhygtwM2110dejbEsgg==
|
||||
-----END CERTIFICATE-----`)
|
||||
rawKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAsn6r79JA64nIhAFSFuiWlmBQi/WtfY49cJgBplRsVd5DbK9N
|
||||
ZKWujdBAap63Mu1pJ4QrdXeNwqoSrU3F4CfyfaYWVcZYs2/LFAfOYZvjLz9Ddxwo
|
||||
N39CcIS7jKz4o0usY8sNHygiv6ejlGDCH6OXHanmYndlleDs7bObkXeg5stpMgXv
|
||||
GYNRePGCRrk1mZi9i+7mVIj2OE96vftYrnKt71KccplUYtQU1506cfgtBkvDTYHh
|
||||
Jnrx7+PNrxzOQzX5QEPprxmbwVXbqvwH4qwg57dgyGZtQ3GVy62PRdxD65UWXsZg
|
||||
IQgxkSyCpzJqBonATNdQMNXwyHyQB8p5ON7YzwIDAQABAoIBAQCG4doj3Apa8z+n
|
||||
IShbT1+cOyQi34A+xOIA151Hh7xmFxN0afRd/iWt3JUQ/OcLgQRZbDM7DSD+3W5H
|
||||
r+G7xfQkpwFxx/T3g58+f7ehYx+GcJQWyhxJ88zNIkBnyb4KCAE5WBOOW9IGajPe
|
||||
yE9pgUGMlPsXpYoKfHIOHg+NGY1pWUGBfBNR2kGrbkpZMmyy5bGa8dyrwAFBFRru
|
||||
kcmmKvate8UlbRspFtd4nR/GQLTBrcDJ1k1i1Su/4BpDuDeK6LPI8ZRePGqbdcxk
|
||||
TS30lsdYozuGfjZ5Zu8lSIJ//+7RjfDg8r684dpWjpalq8Quen60ZrIs01CSbfyU
|
||||
k8gOzTHhAoGBAOKhp41wXveegq+WylSXFyngm4bzF4dVdTRsSbJVk7NaOx1vCU6o
|
||||
/xIHoGEQyLI6wF+EaHmY89/Qu6tSV97XyBbiKeskopv5iXS/BsWTHJ1VbCA1ZLmK
|
||||
HgGllEkS0xfc9AdB7b6/K7LxAAQVKP3DtN6+6pSDZh9Sv2M1j0DbhkNbAoGBAMmg
|
||||
HcMfExaaeskjHqyLudtKX+znwaIoumleOGuavohR4R+Fpk8Yv8Xhb5U7Yr4gk0vY
|
||||
CFmhp1WAi6QMZ/8jePlKKXl3Ney827luoKiMczp2DoYE0t0u2Kw3LfkNKfjADZ7d
|
||||
JI6xPJV9/X1erwjq+4UdKqrpOf05SY4nkBMcvr6dAoGAXzisvbDJNiFTp5Mj0Abr
|
||||
pJzKvBjHegVeCXi2PkfWlzUCQYu1zWcURO8PY7k5mik1SuzHONAbJ578Oy+N3AOt
|
||||
/m9oTXRHHmHqbzMUFU+KZlDN7XqBp7NwiCCZ/Vn7d7tOjP4Wdl68baL07sI1RupD
|
||||
xJNS3LOY5PBPmc+XMRkLgKECgYEAgBNDlJSCrZMHeAjlDTncn53I/VXiPD2e3BvL
|
||||
vx6W9UT9ueZN1GSmPO6M0MDeYmOS7VSXSUhUYQ28pkJzNTC1QbWITu4YxP7anBnX
|
||||
1/kPoQ0pAJzDzVharlqGy3M/PBHTFRzogfO3xkY35ZFlokaR6uayGcr42Q+w16nt
|
||||
7RYPXEkCgYEA3GQYirHnGZuQ952jMvduqnpgkJiSnr0fa+94Rwa1pAhxHLFMo5s4
|
||||
fqZOtqKPj2s5X1JR0VCey1ilCcaAhWeb3tXCpbYLZSbMtjtqwA6LUeGY+Xdupsjw
|
||||
cfWIcOfHsIm2kP+RCxEnZf1XwiN9wyJeiUKlE0dqmx9j7F0Bm+7YDhI=
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
)
|
||||
|
||||
func tlsConfig() *tls.Config {
|
||||
cert, err := tls.X509KeyPair(rawCert, rawKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||
}
|
110
examples/quic/quicc.go
Normal file
110
examples/quic/quicc.go
Normal file
@ -0,0 +1,110 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/ginuerzh/gost"
|
||||
)
|
||||
|
||||
var (
|
||||
laddr, faddr string
|
||||
quiet bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
|
||||
flag.StringVar(&laddr, "L", ":18080", "listen address")
|
||||
flag.StringVar(&faddr, "F", "localhost:6121", "forward address")
|
||||
flag.BoolVar(&quiet, "q", false, "quiet mode")
|
||||
flag.BoolVar(&gost.Debug, "d", false, "debug mode")
|
||||
flag.Parse()
|
||||
|
||||
if quiet {
|
||||
gost.SetLogger(&gost.NopLogger{})
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
chain := gost.NewChain(
|
||||
gost.Node{
|
||||
Protocol: "socks5",
|
||||
Transport: "quic",
|
||||
Addr: faddr,
|
||||
Client: &gost.Client{
|
||||
Connector: gost.SOCKS5Connector(nil),
|
||||
Transporter: gost.QUICTransporter(&gost.QUICConfig{Timeout: 30 * time.Second, KeepAlive: true}),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.TCPListener(laddr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.SOCKS5Handler(
|
||||
gost.ChainHandlerOption(chain),
|
||||
gost.TLSConfigHandlerOption(tlsConfig()),
|
||||
)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
var (
|
||||
rawCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIC+jCCAeKgAwIBAgIRAMlREhz8Miu1FQozsxbeqyMwDQYJKoZIhvcNAQELBQAw
|
||||
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNzA1MTkwNTM5MDJaFw0xODA1MTkwNTM5
|
||||
MDJaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
|
||||
ggEKAoIBAQCyfqvv0kDriciEAVIW6JaWYFCL9a19jj1wmAGmVGxV3kNsr01kpa6N
|
||||
0EBqnrcy7WknhCt1d43CqhKtTcXgJ/J9phZVxlizb8sUB85hm+MvP0N3HCg3f0Jw
|
||||
hLuMrPijS6xjyw0fKCK/p6OUYMIfo5cdqeZid2WV4Ozts5uRd6Dmy2kyBe8Zg1F4
|
||||
8YJGuTWZmL2L7uZUiPY4T3q9+1iucq3vUpxymVRi1BTXnTpx+C0GS8NNgeEmevHv
|
||||
482vHM5DNflAQ+mvGZvBVduq/AfirCDnt2DIZm1DcZXLrY9F3EPrlRZexmAhCDGR
|
||||
LIKnMmoGicBM11Aw1fDIfJAHynk43tjPAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF
|
||||
oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC
|
||||
CWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAx8Lna8DcQv0bRB3L9i2+KRN
|
||||
l/UhPCoFagxk1cZore4p0w+1m7OgigOoTpg5jh78DzVDhScZlgJ0bBVYp5rojeJS
|
||||
cBDC9lCDcaXQfFmT5LykCAwIgw/gs+rw5Aq0y3D0m8CcqKosyZa9wnZ2cVy/+45w
|
||||
emcSdboc65ueZScv38/W7aTUoVRcjyRUv0jv0zW0EPnnDlluVkeZo9spBhiTTwoj
|
||||
b3zGODs6alTNIJwZIHNxxyOmfJPpVVp8BzGbMk7YBixSlZ/vbrrYV34TcSiy7J57
|
||||
lNNoVWM+OwiVk1+AEZfQDwaQfef5tsIkAZBUyITkkDKRhygtwM2110dejbEsgg==
|
||||
-----END CERTIFICATE-----`)
|
||||
rawKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAsn6r79JA64nIhAFSFuiWlmBQi/WtfY49cJgBplRsVd5DbK9N
|
||||
ZKWujdBAap63Mu1pJ4QrdXeNwqoSrU3F4CfyfaYWVcZYs2/LFAfOYZvjLz9Ddxwo
|
||||
N39CcIS7jKz4o0usY8sNHygiv6ejlGDCH6OXHanmYndlleDs7bObkXeg5stpMgXv
|
||||
GYNRePGCRrk1mZi9i+7mVIj2OE96vftYrnKt71KccplUYtQU1506cfgtBkvDTYHh
|
||||
Jnrx7+PNrxzOQzX5QEPprxmbwVXbqvwH4qwg57dgyGZtQ3GVy62PRdxD65UWXsZg
|
||||
IQgxkSyCpzJqBonATNdQMNXwyHyQB8p5ON7YzwIDAQABAoIBAQCG4doj3Apa8z+n
|
||||
IShbT1+cOyQi34A+xOIA151Hh7xmFxN0afRd/iWt3JUQ/OcLgQRZbDM7DSD+3W5H
|
||||
r+G7xfQkpwFxx/T3g58+f7ehYx+GcJQWyhxJ88zNIkBnyb4KCAE5WBOOW9IGajPe
|
||||
yE9pgUGMlPsXpYoKfHIOHg+NGY1pWUGBfBNR2kGrbkpZMmyy5bGa8dyrwAFBFRru
|
||||
kcmmKvate8UlbRspFtd4nR/GQLTBrcDJ1k1i1Su/4BpDuDeK6LPI8ZRePGqbdcxk
|
||||
TS30lsdYozuGfjZ5Zu8lSIJ//+7RjfDg8r684dpWjpalq8Quen60ZrIs01CSbfyU
|
||||
k8gOzTHhAoGBAOKhp41wXveegq+WylSXFyngm4bzF4dVdTRsSbJVk7NaOx1vCU6o
|
||||
/xIHoGEQyLI6wF+EaHmY89/Qu6tSV97XyBbiKeskopv5iXS/BsWTHJ1VbCA1ZLmK
|
||||
HgGllEkS0xfc9AdB7b6/K7LxAAQVKP3DtN6+6pSDZh9Sv2M1j0DbhkNbAoGBAMmg
|
||||
HcMfExaaeskjHqyLudtKX+znwaIoumleOGuavohR4R+Fpk8Yv8Xhb5U7Yr4gk0vY
|
||||
CFmhp1WAi6QMZ/8jePlKKXl3Ney827luoKiMczp2DoYE0t0u2Kw3LfkNKfjADZ7d
|
||||
JI6xPJV9/X1erwjq+4UdKqrpOf05SY4nkBMcvr6dAoGAXzisvbDJNiFTp5Mj0Abr
|
||||
pJzKvBjHegVeCXi2PkfWlzUCQYu1zWcURO8PY7k5mik1SuzHONAbJ578Oy+N3AOt
|
||||
/m9oTXRHHmHqbzMUFU+KZlDN7XqBp7NwiCCZ/Vn7d7tOjP4Wdl68baL07sI1RupD
|
||||
xJNS3LOY5PBPmc+XMRkLgKECgYEAgBNDlJSCrZMHeAjlDTncn53I/VXiPD2e3BvL
|
||||
vx6W9UT9ueZN1GSmPO6M0MDeYmOS7VSXSUhUYQ28pkJzNTC1QbWITu4YxP7anBnX
|
||||
1/kPoQ0pAJzDzVharlqGy3M/PBHTFRzogfO3xkY35ZFlokaR6uayGcr42Q+w16nt
|
||||
7RYPXEkCgYEA3GQYirHnGZuQ952jMvduqnpgkJiSnr0fa+94Rwa1pAhxHLFMo5s4
|
||||
fqZOtqKPj2s5X1JR0VCey1ilCcaAhWeb3tXCpbYLZSbMtjtqwA6LUeGY+Xdupsjw
|
||||
cfWIcOfHsIm2kP+RCxEnZf1XwiN9wyJeiUKlE0dqmx9j7F0Bm+7YDhI=
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
)
|
||||
|
||||
func tlsConfig() *tls.Config {
|
||||
cert, err := tls.X509KeyPair(rawCert, rawKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||
}
|
99
examples/quic/quics.go
Normal file
99
examples/quic/quics.go
Normal file
@ -0,0 +1,99 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
"log"
|
||||
|
||||
"github.com/ginuerzh/gost"
|
||||
)
|
||||
|
||||
var (
|
||||
laddr string
|
||||
quiet bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
|
||||
flag.StringVar(&laddr, "L", ":6121", "listen address")
|
||||
flag.BoolVar(&quiet, "q", false, "quiet mode")
|
||||
flag.BoolVar(&gost.Debug, "d", false, "debug mode")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if quiet {
|
||||
gost.SetLogger(&gost.NopLogger{})
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
quicServer()
|
||||
}
|
||||
|
||||
func quicServer() {
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.QUICListener(laddr, &gost.QUICConfig{TLSConfig: tlsConfig()})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.SOCKS5Handler(gost.TLSConfigHandlerOption(tlsConfig()))
|
||||
log.Println("server listen on", laddr)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
var (
|
||||
rawCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIC+jCCAeKgAwIBAgIRAMlREhz8Miu1FQozsxbeqyMwDQYJKoZIhvcNAQELBQAw
|
||||
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNzA1MTkwNTM5MDJaFw0xODA1MTkwNTM5
|
||||
MDJaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
|
||||
ggEKAoIBAQCyfqvv0kDriciEAVIW6JaWYFCL9a19jj1wmAGmVGxV3kNsr01kpa6N
|
||||
0EBqnrcy7WknhCt1d43CqhKtTcXgJ/J9phZVxlizb8sUB85hm+MvP0N3HCg3f0Jw
|
||||
hLuMrPijS6xjyw0fKCK/p6OUYMIfo5cdqeZid2WV4Ozts5uRd6Dmy2kyBe8Zg1F4
|
||||
8YJGuTWZmL2L7uZUiPY4T3q9+1iucq3vUpxymVRi1BTXnTpx+C0GS8NNgeEmevHv
|
||||
482vHM5DNflAQ+mvGZvBVduq/AfirCDnt2DIZm1DcZXLrY9F3EPrlRZexmAhCDGR
|
||||
LIKnMmoGicBM11Aw1fDIfJAHynk43tjPAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF
|
||||
oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC
|
||||
CWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAx8Lna8DcQv0bRB3L9i2+KRN
|
||||
l/UhPCoFagxk1cZore4p0w+1m7OgigOoTpg5jh78DzVDhScZlgJ0bBVYp5rojeJS
|
||||
cBDC9lCDcaXQfFmT5LykCAwIgw/gs+rw5Aq0y3D0m8CcqKosyZa9wnZ2cVy/+45w
|
||||
emcSdboc65ueZScv38/W7aTUoVRcjyRUv0jv0zW0EPnnDlluVkeZo9spBhiTTwoj
|
||||
b3zGODs6alTNIJwZIHNxxyOmfJPpVVp8BzGbMk7YBixSlZ/vbrrYV34TcSiy7J57
|
||||
lNNoVWM+OwiVk1+AEZfQDwaQfef5tsIkAZBUyITkkDKRhygtwM2110dejbEsgg==
|
||||
-----END CERTIFICATE-----`)
|
||||
rawKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAsn6r79JA64nIhAFSFuiWlmBQi/WtfY49cJgBplRsVd5DbK9N
|
||||
ZKWujdBAap63Mu1pJ4QrdXeNwqoSrU3F4CfyfaYWVcZYs2/LFAfOYZvjLz9Ddxwo
|
||||
N39CcIS7jKz4o0usY8sNHygiv6ejlGDCH6OXHanmYndlleDs7bObkXeg5stpMgXv
|
||||
GYNRePGCRrk1mZi9i+7mVIj2OE96vftYrnKt71KccplUYtQU1506cfgtBkvDTYHh
|
||||
Jnrx7+PNrxzOQzX5QEPprxmbwVXbqvwH4qwg57dgyGZtQ3GVy62PRdxD65UWXsZg
|
||||
IQgxkSyCpzJqBonATNdQMNXwyHyQB8p5ON7YzwIDAQABAoIBAQCG4doj3Apa8z+n
|
||||
IShbT1+cOyQi34A+xOIA151Hh7xmFxN0afRd/iWt3JUQ/OcLgQRZbDM7DSD+3W5H
|
||||
r+G7xfQkpwFxx/T3g58+f7ehYx+GcJQWyhxJ88zNIkBnyb4KCAE5WBOOW9IGajPe
|
||||
yE9pgUGMlPsXpYoKfHIOHg+NGY1pWUGBfBNR2kGrbkpZMmyy5bGa8dyrwAFBFRru
|
||||
kcmmKvate8UlbRspFtd4nR/GQLTBrcDJ1k1i1Su/4BpDuDeK6LPI8ZRePGqbdcxk
|
||||
TS30lsdYozuGfjZ5Zu8lSIJ//+7RjfDg8r684dpWjpalq8Quen60ZrIs01CSbfyU
|
||||
k8gOzTHhAoGBAOKhp41wXveegq+WylSXFyngm4bzF4dVdTRsSbJVk7NaOx1vCU6o
|
||||
/xIHoGEQyLI6wF+EaHmY89/Qu6tSV97XyBbiKeskopv5iXS/BsWTHJ1VbCA1ZLmK
|
||||
HgGllEkS0xfc9AdB7b6/K7LxAAQVKP3DtN6+6pSDZh9Sv2M1j0DbhkNbAoGBAMmg
|
||||
HcMfExaaeskjHqyLudtKX+znwaIoumleOGuavohR4R+Fpk8Yv8Xhb5U7Yr4gk0vY
|
||||
CFmhp1WAi6QMZ/8jePlKKXl3Ney827luoKiMczp2DoYE0t0u2Kw3LfkNKfjADZ7d
|
||||
JI6xPJV9/X1erwjq+4UdKqrpOf05SY4nkBMcvr6dAoGAXzisvbDJNiFTp5Mj0Abr
|
||||
pJzKvBjHegVeCXi2PkfWlzUCQYu1zWcURO8PY7k5mik1SuzHONAbJ578Oy+N3AOt
|
||||
/m9oTXRHHmHqbzMUFU+KZlDN7XqBp7NwiCCZ/Vn7d7tOjP4Wdl68baL07sI1RupD
|
||||
xJNS3LOY5PBPmc+XMRkLgKECgYEAgBNDlJSCrZMHeAjlDTncn53I/VXiPD2e3BvL
|
||||
vx6W9UT9ueZN1GSmPO6M0MDeYmOS7VSXSUhUYQ28pkJzNTC1QbWITu4YxP7anBnX
|
||||
1/kPoQ0pAJzDzVharlqGy3M/PBHTFRzogfO3xkY35ZFlokaR6uayGcr42Q+w16nt
|
||||
7RYPXEkCgYEA3GQYirHnGZuQ952jMvduqnpgkJiSnr0fa+94Rwa1pAhxHLFMo5s4
|
||||
fqZOtqKPj2s5X1JR0VCey1ilCcaAhWeb3tXCpbYLZSbMtjtqwA6LUeGY+Xdupsjw
|
||||
cfWIcOfHsIm2kP+RCxEnZf1XwiN9wyJeiUKlE0dqmx9j7F0Bm+7YDhI=
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
)
|
||||
|
||||
func tlsConfig() *tls.Config {
|
||||
cert, err := tls.X509KeyPair(rawCert, rawKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||
}
|
113
examples/ssh/sshc.go
Normal file
113
examples/ssh/sshc.go
Normal file
@ -0,0 +1,113 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/ginuerzh/gost"
|
||||
)
|
||||
|
||||
var (
|
||||
laddr, faddr string
|
||||
quiet bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
|
||||
flag.StringVar(&laddr, "L", ":18080", "listen address")
|
||||
flag.StringVar(&faddr, "F", ":12222", "forward address")
|
||||
flag.BoolVar(&quiet, "q", false, "quiet mode")
|
||||
flag.BoolVar(&gost.Debug, "d", false, "debug mode")
|
||||
flag.Parse()
|
||||
|
||||
if quiet {
|
||||
gost.SetLogger(&gost.NopLogger{})
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
chain := gost.NewChain(
|
||||
gost.Node{
|
||||
Protocol: "socks5",
|
||||
Transport: "ssh",
|
||||
Addr: faddr,
|
||||
HandshakeOptions: []gost.HandshakeOption{
|
||||
gost.IntervalHandshakeOption(30 * time.Second),
|
||||
},
|
||||
Client: &gost.Client{
|
||||
Connector: gost.SOCKS5Connector(nil),
|
||||
Transporter: gost.SSHTunnelTransporter(),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.TCPListener(laddr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.SOCKS5Handler(
|
||||
gost.ChainHandlerOption(chain),
|
||||
gost.TLSConfigHandlerOption(tlsConfig()),
|
||||
)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
var (
|
||||
rawCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIC+jCCAeKgAwIBAgIRAMlREhz8Miu1FQozsxbeqyMwDQYJKoZIhvcNAQELBQAw
|
||||
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNzA1MTkwNTM5MDJaFw0xODA1MTkwNTM5
|
||||
MDJaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
|
||||
ggEKAoIBAQCyfqvv0kDriciEAVIW6JaWYFCL9a19jj1wmAGmVGxV3kNsr01kpa6N
|
||||
0EBqnrcy7WknhCt1d43CqhKtTcXgJ/J9phZVxlizb8sUB85hm+MvP0N3HCg3f0Jw
|
||||
hLuMrPijS6xjyw0fKCK/p6OUYMIfo5cdqeZid2WV4Ozts5uRd6Dmy2kyBe8Zg1F4
|
||||
8YJGuTWZmL2L7uZUiPY4T3q9+1iucq3vUpxymVRi1BTXnTpx+C0GS8NNgeEmevHv
|
||||
482vHM5DNflAQ+mvGZvBVduq/AfirCDnt2DIZm1DcZXLrY9F3EPrlRZexmAhCDGR
|
||||
LIKnMmoGicBM11Aw1fDIfJAHynk43tjPAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF
|
||||
oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC
|
||||
CWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAx8Lna8DcQv0bRB3L9i2+KRN
|
||||
l/UhPCoFagxk1cZore4p0w+1m7OgigOoTpg5jh78DzVDhScZlgJ0bBVYp5rojeJS
|
||||
cBDC9lCDcaXQfFmT5LykCAwIgw/gs+rw5Aq0y3D0m8CcqKosyZa9wnZ2cVy/+45w
|
||||
emcSdboc65ueZScv38/W7aTUoVRcjyRUv0jv0zW0EPnnDlluVkeZo9spBhiTTwoj
|
||||
b3zGODs6alTNIJwZIHNxxyOmfJPpVVp8BzGbMk7YBixSlZ/vbrrYV34TcSiy7J57
|
||||
lNNoVWM+OwiVk1+AEZfQDwaQfef5tsIkAZBUyITkkDKRhygtwM2110dejbEsgg==
|
||||
-----END CERTIFICATE-----`)
|
||||
rawKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAsn6r79JA64nIhAFSFuiWlmBQi/WtfY49cJgBplRsVd5DbK9N
|
||||
ZKWujdBAap63Mu1pJ4QrdXeNwqoSrU3F4CfyfaYWVcZYs2/LFAfOYZvjLz9Ddxwo
|
||||
N39CcIS7jKz4o0usY8sNHygiv6ejlGDCH6OXHanmYndlleDs7bObkXeg5stpMgXv
|
||||
GYNRePGCRrk1mZi9i+7mVIj2OE96vftYrnKt71KccplUYtQU1506cfgtBkvDTYHh
|
||||
Jnrx7+PNrxzOQzX5QEPprxmbwVXbqvwH4qwg57dgyGZtQ3GVy62PRdxD65UWXsZg
|
||||
IQgxkSyCpzJqBonATNdQMNXwyHyQB8p5ON7YzwIDAQABAoIBAQCG4doj3Apa8z+n
|
||||
IShbT1+cOyQi34A+xOIA151Hh7xmFxN0afRd/iWt3JUQ/OcLgQRZbDM7DSD+3W5H
|
||||
r+G7xfQkpwFxx/T3g58+f7ehYx+GcJQWyhxJ88zNIkBnyb4KCAE5WBOOW9IGajPe
|
||||
yE9pgUGMlPsXpYoKfHIOHg+NGY1pWUGBfBNR2kGrbkpZMmyy5bGa8dyrwAFBFRru
|
||||
kcmmKvate8UlbRspFtd4nR/GQLTBrcDJ1k1i1Su/4BpDuDeK6LPI8ZRePGqbdcxk
|
||||
TS30lsdYozuGfjZ5Zu8lSIJ//+7RjfDg8r684dpWjpalq8Quen60ZrIs01CSbfyU
|
||||
k8gOzTHhAoGBAOKhp41wXveegq+WylSXFyngm4bzF4dVdTRsSbJVk7NaOx1vCU6o
|
||||
/xIHoGEQyLI6wF+EaHmY89/Qu6tSV97XyBbiKeskopv5iXS/BsWTHJ1VbCA1ZLmK
|
||||
HgGllEkS0xfc9AdB7b6/K7LxAAQVKP3DtN6+6pSDZh9Sv2M1j0DbhkNbAoGBAMmg
|
||||
HcMfExaaeskjHqyLudtKX+znwaIoumleOGuavohR4R+Fpk8Yv8Xhb5U7Yr4gk0vY
|
||||
CFmhp1WAi6QMZ/8jePlKKXl3Ney827luoKiMczp2DoYE0t0u2Kw3LfkNKfjADZ7d
|
||||
JI6xPJV9/X1erwjq+4UdKqrpOf05SY4nkBMcvr6dAoGAXzisvbDJNiFTp5Mj0Abr
|
||||
pJzKvBjHegVeCXi2PkfWlzUCQYu1zWcURO8PY7k5mik1SuzHONAbJ578Oy+N3AOt
|
||||
/m9oTXRHHmHqbzMUFU+KZlDN7XqBp7NwiCCZ/Vn7d7tOjP4Wdl68baL07sI1RupD
|
||||
xJNS3LOY5PBPmc+XMRkLgKECgYEAgBNDlJSCrZMHeAjlDTncn53I/VXiPD2e3BvL
|
||||
vx6W9UT9ueZN1GSmPO6M0MDeYmOS7VSXSUhUYQ28pkJzNTC1QbWITu4YxP7anBnX
|
||||
1/kPoQ0pAJzDzVharlqGy3M/PBHTFRzogfO3xkY35ZFlokaR6uayGcr42Q+w16nt
|
||||
7RYPXEkCgYEA3GQYirHnGZuQ952jMvduqnpgkJiSnr0fa+94Rwa1pAhxHLFMo5s4
|
||||
fqZOtqKPj2s5X1JR0VCey1ilCcaAhWeb3tXCpbYLZSbMtjtqwA6LUeGY+Xdupsjw
|
||||
cfWIcOfHsIm2kP+RCxEnZf1XwiN9wyJeiUKlE0dqmx9j7F0Bm+7YDhI=
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
)
|
||||
|
||||
func tlsConfig() *tls.Config {
|
||||
cert, err := tls.X509KeyPair(rawCert, rawKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||
}
|
99
examples/ssh/sshd.go
Normal file
99
examples/ssh/sshd.go
Normal file
@ -0,0 +1,99 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
"log"
|
||||
|
||||
"github.com/ginuerzh/gost"
|
||||
)
|
||||
|
||||
var (
|
||||
laddr string
|
||||
quiet bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
|
||||
flag.StringVar(&laddr, "L", ":12222", "listen address")
|
||||
flag.BoolVar(&quiet, "q", false, "quiet mode")
|
||||
flag.BoolVar(&gost.Debug, "d", false, "debug mode")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if quiet {
|
||||
gost.SetLogger(&gost.NopLogger{})
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
sshTunnelServer()
|
||||
}
|
||||
|
||||
func sshTunnelServer() {
|
||||
s := &gost.Server{}
|
||||
ln, err := gost.SSHTunnelListener(laddr, &gost.SSHConfig{TLSConfig: tlsConfig()})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := gost.SOCKS5Handler(gost.TLSConfigHandlerOption(tlsConfig()))
|
||||
log.Println("server listen on", laddr)
|
||||
log.Fatal(s.Serve(ln, h))
|
||||
}
|
||||
|
||||
var (
|
||||
rawCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIC+jCCAeKgAwIBAgIRAMlREhz8Miu1FQozsxbeqyMwDQYJKoZIhvcNAQELBQAw
|
||||
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNzA1MTkwNTM5MDJaFw0xODA1MTkwNTM5
|
||||
MDJaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
|
||||
ggEKAoIBAQCyfqvv0kDriciEAVIW6JaWYFCL9a19jj1wmAGmVGxV3kNsr01kpa6N
|
||||
0EBqnrcy7WknhCt1d43CqhKtTcXgJ/J9phZVxlizb8sUB85hm+MvP0N3HCg3f0Jw
|
||||
hLuMrPijS6xjyw0fKCK/p6OUYMIfo5cdqeZid2WV4Ozts5uRd6Dmy2kyBe8Zg1F4
|
||||
8YJGuTWZmL2L7uZUiPY4T3q9+1iucq3vUpxymVRi1BTXnTpx+C0GS8NNgeEmevHv
|
||||
482vHM5DNflAQ+mvGZvBVduq/AfirCDnt2DIZm1DcZXLrY9F3EPrlRZexmAhCDGR
|
||||
LIKnMmoGicBM11Aw1fDIfJAHynk43tjPAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF
|
||||
oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC
|
||||
CWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAx8Lna8DcQv0bRB3L9i2+KRN
|
||||
l/UhPCoFagxk1cZore4p0w+1m7OgigOoTpg5jh78DzVDhScZlgJ0bBVYp5rojeJS
|
||||
cBDC9lCDcaXQfFmT5LykCAwIgw/gs+rw5Aq0y3D0m8CcqKosyZa9wnZ2cVy/+45w
|
||||
emcSdboc65ueZScv38/W7aTUoVRcjyRUv0jv0zW0EPnnDlluVkeZo9spBhiTTwoj
|
||||
b3zGODs6alTNIJwZIHNxxyOmfJPpVVp8BzGbMk7YBixSlZ/vbrrYV34TcSiy7J57
|
||||
lNNoVWM+OwiVk1+AEZfQDwaQfef5tsIkAZBUyITkkDKRhygtwM2110dejbEsgg==
|
||||
-----END CERTIFICATE-----`)
|
||||
rawKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAsn6r79JA64nIhAFSFuiWlmBQi/WtfY49cJgBplRsVd5DbK9N
|
||||
ZKWujdBAap63Mu1pJ4QrdXeNwqoSrU3F4CfyfaYWVcZYs2/LFAfOYZvjLz9Ddxwo
|
||||
N39CcIS7jKz4o0usY8sNHygiv6ejlGDCH6OXHanmYndlleDs7bObkXeg5stpMgXv
|
||||
GYNRePGCRrk1mZi9i+7mVIj2OE96vftYrnKt71KccplUYtQU1506cfgtBkvDTYHh
|
||||
Jnrx7+PNrxzOQzX5QEPprxmbwVXbqvwH4qwg57dgyGZtQ3GVy62PRdxD65UWXsZg
|
||||
IQgxkSyCpzJqBonATNdQMNXwyHyQB8p5ON7YzwIDAQABAoIBAQCG4doj3Apa8z+n
|
||||
IShbT1+cOyQi34A+xOIA151Hh7xmFxN0afRd/iWt3JUQ/OcLgQRZbDM7DSD+3W5H
|
||||
r+G7xfQkpwFxx/T3g58+f7ehYx+GcJQWyhxJ88zNIkBnyb4KCAE5WBOOW9IGajPe
|
||||
yE9pgUGMlPsXpYoKfHIOHg+NGY1pWUGBfBNR2kGrbkpZMmyy5bGa8dyrwAFBFRru
|
||||
kcmmKvate8UlbRspFtd4nR/GQLTBrcDJ1k1i1Su/4BpDuDeK6LPI8ZRePGqbdcxk
|
||||
TS30lsdYozuGfjZ5Zu8lSIJ//+7RjfDg8r684dpWjpalq8Quen60ZrIs01CSbfyU
|
||||
k8gOzTHhAoGBAOKhp41wXveegq+WylSXFyngm4bzF4dVdTRsSbJVk7NaOx1vCU6o
|
||||
/xIHoGEQyLI6wF+EaHmY89/Qu6tSV97XyBbiKeskopv5iXS/BsWTHJ1VbCA1ZLmK
|
||||
HgGllEkS0xfc9AdB7b6/K7LxAAQVKP3DtN6+6pSDZh9Sv2M1j0DbhkNbAoGBAMmg
|
||||
HcMfExaaeskjHqyLudtKX+znwaIoumleOGuavohR4R+Fpk8Yv8Xhb5U7Yr4gk0vY
|
||||
CFmhp1WAi6QMZ/8jePlKKXl3Ney827luoKiMczp2DoYE0t0u2Kw3LfkNKfjADZ7d
|
||||
JI6xPJV9/X1erwjq+4UdKqrpOf05SY4nkBMcvr6dAoGAXzisvbDJNiFTp5Mj0Abr
|
||||
pJzKvBjHegVeCXi2PkfWlzUCQYu1zWcURO8PY7k5mik1SuzHONAbJ578Oy+N3AOt
|
||||
/m9oTXRHHmHqbzMUFU+KZlDN7XqBp7NwiCCZ/Vn7d7tOjP4Wdl68baL07sI1RupD
|
||||
xJNS3LOY5PBPmc+XMRkLgKECgYEAgBNDlJSCrZMHeAjlDTncn53I/VXiPD2e3BvL
|
||||
vx6W9UT9ueZN1GSmPO6M0MDeYmOS7VSXSUhUYQ28pkJzNTC1QbWITu4YxP7anBnX
|
||||
1/kPoQ0pAJzDzVharlqGy3M/PBHTFRzogfO3xkY35ZFlokaR6uayGcr42Q+w16nt
|
||||
7RYPXEkCgYEA3GQYirHnGZuQ952jMvduqnpgkJiSnr0fa+94Rwa1pAhxHLFMo5s4
|
||||
fqZOtqKPj2s5X1JR0VCey1ilCcaAhWeb3tXCpbYLZSbMtjtqwA6LUeGY+Xdupsjw
|
||||
cfWIcOfHsIm2kP+RCxEnZf1XwiN9wyJeiUKlE0dqmx9j7F0Bm+7YDhI=
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
)
|
||||
|
||||
func tlsConfig() *tls.Config {
|
||||
cert, err := tls.X509KeyPair(rawCert, rawKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||
}
|
65
examples/ssu/ssu.go
Normal file
65
examples/ssu/ssu.go
Normal file
@ -0,0 +1,65 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ssuClient()
|
||||
}
|
||||
|
||||
func ssuClient() {
|
||||
addr, err := net.ResolveUDPAddr("udp", ":18338")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
laddr, _ := net.ResolveUDPAddr("udp", ":10800")
|
||||
conn, err := net.ListenUDP("udp", laddr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
cp, err := ss.NewCipher("chacha20", "123456")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
cc := ss.NewSecurePacketConn(conn, cp, false)
|
||||
|
||||
raddr, _ := net.ResolveUDPAddr("udp", ":8080")
|
||||
msg := []byte(`abcdefghijklmnopqrstuvwxyz`)
|
||||
dgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(0, 0, toSocksAddr(raddr)), msg)
|
||||
buf := bytes.Buffer{}
|
||||
dgram.Write(&buf)
|
||||
for {
|
||||
log.Printf("%# x", buf.Bytes()[3:])
|
||||
if _, err := cc.WriteTo(buf.Bytes()[3:], addr); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
b := make([]byte, 1024)
|
||||
n, adr, err := cc.ReadFrom(b)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Printf("%s: %# x", adr, b[:n])
|
||||
}
|
||||
}
|
||||
|
||||
func toSocksAddr(addr net.Addr) *gosocks5.Addr {
|
||||
host := "0.0.0.0"
|
||||
port := 0
|
||||
if addr != nil {
|
||||
h, p, _ := net.SplitHostPort(addr.String())
|
||||
host = h
|
||||
port, _ = strconv.Atoi(p)
|
||||
}
|
||||
return &gosocks5.Addr{
|
||||
Type: gosocks5.AddrIPv4,
|
||||
Host: host,
|
||||
Port: uint16(port),
|
||||
}
|
||||
}
|
1010
forward.go
1010
forward.go
File diff suppressed because it is too large
Load Diff
186
gost.go
186
gost.go
@ -1,146 +1,104 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"github.com/golang/glog"
|
||||
"net"
|
||||
"strings"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/go-log/log"
|
||||
)
|
||||
|
||||
const (
|
||||
Version = "2.4-dev"
|
||||
)
|
||||
// Version is the gost version.
|
||||
const Version = "2.4-rc1"
|
||||
|
||||
// Log level for glog
|
||||
const (
|
||||
LFATAL = iota
|
||||
LERROR
|
||||
LWARNING
|
||||
LINFO
|
||||
LDEBUG
|
||||
// Debug is a flag that enables the debug log.
|
||||
var Debug bool
|
||||
|
||||
var (
|
||||
tinyBufferSize = 128
|
||||
smallBufferSize = 1 * 1024 // 1KB small buffer
|
||||
mediumBufferSize = 8 * 1024 // 8KB medium buffer
|
||||
largeBufferSize = 32 * 1024 // 32KB large buffer
|
||||
)
|
||||
|
||||
var (
|
||||
// KeepAliveTime is the keep alive time period for TCP connection.
|
||||
KeepAliveTime = 180 * time.Second
|
||||
DialTimeout = 30 * time.Second
|
||||
ReadTimeout = 90 * time.Second
|
||||
WriteTimeout = 90 * time.Second
|
||||
|
||||
DefaultTTL = 60 // default udp node TTL in second for udp port forwarding
|
||||
// DialTimeout is the timeout of dial.
|
||||
DialTimeout = 30 * time.Second
|
||||
// ReadTimeout is the timeout for reading.
|
||||
ReadTimeout = 30 * time.Second
|
||||
// WriteTimeout is the timeout for writing.
|
||||
WriteTimeout = 60 * time.Second
|
||||
// PingTimeout is the timeout for pinging.
|
||||
PingTimeout = 30 * time.Second
|
||||
// PingRetries is the reties of ping.
|
||||
PingRetries = 3
|
||||
// default udp node TTL in second for udp port forwarding.
|
||||
defaultTTL = 60 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
SmallBufferSize = 1 * 1024 // 1KB small buffer
|
||||
MediumBufferSize = 8 * 1024 // 8KB medium buffer
|
||||
LargeBufferSize = 32 * 1024 // 32KB large buffer
|
||||
DefaultTLSConfig *tls.Config
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultCertFile = "cert.pem"
|
||||
DefaultKeyFile = "key.pem"
|
||||
|
||||
// This is the default cert and key data for convenience, providing your own cert is recommended.
|
||||
defaultRawCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIC5jCCAdCgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD
|
||||
bzAeFw0xNDAzMTcwNjIwNTFaFw0xNTAzMTcwNjIwNTFaMBIxEDAOBgNVBAoTB0Fj
|
||||
bWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDccNO1xmd4lWSf
|
||||
d/0/QS3E93cYIWHw831i/IKxigdRD/XMZonLdEHywW6lOiXazaP8e6CqPGSmnl0x
|
||||
5k/3dvGCMj2JCVxM6+z7NpL+AiwvXmvkj/TOciCgwqssCwYS2CiVwjfazRjx1ZUJ
|
||||
VDC5qiyRsfktQ2fVHrpnJGVSRagmiQgwGWBilVG9B8QvRtpQKN/GQGq17oIQm8aK
|
||||
kOdPt93g93ojMIg7YJpgDgOirvVz/hDn7YD4ryrtPos9CMafFkJprymKpRHyvz7P
|
||||
8a3+OkuPjFjPnwOHQ5u1U3+8vC44vfb1ExWzDLoT8Xp8Gndx39k0f7MVOol3GnYu
|
||||
MN/dvNUdAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIAoDATBgNVHSUEDDAKBggrBgEF
|
||||
BQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDALBgkqhkiG
|
||||
9w0BAQUDggEBAIG8CJqvTIgJnNOK+i5/IUc/3yF/mSCWuG8qP+Fmo2t6T0PVOtc0
|
||||
8wiWH5iWtCAhjn0MRY9l/hIjWm6gUZGHCGuEgsOPpJDYGoNLjH9Xwokm4y3LFNRK
|
||||
UBrrrDbKRNibApBHCapPf6gC5sXcjOwx7P2/kiHDgY7YH47jfcRhtAPNsM4gjsEO
|
||||
RmwENY+hRUFHIRfQTyalqND+x6PWhRo3K6hpHs4DQEYPq4P2kFPqUqSBymH+Ny5/
|
||||
BcQ3wdMNmC6Bm/oiL1QV0M+/InOsAgQk/EDd0kmoU1ZT2lYHQduGmP099bOlHNpS
|
||||
uqO3vXF3q8SPPr/A9TqSs7BKkBQbe0+cdsA=
|
||||
-----END CERTIFICATE-----`)
|
||||
defaultRawKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA3HDTtcZneJVkn3f9P0EtxPd3GCFh8PN9YvyCsYoHUQ/1zGaJ
|
||||
y3RB8sFupTol2s2j/Hugqjxkpp5dMeZP93bxgjI9iQlcTOvs+zaS/gIsL15r5I/0
|
||||
znIgoMKrLAsGEtgolcI32s0Y8dWVCVQwuaoskbH5LUNn1R66ZyRlUkWoJokIMBlg
|
||||
YpVRvQfEL0baUCjfxkBqte6CEJvGipDnT7fd4Pd6IzCIO2CaYA4Doq71c/4Q5+2A
|
||||
+K8q7T6LPQjGnxZCaa8piqUR8r8+z/Gt/jpLj4xYz58Dh0ObtVN/vLwuOL329RMV
|
||||
swy6E/F6fBp3cd/ZNH+zFTqJdxp2LjDf3bzVHQIDAQABAoIBAHal26147nQ+pHwY
|
||||
jxwers3XDCjWvup7g79lfcqlKi79UiUEA6KYHm7UogMYewt7p4nb2KwH+XycvDiB
|
||||
aAUf5flXpTs+6IkWauUDiLZi4PlV7uiEexUq5FjirlL0U/6MjbudX4bK4WQ4uxDc
|
||||
WaV07Kw2iJFOOHLDKT0en9JaX5jtJNc4ZnE9efFoQ5jfypPWtRw65G1rULEg6nvc
|
||||
GDh+1ce+4foCkpLRC9c24xAwJONZG6x3UqrSS9qfAsb73nWRQrTfUcO3nhoN8VvL
|
||||
kL9skn1+S06NyUN0KoEtyRBp+RcpXSsBWAo6qZmo/WqhB/gjzWrxVwn20+yJSm35
|
||||
ZsMc6QECgYEA8GS+Mp9xfB2szWHz6YTOO1Uu4lHM1ccZMwS1G+dL0KO3uGAiPdvp
|
||||
woVot6v6w88t7onXsLo5pgz7SYug0CpkF3K/MRd1Ar4lH7PK7IBQ6rFr9ppVxDbx
|
||||
AEWRswUoPbKCr7W6HU8LbQHDavsDlEIwc6+DiwnL4BzlKjb7RpgQEz0CgYEA6sB5
|
||||
uHvx3Y5FDcGk1n73leQSAcq14l3ZLNpjrs8msoREDil/j5WmuSN58/7PGMiMgHEi
|
||||
1vLm3H796JmvGr9OBvspOjHyk07ui2/We/j9Hoxm1VWhyi8HkLNDj70HKalTTFMz
|
||||
RHO4O+0xCva+h9mKZrRMVktXr2jjdFn/0MYIZ2ECgYAIIsC1IeRLWQ3CHbCNlKsO
|
||||
IwHlMvOFwKk/qsceXKOaOhA7szU1dr3gkXdL0Aw6mEZrrkqYdpUA46uVf54/rU+Z
|
||||
445I8QxKvXiwK/uQKX+TkdGflPWWIG3jnnch4ejMvb/ihnn4B/bRB6A/fKNQXzUY
|
||||
lTYUfI5j1VaEKTwz1W2l2QKBgByFCcSp+jZqhGUpc3dDsZyaOr3Q/Mvlju7uEVI5
|
||||
hIAHpaT60a6GBd1UPAqymEJwivFHzW3D0NxU6VAK68UaHMaoWNfjHY9b9YsnKS2i
|
||||
kE3XzN56Ks+/avHfdYPO+UHMenw5V28nh+hv5pdoZrlmanQTz3pkaOC8o3WNQZEB
|
||||
nh/BAoGBAMY5z2f1pmMhrvtPDSlEVjgjELbaInxFaxPLR4Pdyzn83gtIIU14+R8X
|
||||
2LPs6PPwrNjWnIgrUSVXncIFL3pa45B+Mx1pYCpOAB1+nCZjIBQmpeo4Y0dwA/XH
|
||||
85EthKPvoszm+OPbyI16OcePV5ocX7lupRYuAo0pek7bomhmHWHz
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
)
|
||||
|
||||
var (
|
||||
ErrEmptyChain = errors.New("empty chain")
|
||||
)
|
||||
|
||||
func setKeepAlive(conn net.Conn, d time.Duration) error {
|
||||
c, ok := conn.(*net.TCPConn)
|
||||
if !ok {
|
||||
return errors.New("Not a TCP connection")
|
||||
func init() {
|
||||
rawCert, rawKey, err := generateKeyPair()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := c.SetKeepAlive(true); err != nil {
|
||||
return err
|
||||
cert, err := tls.X509KeyPair(rawCert, rawKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := c.SetKeepAlivePeriod(d); err != nil {
|
||||
return err
|
||||
DefaultTLSConfig = &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
}
|
||||
return nil
|
||||
|
||||
log.DefaultLogger = &LogLogger{}
|
||||
}
|
||||
|
||||
// Load the certificate from cert and key files, will use the default certificate if the provided info are invalid.
|
||||
func LoadCertificate(certFile, keyFile string) (tls.Certificate, error) {
|
||||
tlsCert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err == nil {
|
||||
return tlsCert, nil
|
||||
}
|
||||
glog.V(LWARNING).Infoln(err)
|
||||
return tls.X509KeyPair(defaultRawCert, defaultRawKey)
|
||||
func SetLogger(logger log.Logger) {
|
||||
log.DefaultLogger = logger
|
||||
}
|
||||
|
||||
// Replace the default certificate by your own
|
||||
func SetDefaultCertificate(rawCert, rawKey []byte) {
|
||||
defaultRawCert = rawCert
|
||||
defaultRawKey = rawKey
|
||||
}
|
||||
func generateKeyPair() (rawCert, rawKey []byte, err error) {
|
||||
// Create private key and self-signed certificate
|
||||
// Adapted from https://golang.org/src/crypto/tls/generate_cert.go
|
||||
|
||||
func basicProxyAuth(proxyAuth string) (username, password string, ok bool) {
|
||||
if proxyAuth == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(proxyAuth, "Basic ") {
|
||||
return
|
||||
}
|
||||
c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(proxyAuth, "Basic "))
|
||||
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cs := string(c)
|
||||
s := strings.IndexByte(cs, ':')
|
||||
if s < 0 {
|
||||
validFor := time.Hour * 24 * 365 * 10 // ten years
|
||||
notBefore := time.Now()
|
||||
notAfter := notBefore.Add(validFor)
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"gost"},
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return cs[:s], cs[s+1:], true
|
||||
rawCert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||
rawKey = pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
|
||||
|
||||
return
|
||||
}
|
||||
|
114
handler.go
Normal file
114
handler.go
Normal file
@ -0,0 +1,114 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/url"
|
||||
|
||||
"github.com/ginuerzh/gosocks4"
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
"github.com/go-log/log"
|
||||
)
|
||||
|
||||
// Handler is a proxy server handler
|
||||
type Handler interface {
|
||||
Handle(net.Conn)
|
||||
}
|
||||
|
||||
// HandlerOptions describes the options for Handler.
|
||||
type HandlerOptions struct {
|
||||
Addr string
|
||||
Chain *Chain
|
||||
Users []*url.Userinfo
|
||||
TLSConfig *tls.Config
|
||||
Whitelist *Permissions
|
||||
Blacklist *Permissions
|
||||
}
|
||||
|
||||
// HandlerOption allows a common way to set handler options.
|
||||
type HandlerOption func(opts *HandlerOptions)
|
||||
|
||||
// AddrHandlerOption sets the Addr option of HandlerOptions.
|
||||
func AddrHandlerOption(addr string) HandlerOption {
|
||||
return func(opts *HandlerOptions) {
|
||||
opts.Addr = addr
|
||||
}
|
||||
}
|
||||
|
||||
// ChainHandlerOption sets the Chain option of HandlerOptions.
|
||||
func ChainHandlerOption(chain *Chain) HandlerOption {
|
||||
return func(opts *HandlerOptions) {
|
||||
opts.Chain = chain
|
||||
}
|
||||
}
|
||||
|
||||
// UsersHandlerOption sets the Users option of HandlerOptions.
|
||||
func UsersHandlerOption(users ...*url.Userinfo) HandlerOption {
|
||||
return func(opts *HandlerOptions) {
|
||||
opts.Users = users
|
||||
}
|
||||
}
|
||||
|
||||
// TLSConfigHandlerOption sets the TLSConfig option of HandlerOptions.
|
||||
func TLSConfigHandlerOption(config *tls.Config) HandlerOption {
|
||||
return func(opts *HandlerOptions) {
|
||||
opts.TLSConfig = config
|
||||
}
|
||||
}
|
||||
|
||||
// WhitelistHandlerOption sets the Whitelist option of HandlerOptions.
|
||||
func WhitelistHandlerOption(whitelist *Permissions) HandlerOption {
|
||||
return func(opts *HandlerOptions) {
|
||||
opts.Whitelist = whitelist
|
||||
}
|
||||
}
|
||||
|
||||
// BlacklistHandlerOption sets the Blacklist option of HandlerOptions.
|
||||
func BlacklistHandlerOption(blacklist *Permissions) HandlerOption {
|
||||
return func(opts *HandlerOptions) {
|
||||
opts.Blacklist = blacklist
|
||||
}
|
||||
}
|
||||
|
||||
type autoHandler struct {
|
||||
options []HandlerOption
|
||||
}
|
||||
|
||||
// AutoHandler creates a server Handler for auto proxy server.
|
||||
func AutoHandler(opts ...HandlerOption) Handler {
|
||||
h := &autoHandler{
|
||||
options: opts,
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *autoHandler) Handle(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
br := bufio.NewReader(conn)
|
||||
b, err := br.Peek(1)
|
||||
if err != nil {
|
||||
log.Log(err)
|
||||
return
|
||||
}
|
||||
|
||||
cc := &bufferdConn{Conn: conn, br: br}
|
||||
switch b[0] {
|
||||
case gosocks4.Ver4:
|
||||
SOCKS4Handler(h.options...).Handle(cc)
|
||||
case gosocks5.Ver5:
|
||||
SOCKS5Handler(h.options...).Handle(cc)
|
||||
default: // http
|
||||
HTTPHandler(h.options...).Handle(cc)
|
||||
}
|
||||
}
|
||||
|
||||
type bufferdConn struct {
|
||||
net.Conn
|
||||
br *bufio.Reader
|
||||
}
|
||||
|
||||
func (c *bufferdConn) Read(b []byte) (int, error) {
|
||||
return c.br.Read(b)
|
||||
}
|
514
http.go
514
http.go
@ -2,133 +2,208 @@ package gost
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/http2"
|
||||
"io"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
//"strings"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-log/log"
|
||||
)
|
||||
|
||||
type HttpServer struct {
|
||||
conn net.Conn
|
||||
Base *ProxyServer
|
||||
type httpConnector struct {
|
||||
User *url.Userinfo
|
||||
}
|
||||
|
||||
func NewHttpServer(conn net.Conn, base *ProxyServer) *HttpServer {
|
||||
return &HttpServer{
|
||||
conn: conn,
|
||||
Base: base,
|
||||
// HTTPConnector creates a Connector for HTTP proxy client.
|
||||
// It accepts an optional auth info for HTTP Basic Authentication.
|
||||
func HTTPConnector(user *url.Userinfo) Connector {
|
||||
return &httpConnector{User: user}
|
||||
}
|
||||
|
||||
func (c *httpConnector) Connect(conn net.Conn, addr string) (net.Conn, error) {
|
||||
req := &http.Request{
|
||||
Method: http.MethodConnect,
|
||||
URL: &url.URL{Host: addr},
|
||||
Host: addr,
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Header: make(http.Header),
|
||||
}
|
||||
}
|
||||
req.Header.Set("Proxy-Connection", "keep-alive")
|
||||
|
||||
// Default HTTP server handler
|
||||
func (s *HttpServer) HandleRequest(req *http.Request) {
|
||||
glog.V(LINFO).Infof("[http] %s %s - %s %s", req.Method, s.conn.RemoteAddr(), req.Host, req.Proto)
|
||||
if c.User != nil {
|
||||
s := c.User.String()
|
||||
if _, set := c.User.Password(); !set {
|
||||
s += ":"
|
||||
}
|
||||
req.Header.Set("Proxy-Authorization",
|
||||
"Basic "+base64.StdEncoding.EncodeToString([]byte(s)))
|
||||
}
|
||||
|
||||
if glog.V(LDEBUG) {
|
||||
if err := req.Write(conn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if Debug {
|
||||
dump, _ := httputil.DumpRequest(req, false)
|
||||
glog.Infoln(string(dump))
|
||||
log.Log(string(dump))
|
||||
}
|
||||
|
||||
if req.Method == "PRI" && req.ProtoMajor == 2 {
|
||||
glog.V(LWARNING).Infof("[http] %s <- %s : Not an HTTP2 server", s.conn.RemoteAddr(), req.Host)
|
||||
resp := "HTTP/1.1 400 Bad Request\r\n" +
|
||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n"
|
||||
s.conn.Write([]byte(resp))
|
||||
resp, err := http.ReadResponse(bufio.NewReader(conn), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if Debug {
|
||||
dump, _ := httputil.DumpResponse(resp, false)
|
||||
log.Log(string(dump))
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("%s", resp.Status)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
type httpHandler struct {
|
||||
options *HandlerOptions
|
||||
}
|
||||
|
||||
// HTTPHandler creates a server Handler for HTTP proxy server.
|
||||
func HTTPHandler(opts ...HandlerOption) Handler {
|
||||
h := &httpHandler{
|
||||
options: &HandlerOptions{},
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(h.options)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *httpHandler) Handle(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
req, err := http.ReadRequest(bufio.NewReader(conn))
|
||||
if err != nil {
|
||||
log.Logf("[http] %s - %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
|
||||
return
|
||||
}
|
||||
|
||||
valid := false
|
||||
u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
|
||||
glog.V(LINFO).Infoln(u, p)
|
||||
for _, user := range s.Base.Node.Users {
|
||||
username := user.Username()
|
||||
password, _ := user.Password()
|
||||
if (u == username && p == password) ||
|
||||
(u == username && password == "") ||
|
||||
(username == "" && p == password) {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
if Debug {
|
||||
log.Logf("[http] %s %s - %s %s", req.Method, conn.RemoteAddr(), req.Host, req.Proto)
|
||||
dump, _ := httputil.DumpRequest(req, false)
|
||||
log.Logf(string(dump))
|
||||
}
|
||||
|
||||
if len(s.Base.Node.Users) > 0 && !valid {
|
||||
glog.V(LWARNING).Infof("[http] %s <- %s : proxy authentication required", s.conn.RemoteAddr(), req.Host)
|
||||
if req.Method == "PRI" && req.ProtoMajor == 2 {
|
||||
log.Logf("[http] %s <- %s : Not an HTTP2 server", conn.RemoteAddr(), req.Host)
|
||||
resp := "HTTP/1.1 400 Bad Request\r\n" +
|
||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n"
|
||||
conn.Write([]byte(resp))
|
||||
return
|
||||
}
|
||||
|
||||
u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
|
||||
if Debug && (u != "" || p != "") {
|
||||
log.Logf("[http] %s - %s : Authorization: '%s' '%s'", conn.RemoteAddr(), req.Host, u, p)
|
||||
}
|
||||
if !authenticate(u, p, h.options.Users...) {
|
||||
log.Logf("[http] %s <- %s : proxy authentication required", conn.RemoteAddr(), req.Host)
|
||||
resp := "HTTP/1.1 407 Proxy Authentication Required\r\n" +
|
||||
"Proxy-Authenticate: Basic realm=\"gost\"\r\n" +
|
||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n"
|
||||
s.conn.Write([]byte(resp))
|
||||
conn.Write([]byte(resp))
|
||||
return
|
||||
}
|
||||
|
||||
req.Header.Del("Proxy-Authorization")
|
||||
req.Header.Del("Proxy-Connection")
|
||||
|
||||
if !Can("tcp", req.Host, h.options.Whitelist, h.options.Blacklist) {
|
||||
log.Logf("[http] Unauthorized to tcp connect to %s", req.Host)
|
||||
b := []byte("HTTP/1.1 403 Forbidden\r\n" +
|
||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
||||
conn.Write(b)
|
||||
if Debug {
|
||||
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// forward http request
|
||||
lastNode := s.Base.Chain.lastNode
|
||||
if lastNode != nil && (lastNode.Protocol == "http" || lastNode.Protocol == "") {
|
||||
s.forwardRequest(req)
|
||||
lastNode := h.options.Chain.LastNode()
|
||||
if req.Method != http.MethodConnect && lastNode.Protocol == "http" {
|
||||
h.forwardRequest(conn, req)
|
||||
return
|
||||
}
|
||||
|
||||
c, err := s.Base.Chain.Dial(req.Host)
|
||||
host := req.Host
|
||||
if !strings.Contains(req.Host, ":") {
|
||||
host += ":80"
|
||||
}
|
||||
cc, err := h.options.Chain.Dial(host)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[http] %s -> %s : %s", s.conn.RemoteAddr(), req.Host, err)
|
||||
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)
|
||||
|
||||
b := []byte("HTTP/1.1 503 Service unavailable\r\n" +
|
||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
||||
glog.V(LDEBUG).Infof("[http] %s <- %s\n%s", s.conn.RemoteAddr(), req.Host, string(b))
|
||||
s.conn.Write(b)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
if req.Method == http.MethodConnect {
|
||||
b := []byte("HTTP/1.1 200 Connection established\r\n" +
|
||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
||||
glog.V(LDEBUG).Infof("[http] %s <- %s\n%s", s.conn.RemoteAddr(), req.Host, string(b))
|
||||
s.conn.Write(b)
|
||||
} else {
|
||||
req.Header.Del("Proxy-Connection")
|
||||
req.Header.Set("Connection", "Keep-Alive")
|
||||
|
||||
if err = req.Write(c); err != nil {
|
||||
glog.V(LWARNING).Infof("[http] %s -> %s : %s", s.conn.RemoteAddr(), req.Host, err)
|
||||
return
|
||||
if Debug {
|
||||
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b))
|
||||
}
|
||||
}
|
||||
|
||||
glog.V(LINFO).Infof("[http] %s <-> %s", s.conn.RemoteAddr(), req.Host)
|
||||
s.Base.transport(s.conn, c)
|
||||
glog.V(LINFO).Infof("[http] %s >-< %s", s.conn.RemoteAddr(), req.Host)
|
||||
}
|
||||
|
||||
func (s *HttpServer) forwardRequest(req *http.Request) {
|
||||
last := s.Base.Chain.lastNode
|
||||
if last == nil {
|
||||
return
|
||||
}
|
||||
cc, err := s.Base.Chain.GetConn()
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[http] %s -> %s : %s", s.conn.RemoteAddr(), last.Addr, err)
|
||||
|
||||
b := []byte("HTTP/1.1 503 Service unavailable\r\n" +
|
||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
||||
glog.V(LDEBUG).Infof("[http] %s <- %s\n%s", s.conn.RemoteAddr(), last.Addr, string(b))
|
||||
s.conn.Write(b)
|
||||
conn.Write(b)
|
||||
return
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
if len(last.Users) > 0 {
|
||||
user := last.Users[0]
|
||||
s := user.String()
|
||||
if _, set := user.Password(); !set {
|
||||
if req.Method == http.MethodConnect {
|
||||
b := []byte("HTTP/1.1 200 Connection established\r\n" +
|
||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
||||
if Debug {
|
||||
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b))
|
||||
}
|
||||
conn.Write(b)
|
||||
} else {
|
||||
req.Header.Del("Proxy-Connection")
|
||||
|
||||
if err = req.Write(cc); err != nil {
|
||||
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
log.Logf("[http] %s <-> %s", cc.LocalAddr(), req.Host)
|
||||
transport(conn, cc)
|
||||
log.Logf("[http] %s >-< %s", cc.LocalAddr(), req.Host)
|
||||
}
|
||||
|
||||
func (h *httpHandler) forwardRequest(conn net.Conn, req *http.Request) {
|
||||
if h.options.Chain.IsEmpty() {
|
||||
return
|
||||
}
|
||||
lastNode := h.options.Chain.LastNode()
|
||||
|
||||
cc, err := h.options.Chain.Conn()
|
||||
if err != nil {
|
||||
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), lastNode.Addr, err)
|
||||
|
||||
b := []byte("HTTP/1.1 503 Service unavailable\r\n" +
|
||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
||||
if Debug {
|
||||
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), lastNode.Addr, string(b))
|
||||
}
|
||||
conn.Write(b)
|
||||
return
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
if lastNode.User != nil {
|
||||
s := lastNode.User.String()
|
||||
if _, set := lastNode.User.Password(); !set {
|
||||
s += ":"
|
||||
}
|
||||
req.Header.Set("Proxy-Authorization",
|
||||
@ -137,250 +212,51 @@ func (s *HttpServer) forwardRequest(req *http.Request) {
|
||||
|
||||
cc.SetWriteDeadline(time.Now().Add(WriteTimeout))
|
||||
if err = req.WriteProxy(cc); err != nil {
|
||||
glog.V(LWARNING).Infof("[http] %s -> %s : %s", s.conn.RemoteAddr(), req.Host, err)
|
||||
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)
|
||||
return
|
||||
}
|
||||
cc.SetWriteDeadline(time.Time{})
|
||||
|
||||
glog.V(LINFO).Infof("[http] %s <-> %s", s.conn.RemoteAddr(), req.Host)
|
||||
s.Base.transport(s.conn, cc)
|
||||
glog.V(LINFO).Infof("[http] %s >-< %s", s.conn.RemoteAddr(), req.Host)
|
||||
log.Logf("[http] %s <-> %s", conn.RemoteAddr(), req.Host)
|
||||
transport(conn, cc)
|
||||
log.Logf("[http] %s >-< %s", conn.RemoteAddr(), req.Host)
|
||||
return
|
||||
}
|
||||
|
||||
type Http2Server struct {
|
||||
Base *ProxyServer
|
||||
Handler http.Handler
|
||||
TLSConfig *tls.Config
|
||||
}
|
||||
|
||||
func NewHttp2Server(base *ProxyServer) *Http2Server {
|
||||
return &Http2Server{Base: base}
|
||||
}
|
||||
|
||||
func (s *Http2Server) ListenAndServeTLS(config *tls.Config) error {
|
||||
srv := http.Server{
|
||||
Addr: s.Base.Node.Addr,
|
||||
Handler: s.Handler,
|
||||
TLSConfig: config,
|
||||
}
|
||||
if srv.Handler == nil {
|
||||
srv.Handler = http.HandlerFunc(s.HandleRequest)
|
||||
}
|
||||
http2.ConfigureServer(&srv, nil)
|
||||
return srv.ListenAndServeTLS("", "")
|
||||
}
|
||||
|
||||
// Default HTTP2 server handler
|
||||
func (s *Http2Server) HandleRequest(w http.ResponseWriter, req *http.Request) {
|
||||
target := req.Header.Get("Gost-Target")
|
||||
if target == "" {
|
||||
target = req.Host
|
||||
}
|
||||
glog.V(LINFO).Infof("[http2] %s %s - %s %s", req.Method, req.RemoteAddr, target, req.Proto)
|
||||
if glog.V(LDEBUG) {
|
||||
dump, _ := httputil.DumpRequest(req, false)
|
||||
glog.Infoln(string(dump))
|
||||
}
|
||||
|
||||
w.Header().Set("Proxy-Agent", "gost/"+Version)
|
||||
|
||||
// HTTP2 as transport
|
||||
if req.Header.Get("Proxy-Switch") == "gost" {
|
||||
conn, err := s.Upgrade(w, req)
|
||||
if err != nil {
|
||||
glog.V(LINFO).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err)
|
||||
return
|
||||
}
|
||||
glog.V(LINFO).Infof("[http2] %s - %s : switch to HTTP2 transport mode OK", req.RemoteAddr, target)
|
||||
s.Base.handleConn(conn)
|
||||
func basicProxyAuth(proxyAuth string) (username, password string, ok bool) {
|
||||
if proxyAuth == "" {
|
||||
return
|
||||
}
|
||||
|
||||
valid := false
|
||||
u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
|
||||
for _, user := range s.Base.Node.Users {
|
||||
username := user.Username()
|
||||
password, _ := user.Password()
|
||||
if !strings.HasPrefix(proxyAuth, "Basic ") {
|
||||
return
|
||||
}
|
||||
c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(proxyAuth, "Basic "))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cs := string(c)
|
||||
s := strings.IndexByte(cs, ':')
|
||||
if s < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
return cs[:s], cs[s+1:], true
|
||||
}
|
||||
|
||||
func authenticate(username, password string, users ...*url.Userinfo) bool {
|
||||
if len(users) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
u := user.Username()
|
||||
p, _ := user.Password()
|
||||
if (u == username && p == password) ||
|
||||
(u == username && password == "") ||
|
||||
(username == "" && p == password) {
|
||||
valid = true
|
||||
break
|
||||
(u == username && p == "") ||
|
||||
(u == "" && p == password) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if len(s.Base.Node.Users) > 0 && !valid {
|
||||
glog.V(LWARNING).Infof("[http2] %s <- %s : proxy authentication required", req.RemoteAddr, target)
|
||||
w.Header().Set("Proxy-Authenticate", "Basic realm=\"gost\"")
|
||||
w.WriteHeader(http.StatusProxyAuthRequired)
|
||||
return
|
||||
}
|
||||
|
||||
req.Header.Del("Proxy-Authorization")
|
||||
|
||||
c, err := s.Base.Chain.Dial(target)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err)
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
glog.V(LINFO).Infof("[http2] %s <-> %s", req.RemoteAddr, target)
|
||||
|
||||
if req.Method == http.MethodConnect {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if fw, ok := w.(http.Flusher); ok {
|
||||
fw.Flush()
|
||||
}
|
||||
|
||||
// compatible with HTTP1.x
|
||||
if hj, ok := w.(http.Hijacker); ok && req.ProtoMajor == 1 {
|
||||
// we take over the underly connection
|
||||
conn, _, err := hj.Hijack()
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
s.Base.transport(conn, c)
|
||||
return
|
||||
}
|
||||
|
||||
errc := make(chan error, 2)
|
||||
|
||||
go func() {
|
||||
_, err := io.Copy(c, req.Body)
|
||||
errc <- err
|
||||
}()
|
||||
go func() {
|
||||
_, err := io.Copy(flushWriter{w}, c)
|
||||
errc <- err
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-errc:
|
||||
// glog.V(LWARNING).Infoln("exit", err)
|
||||
}
|
||||
glog.V(LINFO).Infof("[http2] %s >-< %s", req.RemoteAddr, target)
|
||||
return
|
||||
}
|
||||
|
||||
req.Header.Set("Connection", "Keep-Alive")
|
||||
if err = req.Write(c); err != nil {
|
||||
glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(c), req)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln(err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
for k, v := range resp.Header {
|
||||
for _, vv := range v {
|
||||
w.Header().Add(k, vv)
|
||||
}
|
||||
}
|
||||
w.WriteHeader(resp.StatusCode)
|
||||
if _, err := io.Copy(flushWriter{w}, resp.Body); err != nil {
|
||||
glog.V(LWARNING).Infof("[http2] %s <- %s : %s", req.RemoteAddr, target, err)
|
||||
}
|
||||
|
||||
glog.V(LINFO).Infof("[http2] %s >-< %s", req.RemoteAddr, target)
|
||||
}
|
||||
|
||||
// Upgrade upgrade an HTTP2 request to a bidirectional connection that preparing for tunneling other protocol, just like a websocket connection.
|
||||
func (s *Http2Server) Upgrade(w http.ResponseWriter, r *http.Request) (net.Conn, error) {
|
||||
if r.Method != http.MethodConnect {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return nil, errors.New("Method not allowed")
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
if fw, ok := w.(http.Flusher); ok {
|
||||
fw.Flush()
|
||||
}
|
||||
|
||||
conn := &http2Conn{r: r.Body, w: flushWriter{w}}
|
||||
conn.remoteAddr, _ = net.ResolveTCPAddr("tcp", r.RemoteAddr)
|
||||
conn.localAddr, _ = net.ResolveTCPAddr("tcp", r.Host)
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// HTTP2 client connection, wrapped up just like a net.Conn
|
||||
type http2Conn struct {
|
||||
r io.Reader
|
||||
w io.Writer
|
||||
remoteAddr net.Addr
|
||||
localAddr net.Addr
|
||||
}
|
||||
|
||||
func (c *http2Conn) Read(b []byte) (n int, err error) {
|
||||
return c.r.Read(b)
|
||||
}
|
||||
|
||||
func (c *http2Conn) Write(b []byte) (n int, err error) {
|
||||
return c.w.Write(b)
|
||||
}
|
||||
|
||||
func (c *http2Conn) Close() (err error) {
|
||||
if rc, ok := c.r.(io.Closer); ok {
|
||||
err = rc.Close()
|
||||
}
|
||||
if w, ok := c.w.(io.Closer); ok {
|
||||
err = w.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *http2Conn) LocalAddr() net.Addr {
|
||||
return c.localAddr
|
||||
}
|
||||
|
||||
func (c *http2Conn) RemoteAddr() net.Addr {
|
||||
return c.remoteAddr
|
||||
}
|
||||
|
||||
func (c *http2Conn) SetDeadline(t time.Time) error {
|
||||
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||
}
|
||||
|
||||
func (c *http2Conn) SetReadDeadline(t time.Time) error {
|
||||
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||
}
|
||||
|
||||
func (c *http2Conn) SetWriteDeadline(t time.Time) error {
|
||||
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||
}
|
||||
|
||||
type flushWriter struct {
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func (fw flushWriter) Write(p []byte) (n int, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if s, ok := r.(string); ok {
|
||||
err = errors.New(s)
|
||||
return
|
||||
}
|
||||
err = r.(error)
|
||||
}
|
||||
}()
|
||||
|
||||
n, err = fw.w.Write(p)
|
||||
if err != nil {
|
||||
// glog.V(LWARNING).Infoln("flush writer:", err)
|
||||
return
|
||||
}
|
||||
if f, ok := fw.w.(http.Flusher); ok {
|
||||
f.Flush()
|
||||
}
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
772
http2.go
Normal file
772
http2.go
Normal file
@ -0,0 +1,772 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-log/log"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
type http2Connector struct {
|
||||
User *url.Userinfo
|
||||
}
|
||||
|
||||
// HTTP2Connector creates a Connector for HTTP2 proxy client.
|
||||
// It accepts an optional auth info for HTTP Basic Authentication.
|
||||
func HTTP2Connector(user *url.Userinfo) Connector {
|
||||
return &http2Connector{User: user}
|
||||
}
|
||||
|
||||
func (c *http2Connector) Connect(conn net.Conn, addr string) (net.Conn, error) {
|
||||
cc, ok := conn.(*http2ClientConn)
|
||||
if !ok {
|
||||
return nil, errors.New("wrong connection type")
|
||||
}
|
||||
|
||||
pr, pw := io.Pipe()
|
||||
req := &http.Request{
|
||||
Method: http.MethodConnect,
|
||||
URL: &url.URL{Scheme: "https", Host: addr},
|
||||
Header: make(http.Header),
|
||||
Proto: "HTTP/2.0",
|
||||
ProtoMajor: 2,
|
||||
ProtoMinor: 0,
|
||||
Body: pr,
|
||||
Host: addr,
|
||||
ContentLength: -1,
|
||||
}
|
||||
if c.User != nil {
|
||||
req.Header.Set("Proxy-Authorization",
|
||||
"Basic "+base64.StdEncoding.EncodeToString([]byte(c.User.String())))
|
||||
}
|
||||
if Debug {
|
||||
dump, _ := httputil.DumpRequest(req, false)
|
||||
log.Log("[http2]", string(dump))
|
||||
}
|
||||
resp, err := cc.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if Debug {
|
||||
dump, _ := httputil.DumpResponse(resp, false)
|
||||
log.Log("[http2]", string(dump))
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
resp.Body.Close()
|
||||
return nil, errors.New(resp.Status)
|
||||
}
|
||||
hc := &http2Conn{
|
||||
r: resp.Body,
|
||||
w: pw,
|
||||
closed: make(chan struct{}),
|
||||
}
|
||||
hc.remoteAddr, _ = net.ResolveTCPAddr("tcp", addr)
|
||||
hc.localAddr, _ = net.ResolveTCPAddr("tcp", cc.addr)
|
||||
|
||||
return hc, nil
|
||||
}
|
||||
|
||||
type http2Transporter struct {
|
||||
clients map[string]*http.Client
|
||||
clientMutex sync.Mutex
|
||||
tlsConfig *tls.Config
|
||||
}
|
||||
|
||||
// HTTP2Transporter creates a Transporter that is used by HTTP2 h2 proxy client.
|
||||
func HTTP2Transporter(config *tls.Config) Transporter {
|
||||
if config == nil {
|
||||
config = &tls.Config{InsecureSkipVerify: true}
|
||||
}
|
||||
return &http2Transporter{
|
||||
clients: make(map[string]*http.Client),
|
||||
tlsConfig: config,
|
||||
}
|
||||
}
|
||||
|
||||
func (tr *http2Transporter) Dial(addr string, options ...DialOption) (net.Conn, error) {
|
||||
opts := &DialOptions{}
|
||||
for _, option := range options {
|
||||
option(opts)
|
||||
}
|
||||
|
||||
tr.clientMutex.Lock()
|
||||
client, ok := tr.clients[addr]
|
||||
if !ok {
|
||||
transport := http2.Transport{
|
||||
TLSClientConfig: tr.tlsConfig,
|
||||
DialTLS: func(network, adr string, cfg *tls.Config) (net.Conn, error) {
|
||||
conn, err := opts.Chain.Dial(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return wrapTLSClient(conn, cfg)
|
||||
},
|
||||
}
|
||||
client = &http.Client{
|
||||
Transport: &transport,
|
||||
Timeout: opts.Timeout,
|
||||
}
|
||||
tr.clients[addr] = client
|
||||
}
|
||||
tr.clientMutex.Unlock()
|
||||
|
||||
return &http2ClientConn{
|
||||
addr: addr,
|
||||
client: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (tr *http2Transporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (tr *http2Transporter) Multiplex() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type h2Transporter struct {
|
||||
clients map[string]*http.Client
|
||||
clientMutex sync.Mutex
|
||||
tlsConfig *tls.Config
|
||||
}
|
||||
|
||||
// H2Transporter creates a Transporter that is used by HTTP2 h2 tunnel client.
|
||||
func H2Transporter(config *tls.Config) Transporter {
|
||||
if config == nil {
|
||||
config = &tls.Config{InsecureSkipVerify: true}
|
||||
}
|
||||
return &h2Transporter{
|
||||
clients: make(map[string]*http.Client),
|
||||
tlsConfig: config,
|
||||
}
|
||||
}
|
||||
|
||||
// H2CTransporter creates a Transporter that is used by HTTP2 h2c tunnel client.
|
||||
func H2CTransporter() Transporter {
|
||||
return &h2Transporter{
|
||||
clients: make(map[string]*http.Client),
|
||||
}
|
||||
}
|
||||
|
||||
func (tr *h2Transporter) Dial(addr string, options ...DialOption) (net.Conn, error) {
|
||||
opts := &DialOptions{}
|
||||
for _, option := range options {
|
||||
option(opts)
|
||||
}
|
||||
|
||||
tr.clientMutex.Lock()
|
||||
client, ok := tr.clients[addr]
|
||||
if !ok {
|
||||
transport := http2.Transport{
|
||||
TLSClientConfig: tr.tlsConfig,
|
||||
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
|
||||
conn, err := opts.Chain.Dial(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tr.tlsConfig == nil {
|
||||
return conn, nil
|
||||
}
|
||||
return wrapTLSClient(conn, cfg)
|
||||
},
|
||||
}
|
||||
client = &http.Client{
|
||||
Transport: &transport,
|
||||
Timeout: opts.Timeout,
|
||||
}
|
||||
tr.clients[addr] = client
|
||||
}
|
||||
tr.clientMutex.Unlock()
|
||||
|
||||
pr, pw := io.Pipe()
|
||||
req := &http.Request{
|
||||
Method: http.MethodConnect,
|
||||
URL: &url.URL{Scheme: "https", Host: addr},
|
||||
Header: make(http.Header),
|
||||
Proto: "HTTP/2.0",
|
||||
ProtoMajor: 2,
|
||||
ProtoMinor: 0,
|
||||
Body: pr,
|
||||
Host: addr,
|
||||
ContentLength: -1,
|
||||
}
|
||||
if Debug {
|
||||
dump, _ := httputil.DumpRequest(req, false)
|
||||
log.Log("[http2]", string(dump))
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if Debug {
|
||||
dump, _ := httputil.DumpResponse(resp, false)
|
||||
log.Log("[http2]", string(dump))
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
resp.Body.Close()
|
||||
return nil, errors.New(resp.Status)
|
||||
}
|
||||
conn := &http2Conn{
|
||||
r: resp.Body,
|
||||
w: pw,
|
||||
closed: make(chan struct{}),
|
||||
}
|
||||
conn.remoteAddr, _ = net.ResolveTCPAddr("tcp", addr)
|
||||
conn.localAddr = &net.TCPAddr{IP: net.IPv4zero, Port: 0}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (tr *h2Transporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {
|
||||
opts := &HandshakeOptions{}
|
||||
for _, option := range options {
|
||||
option(opts)
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (tr *h2Transporter) Multiplex() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type http2Handler struct {
|
||||
options *HandlerOptions
|
||||
}
|
||||
|
||||
// HTTP2Handler creates a server Handler for HTTP2 proxy server.
|
||||
func HTTP2Handler(opts ...HandlerOption) Handler {
|
||||
h := &http2Handler{
|
||||
options: new(HandlerOptions),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(h.options)
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *http2Handler) Handle(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
h2c, ok := conn.(*http2ServerConn)
|
||||
if !ok {
|
||||
log.Log("[http2] wrong connection type")
|
||||
return
|
||||
}
|
||||
|
||||
h.roundTrip(h2c.w, h2c.r)
|
||||
}
|
||||
|
||||
func (h *http2Handler) roundTrip(w http.ResponseWriter, r *http.Request) {
|
||||
target := r.Host
|
||||
if !strings.Contains(target, ":") {
|
||||
target += ":80"
|
||||
}
|
||||
|
||||
if Debug {
|
||||
log.Logf("[http2] %s %s - %s %s", r.Method, r.RemoteAddr, target, r.Proto)
|
||||
dump, _ := httputil.DumpRequest(r, false)
|
||||
log.Log("[http2]", string(dump))
|
||||
}
|
||||
|
||||
w.Header().Set("Proxy-Agent", "gost/"+Version)
|
||||
|
||||
if !Can("tcp", target, h.options.Whitelist, h.options.Blacklist) {
|
||||
log.Logf("[http2] Unauthorized to tcp connect to %s", target)
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
u, p, _ := basicProxyAuth(r.Header.Get("Proxy-Authorization"))
|
||||
if Debug && (u != "" || p != "") {
|
||||
log.Logf("[http] %s - %s : Authorization: '%s' '%s'", r.RemoteAddr, target, u, p)
|
||||
}
|
||||
if !authenticate(u, p, h.options.Users...) {
|
||||
log.Logf("[http2] %s <- %s : proxy authentication required", r.RemoteAddr, target)
|
||||
w.Header().Set("Proxy-Authenticate", "Basic realm=\"gost\"")
|
||||
w.WriteHeader(http.StatusProxyAuthRequired)
|
||||
return
|
||||
}
|
||||
|
||||
r.Header.Del("Proxy-Authorization")
|
||||
r.Header.Del("Proxy-Connection")
|
||||
|
||||
cc, err := h.options.Chain.Dial(target)
|
||||
if err != nil {
|
||||
log.Logf("[http2] %s -> %s : %s", r.RemoteAddr, target, err)
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
if r.Method == http.MethodConnect {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if fw, ok := w.(http.Flusher); ok {
|
||||
fw.Flush()
|
||||
}
|
||||
|
||||
// compatible with HTTP1.x
|
||||
if hj, ok := w.(http.Hijacker); ok && r.ProtoMajor == 1 {
|
||||
// we take over the underly connection
|
||||
conn, _, err := hj.Hijack()
|
||||
if err != nil {
|
||||
log.Logf("[http2] %s -> %s : %s", r.RemoteAddr, target, err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
log.Logf("[http2] %s <-> %s : downgrade to HTTP/1.1", r.RemoteAddr, target)
|
||||
transport(conn, cc)
|
||||
log.Logf("[http2] %s >-< %s", r.RemoteAddr, target)
|
||||
return
|
||||
}
|
||||
|
||||
log.Logf("[http2] %s <-> %s", r.RemoteAddr, target)
|
||||
errc := make(chan error, 2)
|
||||
go func() {
|
||||
_, err := io.Copy(cc, r.Body)
|
||||
errc <- err
|
||||
}()
|
||||
go func() {
|
||||
_, err := io.Copy(flushWriter{w}, cc)
|
||||
errc <- err
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-errc:
|
||||
// glog.V(LWARNING).Infoln("exit", err)
|
||||
}
|
||||
log.Logf("[http2] %s >-< %s", r.RemoteAddr, target)
|
||||
return
|
||||
}
|
||||
|
||||
log.Logf("[http2] %s <-> %s", r.RemoteAddr, target)
|
||||
if err = r.Write(cc); err != nil {
|
||||
log.Logf("[http2] %s -> %s : %s", r.RemoteAddr, target, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(cc), r)
|
||||
if err != nil {
|
||||
log.Logf("[http2] %s -> %s : %s", r.RemoteAddr, target, err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
for k, v := range resp.Header {
|
||||
for _, vv := range v {
|
||||
w.Header().Add(k, vv)
|
||||
}
|
||||
}
|
||||
w.WriteHeader(resp.StatusCode)
|
||||
if _, err := io.Copy(flushWriter{w}, resp.Body); err != nil {
|
||||
log.Logf("[http2] %s <- %s : %s", r.RemoteAddr, target, err)
|
||||
}
|
||||
log.Logf("[http2] %s >-< %s", r.RemoteAddr, target)
|
||||
}
|
||||
|
||||
type http2Listener struct {
|
||||
server *http.Server
|
||||
connChan chan *http2ServerConn
|
||||
errChan chan error
|
||||
}
|
||||
|
||||
// HTTP2Listener creates a Listener for HTTP2 proxy server.
|
||||
func HTTP2Listener(addr string, config *tls.Config) (Listener, error) {
|
||||
l := &http2Listener{
|
||||
connChan: make(chan *http2ServerConn, 1024),
|
||||
errChan: make(chan error, 1),
|
||||
}
|
||||
if config == nil {
|
||||
config = DefaultTLSConfig
|
||||
}
|
||||
server := &http.Server{
|
||||
Addr: addr,
|
||||
Handler: http.HandlerFunc(l.handleFunc),
|
||||
TLSConfig: config,
|
||||
}
|
||||
if err := http2.ConfigureServer(server, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l.server = server
|
||||
|
||||
ln, err := tls.Listen("tcp", addr, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go func() {
|
||||
err := server.Serve(ln)
|
||||
if err != nil {
|
||||
log.Log("[http2]", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (l *http2Listener) handleFunc(w http.ResponseWriter, r *http.Request) {
|
||||
conn := &http2ServerConn{
|
||||
r: r,
|
||||
w: w,
|
||||
closed: make(chan struct{}),
|
||||
}
|
||||
select {
|
||||
case l.connChan <- conn:
|
||||
default:
|
||||
log.Logf("[http2] %s - %s: connection queue is full", r.RemoteAddr, l.server.Addr)
|
||||
return
|
||||
}
|
||||
|
||||
<-conn.closed
|
||||
}
|
||||
|
||||
func (l *http2Listener) Accept() (conn net.Conn, err error) {
|
||||
select {
|
||||
case conn = <-l.connChan:
|
||||
case err = <-l.errChan:
|
||||
if err == nil {
|
||||
err = errors.New("accpet on closed listener")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (l *http2Listener) Addr() net.Addr {
|
||||
addr, _ := net.ResolveTCPAddr("tcp", l.server.Addr)
|
||||
return addr
|
||||
}
|
||||
|
||||
func (l *http2Listener) Close() (err error) {
|
||||
select {
|
||||
case <-l.errChan:
|
||||
default:
|
||||
err = l.server.Close()
|
||||
l.errChan <- err
|
||||
close(l.errChan)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type h2Listener struct {
|
||||
net.Listener
|
||||
server *http2.Server
|
||||
tlsConfig *tls.Config
|
||||
connChan chan net.Conn
|
||||
errChan chan error
|
||||
}
|
||||
|
||||
// H2Listener creates a Listener for HTTP2 h2 tunnel server.
|
||||
func H2Listener(addr string, config *tls.Config) (Listener, error) {
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if config == nil {
|
||||
config = DefaultTLSConfig
|
||||
}
|
||||
|
||||
l := &h2Listener{
|
||||
Listener: tcpKeepAliveListener{ln.(*net.TCPListener)},
|
||||
server: &http2.Server{
|
||||
// MaxConcurrentStreams: 1000,
|
||||
PermitProhibitedCipherSuites: true,
|
||||
IdleTimeout: 5 * time.Minute,
|
||||
},
|
||||
tlsConfig: config,
|
||||
connChan: make(chan net.Conn, 1024),
|
||||
errChan: make(chan error, 1),
|
||||
}
|
||||
go l.listenLoop()
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// H2CListener creates a Listener for HTTP2 h2c tunnel server.
|
||||
func H2CListener(addr string) (Listener, error) {
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := &h2Listener{
|
||||
Listener: tcpKeepAliveListener{ln.(*net.TCPListener)},
|
||||
server: &http2.Server{
|
||||
// MaxConcurrentStreams: 1000,
|
||||
},
|
||||
connChan: make(chan net.Conn, 1024),
|
||||
errChan: make(chan error, 1),
|
||||
}
|
||||
go l.listenLoop()
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (l *h2Listener) listenLoop() {
|
||||
for {
|
||||
conn, err := l.Listener.Accept()
|
||||
if err != nil {
|
||||
log.Log("[http2] accept:", err)
|
||||
l.errChan <- err
|
||||
close(l.errChan)
|
||||
return
|
||||
}
|
||||
go l.handleLoop(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *h2Listener) handleLoop(conn net.Conn) {
|
||||
if l.tlsConfig != nil {
|
||||
conn = tls.Server(conn, l.tlsConfig)
|
||||
}
|
||||
|
||||
if tc, ok := conn.(*tls.Conn); ok {
|
||||
// NOTE: HTTP2 server will check the TLS version,
|
||||
// so we must ensure that the TLS connection is handshake completed.
|
||||
if err := tc.Handshake(); err != nil {
|
||||
log.Logf("[http2] %s - %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
opt := http2.ServeConnOpts{
|
||||
Handler: http.HandlerFunc(l.handleFunc),
|
||||
}
|
||||
l.server.ServeConn(conn, &opt)
|
||||
}
|
||||
|
||||
func (l *h2Listener) handleFunc(w http.ResponseWriter, r *http.Request) {
|
||||
log.Logf("[http2] %s %s - %s %s", r.Method, r.RemoteAddr, r.Host, r.Proto)
|
||||
if Debug {
|
||||
dump, _ := httputil.DumpRequest(r, false)
|
||||
log.Log("[http2]", string(dump))
|
||||
}
|
||||
w.Header().Set("Proxy-Agent", "gost/"+Version)
|
||||
conn, err := l.upgrade(w, r)
|
||||
if err != nil {
|
||||
log.Logf("[http2] %s %s - %s %s", r.Method, r.RemoteAddr, r.Host, r.Proto)
|
||||
return
|
||||
}
|
||||
select {
|
||||
case l.connChan <- conn:
|
||||
default:
|
||||
conn.Close()
|
||||
log.Logf("[http2] %s - %s: connection queue is full", conn.RemoteAddr(), conn.LocalAddr())
|
||||
}
|
||||
|
||||
<-conn.closed // NOTE: we need to wait for streaming end, or the connection will be closed
|
||||
}
|
||||
|
||||
func (l *h2Listener) upgrade(w http.ResponseWriter, r *http.Request) (*http2Conn, error) {
|
||||
if r.Method != http.MethodConnect {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return nil, errors.New("Method not allowed")
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if fw, ok := w.(http.Flusher); ok {
|
||||
fw.Flush() // write header to client
|
||||
}
|
||||
|
||||
remoteAddr, _ := net.ResolveTCPAddr("tcp", r.RemoteAddr)
|
||||
if remoteAddr == nil {
|
||||
remoteAddr = &net.TCPAddr{
|
||||
IP: net.IPv4zero,
|
||||
Port: 0,
|
||||
}
|
||||
}
|
||||
conn := &http2Conn{
|
||||
r: r.Body,
|
||||
w: flushWriter{w},
|
||||
localAddr: l.Listener.Addr(),
|
||||
remoteAddr: remoteAddr,
|
||||
closed: make(chan struct{}),
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (l *h2Listener) Accept() (conn net.Conn, err error) {
|
||||
var ok bool
|
||||
select {
|
||||
case conn = <-l.connChan:
|
||||
case err, ok = <-l.errChan:
|
||||
if !ok {
|
||||
err = errors.New("accpet on closed listener")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// HTTP2 connection, wrapped up just like a net.Conn
|
||||
type http2Conn struct {
|
||||
r io.Reader
|
||||
w io.Writer
|
||||
remoteAddr net.Addr
|
||||
localAddr net.Addr
|
||||
closed chan struct{}
|
||||
}
|
||||
|
||||
func (c *http2Conn) Read(b []byte) (n int, err error) {
|
||||
return c.r.Read(b)
|
||||
}
|
||||
|
||||
func (c *http2Conn) Write(b []byte) (n int, err error) {
|
||||
return c.w.Write(b)
|
||||
}
|
||||
|
||||
func (c *http2Conn) Close() (err error) {
|
||||
select {
|
||||
case <-c.closed:
|
||||
return
|
||||
default:
|
||||
close(c.closed)
|
||||
}
|
||||
if rc, ok := c.r.(io.Closer); ok {
|
||||
err = rc.Close()
|
||||
}
|
||||
if w, ok := c.w.(io.Closer); ok {
|
||||
err = w.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *http2Conn) LocalAddr() net.Addr {
|
||||
return c.localAddr
|
||||
}
|
||||
|
||||
func (c *http2Conn) RemoteAddr() net.Addr {
|
||||
return c.remoteAddr
|
||||
}
|
||||
|
||||
func (c *http2Conn) SetDeadline(t time.Time) error {
|
||||
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||
}
|
||||
|
||||
func (c *http2Conn) SetReadDeadline(t time.Time) error {
|
||||
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||
}
|
||||
|
||||
func (c *http2Conn) SetWriteDeadline(t time.Time) error {
|
||||
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||
}
|
||||
|
||||
// a dummy HTTP2 server conn used by HTTP2 handler
|
||||
type http2ServerConn struct {
|
||||
r *http.Request
|
||||
w http.ResponseWriter
|
||||
closed chan struct{}
|
||||
}
|
||||
|
||||
func (c *http2ServerConn) Read(b []byte) (n int, err error) {
|
||||
return 0, &net.OpError{Op: "read", Net: "http2", Source: nil, Addr: nil, Err: errors.New("read not supported")}
|
||||
}
|
||||
|
||||
func (c *http2ServerConn) Write(b []byte) (n int, err error) {
|
||||
return 0, &net.OpError{Op: "write", Net: "http2", Source: nil, Addr: nil, Err: errors.New("write not supported")}
|
||||
}
|
||||
|
||||
func (c *http2ServerConn) Close() error {
|
||||
select {
|
||||
case <-c.closed:
|
||||
default:
|
||||
close(c.closed)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *http2ServerConn) LocalAddr() net.Addr {
|
||||
addr, _ := net.ResolveTCPAddr("tcp", c.r.Host)
|
||||
return addr
|
||||
}
|
||||
|
||||
func (c *http2ServerConn) RemoteAddr() net.Addr {
|
||||
addr, _ := net.ResolveTCPAddr("tcp", c.r.RemoteAddr)
|
||||
return addr
|
||||
}
|
||||
|
||||
func (c *http2ServerConn) SetDeadline(t time.Time) error {
|
||||
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||
}
|
||||
|
||||
func (c *http2ServerConn) SetReadDeadline(t time.Time) error {
|
||||
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||
}
|
||||
|
||||
func (c *http2ServerConn) SetWriteDeadline(t time.Time) error {
|
||||
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||
}
|
||||
|
||||
// a dummy HTTP2 client conn used by HTTP2 client connector
|
||||
type http2ClientConn struct {
|
||||
addr string
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
func (c *http2ClientConn) Read(b []byte) (n int, err error) {
|
||||
return 0, &net.OpError{Op: "read", Net: "http2", Source: nil, Addr: nil, Err: errors.New("read not supported")}
|
||||
}
|
||||
|
||||
func (c *http2ClientConn) Write(b []byte) (n int, err error) {
|
||||
return 0, &net.OpError{Op: "write", Net: "http2", Source: nil, Addr: nil, Err: errors.New("write not supported")}
|
||||
}
|
||||
|
||||
func (c *http2ClientConn) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *http2ClientConn) LocalAddr() net.Addr {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *http2ClientConn) RemoteAddr() net.Addr {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *http2ClientConn) SetDeadline(t time.Time) error {
|
||||
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||
}
|
||||
|
||||
func (c *http2ClientConn) SetReadDeadline(t time.Time) error {
|
||||
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||
}
|
||||
|
||||
func (c *http2ClientConn) SetWriteDeadline(t time.Time) error {
|
||||
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||
}
|
||||
|
||||
type flushWriter struct {
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func (fw flushWriter) Write(p []byte) (n int, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if s, ok := r.(string); ok {
|
||||
err = errors.New(s)
|
||||
log.Log("[http2]", err)
|
||||
return
|
||||
}
|
||||
err = r.(error)
|
||||
}
|
||||
}()
|
||||
|
||||
n, err = fw.w.Write(p)
|
||||
if err != nil {
|
||||
// log.Log("flush writer:", err)
|
||||
return
|
||||
}
|
||||
if f, ok := fw.w.(http.Flusher); ok {
|
||||
f.Flush()
|
||||
}
|
||||
return
|
||||
}
|
518
kcp.go
518
kcp.go
@ -1,28 +1,30 @@
|
||||
// KCP feature is based on https://github.com/xtaci/kcptun
|
||||
|
||||
package gost
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/json"
|
||||
"github.com/golang/glog"
|
||||
"github.com/klauspost/compress/snappy"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"gopkg.in/xtaci/kcp-go.v2"
|
||||
"gopkg.in/xtaci/smux.v1"
|
||||
"encoding/csv"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultKCPConfigFile = "kcp.json"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
|
||||
"sync"
|
||||
|
||||
"github.com/go-log/log"
|
||||
"github.com/klauspost/compress/snappy"
|
||||
"gopkg.in/xtaci/kcp-go.v2"
|
||||
"gopkg.in/xtaci/smux.v1"
|
||||
)
|
||||
|
||||
var (
|
||||
SALT = "kcp-go"
|
||||
// KCPSalt is the default salt for KCP cipher.
|
||||
KCPSalt = "kcp-go"
|
||||
)
|
||||
|
||||
// KCPConfig describes the config for KCP.
|
||||
type KCPConfig struct {
|
||||
Key string `json:"key"`
|
||||
Crypt string `json:"crypt"`
|
||||
@ -41,41 +43,29 @@ type KCPConfig struct {
|
||||
NoCongestion int `json:"nc"`
|
||||
SockBuf int `json:"sockbuf"`
|
||||
KeepAlive int `json:"keepalive"`
|
||||
SnmpLog string `json:"snmplog"`
|
||||
SnmpPeriod int `json:"snmpperiod"`
|
||||
Signal bool `json:"signal"` // Signal enables the signal SIGUSR1 feature.
|
||||
}
|
||||
|
||||
func ParseKCPConfig(configFile string) (*KCPConfig, error) {
|
||||
if configFile == "" {
|
||||
configFile = DefaultKCPConfigFile
|
||||
}
|
||||
file, err := os.Open(configFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
config := &KCPConfig{}
|
||||
if err = json.NewDecoder(file).Decode(config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// Init initializes the KCP config.
|
||||
func (c *KCPConfig) Init() {
|
||||
switch c.Mode {
|
||||
case "normal":
|
||||
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 0, 30, 2, 1
|
||||
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 0, 50, 2, 1
|
||||
case "fast2":
|
||||
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 1, 20, 2, 1
|
||||
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 1, 30, 2, 1
|
||||
case "fast3":
|
||||
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 1, 10, 2, 1
|
||||
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 1, 20, 2, 1
|
||||
case "fast":
|
||||
fallthrough
|
||||
default:
|
||||
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 0, 20, 2, 1
|
||||
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 0, 40, 2, 1
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
// DefaultKCPConfig is the default KCP config.
|
||||
DefaultKCPConfig = &KCPConfig{
|
||||
Key: "it's a secrect",
|
||||
Crypt: "aes",
|
||||
@ -89,92 +79,330 @@ var (
|
||||
NoComp: false,
|
||||
AckNodelay: false,
|
||||
NoDelay: 0,
|
||||
Interval: 40,
|
||||
Interval: 50,
|
||||
Resend: 0,
|
||||
NoCongestion: 0,
|
||||
SockBuf: 4194304,
|
||||
KeepAlive: 10,
|
||||
SnmpLog: "",
|
||||
SnmpPeriod: 60,
|
||||
Signal: false,
|
||||
}
|
||||
)
|
||||
|
||||
type KCPServer struct {
|
||||
Base *ProxyServer
|
||||
Config *KCPConfig
|
||||
type kcpConn struct {
|
||||
conn net.Conn
|
||||
stream *smux.Stream
|
||||
}
|
||||
|
||||
func NewKCPServer(base *ProxyServer, config *KCPConfig) *KCPServer {
|
||||
return &KCPServer{Base: base, Config: config}
|
||||
func (c *kcpConn) Read(b []byte) (n int, err error) {
|
||||
return c.stream.Read(b)
|
||||
}
|
||||
|
||||
func (s *KCPServer) ListenAndServe() (err error) {
|
||||
if s.Config == nil {
|
||||
s.Config = DefaultKCPConfig
|
||||
}
|
||||
s.Config.Init()
|
||||
func (c *kcpConn) Write(b []byte) (n int, err error) {
|
||||
return c.stream.Write(b)
|
||||
}
|
||||
|
||||
ln, err := kcp.ListenWithOptions(s.Base.Node.Addr,
|
||||
blockCrypt(s.Config.Key, s.Config.Crypt, SALT), s.Config.DataShard, s.Config.ParityShard)
|
||||
func (c *kcpConn) Close() error {
|
||||
return c.stream.Close()
|
||||
}
|
||||
|
||||
func (c *kcpConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *kcpConn) RemoteAddr() net.Addr {
|
||||
return c.conn.RemoteAddr()
|
||||
}
|
||||
|
||||
func (c *kcpConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *kcpConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *kcpConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
type kcpSession struct {
|
||||
conn net.Conn
|
||||
session *smux.Session
|
||||
}
|
||||
|
||||
func (session *kcpSession) GetConn() (*kcpConn, error) {
|
||||
stream, err := session.session.OpenStream()
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
if err = ln.SetDSCP(s.Config.DSCP); err != nil {
|
||||
glog.V(LWARNING).Infoln("[kcp]", err)
|
||||
return &kcpConn{conn: session.conn, stream: stream}, nil
|
||||
}
|
||||
|
||||
func (session *kcpSession) Close() error {
|
||||
return session.session.Close()
|
||||
}
|
||||
|
||||
func (session *kcpSession) IsClosed() bool {
|
||||
return session.session.IsClosed()
|
||||
}
|
||||
|
||||
func (session *kcpSession) NumStreams() int {
|
||||
return session.session.NumStreams()
|
||||
}
|
||||
|
||||
type kcpTransporter struct {
|
||||
sessions map[string]*kcpSession
|
||||
sessionMutex sync.Mutex
|
||||
config *KCPConfig
|
||||
}
|
||||
|
||||
// KCPTransporter creates a Transporter that is used by KCP proxy client.
|
||||
func KCPTransporter(config *KCPConfig) Transporter {
|
||||
if config == nil {
|
||||
config = DefaultKCPConfig
|
||||
}
|
||||
if err = ln.SetReadBuffer(s.Config.SockBuf); err != nil {
|
||||
glog.V(LWARNING).Infoln("[kcp]", err)
|
||||
}
|
||||
if err = ln.SetWriteBuffer(s.Config.SockBuf); err != nil {
|
||||
glog.V(LWARNING).Infoln("[kcp]", err)
|
||||
config.Init()
|
||||
|
||||
go snmpLogger(config.SnmpLog, config.SnmpPeriod)
|
||||
if config.Signal {
|
||||
go kcpSigHandler()
|
||||
}
|
||||
|
||||
for {
|
||||
conn, err := ln.AcceptKCP()
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln(err)
|
||||
continue
|
||||
}
|
||||
|
||||
conn.SetStreamMode(true)
|
||||
conn.SetNoDelay(s.Config.NoDelay, s.Config.Interval, s.Config.Resend, s.Config.NoCongestion)
|
||||
conn.SetMtu(s.Config.MTU)
|
||||
conn.SetWindowSize(s.Config.SndWnd, s.Config.RcvWnd)
|
||||
conn.SetACKNoDelay(s.Config.AckNodelay)
|
||||
conn.SetKeepAlive(s.Config.KeepAlive)
|
||||
|
||||
go s.handleMux(conn)
|
||||
return &kcpTransporter{
|
||||
config: config,
|
||||
sessions: make(map[string]*kcpSession),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *KCPServer) handleMux(conn net.Conn) {
|
||||
func (tr *kcpTransporter) Dial(addr string, options ...DialOption) (conn net.Conn, err error) {
|
||||
uaddr, err := net.ResolveUDPAddr("udp", addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
tr.sessionMutex.Lock()
|
||||
defer tr.sessionMutex.Unlock()
|
||||
|
||||
session, ok := tr.sessions[addr]
|
||||
if !ok {
|
||||
conn, err = net.DialUDP("udp", nil, uaddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
session = &kcpSession{conn: conn}
|
||||
tr.sessions[addr] = session
|
||||
}
|
||||
return session.conn, nil
|
||||
}
|
||||
|
||||
func (tr *kcpTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {
|
||||
opts := &HandshakeOptions{}
|
||||
for _, option := range options {
|
||||
option(opts)
|
||||
}
|
||||
config := tr.config
|
||||
if opts.KCPConfig != nil {
|
||||
config = opts.KCPConfig
|
||||
}
|
||||
tr.sessionMutex.Lock()
|
||||
defer tr.sessionMutex.Unlock()
|
||||
|
||||
session, ok := tr.sessions[opts.Addr]
|
||||
if session != nil && session.conn != conn {
|
||||
conn.Close()
|
||||
return nil, errors.New("kcp: unrecognized connection")
|
||||
}
|
||||
if !ok || session.session == nil {
|
||||
s, err := tr.initSession(opts.Addr, conn, config)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
delete(tr.sessions, opts.Addr)
|
||||
return nil, err
|
||||
}
|
||||
session = s
|
||||
tr.sessions[opts.Addr] = session
|
||||
}
|
||||
cc, err := session.GetConn()
|
||||
if err != nil {
|
||||
session.Close()
|
||||
delete(tr.sessions, opts.Addr)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
func (tr *kcpTransporter) initSession(addr string, conn net.Conn, config *KCPConfig) (*kcpSession, error) {
|
||||
udpConn, ok := conn.(*net.UDPConn)
|
||||
if !ok {
|
||||
return nil, errors.New("kcp: wrong connection type")
|
||||
}
|
||||
|
||||
kcpconn, err := kcp.NewConn(addr,
|
||||
blockCrypt(config.Key, config.Crypt, KCPSalt),
|
||||
config.DataShard, config.ParityShard,
|
||||
&kcp.ConnectedUDPConn{UDPConn: udpConn, Conn: udpConn})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kcpconn.SetStreamMode(true)
|
||||
kcpconn.SetNoDelay(config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
|
||||
kcpconn.SetWindowSize(config.SndWnd, config.RcvWnd)
|
||||
kcpconn.SetMtu(config.MTU)
|
||||
kcpconn.SetACKNoDelay(config.AckNodelay)
|
||||
kcpconn.SetKeepAlive(config.KeepAlive)
|
||||
|
||||
if err := kcpconn.SetDSCP(config.DSCP); err != nil {
|
||||
log.Log("[kcp]", err)
|
||||
}
|
||||
if err := kcpconn.SetReadBuffer(config.SockBuf); err != nil {
|
||||
log.Log("[kcp]", err)
|
||||
}
|
||||
if err := kcpconn.SetWriteBuffer(config.SockBuf); err != nil {
|
||||
log.Log("[kcp]", err)
|
||||
}
|
||||
|
||||
// stream multiplex
|
||||
smuxConfig := smux.DefaultConfig()
|
||||
smuxConfig.MaxReceiveBuffer = s.Config.SockBuf
|
||||
smuxConfig.MaxReceiveBuffer = config.SockBuf
|
||||
var cc net.Conn = kcpconn
|
||||
if !config.NoComp {
|
||||
cc = newCompStreamConn(kcpconn)
|
||||
}
|
||||
session, err := smux.Client(cc, smuxConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &kcpSession{conn: conn, session: session}, nil
|
||||
}
|
||||
|
||||
glog.V(LINFO).Infof("[kcp] %s - %s", conn.RemoteAddr(), s.Base.Node.Addr)
|
||||
func (tr *kcpTransporter) Multiplex() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
if !s.Config.NoComp {
|
||||
type kcpListener struct {
|
||||
config *KCPConfig
|
||||
ln *kcp.Listener
|
||||
connChan chan net.Conn
|
||||
errChan chan error
|
||||
}
|
||||
|
||||
// KCPListener creates a Listener for KCP proxy server.
|
||||
func KCPListener(addr string, config *KCPConfig) (Listener, error) {
|
||||
if config == nil {
|
||||
config = DefaultKCPConfig
|
||||
}
|
||||
config.Init()
|
||||
|
||||
ln, err := kcp.ListenWithOptions(addr,
|
||||
blockCrypt(config.Key, config.Crypt, KCPSalt), config.DataShard, config.ParityShard)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = ln.SetDSCP(config.DSCP); err != nil {
|
||||
log.Log("[kcp]", err)
|
||||
}
|
||||
if err = ln.SetReadBuffer(config.SockBuf); err != nil {
|
||||
log.Log("[kcp]", err)
|
||||
}
|
||||
if err = ln.SetWriteBuffer(config.SockBuf); err != nil {
|
||||
log.Log("[kcp]", err)
|
||||
}
|
||||
|
||||
go snmpLogger(config.SnmpLog, config.SnmpPeriod)
|
||||
if config.Signal {
|
||||
go kcpSigHandler()
|
||||
}
|
||||
|
||||
l := &kcpListener{
|
||||
config: config,
|
||||
ln: ln,
|
||||
connChan: make(chan net.Conn, 1024),
|
||||
errChan: make(chan error, 1),
|
||||
}
|
||||
go l.listenLoop()
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (l *kcpListener) listenLoop() {
|
||||
for {
|
||||
conn, err := l.ln.AcceptKCP()
|
||||
if err != nil {
|
||||
log.Log("[kcp] accept:", err)
|
||||
l.errChan <- err
|
||||
close(l.errChan)
|
||||
return
|
||||
}
|
||||
conn.SetStreamMode(true)
|
||||
conn.SetNoDelay(l.config.NoDelay, l.config.Interval, l.config.Resend, l.config.NoCongestion)
|
||||
conn.SetMtu(l.config.MTU)
|
||||
conn.SetWindowSize(l.config.SndWnd, l.config.RcvWnd)
|
||||
conn.SetACKNoDelay(l.config.AckNodelay)
|
||||
conn.SetKeepAlive(l.config.KeepAlive)
|
||||
go l.mux(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *kcpListener) mux(conn net.Conn) {
|
||||
smuxConfig := smux.DefaultConfig()
|
||||
smuxConfig.MaxReceiveBuffer = l.config.SockBuf
|
||||
|
||||
log.Logf("[kcp] %s - %s", conn.RemoteAddr(), l.Addr())
|
||||
|
||||
if !l.config.NoComp {
|
||||
conn = newCompStreamConn(conn)
|
||||
}
|
||||
|
||||
mux, err := smux.Server(conn, smuxConfig)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln("[kcp]", err)
|
||||
log.Log("[kcp]", err)
|
||||
return
|
||||
}
|
||||
defer mux.Close()
|
||||
|
||||
glog.V(LINFO).Infof("[kcp] %s <-> %s", conn.RemoteAddr(), s.Base.Node.Addr)
|
||||
defer glog.V(LINFO).Infof("[kcp] %s >-< %s", conn.RemoteAddr(), s.Base.Node.Addr)
|
||||
log.Logf("[kcp] %s <-> %s", conn.RemoteAddr(), l.Addr())
|
||||
defer log.Logf("[kcp] %s >-< %s", conn.RemoteAddr(), l.Addr())
|
||||
|
||||
for {
|
||||
stream, err := mux.AcceptStream()
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln("[kcp]", err)
|
||||
log.Log("[kcp] accept stream:", err)
|
||||
return
|
||||
}
|
||||
go s.Base.handleConn(NewKCPConn(conn, stream))
|
||||
|
||||
cc := &kcpConn{conn: conn, stream: stream}
|
||||
select {
|
||||
case l.connChan <- cc:
|
||||
default:
|
||||
cc.Close()
|
||||
log.Logf("[kcp] %s - %s: connection queue is full", conn.RemoteAddr(), conn.LocalAddr())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *kcpListener) Accept() (conn net.Conn, err error) {
|
||||
var ok bool
|
||||
select {
|
||||
case conn = <-l.connChan:
|
||||
case err, ok = <-l.errChan:
|
||||
if !ok {
|
||||
err = errors.New("accpet on closed listener")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
func (l *kcpListener) Addr() net.Addr {
|
||||
return l.ln.Addr()
|
||||
}
|
||||
|
||||
func (l *kcpListener) Close() error {
|
||||
return l.ln.Close()
|
||||
}
|
||||
|
||||
func blockCrypt(key, crypt, salt string) (block kcp.BlockCrypt) {
|
||||
pass := pbkdf2.Key([]byte(key), []byte(salt), 4096, 32, sha1.New)
|
||||
|
||||
@ -209,115 +437,35 @@ func blockCrypt(key, crypt, salt string) (block kcp.BlockCrypt) {
|
||||
return
|
||||
}
|
||||
|
||||
type KCPSession struct {
|
||||
conn net.Conn
|
||||
session *smux.Session
|
||||
}
|
||||
|
||||
func DialKCP(addr string, config *KCPConfig) (*KCPSession, error) {
|
||||
if config == nil {
|
||||
config = DefaultKCPConfig
|
||||
func snmpLogger(format string, interval int) {
|
||||
if format == "" || interval == 0 {
|
||||
return
|
||||
}
|
||||
config.Init()
|
||||
|
||||
kcpconn, err := kcp.DialWithOptions(addr,
|
||||
blockCrypt(config.Key, config.Crypt, SALT), config.DataShard, config.ParityShard)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
ticker := time.NewTicker(time.Duration(interval) * time.Second)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
f, err := os.OpenFile(time.Now().Format(format), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
log.Log("[kcp]", err)
|
||||
return
|
||||
}
|
||||
w := csv.NewWriter(f)
|
||||
// write header in empty file
|
||||
if stat, err := f.Stat(); err == nil && stat.Size() == 0 {
|
||||
if err := w.Write(append([]string{"Unix"}, kcp.DefaultSnmp.Header()...)); err != nil {
|
||||
log.Log("[kcp]", err)
|
||||
}
|
||||
}
|
||||
if err := w.Write(append([]string{fmt.Sprint(time.Now().Unix())}, kcp.DefaultSnmp.ToSlice()...)); err != nil {
|
||||
log.Log("[kcp]", err)
|
||||
}
|
||||
kcp.DefaultSnmp.Reset()
|
||||
w.Flush()
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
|
||||
kcpconn.SetStreamMode(true)
|
||||
kcpconn.SetNoDelay(config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
|
||||
kcpconn.SetWindowSize(config.SndWnd, config.RcvWnd)
|
||||
kcpconn.SetMtu(config.MTU)
|
||||
kcpconn.SetACKNoDelay(config.AckNodelay)
|
||||
kcpconn.SetKeepAlive(config.KeepAlive)
|
||||
|
||||
if err := kcpconn.SetDSCP(config.DSCP); err != nil {
|
||||
glog.V(LWARNING).Infoln("[kcp]", err)
|
||||
}
|
||||
if err := kcpconn.SetReadBuffer(config.SockBuf); err != nil {
|
||||
glog.V(LWARNING).Infoln("[kcp]", err)
|
||||
}
|
||||
if err := kcpconn.SetWriteBuffer(config.SockBuf); err != nil {
|
||||
glog.V(LWARNING).Infoln("[kcp]", err)
|
||||
}
|
||||
|
||||
// stream multiplex
|
||||
smuxConfig := smux.DefaultConfig()
|
||||
smuxConfig.MaxReceiveBuffer = config.SockBuf
|
||||
var conn net.Conn = kcpconn
|
||||
if !config.NoComp {
|
||||
conn = newCompStreamConn(kcpconn)
|
||||
}
|
||||
session, err := smux.Client(conn, smuxConfig)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return &KCPSession{conn: conn, session: session}, nil
|
||||
}
|
||||
|
||||
func (session *KCPSession) GetConn() (*KCPConn, error) {
|
||||
stream, err := session.session.OpenStream()
|
||||
if err != nil {
|
||||
session.Close()
|
||||
return nil, err
|
||||
}
|
||||
return NewKCPConn(session.conn, stream), nil
|
||||
}
|
||||
|
||||
func (session *KCPSession) Close() error {
|
||||
return session.session.Close()
|
||||
}
|
||||
|
||||
func (session *KCPSession) IsClosed() bool {
|
||||
return session.session.IsClosed()
|
||||
}
|
||||
|
||||
func (session *KCPSession) NumStreams() int {
|
||||
return session.session.NumStreams()
|
||||
}
|
||||
|
||||
type KCPConn struct {
|
||||
conn net.Conn
|
||||
stream *smux.Stream
|
||||
}
|
||||
|
||||
func NewKCPConn(conn net.Conn, stream *smux.Stream) *KCPConn {
|
||||
return &KCPConn{conn: conn, stream: stream}
|
||||
}
|
||||
|
||||
func (c *KCPConn) Read(b []byte) (n int, err error) {
|
||||
return c.stream.Read(b)
|
||||
}
|
||||
|
||||
func (c *KCPConn) Write(b []byte) (n int, err error) {
|
||||
return c.stream.Write(b)
|
||||
}
|
||||
|
||||
func (c *KCPConn) Close() error {
|
||||
return c.stream.Close()
|
||||
}
|
||||
|
||||
func (c *KCPConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *KCPConn) RemoteAddr() net.Addr {
|
||||
return c.conn.RemoteAddr()
|
||||
}
|
||||
|
||||
func (c *KCPConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *KCPConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *KCPConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
type compStreamConn struct {
|
||||
|
32
log.go
Normal file
32
log.go
Normal file
@ -0,0 +1,32 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
}
|
||||
|
||||
// LogLogger uses the standard log package as the logger
|
||||
type LogLogger struct {
|
||||
}
|
||||
|
||||
func (l *LogLogger) Log(v ...interface{}) {
|
||||
log.Output(3, fmt.Sprintln(v...))
|
||||
}
|
||||
|
||||
func (l *LogLogger) Logf(format string, v ...interface{}) {
|
||||
log.Output(3, fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
// NopLogger is a dummy logger that discards the log outputs
|
||||
type NopLogger struct {
|
||||
}
|
||||
|
||||
func (l *NopLogger) Log(v ...interface{}) {
|
||||
}
|
||||
|
||||
func (l *NopLogger) Logf(format string, v ...interface{}) {
|
||||
}
|
141
node.go
141
node.go
@ -1,63 +1,41 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/golang/glog"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Proxy node represent a proxy
|
||||
type ProxyNode struct {
|
||||
Addr string // [host]:port
|
||||
Protocol string // protocol: http/socks5/ss
|
||||
Transport string // transport: ws/wss/tls/http2/tcp/udp/rtcp/rudp
|
||||
Remote string // remote address, used by tcp/udp port forwarding
|
||||
Users []*url.Userinfo // authentication for proxy
|
||||
values url.Values
|
||||
serverName string
|
||||
conn net.Conn
|
||||
// Node is a proxy node, mainly used to construct a proxy chain.
|
||||
type Node struct {
|
||||
Addr string
|
||||
Protocol string
|
||||
Transport string
|
||||
Remote string // remote address, used by tcp/udp port forwarding
|
||||
User *url.Userinfo
|
||||
Values url.Values
|
||||
Client *Client
|
||||
DialOptions []DialOption
|
||||
HandshakeOptions []HandshakeOption
|
||||
}
|
||||
|
||||
// ParseNode parses the node info.
|
||||
// The proxy node string pattern is [scheme://][user:pass@host]:port.
|
||||
//
|
||||
// Scheme can be devided into two parts by character '+', such as: http+tls.
|
||||
func ParseProxyNode(s string, isServeNode bool) (node ProxyNode, err error) {
|
||||
func ParseNode(s string) (node Node, err error) {
|
||||
if !strings.Contains(s, "://") {
|
||||
s = "gost://" + s
|
||||
s = "auto://" + s
|
||||
}
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
node = ProxyNode{
|
||||
Addr: u.Host,
|
||||
values: u.Query(),
|
||||
serverName: u.Host,
|
||||
}
|
||||
|
||||
if u.User != nil {
|
||||
node.Users = append(node.Users, u.User)
|
||||
}
|
||||
|
||||
users, er := parseUsers(node.Get("secrets"))
|
||||
if users != nil {
|
||||
node.Users = append(node.Users, users...)
|
||||
}
|
||||
if er != nil {
|
||||
glog.V(LWARNING).Infoln("secrets:", er)
|
||||
}
|
||||
|
||||
if strings.Contains(u.Host, ":") {
|
||||
node.serverName, _, _ = net.SplitHostPort(u.Host)
|
||||
if node.serverName == "" {
|
||||
node.serverName = "localhost" // default server name
|
||||
}
|
||||
node = Node{
|
||||
Addr: u.Host,
|
||||
Values: u.Query(),
|
||||
User: u.User,
|
||||
}
|
||||
|
||||
schemes := strings.Split(u.Scheme, "+")
|
||||
@ -71,26 +49,24 @@ func ParseProxyNode(s string, isServeNode bool) (node ProxyNode, err error) {
|
||||
}
|
||||
|
||||
switch node.Transport {
|
||||
case "ws", "wss", "tls", "http2", "quic", "kcp", "redirect", "ssu":
|
||||
case "tls", "ws", "wss", "kcp", "ssh", "quic", "ssu", "http2", "h2", "h2c", "redirect", "obfs4":
|
||||
case "https":
|
||||
node.Protocol = "http"
|
||||
node.Transport = "tls"
|
||||
case "tcp", "udp": // started from v2.1, tcp and udp are for local port forwarding
|
||||
node.Remote = strings.Trim(u.EscapedPath(), "/")
|
||||
case "rtcp", "rudp": // started from v2.1, rtcp and rudp are for remote port forwarding
|
||||
case "rtcp", "rudp": // rtcp and rudp are for remote port forwarding
|
||||
node.Remote = strings.Trim(u.EscapedPath(), "/")
|
||||
case "obfs4":
|
||||
err := node.Obfs4Init(isServeNode)
|
||||
if err != nil {
|
||||
glog.V(LDEBUG).Infoln("obfs4 init failed", err)
|
||||
return node, err
|
||||
}
|
||||
default:
|
||||
node.Transport = ""
|
||||
}
|
||||
|
||||
switch node.Protocol {
|
||||
case "http", "http2", "socks", "socks5", "ss":
|
||||
case "http", "http2", "socks4", "socks4a", "ss", "ssu":
|
||||
case "socks", "socks5":
|
||||
node.Protocol = "socks5"
|
||||
case "tcp", "udp", "rtcp", "rudp": // port forwarding
|
||||
case "direct", "remote", "forward": // SSH port forwarding
|
||||
default:
|
||||
node.Protocol = ""
|
||||
}
|
||||
@ -98,70 +74,21 @@ func ParseProxyNode(s string, isServeNode bool) (node ProxyNode, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func parseUsers(authFile string) (users []*url.Userinfo, err error) {
|
||||
if authFile == "" {
|
||||
return
|
||||
func Can(action string, addr string, whitelist, blacklist *Permissions) bool {
|
||||
if !strings.Contains(addr, ":") {
|
||||
addr = addr + ":80"
|
||||
}
|
||||
host, strport, err := net.SplitHostPort(addr)
|
||||
|
||||
file, err := os.Open(authFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
|
||||
s := strings.SplitN(line, " ", 2)
|
||||
if len(s) == 1 {
|
||||
users = append(users, url.User(strings.TrimSpace(s[0])))
|
||||
} else if len(s) == 2 {
|
||||
users = append(users, url.UserPassword(strings.TrimSpace(s[0]), strings.TrimSpace(s[1])))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
err = scanner.Err()
|
||||
return
|
||||
}
|
||||
port, err := strconv.Atoi(strport)
|
||||
|
||||
// Get get node parameter by key
|
||||
func (node *ProxyNode) Get(key string) string {
|
||||
return node.values.Get(key)
|
||||
}
|
||||
|
||||
func (node *ProxyNode) getBool(key string) bool {
|
||||
s := node.Get(key)
|
||||
if b, _ := strconv.ParseBool(s); b {
|
||||
return b
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
n, _ := strconv.Atoi(s)
|
||||
return n > 0
|
||||
}
|
||||
|
||||
func (node *ProxyNode) Set(key, value string) {
|
||||
node.values.Set(key, value)
|
||||
}
|
||||
|
||||
func (node *ProxyNode) insecureSkipVerify() bool {
|
||||
return !node.getBool("secure")
|
||||
}
|
||||
|
||||
func (node *ProxyNode) certFile() string {
|
||||
if cert := node.Get("cert"); cert != "" {
|
||||
return cert
|
||||
}
|
||||
return DefaultCertFile
|
||||
}
|
||||
|
||||
func (node *ProxyNode) keyFile() string {
|
||||
if key := node.Get("key"); key != "" {
|
||||
return key
|
||||
}
|
||||
return DefaultKeyFile
|
||||
}
|
||||
|
||||
func (node ProxyNode) String() string {
|
||||
return fmt.Sprintf("transport: %s, protocol: %s, addr: %s", node.Transport, node.Protocol, node.Addr)
|
||||
return whitelist.Can(action, host, port) && !blacklist.Can(action, host, port)
|
||||
}
|
||||
|
134
obfs4.go
134
obfs4.go
@ -1,4 +1,4 @@
|
||||
// obfs4 connection wrappers
|
||||
// obfs4 connection wrappers
|
||||
|
||||
package gost
|
||||
|
||||
@ -6,47 +6,36 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"git.torproject.org/pluggable-transports/goptlib.git" // package pt
|
||||
|
||||
"github.com/go-log/log"
|
||||
|
||||
pt "git.torproject.org/pluggable-transports/goptlib.git"
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/transports/base"
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4"
|
||||
"git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4"
|
||||
)
|
||||
|
||||
// Factories are stored per node since some arguments reside in them.
|
||||
// For simplicity, c & s variables are packed together.
|
||||
|
||||
type obfs4Context struct {
|
||||
cf base.ClientFactory
|
||||
cargs interface{} // type obfs4ClientArgs
|
||||
sf base.ServerFactory
|
||||
sargs *pt.Args
|
||||
cf base.ClientFactory
|
||||
cargs interface{} // type obfs4ClientArgs
|
||||
sf base.ServerFactory
|
||||
sargs *pt.Args
|
||||
}
|
||||
|
||||
|
||||
var obfs4Map = make(map[string]obfs4Context)
|
||||
|
||||
func (node *ProxyNode) obfs4GetContext() (obfs4Context, error) {
|
||||
if node.Transport != "obfs4" {
|
||||
return obfs4Context{}, fmt.Errorf("non-obfs4 node has no obfs4 context")
|
||||
}
|
||||
ctx, ok := obfs4Map[node.Addr]
|
||||
if !ok {
|
||||
return obfs4Context{}, fmt.Errorf("obfs4 context not inited")
|
||||
}
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func (node *ProxyNode) Obfs4Init(isServeNode bool) error {
|
||||
func Obfs4Init(node Node, isServeNode bool) error {
|
||||
if _, ok := obfs4Map[node.Addr]; ok {
|
||||
return fmt.Errorf("obfs4 context already inited")
|
||||
}
|
||||
|
||||
t := new(obfs4.Transport)
|
||||
|
||||
stateDir := node.values.Get("state-dir")
|
||||
stateDir := node.Values.Get("state-dir")
|
||||
if stateDir == "" {
|
||||
stateDir = "."
|
||||
}
|
||||
|
||||
ptArgs := pt.Args(node.values)
|
||||
ptArgs := pt.Args(node.Values)
|
||||
|
||||
if !isServeNode {
|
||||
cf, err := t.ClientFactory(stateDir)
|
||||
@ -70,40 +59,28 @@ func (node *ProxyNode) Obfs4Init(isServeNode bool) error {
|
||||
|
||||
obfs4Map[node.Addr] = obfs4Context{sf: sf, sargs: sargs}
|
||||
|
||||
fmt.Println("[obfs4 server inited]", node.Obfs4ServerURL())
|
||||
log.Log("[obfs4] server inited:", obfs4ServerURL(node))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (node *ProxyNode) Obfs4ClientConn(conn net.Conn) (net.Conn, error) {
|
||||
ctx, err := node.obfs4GetContext()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func obfs4GetContext(addr string) (obfs4Context, error) {
|
||||
ctx, ok := obfs4Map[addr]
|
||||
if !ok {
|
||||
return obfs4Context{}, fmt.Errorf("obfs4 context not inited")
|
||||
}
|
||||
|
||||
pseudoDial := func (a, b string) (net.Conn, error) {return conn, nil}
|
||||
return ctx.cf.Dial("tcp", "", pseudoDial, ctx.cargs)
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func (node *ProxyNode) Obfs4ServerConn(conn net.Conn) (net.Conn, error) {
|
||||
ctx, err := node.obfs4GetContext()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ctx.sf.WrapConn(conn)
|
||||
}
|
||||
|
||||
func (node *ProxyNode) Obfs4ServerURL() string {
|
||||
ctx, err := node.obfs4GetContext()
|
||||
func obfs4ServerURL(node Node) string {
|
||||
ctx, err := obfs4GetContext(node.Addr)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
values := (*url.Values)(ctx.sargs)
|
||||
query := values.Encode()
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%s+%s://%s/?%s", //obfs4-cert=%s&iat-mode=%s",
|
||||
node.Protocol,
|
||||
@ -112,3 +89,70 @@ func (node *ProxyNode) Obfs4ServerURL() string {
|
||||
query,
|
||||
)
|
||||
}
|
||||
|
||||
func obfs4ClientConn(addr string, conn net.Conn) (net.Conn, error) {
|
||||
ctx, err := obfs4GetContext(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pseudoDial := func(a, b string) (net.Conn, error) { return conn, nil }
|
||||
return ctx.cf.Dial("tcp", "", pseudoDial, ctx.cargs)
|
||||
}
|
||||
|
||||
func obfs4ServerConn(addr string, conn net.Conn) (net.Conn, error) {
|
||||
ctx, err := obfs4GetContext(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ctx.sf.WrapConn(conn)
|
||||
}
|
||||
|
||||
type obfs4Transporter struct {
|
||||
tcpTransporter
|
||||
}
|
||||
|
||||
// Obfs4Transporter creates a Transporter that is used by obfs4 client.
|
||||
func Obfs4Transporter() Transporter {
|
||||
return &obfs4Transporter{}
|
||||
}
|
||||
|
||||
func (tr *obfs4Transporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {
|
||||
opts := &HandshakeOptions{}
|
||||
for _, option := range options {
|
||||
option(opts)
|
||||
}
|
||||
return obfs4ClientConn(opts.Addr, conn)
|
||||
}
|
||||
|
||||
type obfs4Listener struct {
|
||||
addr string
|
||||
net.Listener
|
||||
}
|
||||
|
||||
// Obfs4Listener creates a Listener for obfs4 server.
|
||||
func Obfs4Listener(addr string) (Listener, error) {
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := &obfs4Listener{
|
||||
addr: addr,
|
||||
Listener: ln,
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (l *obfs4Listener) Accept() (net.Conn, error) {
|
||||
conn, err := l.Listener.Accept()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc, err := obfs4ServerConn(l.addr, conn)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return cc, nil
|
||||
}
|
||||
|
185
permissions.go
Normal file
185
permissions.go
Normal file
@ -0,0 +1,185 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
glob "github.com/ryanuber/go-glob"
|
||||
)
|
||||
|
||||
type Permission struct {
|
||||
Actions StringSet
|
||||
Hosts StringSet
|
||||
Ports PortSet
|
||||
}
|
||||
|
||||
type Permissions []Permission
|
||||
|
||||
func minint(x, y int) int {
|
||||
if x < y {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
func maxint(x, y int) int {
|
||||
if x > y {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
type PortRange struct {
|
||||
Min, Max int
|
||||
}
|
||||
|
||||
func (ir *PortRange) Contains(value int) bool {
|
||||
return value >= ir.Min && value <= ir.Max
|
||||
}
|
||||
|
||||
func ParsePortRange(s string) (*PortRange, error) {
|
||||
if s == "*" {
|
||||
return &PortRange{Min: 0, Max: 65535}, nil
|
||||
}
|
||||
|
||||
minmax := strings.Split(s, "-")
|
||||
switch len(minmax) {
|
||||
case 1:
|
||||
port, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if port < 0 || port > 65535 {
|
||||
return nil, fmt.Errorf("invalid port: %s", s)
|
||||
}
|
||||
return &PortRange{Min: port, Max: port}, nil
|
||||
case 2:
|
||||
min, err := strconv.Atoi(minmax[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
max, err := strconv.Atoi(minmax[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
realmin := maxint(0, minint(min, max))
|
||||
realmax := minint(65535, maxint(min, max))
|
||||
|
||||
return &PortRange{Min: realmin, Max: realmax}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid range: %s", s)
|
||||
}
|
||||
}
|
||||
|
||||
func (ps *PortSet) Contains(value int) bool {
|
||||
for _, portRange := range *ps {
|
||||
if portRange.Contains(value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
type PortSet []PortRange
|
||||
|
||||
func ParsePortSet(s string) (*PortSet, error) {
|
||||
ps := &PortSet{}
|
||||
|
||||
if s == "" {
|
||||
return nil, errors.New("must specify at least one port")
|
||||
}
|
||||
|
||||
ranges := strings.Split(s, ",")
|
||||
|
||||
for _, r := range ranges {
|
||||
portRange, err := ParsePortRange(r)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
*ps = append(*ps, *portRange)
|
||||
}
|
||||
|
||||
return ps, nil
|
||||
}
|
||||
|
||||
func (ss *StringSet) Contains(subj string) bool {
|
||||
for _, s := range *ss {
|
||||
if glob.Glob(s, subj) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
type StringSet []string
|
||||
|
||||
func ParseStringSet(s string) (*StringSet, error) {
|
||||
ss := &StringSet{}
|
||||
if s == "" {
|
||||
return nil, errors.New("cannot be empty")
|
||||
}
|
||||
|
||||
*ss = strings.Split(s, ",")
|
||||
|
||||
return ss, nil
|
||||
}
|
||||
|
||||
func (ps *Permissions) Can(action string, host string, port int) bool {
|
||||
for _, p := range *ps {
|
||||
if p.Actions.Contains(action) && p.Hosts.Contains(host) && p.Ports.Contains(port) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func ParsePermissions(s string) (*Permissions, error) {
|
||||
ps := &Permissions{}
|
||||
|
||||
if s == "" {
|
||||
return &Permissions{}, nil
|
||||
}
|
||||
|
||||
perms := strings.Split(s, " ")
|
||||
|
||||
for _, perm := range perms {
|
||||
parts := strings.Split(perm, ":")
|
||||
|
||||
switch len(parts) {
|
||||
case 3:
|
||||
actions, err := ParseStringSet(parts[0])
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("action list must look like connect,bind given: %s", parts[0])
|
||||
}
|
||||
|
||||
hosts, err := ParseStringSet(parts[1])
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("hosts list must look like google.pl,*.google.com given: %s", parts[1])
|
||||
}
|
||||
|
||||
ports, err := ParsePortSet(parts[2])
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ports list must look like 80,8000-9000, given: %s", parts[2])
|
||||
}
|
||||
|
||||
permission := Permission{Actions: *actions, Hosts: *hosts, Ports: *ports}
|
||||
|
||||
*ps = append(*ps, permission)
|
||||
default:
|
||||
return nil, fmt.Errorf("permission must have format [actions]:[hosts]:[ports] given: %s", perm)
|
||||
}
|
||||
}
|
||||
|
||||
return ps, nil
|
||||
}
|
152
permissions_test.go
Normal file
152
permissions_test.go
Normal file
@ -0,0 +1,152 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var portRangeTests = []struct {
|
||||
in string
|
||||
out *PortRange
|
||||
}{
|
||||
{"1", &PortRange{Min: 1, Max: 1}},
|
||||
{"1-3", &PortRange{Min: 1, Max: 3}},
|
||||
{"3-1", &PortRange{Min: 1, Max: 3}},
|
||||
{"0-100000", &PortRange{Min: 0, Max: 65535}},
|
||||
{"*", &PortRange{Min: 0, Max: 65535}},
|
||||
}
|
||||
|
||||
var stringSetTests = []struct {
|
||||
in string
|
||||
out *StringSet
|
||||
}{
|
||||
{"*", &StringSet{"*"}},
|
||||
{"google.pl,google.com", &StringSet{"google.pl", "google.com"}},
|
||||
}
|
||||
|
||||
var portSetTests = []struct {
|
||||
in string
|
||||
out *PortSet
|
||||
}{
|
||||
{"1,3", &PortSet{PortRange{Min: 1, Max: 1}, PortRange{Min: 3, Max: 3}}},
|
||||
{"1-3,7-5", &PortSet{PortRange{Min: 1, Max: 3}, PortRange{Min: 5, Max: 7}}},
|
||||
{"0-100000", &PortSet{PortRange{Min: 0, Max: 65535}}},
|
||||
{"*", &PortSet{PortRange{Min: 0, Max: 65535}}},
|
||||
}
|
||||
|
||||
var permissionsTests = []struct {
|
||||
in string
|
||||
out *Permissions
|
||||
}{
|
||||
{"", &Permissions{}},
|
||||
{"*:*:*", &Permissions{
|
||||
Permission{
|
||||
Actions: StringSet{"*"},
|
||||
Hosts: StringSet{"*"},
|
||||
Ports: PortSet{PortRange{Min: 0, Max: 65535}},
|
||||
},
|
||||
}},
|
||||
{"bind:127.0.0.1,localhost:80,443,8000-8100 connect:*.google.pl:80,443", &Permissions{
|
||||
Permission{
|
||||
Actions: StringSet{"bind"},
|
||||
Hosts: StringSet{"127.0.0.1", "localhost"},
|
||||
Ports: PortSet{
|
||||
PortRange{Min: 80, Max: 80},
|
||||
PortRange{Min: 443, Max: 443},
|
||||
PortRange{Min: 8000, Max: 8100},
|
||||
},
|
||||
},
|
||||
Permission{
|
||||
Actions: StringSet{"connect"},
|
||||
Hosts: StringSet{"*.google.pl"},
|
||||
Ports: PortSet{
|
||||
PortRange{Min: 80, Max: 80},
|
||||
PortRange{Min: 443, Max: 443},
|
||||
},
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
func TestPortRangeParse(t *testing.T) {
|
||||
for _, test := range portRangeTests {
|
||||
actual, err := ParsePortRange(test.in)
|
||||
if err != nil {
|
||||
t.Errorf("ParsePortRange(%q) returned error: %v", test.in, err)
|
||||
} else if *actual != *test.out {
|
||||
t.Errorf("ParsePortRange(%q): got %v, want %v", test.in, actual, test.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPortRangeContains(t *testing.T) {
|
||||
actual, _ := ParsePortRange("5-10")
|
||||
|
||||
if !actual.Contains(5) || !actual.Contains(7) || !actual.Contains(10) {
|
||||
t.Errorf("5-10 should contain 5, 7 and 10")
|
||||
}
|
||||
|
||||
if actual.Contains(4) || actual.Contains(11) {
|
||||
t.Errorf("5-10 should not contain 4, 11")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringSetParse(t *testing.T) {
|
||||
for _, test := range stringSetTests {
|
||||
actual, err := ParseStringSet(test.in)
|
||||
if err != nil {
|
||||
t.Errorf("ParseStringSet(%q) returned error: %v", test.in, err)
|
||||
} else if fmt.Sprintln(actual) != fmt.Sprintln(test.out) {
|
||||
t.Errorf("ParseStringSet(%q): got %v, want %v", test.in, actual, test.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringSetContains(t *testing.T) {
|
||||
ss, _ := ParseStringSet("google.pl,*.google.com")
|
||||
|
||||
if !ss.Contains("google.pl") || !ss.Contains("www.google.com") {
|
||||
t.Errorf("google.pl,*.google.com should contain google.pl and www.google.com")
|
||||
}
|
||||
|
||||
if ss.Contains("www.google.pl") || ss.Contains("foobar.com") {
|
||||
t.Errorf("google.pl,*.google.com shound not contain www.google.pl and foobar.com")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPortSetParse(t *testing.T) {
|
||||
for _, test := range portSetTests {
|
||||
actual, err := ParsePortSet(test.in)
|
||||
if err != nil {
|
||||
t.Errorf("ParsePortRange(%q) returned error: %v", test.in, err)
|
||||
} else if fmt.Sprintln(actual) != fmt.Sprintln(test.out) {
|
||||
t.Errorf("ParsePortRange(%q): got %v, want %v", test.in, actual, test.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPortSetContains(t *testing.T) {
|
||||
actual, _ := ParsePortSet("5-10,20-30")
|
||||
|
||||
if !actual.Contains(5) || !actual.Contains(7) || !actual.Contains(10) {
|
||||
t.Errorf("5-10,20-30 should contain 5, 7 and 10")
|
||||
}
|
||||
|
||||
if !actual.Contains(20) || !actual.Contains(27) || !actual.Contains(30) {
|
||||
t.Errorf("5-10,20-30 should contain 20, 27 and 30")
|
||||
}
|
||||
|
||||
if actual.Contains(4) || actual.Contains(11) || actual.Contains(31) {
|
||||
t.Errorf("5-10,20-30 should not contain 4, 11, 31")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPermissionsParse(t *testing.T) {
|
||||
for _, test := range permissionsTests {
|
||||
actual, err := ParsePermissions(test.in)
|
||||
if err != nil {
|
||||
t.Errorf("ParsePermissions(%q) returned error: %v", test.in, err)
|
||||
} else if fmt.Sprintln(actual) != fmt.Sprintln(test.out) {
|
||||
t.Errorf("ParsePermissions(%q): got %v, want %v", test.in, actual, test.out)
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user