socks5

package module
v0.0.11 Latest Latest
Warning

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

Go to latest
Published: May 1, 2023 License: MIT Imports: 13 Imported by: 0

README

socks5

This is a Golang implementation of the Socks5 protocol library.
To see in this SOCKS Protocol Version 5.
This library is also compatible with Socks4 and Socks4a.

Contents

Features

  • socks5:
    • command: CONNECT, UDP ASSOCIATE, BIND.
    • auth methods:
      • Username/Password authentication.
      • No Authentication Required.
  • socks4:
    • command: CONNECT, BIND.
    • auth: (no support).
  • sock4a: same as socks4.
  • Custom client and server authenticator.
  • Easy to read source code.
  • Similar to the Golang standard library experience.

Installation

$ go get "github.com/PIngBZ/socks5"`

Examples

Server example

simple (no authentication):
package main

import (
	"log"
	"github.com/PIngBZ/socks5"
)

func main() {
	// create socks server.
	srv := &socks5.Server{
		// socks server listen address.
		Addr: "127.0.0.1:1080",
		// UDP assocaite and bind command listen ip.
		// Don't need port, the port will automatically chosen.
		BindIP: "127.0.0.1",
		// if nil server will provide no authentication required method.
		Authenticators: nil,
	}

	// start listen
	err := srv.ListenAndServe()
	if err != nil {
		log.Fatal(err)
	}
}


username/password authentication in memory:
package main

import (
	"crypto/md5"
	"log"

	"github.com/PIngBZ/socks5"
)

func main() {
	// create a username/password store in memory.
	var userStorage socks5.UserPwdStore = socks5.NewMemeryStore(md5.New(), "secret")
	// set a pair of username/password.
	userStorage.Set("admin", "123456")

	srv := &socks5.Server{
		Addr:   "127.0.0.1:1080",
		BindIP: "127.0.0.1",
		// enable username/password method and authenticator.
		Authenticators: map[socks5.METHOD]socks5.Authenticator{
			socks5.USERNAME_PASSWORD: socks5.UserPwdAuth{UserPwdStore: userStorage},
			// There is already an authentication method.
			// If want enable no authentication required method.
			// you should enable it explicit.
			socks5.NO_AUTHENTICATION_REQUIRED: socks5.NoAuth{},
		},
	}

	err := srv.ListenAndServe()
	if err != nil {
		log.Fatal(err)
	}
}
custom transporter to transmit data between client and remote.
package main

import (
	"log"
	"net"

	"github.com/PIngBZ/socks5"
)

// simulate to impl socks5.Transporter interface.
// transport encrypted data.
type cryptTransport struct {
}

func (c *cryptTransport) TransportStream(client *net.Conn, remote *net.Conn) <-chan error {
	//encrypt data and send to remote
	//decrypt data and send to client
	return nil
}

func (c *cryptTransport) TransportUDP(server *socks5.UDPConn, request *socks5.Request) error {
	panic("implement me")
	return nil
}

func main() {
	server := &socks5.Server{
		Addr:   "127.0.0.1:1080",
		BindIP: "127.0.0.1",
		// replace default Transporter interface
		Transporter: &cryptTransport{},
	}

	err := server.ListenAndServe()
	if err != nil {
		log.Fatal(err)
	}
}

Client example

CONNECT usage:
package main

import (
	"log"

	"github.com/PIngBZ/socks5"
)

func main() {
	// create socks client
	clnt := socks5.Client{
		ProxyAddr: "127.0.0.1:1080",
		// Authenticator supported by the client.
		// It must not be nil.
		Auth: map[socks5.METHOD]socks5.Authenticator{
			// If client want send NO_AUTHENTICATION_REQUIRED method to server, must
			// add socks5.NoAuth authenticator explicitly
			socks5.NO_AUTHENTICATION_REQUIRED: &socks5.NoAuth{},
		},
	}

	// client send CONNECT command and get a tcp connection.
	// and use this connection transit data between you and www.google.com:80.
	conn, err := clnt.Connect(socks5.Version5, "www.baidu.com:80")
	if err != nil {
		log.Fatal(err)
	}

	// close connection.
	conn.Close()
}

UDP_ASSOCIATE usage:
package main

import (
	"fmt"
	"log"

	"github.com/PIngBZ/socks5"
)

func main() {
	clnt := socks5.Client{
		ProxyAddr: "127.0.0.1:1080",
		// client provide USERNAME_PASSWORD method and 
		// NO_AUTHENTICATION_REQUIRED.
		Auth: map[socks5.METHOD]socks5.Authenticator{
			socks5.NO_AUTHENTICATION_REQUIRED: &socks5.NoAuth{},
			socks5.USERNAME_PASSWORD:          &socks5.UserPasswd{Username: "admin", Password: "123456"},
		},
	}

	// client send UDP_ASSOCIATE command and get a udp connection.
	// Empty local addr string a local address (127.0.0.1:port) is automatically chosen.
	// you can specific a address to tell socks server which client address will
	// send udp data. Such as clnt.UDPForward("127.0.0.1:9999").
	conn, err := clnt.UDPForward("")
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	// send every datagram should add UDP request header.
	someData := []byte("some data")
	// dest addr where are you send to.
	destAddr, _ := socks5.ParseAddress("127.0.0.1:9190")
	// packing socks5 UDP data with dest addr.
	pakcedData, err := socks5.PackUDPData(destAddr, someData)
	// final send you data
	conn.Write(pakcedData)

	// on the contrary.
	// you should unpacked the packet, after received  every packedData.
	buf := make([]byte, 65507)
	conn.Read(buf)

	// unpacking data.
	destAddr, unpackedData, err := socks5.UnpackUDPData(buf)
	// operate your udp data. 
	fmt.Println(unpackedData)
}
BIND usage:
package main

import (
	"encoding/binary"
	"github.com/PIngBZ/socks5"
	"log"
)

func main() {
	c := socks5.Client{
		ProxyAddr: "172.16.1.28:1080",
		Auth: map[socks5.METHOD]socks5.Authenticator{
			socks5.USERNAME_PASSWORD:          &socks5.UserPasswd{"admin", "123456"},
			socks5.NO_AUTHENTICATION_REQUIRED: &socks5.NoAuth{},
		},
	}

	// connect
	conn1, err := c.Connect(5, "127.0.0.1:9000")
	if err != nil {
		log.Fatal(err)
	}

	dest := "127.0.0.1:9001"
	// bind
	bindAddr, errors, conn2, err := c.Bind(4, dest)
	if err != nil {
		log.Fatal(err)
	}

	// An example tell dest about socks server bind address 
	// via CONNECT proxy connection.
	port := make([]byte, 2)
	binary.BigEndian.PutUint16(port, bindAddr.Port)
	conn1.Write(append(bindAddr.Addr, port...))

	// wait the second reply. if nil the dest already
	// established with socks server.
	err = <-errors
	if err != nil {
		log.Fatal(err)
		return
	}

	// bind success
	_, err = conn2.Write([]byte("hello"))
	if err != nil {
		return
		log.Fatal(err)
	}
}

FAQ:

  • Server default enable socks4. How to disable socks4 support?
    when you initialize a socks5 server, you should spefic this flag to disable explicitly.
    server := &socks5.Server{
        DisableSocks4: true,
    }
    

Documentation

Index

Constants

View Source
const (
	Version4 = 0x04
	Version5 = 0x05
)
View Source
const (
	// Granted means server allow  client request
	Granted = 90
	// Rejected means server refuse client request
	Rejected = 91
)

socks4 reply

View Source
const NULL byte = 0

Variables

View Source
var ErrServerClosed = errors.New("socks: Server closed")

ErrServerClosed is returned by the Server's Serve, ListenAndServe methods after a call to Shutdown or Close.

Functions

func GetRandomPort

func GetRandomPort(network string) (err error, port uint16)

GetRandomPort return a random port by specific network. The network must be "tcp", "udp".

func IsFreePort

func IsFreePort(network string, port uint16) error

IsFreePort indicates the port is available. The network must be "tcp", "udp".

func PackUDPData

func PackUDPData(addr *Address, payload []byte) ([]byte, error)

PackUDPData add UDP request header before payload.

func ReadNBytes

func ReadNBytes(reader io.Reader, n int) ([]byte, error)

ReadNBytes wrap io.ReadFull. read n bytes. The error is EOF only if no bytes were read. If an EOF happens after reading some but not all the bytes, ReadFull returns ErrUnexpectedEOF.

func ReadUntilNULL

func ReadUntilNULL(reader io.Reader) ([]byte, error)

ReadUntilNULL Read all not Null byte. Until read first Null byte(all zero bits)

Types

type ATYPE

type ATYPE = uint8

ATYPE indicates address type in Request and Reply struct

const (
	IPV4_ADDRESS ATYPE = 0x01
	DOMAINNAME   ATYPE = 0x03
	IPV6_ADDRESS ATYPE = 0x04
)

type Address

type Address struct {
	Addr net.IP
	ATYPE
	Port uint16
}

Address represents address in socks protocol.

func ParseAddress

func ParseAddress(addr string) (*Address, error)

ParseAddress parse address to *Address Input Examples:

127.0.0.1:80
example.com:443
[fe80::1%lo0]:80

func UnpackUDPData

func UnpackUDPData(data []byte) (addr *Address, payload []byte, err error)

UnpackUDPData split UDP header and payload.

func (*Address) Bytes

func (a *Address) Bytes(ver VER) ([]byte, error)

Bytes return bytes slice of Address by ver param. If ver is socks4, the returned socks4 address format as follows:

+----+----+----+----+----+----+....+----+....+----+
| DSTPORT |      DSTIP        | USERID       |NULL|
+----+----+----+----+----+----+----+----+....+----+

If ver is socks4 and address type is domain name, the returned socks4 address format as follows:

+----+----+----+----+----+----+....+----+....+----+....+----+....+----+
| DSTPORT |      DSTIP        | USERID       |NULL|   HOSTNAME   |NULL|
+----+----+----+----+----+----+----+----+....+----+----+----+....+----+

If ver is socks5 the returned socks5 address format as follows:

+------+----------+----------+
| ATYP | DST.ADDR | DST.PORT |
+------+----------+----------+
|  1   | Variable |    2     |
+------+----------+----------+

Socks4 call this method return bytes end with NULL, socks4 client use normally, Socks4 server should trim terminative NULL. Socks4 server should not call this method if server address type is DOMAINNAME

func (*Address) String

func (a *Address) String() string

Address return address Examples:

127.0.0.1:80
example.com:443
[fe80::1%lo0]:80

func (*Address) TCPAddr

func (a *Address) TCPAddr() (*net.TCPAddr, error)

TCPAddr return TCP Address.

func (*Address) UDPAddr

func (a *Address) UDPAddr() (*net.UDPAddr, error)

UDPAddr return UDP Address.

type AtypeError

type AtypeError struct {
	ATYPE
}

func (*AtypeError) Error

func (a *AtypeError) Error() string

type Authenticator

type Authenticator interface {
	Authenticate(in io.Reader, out io.Writer) error
}

Authenticator provides socks5's authentication sub negotiation.

type CMD

type CMD = uint8

CMD is one of a field in Socks5 Request

const (
	CONNECT       CMD = 0x01
	BIND          CMD = 0x02
	UDP_ASSOCIATE CMD = 0x03
)

type CMDError

type CMDError struct {
	CMD
}

CMDError cmd error type

func (*CMDError) Error

func (c *CMDError) Error() string

type Client

type Client struct {
	// ProxyAddr in the form "host:port". It not be empty.
	ProxyAddr string

	// Timeout specifies a time limit for requests made by this
	// Client. The timeout includes connection time, reading the response body.
	//
	// A Timeout of zero means no timeout.
	//
	// The Client cancels requests to the underlying Transport
	// as if the Request's Context ended.
	HandshakeTimeout time.Duration

	DialTimeout time.Duration

	// method mapping to the authenticator
	Auth map[METHOD]Authenticator

	// ErrorLog specifics an options logger for errors accepting
	// If nil, logging is done via log package's standard logger.
	ErrorLog *log.Logger

	Dialer func(client *Client, request *Request) (net.Conn, error)
}

Client defines parameters for running socks client.

func (*Client) Bind

func (clt *Client) Bind(ver VER, destAddr string) (*Address, <-chan error, net.Conn, error)

Bind send BIND Request. return 4 params: 1. Server bind address. 2. a readable chan to recv second reply from socks server. 3. A connection that is not immediately available, until read a nil from err chan. 4. An error, indicate the first reply result. If nil, successes.

func (*Client) Connect

func (clt *Client) Connect(ver VER, dest string) (net.Conn, error)

Connect send CONNECT Request. Returned a connected proxy connection.

func (*Client) UDPForward

func (clt *Client) UDPForward(laddr string) (*UDPConn, error)

UDPForward send UDP_ASSOCIATE Request. The laddr Param specific Client address to send udp datagram. If laddr is empty string, a local address (127.0.0.1:port) is automatically chosen. If port is occupied will return error.

type METHOD

type METHOD = uint8

METHOD Defined authentication methods

const (
	NO_AUTHENTICATION_REQUIRED METHOD = 0x00
	GSSAPI                     METHOD = 0x01
	USERNAME_PASSWORD          METHOD = 0x02
	IANA_ASSIGNED              METHOD = 0x03
	NO_ACCEPTABLE_METHODS      METHOD = 0xff
)

type MemoryStore

type MemoryStore struct {
	Users map[string][]byte

	hash.Hash
	// contains filtered or unexported fields
}

MemoryStore store username&password in memory. the password is encrypt with hash method.

func NewMemeryStore

func NewMemeryStore(algo hash.Hash, secret string) *MemoryStore

NewMemeryStore return a new MemoryStore

func (*MemoryStore) Del

func (m *MemoryStore) Del(username string) error

Del delete by username

func (*MemoryStore) Set

func (m *MemoryStore) Set(username string, password string) error

Set the mapping of username and password.

func (*MemoryStore) Validate

func (m *MemoryStore) Validate(username string, password string) error

Validate validate username and password.

type MethodError

type MethodError struct {
	METHOD
}

func (*MethodError) Error

func (m *MethodError) Error() string

type NoAuth

type NoAuth struct {
}

NoAuth NO_AUTHENTICATION_REQUIRED implementation.

func (NoAuth) Authenticate

func (n NoAuth) Authenticate(in io.Reader, out io.Writer) error

Authenticate NO_AUTHENTICATION_REQUIRED Authentication for socks5 Server and Client.

type OpError

type OpError struct {
	// VER describe the socks server version on process.
	VER

	// Op is the operation which caused the error, such as
	// "read", "write".
	Op string

	// Addr define client's address which caused the error.
	Addr net.Addr

	// Step is the client's current connection stage, such as
	// "check version", "authentication", "process request",
	Step string

	// Err is the error that occurred during the operation.
	// The Error method panics if the error is nil.
	Err error
}

OpError is the error type usually returned by functions in the socks5 package. It describes the socks version, operation, client address, and address of an error.

func (*OpError) Error

func (o *OpError) Error() string

type REP

type REP = uint8

REP is one of a filed in Socks5 Reply

const (
	SUCCESSED                       REP = 0x00
	GENERAL_SOCKS_SERVER_FAILURE    REP = 0x01
	CONNECTION_NOT_ALLOW_BY_RULESET REP = 0x02
	NETWORK_UNREACHABLE             REP = 0x03
	HOST_UNREACHABLE                REP = 0x04
	CONNECTION_REFUSED              REP = 0x05
	TTL_EXPIRED                     REP = 0x06
	COMMAND_NOT_SUPPORTED           REP = 0x07
	ADDRESS_TYPE_NOT_SUPPORTED      REP = 0x08
	UNASSIGNED                      REP = 0x09
)

socks5 reply

type REPError

type REPError struct {
	REP
}

func (*REPError) Error

func (r *REPError) Error() string

type Reply

type Reply struct {
	VER
	REP
	RSV uint8
	*Address
}

Reply a reply formed as follows:

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

type Request

type Request struct {
	VER
	CMD
	RSV uint8
	*Address
}

Request The SOCKS request is formed as follows:

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

type Server

type Server struct {
	// Addr optionally specifies the TCP address for the server to listen on,
	// in the form "host:port". If empty, ":1080" (port 1080) is used.
	Addr string

	// BindIP specific UDP relay server IP and bind listen IP.
	// It shouldn't be ipv4zero like "0,0,0,0" or ipv6zero like [:]
	// If empty, localhost used.
	BindIP string

	DialTimeout time.Duration

	// ReadTimeout is the maximum duration for reading from socks client.
	// it's only effective to socks server handshake process.
	//
	// If zero, there is no timeout.
	HandshakeReadTimeout time.Duration

	// WriteTimeout is the maximum duration for writing to socks client.
	// it's only effective to socks server handshake process.
	//
	// If zero, there is no timeout.
	HandshakeWriteTimeout time.Duration

	// method mapping to the authenticator
	// if nil server provide NO_AUTHENTICATION_REQUIRED method by default
	Authenticators map[METHOD]Authenticator

	// Server transmit data between client and dest server.
	// if nil, DefaultTransport is used.
	Transporter

	CallbackAfterHandshake func(server *Server, request *Request) bool

	Dialer func(server *Server, request *Request, address string) (net.Conn, error)

	// ErrorLog specifics an options logger for errors accepting
	// connections, unexpected socks protocol handshake process,
	// and server to remote connection errors.
	// If nil, logging is done via log package's standard logger.
	ErrorLog *log.Logger
	// contains filtered or unexported fields
}

Server defines parameters for running socks server. The zero value for Server is a valid configuration(tcp listen on :1080).

func (*Server) Close

func (srv *Server) Close() error

func (*Server) IsAllowNoAuthRequired

func (srv *Server) IsAllowNoAuthRequired() bool

IsAllowNoAuthRequired return true if server enable NO_AUTHENTICATION_REQUIRED. Or the server doesn't has no Authenticator return true. Otherwise return false.

func (*Server) ListenAndServe

func (srv *Server) ListenAndServe() error

ListenAndServe listens on the TCP network address srv.Addr and then calls serve to handle requests on incoming connections.

If srv.Addr is blank, ":1080" is used.

func (*Server) MethodSelect

func (srv *Server) MethodSelect(methods []CMD, client net.Conn) error

MethodSelect select authentication method and reply to client.

func (*Server) Serve

func (srv *Server) Serve(l net.Listener) error

Serve accepts incoming connections on the Listener l, creating a new service goroutine for each. The service goroutine select client list methods and reply client. Then process authentication and reply to them. At then end of handshake, read socks request from client and establish a connection to the target.

type Transporter

type Transporter interface {
	TransportStream(client io.ReadWriteCloser, remote io.ReadWriteCloser) <-chan error
	TransportUDP(server *UDPConn, request *Request) error
}

Transporter transmit data between client and dest server.

var DefaultTransporter Transporter = &transport{}

type UDPConn

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

UDPConn be associated with TCP connections. The UDP connection will close immediately, When TCP connection closed, UDPConn only use in UDP_ASSOCIATE command.

func NewUDPConn

func NewUDPConn(udp *net.UDPConn, tcp net.Conn) *UDPConn

NewUDPConn get a *UDPConn through provide a tcp and udp connection. the tcp connection is used for socks UDP_ASSOCIATE handshake. the udp connection is used for socks udp forwarding.

After UDP_ASSOCIATE handshake, the tcp transit nothing. Its only function is udp relay connection still running.

If one of them shuts off, it will close them all.

func (*UDPConn) Close

func (u *UDPConn) Close() error

func (*UDPConn) CloseCh

func (u *UDPConn) CloseCh() <-chan struct{}

func (*UDPConn) LocalAddr

func (u *UDPConn) LocalAddr() net.Addr

func (*UDPConn) Read

func (u *UDPConn) Read(b []byte) (n int, err error)

func (*UDPConn) ReadFromUDP

func (u *UDPConn) ReadFromUDP(b []byte) (int, *net.UDPAddr, error)

func (*UDPConn) RemoteAddr

func (u *UDPConn) RemoteAddr() net.Addr

func (*UDPConn) SetDeadline

func (u *UDPConn) SetDeadline(t time.Time) error

func (*UDPConn) SetReadDeadline

func (u *UDPConn) SetReadDeadline(t time.Time) error

func (*UDPConn) SetWriteDeadline

func (u *UDPConn) SetWriteDeadline(t time.Time) error

func (*UDPConn) Write

func (u *UDPConn) Write(b []byte) (n int, err error)

func (*UDPConn) WriteToUDP

func (u *UDPConn) WriteToUDP(b []byte, addr *net.UDPAddr) (int, error)

type UDPHeader

type UDPHeader struct {
	RSV  uint16
	FRAG uint8
	*Address
	Data []byte
}

UDPHeader Each UDP datagram carries a UDP request header with it:

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

type UserNotExist

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

UserNotExist the error type used in UserPwdStore.Del() method and UserPwdStore.Validate method.

func (UserNotExist) Error

func (u UserNotExist) Error() string

type UserPasswd

type UserPasswd struct {
	Username string
	Password string
}

UserPasswd provide socks5 Client Username/Password Authenticator.

func (*UserPasswd) Authenticate

func (c *UserPasswd) Authenticate(in io.Reader, out io.Writer) error

Authenticate socks5 Client Username/Password Authentication.

type UserPwdAuth

type UserPwdAuth struct {
	UserPwdStore
}

UserPwdAuth provides socks5 Server Username/Password Authenticator.

func (UserPwdAuth) Authenticate

func (u UserPwdAuth) Authenticate(in io.Reader, out io.Writer) error

Authenticate provide socks5 Server Username/Password authentication.

func (UserPwdAuth) ReadUserPwd

func (u UserPwdAuth) ReadUserPwd(in io.Reader) ([]byte, []byte, error)

ReadUserPwd read Username/Password request from client return username and password. Username/Password request format is as follows:

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

For standard details, please see (https://www.rfc-editor.org/rfc/rfc1929.html)

type UserPwdStore

type UserPwdStore interface {
	Set(username string, password string) error
	Del(username string) error
	Validate(username string, password string) error
}

UserPwdStore provide username and password storage.

type VER

type VER = uint8

VER indicates protocol version

type VersionError

type VersionError struct {
	VER
}

func (*VersionError) Error

func (v *VersionError) Error() string

Jump to

Keyboard shortcuts

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