github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/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  	closeConn := &conn
    76  	defer func() {
    77  		if closeConn != nil {
    78  			(*closeConn).Close()
    79  		}
    80  	}()
    81  
    82  	host, portStr, err := net.SplitHostPort(addr)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	port, err := strconv.Atoi(portStr)
    88  	if err != nil {
    89  		return nil, errors.New("proxy: failed to parse port number: " + portStr)
    90  	}
    91  	if port < 1 || port > 0xffff {
    92  		return nil, errors.New("proxy: port number out of range: " + portStr)
    93  	}
    94  
    95  	// the size here is just an estimate
    96  	buf := make([]byte, 0, 6+len(host))
    97  
    98  	buf = append(buf, socks5Version)
    99  	if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
   100  		buf = append(buf, 2 /* num auth methods */, socks5AuthNone, socks5AuthPassword)
   101  	} else {
   102  		buf = append(buf, 1 /* num auth methods */, socks5AuthNone)
   103  	}
   104  
   105  	if _, err := conn.Write(buf); err != nil {
   106  		return nil, errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
   107  	}
   108  
   109  	if _, err := io.ReadFull(conn, buf[:2]); err != nil {
   110  		return nil, errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
   111  	}
   112  	if buf[0] != 5 {
   113  		return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
   114  	}
   115  	if buf[1] == 0xff {
   116  		return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
   117  	}
   118  
   119  	if buf[1] == socks5AuthPassword {
   120  		buf = buf[:0]
   121  		buf = append(buf, 1 /* password protocol version */)
   122  		buf = append(buf, uint8(len(s.user)))
   123  		buf = append(buf, s.user...)
   124  		buf = append(buf, uint8(len(s.password)))
   125  		buf = append(buf, s.password...)
   126  
   127  		if _, err := conn.Write(buf); err != nil {
   128  			return nil, errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
   129  		}
   130  
   131  		if _, err := io.ReadFull(conn, buf[:2]); err != nil {
   132  			return nil, errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
   133  		}
   134  
   135  		if buf[1] != 0 {
   136  			return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
   137  		}
   138  	}
   139  
   140  	buf = buf[:0]
   141  	buf = append(buf, socks5Version, socks5Connect, 0 /* reserved */)
   142  
   143  	if ip := net.ParseIP(host); ip != nil {
   144  		if ip4 := ip.To4(); ip4 != nil {
   145  			buf = append(buf, socks5IP4)
   146  			ip = ip4
   147  		} else {
   148  			buf = append(buf, socks5IP6)
   149  		}
   150  		buf = append(buf, ip...)
   151  	} else {
   152  		if len(host) > 255 {
   153  			return nil, errors.New("proxy: destination hostname too long: " + host)
   154  		}
   155  		buf = append(buf, socks5Domain)
   156  		buf = append(buf, byte(len(host)))
   157  		buf = append(buf, host...)
   158  	}
   159  	buf = append(buf, byte(port>>8), byte(port))
   160  
   161  	if _, err := conn.Write(buf); err != nil {
   162  		return nil, errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
   163  	}
   164  
   165  	if _, err := io.ReadFull(conn, buf[:4]); err != nil {
   166  		return nil, errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
   167  	}
   168  
   169  	failure := "unknown error"
   170  	if int(buf[1]) < len(socks5Errors) {
   171  		failure = socks5Errors[buf[1]]
   172  	}
   173  
   174  	if len(failure) > 0 {
   175  		return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
   176  	}
   177  
   178  	bytesToDiscard := 0
   179  	switch buf[3] {
   180  	case socks5IP4:
   181  		bytesToDiscard = net.IPv4len
   182  	case socks5IP6:
   183  		bytesToDiscard = net.IPv6len
   184  	case socks5Domain:
   185  		_, err := io.ReadFull(conn, buf[:1])
   186  		if err != nil {
   187  			return nil, errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
   188  		}
   189  		bytesToDiscard = int(buf[0])
   190  	default:
   191  		return nil, errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
   192  	}
   193  
   194  	if cap(buf) < bytesToDiscard {
   195  		buf = make([]byte, bytesToDiscard)
   196  	} else {
   197  		buf = buf[:bytesToDiscard]
   198  	}
   199  	if _, err := io.ReadFull(conn, buf); err != nil {
   200  		return nil, errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
   201  	}
   202  
   203  	// Also need to discard the port number
   204  	if _, err := io.ReadFull(conn, buf[:2]); err != nil {
   205  		return nil, errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
   206  	}
   207  
   208  	closeConn = nil
   209  	return conn, nil
   210  }