socks5

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 9, 2023 License: GPL-3.0 Imports: 12 Imported by: 2

README

socks5

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 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.

SOCKS5 Features
  • Authentication Methods
    • NO AUTHENTICATION REQUIRED
    • USERNAME/PASSWORD (untested)
    • GSSAPI (custom support possible via PrivateMethods in Authenticator)
  • Commands
    • CONNECT
    • BIND
    • UDP ASSOCIATE

Usage

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:

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.

Documentation

Index

Constants

View Source
const (
	USERPASS_AUTH_SUCCESS byte = 0x00
	USERPASS_AUTH_FAILURE byte = 0x01
)
View Source
const (
	NO_AUTHENTICATION_REQUIRED byte = 0x00
	GSSAPI                     byte = 0x01
	USERNAME_PASSWORD          byte = 0x02
	NO_ACCEPTABLE_METHODS      byte = 0xFF
)
View Source
const (
	REQUEST_CMD_CONNECT       byte = 0x01
	REQUEST_CMD_BIND          byte = 0x02
	REQUEST_CMD_UDP_ASSOCIATE byte = 0x03

	REQUEST_ATYP_IPV4       byte = 0x01
	REQUEST_ATYP_DOMAINNAME byte = 0x03
	REQUEST_ATYP_IPV6       byte = 0x04
)
View Source
const (
	REPLY_REP_SUCCEEDED                byte = 0x00
	REPLY_REP_GENERAL_SOCKS_SERVER_ERR byte = 0x01
	REPLY_REP_CONNECTION_NOT_ALLOWED   byte = 0x02
	REPLY_REP_NETWORK_UNREACHABLE      byte = 0x03
	REPLY_REP_HOST_UNREACHABLE         byte = 0x04
	REPLY_REP_CONNECTION_REFUSED       byte = 0x05
	REPLY_REP_TTL_EXPIRED              byte = 0x06
	REPLY_REP_COMMAND_NOT_SUPPORTED    byte = 0x07
	REPLY_REP_ADDRESS_TYPE_NOT_SUPP    byte = 0x08

	REPLY_ATYP_IPV4       byte = 0x01
	REPLY_ATYP_DOMAINNAME byte = 0x03
	REPLY_ATYP_IPV6       byte = 0x04
)
View Source
const (
	UDP_REQUEST_NOFRAG byte = 0x00 // flushing previous reassembly queue & timer
	UDP_REQUEST_FRAG   byte = 0x01 // creating a new reassambly queue & timer

	UDP_REQUEST_ATYP_IPV4       byte = 0x01
	UDP_REQUEST_ATYP_DOMAINNAME byte = 0x03
	UDP_REQUEST_ATYP_IPV6       byte = 0x04

	UDP_RSV_EXPECTED uint16 = 0x0000
)
View Source
const (
	PROTOCOL_VERSION byte = 0x05
)
View Source
const (
	USERPASS_AUTH_VERSION byte = 0x01
)

Variables

View Source
var (
	ErrConnNotAllowed          = errors.New("connection not allowed by ruleset") // 0x02
	ErrNetworkUnreachable      = errors.New("network unreachable")               // 0x03
	ErrHostUnreachable         = errors.New("host unreachable")                  // 0x04
	ErrConnectionRefused       = errors.New("connection refused")                // 0x05
	ErrTTLExpired              = errors.New("ttl expired")                       // 0x06
	ErrCommandNotSupported     = errors.New("command not supported")             // 0x07
	ErrAddressTypeNotSupported = errors.New("address type not supported")        // 0x08

)

Errors returned by a Proxy interface implementation.

Functions

func NewLocalProxy added in v0.2.0

func NewLocalProxy(serverIP string) *localProxy

NewLocalProxy creates a new localProxy with the given serverIP. If the serverIP is unknown, an empty string should be passed in.

Types

type AuthenticationMethod

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.
	//
	// If returned error is nil, the authentication process is considered successful.
	// Otherwise, the authentication process is considered failed and the connection
	// MUST be closed by the caller.
	Authenticate(net.Conn) error
}

