github.com/btcsuite/btcd@v0.24.0/connmgr/tor.go (about)

     1  // Copyright (c) 2013-2016 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package connmgr
     6  
     7  import (
     8  	"encoding/binary"
     9  	"errors"
    10  	"net"
    11  )
    12  
    13  const (
    14  	torSucceeded         = 0x00
    15  	torGeneralError      = 0x01
    16  	torNotAllowed        = 0x02
    17  	torNetUnreachable    = 0x03
    18  	torHostUnreachable   = 0x04
    19  	torConnectionRefused = 0x05
    20  	torTTLExpired        = 0x06
    21  	torCmdNotSupported   = 0x07
    22  	torAddrNotSupported  = 0x08
    23  )
    24  
    25  var (
    26  	// ErrTorInvalidAddressResponse indicates an invalid address was
    27  	// returned by the Tor DNS resolver.
    28  	ErrTorInvalidAddressResponse = errors.New("invalid address response")
    29  
    30  	// ErrTorInvalidProxyResponse indicates the Tor proxy returned a
    31  	// response in an unexpected format.
    32  	ErrTorInvalidProxyResponse = errors.New("invalid proxy response")
    33  
    34  	// ErrTorUnrecognizedAuthMethod indicates the authentication method
    35  	// provided is not recognized.
    36  	ErrTorUnrecognizedAuthMethod = errors.New("invalid proxy authentication method")
    37  
    38  	torStatusErrors = map[byte]error{
    39  		torSucceeded:         errors.New("tor succeeded"),
    40  		torGeneralError:      errors.New("tor general error"),
    41  		torNotAllowed:        errors.New("tor not allowed"),
    42  		torNetUnreachable:    errors.New("tor network is unreachable"),
    43  		torHostUnreachable:   errors.New("tor host is unreachable"),
    44  		torConnectionRefused: errors.New("tor connection refused"),
    45  		torTTLExpired:        errors.New("tor TTL expired"),
    46  		torCmdNotSupported:   errors.New("tor command not supported"),
    47  		torAddrNotSupported:  errors.New("tor address type not supported"),
    48  	}
    49  )
    50  
    51  // TorLookupIP uses Tor to resolve DNS via the SOCKS extension they provide for
    52  // resolution over the Tor network. Tor itself doesn't support ipv6 so this
    53  // doesn't either.
    54  func TorLookupIP(host, proxy string) ([]net.IP, error) {
    55  	conn, err := net.Dial("tcp", proxy)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  	defer conn.Close()
    60  
    61  	buf := []byte{'\x05', '\x01', '\x00'}
    62  	_, err = conn.Write(buf)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  
    67  	buf = make([]byte, 2)
    68  	_, err = conn.Read(buf)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  	if buf[0] != '\x05' {
    73  		return nil, ErrTorInvalidProxyResponse
    74  	}
    75  	if buf[1] != '\x00' {
    76  		return nil, ErrTorUnrecognizedAuthMethod
    77  	}
    78  
    79  	buf = make([]byte, 7+len(host))
    80  	buf[0] = 5      // protocol version
    81  	buf[1] = '\xF0' // Tor Resolve
    82  	buf[2] = 0      // reserved
    83  	buf[3] = 3      // Tor Resolve
    84  	buf[4] = byte(len(host))
    85  	copy(buf[5:], host)
    86  	buf[5+len(host)] = 0 // Port 0
    87  
    88  	_, err = conn.Write(buf)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	buf = make([]byte, 4)
    94  	_, err = conn.Read(buf)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	if buf[0] != 5 {
    99  		return nil, ErrTorInvalidProxyResponse
   100  	}
   101  	if buf[1] != 0 {
   102  		if int(buf[1]) >= len(torStatusErrors) {
   103  			return nil, ErrTorInvalidProxyResponse
   104  		} else if err := torStatusErrors[buf[1]]; err != nil {
   105  			return nil, err
   106  		}
   107  		return nil, ErrTorInvalidProxyResponse
   108  	}
   109  	if buf[3] != 1 {
   110  		err := torStatusErrors[torGeneralError]
   111  		return nil, err
   112  	}
   113  
   114  	buf = make([]byte, 4)
   115  	bytes, err := conn.Read(buf)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  	if bytes != 4 {
   120  		return nil, ErrTorInvalidAddressResponse
   121  	}
   122  
   123  	r := binary.BigEndian.Uint32(buf)
   124  
   125  	addr := make([]net.IP, 1)
   126  	addr[0] = net.IPv4(byte(r>>24), byte(r>>16), byte(r>>8), byte(r))
   127  
   128  	return addr, nil
   129  }