github.com/Cloud-Foundations/Dominator@v0.3.4/lib/net/proxy/socks.go (about)

     1  package proxy
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"net"
     7  	"strconv"
     8  )
     9  
    10  var (
    11  	errorNoIPs                = errors.New("no IP addresses")
    12  	errorNoIPv4s              = errors.New("no IPv4 addresses")
    13  	errorNotIPv4              = errors.New("not an IPv4 address")
    14  	errorShortRead            = errors.New("short read")
    15  	errorShortWrite           = errors.New("short write")
    16  	errorUnsupportedTransport = errors.New("unsupported transport")
    17  
    18  	errorRequestRejected     = errors.New("request rejected")
    19  	errorNoIdentd            = errors.New("no client identd")
    20  	errorUserIdNotConfirmed  = errors.New("user ID not confirmed")
    21  	errorUnknownResponseCode = errors.New("unknown response code")
    22  )
    23  
    24  type socksDialer struct {
    25  	dialer       *net.Dialer
    26  	proxyAddress string
    27  	proxyDNS     bool
    28  	udpSupported bool
    29  }
    30  
    31  func (d *socksDialer) Dial(network, address string) (net.Conn, error) {
    32  	switch network {
    33  	case "tcp":
    34  		return d.dialTCP(address)
    35  	case "udp":
    36  	}
    37  	return nil, errorUnsupportedTransport
    38  }
    39  
    40  func (d *socksDialer) dialTCP(address string) (net.Conn, error) {
    41  	host, portStr, err := net.SplitHostPort(address)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  	port, err := strconv.ParseUint(portStr, 10, 16)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	var request []byte
    50  	if ip := net.ParseIP(host); ip != nil {
    51  		if request, err = makeSocks4IpRequest(ip, uint16(port)); err != nil {
    52  			return nil, err
    53  		}
    54  	} else if d.proxyDNS {
    55  		if request, err = makeSocks4aRequest(host, uint16(port)); err != nil {
    56  			return nil, err
    57  		}
    58  	} else {
    59  		if request, err = makeSocks4Request(host, uint16(port)); err != nil {
    60  			return nil, err
    61  		}
    62  	}
    63  	if conn, err := d.dialer.Dial("tcp", d.proxyAddress); err != nil {
    64  		return nil, err
    65  	} else {
    66  		if nWritten, err := conn.Write(request); err != nil {
    67  			conn.Close()
    68  			return nil, err
    69  		} else if nWritten < len(request) {
    70  			conn.Close()
    71  			return nil, errorShortWrite
    72  		}
    73  		if err := readSocks4Response(conn); err != nil {
    74  			conn.Close()
    75  			return nil, err
    76  		}
    77  		return conn, nil
    78  	}
    79  }
    80  
    81  func makeSocks4aRequest(host string, port uint16) ([]byte, error) {
    82  	request := make([]byte, 10+len(host))
    83  	request[0] = 0x04
    84  	request[1] = 0x01
    85  	request[2] = byte(port >> 8)
    86  	request[3] = byte(port & 0xff)
    87  	request[7] = 0xff
    88  	copy(request[9:], host)
    89  	return request, nil
    90  }
    91  
    92  func makeSocks4IpRequest(ip net.IP, port uint16) ([]byte, error) {
    93  	if ip = ip.To4(); ip == nil {
    94  		return nil, errorNoIPv4s
    95  	}
    96  	request := make([]byte, 9)
    97  	request[0] = 0x04
    98  	request[1] = 0x01
    99  	request[2] = byte(port >> 8)
   100  	request[3] = byte(port & 0xff)
   101  	request[4] = ip[0]
   102  	request[5] = ip[1]
   103  	request[6] = ip[2]
   104  	request[7] = ip[3]
   105  	return request, nil
   106  }
   107  
   108  func makeSocks4Request(host string, port uint16) ([]byte, error) {
   109  	ips, err := net.LookupIP(host)
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	if len(ips) < 1 {
   114  		return nil, errorNoIPs
   115  	}
   116  	var ip4 net.IP
   117  	for _, ip := range ips {
   118  		if ip4 = ip.To4(); ip4 != nil {
   119  			break
   120  		}
   121  	}
   122  	if len(ip4) != 4 {
   123  		return nil, errorNoIPv4s
   124  	}
   125  	return makeSocks4IpRequest(ip4, port)
   126  }
   127  
   128  func readSocks4Response(reader io.Reader) error {
   129  	response := make([]byte, 8)
   130  	if nRead, err := reader.Read(response); err != nil {
   131  		return err
   132  	} else if nRead < len(response) {
   133  		return errorShortRead
   134  	} else {
   135  		switch response[1] {
   136  		case 0x5a:
   137  			return nil
   138  		case 0x5b:
   139  			return errorRequestRejected
   140  		case 0x5c:
   141  			return errorNoIdentd
   142  		case 0x5d:
   143  			return errorUserIdNotConfirmed
   144  		default:
   145  			return errorUnknownResponseCode
   146  		}
   147  	}
   148  }