github.com/AntonOrnatskyi/goproxy@v0.0.0-20190205095733-4526a9fa18b4/core/proxy/client/socks5/socks5.go (about)

     1  package socks5
     2  
     3  import (
     4  	"encoding/binary"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"net"
     9  	"strconv"
    10  	"time"
    11  
    12  	socks5c "github.com/AntonOrnatskyi/goproxy/core/lib/socks5"
    13  )
    14  
    15  type Dialer struct {
    16  	timeout          time.Duration
    17  	usernamePassword *socks5c.UsernamePassword
    18  }
    19  
    20  // NewDialer returns a new Dialer that dials through the provided
    21  // proxy server's network and address.
    22  func NewDialer(auth *socks5c.UsernamePassword, timeout time.Duration) *Dialer {
    23  	if auth != nil && auth.Password == "" && auth.Username == "" {
    24  		auth = nil
    25  	}
    26  	return &Dialer{
    27  		usernamePassword: auth,
    28  		timeout:          timeout,
    29  	}
    30  }
    31  
    32  func (d *Dialer) DialConn(conn *net.Conn, network, addr string) (err error) {
    33  	client := NewClientConn(conn, network, addr, d.timeout, d.usernamePassword, nil)
    34  	err = client._Handshake()
    35  	return
    36  }
    37  
    38  type ClientConn struct {
    39  	user     string
    40  	password string
    41  	conn     *net.Conn
    42  	header   []byte
    43  	timeout  time.Duration
    44  	addr     string
    45  	network  string
    46  	udpAddr  string
    47  }
    48  
    49  // SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address
    50  // with an optional username and password. See RFC 1928 and RFC 1929.
    51  // target must be a canonical address with a host and port.
    52  // network : tcp udp
    53  func NewClientConn(conn *net.Conn, network, target string, timeout time.Duration, auth *socks5c.UsernamePassword, header []byte) *ClientConn {
    54  	s := &ClientConn{
    55  		conn:    conn,
    56  		network: network,
    57  		timeout: timeout,
    58  	}
    59  	if auth != nil {
    60  		s.user = auth.Username
    61  		s.password = auth.Password
    62  	}
    63  	if header != nil && len(header) > 0 {
    64  		s.header = header
    65  	}
    66  	if network == "udp" && target == "" {
    67  		target = "0.0.0.0:1"
    68  	}
    69  	s.addr = target
    70  	return s
    71  }
    72  
    73  // connect takes an existing connection to a socks5 proxy server,
    74  // and commands the server to extend that connection to target,
    75  // which must be a canonical address with a host and port.
    76  func (s *ClientConn) _Handshake() error {
    77  	host, portStr, err := net.SplitHostPort(s.addr)
    78  	if err != nil {
    79  		return err
    80  	}
    81  	port, err := strconv.Atoi(portStr)
    82  	if err != nil {
    83  		return errors.New("proxy: failed to parse port number: " + portStr)
    84  	}
    85  	if port < 1 || port > 0xffff {
    86  		return errors.New("proxy: port number out of range: " + portStr)
    87  	}
    88  
    89  	if err := s.auth(host); err != nil {
    90  		return err
    91  	}
    92  	buf := []byte{}
    93  	if s.network == "tcp" {
    94  		buf = append(buf, socks5c.VERSION_V5, socks5c.CMD_CONNECT, 0 /* reserved */)
    95  
    96  	} else {
    97  		buf = append(buf, socks5c.VERSION_V5, socks5c.CMD_ASSOCIATE, 0 /* reserved */)
    98  	}
    99  	if ip := net.ParseIP(host); ip != nil {
   100  		if ip4 := ip.To4(); ip4 != nil {
   101  			buf = append(buf, socks5c.ATYP_IPV4)
   102  			ip = ip4
   103  		} else {
   104  			buf = append(buf, socks5c.ATYP_IPV6)
   105  		}
   106  		buf = append(buf, ip...)
   107  	} else {
   108  		if len(host) > 255 {
   109  			return errors.New("proxy: destination host name too long: " + host)
   110  		}
   111  		buf = append(buf, socks5c.ATYP_DOMAIN)
   112  		buf = append(buf, byte(len(host)))
   113  		buf = append(buf, host...)
   114  	}
   115  	buf = append(buf, byte(port>>8), byte(port))
   116  	(*s.conn).SetDeadline(time.Now().Add(s.timeout))
   117  	if _, err := (*s.conn).Write(buf); err != nil {
   118  		return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
   119  	}
   120  	(*s.conn).SetDeadline(time.Time{})
   121  	(*s.conn).SetDeadline(time.Now().Add(s.timeout))
   122  	if _, err := io.ReadFull((*s.conn), buf[:4]); err != nil {
   123  		return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
   124  	}
   125  	(*s.conn).SetDeadline(time.Time{})
   126  	failure := "unknown error"
   127  	if int(buf[1]) < len(socks5c.Socks5Errors) {
   128  		failure = socks5c.Socks5Errors[buf[1]]
   129  	}
   130  
   131  	if len(failure) > 0 {
   132  		return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
   133  	}
   134  
   135  	bytesToDiscard := 0
   136  	switch buf[3] {
   137  	case socks5c.ATYP_IPV4:
   138  		bytesToDiscard = net.IPv4len
   139  	case socks5c.ATYP_IPV6:
   140  		bytesToDiscard = net.IPv6len
   141  	case socks5c.ATYP_DOMAIN:
   142  		(*s.conn).SetDeadline(time.Now().Add(s.timeout))
   143  		_, err := io.ReadFull((*s.conn), buf[:1])
   144  		(*s.conn).SetDeadline(time.Time{})
   145  		if err != nil {
   146  			return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
   147  		}
   148  		bytesToDiscard = int(buf[0])
   149  	default:
   150  		return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
   151  	}
   152  
   153  	if cap(buf) < bytesToDiscard {
   154  		buf = make([]byte, bytesToDiscard)
   155  	} else {
   156  		buf = buf[:bytesToDiscard]
   157  	}
   158  	(*s.conn).SetDeadline(time.Now().Add(s.timeout))
   159  	if _, err := io.ReadFull((*s.conn), buf); err != nil {
   160  		return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
   161  	}
   162  	(*s.conn).SetDeadline(time.Time{})
   163  	var ip net.IP
   164  	ip = buf
   165  	ipStr := ""
   166  	if bytesToDiscard == net.IPv4len || bytesToDiscard == net.IPv6len {
   167  		if ipv4 := ip.To4(); ipv4 != nil {
   168  			ipStr = ipv4.String()
   169  		} else {
   170  			ipStr = ip.To16().String()
   171  		}
   172  	}
   173  	//log.Printf("%v", ipStr)
   174  	// Also need to discard the port number
   175  	(*s.conn).SetDeadline(time.Now().Add(s.timeout))
   176  	if _, err := io.ReadFull((*s.conn), buf[:2]); err != nil {
   177  		return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
   178  	}
   179  	p := binary.BigEndian.Uint16([]byte{buf[0], buf[1]})
   180  	//log.Printf("%v", p)
   181  	s.udpAddr = net.JoinHostPort(ipStr, fmt.Sprintf("%d", p))
   182  	//log.Printf("%v", s.udpAddr)
   183  	(*s.conn).SetDeadline(time.Time{})
   184  	return nil
   185  }
   186  func (s *ClientConn) SendUDP(data []byte, addr string) (respData []byte, err error) {
   187  
   188  	c, err := net.DialTimeout("udp", s.udpAddr, s.timeout)
   189  	if err != nil {
   190  		return
   191  	}
   192  	conn := c.(*net.UDPConn)
   193  
   194  	p := socks5c.NewPacketUDP()
   195  	p.Build(addr, data)
   196  	conn.SetDeadline(time.Now().Add(s.timeout))
   197  	conn.Write(p.Bytes())
   198  	conn.SetDeadline(time.Time{})
   199  
   200  	buf := make([]byte, 1024)
   201  	conn.SetDeadline(time.Now().Add(s.timeout))
   202  	n, _, err := conn.ReadFrom(buf)
   203  	conn.SetDeadline(time.Time{})
   204  	if err != nil {
   205  		return
   206  	}
   207  	respData = buf[:n]
   208  	return
   209  }
   210  func (s *ClientConn) auth(host string) error {
   211  
   212  	// the size here is just an estimate
   213  	buf := make([]byte, 0, 6+len(host))
   214  
   215  	buf = append(buf, socks5c.VERSION_V5)
   216  	if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
   217  		buf = append(buf, 2 /* num auth methods */, socks5c.Method_NO_AUTH, socks5c.Method_USER_PASS)
   218  	} else {
   219  		buf = append(buf, 1 /* num auth methods */, socks5c.Method_NO_AUTH)
   220  	}
   221  	(*s.conn).SetDeadline(time.Now().Add(s.timeout))
   222  	if _, err := (*s.conn).Write(buf); err != nil {
   223  		return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
   224  	}
   225  	(*s.conn).SetDeadline(time.Time{})
   226  
   227  	(*s.conn).SetDeadline(time.Now().Add(s.timeout))
   228  	if _, err := io.ReadFull((*s.conn), buf[:2]); err != nil {
   229  		return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
   230  	}
   231  	(*s.conn).SetDeadline(time.Time{})
   232  
   233  	if buf[0] != 5 {
   234  		return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
   235  	}
   236  	if buf[1] == 0xff {
   237  		return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
   238  	}
   239  
   240  	// See RFC 1929
   241  	if buf[1] == socks5c.Method_USER_PASS {
   242  		buf = buf[:0]
   243  		buf = append(buf, 1 /* password protocol version */)
   244  		buf = append(buf, uint8(len(s.user)))
   245  		buf = append(buf, s.user...)
   246  		buf = append(buf, uint8(len(s.password)))
   247  		buf = append(buf, s.password...)
   248  		(*s.conn).SetDeadline(time.Now().Add(s.timeout))
   249  		if _, err := (*s.conn).Write(buf); err != nil {
   250  			return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
   251  		}
   252  		(*s.conn).SetDeadline(time.Time{})
   253  		(*s.conn).SetDeadline(time.Now().Add(s.timeout))
   254  		if _, err := io.ReadFull((*s.conn), buf[:2]); err != nil {
   255  			return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
   256  		}
   257  		(*s.conn).SetDeadline(time.Time{})
   258  		if buf[1] != 0 {
   259  			return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
   260  		}
   261  	}
   262  	return nil
   263  }