mirror of
https://github.com/muun/recovery.git
synced 2025-11-11 06:20:16 -05:00
Release v0.1.0
This commit is contained in:
21
vendor/github.com/lightningnetwork/lnd/tor/README.md
generated
vendored
Normal file
21
vendor/github.com/lightningnetwork/lnd/tor/README.md
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
tor
|
||||
===
|
||||
|
||||
The tor package contains utility functions that allow for interacting with the
|
||||
Tor daemon. So far, supported functions include:
|
||||
|
||||
* Routing all traffic over Tor's exposed SOCKS5 proxy.
|
||||
* Routing DNS queries over Tor (A, AAAA, SRV).
|
||||
* Limited Tor Control functionality (synchronous messages only). So far, this
|
||||
includes:
|
||||
* Support for SAFECOOKIE authentication only as a sane default.
|
||||
* Creating v2 onion services.
|
||||
|
||||
In the future, the Tor Control functionality will be extended to support v3
|
||||
onion services, asynchronous messages, etc.
|
||||
|
||||
## Installation and Updating
|
||||
|
||||
```bash
|
||||
$ go get -u github.com/lightningnetwork/lnd/tor
|
||||
```
|
||||
530
vendor/github.com/lightningnetwork/lnd/tor/controller.go
generated
vendored
Normal file
530
vendor/github.com/lightningnetwork/lnd/tor/controller.go
generated
vendored
Normal file
@@ -0,0 +1,530 @@
|
||||
package tor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/textproto"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
const (
|
||||
// success is the Tor Control response code representing a successful
|
||||
// request.
|
||||
success = 250
|
||||
|
||||
// nonceLen is the length of a nonce generated by either the controller
|
||||
// or the Tor server
|
||||
nonceLen = 32
|
||||
|
||||
// cookieLen is the length of the authentication cookie.
|
||||
cookieLen = 32
|
||||
|
||||
// ProtocolInfoVersion is the `protocolinfo` version currently supported
|
||||
// by the Tor server.
|
||||
ProtocolInfoVersion = 1
|
||||
|
||||
// MinTorVersion is the minimum supported version that the Tor server
|
||||
// must be running on. This is needed in order to create v3 onion
|
||||
// services through Tor's control port.
|
||||
MinTorVersion = "0.3.3.6"
|
||||
)
|
||||
|
||||
var (
|
||||
// serverKey is the key used when computing the HMAC-SHA256 of a message
|
||||
// from the server.
|
||||
serverKey = []byte("Tor safe cookie authentication " +
|
||||
"server-to-controller hash")
|
||||
|
||||
// controllerKey is the key used when computing the HMAC-SHA256 of a
|
||||
// message from the controller.
|
||||
controllerKey = []byte("Tor safe cookie authentication " +
|
||||
"controller-to-server hash")
|
||||
)
|
||||
|
||||
// Controller is an implementation of the Tor Control protocol. This is used in
|
||||
// order to communicate with a Tor server. Its only supported method of
|
||||
// authentication is the SAFECOOKIE method.
|
||||
//
|
||||
// NOTE: The connection to the Tor server must be authenticated before
|
||||
// proceeding to send commands. Otherwise, the connection will be closed.
|
||||
//
|
||||
// TODO:
|
||||
// * if adding support for more commands, extend this with a command queue?
|
||||
// * place under sub-package?
|
||||
// * support async replies from the server
|
||||
type Controller struct {
|
||||
// started is used atomically in order to prevent multiple calls to
|
||||
// Start.
|
||||
started int32
|
||||
|
||||
// stopped is used atomically in order to prevent multiple calls to
|
||||
// Stop.
|
||||
stopped int32
|
||||
|
||||
// conn is the underlying connection between the controller and the
|
||||
// Tor server. It provides read and write methods to simplify the
|
||||
// text-based messages within the connection.
|
||||
conn *textproto.Conn
|
||||
|
||||
// controlAddr is the host:port the Tor server is listening locally for
|
||||
// controller connections on.
|
||||
controlAddr string
|
||||
|
||||
// version is the current version of the Tor server.
|
||||
version string
|
||||
}
|
||||
|
||||
// NewController returns a new Tor controller that will be able to interact with
|
||||
// a Tor server.
|
||||
func NewController(controlAddr string) *Controller {
|
||||
return &Controller{controlAddr: controlAddr}
|
||||
}
|
||||
|
||||
// Start establishes and authenticates the connection between the controller and
|
||||
// a Tor server. Once done, the controller will be able to send commands and
|
||||
// expect responses.
|
||||
func (c *Controller) Start() error {
|
||||
if !atomic.CompareAndSwapInt32(&c.started, 0, 1) {
|
||||
return nil
|
||||
}
|
||||
|
||||
conn, err := textproto.Dial("tcp", c.controlAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to connect to Tor server: %v", err)
|
||||
}
|
||||
|
||||
c.conn = conn
|
||||
|
||||
return c.authenticate()
|
||||
}
|
||||
|
||||
// Stop closes the connection between the controller and the Tor server.
|
||||
func (c *Controller) Stop() error {
|
||||
if !atomic.CompareAndSwapInt32(&c.stopped, 0, 1) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
// sendCommand sends a command to the Tor server and returns its response, as a
|
||||
// single space-delimited string, and code.
|
||||
func (c *Controller) sendCommand(command string) (int, string, error) {
|
||||
if err := c.conn.Writer.PrintfLine(command); err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
// We'll use ReadResponse as it has built-in support for multi-line
|
||||
// text protocol responses.
|
||||
code, reply, err := c.conn.Reader.ReadResponse(success)
|
||||
if err != nil {
|
||||
return code, reply, err
|
||||
}
|
||||
|
||||
return code, reply, nil
|
||||
}
|
||||
|
||||
// parseTorReply parses the reply from the Tor server after receiving a command
|
||||
// from a controller. This will parse the relevant reply parameters into a map
|
||||
// of keys and values.
|
||||
func parseTorReply(reply string) map[string]string {
|
||||
params := make(map[string]string)
|
||||
|
||||
// Replies can either span single or multiple lines, so we'll default
|
||||
// to stripping whitespace and newlines in order to retrieve the
|
||||
// individual contents of it. The -1 indicates that we want this to span
|
||||
// across all instances of a newline.
|
||||
contents := strings.Split(strings.Replace(reply, "\n", " ", -1), " ")
|
||||
for _, content := range contents {
|
||||
// Each parameter within the reply should be of the form
|
||||
// "KEY=VALUE". If the parameter doesn't contain "=", then we
|
||||
// can assume it does not provide any other relevant information
|
||||
// already known.
|
||||
keyValue := strings.SplitN(content, "=", 2)
|
||||
if len(keyValue) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := keyValue[0]
|
||||
value := keyValue[1]
|
||||
params[key] = value
|
||||
}
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
// authenticate authenticates the connection between the controller and the
|
||||
// Tor server using the SAFECOOKIE or NULL authentication method.
|
||||
func (c *Controller) authenticate() error {
|
||||
// Before proceeding to authenticate the connection, we'll retrieve
|
||||
// the authentication cookie of the Tor server. This will be used
|
||||
// throughout the authentication routine. We do this before as once the
|
||||
// authentication routine has begun, it is not possible to retrieve it
|
||||
// mid-way.
|
||||
cookie, err := c.getAuthCookie()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to retrieve authentication cookie: "+
|
||||
"%v", err)
|
||||
}
|
||||
|
||||
// If cookie is empty and there's no error, we have a NULL
|
||||
// authentication method that we should use instead.
|
||||
if len(cookie) == 0 {
|
||||
_, _, err := c.sendCommand("AUTHENTICATE")
|
||||
return err
|
||||
}
|
||||
|
||||
// Authenticating using the SAFECOOKIE authentication method is a two
|
||||
// step process. We'll kick off the authentication routine by sending
|
||||
// the AUTHCHALLENGE command followed by a hex-encoded 32-byte nonce.
|
||||
clientNonce := make([]byte, nonceLen)
|
||||
if _, err := rand.Read(clientNonce); err != nil {
|
||||
return fmt.Errorf("unable to generate client nonce: %v", err)
|
||||
}
|
||||
|
||||
cmd := fmt.Sprintf("AUTHCHALLENGE SAFECOOKIE %x", clientNonce)
|
||||
_, reply, err := c.sendCommand(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If successful, the reply from the server should be of the following
|
||||
// format:
|
||||
//
|
||||
// "250 AUTHCHALLENGE"
|
||||
// SP "SERVERHASH=" ServerHash
|
||||
// SP "SERVERNONCE=" ServerNonce
|
||||
// CRLF
|
||||
//
|
||||
// We're interested in retrieving the SERVERHASH and SERVERNONCE
|
||||
// parameters, so we'll parse our reply to do so.
|
||||
replyParams := parseTorReply(reply)
|
||||
|
||||
// Once retrieved, we'll ensure these values are of proper length when
|
||||
// decoded.
|
||||
serverHash, ok := replyParams["SERVERHASH"]
|
||||
if !ok {
|
||||
return errors.New("server hash not found in reply")
|
||||
}
|
||||
decodedServerHash, err := hex.DecodeString(serverHash)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode server hash: %v", err)
|
||||
}
|
||||
if len(decodedServerHash) != sha256.Size {
|
||||
return errors.New("invalid server hash length")
|
||||
}
|
||||
|
||||
serverNonce, ok := replyParams["SERVERNONCE"]
|
||||
if !ok {
|
||||
return errors.New("server nonce not found in reply")
|
||||
}
|
||||
decodedServerNonce, err := hex.DecodeString(serverNonce)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode server nonce: %v", err)
|
||||
}
|
||||
if len(decodedServerNonce) != nonceLen {
|
||||
return errors.New("invalid server nonce length")
|
||||
}
|
||||
|
||||
// The server hash above was constructed by computing the HMAC-SHA256
|
||||
// of the message composed of the cookie, client nonce, and server
|
||||
// nonce. We'll redo this computation ourselves to ensure the integrity
|
||||
// and authentication of the message.
|
||||
hmacMessage := bytes.Join(
|
||||
[][]byte{cookie, clientNonce, decodedServerNonce}, []byte{},
|
||||
)
|
||||
computedServerHash := computeHMAC256(serverKey, hmacMessage)
|
||||
if !hmac.Equal(computedServerHash, decodedServerHash) {
|
||||
return fmt.Errorf("expected server hash %x, got %x",
|
||||
decodedServerHash, computedServerHash)
|
||||
}
|
||||
|
||||
// If the MAC check was successful, we'll proceed with the last step of
|
||||
// the authentication routine. We'll now send the AUTHENTICATE command
|
||||
// followed by a hex-encoded client hash constructed by computing the
|
||||
// HMAC-SHA256 of the same message, but this time using the controller's
|
||||
// key.
|
||||
clientHash := computeHMAC256(controllerKey, hmacMessage)
|
||||
if len(clientHash) != sha256.Size {
|
||||
return errors.New("invalid client hash length")
|
||||
}
|
||||
|
||||
cmd = fmt.Sprintf("AUTHENTICATE %x", clientHash)
|
||||
if _, _, err := c.sendCommand(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getAuthCookie retrieves the authentication cookie in bytes from the Tor
|
||||
// server. Cookie authentication must be enabled for this to work. The boolean
|
||||
func (c *Controller) getAuthCookie() ([]byte, error) {
|
||||
// Retrieve the authentication methods currently supported by the Tor
|
||||
// server.
|
||||
authMethods, cookieFilePath, version, err := c.ProtocolInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// With the version retrieved, we'll cache it now in case it needs to be
|
||||
// used later on.
|
||||
c.version = version
|
||||
|
||||
// Ensure that the Tor server supports the SAFECOOKIE authentication
|
||||
// method or the NULL method. If NULL, we don't need the cookie info
|
||||
// below this loop, so we just return.
|
||||
safeCookieSupport := false
|
||||
for _, authMethod := range authMethods {
|
||||
if authMethod == "SAFECOOKIE" {
|
||||
safeCookieSupport = true
|
||||
}
|
||||
if authMethod == "NULL" {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
if !safeCookieSupport {
|
||||
return nil, errors.New("the Tor server is currently not " +
|
||||
"configured for cookie or null authentication")
|
||||
}
|
||||
|
||||
// Read the cookie from the file and ensure it has the correct length.
|
||||
cookie, err := ioutil.ReadFile(cookieFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(cookie) != cookieLen {
|
||||
return nil, errors.New("invalid authentication cookie length")
|
||||
}
|
||||
|
||||
return cookie, nil
|
||||
}
|
||||
|
||||
// computeHMAC256 computes the HMAC-SHA256 of a key and message.
|
||||
func computeHMAC256(key, message []byte) []byte {
|
||||
mac := hmac.New(sha256.New, key)
|
||||
mac.Write(message)
|
||||
return mac.Sum(nil)
|
||||
}
|
||||
|
||||
// supportsV3 is a helper function that parses the current version of the Tor
|
||||
// server and determines whether it supports creationg v3 onion services through
|
||||
// Tor's control port. The version string should be of the format:
|
||||
// major.minor.revision.build
|
||||
func supportsV3(version string) error {
|
||||
// We'll split the minimum Tor version that's supported and the given
|
||||
// version in order to individually compare each number.
|
||||
parts := strings.Split(version, ".")
|
||||
if len(parts) != 4 {
|
||||
return errors.New("version string is not of the format " +
|
||||
"major.minor.revision.build")
|
||||
}
|
||||
|
||||
// It's possible that the build number (the last part of the version
|
||||
// string) includes a pre-release string, e.g. rc, beta, etc., so we'll
|
||||
// parse that as well.
|
||||
build := strings.Split(parts[len(parts)-1], "-")
|
||||
parts[len(parts)-1] = build[0]
|
||||
|
||||
// Ensure that each part of the version string corresponds to a number.
|
||||
for _, part := range parts {
|
||||
if _, err := strconv.Atoi(part); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Once we've determined we have a proper version string of the format
|
||||
// major.minor.revision.build, we can just do a string comparison to
|
||||
// determine if it satisfies the minimum version supported.
|
||||
if version < MinTorVersion {
|
||||
return fmt.Errorf("version %v below minimum version supported "+
|
||||
"%v", version, MinTorVersion)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProtocolInfo returns the different authentication methods supported by the
|
||||
// Tor server and the version of the Tor server.
|
||||
func (c *Controller) ProtocolInfo() ([]string, string, string, error) {
|
||||
// We'll start off by sending the "PROTOCOLINFO" command to the Tor
|
||||
// server. We should receive a reply of the following format:
|
||||
//
|
||||
// METHODS=COOKIE,SAFECOOKIE
|
||||
// COOKIEFILE="/home/user/.tor/control_auth_cookie"
|
||||
// VERSION Tor="0.3.2.10"
|
||||
//
|
||||
// We're interested in retrieving all of these fields, so we'll parse
|
||||
// our reply to do so.
|
||||
cmd := fmt.Sprintf("PROTOCOLINFO %d", ProtocolInfoVersion)
|
||||
_, reply, err := c.sendCommand(cmd)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
|
||||
info := parseTorReply(reply)
|
||||
methods, ok := info["METHODS"]
|
||||
if !ok {
|
||||
return nil, "", "", errors.New("auth methods not found in " +
|
||||
"reply")
|
||||
}
|
||||
|
||||
cookieFile, ok := info["COOKIEFILE"]
|
||||
if !ok && !strings.Contains(methods, "NULL") {
|
||||
return nil, "", "", errors.New("cookie file path not found " +
|
||||
"in reply")
|
||||
}
|
||||
|
||||
version, ok := info["Tor"]
|
||||
if !ok {
|
||||
return nil, "", "", errors.New("Tor version not found in reply")
|
||||
}
|
||||
|
||||
// Finally, we'll clean up the results before returning them.
|
||||
authMethods := strings.Split(methods, ",")
|
||||
cookieFilePath := strings.Trim(cookieFile, "\"")
|
||||
torVersion := strings.Trim(version, "\"")
|
||||
|
||||
return authMethods, cookieFilePath, torVersion, nil
|
||||
}
|
||||
|
||||
// OnionType denotes the type of the onion service.
|
||||
type OnionType int
|
||||
|
||||
const (
|
||||
// V2 denotes that the onion service is V2.
|
||||
V2 OnionType = iota
|
||||
|
||||
// V3 denotes that the onion service is V3.
|
||||
V3
|
||||
)
|
||||
|
||||
// AddOnionConfig houses all of the required parameters in order to successfully
|
||||
// create a new onion service or restore an existing one.
|
||||
type AddOnionConfig struct {
|
||||
// Type denotes the type of the onion service that should be created.
|
||||
Type OnionType
|
||||
|
||||
// VirtualPort is the externally reachable port of the onion address.
|
||||
VirtualPort int
|
||||
|
||||
// TargetPorts is the set of ports that the service will be listening on
|
||||
// locally. The Tor server will use choose a random port from this set
|
||||
// to forward the traffic from the virtual port.
|
||||
//
|
||||
// NOTE: If nil/empty, the virtual port will be used as the only target
|
||||
// port.
|
||||
TargetPorts []int
|
||||
|
||||
// PrivateKeyPath is the full path to where the onion service's private
|
||||
// key is stored. This can be used to restore an existing onion service.
|
||||
PrivateKeyPath string
|
||||
}
|
||||
|
||||
// AddOnion creates an onion service and returns its onion address. Once
|
||||
// created, the new onion service will remain active until the connection
|
||||
// between the controller and the Tor server is closed.
|
||||
func (c *Controller) AddOnion(cfg AddOnionConfig) (*OnionAddr, error) {
|
||||
// Before sending the request to create an onion service to the Tor
|
||||
// server, we'll make sure that it supports V3 onion services if that
|
||||
// was the type requested.
|
||||
if cfg.Type == V3 {
|
||||
if err := supportsV3(c.version); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// We'll start off by checking if the file containing the private key
|
||||
// exists. If it does not, then we should request the server to create
|
||||
// a new onion service and return its private key. Otherwise, we'll
|
||||
// request the server to recreate the onion server from our private key.
|
||||
var keyParam string
|
||||
if _, err := os.Stat(cfg.PrivateKeyPath); os.IsNotExist(err) {
|
||||
switch cfg.Type {
|
||||
case V2:
|
||||
keyParam = "NEW:RSA1024"
|
||||
case V3:
|
||||
keyParam = "NEW:ED25519-V3"
|
||||
}
|
||||
} else {
|
||||
privateKey, err := ioutil.ReadFile(cfg.PrivateKeyPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyParam = string(privateKey)
|
||||
}
|
||||
|
||||
// Now, we'll create a mapping from the virtual port to each target
|
||||
// port. If no target ports were specified, we'll use the virtual port
|
||||
// to provide a one-to-one mapping.
|
||||
var portParam string
|
||||
if len(cfg.TargetPorts) == 0 {
|
||||
portParam += fmt.Sprintf("Port=%d,%d ", cfg.VirtualPort,
|
||||
cfg.VirtualPort)
|
||||
} else {
|
||||
for _, targetPort := range cfg.TargetPorts {
|
||||
portParam += fmt.Sprintf("Port=%d,%d ", cfg.VirtualPort,
|
||||
targetPort)
|
||||
}
|
||||
}
|
||||
|
||||
// Send the command to create the onion service to the Tor server and
|
||||
// await its response.
|
||||
cmd := fmt.Sprintf("ADD_ONION %s %s", keyParam, portParam)
|
||||
_, reply, err := c.sendCommand(cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If successful, the reply from the server should be of the following
|
||||
// format, depending on whether a private key has been requested:
|
||||
//
|
||||
// C: ADD_ONION RSA1024:[Blob Redacted] Port=80,8080
|
||||
// S: 250-ServiceID=testonion1234567
|
||||
// S: 250 OK
|
||||
//
|
||||
// C: ADD_ONION NEW:RSA1024 Port=80,8080
|
||||
// S: 250-ServiceID=testonion1234567
|
||||
// S: 250-PrivateKey=RSA1024:[Blob Redacted]
|
||||
// S: 250 OK
|
||||
//
|
||||
// We're interested in retrieving the service ID, which is the public
|
||||
// name of the service, and the private key if requested.
|
||||
replyParams := parseTorReply(reply)
|
||||
serviceID, ok := replyParams["ServiceID"]
|
||||
if !ok {
|
||||
return nil, errors.New("service id not found in reply")
|
||||
}
|
||||
|
||||
// If a new onion service was created, we'll write its private key to
|
||||
// disk under strict permissions in the event that it needs to be
|
||||
// recreated later on.
|
||||
if privateKey, ok := replyParams["PrivateKey"]; ok {
|
||||
err := ioutil.WriteFile(
|
||||
cfg.PrivateKeyPath, []byte(privateKey), 0600,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to write private key "+
|
||||
"to file: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, we'll return the onion address composed of the service ID,
|
||||
// along with the onion suffix, and the port this onion service can be
|
||||
// reached at externally.
|
||||
return &OnionAddr{
|
||||
OnionService: serviceID + ".onion",
|
||||
Port: cfg.VirtualPort,
|
||||
}, nil
|
||||
}
|
||||
104
vendor/github.com/lightningnetwork/lnd/tor/net.go
generated
vendored
Normal file
104
vendor/github.com/lightningnetwork/lnd/tor/net.go
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
package tor
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
// TODO: this interface and its implementations should ideally be moved
|
||||
// elsewhere as they are not Tor-specific.
|
||||
|
||||
// Net is an interface housing a Dial function and several DNS functions that
|
||||
// allows us to abstract the implementations of these functions over different
|
||||
// networks, e.g. clearnet, Tor net, etc.
|
||||
type Net interface {
|
||||
// Dial connects to the address on the named network.
|
||||
Dial(network, address string) (net.Conn, error)
|
||||
|
||||
// LookupHost performs DNS resolution on a given host and returns its
|
||||
// addresses.
|
||||
LookupHost(host string) ([]string, error)
|
||||
|
||||
// LookupSRV tries to resolve an SRV query of the given service,
|
||||
// protocol, and domain name.
|
||||
LookupSRV(service, proto, name string) (string, []*net.SRV, error)
|
||||
|
||||
// ResolveTCPAddr resolves TCP addresses.
|
||||
ResolveTCPAddr(network, address string) (*net.TCPAddr, error)
|
||||
}
|
||||
|
||||
// ClearNet is an implementation of the Net interface that defines behaviour
|
||||
// for regular network connections.
|
||||
type ClearNet struct{}
|
||||
|
||||
// Dial on the regular network uses net.Dial
|
||||
func (r *ClearNet) Dial(network, address string) (net.Conn, error) {
|
||||
return net.Dial(network, address)
|
||||
}
|
||||
|
||||
// LookupHost for regular network uses the net.LookupHost function
|
||||
func (r *ClearNet) LookupHost(host string) ([]string, error) {
|
||||
return net.LookupHost(host)
|
||||
}
|
||||
|
||||
// LookupSRV for regular network uses net.LookupSRV function
|
||||
func (r *ClearNet) LookupSRV(service, proto, name string) (string, []*net.SRV, error) {
|
||||
return net.LookupSRV(service, proto, name)
|
||||
}
|
||||
|
||||
// ResolveTCPAddr for regular network uses net.ResolveTCPAddr function
|
||||
func (r *ClearNet) ResolveTCPAddr(network, address string) (*net.TCPAddr, error) {
|
||||
return net.ResolveTCPAddr(network, address)
|
||||
}
|
||||
|
||||
// ProxyNet is an implementation of the Net interface that defines behaviour
|
||||
// for Tor network connections.
|
||||
type ProxyNet struct {
|
||||
// SOCKS is the host:port which Tor's exposed SOCKS5 proxy is listening
|
||||
// on.
|
||||
SOCKS string
|
||||
|
||||
// DNS is the host:port of the DNS server for Tor to use for SRV
|
||||
// queries.
|
||||
DNS string
|
||||
|
||||
// StreamIsolation is a bool that determines if we should force the
|
||||
// creation of a new circuit for this connection. If true, then this
|
||||
// means that our traffic may be harder to correlate as each connection
|
||||
// will now use a distinct circuit.
|
||||
StreamIsolation bool
|
||||
}
|
||||
|
||||
// Dial uses the Tor Dial function in order to establish connections through
|
||||
// Tor. Since Tor only supports TCP connections, only TCP networks are allowed.
|
||||
func (p *ProxyNet) Dial(network, address string) (net.Conn, error) {
|
||||
switch network {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
default:
|
||||
return nil, errors.New("cannot dial non-tcp network via Tor")
|
||||
}
|
||||
return Dial(address, p.SOCKS, p.StreamIsolation)
|
||||
}
|
||||
|
||||
// LookupHost uses the Tor LookupHost function in order to resolve hosts over
|
||||
// Tor.
|
||||
func (p *ProxyNet) LookupHost(host string) ([]string, error) {
|
||||
return LookupHost(host, p.SOCKS)
|
||||
}
|
||||
|
||||
// LookupSRV uses the Tor LookupSRV function in order to resolve SRV DNS queries
|
||||
// over Tor.
|
||||
func (p *ProxyNet) LookupSRV(service, proto, name string) (string, []*net.SRV, error) {
|
||||
return LookupSRV(service, proto, name, p.SOCKS, p.DNS, p.StreamIsolation)
|
||||
}
|
||||
|
||||
// ResolveTCPAddr uses the Tor ResolveTCPAddr function in order to resolve TCP
|
||||
// addresses over Tor.
|
||||
func (p *ProxyNet) ResolveTCPAddr(network, address string) (*net.TCPAddr, error) {
|
||||
switch network {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
default:
|
||||
return nil, errors.New("cannot dial non-tcp network via Tor")
|
||||
}
|
||||
return ResolveTCPAddr(address, p.SOCKS)
|
||||
}
|
||||
63
vendor/github.com/lightningnetwork/lnd/tor/onionaddr.go
generated
vendored
Normal file
63
vendor/github.com/lightningnetwork/lnd/tor/onionaddr.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
package tor
|
||||
|
||||
import (
|
||||
"encoding/base32"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
// base32Alphabet is the alphabet used for encoding and decoding v2 and
|
||||
// v3 onion addresses.
|
||||
base32Alphabet = "abcdefghijklmnopqrstuvwxyz234567"
|
||||
|
||||
// OnionSuffix is the ".onion" suffix for v2 and v3 onion addresses.
|
||||
OnionSuffix = ".onion"
|
||||
|
||||
// OnionSuffixLen is the length of the ".onion" suffix.
|
||||
OnionSuffixLen = len(OnionSuffix)
|
||||
|
||||
// V2DecodedLen is the length of a decoded v2 onion service.
|
||||
V2DecodedLen = 10
|
||||
|
||||
// V2Len is the length of a v2 onion service including the ".onion"
|
||||
// suffix.
|
||||
V2Len = 22
|
||||
|
||||
// V3DecodedLen is the length of a decoded v3 onion service.
|
||||
V3DecodedLen = 35
|
||||
|
||||
// V3Len is the length of a v2 onion service including the ".onion"
|
||||
// suffix.
|
||||
V3Len = 62
|
||||
)
|
||||
|
||||
var (
|
||||
// Base32Encoding represents the Tor's base32-encoding scheme for v2 and
|
||||
// v3 onion addresses.
|
||||
Base32Encoding = base32.NewEncoding(base32Alphabet)
|
||||
)
|
||||
|
||||
// OnionAddr represents a Tor network end point onion address.
|
||||
type OnionAddr struct {
|
||||
// OnionService is the host of the onion address.
|
||||
OnionService string
|
||||
|
||||
// Port is the port of the onion address.
|
||||
Port int
|
||||
}
|
||||
|
||||
// A compile-time check to ensure that OnionAddr implements the net.Addr
|
||||
// interface.
|
||||
var _ net.Addr = (*OnionAddr)(nil)
|
||||
|
||||
// String returns the string representation of an onion address.
|
||||
func (o *OnionAddr) String() string {
|
||||
return net.JoinHostPort(o.OnionService, strconv.Itoa(o.Port))
|
||||
}
|
||||
|
||||
// Network returns the network that this implementation of net.Addr will use.
|
||||
// In this case, because Tor only allows TCP connections, the network is "tcp".
|
||||
func (o *OnionAddr) Network() string {
|
||||
return "tcp"
|
||||
}
|
||||
243
vendor/github.com/lightningnetwork/lnd/tor/tor.go
generated
vendored
Normal file
243
vendor/github.com/lightningnetwork/lnd/tor/tor.go
generated
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
package tor
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/btcsuite/btcd/connmgr"
|
||||
"github.com/miekg/dns"
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
|
||||
var (
|
||||
// dnsCodes maps the DNS response codes to a friendly description. This
|
||||
// does not include the BADVERS code because of duplicate keys and the
|
||||
// underlying DNS (miekg/dns) package not using it. For more info, see
|
||||
// https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml.
|
||||
dnsCodes = map[int]string{
|
||||
0: "no error",
|
||||
1: "format error",
|
||||
2: "server failure",
|
||||
3: "non-existent domain",
|
||||
4: "not implemented",
|
||||
5: "query refused",
|
||||
6: "name exists when it should not",
|
||||
7: "RR set exists when it should not",
|
||||
8: "RR set that should exist does not",
|
||||
9: "server not authoritative for zone",
|
||||
10: "name not contained in zone",
|
||||
16: "TSIG signature failure",
|
||||
17: "key not recognized",
|
||||
18: "signature out of time window",
|
||||
19: "bad TKEY mode",
|
||||
20: "duplicate key name",
|
||||
21: "algorithm not supported",
|
||||
22: "bad truncation",
|
||||
23: "bad/missing server cookie",
|
||||
}
|
||||
)
|
||||
|
||||
// proxyConn is a wrapper around net.Conn that allows us to expose the actual
|
||||
// remote address we're dialing, rather than the proxy's address.
|
||||
type proxyConn struct {
|
||||
net.Conn
|
||||
remoteAddr net.Addr
|
||||
}
|
||||
|
||||
func (c *proxyConn) RemoteAddr() net.Addr {
|
||||
return c.remoteAddr
|
||||
}
|
||||
|
||||
// Dial is a wrapper over the non-exported dial function that returns a wrapper
|
||||
// around net.Conn in order to expose the actual remote address we're dialing,
|
||||
// rather than the proxy's address.
|
||||
func Dial(address, socksAddr string, streamIsolation bool) (net.Conn, error) {
|
||||
conn, err := dial(address, socksAddr, streamIsolation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Now that the connection is established, we'll create our internal
|
||||
// proxyConn that will serve in populating the correct remote address
|
||||
// of the connection, rather than using the proxy's address.
|
||||
remoteAddr, err := ParseAddr(address, socksAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &proxyConn{
|
||||
Conn: conn,
|
||||
remoteAddr: remoteAddr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// dial establishes a connection to the address via Tor's SOCKS proxy. Only TCP
|
||||
// is supported over Tor. The final argument determines if we should force
|
||||
// stream isolation for this new connection. If we do, then this means this new
|
||||
// connection will use a fresh circuit, rather than possibly re-using an
|
||||
// existing circuit.
|
||||
func dial(address, socksAddr string, streamIsolation bool) (net.Conn, error) {
|
||||
// If we were requested to force stream isolation for this connection,
|
||||
// we'll populate the authentication credentials with random data as
|
||||
// Tor will create a new circuit for each set of credentials.
|
||||
var auth *proxy.Auth
|
||||
if streamIsolation {
|
||||
var b [16]byte
|
||||
if _, err := rand.Read(b[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
auth = &proxy.Auth{
|
||||
User: hex.EncodeToString(b[:8]),
|
||||
Password: hex.EncodeToString(b[8:]),
|
||||
}
|
||||
}
|
||||
|
||||
// Establish the connection through Tor's SOCKS proxy.
|
||||
dialer, err := proxy.SOCKS5("tcp", socksAddr, auth, proxy.Direct)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dialer.Dial("tcp", address)
|
||||
}
|
||||
|
||||
// LookupHost performs DNS resolution on a given host via Tor's native resolver.
|
||||
// Only IPv4 addresses are returned.
|
||||
func LookupHost(host, socksAddr string) ([]string, error) {
|
||||
ip, err := connmgr.TorLookupIP(host, socksAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Only one IPv4 address is returned by the TorLookupIP function.
|
||||
return []string{ip[0].String()}, nil
|
||||
}
|
||||
|
||||
// LookupSRV uses Tor's SOCKS proxy to route DNS SRV queries. Tor does not
|
||||
// natively support SRV queries so we must route all SRV queries through the
|
||||
// proxy by connecting directly to a DNS server and querying it. The DNS server
|
||||
// must have TCP resolution enabled for the given port.
|
||||
func LookupSRV(service, proto, name, socksAddr, dnsServer string,
|
||||
streamIsolation bool) (string, []*net.SRV, error) {
|
||||
|
||||
// Connect to the DNS server we'll be using to query SRV records.
|
||||
conn, err := dial(dnsServer, socksAddr, streamIsolation)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
dnsConn := &dns.Conn{Conn: conn}
|
||||
defer dnsConn.Close()
|
||||
|
||||
// Once connected, we'll construct the SRV request for the host
|
||||
// following the format _service._proto.name. as described in RFC #2782.
|
||||
host := fmt.Sprintf("_%s._%s.%s.", service, proto, name)
|
||||
msg := new(dns.Msg).SetQuestion(host, dns.TypeSRV)
|
||||
|
||||
// Send the request to the DNS server and read its response.
|
||||
if err := dnsConn.WriteMsg(msg); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
resp, err := dnsConn.ReadMsg()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
// We'll fail if we were unable to query the DNS server for our record.
|
||||
if resp.Rcode != dns.RcodeSuccess {
|
||||
return "", nil, fmt.Errorf("unable to query for SRV records: "+
|
||||
"%s", dnsCodes[resp.Rcode])
|
||||
}
|
||||
|
||||
// Retrieve the RR(s) of the Answer section.
|
||||
var rrs []*net.SRV
|
||||
for _, rr := range resp.Answer {
|
||||
srv := rr.(*dns.SRV)
|
||||
rrs = append(rrs, &net.SRV{
|
||||
Target: srv.Target,
|
||||
Port: srv.Port,
|
||||
Priority: srv.Priority,
|
||||
Weight: srv.Weight,
|
||||
})
|
||||
}
|
||||
|
||||
return "", rrs, nil
|
||||
}
|
||||
|
||||
// ResolveTCPAddr uses Tor's proxy to resolve TCP addresses instead of the
|
||||
// standard system resolver provided in the `net` package.
|
||||
func ResolveTCPAddr(address, socksAddr string) (*net.TCPAddr, error) {
|
||||
// Split host:port since the lookup function does not take a port.
|
||||
host, port, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ip, err := LookupHost(host, socksAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p, err := strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &net.TCPAddr{
|
||||
IP: net.ParseIP(ip[0]),
|
||||
Port: p,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ParseAddr parses an address from its string format to a net.Addr.
|
||||
func ParseAddr(address, socksAddr string) (net.Addr, error) {
|
||||
host, portStr, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if IsOnionHost(host) {
|
||||
return &OnionAddr{OnionService: host, Port: port}, nil
|
||||
}
|
||||
|
||||
return ResolveTCPAddr(address, socksAddr)
|
||||
}
|
||||
|
||||
// IsOnionHost determines whether a host is part of an onion address.
|
||||
func IsOnionHost(host string) bool {
|
||||
// Note the starting index of the onion suffix in the host depending
|
||||
// on its length.
|
||||
var suffixIndex int
|
||||
switch len(host) {
|
||||
case V2Len:
|
||||
suffixIndex = V2Len - OnionSuffixLen
|
||||
case V3Len:
|
||||
suffixIndex = V3Len - OnionSuffixLen
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
// Make sure the host ends with the ".onion" suffix.
|
||||
if host[suffixIndex:] != OnionSuffix {
|
||||
return false
|
||||
}
|
||||
|
||||
// We'll now attempt to decode the host without its suffix, as the
|
||||
// suffix includes invalid characters. This will tell us if the host is
|
||||
// actually valid if successful.
|
||||
host = host[:suffixIndex]
|
||||
if _, err := Base32Encoding.DecodeString(host); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user