Remove gost itself from vendored packages

This commit is contained in:
Adam Stankiewicz 2017-03-09 19:44:40 +01:00
parent 479cbd9527
commit 42a9d102dd
No known key found for this signature in database
GPG Key ID: A62480DCEAC884DF
21 changed files with 0 additions and 5332 deletions

View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2016 ginuerzh
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,358 +0,0 @@
gost - GO Simple Tunnel
======
### GO语言实现的安全隧道
[English README](README_en.md)
特性
------
* 可同时监听多端口
* 可设置转发代理,支持多级转发(代理链)
* 支持标准HTTP/HTTPS/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+)
* SSH隧道 (2.4+)
二进制文件下载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, socks4(a), socks5, shadowsocks), transport: 数据传输方式(ws, wss, tls, http2, quic, kcp, pht), 二者可以任意组合,或单独使用:
> http - HTTP代理: http://:8080
> http+tls - HTTPS代理(可能需要提供受信任的证书): http+tls://:443或https://:443
> http2 - HTTP2代理并向下兼容HTTPS代理: http2://:443
> socks4(a) - 标准SOCKS4(A)代理: socks4://:1080或socks4a://:1080
> 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 relayssu://chacha20:123456@:8338
> quic - QUIC代理quic://:6121
> kcp - KCP通道kcp://:8388或kcp://aes:123456@:8388
> pht - 普通HTTP通道pht://:8080
> redirect - 透明代理redirect://:12345
> ssh - SSH转发隧道ssh://admin:123456@:2222
#### 端口转发
适用于-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上。当代理链末端(最后一个-F参数)为SSH类型时gost会直接使用SSH的本地端口转发功能。
#### 本地端口转发(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上。当代理链末端(最后一个-F参数)为SSH类型时gost会直接使用SSH的远程端口转发功能。
#### 远程端口转发(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类型代理。

View File

@ -1,362 +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/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+)
* SSH tunnel (2.4+)
Binary file downloadhttps://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, socks4(a), socks5, shadowsocks),
transport: data transmission mode (ws, wss, tls, http2, quic, kcp, pht), 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
> socks4(a) - standard SOCKS4(A) proxy: socks4://:1080 or socks4a://:1080
> 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 relayssu://chacha20:123456@:8338
> quic - standard QUIC proxy, quic://:6121
> kcp - standard KCP tunnelkcp://:8388 or kcp://aes:123456@:8388
> pht - plain HTTP tunnel, pht://:8080
> redirect - transparent proxyredirect://:12345
> ssh - SSH tunnel, ssh://admin:123456@:2222
#### 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). If the last node of the chain (the last -F parameter) is a SSH tunnel, then gost will use the local port forwarding function of SSH directly.
#### 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). 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.
#### 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.

View File

@ -1,473 +0,0 @@
package gost
import (
"crypto/rand"
"crypto/tls"
"encoding/base64"
"errors"
"github.com/ginuerzh/pht"
"github.com/golang/glog"
"github.com/lucas-clemente/quic-go/h2quic"
"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
phtClient *pht.Client
quicClient *http.Client
}
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" || node.Transport == "pht" || node.Transport == "quic") && i > 0 {
glog.Fatal("KCP/PHT/QUIC 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
go snmpLogger(config.SnmpLog, config.SnmpPeriod)
go kcpSigHandler()
return
}
if c.nodes[0].Transport == "quic" {
glog.V(LINFO).Infoln("QUIC is enabled")
c.quicClient = &http.Client{
Transport: &h2quic.QuicRoundTripper{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: c.nodes[0].insecureSkipVerify(),
ServerName: c.nodes[0].serverName,
},
},
}
}
if c.nodes[0].Transport == "pht" {
glog.V(LINFO).Infoln("Pure HTTP mode is enabled")
c.phtClient = pht.NewClient(c.nodes[0].Addr, c.nodes[0].Get("key"))
}
}
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)
}
}
if nodes[0].Transport == "quic" {
glog.V(LINFO).Infoln("Dial with QUIC")
return c.quicConnect(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 if node.Transport == "pht" {
cc, err = c.phtClient.Dial()
} 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)
}
func (c *ProxyChain) quicConnect(addr string) (net.Conn, error) {
quicNode := c.nodes[0]
header := make(http.Header)
header.Set("Gost-Target", addr) // Flag header to indicate the address that server connected to
if quicNode.Users != nil {
header.Set("Proxy-Authorization",
"Basic "+base64.StdEncoding.EncodeToString([]byte(quicNode.Users[0].String())))
}
return c.getQuicConn(header)
}
func (c *ProxyChain) getQuicConn(header http.Header) (net.Conn, error) {
quicNode := c.nodes[0]
pr, pw := io.Pipe()
if header == nil {
header = make(http.Header)
}
/*
req := http.Request{
Method: http.MethodGet,
URL: &url.URL{Scheme: "https", Host: quicNode.Addr},
Header: header,
Proto: "HTTP/2.0",
ProtoMajor: 2,
ProtoMinor: 0,
Body: pr,
Host: quicNode.Addr,
ContentLength: -1,
}
*/
req, err := http.NewRequest(http.MethodPost, "https://"+quicNode.Addr, pr)
if err != nil {
return nil, err
}
req.ContentLength = -1
req.Header = header
if glog.V(LDEBUG) {
dump, _ := httputil.DumpRequest(req, false)
glog.Infoln(string(dump))
}
resp, err := c.quicClient.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.ResolveUDPAddr("udp", quicNode.Addr)
return conn, nil
}

