Skip to content

Commit ff6d3e1

Browse files
author
rui.zheng
committed
add gost library
1 parent b3461ea commit ff6d3e1

File tree

20 files changed

+2262
-1279
lines changed

20 files changed

+2262
-1279
lines changed

LICENSE

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

chain.go

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package gost
2+
3+
import (
4+
"crypto/tls"
5+
"errors"
6+
"github.com/golang/glog"
7+
"golang.org/x/net/http2"
8+
"io"
9+
"io/ioutil"
10+
"net"
11+
"net/http"
12+
"net/http/httputil"
13+
"net/url"
14+
"strings"
15+
)
16+
17+
// Proxy chain holds a list of proxy nodes
18+
type ProxyChain struct {
19+
nodes []ProxyNode
20+
lastNode *ProxyNode
21+
http2NodeIndex int
22+
http2Enabled bool
23+
http2Client *http.Client
24+
}
25+
26+
func NewProxyChain(nodes ...ProxyNode) *ProxyChain {
27+
chain := &ProxyChain{nodes: nodes, http2NodeIndex: -1}
28+
return chain
29+
}
30+
31+
func (c *ProxyChain) AddProxyNode(node ...ProxyNode) {
32+
c.nodes = append(c.nodes, node...)
33+
}
34+
35+
// Initialize proxy nodes, mainly check for http2 feature.
36+
// Should be called immediately when proxy nodes are ready.
37+
//
38+
// NOTE: http2 will not be enabled if not called.
39+
func (c *ProxyChain) Init() {
40+
length := len(c.nodes)
41+
if length == 0 {
42+
return
43+
}
44+
45+
c.lastNode = &c.nodes[length-1]
46+
47+
// http2 restrict: http2 will be enabled when at least one http2 proxy node present
48+
for i, node := range c.nodes {
49+
if node.Transport == "http2" {
50+
glog.V(LINFO).Infoln("http2 enabled")
51+
cfg := &tls.Config{
52+
InsecureSkipVerify: node.insecureSkipVerify(),
53+
ServerName: node.serverName,
54+
}
55+
c.initHttp2Client(node.Addr, cfg, c.nodes[:i]...)
56+
c.http2NodeIndex = i
57+
break // shortest chain for http2
58+
}
59+
}
60+
}
61+
62+
func (c *ProxyChain) initHttp2Client(addr string, config *tls.Config, nodes ...ProxyNode) {
63+
tr := http2.Transport{
64+
TLSClientConfig: config,
65+
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
66+
// replace the default dialer with our proxy chain.
67+
conn, err := c.dialWithNodes(addr, nodes...)
68+
if err != nil {
69+
return conn, err
70+
}
71+
return tls.Client(conn, cfg), nil
72+
},
73+
}
74+
c.http2Client = &http.Client{Transport: &tr}
75+
c.http2Enabled = true
76+
77+
}
78+
79+
func (c *ProxyChain) Http2Enabled() bool {
80+
return c.http2Enabled
81+
}
82+
83+
// Connect to addr through proxy chain
84+
func (c *ProxyChain) Dial(addr string) (net.Conn, error) {
85+
if !strings.Contains(addr, ":") {
86+
addr += ":80"
87+
}
88+
return c.dialWithNodes(addr, c.nodes...)
89+
}
90+
91+
func (c *ProxyChain) dialWithNodes(addr string, nodes ...ProxyNode) (conn net.Conn, err error) {
92+
if len(nodes) == 0 {
93+
return net.DialTimeout("tcp", addr, DialTimeout)
94+
}
95+
96+
var pc *ProxyConn
97+
98+
if c.Http2Enabled() {
99+
nodes = nodes[c.http2NodeIndex+1:]
100+
if len(nodes) == 0 {
101+
return c.http2Connect("http", addr)
102+
}
103+
}
104+
pc, err = c.travelNodes(nodes...)
105+
if err != nil {
106+
return
107+
}
108+
if err = pc.Connect(addr); err != nil {
109+
pc.Close()
110+
return
111+
}
112+
113+
return pc, nil
114+
}
115+
116+
func (c *ProxyChain) travelNodes(nodes ...ProxyNode) (conn *ProxyConn, err error) {
117+
defer func() {
118+
if err != nil && conn != nil {
119+
conn.Close()
120+
conn = nil
121+
}
122+
}()
123+
124+
var cc net.Conn
125+
node := nodes[0]
126+
127+
if c.Http2Enabled() {
128+
cc, err = c.http2Connect("http", node.Addr)
129+
} else {
130+
cc, err = net.DialTimeout("tcp", node.Addr, DialTimeout)
131+
}
132+
if err != nil {
133+
return
134+
}
135+
setKeepAlive(cc, KeepAliveTime)
136+
137+
pc := NewProxyConn(cc, node)
138+
if err = pc.Handshake(); err != nil {
139+
return
140+
}
141+
conn = pc
142+
for _, node := range nodes[1:] {
143+
if err = conn.Connect(node.Addr); err != nil {
144+
return
145+
}
146+
pc := NewProxyConn(conn, node)
147+
if err = pc.Handshake(); err != nil {
148+
return
149+
}
150+
conn = pc
151+
}
152+
return
153+
}
154+
155+
func (c *ProxyChain) http2Connect(protocol, addr string) (net.Conn, error) {
156+
if !c.Http2Enabled() {
157+
return nil, errors.New("http2 not enabled")
158+
}
159+
http2Node := c.nodes[c.http2NodeIndex]
160+
161+
pr, pw := io.Pipe()
162+
req := http.Request{
163+
Method: http.MethodConnect,
164+
URL: &url.URL{Scheme: "https", Host: http2Node.Addr},
165+
Header: make(http.Header),
166+
Proto: "HTTP/2.0",
167+
ProtoMajor: 2,
168+
ProtoMinor: 0,
169+
Body: ioutil.NopCloser(pr),
170+
Host: http2Node.Addr,
171+
ContentLength: -1,
172+
}
173+
req.Header.Set("gost-target", addr)
174+
if protocol != "" {
175+
req.Header.Set("gost-protocol", protocol)
176+
}
177+
178+
if glog.V(LDEBUG) {
179+
dump, _ := httputil.DumpRequest(&req, false)
180+
glog.Infoln(string(dump))
181+
}
182+
resp, err := c.http2Client.Do(&req)
183+
if err != nil {
184+
return nil, err
185+
}
186+
if resp.StatusCode != http.StatusOK {
187+
resp.Body.Close()
188+
return nil, errors.New(resp.Status)
189+
}
190+
conn := &Http2ClientConn{r: resp.Body, w: pw}
191+
conn.remoteAddr, _ = net.ResolveTCPAddr("tcp", addr)
192+
return conn, nil
193+
}

0 commit comments

Comments
 (0)