Skip to content

Commit

Permalink
docs: add comments and docs
Browse files Browse the repository at this point in the history
- modify README.md
- add more comments!
  • Loading branch information
gaukas committed May 25, 2023
1 parent e754c26 commit 73ef2e6
Show file tree
Hide file tree
Showing 9 changed files with 54 additions and 7 deletions.
22 changes: 18 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# socks5

A sub-RFC1928 SOCKS5 implementation in pure Go with no external dependency.
A sub-RFC1928 SOCKS5 server supporting custom transport layer implemented in pure Go with no external dependency.

## Overview

This package implements the SOCKS5 protocol as described in [RFC1928](https://tools.ietf.org/html/rfc1928) in Go with no external dependency. It is designed to be used as a library in other projects, for purposes including but not limited to: proxy client/server, traffic analysis, etc.
This package implements the SOCKS5 protocol as described in [RFC1928](https://tools.ietf.org/html/rfc1928) in Go with no external dependency. Unlike a traditional SOCKS5 server, this implementation separates the SOCKS5 server from the **actual** proxy server, which allows it to be used with any custom transport and/or in other applications.

### Features
### SOCKS5 Features

- Authentication Methods
- [x] NO AUTHENTICATION REQUIRED
Expand All @@ -19,4 +19,18 @@ This package implements the SOCKS5 protocol as described in [RFC1928](https://to

## Usage

See `example/min` for a minimal example with a minimal SOCKS5 proxy module implementation.
It is mandatory to provide a `Proxy` implementation to spin up a SOCKS5 `Server` with this package.

A `Proxy` interface provides a general-purpose proxy backend service with following methods:

```go
type Proxy interface {
Connect(dst net.Addr) (conn net.Conn, err error)
Bind(dst net.Addr) (net.Listener, error)
UDPAssociate() (net.PacketConn, error)
}
```

Essentially, by allowing custom `Proxy`, this package enables high programmability and flexibility for how SOCKS5 server proxies network traffic. It is possible to implement a `Proxy` that proxies traffic via another remote server via some more complex protocol such as TLS.

An example of a `Proxy` implementation can be found as `localProxy` in `proxy.go`. If a `Server` is spun up with this `localProxy`, it will act as a traditional SOCKS5 server that proxies traffic directly from the machine it runs on.
4 changes: 4 additions & 0 deletions addr.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,18 +73,22 @@ func newAddr(network, adr string) *addr {
}
}

// Network interfaces net.Addr
func (a *addr) Network() string {
return a.network
}

// String interfaces net.Addr
func (a *addr) String() string {
return a.addr
}

// StringEqual compares the string representation an addr with another net.Addr
func (a *addr) StringEqual(b net.Addr) bool {
return a.String() == b.String()
}

// HostMatching compares the host part of an addr with another net.Addr
func (a *addr) HostMatching(b net.Addr) bool {
// split the host and port from hostOrAddr
host, _, err := net.SplitHostPort(b.String())
Expand Down
3 changes: 3 additions & 0 deletions authentication_methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net"
)

// AuthenticationMethod is an interface that represents a custom SOCKS5 authentication method.
type AuthenticationMethod interface {
// Authenticate will respond to the net.Conn with the selected authentication method.
// Then, the AuthenticationMethod should proceed and finish the authentication process.
Expand All @@ -25,6 +26,7 @@ func (*NoAuthenticationRequired) Authenticate(conn net.Conn) error {
return authSelect.Write(conn)
}

// UsernamePassword is a AuthenticationMethod that requires username/password authentication.
type UsernamePassword struct {
UserPass map[string]string
}
Expand All @@ -34,6 +36,7 @@ const (
USERPASS_AUTH_FAILURE byte = 0x01
)

// Authenticate implements the AuthenticationMethod interface.
func (up *UsernamePassword) Authenticate(conn net.Conn) error {
var authSelect *PacketAuthSelect = &PacketAuthSelect{PROTOCOL_VERSION, USERNAME_PASSWORD}
err := authSelect.Write(conn)
Expand Down
2 changes: 2 additions & 0 deletions authenticator.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"net"
)

// Authenticator is used to authenticate the client. It supports Username/Password method
// (and No Auth method) by default and allows custom authentication methods via PrivateMethods.
type Authenticator struct {
Forced bool // default: false, if set to true, NO_AUTHENTICATION_REQUIRED is not accepted
UserPass map[string]string
Expand Down
10 changes: 10 additions & 0 deletions logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,33 +21,43 @@ type Logger interface {
// noLogger is a no-op logger
type noLogger struct{}

// Debug interfaces Logger
func (*noLogger) Debug(_ ...any) {
}

// Debugf interfaces Logger
func (*noLogger) Debugf(_ string, _ ...any) {
}

// Info interfaces Logger
func (*noLogger) Info(_ ...any) {
}

// Infof interfaces Logger
func (*noLogger) Infof(_ string, _ ...any) {
}

// Warn interfaces Logger
func (*noLogger) Warn(_ ...any) {
}

// Warnf interfaces Logger
func (*noLogger) Warnf(_ string, _ ...any) {
}

// Error interfaces Logger
func (*noLogger) Error(_ ...any) {
}

// Errorf interfaces Logger
func (*noLogger) Errorf(_ string, _ ...any) {
}

// Fatal interfaces Logger
func (*noLogger) Fatal(_ ...any) {
}

// Fatalf interfaces Logger
func (*noLogger) Fatalf(_ string, _ ...any) {
}

Expand Down
14 changes: 14 additions & 0 deletions packets.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type PacketAuthRequest struct {
METHODS []byte // length: NMETHODS
}

// Read interfaces Packet
func (p *PacketAuthRequest) Read(r io.Reader) error {
// Read VER and NMETHODS
hdr := make([]byte, 2)
Expand Down Expand Up @@ -73,6 +74,7 @@ func (p *PacketAuthRequest) Read(r io.Reader) error {
return nil
}

// Write interfaces Packet
func (*PacketAuthRequest) Write(_ io.Writer) error {
return fmt.Errorf("not implemented for client-sent packet")
}
Expand All @@ -93,10 +95,12 @@ type PacketAuthSelect struct {
METHOD byte
}

// Read interfaces Packet
func (*PacketAuthSelect) Read(_ io.Reader) error {
return fmt.Errorf("not implemented for server-sent packet")
}

// Write interfaces Packet
func (p *PacketAuthSelect) Write(w io.Writer) error {
n, err := w.Write([]byte{p.VER, p.METHOD})
if err != nil {
Expand Down Expand Up @@ -131,6 +135,7 @@ const (
USERPASS_AUTH_VERSION byte = 0x01
)

// Read interfaces Packet
func (p *PacketUserPassAuth) Read(r io.Reader) error {
// Read VER, ULEN
verulen := make([]byte, 2)
Expand Down Expand Up @@ -192,6 +197,7 @@ func (p *PacketUserPassAuth) Read(r io.Reader) error {
return nil
}

// Write interfaces Packet
func (*PacketUserPassAuth) Write(_ io.Writer) error {
return fmt.Errorf("not implemented for client-sent packet")
}
Expand All @@ -212,10 +218,12 @@ type PacketUserPassAuthStatus struct {
STATUS byte
}

// Read interfaces Packet
func (*PacketUserPassAuthStatus) Read(_ io.Reader) error {
return fmt.Errorf("not implemented for server-sent packet")
}

// Write interfaces Packet
func (p *PacketUserPassAuthStatus) Write(w io.Writer) error {
n, err := w.Write([]byte{p.VER, p.STATUS})
if err != nil {
Expand Down Expand Up @@ -257,6 +265,7 @@ const (
REQUEST_ATYP_IPV6 byte = 0x04
)

// Read interfaces Packet
func (p *PacketRequest) Read(r io.Reader) error {
// Read VER, CMD, RSV, ATYP
vercmdrsvatyp := make([]byte, 4)
Expand Down Expand Up @@ -351,6 +360,7 @@ func (p *PacketRequest) Read(r io.Reader) error {
return nil
}

// Write interfaces Packet
func (*PacketRequest) Write(_ io.Writer) error {
return fmt.Errorf("not implemented for client-sent packet")
}
Expand Down Expand Up @@ -391,10 +401,12 @@ const (
REPLY_ATYP_IPV6 byte = 0x04
)

// Read interfaces Packet
func (*PacketReply) Read(_ io.Reader) error {
return fmt.Errorf("not implemented for server-sent packet")
}

// Write interfaces Packet
func (p *PacketReply) Write(w io.Writer) error {
if p.VER == 0x00 {
p.VER = PROTOCOL_VERSION // by default, use the implemented version
Expand Down Expand Up @@ -472,6 +484,7 @@ const (
UDP_RSV_EXPECTED uint16 = 0x0000
)

// Read reads next UDP request from the given packet connection.
func (p *PacketUDPRequest) Read(pc net.PacketConn) error {
var buf []byte = make([]byte, 65535)
var n int
Expand Down Expand Up @@ -512,6 +525,7 @@ func (p *PacketUDPRequest) Read(pc net.PacketConn) error {
return nil
}

// Write writes the UDP request to the given packet connection.
func (p *PacketUDPRequest) Write(pc net.PacketConn) error {
var bndaddr []byte
var err error
Expand Down
3 changes: 1 addition & 2 deletions proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import (
"net"
)

// Proxy is the interface for an underlying transport which delivers proxy requests and responses
// between the client and the proxy worker server.
// Proxy is the interface for an underlying implementation of a general-purpose proxy.
type Proxy interface {
// Connect creates an outgoing (TCP) connection to the destination (dst) from the proxy server.
//
Expand Down
2 changes: 2 additions & 0 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ type Server struct {
wg *sync.WaitGroup
}

// NewServer creates a new Server.
// Wrap or Listen must be called explicitly before the server can accept connections.
func NewServer(auth *Authenticator, proxy Proxy, logger Logger) (*Server, error) {
if proxy == nil {
return nil, errors.New("no proxy provided")
Expand Down
1 change: 0 additions & 1 deletion server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
// - SOCKS5 Proxy Server binds to 127.0.0.2:8080
// - Underlying Proxy Server Implementation works on 127.0.1.2
// - Dummy Remote Destination binds to 127.0.0.3:8080

func TestServer(t *testing.T) {
t.Run("HandleConnIPv4", func(t *testing.T) {
t.Run("CmdConnectIPv4", testHandleCmdConnectIPv4)
Expand Down

0 comments on commit 73ef2e6

Please sign in to comment.