socks5

package module
v0.0.0-...-4230056 Latest Latest
Warning

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

Go to latest
Published: Mar 25, 2023 License: MIT Imports: 13 Imported by: 125

README

socks5

中文

Go Report Card GoDoc

🗣 News 🩸 Youtube

SOCKS Protocol Version 5 Library.

Full TCP/UDP and IPv4/IPv6 support. Goals: KISS, less is more, small API, code is like the original protocol.

❤️ A project by txthinking.com

Install

$ go get github.com/txthinking/socks5

Struct is like concept in protocol

  • Negotiation:
    • type NegotiationRequest struct
      • func NewNegotiationRequest(methods []byte), in client
      • func (r *NegotiationRequest) WriteTo(w io.Writer), client writes to server
      • func NewNegotiationRequestFrom(r io.Reader), server reads from client
    • type NegotiationReply struct
      • func NewNegotiationReply(method byte), in server
      • func (r *NegotiationReply) WriteTo(w io.Writer), server writes to client
      • func NewNegotiationReplyFrom(r io.Reader), client reads from server
  • User and password negotiation:
    • type UserPassNegotiationRequest struct
      • func NewUserPassNegotiationRequest(username []byte, password []byte), in client
      • func (r *UserPassNegotiationRequest) WriteTo(w io.Writer), client writes to server
      • func NewUserPassNegotiationRequestFrom(r io.Reader), server reads from client
    • type UserPassNegotiationReply struct
      • func NewUserPassNegotiationReply(status byte), in server
      • func (r *UserPassNegotiationReply) WriteTo(w io.Writer), server writes to client
      • func NewUserPassNegotiationReplyFrom(r io.Reader), client reads from server
  • Request:
    • type Request struct
      • func NewRequest(cmd byte, atyp byte, dstaddr []byte, dstport []byte), in client
      • func (r *Request) WriteTo(w io.Writer), client writes to server
      • func NewRequestFrom(r io.Reader), server reads from client
      • After server gets the client's *Request, processes...
  • Reply:
    • type Reply struct
      • func NewReply(rep byte, atyp byte, bndaddr []byte, bndport []byte), in server
      • func (r *Reply) WriteTo(w io.Writer), server writes to client
      • func NewReplyFrom(r io.Reader), client reads from server
  • Datagram:
    • type Datagram struct
      • func NewDatagram(atyp byte, dstaddr []byte, dstport []byte, data []byte)
      • func NewDatagramFromBytes(bb []byte)
      • func (d *Datagram) Bytes()

Advanced API

This can satisfy the classic scenario, and it is still recommended that you choose the above small API to customize for special scenarios.

Server: support both TCP and UDP

  • type Server struct
  • type Handler interface
    • TCPHandle(*Server, *net.TCPConn, *Request) error
    • UDPHandle(*Server, *net.UDPAddr, *Datagram) error

Example:

server, _ := NewClassicServer(addr, ip, username, password, tcpTimeout, udpTimeout)
server.ListenAndServe(Handler)

Client: support both TCP and UDP and return net.Conn

  • type Client struct

Example:

client, _ := socks5.NewClient(server, username, password, tcpTimeout, udpTimeout)
conn, _ := client.Dial(network, addr)

Projects using this library

License

Licensed under The MIT License

Documentation

Index

Examples

Constants