View File

@ -1,292 +0,0 @@
package gost
import (
"bufio"
"crypto/tls"
"encoding/base64"
"errors"
"fmt"
"github.com/ginuerzh/gosocks4"
"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 "socks4", "socks4a":
atype := gosocks4.AddrDomain
host, port, err := net.SplitHostPort(addr)
if err != nil {
return err
}
p, _ := strconv.Atoi(port)
if c.Node.Protocol == "socks4" {
taddr, err := net.ResolveTCPAddr("tcp4", addr)
if err != nil {
return err
}
host = taddr.IP.String()
p = taddr.Port
atype = gosocks4.AddrIPv4
}
req := gosocks4.NewRequest(gosocks4.CmdConnect,
&gosocks4.Addr{Type: atype, Host: host, Port: uint16(p)}, nil)
if err := req.Write(c); err != nil {
return err
}
glog.V(LDEBUG).Infof("[%s] %s", c.Node.Protocol, req)
reply, err := gosocks4.ReadReply(c)
if err != nil {
return err
}
glog.V(LDEBUG).Infof("[%s] %s", c.Node.Protocol, reply)
if reply.Code != gosocks4.Granted {
return errors.New(fmt.Sprintf("%s: code=%d", c.Node.Protocol, reply.Code))
}
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)
}

View File

