Skip to content

Commit

Permalink
opensource
Browse files Browse the repository at this point in the history
  • Loading branch information
txthinking committed Jan 2, 2017
1 parent c9252b9 commit bff9f50
Show file tree
Hide file tree
Showing 12 changed files with 317 additions and 88 deletions.
85 changes: 83 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,83 @@
# socks5
SOCKS Protocol Version 5
## socks5

[![Go Report Card](https://goreportcard.com/badge/github.com/txthinking/socks5)](https://goreportcard.com/report/github.com/txthinking/socks5)
[![GoDoc](https://godoc.org/github.com/txthinking/socks5?status.svg)](https://godoc.org/github.com/txthinking/socks5)

SOCKS Protocol Version 5 Library

### Install
```
$ go get github.com/txthinking/socks5
```

### Example

```
func ExampleSocks5Server() {
timeout := 60 // 60s
socks5.Debug = true // enable socks5 debug log
l, err := net.Listen("tcp", ":1980")
if err != nil {
log.Println(err)
return
}
defer l.Close()
for {
c, err := l.Accept()
if err != nil {
log.Println(err)
return
}
go func(c net.Conn) {
defer c.Close()
if err := c.SetReadDeadline(time.Now().Add(time.Duration(timeout) * time.Second)); err != nil {
log.Println("set local timeout:", err)
return
}
s5s := &socks5.Server{
C: c,
SelectMethod: func(methods []byte) (method byte, got bool) {
for _, m := range methods {
if m == socks5.MethodNone {
method = socks5.MethodNone
got = true
return
}
}
return
},
SupportedCommands: []byte{socks5.CmdConnect},
}
if err := s5s.Negotiate(); err != nil {
log.Println(err)
return
}
r, err := s5s.GetRequest()
if err != nil {
log.Println(err)
return
}
rc, err := r.Connect(c)
if err != nil {
log.Println(err)
return
}
defer rc.Close()
if err := rc.SetReadDeadline(time.Now().Add(time.Duration(timeout) * time.Second)); err != nil {
log.Println("set remote timeout:", err)
return
}
go func() {
_, _ = io.Copy(c, rc)
}()
_, _ = io.Copy(rc, c)
}(c)
}
}
```
9 changes: 6 additions & 3 deletions bind.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package socks5

import "net"
import (
"errors"
"net"
)

// todo
// TODO
func (r *Request) bind(c net.Conn) error {
return nil
return errors.New("Unsupport BIND now")
}
46 changes: 28 additions & 18 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,21 @@ import (
)

var (
ERROR_BAD_REPLY = errors.New("Bad Reply")
// ErrBadReply is the error when read reply
ErrBadReply = errors.New("Bad Reply")
)

// NewNegotiationRequest return negotiation request packet can be writed into server
func NewNegotiationRequest(methods []byte) *NegotiationRequest {
return &NegotiationRequest{
Ver: VER,
Ver: Ver,
NMethods: byte(len(methods)),
Methods: methods,
}
}

func (r *NegotiationRequest) Write(w io.Writer) error {
// WriteTo write negotiation request packet into server
func (r *NegotiationRequest) WriteTo(w io.Writer) error {
if _, err := w.Write([]byte{r.Ver}); err != nil {
return err
}
Expand All @@ -34,13 +37,14 @@ func (r *NegotiationRequest) Write(w io.Writer) error {
return nil
}

// NewNegotiationReplyFrom read negotiation reply packet from server
func NewNegotiationReplyFrom(r io.Reader) (*NegotiationReply, error) {
bb := make([]byte, 2)
if _, err := io.ReadFull(r, bb); err != nil {
return nil, err
}
if bb[0] != VER {
return nil, ERROR_VERSION
if bb[0] != Ver {
return nil, ErrVersion
}
if Debug {
log.Printf("Got NegotiationReply: %#v %#v\n", bb[0], bb[1])
Expand All @@ -51,16 +55,18 @@ func NewNegotiationReplyFrom(r io.Reader) (*NegotiationReply, error) {
}, nil
}

// NewUserPassNegotiationRequest return user password negotiation request packet can be writed into server
func NewUserPassNegotiationRequest(username []byte, password []byte) *UserPassNegotiationRequest {
return &UserPassNegotiationRequest{
Ver: USER_PASS_VER,
Ver: UserPassVer,
Ulen: byte(len(username)),
Uname: username,
Plen: byte(len(password)),
Passwd: password,
}
}

// WriteTo write user password negotiation request packet into server
func (r *UserPassNegotiationRequest) WriteTo(w io.Writer) error {
if _, err := w.Write([]byte{r.Ver, r.Ulen}); err != nil {
return err
Expand All @@ -80,13 +86,14 @@ func (r *UserPassNegotiationRequest) WriteTo(w io.Writer) error {
return nil
}

// NewUserPassNegotiationReplyFrom read user password negotiation reply packet from server
func NewUserPassNegotiationReplyFrom(r io.Reader) (*UserPassNegotiationReply, error) {
bb := make([]byte, 2)
if _, err := io.ReadFull(r, bb); err != nil {
return nil, err
}
if bb[0] != USER_PASS_VER {
return nil, ERROR_USER_PASS_VERSION
if bb[0] != UserPassVer {
return nil, ErrUserPassVersion
}
if Debug {
log.Printf("Got UserPassNegotiationReply: %#v %#v \n", bb[0], bb[1])
Expand All @@ -97,12 +104,13 @@ func NewUserPassNegotiationReplyFrom(r io.Reader) (*UserPassNegotiationReply, er
}, nil
}

// NewRequest return request packet can be writed into server
func NewRequest(cmd byte, atyp byte, dstaddr []byte, dstport []byte) *Request {
if atyp == ATYP_DOMAIN {
if atyp == ATYPDomain {
dstaddr = append([]byte{byte(len(dstaddr))}, dstaddr...)
}
return &Request{
Ver: VER,
Ver: Ver,
Cmd: cmd,
Rsv: 0x00,
Atyp: atyp,
Expand All @@ -111,11 +119,12 @@ func NewRequest(cmd byte, atyp byte, dstaddr []byte, dstport []byte) *Request {
}
}

// WriteTo write request packet into server
func (r *Request) WriteTo(w io.Writer) error {
if _, err := w.Write([]byte{r.Ver, r.Cmd, r.Rsv, r.Atyp}); err != nil {
return err
}
if r.Atyp == ATYP_DOMAIN {
if r.Atyp == ATYPDomain {
if _, err := w.Write([]byte{byte(len(r.DstAddr))}); err != nil {
return err
}
Expand All @@ -132,40 +141,41 @@ func (r *Request) WriteTo(w io.Writer) error {
return nil
}

// NewReplyFrom read reply packet from server
func NewReplyFrom(r io.Reader) (*Reply, error) {
bb := make([]byte, 4)
if _, err := io.ReadFull(r, bb); err != nil {
return nil, err
}
if bb[0] != VER {
return nil, ERROR_VERSION
if bb[0] != Ver {
return nil, ErrVersion
}
var addr []byte
if bb[3] == ATYP_IPV4 {
if bb[3] == ATYPIPv4 {
addr = make([]byte, 4)
if _, err := io.ReadFull(r, addr); err != nil {
return nil, err
}
} else if bb[3] == ATYP_IPV6 {
} else if bb[3] == ATYPIPv6 {
addr = make([]byte, 16)
if _, err := io.ReadFull(r, addr); err != nil {
return nil, err
}
} else if bb[3] == ATYP_DOMAIN {
} else if bb[3] == ATYPDomain {
dal := make([]byte, 1)
if _, err := io.ReadFull(r, dal); err != nil {
return nil, err
}
if dal[0] == 0 {
return nil, ERROR_BAD_REPLY
return nil, ErrBadReply
}
addr = make([]byte, int(dal[0]))
if _, err := io.ReadFull(r, addr); err != nil {
return nil, err
}
addr = append(dal, addr...)
} else {
return nil, ERROR_BAD_REPLY
return nil, ErrBadReply
}
port := make([]byte, 2)
if _, err := io.ReadFull(r, port); err != nil {
Expand Down
7 changes: 4 additions & 3 deletions connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,23 @@ import (
"net"
)

// return remote conn which u want to connect
// Connect remote conn which u want to connect.
// You may should write your method instead of use this method.
func (r *Request) Connect(c net.Conn) (net.Conn, error) {
if Debug {
log.Println("Call:", r.Address())
}
rc, err := net.Dial("tcp", r.Address())
if err != nil {
p := NewReply(REP_HOST_UNREACHABLE, ATYP_IPV4, []byte{0x00, 0x00, 0x00, 0x00}, []byte{0x00, 0x00})
p := NewReply(RepHostUnreachable, ATYPIPv4, []byte{0x00, 0x00, 0x00, 0x00}, []byte{0x00, 0x00})
if err := p.WriteTo(c); err != nil {
return nil, err
}
return nil, err
}

a, addr, port := ParseAddress(rc.LocalAddr())
p := NewReply(REP_SUCCESS, a, addr, port)
p := NewReply(RepSuccess, a, addr, port)
if err := p.WriteTo(c); err != nil {
return nil, err
}
Expand Down
78 changes: 78 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package socks5_test

import (
"io"
"log"
"net"
"time"

"github.com/txthinking/socks5"
)

func ExampleSocks5Server() {
timeout := 60 // 60s
socks5.Debug = true // enable socks5 debug log

l, err := net.Listen("tcp", ":1980")
if err != nil {
log.Println(err)
return
}
defer l.Close()

for {
c, err := l.Accept()
if err != nil {
log.Println(err)
return
}

go func(c net.Conn) {
defer c.Close()
if err := c.SetReadDeadline(time.Now().Add(time.Duration(timeout) * time.Second)); err != nil {
log.Println("set local timeout:", err)
return
}

s5s := &socks5.Server{
C: c,
SelectMethod: func(methods []byte) (method byte, got bool) {
for _, m := range methods {
if m == socks5.MethodNone {
method = socks5.MethodNone
got = true
return
}
}
return
},
SupportedCommands: []byte{socks5.CmdConnect},
}
if err := s5s.Negotiate(); err != nil {
log.Println(err)
return
}
r, err := s5s.GetRequest()
if err != nil {
log.Println(err)
return
}
rc, err := r.Connect(c)
if err != nil {
log.Println(err)
return
}
defer rc.Close()
if err := rc.SetReadDeadline(time.Now().Add(time.Duration(timeout) * time.Second)); err != nil {
log.Println("set remote timeout:", err)
return
}
go func() {
_, _ = io.Copy(c, rc)
}()
_, _ = io.Copy(rc, c)

}(c)

}
}
Loading

0 comments on commit bff9f50

Please sign in to comment.