AuthenticationMethod is an interface that represents a custom SOCKS5 authentication method.

type Authenticator

type Authenticator struct {
	Forced         bool // default: false, if set to true, NO_AUTHENTICATION_REQUIRED is not accepted
	UserPass       map[string]string
	PrivateMethods map[byte]AuthenticationMethod
}

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.

func (*Authenticator) Auth

func (a *Authenticator) Auth(client net.Conn) error

Auth parses the client's authentication request and calls the appropriate AuthenticationMethod to authenticate the client.

type Config added in v0.3.0

type Config struct {
	// Auth specifies an *Authenticator to be used by the SOCKS5 server to
	// authenticate incoming SOCKS5 client connections. If nil, the SOCKS5 server
	// will not require authentication.
	Auth *Authenticator

	// LoggingHandler specifies a handler to be used by the SOCKS5 server. If nil,
	// the default logging handler will be used.
	//
	// TODO: switch to log/slog from experimental once it became stable in Go 1.21
	LoggingHandler slog.Handler

	// Proxy specifies a proxy implementation to be used by the SOCKS5 server.
	//
	// See proxy.go for more details about the Proxy interface and the localProxy
	// implementation.
	Proxy Proxy

	// ConnectTimeout specifies the maximum amount of time a Server will wait for
	// a Connect invocation to complete on the Proxy.
	//
	// If zero, no timeout is set and the Server may block indefinitely.
	ConnectTimeout time.Duration

	// BindTimeout specifies the maximum amount of time a Server will wait for a
	// Bind invocation to complete on the Proxy. It is recommended that this value
	// be set to a reasonable value to prevent port exhaustion.
	//
	// If zero, no timeout is set and the Server may block indefinitely.
	BindTimeout time.Duration

	// UDPTimeout specifies the maximum amount of time a Server will wait for a
	// UDP associate invocation to complete on the Proxy.
	//
	// If zero, no timeout is set and the Server may block indefinitely.
	UDPTimeout time.Duration
}

Config details the configuration of the SOCKS5 server.

type NoAuthenticationRequired

type NoAuthenticationRequired struct{}

NoAuthenticationRequired is a AuthenticationMethod that does not require any authentication. It only responds to the client with the selected authentication method and returns nil.

func (*NoAuthenticationRequired) Authenticate

func (*NoAuthenticationRequired) Authenticate(conn net.Conn) error

Authenticate implements the AuthenticationMethod interface.

type Packet

type Packet interface {
	// Read reads the packet from the given reader into the Packet object.
	//
	// SHOULD ONLY be called for client-sent packets. Calling this method
	// on a server-sent packet SHOULD result in an error.
	Read(io.Reader) error

	// Write writes the packet to the given writer.
	//
	// SHOULD ONLY be called for server-sent packets. Calling this method
	// on a client-sent packet SHOULD result in an error.
	Write(io.Writer) error
}

Packet defines the interface for all SOCKS5 packets except the UDP request. UDP request works on net.PacketConn instead of io.Reader/io.Writer.

type PacketAuthRequest

type PacketAuthRequest struct {
	VER      byte
	NMETHODS byte
	METHODS  []byte // length: NMETHODS
}

PacketAuthRequest is the packet sent by the client to request to authenticate using a certain type of authentication method.

The packet is structured as follows:

+----+----------+----------+
|VER | NMETHODS | METHODS  |
+----+----------+----------+
| 1  |    1     | 1 to 255 |
+----+----------+----------+

func (*PacketAuthRequest) Read

func (p *PacketAuthRequest) Read(r io.Reader) error

Read interfaces Packet

func (*PacketAuthRequest) Write

func (*PacketAuthRequest) Write(_ io.Writer) error

Write interfaces Packet

type PacketAuthSelect

type PacketAuthSelect struct {
	VER    byte
	METHOD byte
}

PacketAuthSelect is the packet sent by the server to indicate the selected authentication method that client should use. Sent by the server in response to PacketAuthRequest.

The packet is structured as follows:

