#69 KCP client now supports proxy chain

This commit is contained in:
rui.zheng 2017-01-19 15:34:22 +08:00
parent c1d7927257
commit 2dd7ace3ce
6 changed files with 66 additions and 110 deletions

View File

@ -38,25 +38,25 @@ scheme分为两部分: protocol+transport
protocol: 代理协议类型(http, socks5, shadowsocks), transport: 数据传输方式(ws, wss, tls, http2, quic, kcp), 二者可以任意组合,或单独使用: protocol: 代理协议类型(http, socks5, shadowsocks), transport: 数据传输方式(ws, wss, tls, http2, quic, kcp), 二者可以任意组合,或单独使用:
> http - 作为HTTP代理: http://:8080 > http - HTTP代理: http://:8080
> http+tls - 作为HTTPS代理(可能需要提供受信任的证书): http+tls://:443 > http+tls - HTTPS代理(可能需要提供受信任的证书): http+tls://:443或https://:443
> http2 - 作为HTTP2代理并向下兼容HTTPS代理: http2://:443 > http2 - HTTP2代理并向下兼容HTTPS代理: http2://:443
> socks - 作为标准SOCKS5代理(支持tls协商加密): socks://:1080 > socks - 标准SOCKS5代理(支持tls协商加密): socks://:1080
> socks+wss - 作为SOCKS5代理使用websocket传输数据: socks+wss://:1080 > socks+wss - SOCKS5代理使用websocket传输数据: socks+wss://:1080
> tls - 作为HTTPS/SOCKS5代理使用tls传输数据: tls://:443 > tls - HTTPS/SOCKS5代理使用tls传输数据: tls://:443
> ss - 作为Shadowsocks服务ss://aes-256-cfb:123456@:8338 > ss - Shadowsocks代理ss://aes-256-cfb:123456@:8338
> quic - 作为QUIC代理quic://:6121 > quic - QUIC代理quic://:6121
> kcp - 作为KCP代理kcp://:8388或kcp://aes:123456@:8388 > kcp - KCP代理kcp://:8388或kcp://aes:123456@:8388
> redirect - 作为透明代理redirect://:12345 > redirect - 透明代理redirect://:12345
#### 端口转发 #### 端口转发
@ -251,7 +251,6 @@ gost -L=kcp://:8388?c=/path/to/conf/file
``` ```
**注:** 客户端若要开启KCP转发当且仅当代理链不为空且首个代理节点(第一个-F参数)为kcp类型。 **注:** 客户端若要开启KCP转发当且仅当代理链不为空且首个代理节点(第一个-F参数)为kcp类型。
当KCP转发开启代理链中的其他代理节点将被忽略。
#### 透明代理 #### 透明代理
基于iptables的透明代理。 基于iptables的透明代理。

View File

@ -40,7 +40,7 @@ transport: data transmission mode (ws, wss, tls, http2, quic, kcp), may be used
> http - standard HTTP proxy: http://:8080 > http - standard HTTP proxy: http://:8080
> http+tls - standard HTTPS proxy (may need to provide a trusted certificate): http+tls://:443 > 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 > http2 - HTTP2 proxy and backwards-compatible with HTTPS proxy: http2://:443
@ -253,7 +253,6 @@ gost -L=kcp://:8388?c=/path/to/conf/file
``` ```
**NOTE:** KCP will be enabled if and only if the proxy chain is not empty and the first proxy node (the first -F parameter) is of type KCP. **NOTE:** KCP 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.
When KCP is enabled, other proxy nodes are ignored.
#### Transparent proxy #### Transparent proxy
Iptables-based transparent proxy Iptables-based transparent proxy

View File

