golang.org/toolchain@v0.0.1-go1.9rc2.windows-amd64/src/vendor/golang_org/x/net/proxy/socks5.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package proxy
     6  
     7  import (
     8  	"errors"
     9  	"io"
    10  	"net"
    11  	"strconv"
    12  )
    13  
    14  // SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address
    15  // with an optional username and password. See RFC 1928.
    16  func SOCKS5(network, addr string, auth *Auth, forward Dialer) (Dialer, error) {
    17  	s := &socks5{
    18  		network: network,
    19  		addr:    addr,
    20  		forward: forward,
    21  	}
    22  	if auth != nil {
    23  		s.user = auth.User
    24  		s.password = auth.Password
    25  	}
    26  
    27  	return s, nil
    28  }
    29  
    30  type socks5 struct {
    31  	user, password string
    32  	network, addr  string
    33  	forward        Dialer
    34  }
    35  
    36  const socks5Version = 5
    37  
    38  const (
    39  	socks5AuthNone     = 0
    40  	socks5AuthPassword = 2
    41  )
    42  
    43  const socks5Connect = 1
    44  
    45  const (
    46  	socks5IP4    = 1
    47  	socks5Domain = 3
    48  	socks5IP6    = 4
    49  )
    50  
    51  var socks5Errors = []string{
    52  	"",
    53  	"general failure",
    54  	"connection forbidden",
    55  	"network unreachable",
    56  	"host unreachable",
    57  	"connection refused",
    58  	"TTL expired",
    59  	"command not supported",
    60  	"address type not supported",
    61  }
    62  
    63  // Dial connects to the address addr on the network net via the SOCKS5 proxy.
    64  func (s *socks5) Dial(network, addr string) (net.Conn, error) {
    65  	switch network {
    66  	case "tcp", "tcp6", "tcp4":
    67  	default:
    68  		return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network)
    69  	}
    70  
    71  	conn, err := s.forward.Dial(s.network, s.addr)
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  	if err := s.connect(conn, addr); err != nil {
    76  		conn.Close()
    77  		return nil, err
    78  	}
    79  	return conn, nil
    80  }
    81  
    82  // connect takes an existing connection to a socks5 proxy server,
    83  // and commands the server to extend that connection to target,
    84  // which must be a canonical address with a host and port.
    85  func (s *socks5) connect(conn net.Conn, target string) error {
    86  	host, portStr, err := net.SplitHostPort(target)
    87  	if err != nil {
    88  		return err
    89  	}
    90  
    91  	port, err := strconv.Atoi(portStr)
    92  	if err != nil {
    93  		return errors.New("proxy: failed to parse port number: " + portStr)
    94  	}
    95  	if port < 1 || port > 0xffff {
    96  		return errors.New("proxy: port number out of range: " + portStr)
    97  	}
    98  
    99  	// the size here is just an estimate
   100  	buf := make([]byte, 0, 6+len(host))
   101  
   102  	buf = append(buf, socks5Version)
   103  	if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
   104  		buf = append(buf, 2 /* num auth methods */, socks5AuthNone, socks5AuthPassword)
   105  	} else {
   106  		buf = append(buf, 1 /* num auth methods */, socks5AuthNone)
   107  	}
   108  
   109  	if _, err := conn.Write(buf); err != nil {
   110  		return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
   111  	}
   112  
   113  	if _, err := io.ReadFull(conn, buf[:2]); err != nil {
   114  		return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
   115  	}
   116  	if buf[0] != 5 {
   117  		return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
   118  	}
   119  	if buf[1] == 0xff {
   120  		return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
   121  	}
   122  
   123  	if buf[1] == socks5AuthPassword {
   124  		buf = buf[:0]
   125  		buf = append(buf, 1 /* password protocol version */)
   126  		buf = append(buf, uint8(len(s.user)))
   127  		buf = append(buf, s.user...)
   128  		buf = append(buf, uint8(len(s.password)))
   129  		buf = append(buf, s.password...)
   130  
   131  		if _, err := conn.Write(buf); err != nil {
   132  			return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
   133  		}
   134  
   135  		if _, err := io.ReadFull(conn, buf[:2]); err != nil {
   136  			return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
   137  		}
   138  
   139  		if buf[1] != 0 {
   140  			return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
   141  		}
   142  	}
   143  
   144  	buf = buf[:0]
   145  	buf = append(buf, socks5Version, socks5Connect, 0 /* reserved */)
   146  
   147  	if ip := net.ParseIP(host); ip != nil {
   148  		if ip4 := ip.To4(); ip4 != nil {
   149  			buf = append(buf, socks5IP4)
   150  			ip = ip4
   151  		} else {
   152  			buf = append(buf, socks5IP6)
   153  		}
   154  		buf = append(buf, ip...)
   155  	} else {
   156  		if len(host) > 255 {
   157  			return errors.New("proxy: destination hostname too long: " + host)
   158  		}
   159  		buf = append(buf, socks5Domain)
   160  		buf = append(buf, byte(len(host)))
   161  		buf = append(buf, host...)
   162  	}
   163  	buf = append(buf, byte(port>>8), byte(port))
   164  
   165  	if _, err := conn.Write(buf); err != nil {
   166  		return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
   167  	}
   168  
   169  	if _, err := io.ReadFull(conn, buf[:4]); err != nil {
   170  		return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
   171  	}
   172  
   173  	failure := "unknown error"
   174  	if int(buf[1]) < len(socks5Errors) {
   175  		failure = socks5Errors[buf[1]]
   176  	}
   177  
   178  	if len(failure) > 0 {
   179  		return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
   180  	}
   181  
   182  	bytesToDiscard := 0
   183  	switch buf[3] {
   184  	case socks5IP4:
   185  		bytesToDiscard = net.IPv4len
   186  	case socks5IP6:
   187  		bytesToDiscard = net.IPv6len
   188  	case socks5Domain:
   189  		_, err := io.ReadFull(conn, buf[:1])
   190  		if err != nil {
   191  			return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
   192  		}
   193  		bytesToDiscard = int(buf[0])
   194  	default:
   195  		return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
   196  	}
   197  
   198  	if cap(buf) < bytesToDiscard {
   199  		buf = make([]byte, bytesToDiscard)
   200  	} else {
   201  		buf = buf[:bytesToDiscard]
   202  	}
   203  	if _, err := io.ReadFull(conn, buf); err != nil {
   204  		return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
   205  	}
   206  
   207  	// Also need to discard the port number
   208  	if _, err := io.ReadFull(conn, buf[:2]); err != nil {
   209  		return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
   210  	}
   211  
   212  	return nil
   213  }