redesign client
This commit is contained in:
parent
962fd9df51
commit
289acfd058
@ -9,6 +9,12 @@ type Chain struct {
|
||||
Nodes []Node
|
||||
}
|
||||
|
||||
func NewChain(nodes ...Node) *Chain {
|
||||
return &Chain{
|
||||
Nodes: nodes,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Chain) Dial(ctx context.Context, addr string) (net.Conn, error) {
|
||||
if len(c.Nodes) == 0 {
|
||||
return net.Dial("tcp", addr)
|
||||
@ -20,23 +26,35 @@ func (c *Chain) Dial(ctx context.Context, addr string) (net.Conn, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn, err = nodes[0].Client.Handshake(ctx, conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, node := range nodes {
|
||||
if i == len(nodes)-1 {
|
||||
break
|
||||
}
|
||||
|
||||
cn, err := node.Client.Connect(ctx, conn, nodes[i+1].Addr)
|
||||
next := nodes[i+1]
|
||||
cc, err := node.Client.Connect(ctx, conn, next.Addr)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
conn = cn
|
||||
cc, err = next.Client.Handshake(ctx, cc)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cn, err := nodes[len(nodes)-1].Client.Connect(ctx, conn, addr)
|
||||
conn = cc
|
||||
}
|
||||
|
||||
cc, err := nodes[len(nodes)-1].Client.Connect(ctx, conn, addr)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return cn, nil
|
||||
return cc, nil
|
||||
}
|
||||
|
66
gost/cli/cli.go
Normal file
66
gost/cli/cli.go
Normal file
@ -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))
|
||||
}
|
169
gost/client.go
169
gost/client.go
@ -1,167 +1,58 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"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/go-log/log"
|
||||
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
Protocol string
|
||||
Transport *Transport
|
||||
User *url.Userinfo
|
||||
Connector Connector
|
||||
Transporter Transporter
|
||||
}
|
||||
|
||||
// DefaultClient is a standard HTTP proxy
|
||||
var DefaultClient = NewClient(HTTPConnector(nil), TCPTransporter())
|
||||
|
||||
func NewClient(c Connector, tr Transporter) *Client {
|
||||
return &Client{
|
||||
Connector: c,
|
||||
Transporter: tr,
|
||||
}
|
||||
}
|
||||
|
||||
// Dial connects to the target address
|
||||
func (c *Client) Dial(ctx context.Context, addr string) (net.Conn, error) {
|
||||
return c.Transport.Dial(ctx, addr)
|
||||
return net.Dial(c.Transporter.Network(), addr)
|
||||
}
|
||||
|
||||
func (c *Client) Handshake(ctx context.Context, conn net.Conn) (net.Conn, error) {
|
||||
return c.Transporter.Handshake(ctx, conn)
|
||||
}
|
||||
|
||||
func (c *Client) Connect(ctx context.Context, conn net.Conn, addr string) (net.Conn, error) {
|
||||
protocol := c.Protocol
|
||||
|
||||
switch protocol {
|
||||
case "ss": // shadowsocks
|
||||
rawaddr, err := ss.RawAddr(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return c.Connector.Connect(ctx, conn, addr)
|
||||
}
|
||||
|
||||
var method, password string
|
||||
if c.User != nil {
|
||||
method = c.User.Username()
|
||||
password, _ = c.User.Password()
|
||||
type Connector interface {
|
||||
Connect(ctx context.Context, conn net.Conn, addr string) (net.Conn, error)
|
||||
}
|
||||
|
||||
cipher, err := ss.NewCipher(method, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
type Transporter interface {
|
||||
Network() string
|
||||
Handshake(ctx context.Context, conn net.Conn) (net.Conn, error)
|
||||
}
|
||||
|
||||
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")
|
||||
type tcpTransporter struct {
|
||||
}
|
||||
|
||||
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")
|
||||
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
|
||||
func TCPTransporter() Transporter {
|
||||
return &tcpTransporter{}
|
||||
}
|
||||
|
||||
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("%d %s", resp.StatusCode, resp.Status)
|
||||
}
|
||||
func (tr *tcpTransporter) Network() string {
|
||||
return "tcp"
|
||||
}
|
||||
|
||||
func (tr *tcpTransporter) Handshake(ctx context.Context, conn net.Conn) (net.Conn, error) {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
type Transport struct {
|
||||
Dial func(ctx context.Context, addr string) (net.Conn, error)
|
||||
TLSClientConfig *tls.Config
|
||||
}
|
||||
|
@ -1,3 +1,11 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"github.com/go-log/log"
|
||||
)
|
||||
|
||||
var Debug bool
|
||||
|
||||
func init() {
|
||||
log.DefaultLogger = &logger{}
|
||||
}
|
||||
|
68
gost/http.go
Normal file
68
gost/http.go
Normal file
@ -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
|
||||
}
|
18
gost/log.go
Normal file
18
gost/log.go
Normal file
@ -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...)
|
||||
}
|
@ -2,5 +2,7 @@ package gost
|
||||
|
||||
type Node struct {
|
||||
Addr string
|
||||
Protocol string
|
||||
Transport string
|
||||
Client *Client
|
||||
}
|
||||
|
225
gost/socks.go
Normal file
225
gost/socks.go
Normal file
@ -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
|
||||
}
|
36
gost/ss.go
36
gost/ss.go
@ -1,8 +1,12 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
|
||||
)
|
||||
|
||||
// Due to in/out byte length is inconsistent of the shadowsocks.Conn.Write,
|
||||
@ -48,3 +52,35 @@ func (c *shadowConn) SetReadDeadline(t time.Time) error {
|
||||
func (c *shadowConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
type shadowConnector struct {
|
||||
Cipher *url.Userinfo
|
||||
}
|
||||
|
||||
func ShadowConnector(cipher *url.Userinfo) Connector {
|
||||
return &shadowConnector{Cipher: cipher}
|
||||
}
|
||||
|
||||
func (c *shadowConnector) Connect(ctx context.Context, conn net.Conn, addr string) (net.Conn, error) {
|
||||
rawaddr, err := ss.RawAddr(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var method, password string
|
||||
if c.Cipher != nil {
|
||||
method = c.Cipher.Username()
|
||||
password, _ = c.Cipher.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
|
||||
}
|
||||
return &shadowConn{conn: sc}, nil
|
||||
}
|
||||
|
23
gost/tls.go
Normal file
23
gost/tls.go
Normal file
@ -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
|
||||
}
|
202
tcp/client.go
202
tcp/client.go
@ -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
Block a user