mirror of https://github.com/ginuerzh/gost
11 changed files with 506 additions and 353 deletions
@ -0,0 +1,66 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"bufio" |
||||
|
"context" |
||||
|
"crypto/tls" |
||||
|
"log" |
||||
|
"net/http" |
||||
|
"net/http/httputil" |
||||
|
"net/url" |
||||
|
|
||||
|
"github.com/ginuerzh/gost/gost" |
||||
|
) |
||||
|
|
||||
|
func init() { |
||||
|
log.SetFlags(log.LstdFlags | log.Lshortfile) |
||||
|
gost.Debug = true |
||||
|
} |
||||
|
|
||||
|
func main() { |
||||
|
chain := gost.NewChain( |
||||
|
gost.Node{ |
||||
|
Addr: "127.0.0.1:1080", |
||||
|
Client: gost.NewClient( |
||||
|
gost.HTTPConnector(url.UserPassword("admin", "123456")), |
||||
|
gost.TCPTransporter(), |
||||
|
), |
||||
|
}, |
||||
|
gost.Node{ |
||||
|
Addr: "172.24.222.54:8338", |
||||
|
Client: gost.NewClient( |
||||
|
gost.ShadowConnector(url.UserPassword("chacha20", "123456")), |
||||
|
gost.TCPTransporter(), |
||||
|
), |
||||
|
}, |
||||
|
gost.Node{ |
||||
|
Addr: "172.24.222.54:8080", |
||||
|
Client: gost.NewClient( |
||||
|
gost.SOCKS5Connector(url.UserPassword("cmdsh", "cmdsh123456")), |
||||
|
gost.TCPTransporter(), |
||||
|
), |
||||
|
}, |
||||
|
) |
||||
|
conn, err := chain.Dial(context.Background(), "baidu.com:443") |
||||
|
if err != nil { |
||||
|
log.Fatal(err) |
||||
|
} |
||||
|
conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true}) |
||||
|
req, err := http.NewRequest(http.MethodGet, "https://www.baidu.com", nil) |
||||
|
if err != nil { |
||||
|
log.Fatal(err) |
||||
|
} |
||||
|
if err := req.Write(conn); err != nil { |
||||
|
log.Fatal(err) |
||||
|
} |
||||
|
resp, err := http.ReadResponse(bufio.NewReader(conn), req) |
||||
|
if err != nil { |
||||
|
log.Fatal(err) |
||||
|
} |
||||
|
defer resp.Body.Close() |
||||
|
|
||||
|
rb, _ := httputil.DumpRequest(req, true) |
||||
|
log.Println(string(rb)) |
||||
|
rb, _ = httputil.DumpResponse(resp, true) |
||||
|
log.Println(string(rb)) |
||||
|
} |
||||
@ -1,167 +1,58 @@ |
|||||
package gost |
package gost |
||||
|
|
||||
import ( |
import ( |
||||
"bufio" |
|
||||
"context" |
"context" |
||||
"crypto/tls" |
|
||||
"encoding/base64" |
|
||||
"errors" |
|
||||
"fmt" |
|
||||
"net" |
"net" |
||||
"net/http" |
|
||||
"net/http/httputil" |
|
||||
"net/url" |
|
||||
"strconv" |
|
||||
|
|
||||
"github.com/ginuerzh/gosocks4" |
|
||||
"github.com/ginuerzh/gosocks5" |
|
||||
"github.com/go-log/log" |
|
||||
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" |
|
||||
) |
) |
||||
|
|
||||
type Client struct { |
type Client struct { |
||||
Protocol string |
Connector Connector |
||||
Transport *Transport |
Transporter Transporter |
||||
User *url.Userinfo |
|
||||
} |
|
||||
|
|
||||
func (c *Client) Dial(ctx context.Context, addr string) (net.Conn, error) { |
|
||||
return c.Transport.Dial(ctx, addr) |
|
||||
} |
} |
||||
|
|
||||
func (c *Client) Connect(ctx context.Context, conn net.Conn, addr string) (net.Conn, error) { |
// DefaultClient is a standard HTTP proxy
|
||||
protocol := c.Protocol |
var DefaultClient = NewClient(HTTPConnector(nil), TCPTransporter()) |
||||
|
|
||||
switch protocol { |
|
||||
case "ss": // shadowsocks
|
|
||||
rawaddr, err := ss.RawAddr(addr) |
|
||||
if err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
|
|
||||
var method, password string |
|
||||
if c.User != nil { |
|
||||
method = c.User.Username() |
|
||||
password, _ = c.User.Password() |
|
||||
} |
|
||||
|
|
||||
cipher, err := ss.NewCipher(method, password) |
func NewClient(c Connector, tr Transporter) *Client { |
||||
if err != nil { |
return &Client{ |
||||
return nil, err |
Connector: c, |
||||
} |
Transporter: tr, |
||||
|
} |
||||
sc, err := ss.DialWithRawAddrConn(rawaddr, conn, cipher) |
} |
||||
if err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
conn = ShadowConn(sc) |
|
||||
|
|
||||
case "socks", "socks5": |
|
||||
host, port, err := net.SplitHostPort(addr) |
|
||||
if err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
p, _ := strconv.Atoi(port) |
|
||||
req := gosocks5.NewRequest(gosocks5.CmdConnect, &gosocks5.Addr{ |
|
||||
Type: gosocks5.AddrDomain, |
|
||||
Host: host, |
|
||||
Port: uint16(p), |
|
||||
}) |
|
||||
if err := req.Write(conn); err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
log.Log("[socks5]", req) |
|
||||
|
|
||||
reply, err := gosocks5.ReadReply(conn) |
|
||||
if err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
log.Log("[socks5]", reply) |
|
||||
if reply.Rep != gosocks5.Succeeded { |
|
||||
return nil, errors.New("Service unavailable") |
|
||||
} |
|
||||
|
|
||||
case "socks4", "socks4a": |
|
||||
atype := gosocks4.AddrDomain |
|
||||
host, port, err := net.SplitHostPort(addr) |
|
||||
if err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
p, _ := strconv.Atoi(port) |
|
||||
|
|
||||
if protocol == "socks4" { |
// Dial connects to the target address
|
||||
taddr, err := net.ResolveTCPAddr("tcp4", addr) |
func (c *Client) Dial(ctx context.Context, addr string) (net.Conn, error) { |
||||
if err != nil { |
return net.Dial(c.Transporter.Network(), addr) |
||||
return nil, 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(conn); err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
log.Logf("[%s] %s", protocol, req) |
|
||||
|
|
||||
reply, err := gosocks4.ReadReply(conn) |
func (c *Client) Handshake(ctx context.Context, conn net.Conn) (net.Conn, error) { |
||||
if err != nil { |
return c.Transporter.Handshake(ctx, conn) |
||||
return nil, err |
} |
||||
} |
|
||||
log.Logf("[%s] %s", protocol, reply) |
|
||||
|
|
||||
if reply.Code != gosocks4.Granted { |
func (c *Client) Connect(ctx context.Context, conn net.Conn, addr string) (net.Conn, error) { |
||||
return nil, fmt.Errorf("%s: code=%d", protocol, reply.Code) |
return c.Connector.Connect(ctx, conn, addr) |
||||
} |
} |
||||
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 c.User != nil { |
|
||||
s := c.User.String() |
|
||||
if _, set := c.User.Password(); !set { |
|
||||
s += ":" |
|
||||
} |
|
||||
req.Header.Set("Proxy-Authorization", |
|
||||
"Basic "+base64.StdEncoding.EncodeToString([]byte(s))) |
|
||||
} |
|
||||
if err := req.Write(conn); err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
|
|
||||
if Debug { |
type Connector interface { |
||||
dump, _ := httputil.DumpRequest(req, false) |
Connect(ctx context.Context, conn net.Conn, addr string) (net.Conn, error) |
||||
log.Log(string(dump)) |
} |
||||
} |
|
||||
|
|
||||
resp, err := http.ReadResponse(bufio.NewReader(conn), req) |
type Transporter interface { |
||||
if err != nil { |
Network() string |
||||
return nil, err |
Handshake(ctx context.Context, conn net.Conn) (net.Conn, error) |
||||
} |
} |
||||
|
|
||||
if Debug { |
type tcpTransporter struct { |
||||
dump, _ := httputil.DumpResponse(resp, false) |
} |
||||
log.Log(string(dump)) |
|
||||
} |
|
||||
|
|
||||
if resp.StatusCode != http.StatusOK { |
func TCPTransporter() Transporter { |
||||
return nil, fmt.Errorf("%d %s", resp.StatusCode, resp.Status) |
return &tcpTransporter{} |
||||
} |
} |
||||
} |
|
||||
|
|
||||
return conn, nil |
func (tr *tcpTransporter) Network() string { |
||||
|
return "tcp" |
||||
} |
} |
||||
|
|
||||
type Transport struct { |
func (tr *tcpTransporter) Handshake(ctx context.Context, conn net.Conn) (net.Conn, error) { |
||||
Dial func(ctx context.Context, addr string) (net.Conn, error) |
return conn, nil |
||||
TLSClientConfig *tls.Config |
|
||||
} |
} |
||||
|
|||||
@ -1,3 +1,11 @@ |
|||||
package gost |
package gost |
||||
|
|
||||
|
import ( |
||||
|
"github.com/go-log/log" |
||||
|
) |
||||
|
|
||||
var Debug bool |
var Debug bool |
||||
|
|
||||
|
func init() { |
||||
|
log.DefaultLogger = &logger{} |
||||
|
} |
||||
|
|||||
@ -0,0 +1,68 @@ |
|||||
|
package gost |
||||
|
|
||||
|
import ( |
||||
|
"bufio" |
||||
|
"context" |
||||
|
"encoding/base64" |
||||
|
"fmt" |
||||
|
"net" |
||||
|
"net/http" |
||||
|
"net/http/httputil" |
||||
|
"net/url" |
||||
|
|
||||
|
"github.com/go-log/log" |
||||
|
) |
||||
|
|
||||
|
type httpConnector struct { |
||||
|
User *url.Userinfo |
||||
|
} |
||||
|
|
||||
|
func HTTPConnector(user *url.Userinfo) Connector { |
||||
|
return &httpConnector{User: user} |
||||
|
} |
||||
|
|
||||
|
func (c *httpConnector) Connect(ctx context.Context, conn net.Conn, addr string) (net.Conn, error) { |
||||
|
req := &http.Request{ |
||||
|
Method: http.MethodConnect, |
||||
|
URL: &url.URL{Host: addr}, |
||||
|
Host: addr, |
||||
|
ProtoMajor: 1, |
||||
|
ProtoMinor: 1, |
||||
|
Header: make(http.Header), |
||||
|
} |
||||
|
req.Header.Set("Proxy-Connection", "keep-alive") |
||||
|
|
||||
|
if c.User != nil { |
||||
|
s := c.User.String() |
||||
|
if _, set := c.User.Password(); !set { |
||||
|
s += ":" |
||||
|
} |
||||
|
req.Header.Set("Proxy-Authorization", |
||||
|
"Basic "+base64.StdEncoding.EncodeToString([]byte(s))) |
||||
|
} |
||||
|
|
||||
|
if err := req.Write(conn); err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
if Debug { |
||||
|
dump, _ := httputil.DumpRequest(req, false) |
||||
|
log.Log(string(dump)) |
||||
|
} |
||||
|
|
||||
|
resp, err := http.ReadResponse(bufio.NewReader(conn), req) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
if Debug { |
||||
|
dump, _ := httputil.DumpResponse(resp, false) |
||||
|
log.Log(string(dump)) |
||||
|
} |
||||
|
|
||||
|
if resp.StatusCode != http.StatusOK { |
||||
|
return nil, fmt.Errorf("%s", resp.Status) |
||||
|
} |
||||
|
|
||||
|
return conn, nil |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
package gost |
||||
|
|
||||
|
import "log" |
||||
|
|
||||
|
func init() { |
||||
|
log.SetFlags(log.LstdFlags | log.Lshortfile) |
||||
|
} |
||||
|
|
||||
|
type logger struct { |
||||
|
} |
||||
|
|
||||
|
func (l *logger) Log(v ...interface{}) { |
||||
|
log.Println(v...) |
||||
|
} |
||||
|
|
||||
|
func (l *logger) Logf(format string, v ...interface{}) { |
||||
|
log.Printf(format, v...) |
||||
|
} |
||||
@ -1,6 +1,8 @@ |
|||||
package gost |
package gost |
||||
|
|
||||
type Node struct { |
type Node struct { |
||||
Addr string |
Addr string |
||||
Client *Client |
Protocol string |
||||
|
Transport string |
||||
|
Client *Client |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,225 @@ |
|||||
|
package gost |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
"crypto/tls" |
||||
|
"errors" |
||||
|
"fmt" |
||||
|
"net" |
||||
|
"net/url" |
||||
|
"strconv" |
||||
|
|
||||
|
"github.com/ginuerzh/gosocks4" |
||||
|
"github.com/ginuerzh/gosocks5" |
||||
|
"github.com/go-log/log" |
||||
|
) |
||||
|
|
||||
|
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) AddMethod(methods ...uint8) { |
||||
|
selector.methods = append(selector.methods, 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 { |
||||
|
log.Log("[socks5]", err) |
||||
|
return nil, err |
||||
|
} |
||||
|
if Debug { |
||||
|
log.Log("[socks5]", req) |
||||
|
} |
||||
|
resp, err := gosocks5.ReadUserPassResponse(conn) |
||||
|
if err != nil { |
||||
|
log.Log("[socks5]", err) |
||||
|
return nil, err |
||||
|
} |
||||
|
if Debug { |
||||
|
log.Log("[socks5]", resp) |
||||
|
} |
||||
|
if resp.Status != gosocks5.Succeeded { |
||||
|
return nil, gosocks5.ErrAuthFailure |
||||
|
} |
||||
|
case gosocks5.MethodNoAcceptable: |
||||
|
return nil, gosocks5.ErrBadMethod |
||||
|
} |
||||
|
|
||||
|
return conn, nil |
||||
|
} |
||||
|
|
||||
|
type socks5Connector struct { |
||||
|
User *url.Userinfo |
||||
|
} |
||||
|
|
||||
|
func SOCKS5Connector(user *url.Userinfo) Connector { |
||||
|
return &socks5Connector{User: user} |
||||
|
} |
||||
|
|
||||
|
func (c *socks5Connector) Connect(ctx context.Context, conn net.Conn, addr string) (net.Conn, error) { |
||||
|
selector := &ClientSelector{ |
||||
|
TLSConfig: &tls.Config{InsecureSkipVerify: true}, |
||||
|
User: c.User, |
||||
|
} |
||||
|
selector.AddMethod( |
||||
|
gosocks5.MethodNoAuth, |
||||
|
gosocks5.MethodUserPass, |
||||
|
MethodTLS, |
||||
|
) |
||||
|
|
||||
|
cc := gosocks5.ClientConn(conn, selector) |
||||
|
if err := cc.Handleshake(); err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
conn = cc |
||||
|
|
||||
|
host, port, err := net.SplitHostPort(addr) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
p, _ := strconv.Atoi(port) |
||||
|
req := gosocks5.NewRequest(gosocks5.CmdConnect, &gosocks5.Addr{ |
||||
|
Type: gosocks5.AddrDomain, |
||||
|
Host: host, |
||||
|
Port: uint16(p), |
||||
|
}) |
||||
|
if err := req.Write(conn); err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
if Debug { |
||||
|
log.Log("[socks5]", req) |
||||
|
} |
||||
|
|
||||
|
reply, err := gosocks5.ReadReply(conn) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
if Debug { |
||||
|
log.Log("[socks5]", reply) |
||||
|
} |
||||
|
|
||||
|
if reply.Rep != gosocks5.Succeeded { |
||||
|
return nil, errors.New("Service unavailable") |
||||
|
} |
||||
|
|
||||
|
return conn, nil |
||||
|
} |
||||
|
|
||||
|
type socks4Connector struct{} |
||||
|
|
||||
|
func SOCKS4Connector() Connector { |
||||
|
return &socks4Connector{} |
||||
|
} |
||||
|
|
||||
|
func (c *socks4Connector) Connect(ctx context.Context, conn net.Conn, addr string) (net.Conn, error) { |
||||
|
taddr, err := net.ResolveTCPAddr("tcp4", addr) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
req := gosocks4.NewRequest(gosocks4.CmdConnect, |
||||
|
&gosocks4.Addr{ |
||||
|
Type: gosocks4.AddrIPv4, |
||||
|
Host: taddr.IP.String(), |
||||
|
Port: uint16(taddr.Port), |
||||
|
}, nil, |
||||
|
) |
||||
|
if err := req.Write(conn); err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
if Debug { |
||||
|
log.Logf("[socks4] %s", req) |
||||
|
} |
||||
|
|
||||
|
reply, err := gosocks4.ReadReply(conn) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
if Debug { |
||||
|
log.Logf("[socks4] %s", reply) |
||||
|
} |
||||
|
|
||||
|
if reply.Code != gosocks4.Granted { |
||||
|
return nil, fmt.Errorf("[socks4] %d", reply.Code) |
||||
|
} |
||||
|
|
||||
|
return conn, nil |
||||
|
} |
||||
|
|
||||
|
type socks4aConnector struct{} |
||||
|
|
||||
|
func SOCKS4AConnector() Connector { |
||||
|
return &socks4aConnector{} |
||||
|
} |
||||
|
|
||||
|
func (c *socks4aConnector) Connect(ctx context.Context, conn net.Conn, addr string) (net.Conn, error) { |
||||
|
host, port, err := net.SplitHostPort(addr) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
p, _ := strconv.Atoi(port) |
||||
|
|
||||
|
req := gosocks4.NewRequest(gosocks4.CmdConnect, |
||||
|
&gosocks4.Addr{Type: gosocks4.AddrDomain, Host: host, Port: uint16(p)}, nil) |
||||
|
if err := req.Write(conn); err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
if Debug { |
||||
|
log.Logf("[socks4] %s", req) |
||||
|
} |
||||
|
|
||||
|
reply, err := gosocks4.ReadReply(conn) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
if Debug { |
||||
|
log.Logf("[socks4] %s", reply) |
||||
|
} |
||||
|
|
||||
|
if reply.Code != gosocks4.Granted { |
||||
|
return nil, fmt.Errorf("[socks4] %d", reply.Code) |
||||
|
} |
||||
|
|
||||
|
return conn, nil |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
package gost |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
"crypto/tls" |
||||
|
"net" |
||||
|
) |
||||
|
|
||||
|
type tlsTransporter struct { |
||||
|
TLSClientConfig *tls.Config |
||||
|
} |
||||
|
|
||||
|
func TLSTransporter(cfg *tls.Config) Transporter { |
||||
|
return &tlsTransporter{TLSClientConfig: cfg} |
||||
|
} |
||||
|
|
||||
|
func (tr *tlsTransporter) Network() string { |
||||
|
return "tcp" |
||||
|
} |
||||
|
|
||||
|
func (tr *tlsTransporter) Handshake(ctx context.Context, conn net.Conn) (net.Conn, error) { |
||||
|
return tls.Client(conn, tr.TLSClientConfig), nil |
||||
|
} |
||||
@ -1,202 +0,0 @@ |
|||||
package tcp |
|
||||
|
|
||||
import ( |
|
||||
"bufio" |
|
||||
"crypto/tls" |
|
||||
"encoding/base64" |
|
||||
"errors" |
|
||||
"fmt" |
|
||||
"net" |
|
||||
"net/http" |
|
||||
"net/http/httputil" |
|
||||
"net/url" |
|
||||
"strconv" |
|
||||
|
|
||||
"github.com/ginuerzh/gosocks4" |
|
||||
"github.com/ginuerzh/gosocks5" |
|
||||
"github.com/ginuerzh/gost" |
|
||||
"github.com/ginuerzh/gost/client" |
|
||||
"github.com/go-log/log" |
|
||||
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" |
|
||||
) |
|
||||
|
|
||||
type nodeClient struct { |
|
||||
options *client.Options |
|
||||
} |
|
||||
|
|
||||
func (c *nodeClient) Init(opts ...client.Option) { |
|
||||
for _, opt := range opts { |
|
||||
opt(c.options) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (c *nodeClient) Options() *Options { |
|
||||
return c.options |
|
||||
} |
|
||||
|
|
||||
func (c *nodeClient) Connect() (net.Conn, error) { |
|
||||
return net.Dial("tcp", c.options.Addr) |
|
||||
} |
|
||||
|
|
||||
func (c *nodeClient) Handshake(conn net.Conn) (net.Conn, error) { |
|
||||
return conn, nil |
|
||||
} |
|
||||
|
|
||||
func (c *nodeClient) Dial(conn net.Conn, addr string) (net.Conn, error) { |
|
||||
if c.options.Protocol == "socks5" { |
|
||||
selector := &gost.ClientSelector{ |
|
||||
TLSConfig: &tls.Config{ |
|
||||
InsecureSkipVerify: !c.options.SecureVerify, |
|
||||
ServerName: c.options.ServerName, |
|
||||
}, |
|
||||
} |
|
||||
selector.AddMethod( |
|
||||
gosocks5.MethodNoAuth, |
|
||||
gosocks5.MethodUserPass, |
|
||||
gost.MethodTLS, |
|
||||
) |
|
||||
users := c.options.Users |
|
||||
if len(users) > 0 { |
|
||||
selector.User = &users[0] |
|
||||
} |
|
||||
|
|
||||
cc := gosocks5.ClientConn(conn, selector) |
|
||||
if err := cc.Handleshake(); err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
conn = cc |
|
||||
} |
|
||||
|
|
||||
return c.dial(conn, addr) |
|
||||
} |
|
||||
|
|
||||
func (c *nodeClient) dial(conn net.Conn, addr string) (net.Conn, error) { |
|
||||
protocol := c.options.Protocol |
|
||||
switch protocol { |
|
||||
case "ss": // shadowsocks
|
|
||||
rawaddr, err := ss.RawAddr(addr) |
|
||||
if err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
|
|
||||
var method, password string |
|
||||
users := c.options.Users |
|
||||
if len(users) > 0 { |
|
||||
method = users[0].Username() |
|
||||
password, _ = users[0].Password() |
|
||||
} |
|
||||
|
|
||||
cipher, err := ss.NewCipher(method, password) |
|
||||
if err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
|
|
||||
sc, err := ss.DialWithRawAddrConn(rawaddr, conn, cipher) |
|
||||
if err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
conn = gost.ShadowConn(sc) |
|
||||
|
|
||||
case "socks", "socks5": |
|
||||
host, port, err := net.SplitHostPort(addr) |
|
||||
if err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
p, _ := strconv.Atoi(port) |
|
||||
req := gosocks5.NewRequest(gosocks5.CmdConnect, &gosocks5.Addr{ |
|
||||
Type: gosocks5.AddrDomain, |
|
||||
Host: host, |
|
||||
Port: uint16(p), |
|
||||
}) |
|
||||
if err := req.Write(conn); err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
log.Log("[socks5]", req) |
|
||||
|
|
||||
reply, err := gosocks5.ReadReply(conn) |
|
||||
if err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
log.Log("[socks5]", reply) |
|
||||
if reply.Rep != gosocks5.Succeeded { |
|
||||
return nil, errors.New("Service unavailable") |
|
||||
} |
|
||||
|
|
||||
case "socks4", "socks4a": |
|
||||
atype := gosocks4.AddrDomain |
|
||||
host, port, err := net.SplitHostPort(addr) |
|
||||
if err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
p, _ := strconv.Atoi(port) |
|
||||
|
|
||||
if protocol == "socks4" { |
|
||||
taddr, err := net.ResolveTCPAddr("tcp4", addr) |
|
||||
if err != nil { |
|
||||
return nil, 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(conn); err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
log.Logf("[%s] %s", protocol, req) |
|
||||
|
|
||||
reply, err := gosocks4.ReadReply(conn) |
|
||||
if err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
log.Logf("[%s] %s", protocol, reply) |
|
||||
|
|
||||
if reply.Code != gosocks4.Granted { |
|
||||
return nil, fmt.Errorf("%s: code=%d", 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") |
|
||||
users := c.options.Users |
|
||||
if len(users) > 0 { |
|
||||
user := 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(conn); err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
//if glog.V(LDEBUG) {
|
|
||||
dump, _ := httputil.DumpRequest(req, false) |
|
||||
log.Log(string(dump)) |
|
||||
//}
|
|
||||
|
|
||||
resp, err := http.ReadResponse(bufio.NewReader(conn), req) |
|
||||
if err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
//if glog.V(LDEBUG) {
|
|
||||
dump, _ = httputil.DumpResponse(resp, false) |
|
||||
log.Log(string(dump)) |
|
||||
//}
|
|
||||
if resp.StatusCode != http.StatusOK { |
|
||||
return nil, fmt.Errorf("%d %s", resp.StatusCode, resp.Status) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return conn, nil |
|
||||
} |
|
||||
Loading…
Reference in new issue