View Source
const (
	// Ver is socks protocol version
	Ver byte = 0x05

	// MethodNone is none method
	MethodNone byte = 0x00
	// MethodGSSAPI is gssapi method
	MethodGSSAPI byte = 0x01 // MUST support // todo
	// MethodUsernamePassword is username/assword auth method
	MethodUsernamePassword byte = 0x02 // SHOULD support
	// MethodUnsupportAll means unsupport all given methods
	MethodUnsupportAll byte = 0xFF

	// UserPassVer is username/password auth protocol version
	UserPassVer byte = 0x01
	// UserPassStatusSuccess is success status of username/password auth
	UserPassStatusSuccess byte = 0x00
	// UserPassStatusFailure is failure status of username/password auth
	UserPassStatusFailure byte = 0x01 // just other than 0x00

	// CmdConnect is connect command
	CmdConnect byte = 0x01
	// CmdBind is bind command
	CmdBind byte = 0x02
	// CmdUDP is UDP command
	CmdUDP byte = 0x03

	// ATYPIPv4 is ipv4 address type
	ATYPIPv4 byte = 0x01 // 4 octets
	// ATYPDomain is domain address type
	ATYPDomain byte = 0x03 // The first octet of the address field contains the number of octets of name that follow, there is no terminating NUL octet.
	// ATYPIPv6 is ipv6 address type
	ATYPIPv6 byte = 0x04 // 16 octets

	// RepSuccess means that success for repling
	RepSuccess byte = 0x00
	// RepServerFailure means the server failure
	RepServerFailure byte = 0x01
	// RepNotAllowed means the request not allowed
	RepNotAllowed byte = 0x02
	// RepNetworkUnreachable means the network unreachable
	RepNetworkUnreachable byte = 0x03
	// RepHostUnreachable means the host unreachable
	RepHostUnreachable byte = 0x04
	// RepConnectionRefused means the connection refused
	RepConnectionRefused byte = 0x05
	// RepTTLExpired means the TTL expired
	RepTTLExpired byte = 0x06
	// RepCommandNotSupported means the request command not supported
	RepCommandNotSupported byte = 0x07
	// RepAddressNotSupported means the request address not supported
	RepAddressNotSupported byte = 0x08
)

Variables

View Source
var (
	// ErrUnsupportCmd is the error when got unsupport command
	ErrUnsupportCmd = errors.New("Unsupport Command")
	// ErrUserPassAuth is the error when got invalid username or password
	ErrUserPassAuth = errors.New("Invalid Username or Password for Auth")
)
View Source
var (
	// ErrVersion is version error
	ErrVersion = errors.New("Invalid Version")
	// ErrUserPassVersion is username/password auth version error
	ErrUserPassVersion = errors.New("Invalid Version of Username Password Auth")
	// ErrBadRequest is bad request error
	ErrBadRequest = errors.New("Bad Request")
)
View Source
var Debug bool
View Source
var DialTCP func(network string, laddr, raddr string) (net.Conn, error) = func(network string, laddr, raddr string) (net.Conn, error) {
	var la, ra *net.TCPAddr
	if laddr != "" {
		var err error
		la, err = net.ResolveTCPAddr(network, laddr)
		if err != nil {
			return nil, err
		}
	}
	a, err := Resolve(network, raddr)
	if err != nil {
		return nil, err
	}
	ra = a.(*net.TCPAddr)
	return net.DialTCP(network, la, ra)
}
View Source
var DialUDP func(network string, laddr, raddr string) (net.Conn, error) = func(network string, laddr, raddr string) (net.Conn, error) {
	var la, ra *net.UDPAddr
	if laddr != "" {
		var err error
		la, err = net.ResolveUDPAddr(network, laddr)
		if err != nil {
			return nil, err
		}
	}
	a, err := Resolve(network, raddr)
	if err != nil {
		return nil, err
	}
	ra = a.(*net.UDPAddr)
	return net.DialUDP(network, la, ra)
}
View Source
var (
	// ErrBadReply is the error when read reply
	ErrBadReply = errors.New("Bad Reply")
)
View Source
var Resolve func(network string, addr string) (net.Addr, error) = func(network string, addr string) (net.Addr, error) {
	if network == "tcp" {
		return net.ResolveTCPAddr("tcp", addr)
	}
	return net.ResolveUDPAddr("udp", addr)
}

Functions

func ParseAddress

func ParseAddress(address string) (a byte, addr []byte, port []byte, err error)

ParseAddress format address x.x.x.x:xx to raw address. addr contains domain length

func ParseBytesAddress

func ParseBytesAddress(b []byte) (a byte, addr []byte, port []byte, err error)

bytes to address addr contains domain length

func ToAddress

func ToAddress(a byte, addr []byte, port []byte) string

ToAddress format raw address to x.x.x.x:xx addr contains domain length

Types

type Client

type Client struct {
	Server   string
	UserName string
	Password string
	// On cmd UDP, let server control the tcp and udp connection relationship
	TCPConn       net.Conn
	UDPConn       net.Conn
	RemoteAddress net.Addr
	TCPTimeout    int
	UDPTimeout    int
	Dst           string
}

