Merge pull request #4806 from guggero/neutrino-tor-fix
Fix Onion v2 support for Neutrino backends
This commit is contained in:
commit
42d7fcd2f9
33
lnd.go
33
lnd.go
@ -1508,12 +1508,43 @@ func initNeutrinoBackend(cfg *Config, chainDir string) (*neutrino.ChainService,
|
||||
AddPeers: cfg.NeutrinoMode.AddPeers,
|
||||
ConnectPeers: cfg.NeutrinoMode.ConnectPeers,
|
||||
Dialer: func(addr net.Addr) (net.Conn, error) {
|
||||
dialAddr := addr
|
||||
if tor.IsOnionFakeIP(addr) {
|
||||
// Because the Neutrino address manager only
|
||||
// knows IP addresses, we need to turn any fake
|
||||
// tcp6 address that actually encodes an Onion
|
||||
// v2 address back into the hostname
|
||||
// representation before we can pass it to the
|
||||
// dialer.
|
||||
var err error
|
||||
dialAddr, err = tor.FakeIPToOnionHost(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return cfg.net.Dial(
|
||||
addr.Network(), addr.String(),
|
||||
dialAddr.Network(), dialAddr.String(),
|
||||
cfg.ConnectionTimeout,
|
||||
)
|
||||
},
|
||||
NameResolver: func(host string) ([]net.IP, error) {
|
||||
if tor.IsOnionHost(host) {
|
||||
// Neutrino internally uses btcd's address
|
||||
// manager which only operates on an IP level
|
||||
// and does not understand onion hosts. We need
|
||||
// to turn an onion host into a fake
|
||||
// representation of an IP address to make it
|
||||
// possible to connect to a block filter backend
|
||||
// that serves on an Onion v2 hidden service.
|
||||
fakeIP, err := tor.OnionHostToFakeIP(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []net.IP{fakeIP}, nil
|
||||
}
|
||||
|
||||
addrs, err := cfg.net.LookupHost(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
67
tor/tor.go
67
tor/tor.go
@ -1,6 +1,7 @@
|
||||
package tor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
@ -39,6 +40,15 @@ var (
|
||||
22: "bad truncation",
|
||||
23: "bad/missing server cookie",
|
||||
}
|
||||
|
||||
// onionPrefixBytes is a special purpose IPv6 prefix to encode Onion v2
|
||||
// addresses with. Because Neutrino uses the address manager of btcd
|
||||
// which only understands net.IP addresses instead of net.Addr, we need
|
||||
// to convert any .onion addresses into fake IPv6 addresses if we want
|
||||
// to use a Tor hidden service as a Neutrino backend. This is the same
|
||||
// range used by OnionCat, which is part part of the RFC4193 unique
|
||||
// local IPv6 unicast address range.
|
||||
onionPrefixBytes = []byte{0xfd, 0x87, 0xd8, 0x7e, 0xeb, 0x43}
|
||||
)
|
||||
|
||||
// proxyConn is a wrapper around net.Conn that allows us to expose the actual
|
||||
@ -248,3 +258,60 @@ func IsOnionHost(host string) bool {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// IsOnionFakeIP checks whether a given net.Addr is a fake IPv6 address that
|
||||
// encodes an Onion v2 address.
|
||||
func IsOnionFakeIP(addr net.Addr) bool {
|
||||
_, err := FakeIPToOnionHost(addr)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// OnionHostToFakeIP encodes an Onion v2 address into a fake IPv6 address that
|
||||
// encodes the same information but can be used for libraries that operate on an
|
||||
// IP address base only, like btcd's address manager. For example, this will
|
||||
// turn the onion host ld47qlr6h2b7hrrf.onion into the ip6 address
|
||||
// fd87:d87e:eb43:58f9:f82e:3e3e:83f3:c625.
|
||||
func OnionHostToFakeIP(host string) (net.IP, error) {
|
||||
if len(host) != V2Len {
|
||||
return nil, fmt.Errorf("invalid onion v2 host: %v", host)
|
||||
}
|
||||
|
||||
data, err := Base32Encoding.DecodeString(host[:V2Len-OnionSuffixLen])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ip := make([]byte, len(onionPrefixBytes)+len(data))
|
||||
copy(ip, onionPrefixBytes)
|
||||
copy(ip[len(onionPrefixBytes):], data)
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
// FakeIPToOnionHost turns a fake IPv6 address that encodes an Onion v2 address
|
||||
// back into its onion host address representation. For example, this will turn
|
||||
// the fake tcp6 address [fd87:d87e:eb43:58f9:f82e:3e3e:83f3:c625]:8333 back
|
||||
// into ld47qlr6h2b7hrrf.onion:8333.
|
||||
func FakeIPToOnionHost(fakeIP net.Addr) (net.Addr, error) {
|
||||
tcpAddr, ok := fakeIP.(*net.TCPAddr)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid fake onion IP address: %v",
|
||||
fakeIP)
|
||||
}
|
||||
|
||||
ip := tcpAddr.IP
|
||||
if len(ip) != len(onionPrefixBytes)+V2DecodedLen {
|
||||
return nil, fmt.Errorf("invalid fake onion IP address length: "+
|
||||
"%v", fakeIP)
|
||||
}
|
||||
|
||||
if !bytes.Equal(ip[:len(onionPrefixBytes)], onionPrefixBytes) {
|
||||
return nil, fmt.Errorf("invalid fake onion IP address prefix: "+
|
||||
"%v", fakeIP)
|
||||
}
|
||||
|
||||
host := Base32Encoding.EncodeToString(ip[len(onionPrefixBytes):])
|
||||
return &OnionAddr{
|
||||
OnionService: host + ".onion",
|
||||
Port: tcpAddr.Port,
|
||||
}, nil
|
||||
}
|
||||
|
36
tor/tor_test.go
Normal file
36
tor/tor_test.go
Normal file
@ -0,0 +1,36 @@
|
||||
package tor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
testOnion = "ld47qlr6h2b7hrrf.onion"
|
||||
testFakeIP = "fd87:d87e:eb43:58f9:f82e:3e3e:83f3:c625"
|
||||
)
|
||||
|
||||
// TestOnionHostToFakeIP tests that an onion host address can be converted into
|
||||
// a fake tcp6 address successfully.
|
||||
func TestOnionHostToFakeIP(t *testing.T) {
|
||||
ip, err := OnionHostToFakeIP(testOnion)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, testFakeIP, ip.String())
|
||||
}
|
||||
|
||||
// TestFakeIPToOnionHost tests that a fake tcp6 address can be converted back
|
||||
// into its original .onion host address successfully.
|
||||
func TestFakeIPToOnionHost(t *testing.T) {
|
||||
tcpAddr, err := net.ResolveTCPAddr(
|
||||
"tcp6", fmt.Sprintf("[%s]:8333", testFakeIP),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.True(t, IsOnionFakeIP(tcpAddr))
|
||||
|
||||
onionHost, err := FakeIPToOnionHost(tcpAddr)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, fmt.Sprintf("%s:8333", testOnion), onionHost.String())
|
||||
}
|
Loading…
Reference in New Issue
Block a user