From 962fd9df51054d4f0d4a7c5413da7faceb089c73 Mon Sep 17 00:00:00 2001 From: "rui.zheng" Date: Thu, 20 Jul 2017 23:36:09 +0800 Subject: [PATCH] update --- chain.go | 36 --------- client.go | 10 --- conn.go | 6 +- gost.go | 2 + gost/chain.go | 42 ++++++++++ gost/client.go | 167 ++++++++++++++++++++++++++++++++++++++++ gost/gost.go | 3 + gost/node.go | 6 ++ gost/server.go | 18 +++++ gost/ss.go | 50 ++++++++++++ handler.go | 10 ++- http.go | 184 +++++++++++++++++++++++++------------------- node.go | 8 -- options.go | 76 ------------------ server.go | 22 ++---- socks.go | 122 +++++++++++++++++++---------- socks/selector.go | 167 ---------------------------------------- ss.go | 3 +- tcp/client.go | 36 +++++---- tcp/node.go | 46 ----------- tcp/options.go | 49 ------------ tcp/options_test.go | 31 -------- tcp/server.go | 115 +++------------------------ 23 files changed, 524 insertions(+), 685 deletions(-) delete mode 100644 client.go create mode 100644 gost/chain.go create mode 100644 gost/client.go create mode 100644 gost/gost.go create mode 100644 gost/node.go create mode 100644 gost/server.go create mode 100644 gost/ss.go delete mode 100644 options.go delete mode 100644 socks/selector.go delete mode 100644 tcp/node.go delete mode 100644 tcp/options.go delete mode 100644 tcp/options_test.go diff --git a/chain.go b/chain.go index f096866..f4dc6dd 100644 --- a/chain.go +++ b/chain.go @@ -556,39 +556,3 @@ func (c *ProxyChain) getQuicConn(header http.Header) (net.Conn, error) { conn.remoteAddr, _ = net.ResolveUDPAddr("udp", quicNode.Addr) return conn, nil } - -type Chain struct { - nodes []Node -} - -func (c *Chain) Dial(addr string) (net.Conn, error) { - if len(c.nodes) == 0 { - return net.Dial("tcp", addr) - } - - nodes := c.nodes - conn, err := nodes[0].Client().Connect() - if err != nil { - return nil, err - } - - for i, node := range nodes { - if i == len(nodes)-1 { - break - } - - cn, err := node.Client().Dial(conn, nodes[i+1].Options().BaseOptions().Addr) - if err != nil { - conn.Close() - return nil, err - } - conn = cn - } - - cn, err := nodes[len(nodes)-1].Client().Dial(conn, addr) - if err != nil { - conn.Close() - return nil, err - } - return cn, nil -} diff --git a/client.go b/client.go deleted file mode 100644 index e43cfea..0000000 --- a/client.go +++ /dev/null @@ -1,10 +0,0 @@ -package gost - -import "net" - -// Client represents a node client -type Client interface { - Connect() (net.Conn, error) - Handshake(conn net.Conn) (net.Conn, error) - Dial(conn net.Conn, addr string) (net.Conn, error) -} diff --git a/conn.go b/conn.go index f4cb36c..b446425 100644 --- a/conn.go +++ b/conn.go @@ -112,7 +112,7 @@ func (c *ProxyConn) handshake() error { switch c.Node.Protocol { case "socks", "socks5": // socks5 handshake with auth and tls supported - selector := &clientSelector{ + selector := &ClientSelector{ methods: []uint8{ gosocks5.MethodNoAuth, gosocks5.MethodUserPass, @@ -121,12 +121,12 @@ func (c *ProxyConn) handshake() error { } if len(c.Node.Users) > 0 { - selector.user = 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{ + selector.TLSConfig = &tls.Config{ InsecureSkipVerify: c.Node.insecureSkipVerify(), ServerName: c.Node.serverName, } diff --git a/gost.go b/gost.go index cc8eec9..a5b27bd 100644 --- a/gost.go +++ b/gost.go @@ -31,6 +31,8 @@ const ( LDEBUG ) +var Debug bool + var ( KeepAliveTime = 180 * time.Second DialTimeout = 30 * time.Second diff --git a/gost/chain.go b/gost/chain.go new file mode 100644 index 0000000..a798cbc --- /dev/null +++ b/gost/chain.go @@ -0,0 +1,42 @@ +package gost + +import ( + "context" + "net" +) + +type Chain struct { + Nodes []Node +} + +func (c *Chain) Dial(ctx context.Context, addr string) (net.Conn, error) { + if len(c.Nodes) == 0 { + return net.Dial("tcp", addr) + } + + nodes := c.Nodes + conn, err := nodes[0].Client.Dial(ctx, nodes[0].Addr) + 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) + if err != nil { + conn.Close() + return nil, err + } + conn = cn + } + + cn, err := nodes[len(nodes)-1].Client.Connect(ctx, conn, addr) + if err != nil { + conn.Close() + return nil, err + } + return cn, nil +} diff --git a/gost/client.go b/gost/client.go new file mode 100644 index 0000000..8763f0a --- /dev/null +++ b/gost/client.go @@ -0,0 +1,167 @@ +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 +} + +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) { + protocol := c.Protocol + + 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) + if err != nil { + return nil, err + } + + 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" { + 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 + } + + 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) + } + } + + return conn, nil +} + +type Transport struct { + Dial func(ctx context.Context, addr string) (net.Conn, error) + TLSClientConfig *tls.Config +} diff --git a/gost/gost.go b/gost/gost.go new file mode 100644 index 0000000..12bf836 --- /dev/null +++ b/gost/gost.go @@ -0,0 +1,3 @@ +package gost + +var Debug bool diff --git a/gost/node.go b/gost/node.go new file mode 100644 index 0000000..db82c79 --- /dev/null +++ b/gost/node.go @@ -0,0 +1,6 @@ +package gost + +type Node struct { + Addr string + Client *Client +} diff --git a/gost/server.go b/gost/server.go new file mode 100644 index 0000000..3d9af9c --- /dev/null +++ b/gost/server.go @@ -0,0 +1,18 @@ +package gost + +import ( + "crypto/tls" + "net/url" +) + +type Server struct { + Addr string `opt:"addr"` // [host]:port + Protocol string `opt:"protocol"` // protocol: http/socks5/ss + TLSConfig *tls.Config + Chain *Chain + Users []url.Userinfo `opt:"user"` // authentication for proxy +} + +func (s *Server) Run() error { + return nil +} diff --git a/gost/ss.go b/gost/ss.go new file mode 100644 index 0000000..49b6483 --- /dev/null +++ b/gost/ss.go @@ -0,0 +1,50 @@ +package gost + +import ( + "net" + "time" +) + +// 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 net.Conn +} + +func ShadowConn(conn net.Conn) net.Conn { + return &shadowConn{conn: 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) +} diff --git a/handler.go b/handler.go index e832bdc..9662783 100644 --- a/handler.go +++ b/handler.go @@ -8,15 +8,21 @@ type Handler interface { Handle(net.Conn) } -type DefaultHandler struct { +type defaultHandler struct { server Server } -func (h *DefaultHandler) Handle(conn net.Conn) { +func DefaultHandler(server Server) Handler { + return &defaultHandler{server: server} +} + +func (h *defaultHandler) Handle(conn net.Conn) { var handler Handler switch h.server.Options().BaseOptions().Protocol { case "http": + handler = HTTPHandler(h.server) + case "socks", "socks5": case "ss": // shadowsocks handler = ShadowHandler(h.server) diff --git a/http.go b/http.go index dbe269b..7fdc638 100644 --- a/http.go +++ b/http.go @@ -12,6 +12,7 @@ import ( "time" "github.com/ginuerzh/pht" + "github.com/go-log/log" "github.com/golang/glog" "golang.org/x/net/http2" ) @@ -30,87 +31,7 @@ func NewHttpServer(conn net.Conn, base *ProxyServer) *HttpServer { // 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 - } - - if !s.Base.Node.Can("tcp", req.Host) { - glog.Errorf("Unauthorized to tcp connect to %s", req.Host) - 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) { @@ -153,6 +74,109 @@ func (s *HttpServer) forwardRequest(req *http.Request) { return } +type httpHandler struct { + server Server +} + +func HTTPHandler(server Server) Handler { + return &httpHandler{server: server} +} + +func (h *httpHandler) Handle(conn net.Conn) { + req, err := http.ReadRequest(bufio.NewReader(conn)) + if err != nil { + log.Log("[http]", err) + return + } + + log.Logf("[http] %s %s - %s %s", req.Method, conn.RemoteAddr(), req.Host, req.Proto) + + if Debug { + dump, _ := httputil.DumpRequest(req, false) + log.Logf(string(dump)) + } + + if req.Method == "PRI" && req.ProtoMajor == 2 { + log.Logf("[http] %s <- %s : Not an HTTP2 server", conn.RemoteAddr(), req.Host) + resp := "HTTP/1.1 400 Bad Request\r\n" + + "Proxy-Agent: gost/" + Version + "\r\n\r\n" + conn.Write([]byte(resp)) + return + } + + valid := false + u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization")) + users := h.server.Options().BaseOptions().Users + for _, user := range users { + username := user.Username() + password, _ := user.Password() + if (u == username && p == password) || + (u == username && password == "") || + (username == "" && p == password) { + valid = true + break + } + } + + if len(users) > 0 && !valid { + log.Logf("[http] %s <- %s : proxy authentication required", 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" + 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 + //} + + // if !s.Base.Node.Can("tcp", req.Host) { + // glog.Errorf("Unauthorized to tcp connect to %s", req.Host) + // return + // } + + cc, err := h.server.Chain().Dial(req.Host) + if err != nil { + log.Logf("[http] %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") + if Debug { + log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b)) + } + conn.Write(b) + return + } + defer cc.Close() + + if req.Method == http.MethodConnect { + b := []byte("HTTP/1.1 200 Connection established\r\n" + + "Proxy-Agent: gost/" + Version + "\r\n\r\n") + if Debug { + log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b)) + } + conn.Write(b) + } else { + req.Header.Del("Proxy-Connection") + // req.Header.Set("Connection", "Keep-Alive") + + if err = req.Write(cc); err != nil { + log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err) + return + } + } + + log.Logf("[http] %s <-> %s", conn.RemoteAddr(), req.Host) + Transport(conn, cc) + log.Logf("[http] %s >-< %s", conn.RemoteAddr(), req.Host) +} + type Http2Server struct { Base *ProxyServer Handler http.Handler diff --git a/node.go b/node.go index 26b4fdf..bc22387 100644 --- a/node.go +++ b/node.go @@ -211,11 +211,3 @@ func (node *ProxyNode) keyFile() string { func (node ProxyNode) String() string { return fmt.Sprintf("transport: %s, protocol: %s, addr: %s, whitelist: %v, blacklist: %v", node.Transport, node.Protocol, node.Addr, node.Whitelist, node.Blacklist) } - -// Node represents a proxy node -type Node interface { - Init(opts ...Option) error - Client() Client - Server() Server - Options() Options -} diff --git a/options.go b/options.go deleted file mode 100644 index f27b825..0000000 --- a/options.go +++ /dev/null @@ -1,76 +0,0 @@ -package gost - -import ( - "log" - "net/url" - "reflect" -) - -// Options holds options of node -type Options interface { - BaseOptions() *BaseOptions -} - -type Option func(Options) - -type BaseOptions struct { - Addr string `opt:"addr"` // [host]:port - Protocol string `opt:"protocol"` // protocol: http/socks5/ss - Transport string `opt:"transport"` // transport: ws/wss/tls/http2/tcp/udp/rtcp/rudp - Users []url.Userinfo `opt:"users"` // authentication for proxy -} - -func AddrOption(a string) Option { - return func(opts Options) { - opts.BaseOptions().Addr = a - } -} - -func ProtocolOption(p string) Option { - return func(opts Options) { - opts.BaseOptions().Protocol = p - } -} - -func TransportOption(t string) Option { - return func(opts Options) { - opts.BaseOptions().Transport = t - } -} - -func UsersOption(users ...url.Userinfo) Option { - return func(opts Options) { - opts.BaseOptions().Users = users - } -} - -func GetOption(i interface{}, opt string) interface{} { - ps := reflect.ValueOf(i) - if ps.Kind() != reflect.Ptr && ps.Kind() != reflect.Interface { - return nil - } - s := ps.Elem() - for n := 0; n < s.NumField(); n++ { - log.Println("tag:", s.Type().Field(n).Tag.Get("opt")) - if opt == s.Type().Field(n).Tag.Get("opt") && s.Field(n).CanInterface() { - // return s.Field(n).Interface() - } - } - return nil -} - -func SetOption(i interface{}, opt string, v interface{}) { - ps := reflect.ValueOf(i) - if ps.Kind() != reflect.Ptr || ps.Kind() != reflect.Interface { - return - } - s := ps.Elem() - - for n := 0; n < s.NumField(); n++ { - if opt == s.Type().Field(n).Tag.Get("opt") && - s.Field(n).IsValid() && s.Field(n).CanSet() { - s.Field(n).Set(reflect.ValueOf(v)) - return - } - } -} diff --git a/server.go b/server.go index 853ac34..99eda47 100644 --- a/server.go +++ b/server.go @@ -20,7 +20,7 @@ type ProxyServer struct { Node ProxyNode Chain *ProxyChain TLSConfig *tls.Config - selector *serverSelector + selector *ServerSelector cipher *ss.Cipher ota bool } @@ -65,7 +65,7 @@ func NewProxyServer(node ProxyNode, chain *ProxyChain) *ProxyServer { Node: node, Chain: chain, TLSConfig: config, - selector: &serverSelector{ // socks5 server selector + selector: &ServerSelector{ // socks5 server selector // methods that socks5 server supported methods: []uint8{ gosocks5.MethodNoAuth, @@ -73,8 +73,8 @@ func NewProxyServer(node ProxyNode, chain *ProxyChain) *ProxyServer { MethodTLS, MethodTLSAuth, }, - users: node.Users, - tlsConfig: config, + // Users: node.Users, + TLSConfig: config, }, cipher: cipher, ota: ota, @@ -193,9 +193,9 @@ func (s *ProxyServer) handleConn(conn net.Conn) { switch s.Node.Protocol { case "ss": // shadowsocks - server := NewShadowServer(ss.NewConn(conn, s.cipher.Copy()), s) - server.OTA = s.ota - server.Serve() + //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)) @@ -295,11 +295,3 @@ func (_ *ProxyServer) transport(conn1, conn2 net.Conn) (err error) { return } - -// Server represents a node server -type Server interface { - Chain() *Chain - SetChain(chain *Chain) - Options() Options - Run() error -} diff --git a/socks.go b/socks.go index 15faa86..31b1b9e 100644 --- a/socks.go +++ b/socks.go @@ -10,6 +10,7 @@ import ( "github.com/ginuerzh/gosocks4" "github.com/ginuerzh/gosocks5" + "github.com/go-log/log" "github.com/golang/glog" ) @@ -22,50 +23,56 @@ const ( CmdUdpTun uint8 = 0xF3 // extended method for udp over tcp ) -type clientSelector struct { +type ClientSelector struct { methods []uint8 - user *url.Userinfo - tlsConfig *tls.Config + User *url.Userinfo + TLSConfig *tls.Config } -func (selector *clientSelector) Methods() []uint8 { +func (selector *ClientSelector) Methods() []uint8 { return selector.methods } -func (selector *clientSelector) Select(methods ...uint8) (method uint8) { +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) { +func (selector *ClientSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) { switch method { case MethodTLS: - conn = tls.Client(conn, selector.tlsConfig) + conn = tls.Client(conn, selector.TLSConfig) case gosocks5.MethodUserPass, MethodTLSAuth: if method == MethodTLSAuth { - conn = tls.Client(conn, selector.tlsConfig) + conn = tls.Client(conn, selector.TLSConfig) } var username, password string - if selector.user != nil { - username = selector.user.Username() - password, _ = selector.user.Password() + 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) + log.Log("[socks5]", err) return nil, err } - glog.V(LDEBUG).Infoln(req) - + if Debug { + log.Log(req) + } resp, err := gosocks5.ReadUserPassResponse(conn) if err != nil { - glog.V(LWARNING).Infoln("socks5 auth:", err) + log.Log("[socks5]", err) return nil, err } - glog.V(LDEBUG).Infoln(resp) - + if Debug { + log.Log(resp) + } if resp.Status != gosocks5.Succeeded { return nil, gosocks5.ErrAuthFailure } @@ -76,19 +83,24 @@ func (selector *clientSelector) OnSelected(method uint8, conn net.Conn) (net.Con return conn, nil } -type serverSelector struct { +type ServerSelector struct { methods []uint8 - users []*url.Userinfo - tlsConfig *tls.Config + Users []url.Userinfo + TLSConfig *tls.Config } -func (selector *serverSelector) Methods() []uint8 { +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) +func (selector *ServerSelector) AddMethod(methods ...uint8) { + selector.methods = append(selector.methods, methods...) +} +func (selector *ServerSelector) Select(methods ...uint8) (method uint8) { + if Debug { + log.Logf("[socks5] %d %d %v", gosocks5.Ver5, len(methods), methods) + } method = gosocks5.MethodNoAuth for _, m := range methods { if m == MethodTLS { @@ -98,7 +110,7 @@ func (selector *serverSelector) Select(methods ...uint8) (method uint8) { } // when user/pass is set, auth is mandatory - if selector.users != nil { + if selector.Users != nil { if method == gosocks5.MethodNoAuth { method = gosocks5.MethodUserPass } @@ -110,27 +122,29 @@ func (selector *serverSelector) Select(methods ...uint8) (method uint8) { return } -func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) { - glog.V(LDEBUG).Infof("%d %d", gosocks5.Ver5, method) - +func (selector *ServerSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) { + if Debug { + log.Logf("[socks5] %d %d", gosocks5.Ver5, method) + } switch method { case MethodTLS: - conn = tls.Server(conn, selector.tlsConfig) + conn = tls.Server(conn, selector.TLSConfig) case gosocks5.MethodUserPass, MethodTLSAuth: if method == MethodTLSAuth { - conn = tls.Server(conn, selector.tlsConfig) + conn = tls.Server(conn, selector.TLSConfig) } req, err := gosocks5.ReadUserPassRequest(conn) if err != nil { - glog.V(LWARNING).Infoln("[socks5-auth]", err) + log.Log("[socks5]", err) return nil, err } - glog.V(LDEBUG).Infoln("[socks5]", req.String()) - + if Debug { + log.Log("[socks5]", req.String()) + } valid := false - for _, user := range selector.users { + for _, user := range selector.Users { username := user.Username() password, _ := user.Password() if (req.Username == username && req.Password == password) || @@ -140,25 +154,27 @@ func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Con break } } - if len(selector.users) > 0 && !valid { + 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) + log.Log("[socks5]", err) return nil, err } - glog.V(LDEBUG).Infoln("[socks5]", resp) - glog.V(LWARNING).Infoln("[socks5-auth] proxy authentication required") - + if Debug { + log.Log("[socks5]", resp) + } + log.Log("[socks5] 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) + log.Log("[socks5]", err) return nil, err } - glog.V(LDEBUG).Infoln(resp) - + if Debug { + log.Log("[socks5]", resp) + } case gosocks5.MethodNoAcceptable: return nil, gosocks5.ErrBadMethod } @@ -166,6 +182,30 @@ func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Con return conn, nil } +type socks5Handler struct { + server Server +} + +func (h *socks5Handler) Handle(conn net.Conn) { + selector := &ServerSelector{ + Users: h.server.Options().BaseOptions().Users, + TLSConfig: config, + } + // methods that socks5 server supported + selector.AddMethod( + gosocks5.MethodNoAuth, + gosocks5.MethodUserPass, + MethodTLS, + MethodTLSAuth, + ) + conn = gosocks5.ServerConn(conn, s.selector) + req, err := gosocks5.ReadRequest(conn) + if err != nil { + glog.V(LWARNING).Infoln("[socks5]", err) + return + } +} + type Socks5Server struct { conn net.Conn Base *ProxyServer diff --git a/socks/selector.go b/socks/selector.go deleted file mode 100644 index fd530e3..0000000 --- a/socks/selector.go +++ /dev/null @@ -1,167 +0,0 @@ -package socks - -import ( - "crypto/tls" - "net" - "net/url" - - "github.com/ginuerzh/gosocks5" - "github.com/golang/glog" -) - -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 { - glog.Infoln("socks5 auth:", err) - return nil, err - } - glog.Infoln(req) - - resp, err := gosocks5.ReadUserPassResponse(conn) - if err != nil { - glog.Infoln("socks5 auth:", err) - return nil, err - } - glog.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.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.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.Infoln("[socks5-auth]", err) - return nil, err - } - glog.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.Infoln("[socks5-auth]", err) - return nil, err - } - glog.Infoln("[socks5]", resp) - glog.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.Infoln("[socks5-auth]", err) - return nil, err - } - glog.Infoln(resp) - - case gosocks5.MethodNoAcceptable: - return nil, gosocks5.ErrBadMethod - } - - return conn, nil -} diff --git a/ss.go b/ss.go index 3b21ae9..e612b04 100644 --- a/ss.go +++ b/ss.go @@ -99,9 +99,8 @@ func (h *shadowHandler) Handle(conn net.Conn) { defer cc.Close() log.Logf("[ss] %s <-> %s", conn.RemoteAddr(), addr) - defer log.Logf("[ss] %s >-< %s", conn.RemoteAddr(), addr) - Transport(conn, cc) + log.Logf("[ss] %s >-< %s", conn.RemoteAddr(), addr) } const ( diff --git a/tcp/client.go b/tcp/client.go index b177fda..447a981 100644 --- a/tcp/client.go +++ b/tcp/client.go @@ -15,17 +15,27 @@ import ( "github.com/ginuerzh/gosocks4" "github.com/ginuerzh/gosocks5" "github.com/ginuerzh/gost" - "github.com/ginuerzh/gost/socks" + "github.com/ginuerzh/gost/client" "github.com/go-log/log" ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" ) type nodeClient struct { - options *nodeOptions + 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.BaseOptions().Addr) + return net.Dial("tcp", c.options.Addr) } func (c *nodeClient) Handshake(conn net.Conn) (net.Conn, error) { @@ -33,19 +43,19 @@ func (c *nodeClient) Handshake(conn net.Conn) (net.Conn, error) { } func (c *nodeClient) Dial(conn net.Conn, addr string) (net.Conn, error) { - if c.options.BaseOptions().Protocol == "socks5" { - selector := &socks.ClientSelector{ + if c.options.Protocol == "socks5" { + selector := &gost.ClientSelector{ TLSConfig: &tls.Config{ - InsecureSkipVerify: !c.options.secureVerify, - ServerName: c.options.serverName, + InsecureSkipVerify: !c.options.SecureVerify, + ServerName: c.options.ServerName, }, } selector.AddMethod( gosocks5.MethodNoAuth, gosocks5.MethodUserPass, - socks.MethodTLS, + gost.MethodTLS, ) - users := c.options.BaseOptions().Users + users := c.options.Users if len(users) > 0 { selector.User = &users[0] } @@ -61,7 +71,7 @@ func (c *nodeClient) Dial(conn net.Conn, addr string) (net.Conn, error) { } func (c *nodeClient) dial(conn net.Conn, addr string) (net.Conn, error) { - protocol := c.options.BaseOptions().Protocol + protocol := c.options.Protocol switch protocol { case "ss": // shadowsocks rawaddr, err := ss.RawAddr(addr) @@ -70,7 +80,7 @@ func (c *nodeClient) dial(conn net.Conn, addr string) (net.Conn, error) { } var method, password string - users := c.options.BaseOptions().Users + users := c.options.Users if len(users) > 0 { method = users[0].Username() password, _ = users[0].Password() @@ -87,7 +97,7 @@ func (c *nodeClient) dial(conn net.Conn, addr string) (net.Conn, error) { } conn = gost.ShadowConn(sc) - case "socks5": + case "socks", "socks5": host, port, err := net.SplitHostPort(addr) if err != nil { return nil, err @@ -157,7 +167,7 @@ func (c *nodeClient) dial(conn net.Conn, addr string) (net.Conn, error) { Header: make(http.Header), } req.Header.Set("Proxy-Connection", "keep-alive") - users := c.options.BaseOptions().Users + users := c.options.Users if len(users) > 0 { user := users[0] s := user.String() diff --git a/tcp/node.go b/tcp/node.go deleted file mode 100644 index 18cdf82..0000000 --- a/tcp/node.go +++ /dev/null @@ -1,46 +0,0 @@ -package tcp - -import ( - "github.com/ginuerzh/gost" -) - -type tcpNode struct { - options *nodeOptions - client *nodeClient - server *nodeServer -} - -// NewNode creates a tcpNode with options -func NewNode(opts ...gost.Option) gost.Node { - options := new(nodeOptions) - for _, opt := range opts { - opt(options) - } - node := &tcpNode{ - options: options, - client: &nodeClient{options: options}, - server: &nodeServer{options: options}, - } - - return node -} - -func (node *tcpNode) Init(opts ...gost.Option) error { - for _, opt := range opts { - opt(node.options) - } - - return nil -} - -func (node *tcpNode) Client() gost.Client { - return node.client -} - -func (node *tcpNode) Server() gost.Server { - return node.server -} - -func (node *tcpNode) Options() gost.Options { - return node.options -} diff --git a/tcp/options.go b/tcp/options.go deleted file mode 100644 index 045ffc1..0000000 --- a/tcp/options.go +++ /dev/null @@ -1,49 +0,0 @@ -package tcp - -import ( - "github.com/ginuerzh/gost" -) - -type nodeOptions struct { - base *gost.BaseOptions - certFile string `opt:"cert"` - keyFile string `opt:"key"` - serverName string `opt:"server_name"` - secureVerify bool `opt:"secure"` -} - -func (o *nodeOptions) BaseOptions() *gost.BaseOptions { - return o.base -} - -func (o *nodeOptions) ServerNameOption(n string) gost.Option { - return func(opts gost.Options) { - if o, ok := opts.(*nodeOptions); ok { - o.serverName = n - } - } -} - -func (o *nodeOptions) SecureVerifyOption(b bool) gost.Option { - return func(opts gost.Options) { - if o, ok := opts.(*nodeOptions); ok { - o.secureVerify = b - } - } -} - -func (o *nodeOptions) CertFileOption(f string) gost.Option { - return func(opts gost.Options) { - if o, ok := opts.(*nodeOptions); ok { - o.certFile = f - } - } -} - -func (o *nodeOptions) KeyFileOption(f string) gost.Option { - return func(opts gost.Options) { - if o, ok := opts.(*nodeOptions); ok { - o.keyFile = f - } - } -} diff --git a/tcp/options_test.go b/tcp/options_test.go deleted file mode 100644 index ab5bfd5..0000000 --- a/tcp/options_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package tcp - -import "testing" -import "net/url" -import "reflect" - -var tests = []struct { - Opt string - Value interface{} -}{ - {"addr", "localhost:8080"}, - {"protocol", "http"}, - {"transport", "tcp"}, - {"users", []url.Userinfo{*url.UserPassword("admin", "123456")}}, -} - -func TestOptions(t *testing.T) { - opts := new(tcpNodeOptions) - for _, test := range tests { - opts.Set(test.Opt, test.Value) - v := opts.Get(test.Opt) - if !reflect.DeepEqual(v, test.Value) { - t.Log("not equal:", test.Opt, v) - t.Fail() - } - } - t.Log("addr:", opts.Addr) - t.Log("protocol:", opts.Protocol) - t.Log("transport:", opts.Transport) - t.Log("users:", opts.Users) -} diff --git a/tcp/server.go b/tcp/server.go index acee09c..d9dc8f9 100644 --- a/tcp/server.go +++ b/tcp/server.go @@ -1,36 +1,28 @@ package tcp import ( - "bufio" "net" - "net/http" - "github.com/ginuerzh/gosocks4" - "github.com/ginuerzh/gosocks5" "github.com/ginuerzh/gost" - "github.com/ginuerzh/gost/ssocks" - "github.com/go-log/log" + "github.com/ginuerzh/gost/server" ) type nodeServer struct { - options *nodeOptions - chain *gost.Chain + options *server.Server } -func (s *nodeServer) Chain() *gost.Chain { - return s.chain +func (s *nodeServer) Init(opts ...server.Option) { + for _, opt := range opts { + opt(s.options) + } } -func (s *nodeServer) SetChain(chain *gost.Chain) { - s.chain = chain -} - -func (s *nodeServer) Options() gost.Options { +func (s *nodeServer) Options() *server.Options { return s.options } func (s *nodeServer) Run() error { - ln, err := net.Listen("tcp", s.options.BaseOptions().Addr) + ln, err := net.Listen("tcp", s.options.Addr) if err != nil { return err } @@ -43,96 +35,7 @@ func (s *nodeServer) Run() error { } go func(c net.Conn) { defer c.Close() - s.handleConn(c) + gost.DefaultHandler(s).Handle(conn) }(conn) } } - -func (s *nodeServer) handleConn(conn net.Conn) { - - switch s.options.BaseOptions().Protocol { - case "ss": // shadowsocks - server, err := ssocks.NewServer(conn, s) - if err != nil { - log.Log("[ss]", err) - } - server.Serve() - return - - case "http": - req, err := http.ReadRequest(bufio.NewReader(conn)) - if err != nil { - log.Log("[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 { - log.Log("[socks5]", err) - return - } - NewSocks5Server(conn, s).HandleRequest(req) - return - case "socks4", "socks4a": - req, err := gosocks4.ReadRequest(conn) - if err != nil { - log.Log("[socks4]", err) - return - } - NewSocks4Server(conn, s).HandleRequest(req) - return - } - - br := bufio.NewReader(conn) - b, err := br.Peek(1) - if err != nil { - log.Log(err) - return - } - - switch b[0] { - case gosocks4.Ver4: - req, err := gosocks4.ReadRequest(br) - if err != nil { - log.Log("[socks4]", err) - return - } - NewSocks4Server(conn, s).HandleRequest(req) - - case gosocks5.Ver5: - methods, err := gosocks5.ReadMethods(br) - if err != nil { - log.Log("[socks5]", err) - return - } - method := s.selector.Select(methods...) - if _, err := conn.Write([]byte{gosocks5.Ver5, method}); err != nil { - log.Log("[socks5] select:", err) - return - } - c, err := s.selector.OnSelected(method, conn) - if err != nil { - log.Log("[socks5] onselected:", err) - return - } - conn = c - - req, err := gosocks5.ReadRequest(conn) - if err != nil { - log.Log("[socks5] request:", err) - return - } - NewSocks5Server(conn, s).HandleRequest(req) - - default: // http - req, err := http.ReadRequest(br) - if err != nil { - log.Log("[http]", err) - return - } - NewHttpServer(conn, s).HandleRequest(req) - } -}