@ -1,689 +0,0 @@
package gost
import (
"errors"
"fmt"
"github.com/ginuerzh/gosocks5"
"github.com/golang/glog"
"golang.org/x/crypto/ssh"
"net"
"strconv"
"time"
)
type TcpForwardServer struct {
Base *ProxyServer
sshClient *ssh.Client
Handler func(conn net.Conn, raddr *net.TCPAddr)
}
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
}
quit := make(chan interface{})
close(quit)
for {
start:
conn, err := ln.Accept()
if err != nil {
glog.V(LWARNING).Infoln("[tcp]", err)
continue
}
setKeepAlive(conn, KeepAliveTime)
select {
case <-quit:
if s.Base.Chain.lastNode == nil || s.Base.Chain.lastNode.Transport != "ssh" {
break
}
if err := s.initSSHClient(); err != nil {
glog.V(LWARNING).Infoln("[tcp]", err)
conn.Close()
goto start
}
quit = make(chan interface{})
go func(ch chan interface{}) {
s.sshClient.Wait()
glog.V(LINFO).Infoln("[tcp] connection closed")
close(ch)
}(quit)
default:
}
go s.Handler(conn, raddr)
}
}
func (s *TcpForwardServer) initSSHClient() error {
if s.sshClient != nil {
s.sshClient.Close()
s.sshClient = nil
}
sshNode := s.Base.Chain.lastNode
c, err := s.Base.Chain.GetConn()
if err != nil {
return err
}
var user, password string
if len(sshNode.Users) > 0 {
user = sshNode.Users[0].Username()
password, _ = sshNode.Users[0].Password()
}
config := ssh.ClientConfig{
User: user,
Auth: []ssh.AuthMethod{
ssh.Password(password),
},
}
sshConn, chans, reqs, err := ssh.NewClientConn(c, sshNode.Addr, &config)
if err != nil {
return err
}
s.sshClient = ssh.NewClient(sshConn, chans, reqs)
s.Handler = s.handleTcpForwardSSH
return nil
}
func (s *TcpForwardServer) handleTcpForward(conn net.Conn, raddr *net.TCPAddr) {
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)
}
func (s *TcpForwardServer) handleTcpForwardSSH(conn net.Conn, raddr *net.TCPAddr) {
defer conn.Close()
if s.sshClient == nil {
return
}
rc, err := s.sshClient.DialTCP("tcp", nil, raddr)
if err != nil {
glog.V(LWARNING).Infof("[tcp] %s -> %s : %s", conn.RemoteAddr(), raddr, err)
return
}
defer rc.Close()
glog.V(LINFO).Infof("[tcp] %s <-> %s", conn.RemoteAddr(), raddr)
Transport(conn, rc)
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
glog.V(LINFO).Infof("[rtcp] %s - %s", laddr, raddr)
lastNode := s.Base.Chain.lastNode
if lastNode != nil && lastNode.Transport == "ssh" {
s.connectRTcpForwardSSH(conn, lastNode, laddr, raddr)
} else {
if err := s.connectRTcpForward(conn, laddr, raddr); err != nil {
conn.Close()
}
}
time.Sleep(3 * time.Second)
}
}
func (s *RTcpForwardServer) connectRTcpForwardSSH(conn net.Conn, sshNode *ProxyNode, laddr, raddr net.Addr) error {
defer conn.Close()
var user, password string
if len(sshNode.Users) > 0 {
user = sshNode.Users[0].Username()
password, _ = sshNode.Users[0].Password()
}
config := ssh.ClientConfig{
User: user,
Auth: []ssh.AuthMethod{
ssh.Password(password),
},
}
c, chans, reqs, err := ssh.NewClientConn(conn, sshNode.Addr, &config)
if err != nil {
glog.V(LWARNING).Infof("[rtcp] %s -> %s : %s", laddr, raddr, err)
return err
}
client := ssh.NewClient(c, chans, reqs)
quit := make(chan interface{})
defer close(quit)
go func() {
defer client.Close()
var c <-chan time.Time
ping, _ := strconv.Atoi(sshNode.Get("ping"))
if ping > 0 {
d := time.Second * time.Duration(ping)
glog.V(LINFO).Infoln("[rtcp] ping is enabled:", d)
t := time.NewTicker(d)
defer t.Stop()
c = t.C
}
for {
select {
case <-c:
_, _, err := client.SendRequest("ping", true, nil)
if err != nil {
glog.V(LWARNING).Infoln("[rtcp] ping", err)
return
}
glog.V(LDEBUG).Infoln("[rtcp] heartbeat OK")
case <-quit:
glog.V(LWARNING).Infoln("[rtcp] ssh connection closed")
return
}
}
}()
ln, err := client.Listen("tcp", laddr.String())
if err != nil {
glog.V(LWARNING).Infof("[rtcp] %s -> %s : %s", laddr, raddr, err)
return err
}
defer ln.Close()
for {
rc, err := ln.Accept()
if err != nil {
return err
}
go func(c net.Conn) {
defer c.Close()
tc, err := net.DialTimeout("tcp", raddr.String(), time.Second*30)
if err != nil {
glog.V(LWARNING).Infof("[rtcp] %s -> %s : %s", laddr, raddr, err)
return
}
defer tc.Close()
glog.V(LINFO).Infof("[rtcp] %s <-> %s", c.RemoteAddr(), c.LocalAddr())
Transport(c, tc)
glog.V(LINFO).Infof("[rtcp] %s >-< %s", c.RemoteAddr(), c.LocalAddr())
}(rc)
}
}
func (s *RTcpForwardServer) connectRTcpForward(conn net.Conn, laddr, raddr net.Addr) error {
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*30)
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{})
}()
}
}

View File

@ -1,162 +0,0 @@
package gost
import (
"crypto/tls"
"encoding/base64"
"errors"
"github.com/golang/glog"
"io"
"net"
"strings"
"time"
)
const (
Version = "2.4-dev20170303"
)
// 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
}
func Transport(rw1, rw2 io.ReadWriter) error {
errc := make(chan error, 1)
go func() {
_, err := io.Copy(rw1, rw2)
errc <- err
}()
go func() {
_, err := io.Copy(rw2, rw1)
errc <- err
}()
return <-errc
}

