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