+----+--------+
|VER | METHOD |
+----+--------+
| 1  |   1    |
+----+--------+

func (*PacketAuthSelect) Read

func (*PacketAuthSelect) Read(_ io.Reader) error

Read interfaces Packet

func (*PacketAuthSelect) Write

func (p *PacketAuthSelect) Write(w io.Writer) error

Write interfaces Packet

type PacketReply

type PacketReply struct {
	VER     byte
	REP     byte
	RSV     byte // must be 0x00, fail otherwise
	ATYP    byte
	BNDADDR string
	BNDPORT uint16
}

PacketReply is the packet sent by the server in response to a client request. Sent by the server after the client has successfully requested a command.

The packet is structured as follows:

+----+-----+-------+------+----------+----------+
|VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
+----+-----+-------+------+----------+----------+
| 1  |  1  | X'00' |  1   | Variable |    2     |
+----+-----+-------+------+----------+----------+

func (*PacketReply) Read

func (*PacketReply) Read(_ io.Reader) error

Read interfaces Packet

func (*PacketReply) Write

func (p *PacketReply) Write(w io.Writer) error

Write interfaces Packet

type PacketRequest

type PacketRequest struct {
	VER     byte
	CMD     byte
	RSV     byte
	ATYP    byte
	DSTADDR string
	DSTPORT uint16
}

PacketRequest is the packet sent by the client to request a connection to a remote host. Sent by the client after the server has successfully authenticated the client.

The packet is structured as follows:

+----+-----+-------+------+----------+----------+
|VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
+----+-----+-------+------+----------+----------+
| 1  |  1  | X'00' |  1   | Variable |    2     |
+----+-----+-------+------+----------+----------+

func (*PacketRequest) Read

func (p *PacketRequest) Read(r io.Reader) error

Read interfaces Packet

func (*PacketRequest) Write

func (*PacketRequest) Write(_ io.Writer) error

Write interfaces Packet

type PacketUDPRequest

type PacketUDPRequest struct {
	RSV     uint16
	FRAG    byte
	ATYP    byte
	DSTADDR string
	DSTPORT uint16
	DATA    []byte

	ClientAddr net.Addr // Proxy Server should verify this is the same as the client
}

PacketUDPRequest is the packet sent by the client to the server to request a datagram to be sent to a remote host, or sent by the server to the client in response to previous requests.

The packet is structured as follows:

+----+------+------+----------+----------+----------+
|RSV | FRAG | ATYP | DST.ADDR | DST.PORT |   DATA   |
+----+------+------+----------+----------+----------+
| 2  |  1   |  1   | Variable |    2     | Variable |
+----+------+------+----------+----------+----------+

func (*PacketUDPRequest) Read

func (p *PacketUDPRequest) Read(pc net.PacketConn) error

Read reads next UDP request from the given packet connection.

func (*PacketUDPRequest) Write

func (p *PacketUDPRequest) Write(pc net.PacketConn) error

Write writes the UDP request to the given packet connection.

type PacketUserPassAuth

type PacketUserPassAuth struct {
	VER    byte
	ULEN   byte
	UNAME  string // length: ULEN
	PLEN   byte
	PASSWD string // length: PLEN
}

PacketUserPassAuth is the packet sent by the client to authenticate with Username/Password pair. Sent by the client after the server has selected the Username/Password authentication method.

The packet is strctured as follows:

+----+------+----------+------+----------+
|VER | ULEN |  UNAME   | PLEN |  PASSWD  |
+----+------+----------+------+----------+
| 1  |  1   | 1 to 255 |  1   | 1 to 255 |
+----+------+----------+------+----------+

func (*PacketUserPassAuth) Read

func (p *PacketUserPassAuth) Read(r io.Reader) error

Read interfaces Packet

func (*PacketUserPassAuth) Write

func (*PacketUserPassAuth) Write(_ io.Writer) error

Write interfaces Packet

type PacketUserPassAuthStatus

type PacketUserPassAuthStatus struct {
	VER    byte
	STATUS byte
}

