From 5baaf4207eb62509f005eb2f4517a6221c04d8cd Mon Sep 17 00:00:00 2001 From: zhengrui Date: Thu, 6 Dec 2018 20:46:39 +0800 Subject: [PATCH] add more examples --- client/client.go | 42 ++++++++++++++++++++++++++++++++++++------ client/selector.go | 20 ++++++++++++++++---- examples/client.go | 41 +++++++++++++++++++++++++++++++++++++++++ server/handler.go | 2 +- server/selector.go | 21 +++++++++++++++++---- socks5.go | 27 +++++++++++++++++++++++++++ 6 files changed, 138 insertions(+), 15 deletions(-) create mode 100644 examples/client.go diff --git a/client/client.go b/client/client.go index 91247ad..18d2557 100644 --- a/client/client.go +++ b/client/client.go @@ -7,18 +7,48 @@ import ( "github.com/ginuerzh/gosocks5" ) -type Client struct { - selector gosocks5.Selector -} +// Dial connects to the SOCKS5 server. +func Dial(addr string, options ...DialOption) (net.Conn, error) { + opts := &DialOptions{} + for _, o := range options { + o(opts) + } -func (c *Client) Dial(addr string, options ...DialOption) (net.Conn, error) { - return nil, nil + conn, err := net.DialTimeout("tcp", addr, opts.Timeout) + if err != nil { + return nil, err + } + + selector := opts.Selector + if selector == nil { + selector = DefaultSelector + } + + cc := gosocks5.ClientConn(conn, selector) + if err := cc.Handleshake(); err != nil { + conn.Close() + return nil, err + } + return cc, nil } // DialOptions describes the options for Transporter.Dial. type DialOptions struct { - Timeout time.Duration + Selector gosocks5.Selector + Timeout time.Duration } // DialOption allows a common way to set dial options. type DialOption func(opts *DialOptions) + +func SelectorDialOption(selector gosocks5.Selector) DialOption { + return func(opts *DialOptions) { + opts.Selector = selector + } +} + +func TimeoutDialOption(timeout time.Duration) DialOption { + return func(opts *DialOptions) { + opts.Timeout = timeout + } +} diff --git a/client/selector.go b/client/selector.go index cab92c7..fe91fab 100644 --- a/client/selector.go +++ b/client/selector.go @@ -7,9 +7,21 @@ import ( "github.com/ginuerzh/gosocks5" ) +var ( + // DefaultSelector is the default client selector. + DefaultSelector gosocks5.Selector = &clientSelector{} +) + type clientSelector struct { methods []uint8 - User *url.Userinfo + user *url.Userinfo +} + +func NewClientSelector(user *url.Userinfo, methods ...uint8) gosocks5.Selector { + return &clientSelector{ + methods: methods, + user: user, + } } func (selector *clientSelector) Methods() []uint8 { @@ -28,9 +40,9 @@ func (selector *clientSelector) OnSelected(method uint8, conn net.Conn) (net.Con switch method { case gosocks5.MethodUserPass: 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) diff --git a/examples/client.go b/examples/client.go new file mode 100644 index 0000000..1c3732f --- /dev/null +++ b/examples/client.go @@ -0,0 +1,41 @@ +package main + +import ( + "flag" + "log" + + "github.com/ginuerzh/gosocks5" + + "github.com/ginuerzh/gosocks5/client" +) + +var ( + server string +) + +func init() { + flag.StringVar(&server, "p", "", "SOCKS5 server address") + flag.Parse() +} + +func main() { + addr, err := gosocks5.NewAddr(flag.Arg(0)) + if err != nil { + log.Fatal(err) + } + + conn, err := client.Dial(server) + if err != nil { + log.Fatal(err) + } + defer conn.Close() + + if err := gosocks5.NewRequest(gosocks5.CmdConnect, addr).Write(conn); err != nil { + log.Fatal(err) + } + reply, err := gosocks5.ReadReply(conn) + if err != nil { + log.Fatal(err) + } + log.Println("reply:", reply) +} diff --git a/server/handler.go b/server/handler.go index 798bc37..704a1b5 100644 --- a/server/handler.go +++ b/server/handler.go @@ -17,7 +17,7 @@ var ( func init() { DefaultHandler = &serverHandler{ - selector: &serverSelector{}, + selector: DefaultSelector, } } diff --git a/server/selector.go b/server/selector.go index 149fbe4..9bd766b 100644 --- a/server/selector.go +++ b/server/selector.go @@ -7,9 +7,22 @@ import ( "github.com/ginuerzh/gosocks5" ) +var ( + // DefaultSelector is the default server selector. + // It only supports No-Auth Method. + DefaultSelector gosocks5.Selector = &serverSelector{} +) + type serverSelector struct { methods []uint8 - Users []*url.Userinfo + users []*url.Userinfo +} + +func NewServerSelector(users []*url.Userinfo, methods ...uint8) gosocks5.Selector { + return &serverSelector{ + methods: methods, + users: users, + } } func (selector *serverSelector) Methods() []uint8 { @@ -24,7 +37,7 @@ func (selector *serverSelector) Select(methods ...uint8) (method uint8) { method = gosocks5.MethodNoAuth // when user/pass is set, auth is mandatory - if len(selector.Users) > 0 { + if len(selector.users) > 0 { method = gosocks5.MethodUserPass } @@ -40,7 +53,7 @@ func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Con } 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) || @@ -50,7 +63,7 @@ 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 { return nil, err diff --git a/socks5.go b/socks5.go index 2ee533a..1c16054 100644 --- a/socks5.go +++ b/socks5.go @@ -273,6 +273,33 @@ type Addr struct { Port uint16 } +func NewAddr(sa string) (addr *Addr, err error) { + host, sport, err := net.SplitHostPort(sa) + if err != nil { + return nil, err + } + port, err := strconv.Atoi(sport) + if err != nil { + return nil, err + } + + addr = &Addr{ + Type: AddrDomain, + Host: host, + Port: uint16(port), + } + + if ip := net.ParseIP(host); ip != nil { + if ip.To4() != nil { + addr.Type = AddrIPv4 + } else { + addr.Type = AddrIPv6 + } + } + + return +} + func (addr *Addr) Decode(b []byte) error { addr.Type = b[0] pos := 1