github.com/AntonOrnatskyi/goproxy@v0.0.0-20190205095733-4526a9fa18b4/utils/socks/client.go (about)

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