Client is socks5 client wrapper

Example (Tcp)
package main

import (
	"io/ioutil"
	"log"
	"net"
	"net/http"

	"github.com/txthinking/socks5"
)

func ExampleServer() {
	s, err := socks5.NewClassicServer("127.0.0.1:1080", "127.0.0.1", "", "", 0, 60)
	if err != nil {
		log.Println(err)
		return
	}

	s.ListenAndServe(nil)

}

func main() {
	go ExampleServer()
	c, err := socks5.NewClient("127.0.0.1:1080", "", "", 0, 60)
	if err != nil {
		log.Println(err)
		return
	}
	client := &http.Client{
		Transport: &http.Transport{
			Dial: func(network, addr string) (net.Conn, error) {
				return c.Dial(network, addr)
			},
		},
	}
	res, err := client.Get("https://ifconfig.co")
	if err != nil {
		log.Println(err)
		return
	}
	defer res.Body.Close()
	b, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Println(err)
		return
	}
	log.Println("tcp", string(b))
}
Output:

Example (Udp)
package main

import (
	"encoding/hex"
	"log"

	"github.com/miekg/dns"
	"github.com/txthinking/socks5"
)

func ExampleServer() {
	s, err := socks5.NewClassicServer("127.0.0.1:1080", "127.0.0.1", "", "", 0, 60)
	if err != nil {
		log.Println(err)
		return
	}

	s.ListenAndServe(nil)

}

func main() {
	go ExampleServer()
	c, err := socks5.NewClient("127.0.0.1:1080", "", "", 0, 60)
	if err != nil {
		log.Println(err)
		return
	}
	conn, err := c.Dial("udp", "8.8.8.8:53")
	if err != nil {
		log.Println(err)
		return
	}
	b, err := hex.DecodeString("0001010000010000000000000a74787468696e6b696e6703636f6d0000010001")
	if err != nil {
		log.Println(err)
		return
	}
	if _, err := conn.Write(b); err != nil {
		log.Println(err)
		return
	}
	b = make([]byte, 2048)
	n, err := conn.Read(b)
	if err != nil {
		log.Println(err)
		return
	}
	m := &dns.Msg{}
	if err := m.Unpack(b[0:n]); err != nil {
		log.Println(err)
		return
	}
	log.Println(m.String())
}
Output:

func NewClient

func NewClient(addr, username, password string, tcpTimeout, udpTimeout int) (*Client, error)

This is just create a client, you need to use Dial to create conn

func (*Client) Close

func (c *Client) Close() error

func (*Client) Dial

func (c *Client) Dial(network, addr string) (net.Conn, error)

func (*Client) DialWithLocalAddr

func (c *Client) DialWithLocalAddr(network, src, dst string, remoteAddr net.Addr) (net.Conn, error)

If you want to send address that expects to use to send UDP, just assign it to src, otherwise it will send zero address. Recommend specifying the src address in a non-NAT environment, and leave it blank in other cases.

func (*Client) LocalAddr

func (c *Client) LocalAddr() net.Addr

func (*Client) Negotiate

func (c *Client) Negotiate(laddr net.Addr) error

func (*Client) Read

func (c *Client) Read(b []byte) (int, error)

func (*Client) RemoteAddr

func (c *Client) RemoteAddr() net.Addr

func (*Client) Request

func (c *Client) Request(r *Request) (*Reply, error)

func (*Client) SetDeadline

func (c *Client) SetDeadline(t time.Time) error

func (*Client) SetReadDeadline

func (c *Client) SetReadDeadline(t time.Time) error

func (*Client) SetWriteDeadline

func (c *Client) SetWriteDeadline(t time.Time) error

func (*Client) Write

func (c *Client) Write(b []byte) (int, error)

type Datagram

type Datagram struct {
	Rsv     []byte // 0x00 0x00
	Frag    byte
	Atyp    byte
	DstAddr []byte
	DstPort []byte // 2 bytes
	Data    []byte
}

Datagram is the UDP packet

func NewDatagram

func NewDatagram(atyp byte, dstaddr []byte, dstport []byte, data []byte) *Datagram

NewDatagram return datagram packet can be writed into client, dstaddr should not have domain length