@ -69,7 +69,7 @@ func (c *ProxyChain) SetNode(index int, node ProxyNode) {
} }
// Init initialize the proxy chain. // Init initialize the proxy chain.
// KCP will be enabled if the first proxy node is KCP proxy (transport == kcp), the remaining nodes are ignored. // 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. // 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. // NOTE: Should be called immediately when proxy nodes are ready.
@ -81,6 +81,26 @@ func (c *ProxyChain) Init() {
c.lastNode = &c.nodes[length-1] c.lastNode = &c.nodes[length-1]
// HTTP2 restrict: HTTP2 will be enabled when at least one HTTP2 proxy node is present.
for i, node := range c.nodes {
if node.Transport == "http2" {
glog.V(LINFO).Infoln("HTTP2 is enabled")
cfg := &tls.Config{
InsecureSkipVerify: node.insecureSkipVerify(),
ServerName: node.serverName,
}
c.http2NodeIndex = i
c.initHttp2Client(cfg, c.nodes[:i]...)
break // shortest chain for HTTP2
}
}
for i, node := range c.nodes {
if node.Transport == "kcp" && i > 0 {
glog.Fatal("KCP must be the first node in the proxy chain")
}
}
if c.nodes[0].Transport == "kcp" { if c.nodes[0].Transport == "kcp" {
glog.V(LINFO).Infoln("KCP is enabled") glog.V(LINFO).Infoln("KCP is enabled")
c.kcpEnabled = true c.kcpEnabled = true
@ -98,20 +118,6 @@ func (c *ProxyChain) Init() {
c.kcpConfig = config c.kcpConfig = config
return return
} }
// 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
}
}
} }
func (c *ProxyChain) KCPEnabled() bool { func (c *ProxyChain) KCPEnabled() bool {
@ -198,19 +204,6 @@ func (c *ProxyChain) GetConn() (net.Conn, error) {
return nil, ErrEmptyChain return nil, ErrEmptyChain
} }
if c.KCPEnabled() {
kcpConn, err := c.getKCPConn()
if err != nil {
return nil, err
}
pc := NewProxyConn(kcpConn, c.nodes[0])
if err := pc.Handshake(); err != nil {
pc.Close()
return nil, err
}
return pc, nil
}
if c.Http2Enabled() { if c.Http2Enabled() {
nodes = nodes[c.http2NodeIndex+1:] nodes = nodes[c.http2NodeIndex+1:]
if len(nodes) == 0 { if len(nodes) == 0 {
@ -243,23 +236,6 @@ func (c *ProxyChain) dialWithNodes(withHttp2 bool, addr string, nodes ...ProxyNo
return net.DialTimeout("tcp", addr, DialTimeout) return net.DialTimeout("tcp", addr, DialTimeout)
} }
if c.KCPEnabled() {
kcpConn, err := c.getKCPConn()
if err != nil {
return nil, err
}
pc := NewProxyConn(kcpConn, nodes[0])
if err := pc.Handshake(); err != nil {
pc.Close()
return nil, err
}
if err := pc.Connect(addr); err != nil {
pc.Close()
return nil, err
}
return pc, nil
}
if withHttp2 && c.Http2Enabled() { if withHttp2 && c.Http2Enabled() {
nodes = nodes[c.http2NodeIndex+1:] nodes = nodes[c.http2NodeIndex+1:]
if len(nodes) == 0 { if len(nodes) == 0 {
@ -291,6 +267,8 @@ func (c *ProxyChain) travelNodes(withHttp2 bool, nodes ...ProxyNode) (conn *Prox
if withHttp2 && c.Http2Enabled() { if withHttp2 && c.Http2Enabled() {
cc, err = c.http2Connect(node.Addr) cc, err = c.http2Connect(node.Addr)
} else if node.Transport == "kcp" {
cc, err = c.getKCPConn()
} else { } else {
cc, err = net.DialTimeout("tcp", node.Addr, DialTimeout) cc, err = net.DialTimeout("tcp", node.Addr, DialTimeout)
} }
@ -300,19 +278,20 @@ func (c *ProxyChain) travelNodes(withHttp2 bool, nodes ...ProxyNode) (conn *Prox
setKeepAlive(cc, KeepAliveTime) setKeepAlive(cc, KeepAliveTime)
pc := NewProxyConn(cc, node) pc := NewProxyConn(cc, node)
conn = pc
if err = pc.Handshake(); err != nil { if err = pc.Handshake(); err != nil {
return return
} }
conn = pc
for _, node := range nodes[1:] { for _, node := range nodes[1:] {
if err = conn.Connect(node.Addr); err != nil { if err = conn.Connect(node.Addr); err != nil {
return return
} }
pc := NewProxyConn(conn, node) pc := NewProxyConn(conn, node)
conn = pc
if err = pc.Handshake(); err != nil { if err = pc.Handshake(); err != nil {
return return
} }
conn = pc
} }
return return
} }

View File

@ -81,6 +81,26 @@ func (c *ProxyChain) Init() {
c.lastNode = &c.nodes[length-1] c.lastNode = &c.nodes[length-1]
// HTTP2 restrict: HTTP2 will be enabled when at least one HTTP2 proxy node is present.
for i, node := range c.nodes {
if node.Transport == "http2" {
glog.V(LINFO).Infoln("HTTP2 is enabled")
cfg := &tls.Config{
InsecureSkipVerify: node.insecureSkipVerify(),
ServerName: node.serverName,
}
c.http2NodeIndex = i
c.initHttp2Client(cfg, c.nodes[:i]...)
break // shortest chain for HTTP2
}
}
for i, node := range c.nodes {
if node.Transport == "kcp" && i > 0 {
glog.Fatal("KCP must be the first node in the proxy chain")
}
}
if c.nodes[0].Transport == "kcp" { if c.nodes[0].Transport == "kcp" {
glog.V(LINFO).Infoln("KCP is enabled") glog.V(LINFO).Infoln("KCP is enabled")
c.kcpEnabled = true c.kcpEnabled = true
@ -98,20 +118,6 @@ func (c *ProxyChain) Init() {
c.kcpConfig = config c.kcpConfig = config
return return
} }
// 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
}
}
} }
func (c *ProxyChain) KCPEnabled() bool { func (c *ProxyChain) KCPEnabled() bool {
@ -198,19 +204,6 @@ func (c *ProxyChain) GetConn() (net.Conn, error) {
return nil, ErrEmptyChain return nil, ErrEmptyChain
} }
if c.KCPEnabled() {
kcpConn, err := c.getKCPConn()
if err != nil {
return nil, err
}
pc := NewProxyConn(kcpConn, c.nodes[0])
if err := pc.Handshake(); err != nil {
pc.Close()
return nil, err
}
return pc, nil
}
if c.Http2Enabled() { if c.Http2Enabled() {
nodes = nodes[c.http2NodeIndex+1:] nodes = nodes[c.http2NodeIndex+1:]
if len(nodes) == 0 { if len(nodes) == 0 {
@ -243,23 +236,6 @@ func (c *ProxyChain) dialWithNodes(withHttp2 bool, addr string, nodes ...ProxyNo
return net.DialTimeout("tcp", addr, DialTimeout) return net.DialTimeout("tcp", addr, DialTimeout)
} }
if c.KCPEnabled() {
kcpConn, err := c.getKCPConn()
if err != nil {
return nil, err
}
pc := NewProxyConn(kcpConn, nodes[0])
if err := pc.Handshake(); err != nil {
pc.Close()
return nil, err
}
if err := pc.Connect(addr); err != nil {
pc.Close()
return nil, err
}
return pc, nil
}
if withHttp2 && c.Http2Enabled() { if withHttp2 && c.Http2Enabled() {
nodes = nodes[c.http2NodeIndex+1:] nodes = nodes[c.http2NodeIndex+1:]
if len(nodes) == 0 { if len(nodes) == 0 {
@ -291,6 +267,8 @@ func (c *ProxyChain) travelNodes(withHttp2 bool, nodes ...ProxyNode) (conn *Prox
if withHttp2 && c.Http2Enabled() { if withHttp2 && c.Http2Enabled() {
cc, err = c.http2Connect(node.Addr) cc, err = c.http2Connect(node.Addr)
} else if node.Transport == "kcp" {
cc, err = c.getKCPConn()
} else { } else {
cc, err = net.DialTimeout("tcp", node.Addr, DialTimeout) cc, err = net.DialTimeout("tcp", node.Addr, DialTimeout)
} }
@ -300,19 +278,20 @@ func (c *ProxyChain) travelNodes(withHttp2 bool, nodes ...ProxyNode) (conn *Prox
setKeepAlive(cc, KeepAliveTime) setKeepAlive(cc, KeepAliveTime)
pc := NewProxyConn(cc, node) pc := NewProxyConn(cc, node)
conn = pc
if err = pc.Handshake(); err != nil { if err = pc.Handshake(); err != nil {
return return
} }
conn = pc
for _, node := range nodes[1:] { for _, node := range nodes[1:] {
if err = conn.Connect(node.Addr); err != nil { if err = conn.Connect(node.Addr); err != nil {
return return
} }
pc := NewProxyConn(conn, node) pc := NewProxyConn(conn, node)
conn = pc
if err = pc.Handshake(); err != nil { if err = pc.Handshake(); err != nil {
return return
} }
conn = pc
} }
return return
} }

View File

@ -15,10 +15,10 @@
"revisionTime": "2017-01-19T05:34:58Z" "revisionTime": "2017-01-19T05:34:58Z"
}, },
{ {
"checksumSHA1": "g55EnuSESwa92RY7U+shNoPi900=", "checksumSHA1": "oZV/w2ONwoM0nMDddIJQYDAZOPg=",
"path": "github.com/ginuerzh/gost", "path": "github.com/ginuerzh/gost",
"revision": "0585749a819f33404f369650e74716cddf4ed3b7", "revision": "c1d79272573ada276e5a4234774ea4c2e2b4e9ef",
"revisionTime": "2017-01-19T05:40:53Z" "revisionTime": "2017-01-19T05:42:02Z"
}, },
{ {
"checksumSHA1": "URsJa4y/sUUw/STmbeYx9EKqaYE=", "checksumSHA1": "URsJa4y/sUUw/STmbeYx9EKqaYE=",

View File

@ -11,7 +11,7 @@ import (
) )
const ( const (
Version = "2.3-rc2" Version = "2.3-rc3"
) )
// Log level for glog // Log level for glog