123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- package socks
- import (
- "fmt"
- "io"
- "net"
- "strconv"
- )
- const (
- // version
- socks5Version = uint8(5)
- // commands https://tools.ietf.org/html/rfc1928#section-4
- connectCommand = uint8(1)
- bindCommand = uint8(2)
- associateCommand = uint8(3)
- // address types
- ipv4Address = uint8(1)
- fqdnAddress = uint8(3)
- ipv6Address = uint8(4)
- )
- // https://tools.ietf.org/html/rfc1928#section-6
- const (
- successReply uint8 = iota
- serverFailure
- ruleFailure
- networkUnreachable
- hostUnreachable
- connectionRefused
- ttlExpired
- commandNotSupported
- addrTypeNotSupported
- )
- // AddrSpec is used to return the target IPv4, IPv6, or a FQDN
- type AddrSpec struct {
- FQDN string
- IP net.IP
- Port int
- }
- // String gives a host version of the Address
- func (a *AddrSpec) String() string {
- if a.FQDN != "" {
- return fmt.Sprintf("%s (%s):%d", a.FQDN, a.IP, a.Port)
- }
- return fmt.Sprintf("%s:%d", a.IP, a.Port)
- }
- // Address returns a string suitable to dial; prefer returning IP-based
- // address, fallback to FQDN
- func (a AddrSpec) Address() string {
- if len(a.IP) != 0 {
- return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port))
- }
- return net.JoinHostPort(a.FQDN, strconv.Itoa(a.Port))
- }
- // Request is a SOCKS5 command with supporting field of the connection
- type Request struct {
- // Protocol version
- Version uint8
- // Requested command
- Command uint8
- // AddrSpec of the destination
- DestAddr *AddrSpec
- // reading from the connection
- bufConn io.Reader
- }
- // NewRequest creates a new request from the connection data stream
- func NewRequest(bufConn io.Reader) (*Request, error) {
- // Read the version byte
- header := []byte{0, 0, 0}
- if _, err := io.ReadAtLeast(bufConn, header, 3); err != nil {
- return nil, fmt.Errorf("Failed to get command version: %v", err)
- }
- // ensure compatibility
- if header[0] != socks5Version {
- return nil, fmt.Errorf("Unsupported command version: %v", header[0])
- }
- // Read in the destination address
- dest, err := readAddrSpec(bufConn)
- if err != nil {
- return nil, err
- }
- return &Request{
- Version: socks5Version,
- Command: header[1],
- DestAddr: dest,
- bufConn: bufConn,
- }, nil
- }
- func sendReply(w io.Writer, resp uint8, addr *AddrSpec) error {
- var addrType uint8
- var addrBody []byte
- var addrPort uint16
- switch {
- case addr == nil:
- addrType = ipv4Address
- addrBody = []byte{0, 0, 0, 0}
- addrPort = 0
- case addr.FQDN != "":
- addrType = fqdnAddress
- addrBody = append([]byte{byte(len(addr.FQDN))}, addr.FQDN...)
- addrPort = uint16(addr.Port)
- case addr.IP.To4() != nil:
- addrType = ipv4Address
- addrBody = []byte(addr.IP.To4())
- addrPort = uint16(addr.Port)
- case addr.IP.To16() != nil:
- addrType = ipv6Address
- addrBody = []byte(addr.IP.To16())
- addrPort = uint16(addr.Port)
- default:
- return fmt.Errorf("Failed to format address: %v", addr)
- }
- // Format the message
- msg := make([]byte, 6+len(addrBody))
- msg[0] = socks5Version
- msg[1] = resp
- msg[2] = 0 // Reserved
- msg[3] = addrType
- copy(msg[4:], addrBody)
- msg[4+len(addrBody)] = byte(addrPort >> 8)
- msg[4+len(addrBody)+1] = byte(addrPort & 0xff)
- // Send the message
- _, err := w.Write(msg)
- return err
- }
- // readAddrSpec is used to read AddrSpec.
- // Expects an address type byte, followed by the address and port
- func readAddrSpec(r io.Reader) (*AddrSpec, error) {
- d := &AddrSpec{}
- // Get the address type
- addrType := []byte{0}
- if _, err := r.Read(addrType); err != nil {
- return nil, err
- }
- // Handle on a per type basis
- switch addrType[0] {
- case ipv4Address:
- addr := make([]byte, 4)
- if _, err := io.ReadAtLeast(r, addr, len(addr)); err != nil {
- return nil, err
- }
- d.IP = net.IP(addr)
- case ipv6Address:
- addr := make([]byte, 16)
- if _, err := io.ReadAtLeast(r, addr, len(addr)); err != nil {
- return nil, err
- }
- d.IP = net.IP(addr)
- case fqdnAddress:
- if _, err := r.Read(addrType); err != nil {
- return nil, err
- }
- addrLen := int(addrType[0])
- fqdn := make([]byte, addrLen)
- if _, err := io.ReadAtLeast(r, fqdn, addrLen); err != nil {
- return nil, err
- }
- d.FQDN = string(fqdn)
- default:
- return nil, fmt.Errorf("Unrecognized address type")
- }
- // Read the port
- port := []byte{0, 0}
- if _, err := io.ReadAtLeast(r, port, 2); err != nil {
- return nil, err
- }
- d.Port = (int(port[0]) << 8) | int(port[1])
- return d, nil
- }
|