func NewDatagramFromBytes

func NewDatagramFromBytes(bb []byte) (*Datagram, error)

func (*Datagram) Address

func (d *Datagram) Address() string

Address return datagram address like ip:xx

func (*Datagram) Bytes

func (d *Datagram) Bytes() []byte

Bytes return []byte

type DefaultHandle

type DefaultHandle struct {
}

DefaultHandle implements Handler interface

func (*DefaultHandle) TCPHandle

func (h *DefaultHandle) TCPHandle(s *Server, c *net.TCPConn, r *Request) error

TCPHandle auto handle request. You may prefer to do yourself.

func (*DefaultHandle) UDPHandle

func (h *DefaultHandle) UDPHandle(s *Server, addr *net.UDPAddr, d *Datagram) error

UDPHandle auto handle packet. You may prefer to do yourself.

type Handler

type Handler interface {
	// Request has not been replied yet
	TCPHandle(*Server, *net.TCPConn, *Request) error
	UDPHandle(*Server, *net.UDPAddr, *Datagram) error
}

Handler handle tcp, udp request

type NegotiationReply

type NegotiationReply struct {
	Ver    byte
	Method byte
}

NegotiationReply is the negotiation reply packet

func NewNegotiationReply

func NewNegotiationReply(method byte) *NegotiationReply

NewNegotiationReply return negotiation reply packet can be writed into client

func NewNegotiationReplyFrom

func NewNegotiationReplyFrom(r io.Reader) (*NegotiationReply, error)

NewNegotiationReplyFrom read negotiation reply packet from server

func (*NegotiationReply) WriteTo

func (r *NegotiationReply) WriteTo(w io.Writer) (int64, error)

WriteTo write negotiation reply packet into client

type NegotiationRequest

type NegotiationRequest struct {
	Ver      byte
	NMethods byte
	Methods  []byte // 1-255 bytes
}

NegotiationRequest is the negotiation reqeust packet

func NewNegotiationRequest

func NewNegotiationRequest(methods []byte) *NegotiationRequest

NewNegotiationRequest return negotiation request packet can be writed into server

func NewNegotiationRequestFrom

func NewNegotiationRequestFrom(r io.Reader) (*NegotiationRequest, error)

NewNegotiationRequestFrom read negotiation requst packet from client

func (*NegotiationRequest) WriteTo

func (r *NegotiationRequest) WriteTo(w io.Writer) (int64, error)

WriteTo write negotiation request packet into server

type Reply

type Reply struct {
	Ver  byte
	Rep  byte
	Rsv  byte // 0x00
	Atyp byte
	// CONNECT socks server's address which used to connect to dst addr
	// BIND ...
	// UDP socks server's address which used to connect to dst addr
	BndAddr []byte
	// CONNECT socks server's port which used to connect to dst addr
	// BIND ...
	// UDP socks server's port which used to connect to dst addr
	BndPort []byte // 2 bytes
}

Reply is the reply packet

func NewReply

func NewReply(rep byte, atyp byte, bndaddr []byte, bndport []byte) *Reply

NewReply return reply packet can be writed into client, bndaddr should not have domain length

func NewReplyFrom

func NewReplyFrom(r io.Reader) (*Reply, error)

NewReplyFrom read reply packet from server

func (*Reply) Address

func (r *Reply) Address() string

Address return request address like ip:xx

func (*Reply) WriteTo

func (r *Reply) WriteTo(w io.Writer) (int64, error)

WriteTo write reply packet into client

type Request

type Request struct {
	Ver     byte
	Cmd     byte
	Rsv     byte // 0x00
	Atyp    byte
	DstAddr []byte
	DstPort []byte // 2 bytes
}

Request is the request packet

func NewRequest

func NewRequest(cmd byte, atyp byte, dstaddr []byte, dstport []byte) *Request

NewRequest return request packet can be writed into server, dstaddr should not have domain length

func NewRequestFrom

func NewRequestFrom(r io.Reader) (*Request, error)

NewRequestFrom read requst packet from client

func (*Request) Address

func (r *Request) Address() string

Address return request address like ip:xx

func (*Request) Connect

func (r *Request) Connect(w io.Writer) (net.Conn, error)

Connect remote conn which u want to connect with your dialer Error or OK both replied.

func (*Request) UDP

func (r *Request) UDP(c net.Conn, serverAddr net.Addr) (net.Addr, error)

UDP remote conn which u want to connect with your dialer. Error or OK both replied. Addr can be used to associate TCP connection with the coming UDP connection.

func (*Request) WriteTo

func (r *Request) WriteTo(w io.Writer) (int64, error)

WriteTo write request packet into server

type Server

type Server struct {
	UserName          string
	Password          string
	Method            byte
	SupportedCommands []byte
	Addr              string
	ServerAddr        net.Addr
	UDPConn           *net.UDPConn
	UDPExchanges      *cache.Cache
	TCPTimeout        int
	UDPTimeout        int
	Handle            Handler
	AssociatedUDP     *cache.Cache
	UDPSrc            *cache.Cache
	RunnerGroup       *runnergroup.RunnerGroup
	// RFC: [UDP ASSOCIATE] The server MAY use this information to limit access to the association. Default false, no limit.
	LimitUDP bool
}

Server is socks5 server wrapper

Example
package main

import (
	"log"

	"github.com/txthinking/socks5"
)

func main() {
	s, err := socks5.NewClassicServer("127.0.0.1:1080", "127.0.0.1", "", "", 0, 60)
	if err != nil {
		log.Println(err)
		return
	}
	// You can pass in custom Handler
	s.ListenAndServe(nil)
	// #Output:
}
Output:

func NewClassicServer

func NewClassicServer(addr, ip, username, password string, tcpTimeout, udpTimeout int) (*Server, error)

NewClassicServer return a server which allow none method

func (*Server) GetRequest

func (s *Server) GetRequest(rw io.ReadWriter) (*Request, error)

GetRequest get request packet from client, and check command according to SupportedCommands Error replied.

func (*Server) ListenAndServe

func (s *Server) ListenAndServe(h Handler) error

Run server

func (*Server) Negotiate

func (s *Server) Negotiate(rw io.ReadWriter) error

Negotiate handle negotiate packet. This method do not handle gssapi(0x01) method now. Error or OK both replied.

func (*Server) Shutdown

func (s *Server) Shutdown() error

Stop server

type UDPExchange

type UDPExchange struct {
	ClientAddr *net.UDPAddr
	RemoteConn net.Conn
}

UDPExchange used to store client address and remote connection

type UserPassNegotiationReply

type UserPassNegotiationReply struct {
	Ver    byte
	Status byte
}

UserPassNegotiationReply is the negotiation username/password reply packet

func NewUserPassNegotiationReply

func NewUserPassNegotiationReply(status byte) *UserPassNegotiationReply

NewUserPassNegotiationReply return negotiation username password reply packet can be writed into client

func NewUserPassNegotiationReplyFrom

func NewUserPassNegotiationReplyFrom(r io.Reader) (*UserPassNegotiationReply, error)

NewUserPassNegotiationReplyFrom read user password negotiation reply packet from server

func (*UserPassNegotiationReply) WriteTo

func (r *UserPassNegotiationReply) WriteTo(w io.Writer) (int64, error)

WriteTo write negotiation username password reply packet into client

type UserPassNegotiationRequest

type UserPassNegotiationRequest struct {
	Ver    byte
	Ulen   byte
	Uname  []byte // 1-255 bytes
	Plen   byte
	Passwd []byte // 1-255 bytes
}

UserPassNegotiationRequest is the negotiation username/password reqeust packet

func NewUserPassNegotiationRequest

func NewUserPassNegotiationRequest(username []byte, password []byte) *UserPassNegotiationRequest

NewUserPassNegotiationRequest return user password negotiation request packet can be writed into server

func NewUserPassNegotiationRequestFrom

func NewUserPassNegotiationRequestFrom(r io.Reader) (*UserPassNegotiationRequest, error)

NewUserPassNegotiationRequestFrom read user password negotiation request packet from client

func (*UserPassNegotiationRequest) WriteTo

func (r *UserPassNegotiationRequest) WriteTo(w io.Writer) (int64, error)

WriteTo write user password negotiation request packet into server

Jump to

Keyboard shortcuts

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