View File

@ -1,410 +0,0 @@
package gost
import (
"bufio"
"crypto/tls"
"encoding/base64"
"errors"
"github.com/ginuerzh/pht"
"github.com/golang/glog"
"golang.org/x/net/http2"
"io"
"net"
"net/http"
"net/http/httputil"
"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"))
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.Transport == "" && (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")
req.Header.Del("Proxy-Connection")
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()
glog.V(LINFO).Infof("[http2] %s -> %s : downgrade to HTTP/1.1", req.RemoteAddr, target)
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("[http2] %s -> %s : %s", req.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 {
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
}
type PureHttpServer struct {
Base *ProxyServer
Handler func(net.Conn)
}
func NewPureHttpServer(base *ProxyServer) *PureHttpServer {
return &PureHttpServer{
Base: base,
}
}
func (s *PureHttpServer) ListenAndServe() error {
server := pht.Server{
Addr: s.Base.Node.Addr,
Key: s.Base.Node.Get("key"),
}
if server.Handler == nil {
server.Handler = s.handleConn
}
return server.ListenAndServe()
}
func (s *PureHttpServer) handleConn(conn net.Conn) {
glog.V(LINFO).Infof("[pht] %s - %s", conn.RemoteAddr(), conn.LocalAddr())
s.Base.handleConn(conn)
}

View File

@ -1,408 +0,0 @@
// KCP feature is based on https://github.com/xtaci/kcptun
package gost
import (
"crypto/sha1"
"encoding/csv"
"encoding/json"
"fmt"
"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"`
SnmpLog string `json:"snmplog"`
SnmpPeriod int `json:"snmpperiod"`
}
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, 50, 2, 1
case "fast2":
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 1, 30, 2, 1
case "fast3":
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, 40, 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: 50,
Resend: 0,
NoCongestion: 0,
SockBuf: 4194304,
KeepAlive: 10,
SnmpLog: "",
SnmpPeriod: 60,
}
)
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)
}
go snmpLogger(s.Config.SnmpLog, s.Config.SnmpPeriod)
go kcpSigHandler()
for {
conn, err := ln.AcceptKCP()
if err != nil {
glog.V(LWARNING).Infoln("[kcp]", 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
}
func snmpLogger(path string, interval int) {
if path == "" || interval == 0 {
return
}
ticker := time.NewTicker(time.Duration(interval) * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
f, err := os.OpenFile(time.Now().Format(path), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
glog.V(LWARNING).Infoln("[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 {
glog.V(LWARNING).Infoln("[kcp]", err)
}
}
if err := w.Write(append([]string{fmt.Sprint(time.Now().Unix())}, kcp.DefaultSnmp.ToSlice()...)); err != nil {
glog.V(LWARNING).Infoln("[kcp]", err)
}
kcp.DefaultSnmp.Reset()
w.Flush()
f.Close()
}
}
}
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)
}

View File

@ -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", "pht", "ssh":
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", "socks4", "socks4a", "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)
}

View File

@ -1,81 +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)
server.Handler = http.HandlerFunc(NewHttp2Server(s.Base).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)
}

View File

@ -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
}

View File

@ -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")
}

View File

@ -1,284 +0,0 @@
package gost
import (
"bufio"
"crypto/tls"
"github.com/ginuerzh/gosocks4"
"github.com/ginuerzh/gosocks5"
"github.com/golang/glog"
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
"golang.org/x/crypto/ssh"
"io"
"io/ioutil"
"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()
case "pht": // pure http tunnel
return NewPureHttpServer(s).ListenAndServe()
case "ssh": // SSH tunnel
key := s.Node.Get("key")
privateBytes, err := ioutil.ReadFile(key)
if err != nil {
glog.V(LWARNING).Infoln("[ssh]", err)
privateBytes = defaultRawKey
}
private, err := ssh.ParsePrivateKey(privateBytes)
if err != nil {
return err
}
config := ssh.ServerConfig{
PasswordCallback: DefaultPasswordCallback(s.Node.Users),
}
if len(s.Node.Users) == 0 {
config.NoClientAuth = true
}
config.AddHostKey(private)
s := &SSHServer{
Addr: node.Addr,
Base: s,
Config: &config,
}
return s.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
case "socks4", "socks4a":
req, err := gosocks4.ReadRequest(conn)
if err != nil {
glog.V(LWARNING).Infoln("[socks4]", err)
return
}
NewSocks4Server(conn, s).HandleRequest(req)
return
}
br := bufio.NewReader(conn)
b, err := br.Peek(1)
if err != nil {
glog.V(LWARNING).Infoln(err)
return
}
switch b[0] {
case gosocks4.Ver4:
req, err := gosocks4.ReadRequest(br)
if err != nil {
glog.V(LWARNING).Infoln("[socks4]", err)
return
}
NewSocks4Server(conn, s).HandleRequest(req)
case gosocks5.Ver5:
methods, err := gosocks5.ReadMethods(br)
if err != nil {
glog.V(LWARNING).Infoln("[socks5]", err)
return
}
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)
default: // http
req, err := http.ReadRequest(br)
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
}

View File

@ -1,5 +0,0 @@
// +build windows
package gost
func kcpSigHandler() {}

View File

@ -1,23 +0,0 @@
// +build !windows
package gost
import (
"github.com/golang/glog"
"gopkg.in/xtaci/kcp-go.v2"
"os"
"os/signal"
"syscall"
)
func kcpSigHandler() {
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGUSR1)
for {
switch <-ch {
case syscall.SIGUSR1:
glog.V(LINFO).Infof("[kcp] SNMP: %+v", kcp.DefaultSnmp.Copy())
}
}
}

View File

@ -1,746 +0,0 @@
package gost
import (
"bytes"
"crypto/tls"
"github.com/ginuerzh/gosocks4"
"github.com/ginuerzh/gosocks5"
"github.com/golang/glog"
"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),
}
}
type Socks4Server struct {
conn net.Conn
Base *ProxyServer
}
func NewSocks4Server(conn net.Conn, base *ProxyServer) *Socks4Server {
return &Socks4Server{conn: conn, Base: base}
}
func (s *Socks4Server) HandleRequest(req *gosocks4.Request) {
glog.V(LDEBUG).Infof("[socks4] %s -> %s\n%s", s.conn.RemoteAddr(), req.Addr, req)
switch req.Cmd {
case gosocks4.CmdConnect:
glog.V(LINFO).Infof("[socks4-connect] %s -> %s", s.conn.RemoteAddr(), req.Addr)
s.handleConnect(req)
case gosocks4.CmdBind:
glog.V(LINFO).Infof("[socks4-bind] %s - %s", s.conn.RemoteAddr(), req.Addr)
s.handleBind(req)
default:
glog.V(LWARNING).Infoln("[socks4] Unrecognized request:", req.Cmd)
}
}
func (s *Socks4Server) handleConnect(req *gosocks4.Request) {
cc, err := s.Base.Chain.Dial(req.Addr.String())
if err != nil {
glog.V(LWARNING).Infof("[socks4-connect] %s -> %s : %s", s.conn.RemoteAddr(), req.Addr, err)
rep := gosocks4.NewReply(gosocks4.Failed, nil)
rep.Write(s.conn)
glog.V(LDEBUG).Infof("[socks4-connect] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, rep)
return
}
defer cc.Close()
rep := gosocks4.NewReply(gosocks4.Granted, nil)
if err := rep.Write(s.conn); err != nil {
glog.V(LWARNING).Infof("[socks4-connect] %s <- %s : %s", s.conn.RemoteAddr(), req.Addr, err)
return
}
glog.V(LDEBUG).Infof("[socks4-connect] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, rep)
glog.V(LINFO).Infof("[socks4-connect] %s <-> %s", s.conn.RemoteAddr(), req.Addr)
s.Base.transport(s.conn, cc)
glog.V(LINFO).Infof("[socks4-connect] %s >-< %s", s.conn.RemoteAddr(), req.Addr)
}
func (s *Socks4Server) handleBind(req *gosocks4.Request) {
cc, err := s.Base.Chain.GetConn()
// connection error
if err != nil && err != ErrEmptyChain {
glog.V(LWARNING).Infof("[socks4-bind] %s <- %s : %s", s.conn.RemoteAddr(), req.Addr, err)
reply := gosocks4.NewReply(gosocks4.Failed, nil)
reply.Write(s.conn)
glog.V(LDEBUG).Infof("[socks4-bind] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, reply)
return
}
// TODO: serve socks4 bind
if err == ErrEmptyChain {
//s.bindOn(req.Addr.String())
return
}
defer cc.Close()
// forward request
req.Write(cc)
glog.V(LINFO).Infof("[socks4-bind] %s <-> %s", s.conn.RemoteAddr(), cc.RemoteAddr())
s.Base.transport(s.conn, cc)
glog.V(LINFO).Infof("[socks4-bind] %s >-< %s", s.conn.RemoteAddr(), cc.RemoteAddr())
}

353
vendor/github.com/ginuerzh/gost/ss.go generated vendored
View File

@ -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
}

View File

@ -1,236 +0,0 @@
// The ssh tunnel is inspired by easyssh(https://dev.justinjudd.org/justin/easyssh)
package gost
import (
"encoding/binary"
"fmt"
"github.com/golang/glog"
"golang.org/x/crypto/ssh"
"net"
"net/url"
"strconv"
)
// Applicaple SSH Request types for Port Forwarding - RFC 4254 7.X
const (
DirectForwardRequest = "direct-tcpip" // RFC 4254 7.2
RemoteForwardRequest = "tcpip-forward" // RFC 4254 7.1
ForwardedTCPReturnRequest = "forwarded-tcpip" // RFC 4254 7.2
CancelRemoteForwardRequest = "cancel-tcpip-forward" // RFC 4254 7.1
)
type SSHServer struct {
Addr string
Base *ProxyServer
Config *ssh.ServerConfig
Handler func(ssh.Conn, <-chan ssh.NewChannel, <-chan *ssh.Request)
}
func (s *SSHServer) ListenAndServe() error {
ln, err := net.Listen("tcp", s.Addr)
if err != nil {
glog.V(LWARNING).Infoln("[ssh] Listen:", err)
return err
}
defer ln.Close()
for {
conn, err := ln.Accept()
if err != nil {
glog.V(LWARNING).Infoln("[ssh] Accept:", err)
return err
}
go func(conn net.Conn) {
sshConn, chans, reqs, err := ssh.NewServerConn(conn, s.Config)
if err != nil {
glog.V(LWARNING).Infof("[ssh] %s -> %s : %s", conn.RemoteAddr(), s.Addr, err)
return
}
defer sshConn.Close()
if s.Handler == nil {
s.Handler = s.handleSSHConn
}
glog.V(LINFO).Infof("[ssh] %s <-> %s", conn.RemoteAddr(), s.Addr)
s.Handler(sshConn, chans, reqs)
glog.V(LINFO).Infof("[ssh] %s >-< %s", conn.RemoteAddr(), s.Addr)
}(conn)
}
}
func (s *SSHServer) handleSSHConn(conn ssh.Conn, chans <-chan ssh.NewChannel, reqs <-chan *ssh.Request) {
quit := make(chan interface{})
go func() {
for req := range reqs {
switch req.Type {
case RemoteForwardRequest:
go s.tcpipForwardRequest(conn, req, quit)
default:
// glog.V(LWARNING).Infoln("unknown channel type:", req.Type)
if req.WantReply {
req.Reply(false, nil)
}
}
}
}()
go func() {
for newChannel := range chans {
// Check the type of channel
t := newChannel.ChannelType()
switch t {
case DirectForwardRequest:
channel, requests, err := newChannel.Accept()
if err != nil {
glog.V(LINFO).Infoln("[ssh] Could not accept channel:", err)
continue
}
p := directForward{}
ssh.Unmarshal(newChannel.ExtraData(), &p)
go ssh.DiscardRequests(requests)
go s.directPortForwardChannel(channel, fmt.Sprintf("%s:%d", p.Host1, p.Port1))
default:
glog.V(LWARNING).Infoln("[ssh] Unknown channel type:", t)
newChannel.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t))
}
}
}()
conn.Wait()
close(quit)
}
// directForward is structure for RFC 4254 7.2 - can be used for "forwarded-tcpip" and "direct-tcpip"
type directForward struct {
Host1 string
Port1 uint32
Host2 string
Port2 uint32
}
func (p directForward) String() string {
return fmt.Sprintf("%s:%d -> %s:%d", p.Host2, p.Port2, p.Host1, p.Port1)
}
func (s *SSHServer) directPortForwardChannel(channel ssh.Channel, raddr string) {
defer channel.Close()
glog.V(LINFO).Infof("[ssh-tcp] %s - %s", s.Addr, raddr)
conn, err := s.Base.Chain.Dial(raddr)
if err != nil {
glog.V(LINFO).Infof("[ssh-tcp] %s - %s : %s", s.Addr, raddr, err)
return
}
defer conn.Close()
glog.V(LINFO).Infof("[ssh-tcp] %s <-> %s", s.Addr, raddr)
Transport(conn, channel)
glog.V(LINFO).Infof("[ssh-tcp] %s >-< %s", s.Addr, raddr)
}
// tcpipForward is structure for RFC 4254 7.1 "tcpip-forward" request
type tcpipForward struct {
Host string
Port uint32
}
func (s *SSHServer) tcpipForwardRequest(sshConn ssh.Conn, req *ssh.Request, quit <-chan interface{}) {
t := tcpipForward{}
ssh.Unmarshal(req.Payload, &t)
addr := fmt.Sprintf("%s:%d", t.Host, t.Port)
glog.V(LINFO).Infoln("[ssh-rtcp] listening tcp", addr)
ln, err := net.Listen("tcp", addr) //tie to the client connection
if err != nil {
glog.V(LWARNING).Infoln("[ssh-rtcp]", err)
req.Reply(false, nil)
return
}
defer ln.Close()
replyFunc := func() error {
if t.Port == 0 && req.WantReply { // Client sent port 0. let them know which port is actually being used
_, port, err := getHostPortFromAddr(ln.Addr())
if err != nil {
return err
}
var b [4]byte
binary.BigEndian.PutUint32(b[:], uint32(port))
t.Port = uint32(port)
return req.Reply(true, b[:])
}
return req.Reply(true, nil)
}
if err := replyFunc(); err != nil {
glog.V(LWARNING).Infoln("[ssh-rtcp]", err)
return
}
go func() {
for {
conn, err := ln.Accept()
if err != nil { // Unable to accept new connection - listener likely closed
return
}
go func(conn net.Conn) {
defer conn.Close()
p := directForward{}
var err error
var portnum int
p.Host1 = t.Host
p.Port1 = t.Port
p.Host2, portnum, err = getHostPortFromAddr(conn.RemoteAddr())
if err != nil {
return
}
p.Port2 = uint32(portnum)
ch, reqs, err := sshConn.OpenChannel(ForwardedTCPReturnRequest, ssh.Marshal(p))
if err != nil {
glog.V(1).Infoln("[ssh-rtcp] open forwarded channel:", err)
return
}
defer ch.Close()
go ssh.DiscardRequests(reqs)
glog.V(LINFO).Infof("[ssh-rtcp] %s <-> %s", conn.RemoteAddr(), conn.LocalAddr())
Transport(ch, conn)
glog.V(LINFO).Infof("[ssh-rtcp] %s >-< %s", conn.RemoteAddr(), conn.LocalAddr())
}(conn)
}
}()
<-quit
}
func getHostPortFromAddr(addr net.Addr) (host string, port int, err error) {
host, portString, err := net.SplitHostPort(addr.String())
if err != nil {
return
}
port, err = strconv.Atoi(portString)
return
}
type PasswordCallbackFunc func(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error)
func DefaultPasswordCallback(users []*url.Userinfo) PasswordCallbackFunc {
return func(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error) {
for _, user := range users {
u := user.Username()
p, _ := user.Password()
if u == conn.User() && p == string(password) {
return nil, nil
}
}
glog.V(LINFO).Infof("[ssh] %s -> %s : password rejected for %s", conn.RemoteAddr(), conn.LocalAddr(), conn.User())
return nil, fmt.Errorf("password rejected for %s", conn.User())
}
}

142
vendor/github.com/ginuerzh/gost/ws.go generated vendored
View File

@ -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)
}

6
vendor/vendor.json vendored
View File

@ -20,12 +20,6 @@
"revision": "0f737bddba2abd1496dc03c8b39b817cc5f33fa7",
"revisionTime": "2017-01-19T05:34:58Z"
},
{
"checksumSHA1": "90Nj9KD5KzY15ZBn95Coz7G85+0=",
"path": "github.com/ginuerzh/gost",
"revision": "26f4e49f0538177eb5ec33c84a280afe2ae16042",
"revisionTime": "2017-03-03T13:33:05Z"
},
{
"checksumSHA1": "+XIOnTW0rv8Kr/amkXgMraNeUr4=",
"path": "github.com/ginuerzh/pht",