PacketUserPassAuthStatus is the packet sent by the server to indicate the status of the authentication. Sent by the server in response to PacketUserPassAuth.

The packet is structured as follows:

+----+--------+
|VER | STATUS |
+----+--------+
| 1  |   1    |
+----+--------+

func (*PacketUserPassAuthStatus) Read

Read interfaces Packet

func (*PacketUserPassAuthStatus) Write

Write interfaces Packet

type Proxy

type Proxy interface {
	// Connect is used to create an outgoing TCP connection from the proxy server to the
	// destination specified as dst.
	// The proxy implementation SHOULD use the provided context to cancel the connection attempt
	// if the context expires before the connection is established.
	//
	// The returned net.Conn MUST be a connection to the destination, i.e. any data written to
	// it will be send to the remote destination.
	// Its LocalAddr() method SHOULD return the address used to connect to the destination if known,
	// otherwise it MUST return 0.0.0.0 (IPv4) or ::0 (IPv6).
	Connect(ctx context.Context, dst net.Addr) (conn net.Conn, err error)

	// Bind requests the proxy server to listen for an incoming (TCP) connection. The proxy
	// implementation SHOULD bind a TCP address and port, and listen for the FIRST incoming
	// connection on to it.
	// The proxy implementation SHOULD use the provided context to cancel the connection
	// attempt if the context expires before the connection is established.
	//
	// The returned net.Listener MUST return the address the proxy server is listening on when
	// its Addr() method is called. Its Accept() method MUST block until the FIRST incoming
	// connection is received, and return a net.Conn representing the incoming connection, whose
	// RemoteAddr() call MUST return the address of the source and LocalAddr() call is expected to
	// return the address the listener is bound to, same as the one returned by Addr() of the
	// net.Listener.
	//
	// It is up to the implementation to decide how to handle subsequent incoming connections
	// on the same address and port, as well as how to handle multiple calls to Bind() with the same
	// dst address specified.
	Bind(ctx context.Context, dst net.Addr) (net.Listener, error)

	// UDPAssociate creates a UDP socket on the proxy server. The proxy implementation SHOULD use
	// the provided context to cancel the connection attempt if the context expires before the
	// connection is established.
	//
	// Invoking the WriteTo() method on the returned net.PacketConn sends a UDP datagram to the
	// remote address specified in the function call. And UDP datagrams received from the remote
	// address should be accessible by invoking ReadFrom() on the returned net.PacketConn.
	UDPAssociate(ctx context.Context) (net.PacketConn, error)

	// Close closes the proxy server and cleans up any resources associated with it if possible.
	//
	// Calling Close on a proxy server does not necessarily close any connections created by it.
	// However, it SHOULD prevent any new connections from being created. If
	Close() error
}

Proxy is the interface for an underlying implementation of a general-purpose proxy. It is used by Server when handling received SOCKS5 requests. Generally, when any of the methods on Proxy is called, it is expected that the Proxy implementation will either dial out to a remote proxy server for the proxy request, or handle the request locally.

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server acts like an automatic SOCKS5 server which wraps an existing net.Listener.

func NewServer

func NewServer(config Config) (*Server, error)

NewServer creates a new Server.

Wrap or Listen must be called explicitly before the server can accept connections from SOCKS5 clients.

func (*Server) Close

func (s *Server) Close() error

Close stops the Server from accepting new incoming connections from SOCKS5 clients. This function does not guarantee the termination of any established connection.

func (*Server) Listen

func (s *Server) Listen(network, address string) error

Listen creates a net.Listener and calls Wrap on it for incoming connections from SOCKS5 clients.

func (*Server) Wrap

func (s *Server) Wrap(l net.Listener) error

Wrap wraps an existing net.Listener to accept connections from SOCKS5 clients.

type UsernamePassword

type UsernamePassword struct {
	UserPass map[string]string
}

UsernamePassword is a AuthenticationMethod that requires username/password authentication.

func (*UsernamePassword) Authenticate

func (up *UsernamePassword) Authenticate(conn net.Conn) error

Authenticate implements the AuthenticationMethod interface.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL