Merge pull request #1812 from Crypt-iQ/btcd_addrv2

multi: implement BIP-155 addrv2 support
This commit is contained in:
Olaoluwa Osuntokun 2022-04-13 10:25:12 -07:00 committed by GitHub
commit bf64c8bdbb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1969 additions and 277 deletions

View File

@ -70,7 +70,7 @@ type serializedAddrManager struct {
}
type localAddress struct {
na *wire.NetAddress
na *wire.NetAddressV2
score AddressPriority
}
@ -163,7 +163,7 @@ const (
// updateAddress is a helper function to either update an address already known
// to the address manager, or to add the address if not already known.
func (a *AddrManager) updateAddress(netAddr, srcAddr *wire.NetAddress) {
func (a *AddrManager) updateAddress(netAddr, srcAddr *wire.NetAddressV2) {
// Filter out non-routable addresses. Note that non-routable
// also includes invalid and local addresses.
if !IsRoutable(netAddr) {
@ -296,7 +296,7 @@ func (a *AddrManager) pickTried(bucket int) *list.Element {
return oldestElem
}
func (a *AddrManager) getNewBucket(netAddr, srcAddr *wire.NetAddress) int {
func (a *AddrManager) getNewBucket(netAddr, srcAddr *wire.NetAddressV2) int {
// bitcoind:
// doublesha256(key + sourcegroup + int64(doublesha256(key + group + sourcegroup))%bucket_per_source_group) % num_new_buckets
@ -318,7 +318,7 @@ func (a *AddrManager) getNewBucket(netAddr, srcAddr *wire.NetAddress) int {
return int(binary.LittleEndian.Uint64(hash2) % newBucketCount)
}
func (a *AddrManager) getTriedBucket(netAddr *wire.NetAddress) int {
func (a *AddrManager) getTriedBucket(netAddr *wire.NetAddressV2) int {
// bitcoind hashes this as:
// doublesha256(key + group + truncate_to_64bits(doublesha256(key)) % buckets_per_group) % num_buckets
data1 := []byte{}
@ -550,7 +550,7 @@ func (a *AddrManager) deserializePeers(filePath string) error {
// DeserializeNetAddress converts a given address string to a *wire.NetAddress.
func (a *AddrManager) DeserializeNetAddress(addr string,
services wire.ServiceFlag) (*wire.NetAddress, error) {
services wire.ServiceFlag) (*wire.NetAddressV2, error) {
host, portStr, err := net.SplitHostPort(addr)
if err != nil {
@ -599,7 +599,7 @@ func (a *AddrManager) Stop() error {
// AddAddresses adds new addresses to the address manager. It enforces a max
// number of addresses and silently ignores duplicate addresses. It is
// safe for concurrent access.
func (a *AddrManager) AddAddresses(addrs []*wire.NetAddress, srcAddr *wire.NetAddress) {
func (a *AddrManager) AddAddresses(addrs []*wire.NetAddressV2, srcAddr *wire.NetAddressV2) {
a.mtx.Lock()
defer a.mtx.Unlock()
@ -611,7 +611,7 @@ func (a *AddrManager) AddAddresses(addrs []*wire.NetAddress, srcAddr *wire.NetAd
// AddAddress adds a new address to the address manager. It enforces a max
// number of addresses and silently ignores duplicate addresses. It is
// safe for concurrent access.
func (a *AddrManager) AddAddress(addr, srcAddr *wire.NetAddress) {
func (a *AddrManager) AddAddress(addr, srcAddr *wire.NetAddressV2) {
a.mtx.Lock()
defer a.mtx.Unlock()
@ -635,7 +635,7 @@ func (a *AddrManager) AddAddressByIP(addrIP string) error {
if err != nil {
return fmt.Errorf("invalid port %s: %v", portStr, err)
}
na := wire.NewNetAddressIPPort(ip, uint16(port), 0)
na := wire.NetAddressV2FromBytes(time.Now(), 0, ip, uint16(port))
a.AddAddress(na, na) // XXX use correct src address
return nil
}
@ -664,7 +664,7 @@ func (a *AddrManager) NeedMoreAddresses() bool {
// AddressCache returns the current address cache. It must be treated as
// read-only (but since it is a copy now, this is not as dangerous).
func (a *AddrManager) AddressCache() []*wire.NetAddress {
func (a *AddrManager) AddressCache() []*wire.NetAddressV2 {
allAddr := a.getAddresses()
numAddresses := len(allAddr) * getAddrPercent / 100
@ -686,7 +686,7 @@ func (a *AddrManager) AddressCache() []*wire.NetAddress {
// getAddresses returns all of the addresses currently found within the
// manager's address cache.
func (a *AddrManager) getAddresses() []*wire.NetAddress {
func (a *AddrManager) getAddresses() []*wire.NetAddressV2 {
a.mtx.RLock()
defer a.mtx.RUnlock()
@ -695,7 +695,7 @@ func (a *AddrManager) getAddresses() []*wire.NetAddress {
return nil
}
addrs := make([]*wire.NetAddress, 0, addrIndexLen)
addrs := make([]*wire.NetAddressV2, 0, addrIndexLen)
for _, v := range a.addrIndex {
addrs = append(addrs, v.na)
}
@ -722,20 +722,43 @@ func (a *AddrManager) reset() {
// HostToNetAddress returns a netaddress given a host address. If the address
// is a Tor .onion address this will be taken care of. Else if the host is
// not an IP address it will be resolved (via Tor if required).
func (a *AddrManager) HostToNetAddress(host string, port uint16, services wire.ServiceFlag) (*wire.NetAddress, error) {
// Tor address is 16 char base32 + ".onion"
var ip net.IP
if len(host) == 22 && host[16:] == ".onion" {
func (a *AddrManager) HostToNetAddress(host string, port uint16,
services wire.ServiceFlag) (*wire.NetAddressV2, error) {
var (
na *wire.NetAddressV2
ip net.IP
)
// Tor v2 address is 16 char base32 + ".onion"
if len(host) == wire.TorV2EncodedSize && host[wire.TorV2EncodedSize-6:] == ".onion" {
// go base32 encoding uses capitals (as does the rfc
// but Tor and bitcoind tend to user lowercase, so we switch
// case here.
data, err := base32.StdEncoding.DecodeString(
strings.ToUpper(host[:16]))
strings.ToUpper(host[:wire.TorV2EncodedSize-6]))
if err != nil {
return nil, err
}
prefix := []byte{0xfd, 0x87, 0xd8, 0x7e, 0xeb, 0x43}
ip = net.IP(append(prefix, data...))
na = wire.NetAddressV2FromBytes(
time.Now(), services, data, port,
)
} else if len(host) == wire.TorV3EncodedSize && host[wire.TorV3EncodedSize-6:] == ".onion" {
// Tor v3 addresses are 56 base32 characters with the 6 byte
// onion suffix.
data, err := base32.StdEncoding.DecodeString(
strings.ToUpper(host[:wire.TorV3EncodedSize-6]),
)
if err != nil {
return nil, err
}
// The first 32 bytes is the ed25519 public key and is enough
// to reconstruct the .onion address.
na = wire.NetAddressV2FromBytes(
time.Now(), services, data[:wire.TorV3Size], port,
)
} else if ip = net.ParseIP(host); ip == nil {
ips, err := a.lookupFunc(host)
if err != nil {
@ -745,30 +768,23 @@ func (a *AddrManager) HostToNetAddress(host string, port uint16, services wire.S
return nil, fmt.Errorf("no addresses found for %s", host)
}
ip = ips[0]
na = wire.NetAddressV2FromBytes(time.Now(), services, ip, port)
} else {
// This is an non-nil IP address that was parsed in the else if
// above.
na = wire.NetAddressV2FromBytes(time.Now(), services, ip, port)
}
return wire.NewNetAddressIPPort(ip, port, services), nil
}
// ipString returns a string for the ip from the provided NetAddress. If the
// ip is in the range used for Tor addresses then it will be transformed into
// the relevant .onion address.
func ipString(na *wire.NetAddress) string {
if IsOnionCatTor(na) {
// We know now that na.IP is long enough.
base32 := base32.StdEncoding.EncodeToString(na.IP[6:])
return strings.ToLower(base32) + ".onion"
}
return na.IP.String()
return na, nil
}
// NetAddressKey returns a string key in the form of ip:port for IPv4 addresses
// or [ip]:port for IPv6 addresses.
func NetAddressKey(na *wire.NetAddress) string {
// or [ip]:port for IPv6 addresses. It also handles onion v2 and v3 addresses.
func NetAddressKey(na *wire.NetAddressV2) string {
port := strconv.FormatUint(uint64(na.Port), 10)
return net.JoinHostPort(ipString(na), port)
return net.JoinHostPort(na.Addr.String(), port)
}
// GetAddress returns a single address that should be routable. It picks a
@ -842,13 +858,13 @@ func (a *AddrManager) GetAddress() *KnownAddress {
}
}
func (a *AddrManager) find(addr *wire.NetAddress) *KnownAddress {
func (a *AddrManager) find(addr *wire.NetAddressV2) *KnownAddress {
return a.addrIndex[NetAddressKey(addr)]
}
// Attempt increases the given address' attempt counter and updates
// the last attempt time.
func (a *AddrManager) Attempt(addr *wire.NetAddress) {
func (a *AddrManager) Attempt(addr *wire.NetAddressV2) {
a.mtx.Lock()
defer a.mtx.Unlock()
@ -869,7 +885,7 @@ func (a *AddrManager) Attempt(addr *wire.NetAddress) {
// Connected Marks the given address as currently connected and working at the
// current time. The address must already be known to AddrManager else it will
// be ignored.
func (a *AddrManager) Connected(addr *wire.NetAddress) {
func (a *AddrManager) Connected(addr *wire.NetAddressV2) {
a.mtx.Lock()
defer a.mtx.Unlock()
@ -894,7 +910,7 @@ func (a *AddrManager) Connected(addr *wire.NetAddress) {
// Good marks the given address as good. To be called after a successful
// connection and version exchange. If the address is unknown to the address
// manager it will be ignored.
func (a *AddrManager) Good(addr *wire.NetAddress) {
func (a *AddrManager) Good(addr *wire.NetAddressV2) {
a.mtx.Lock()
defer a.mtx.Unlock()
@ -983,7 +999,7 @@ func (a *AddrManager) Good(addr *wire.NetAddress) {
}
// SetServices sets the services for the giiven address to the provided value.
func (a *AddrManager) SetServices(addr *wire.NetAddress, services wire.ServiceFlag) {
func (a *AddrManager) SetServices(addr *wire.NetAddressV2, services wire.ServiceFlag) {
a.mtx.Lock()
defer a.mtx.Unlock()
@ -1005,9 +1021,11 @@ func (a *AddrManager) SetServices(addr *wire.NetAddress, services wire.ServiceFl
// AddLocalAddress adds na to the list of known local addresses to advertise
// with the given priority.
func (a *AddrManager) AddLocalAddress(na *wire.NetAddress, priority AddressPriority) error {
func (a *AddrManager) AddLocalAddress(na *wire.NetAddressV2, priority AddressPriority) error {
if !IsRoutable(na) {
return fmt.Errorf("address %s is not routable", na.IP)
return fmt.Errorf(
"address %s is not routable", na.Addr.String(),
)
}
a.lamtx.Lock()
@ -1030,7 +1048,7 @@ func (a *AddrManager) AddLocalAddress(na *wire.NetAddress, priority AddressPrior
// getReachabilityFrom returns the relative reachability of the provided local
// address to the provided remote address.
func getReachabilityFrom(localAddr, remoteAddr *wire.NetAddress) int {
func getReachabilityFrom(localAddr, remoteAddr *wire.NetAddressV2) int {
const (
Unreachable = 0
Default = iota
@ -1045,36 +1063,66 @@ func getReachabilityFrom(localAddr, remoteAddr *wire.NetAddress) int {
return Unreachable
}
if IsOnionCatTor(remoteAddr) {
if IsOnionCatTor(localAddr) {
if remoteAddr.IsTorV3() {
if localAddr.IsTorV3() {
return Private
}
if IsRoutable(localAddr) && IsIPv4(localAddr) {
lna := localAddr.ToLegacy()
if IsOnionCatTor(lna) {
// Modern v3 clients should not be able to connect to
// deprecated v2 hidden services.
return Unreachable
}
if IsRoutable(localAddr) && IsIPv4(lna) {
return Ipv4
}
return Default
}
if IsRFC4380(remoteAddr) {
// We can't be sure if the remote party can actually connect to this
// address or not.
if localAddr.IsTorV3() {
return Default
}
// Convert the V2 addresses into legacy to access the network
// functions.
remoteLna := remoteAddr.ToLegacy()
localLna := localAddr.ToLegacy()
if IsOnionCatTor(remoteLna) {
if IsOnionCatTor(localLna) {
return Private
}
if IsRoutable(localAddr) && IsIPv4(localLna) {
return Ipv4
}
return Default
}
if IsRFC4380(remoteLna) {
if !IsRoutable(localAddr) {
return Default
}
if IsRFC4380(localAddr) {
if IsRFC4380(localLna) {
return Teredo
}
if IsIPv4(localAddr) {
if IsIPv4(localLna) {
return Ipv4
}
return Ipv6Weak
}
if IsIPv4(remoteAddr) {
if IsRoutable(localAddr) && IsIPv4(localAddr) {
if IsIPv4(remoteLna) {
if IsRoutable(localAddr) && IsIPv4(localLna) {
return Ipv4
}
return Unreachable
@ -1083,7 +1131,7 @@ func getReachabilityFrom(localAddr, remoteAddr *wire.NetAddress) int {
/* ipv6 */
var tunnelled bool
// Is our v6 is tunnelled?
if IsRFC3964(localAddr) || IsRFC6052(localAddr) || IsRFC6145(localAddr) {
if IsRFC3964(localLna) || IsRFC6052(localLna) || IsRFC6145(localLna) {
tunnelled = true
}
@ -1091,11 +1139,11 @@ func getReachabilityFrom(localAddr, remoteAddr *wire.NetAddress) int {
return Default
}
if IsRFC4380(localAddr) {
if IsRFC4380(localLna) {
return Teredo
}
if IsIPv4(localAddr) {
if IsIPv4(localLna) {
return Ipv4
}
@ -1109,13 +1157,13 @@ func getReachabilityFrom(localAddr, remoteAddr *wire.NetAddress) int {
// GetBestLocalAddress returns the most appropriate local address to use
// for the given remote address.
func (a *AddrManager) GetBestLocalAddress(remoteAddr *wire.NetAddress) *wire.NetAddress {
func (a *AddrManager) GetBestLocalAddress(remoteAddr *wire.NetAddressV2) *wire.NetAddressV2 {
a.lamtx.Lock()
defer a.lamtx.Unlock()
bestreach := 0
var bestscore AddressPriority
var bestAddress *wire.NetAddress
var bestAddress *wire.NetAddressV2
for _, la := range a.localAddresses {
reach := getReachabilityFrom(la.na, remoteAddr)
if reach > bestreach ||
@ -1126,21 +1174,29 @@ func (a *AddrManager) GetBestLocalAddress(remoteAddr *wire.NetAddress) *wire.Net
}
}
if bestAddress != nil {
log.Debugf("Suggesting address %s:%d for %s:%d", bestAddress.IP,
bestAddress.Port, remoteAddr.IP, remoteAddr.Port)
log.Debugf("Suggesting address %s:%d for %s:%d",
bestAddress.Addr.String(), bestAddress.Port,
remoteAddr.Addr.String(), remoteAddr.Port)
} else {
log.Debugf("No worthy address for %s:%d", remoteAddr.IP,
remoteAddr.Port)
log.Debugf("No worthy address for %s:%d",
remoteAddr.Addr.String(), remoteAddr.Port)
// Send something unroutable if nothing suitable.
var ip net.IP
if !IsIPv4(remoteAddr) && !IsOnionCatTor(remoteAddr) {
ip = net.IPv6zero
} else {
if remoteAddr.IsTorV3() {
ip = net.IPv4zero
} else {
remoteLna := remoteAddr.ToLegacy()
if !IsIPv4(remoteLna) && !IsOnionCatTor(remoteLna) {
ip = net.IPv6zero
} else {
ip = net.IPv4zero
}
}
services := wire.SFNodeNetwork | wire.SFNodeWitness | wire.SFNodeBloom
bestAddress = wire.NewNetAddressIPPort(ip, 0, services)
bestAddress = wire.NetAddressV2FromBytes(
time.Now(), services, ip, 0,
)
}
return bestAddress

View File

@ -6,12 +6,14 @@ import (
"net"
"os"
"testing"
"time"
"github.com/btcsuite/btcd/wire"
)
// randAddr generates a *wire.NetAddress backed by a random IPv4/IPv6 address.
func randAddr(t *testing.T) *wire.NetAddress {
// randAddr generates a *wire.NetAddressV2 backed by a random IPv4/IPv6
// address.
func randAddr(t *testing.T) *wire.NetAddressV2 {
t.Helper()
ipv4 := rand.Intn(2) == 0
@ -30,22 +32,26 @@ func randAddr(t *testing.T) *wire.NetAddress {
ip = b[:]
}
return &wire.NetAddress{
Services: wire.ServiceFlag(rand.Uint64()),
IP: ip,
Port: uint16(rand.Uint32()),
}
services := wire.ServiceFlag(rand.Uint64())
port := uint16(rand.Uint32())
return wire.NetAddressV2FromBytes(
time.Now(), services, ip, port,
)
}
// assertAddr ensures that the two addresses match. The timestamp is not
// checked as it does not affect uniquely identifying a specific address.
func assertAddr(t *testing.T, got, expected *wire.NetAddress) {
func assertAddr(t *testing.T, got, expected *wire.NetAddressV2) {
if got.Services != expected.Services {
t.Fatalf("expected address services %v, got %v",
expected.Services, got.Services)
}
if !got.IP.Equal(expected.IP) {
t.Fatalf("expected address IP %v, got %v", expected.IP, got.IP)
gotAddr := got.Addr.String()
expectedAddr := expected.Addr.String()
if gotAddr != expectedAddr {
t.Fatalf("expected address IP %v, got %v", expectedAddr,
gotAddr)
}
if got.Port != expected.Port {
t.Fatalf("expected address port %d, got %d", expected.Port,
@ -56,7 +62,7 @@ func assertAddr(t *testing.T, got, expected *wire.NetAddress) {
// assertAddrs ensures that the manager's address cache matches the given
// expected addresses.
func assertAddrs(t *testing.T, addrMgr *AddrManager,
expectedAddrs map[string]*wire.NetAddress) {
expectedAddrs map[string]*wire.NetAddressV2) {
t.Helper()
@ -96,7 +102,7 @@ func TestAddrManagerSerialization(t *testing.T) {
// We'll be adding 5 random addresses to the manager.
const numAddrs = 5
expectedAddrs := make(map[string]*wire.NetAddress, numAddrs)
expectedAddrs := make(map[string]*wire.NetAddressV2, numAddrs)
for i := 0; i < numAddrs; i++ {
addr := randAddr(t)
expectedAddrs[NetAddressKey(addr)] = addr
@ -141,7 +147,7 @@ func TestAddrManagerV1ToV2(t *testing.T) {
// each addresses' services will not be stored.
const numAddrs = 5
expectedAddrs := make(map[string]*wire.NetAddress, numAddrs)
expectedAddrs := make(map[string]*wire.NetAddressV2, numAddrs)
for i := 0; i < numAddrs; i++ {
addr := randAddr(t)
expectedAddrs[NetAddressKey(addr)] = addr

View File

@ -19,7 +19,7 @@ import (
// naTest is used to describe a test to be performed against the NetAddressKey
// method.
type naTest struct {
in wire.NetAddress
in wire.NetAddressV2
want string
}
@ -93,8 +93,10 @@ func addNaTests() {
func addNaTest(ip string, port uint16, want string) {
nip := net.ParseIP(ip)
na := *wire.NewNetAddressIPPort(nip, port, wire.SFNodeNetwork)
test := naTest{na, want}
na := wire.NetAddressV2FromBytes(
time.Now(), wire.SFNodeNetwork, nip, port,
)
test := naTest{*na, want}
naTests = append(naTests, test)
}
@ -157,37 +159,49 @@ func TestAddAddressByIP(t *testing.T) {
func TestAddLocalAddress(t *testing.T) {
var tests = []struct {
address wire.NetAddress
address wire.NetAddressV2
priority addrmgr.AddressPriority
valid bool
}{
{
wire.NetAddress{IP: net.ParseIP("192.168.0.100")},
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.ParseIP("192.168.0.100"), 0,
),
addrmgr.InterfacePrio,
false,
},
{
wire.NetAddress{IP: net.ParseIP("204.124.1.1")},
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.ParseIP("204.124.1.1"), 0,
),
addrmgr.InterfacePrio,
true,
},
{
wire.NetAddress{IP: net.ParseIP("204.124.1.1")},
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.ParseIP("204.124.1.1"), 0,
),
addrmgr.BoundPrio,
true,
},
{
wire.NetAddress{IP: net.ParseIP("::1")},
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.ParseIP("::1"), 0,
),
addrmgr.InterfacePrio,
false,
},
{
wire.NetAddress{IP: net.ParseIP("fe80::1")},
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.ParseIP("fe80::1"), 0,
),
addrmgr.InterfacePrio,
false,
},
{
wire.NetAddress{IP: net.ParseIP("2620:100::1")},
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.ParseIP("2620:100::1"), 0,
),
addrmgr.InterfacePrio,
true,
},
@ -197,12 +211,12 @@ func TestAddLocalAddress(t *testing.T) {
result := amgr.AddLocalAddress(&test.address, test.priority)
if result == nil && !test.valid {
t.Errorf("TestAddLocalAddress test #%d failed: %s should have "+
"been accepted", x, test.address.IP)
"been accepted", x, test.address.Addr.String())
continue
}
if result != nil && test.valid {
t.Errorf("TestAddLocalAddress test #%d failed: %s should not have "+
"been accepted", x, test.address.IP)
"been accepted", x, test.address.Addr.String())
continue
}
}
@ -257,7 +271,7 @@ func TestNeedMoreAddresses(t *testing.T) {
if !b {
t.Errorf("Expected that we need more addresses")
}
addrs := make([]*wire.NetAddress, addrsToAdd)
addrs := make([]*wire.NetAddressV2, addrsToAdd)
var err error
for i := 0; i < addrsToAdd; i++ {
@ -268,7 +282,9 @@ func TestNeedMoreAddresses(t *testing.T) {
}
}
srcAddr := wire.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
srcAddr := wire.NetAddressV2FromBytes(
time.Now(), 0, net.IPv4(173, 144, 173, 111), 8333,
)
n.AddAddresses(addrs, srcAddr)
numAddrs := n.NumAddresses()
@ -285,7 +301,7 @@ func TestNeedMoreAddresses(t *testing.T) {
func TestGood(t *testing.T) {
n := addrmgr.New("testgood", lookupFunc)
addrsToAdd := 64 * 64
addrs := make([]*wire.NetAddress, addrsToAdd)
addrs := make([]*wire.NetAddressV2, addrsToAdd)
var err error
for i := 0; i < addrsToAdd; i++ {
@ -296,7 +312,9 @@ func TestGood(t *testing.T) {
}
}
srcAddr := wire.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
srcAddr := wire.NetAddressV2FromBytes(
time.Now(), 0, net.IPv4(173, 144, 173, 111), 8333,
)
n.AddAddresses(addrs, srcAddr)
for _, addr := range addrs {
@ -331,8 +349,8 @@ func TestGetAddress(t *testing.T) {
if ka == nil {
t.Fatalf("Did not get an address where there is one in the pool")
}
if ka.NetAddress().IP.String() != someIP {
t.Errorf("Wrong IP: got %v, want %v", ka.NetAddress().IP.String(), someIP)
if ka.NetAddress().Addr.String() != someIP {
t.Errorf("Wrong IP: got %v, want %v", ka.NetAddress().Addr.String(), someIP)
}
// Mark this as a good address and get it
@ -341,8 +359,8 @@ func TestGetAddress(t *testing.T) {
if ka == nil {
t.Fatalf("Did not get an address where there is one in the pool")
}
if ka.NetAddress().IP.String() != someIP {
t.Errorf("Wrong IP: got %v, want %v", ka.NetAddress().IP.String(), someIP)
if ka.NetAddress().Addr.String() != someIP {
t.Errorf("Wrong IP: got %v, want %v", ka.NetAddress().Addr.String(), someIP)
}
numAddrs := n.NumAddresses()
@ -352,43 +370,83 @@ func TestGetAddress(t *testing.T) {
}
func TestGetBestLocalAddress(t *testing.T) {
localAddrs := []wire.NetAddress{
{IP: net.ParseIP("192.168.0.100")},
{IP: net.ParseIP("::1")},
{IP: net.ParseIP("fe80::1")},
{IP: net.ParseIP("2001:470::1")},
localAddrs := []wire.NetAddressV2{
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.ParseIP("192.168.0.100"), 0,
),
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.ParseIP("::1"), 0,
),
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.ParseIP("fe80::1"), 0,
),
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.ParseIP("2001:470::1"), 0,
),
}
var tests = []struct {
remoteAddr wire.NetAddress
want0 wire.NetAddress
want1 wire.NetAddress
want2 wire.NetAddress
want3 wire.NetAddress
remoteAddr wire.NetAddressV2
want0 wire.NetAddressV2
want1 wire.NetAddressV2
want2 wire.NetAddressV2
want3 wire.NetAddressV2
}{
{
// Remote connection from public IPv4
wire.NetAddress{IP: net.ParseIP("204.124.8.1")},
wire.NetAddress{IP: net.IPv4zero},
wire.NetAddress{IP: net.IPv4zero},
wire.NetAddress{IP: net.ParseIP("204.124.8.100")},
wire.NetAddress{IP: net.ParseIP("fd87:d87e:eb43:25::1")},
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.ParseIP("204.124.8.1"), 0,
),
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.IPv4zero, 0,
),
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.IPv4zero, 0,
),
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.ParseIP("204.124.8.100"), 0,
),
*wire.NetAddressV2FromBytes(
time.Now(), 0,
net.ParseIP("fd87:d87e:eb43:25::1"), 0,
),
},
{
// Remote connection from private IPv4
wire.NetAddress{IP: net.ParseIP("172.16.0.254")},
wire.NetAddress{IP: net.IPv4zero},
wire.NetAddress{IP: net.IPv4zero},
wire.NetAddress{IP: net.IPv4zero},
wire.NetAddress{IP: net.IPv4zero},
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.ParseIP("172.16.0.254"), 0,
),
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.IPv4zero, 0,
),
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.IPv4zero, 0,
),
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.IPv4zero, 0,
),
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.IPv4zero, 0,
),
},
{
// Remote connection from public IPv6
wire.NetAddress{IP: net.ParseIP("2602:100:abcd::102")},
wire.NetAddress{IP: net.IPv6zero},
wire.NetAddress{IP: net.ParseIP("2001:470::1")},
wire.NetAddress{IP: net.ParseIP("2001:470::1")},
wire.NetAddress{IP: net.ParseIP("2001:470::1")},
*wire.NetAddressV2FromBytes(
time.Now(), 0,
net.ParseIP("2602:100:abcd::102"), 0,
),
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.IPv6zero, 0,
),
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.ParseIP("2001:470::1"), 0,
),
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.ParseIP("2001:470::1"), 0,
),
*wire.NetAddressV2FromBytes(
time.Now(), 0, net.ParseIP("2001:470::1"), 0,
),
},
/* XXX
{
@ -406,9 +464,12 @@ func TestGetBestLocalAddress(t *testing.T) {
// Test against default when there's no address
for x, test := range tests {
got := amgr.GetBestLocalAddress(&test.remoteAddr)
if !test.want0.IP.Equal(got.IP) {
wantAddr := test.want0.Addr.String()
gotAddr := got.Addr.String()
if wantAddr != gotAddr {
remoteAddr := test.remoteAddr.Addr.String()
t.Errorf("TestGetBestLocalAddress test1 #%d failed for remote address %s: want %s got %s",
x, test.remoteAddr.IP, test.want1.IP, got.IP)
x, remoteAddr, wantAddr, gotAddr)
continue
}
}
@ -420,23 +481,31 @@ func TestGetBestLocalAddress(t *testing.T) {
// Test against want1
for x, test := range tests {
got := amgr.GetBestLocalAddress(&test.remoteAddr)
if !test.want1.IP.Equal(got.IP) {
wantAddr := test.want1.Addr.String()
gotAddr := got.Addr.String()
if wantAddr != gotAddr {
remoteAddr := test.remoteAddr.Addr.String()
t.Errorf("TestGetBestLocalAddress test1 #%d failed for remote address %s: want %s got %s",
x, test.remoteAddr.IP, test.want1.IP, got.IP)
x, remoteAddr, wantAddr, gotAddr)
continue
}
}
// Add a public IP to the list of local addresses.
localAddr := wire.NetAddress{IP: net.ParseIP("204.124.8.100")}
amgr.AddLocalAddress(&localAddr, addrmgr.InterfacePrio)
localAddr := wire.NetAddressV2FromBytes(
time.Now(), 0, net.ParseIP("204.124.8.100"), 0,
)
amgr.AddLocalAddress(localAddr, addrmgr.InterfacePrio)
// Test against want2
for x, test := range tests {
got := amgr.GetBestLocalAddress(&test.remoteAddr)
if !test.want2.IP.Equal(got.IP) {
wantAddr := test.want2.Addr.String()
gotAddr := got.Addr.String()
if wantAddr != gotAddr {
remoteAddr := test.remoteAddr.Addr.String()
t.Errorf("TestGetBestLocalAddress test2 #%d failed for remote address %s: want %s got %s",
x, test.remoteAddr.IP, test.want2.IP, got.IP)
x, remoteAddr, wantAddr, gotAddr)
continue
}
}

View File

@ -18,7 +18,7 @@ func TstKnownAddressChance(ka *KnownAddress) float64 {
return ka.chance()
}
func TstNewKnownAddress(na *wire.NetAddress, attempts int,
func TstNewKnownAddress(na *wire.NetAddressV2, attempts int,
lastattempt, lastsuccess time.Time, tried bool, refs int) *KnownAddress {
return &KnownAddress{na: na, attempts: attempts, lastattempt: lastattempt,
lastsuccess: lastsuccess, tried: tried, refs: refs}

View File

@ -15,8 +15,8 @@ import (
// to determine how viable an address is.
type KnownAddress struct {
mtx sync.RWMutex // na and lastattempt
na *wire.NetAddress
srcAddr *wire.NetAddress
na *wire.NetAddressV2
srcAddr *wire.NetAddressV2
attempts int
lastattempt time.Time
lastsuccess time.Time
@ -24,9 +24,9 @@ type KnownAddress struct {
refs int // reference count of new buckets
}
// NetAddress returns the underlying wire.NetAddress associated with the
// NetAddress returns the underlying wire.NetAddressV2 associated with the
// known address.
func (ka *KnownAddress) NetAddress() *wire.NetAddress {
func (ka *KnownAddress) NetAddress() *wire.NetAddressV2 {
ka.mtx.RLock()
defer ka.mtx.RUnlock()
return ka.na

View File

@ -21,27 +21,27 @@ func TestChance(t *testing.T) {
}{
{
//Test normal case
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: now.Add(-35 * time.Second)},
addrmgr.TstNewKnownAddress(&wire.NetAddressV2{Timestamp: now.Add(-35 * time.Second)},
0, time.Now().Add(-30*time.Minute), time.Now(), false, 0),
1.0,
}, {
//Test case in which lastseen < 0
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: now.Add(20 * time.Second)},
addrmgr.TstNewKnownAddress(&wire.NetAddressV2{Timestamp: now.Add(20 * time.Second)},
0, time.Now().Add(-30*time.Minute), time.Now(), false, 0),
1.0,
}, {
//Test case in which lastattempt < 0
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: now.Add(-35 * time.Second)},
addrmgr.TstNewKnownAddress(&wire.NetAddressV2{Timestamp: now.Add(-35 * time.Second)},
0, time.Now().Add(30*time.Minute), time.Now(), false, 0),
1.0 * .01,
}, {
//Test case in which lastattempt < ten minutes
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: now.Add(-35 * time.Second)},
addrmgr.TstNewKnownAddress(&wire.NetAddressV2{Timestamp: now.Add(-35 * time.Second)},
0, time.Now().Add(-5*time.Minute), time.Now(), false, 0),
1.0 * .01,
}, {
//Test case with several failed attempts.
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: now.Add(-35 * time.Second)},
addrmgr.TstNewKnownAddress(&wire.NetAddressV2{Timestamp: now.Add(-35 * time.Second)},
2, time.Now().Add(-30*time.Minute), time.Now(), false, 0),
1 / 1.5 / 1.5,
},
@ -65,10 +65,10 @@ func TestIsBad(t *testing.T) {
hoursOld := now.Add(-5 * time.Hour)
zeroTime := time.Time{}
futureNa := &wire.NetAddress{Timestamp: future}
minutesOldNa := &wire.NetAddress{Timestamp: minutesOld}
monthOldNa := &wire.NetAddress{Timestamp: monthOld}
currentNa := &wire.NetAddress{Timestamp: secondsOld}
futureNa := &wire.NetAddressV2{Timestamp: future}
minutesOldNa := &wire.NetAddressV2{Timestamp: minutesOld}
monthOldNa := &wire.NetAddressV2{Timestamp: monthOld}
currentNa := &wire.NetAddressV2{Timestamp: secondsOld}
//Test addresses that have been tried in the last minute.
if addrmgr.TstKnownAddressIsBad(addrmgr.TstNewKnownAddress(futureNa, 3, secondsOld, zeroTime, false, 0)) {

View File

@ -222,11 +222,20 @@ func IsValid(na *wire.NetAddress) bool {
// IsRoutable returns whether or not the passed address is routable over
// the public internet. This is true as long as the address is valid and is not
// in any reserved ranges.
func IsRoutable(na *wire.NetAddress) bool {
return IsValid(na) && !(IsRFC1918(na) || IsRFC2544(na) ||
IsRFC3927(na) || IsRFC4862(na) || IsRFC3849(na) ||
IsRFC4843(na) || IsRFC5737(na) || IsRFC6598(na) ||
IsLocal(na) || (IsRFC4193(na) && !IsOnionCatTor(na)))
func IsRoutable(na *wire.NetAddressV2) bool {
if na.IsTorV3() {
// na is a torv3 address, return true.
return true
}
// Else na can be represented as a legacy NetAddress since i2p and
// cjdns are unsupported.
lna := na.ToLegacy()
return IsValid(lna) && !(IsRFC1918(lna) || IsRFC2544(lna) ||
IsRFC3927(lna) || IsRFC4862(lna) || IsRFC3849(lna) ||
IsRFC4843(lna) || IsRFC5737(lna) || IsRFC6598(lna) ||
IsLocal(lna) || (IsRFC4193(lna) &&
!IsOnionCatTor(lna)))
}
// GroupKey returns a string representing the network group an address is part
@ -234,48 +243,56 @@ func IsRoutable(na *wire.NetAddress) bool {
// "local" for a local address, the string "tor:key" where key is the /4 of the
// onion address for Tor address, and the string "unroutable" for an unroutable
// address.
func GroupKey(na *wire.NetAddress) string {
if IsLocal(na) {
func GroupKey(na *wire.NetAddressV2) string {
if na.IsTorV3() {
// na is a torv3 address. Use the same network group keying as
// for torv2.
return fmt.Sprintf("tor:%d", na.TorV3Key()&((1<<4)-1))
}
lna := na.ToLegacy()
if IsLocal(lna) {
return "local"
}
if !IsRoutable(na) {
return "unroutable"
}
if IsIPv4(na) {
return na.IP.Mask(net.CIDRMask(16, 32)).String()
if IsIPv4(lna) {
return lna.IP.Mask(net.CIDRMask(16, 32)).String()
}
if IsRFC6145(na) || IsRFC6052(na) {
if IsRFC6145(lna) || IsRFC6052(lna) {
// last four bytes are the ip address
ip := na.IP[12:16]
ip := lna.IP[12:16]
return ip.Mask(net.CIDRMask(16, 32)).String()
}
if IsRFC3964(na) {
ip := na.IP[2:6]
if IsRFC3964(lna) {
ip := lna.IP[2:6]
return ip.Mask(net.CIDRMask(16, 32)).String()
}
if IsRFC4380(na) {
if IsRFC4380(lna) {
// teredo tunnels have the last 4 bytes as the v4 address XOR
// 0xff.
ip := net.IP(make([]byte, 4))
for i, byte := range na.IP[12:16] {
for i, byte := range lna.IP[12:16] {
ip[i] = byte ^ 0xff
}
return ip.Mask(net.CIDRMask(16, 32)).String()
}
if IsOnionCatTor(na) {
if IsOnionCatTor(lna) {
// group is keyed off the first 4 bits of the actual onion key.
return fmt.Sprintf("tor:%d", na.IP[6]&((1<<4)-1))
return fmt.Sprintf("tor:%d", lna.IP[6]&((1<<4)-1))
}
// OK, so now we know ourselves to be a IPv6 address.
// bitcoind uses /32 for everything, except for Hurricane Electric's
// (he.net) IP range, which it uses /36 for.
bits := 32
if heNet.Contains(na.IP) {
if heNet.Contains(lna.IP) {
bits = 36
}
return na.IP.Mask(net.CIDRMask(bits, 128)).String()
return lna.IP.Mask(net.CIDRMask(bits, 128)).String()
}

View File

@ -7,6 +7,7 @@ package addrmgr_test
import (
"net"
"testing"
"time"
"github.com/btcsuite/btcd/addrmgr"
"github.com/btcsuite/btcd/wire"
@ -136,7 +137,10 @@ func TestIPTypes(t *testing.T) {
t.Errorf("IsValid %s\n got: %v want: %v", test.in.IP, rv, test.valid)
}
if rv := addrmgr.IsRoutable(&test.in); rv != test.routable {
currentNa := wire.NetAddressV2FromBytes(
time.Now(), test.in.Services, test.in.IP, test.in.Port,
)
if rv := addrmgr.IsRoutable(currentNa); rv != test.routable {
t.Errorf("IsRoutable %s\n got: %v want: %v", test.in.IP, rv, test.routable)
}
}
@ -192,8 +196,10 @@ func TestGroupKey(t *testing.T) {
for i, test := range tests {
nip := net.ParseIP(test.ip)
na := *wire.NewNetAddressIPPort(nip, 8333, wire.SFNodeNetwork)
if key := addrmgr.GroupKey(&na); key != test.expected {
na := wire.NetAddressV2FromBytes(
time.Now(), wire.SFNodeNetwork, nip, 8333,
)
if key := addrmgr.GroupKey(na); key != test.expected {
t.Errorf("TestGroupKey #%d (%s): unexpected group key "+
"- got '%s', want '%s'", i, test.name,
key, test.expected)

View File

@ -24,7 +24,7 @@ const (
// OnSeed is the signature of the callback function which is invoked when DNS
// seeding is succesfull.
type OnSeed func(addrs []*wire.NetAddress)
type OnSeed func(addrs []*wire.NetAddressV2)
// LookupFunc is the signature of the DNS lookup function.
type LookupFunc func(string) ([]net.IP, error)
@ -56,11 +56,11 @@ func SeedFromDNS(chainParams *chaincfg.Params, reqServices wire.ServiceFlag,
if numPeers == 0 {
return
}
addresses := make([]*wire.NetAddress, len(seedpeers))
addresses := make([]*wire.NetAddressV2, len(seedpeers))
// if this errors then we have *real* problems
intPort, _ := strconv.Atoi(chainParams.DefaultPort)
for i, peer := range seedpeers {
addresses[i] = wire.NewNetAddressTimestamp(
addresses[i] = wire.NetAddressV2FromBytes(
// bitcoind seeds with addresses from
// a time randomly selected between 3
// and 7 days ago.

View File

@ -29,7 +29,7 @@ import (
const (
// MaxProtocolVersion is the max protocol version the peer supports.
MaxProtocolVersion = wire.FeeFilterVersion
MaxProtocolVersion = wire.AddrV2Version
// DefaultTrickleInterval is the min time between attempts to send an
// inv message to a peer.
@ -102,6 +102,9 @@ type MessageListeners struct {
// OnAddr is invoked when a peer receives an addr bitcoin message.
OnAddr func(p *Peer, msg *wire.MsgAddr)
// OnAddrV2 is invoked when a peer receives an addrv2 bitcoin message.
OnAddrV2 func(p *Peer, msg *wire.MsgAddrV2)
// OnPing is invoked when a peer receives a ping bitcoin message.
OnPing func(p *Peer, msg *wire.MsgPing)
@ -197,6 +200,9 @@ type MessageListeners struct {
// message.
OnSendHeaders func(p *Peer, msg *wire.MsgSendHeaders)
// OnSendAddrV2 is invoked when a peer receives a sendaddrv2 message.
OnSendAddrV2 func(p *Peer, msg *wire.MsgSendAddrV2)
// OnRead is invoked when a peer receives a bitcoin message. It
// consists of the number of bytes read, the message, and whether or not
// an error in the read occurred. Typically, callers will opt to use
@ -399,7 +405,7 @@ type AddrFunc func(remoteAddr *wire.NetAddress) *wire.NetAddress
// HostToNetAddrFunc is a func which takes a host, port, services and returns
// the netaddress.
type HostToNetAddrFunc func(host string, port uint16,
services wire.ServiceFlag) (*wire.NetAddress, error)
services wire.ServiceFlag) (*wire.NetAddressV2, error)
// NOTE: The overall data flow of a peer is split into 3 goroutines. Inbound
// messages are read via the inHandler goroutine and generally dispatched to
@ -445,7 +451,7 @@ type Peer struct {
inbound bool
flagsMtx sync.Mutex // protects the peer flags below
na *wire.NetAddress
na *wire.NetAddressV2
id int32
userAgent string
services wire.ServiceFlag
@ -455,6 +461,7 @@ type Peer struct {
sendHeadersPreferred bool // peer sent a sendheaders message
verAckReceived bool
witnessEnabled bool
sendAddrV2 bool
wireEncoding wire.MessageEncoding
@ -585,7 +592,7 @@ func (p *Peer) ID() int32 {
// NA returns the peer network address.
//
// This function is safe for concurrent access.
func (p *Peer) NA() *wire.NetAddress {
func (p *Peer) NA() *wire.NetAddressV2 {
p.flagsMtx.Lock()
na := p.na
p.flagsMtx.Unlock()
@ -820,6 +827,16 @@ func (p *Peer) IsWitnessEnabled() bool {
return witnessEnabled
}
// WantsAddrV2 returns if the peer supports addrv2 messages instead of the
// legacy addr messages.
func (p *Peer) WantsAddrV2() bool {
p.flagsMtx.Lock()
wantsAddrV2 := p.sendAddrV2
p.flagsMtx.Unlock()
return wantsAddrV2
}
// PushAddrMsg sends an addr message to the connected peer using the provided
// addresses. This function is useful over manually sending the message via
// QueueMessage since it automatically limits the addresses to the maximum
@ -856,6 +873,38 @@ func (p *Peer) PushAddrMsg(addresses []*wire.NetAddress) ([]*wire.NetAddress, er
return msg.AddrList, nil
}
// PushAddrV2Msg is used to push an addrv2 message to the remote peer.
//
// This function is safe for concurrent access.
func (p *Peer) PushAddrV2Msg(addrs []*wire.NetAddressV2) (
[]*wire.NetAddressV2, error) {
count := len(addrs)
// Nothing to send.
if count == 0 {
return nil, nil
}
m := wire.NewMsgAddrV2()
m.AddrList = make([]*wire.NetAddressV2, count)
copy(m.AddrList, addrs)
// Randomize the addresses sent if there are more than the maximum.
if count > wire.MaxV2AddrPerMsg {
rand.Shuffle(count, func(i, j int) {
m.AddrList[i] = m.AddrList[j]
m.AddrList[j] = m.AddrList[i]
})
// Truncate it to the maximum size.
m.AddrList = m.AddrList[:wire.MaxV2AddrPerMsg]
}
p.QueueMessage(m, nil)
return m.AddrList, nil
}
// PushGetBlocksMsg sends a getblocks message for the provided block locator
// and stop hash. It will ignore back-to-back duplicate requests.
//
@ -1363,6 +1412,19 @@ out:
continue
}
// Since the protocol version is 70016 but we don't
// implement compact blocks, we have to ignore unknown
// messages after the version-verack handshake. This
// matches bitcoind's behavior and is necessary since
// compact blocks negotiation occurs after the
// handshake.
if err == wire.ErrUnknownMessage {
log.Debugf("Received unknown message from %s:"+
" %v", p, err)
idleTimer.Reset(idleTimeout)
continue
}
// Only log the error and send reject message if the
// local peer is not forcibly disconnecting and the
// remote peer has not disconnected.
@ -1404,6 +1466,11 @@ out:
)
break out
case *wire.MsgSendAddrV2:
// Disconnect if peer sends this after the handshake is
// completed.
break out
case *wire.MsgGetAddr:
if p.cfg.Listeners.OnGetAddr != nil {
p.cfg.Listeners.OnGetAddr(p, msg)
@ -1414,6 +1481,11 @@ out:
p.cfg.Listeners.OnAddr(p, msg)
}
case *wire.MsgAddrV2:
if p.cfg.Listeners.OnAddrV2 != nil {
p.cfg.Listeners.OnAddrV2(p, msg)
}
case *wire.MsgPing:
p.handlePingMsg(msg)
if p.cfg.Listeners.OnPing != nil {
@ -1986,29 +2058,8 @@ func (p *Peer) readRemoteVersionMsg() error {
return nil
}
// readRemoteVerAckMsg waits for the next message to arrive from the remote
// peer. If this message is not a verack message, then an error is returned.
// This method is to be used as part of the version negotiation upon a new
// connection.
func (p *Peer) readRemoteVerAckMsg() error {
// Read the next message from the wire.
remoteMsg, _, err := p.readMessage(wire.LatestEncoding)
if err != nil {
return err
}
// It should be a verack message, otherwise send a reject message to the
// peer explaining why.
msg, ok := remoteMsg.(*wire.MsgVerAck)
if !ok {
reason := "a verack message must follow version"
rejectMsg := wire.NewMsgReject(
msg.Command(), wire.RejectMalformed, reason,
)
_ = p.writeMessage(rejectMsg, wire.LatestEncoding)
return errors.New(reason)
}
// processRemoteVerAckMsg takes the verack from the remote peer and handles it.
func (p *Peer) processRemoteVerAckMsg(msg *wire.MsgVerAck) {
p.flagsMtx.Lock()
p.verAckReceived = true
p.flagsMtx.Unlock()
@ -2016,8 +2067,6 @@ func (p *Peer) readRemoteVerAckMsg() error {
if p.cfg.Listeners.OnVerAck != nil {
p.cfg.Listeners.OnVerAck(p, msg)
}
return nil
}
// localVersionMsg creates a version message that can be used to send to the
@ -2032,7 +2081,15 @@ func (p *Peer) localVersionMsg() (*wire.MsgVersion, error) {
}
}
theirNA := p.na
theirNA := p.na.ToLegacy()
// If p.na is a torv3 hidden service address, we'll need to send over
// an empty NetAddress for their address.
if p.na.IsTorV3() {
theirNA = wire.NewNetAddressIPPort(
net.IP([]byte{0, 0, 0, 0}), p.na.Port, p.na.Services,
)
}
// If we are behind a proxy and the connection comes from the proxy then
// we return an unroutable address as their address. This is to prevent
@ -2040,7 +2097,7 @@ func (p *Peer) localVersionMsg() (*wire.MsgVersion, error) {
if p.cfg.Proxy != "" {
proxyaddress, _, err := net.SplitHostPort(p.cfg.Proxy)
// invalid proxy means poorly configured, be on the safe side.
if err != nil || p.na.IP.String() == proxyaddress {
if err != nil || p.na.Addr.String() == proxyaddress {
theirNA = wire.NewNetAddressIPPort(net.IP([]byte{0, 0, 0, 0}), 0,
theirNA.Services)
}
@ -2092,14 +2149,71 @@ func (p *Peer) writeLocalVersionMsg() error {
return p.writeMessage(localVerMsg, wire.LatestEncoding)
}
// writeSendAddrV2Msg writes our sendaddrv2 message to the remote peer if the
// peer supports protocol version 70016 and above.
func (p *Peer) writeSendAddrV2Msg(pver uint32) error {
if pver < wire.AddrV2Version {
return nil
}
sendAddrMsg := wire.NewMsgSendAddrV2()
return p.writeMessage(sendAddrMsg, wire.LatestEncoding)
}
// waitToFinishNegotiation waits until desired negotiation messages are
// received, recording the remote peer's preference for sendaddrv2 as an
// example. The list of negotiated features can be expanded in the future. If a
// verack is received, negotiation stops and the connection is live.
func (p *Peer) waitToFinishNegotiation(pver uint32) error {
// There are several possible messages that can be received here. We
// could immediately receive verack and be done with the handshake. We
// could receive sendaddrv2 and still have to wait for verack. Or we
// can receive unknown messages before and after sendaddrv2 and still
// have to wait for verack.
for {
remoteMsg, _, err := p.readMessage(wire.LatestEncoding)
if err == wire.ErrUnknownMessage {
continue
} else if err != nil {
return err
}
switch m := remoteMsg.(type) {
case *wire.MsgSendAddrV2:
if pver >= wire.AddrV2Version {
p.flagsMtx.Lock()
p.sendAddrV2 = true
p.flagsMtx.Unlock()
if p.cfg.Listeners.OnSendAddrV2 != nil {
p.cfg.Listeners.OnSendAddrV2(p, m)
}
}
case *wire.MsgVerAck:
// Receiving a verack means we are done with the
// handshake.
p.processRemoteVerAckMsg(m)
return nil
default:
// This is triggered if the peer sends, for example, a
// GETDATA message during this negotiation.
return wire.ErrInvalidHandshake
}
}
}
// negotiateInboundProtocol performs the negotiation protocol for an inbound
// peer. The events should occur in the following order, otherwise an error is
// returned:
//
// 1. Remote peer sends their version.
// 2. We send our version.
// 3. We send our verack.
// 4. Remote peer sends their verack.
// 3. We send sendaddrv2 if their version is >= 70016.
// 4. We send our verack.
// 5. Wait until sendaddrv2 or verack is received. Unknown messages are
// skipped as it could be wtxidrelay or a different message in the future
// that btcd does not implement but bitcoind does.
// 6. If remote peer sent sendaddrv2 above, wait until receipt of verack.
func (p *Peer) negotiateInboundProtocol() error {
if err := p.readRemoteVersionMsg(); err != nil {
return err
@ -2109,12 +2223,22 @@ func (p *Peer) negotiateInboundProtocol() error {
return err
}
var protoVersion uint32
p.flagsMtx.Lock()
protoVersion = p.protocolVersion
p.flagsMtx.Unlock()
if err := p.writeSendAddrV2Msg(protoVersion); err != nil {
return err
}
err := p.writeMessage(wire.NewMsgVerAck(), wire.LatestEncoding)
if err != nil {
return err
}
return p.readRemoteVerAckMsg()
// Finish the negotiation by waiting for negotiable messages or verack.
return p.waitToFinishNegotiation(protoVersion)
}
// negotiateOutboundProtocol performs the negotiation protocol for an outbound
@ -2123,8 +2247,11 @@ func (p *Peer) negotiateInboundProtocol() error {
//
// 1. We send our version.
// 2. Remote peer sends their version.
// 3. Remote peer sends their verack.
// 3. We send sendaddrv2 if their version is >= 70016.
// 4. We send our verack.
// 5. We wait to receive sendaddrv2 or verack, skipping unknown messages as
// in the inbound case.
// 6. If sendaddrv2 was received, wait for receipt of verack.
func (p *Peer) negotiateOutboundProtocol() error {
if err := p.writeLocalVersionMsg(); err != nil {
return err
@ -2134,11 +2261,22 @@ func (p *Peer) negotiateOutboundProtocol() error {
return err
}
if err := p.readRemoteVerAckMsg(); err != nil {
var protoVersion uint32
p.flagsMtx.Lock()
protoVersion = p.protocolVersion
p.flagsMtx.Unlock()
if err := p.writeSendAddrV2Msg(protoVersion); err != nil {
return err
}
return p.writeMessage(wire.NewMsgVerAck(), wire.LatestEncoding)
err := p.writeMessage(wire.NewMsgVerAck(), wire.LatestEncoding)
if err != nil {
return err
}
// Finish the negotiation by waiting for negotiable messages or verack.
return p.waitToFinishNegotiation(protoVersion)
}
// start begins processing input and output messages.
@ -2201,7 +2339,12 @@ func (p *Peer) AssociateConnection(conn net.Conn) {
p.Disconnect()
return
}
p.na = na
// Convert the NetAddress created above into NetAddressV2.
currentNa := wire.NetAddressV2FromBytes(
na.Timestamp, na.Services, na.IP, na.Port,
)
p.na = currentNa
}
go func() {
@ -2267,7 +2410,10 @@ func NewInboundPeer(cfg *Config) *Peer {
return newPeerBase(cfg, true)
}
// NewOutboundPeer returns a new outbound bitcoin peer.
// NewOutboundPeer returns a new outbound bitcoin peer. If the Config argument
// does not set HostToNetAddress, connecting to anything other than an ipv4 or
// ipv6 address will fail and may cause a nil-pointer-dereference. This
// includes hostnames and onion services.
func NewOutboundPeer(cfg *Config, addr string) (*Peer, error) {
p := newPeerBase(cfg, false)
p.addr = addr
@ -2289,7 +2435,12 @@ func NewOutboundPeer(cfg *Config, addr string) (*Peer, error) {
}
p.na = na
} else {
p.na = wire.NewNetAddressIPPort(net.ParseIP(host), uint16(port), 0)
// If host is an onion hidden service or a hostname, it is
// likely that a nil-pointer-dereference will occur. The caller
// should set HostToNetAddress if connecting to these.
p.na = wire.NetAddressV2FromBytes(
time.Now(), 0, net.ParseIP(host), uint16(port),
)
}
return p, nil

View File

@ -289,18 +289,16 @@ func TestPeerConnection(t *testing.T) {
{
"basic handshake",
func() (*peer.Peer, *peer.Peer, error) {
inConn, outConn := pipe(
&conn{raddr: "10.0.0.1:8333"},
&conn{raddr: "10.0.0.2:8333"},
)
inPeer := peer.NewInboundPeer(peer1Cfg)
inPeer.AssociateConnection(inConn)
outPeer, err := peer.NewOutboundPeer(peer2Cfg, "10.0.0.2:8333")
if err != nil {
return nil, nil, err
}
outPeer.AssociateConnection(outConn)
err = setupPeerConnection(inPeer, outPeer)
if err != nil {
return nil, nil, err
}
for i := 0; i < 4; i++ {
select {
@ -315,18 +313,16 @@ func TestPeerConnection(t *testing.T) {
{
"socks proxy",
func() (*peer.Peer, *peer.Peer, error) {
inConn, outConn := pipe(
&conn{raddr: "10.0.0.1:8333", proxy: true},
&conn{raddr: "10.0.0.2:8333"},
)
inPeer := peer.NewInboundPeer(peer1Cfg)
inPeer.AssociateConnection(inConn)
outPeer, err := peer.NewOutboundPeer(peer2Cfg, "10.0.0.2:8333")
if err != nil {
return nil, nil, err
}
outPeer.AssociateConnection(outConn)
err = setupPeerConnection(inPeer, outPeer)
if err != nil {
return nil, nil, err
}
for i := 0; i < 4; i++ {
select {
@ -359,7 +355,7 @@ func TestPeerConnection(t *testing.T) {
// TestPeerListeners tests that the peer listeners are called as expected.
func TestPeerListeners(t *testing.T) {
verack := make(chan struct{}, 1)
ok := make(chan wire.Message, 20)
ok := make(chan wire.Message, 22)
peerCfg := &peer.Config{
Listeners: peer.MessageListeners{
OnGetAddr: func(p *peer.Peer, msg *wire.MsgGetAddr) {
@ -447,6 +443,12 @@ func TestPeerListeners(t *testing.T) {
OnSendHeaders: func(p *peer.Peer, msg *wire.MsgSendHeaders) {
ok <- msg
},
OnSendAddrV2: func(p *peer.Peer, msg *wire.MsgSendAddrV2) {
ok <- msg
},
OnAddrV2: func(p *peer.Peer, msg *wire.MsgAddrV2) {
ok <- msg
},
},
UserAgentName: "peer",
UserAgentVersion: "1.0",
@ -456,12 +458,7 @@ func TestPeerListeners(t *testing.T) {
TrickleInterval: time.Second * 10,
AllowSelfConns: true,
}
inConn, outConn := pipe(
&conn{raddr: "10.0.0.1:8333"},
&conn{raddr: "10.0.0.2:8333"},
)
inPeer := peer.NewInboundPeer(peerCfg)
inPeer.AssociateConnection(inConn)
peerCfg.Listeners = peer.MessageListeners{
OnVerAck: func(p *peer.Peer, msg *wire.MsgVerAck) {
@ -473,7 +470,12 @@ func TestPeerListeners(t *testing.T) {
t.Errorf("NewOutboundPeer: unexpected err %v\n", err)
return
}
outPeer.AssociateConnection(outConn)
err = setupPeerConnection(inPeer, outPeer)
if err != nil {
t.Errorf("setupPeerConnection: failed: %v\n", err)
return
}
for i := 0; i < 2; i++ {
select {
@ -597,6 +599,14 @@ func TestPeerListeners(t *testing.T) {
"OnSendHeaders",
wire.NewMsgSendHeaders(),
},
{
"OnSendAddrV2",
wire.NewMsgSendAddrV2(),
},
{
"OnAddrV2",
wire.NewMsgAddrV2(),
},
}
t.Logf("Running %d tests", len(tests))
for _, test := range tests {
@ -881,17 +891,17 @@ func TestDuplicateVersionMsg(t *testing.T) {
Services: 0,
AllowSelfConns: true,
}
inConn, outConn := pipe(
&conn{laddr: "10.0.0.1:9108", raddr: "10.0.0.2:9108"},
&conn{laddr: "10.0.0.2:9108", raddr: "10.0.0.1:9108"},
)
outPeer, err := peer.NewOutboundPeer(peerCfg, inConn.laddr)
outPeer, err := peer.NewOutboundPeer(peerCfg, "10.0.0.2:8333")
if err != nil {
t.Fatalf("NewOutboundPeer: unexpected err: %v\n", err)
}
outPeer.AssociateConnection(outConn)
inPeer := peer.NewInboundPeer(peerCfg)
inPeer.AssociateConnection(inConn)
err = setupPeerConnection(inPeer, outPeer)
if err != nil {
t.Fatalf("setupPeerConnection failed to connect: %v\n", err)
}
// Wait for the veracks from the initial protocol version negotiation.
for i := 0; i < 2; i++ {
select {
@ -947,17 +957,16 @@ func TestUpdateLastBlockHeight(t *testing.T) {
remotePeerCfg.NewestBlock = func() (*chainhash.Hash, int32, error) {
return &chainhash.Hash{}, remotePeerHeight, nil
}
inConn, outConn := pipe(
&conn{laddr: "10.0.0.1:9108", raddr: "10.0.0.2:9108"},
&conn{laddr: "10.0.0.2:9108", raddr: "10.0.0.1:9108"},
)
localPeer, err := peer.NewOutboundPeer(&peerCfg, inConn.laddr)
localPeer, err := peer.NewOutboundPeer(&peerCfg, "10.0.0.2:8333")
if err != nil {
t.Fatalf("NewOutboundPeer: unexpected err: %v\n", err)
}
localPeer.AssociateConnection(outConn)
inPeer := peer.NewInboundPeer(&remotePeerCfg)
inPeer.AssociateConnection(inConn)
err = setupPeerConnection(inPeer, localPeer)
if err != nil {
t.Fatalf("setupPeerConnection failed to connect: %v\n", err)
}
// Wait for the veracks from the initial protocol version negotiation.
for i := 0; i < 2; i++ {
@ -989,3 +998,214 @@ func TestUpdateLastBlockHeight(t *testing.T) {
remotePeerHeight+1)
}
}
// setupPeerConnection initiates a tcp connection between two peers.
func setupPeerConnection(in, out *peer.Peer) error {
// listenFunc is a function closure that listens for a tcp connection.
// The tcp connection will be the one the inbound peer uses. This will
// be run as a goroutine.
listenFunc := func(l *net.TCPListener, errChan chan error,
listenChan chan struct{}) {
listenChan <- struct{}{}
conn, err := l.Accept()
if err != nil {
errChan <- err
return
}
in.AssociateConnection(conn)
errChan <- nil
}
// dialFunc is a function closure that initiates the tcp connection.
// The tcp connection will be the one the outbound peer uses.
dialFunc := func(addr *net.TCPAddr) error {
conn, err := net.Dial("tcp", addr.String())
if err != nil {
return err
}
out.AssociateConnection(conn)
return nil
}
listenAddr := "localhost:0"
addr, err := net.ResolveTCPAddr("tcp", listenAddr)
if err != nil {
return err
}
l, err := net.ListenTCP("tcp", addr)
if err != nil {
return err
}
errChan := make(chan error, 1)
listenChan := make(chan struct{}, 1)
go listenFunc(l, errChan, listenChan)
<-listenChan
if err := dialFunc(l.Addr().(*net.TCPAddr)); err != nil {
return err
}
select {
case err = <-errChan:
return err
case <-time.After(time.Second * 2):
return errors.New("failed to create connection")
}
}
// TestSendAddrV2Handshake tests that the version-verack handshake with the
// addition of the sendaddrv2 message works as expected.
func TestSendAddrV2Handshake(t *testing.T) {
verack := make(chan struct{}, 2)
sendaddr := make(chan struct{}, 2)
peer1Cfg := &peer.Config{
Listeners: peer.MessageListeners{
OnVerAck: func(p *peer.Peer, msg *wire.MsgVerAck) {
verack <- struct{}{}
},
OnSendAddrV2: func(p *peer.Peer,
msg *wire.MsgSendAddrV2) {
sendaddr <- struct{}{}
},
},
AllowSelfConns: true,
ChainParams: &chaincfg.MainNetParams,
}
peer2Cfg := &peer.Config{
Listeners: peer1Cfg.Listeners,
AllowSelfConns: true,
ChainParams: &chaincfg.MainNetParams,
}
verackErr := errors.New("verack timeout")
tests := []struct {
name string
expectsV2 bool
setup func() (*peer.Peer, *peer.Peer, error)
}{
{
"successful sendaddrv2 handshake",
true,
func() (*peer.Peer, *peer.Peer, error) {
inPeer := peer.NewInboundPeer(peer1Cfg)
outPeer, err := peer.NewOutboundPeer(
peer2Cfg, "10.0.0.2:8333",
)
if err != nil {
return nil, nil, err
}
err = setupPeerConnection(inPeer, outPeer)
if err != nil {
return nil, nil, err
}
for i := 0; i < 4; i++ {
select {
case <-sendaddr:
case <-verack:
case <-time.After(time.Second * 2):
return nil, nil, verackErr
}
}
return inPeer, outPeer, nil
},
},
{
"handshake with legacy inbound peer",
false,
func() (*peer.Peer, *peer.Peer, error) {
legacyVersion := wire.AddrV2Version - 1
peer1Cfg.ProtocolVersion = legacyVersion
inPeer := peer.NewInboundPeer(peer1Cfg)
outPeer, err := peer.NewOutboundPeer(
peer2Cfg, "10.0.0.2:8333",
)
if err != nil {
return nil, nil, err
}
err = setupPeerConnection(inPeer, outPeer)
if err != nil {
return nil, nil, err
}
for i := 0; i < 2; i++ {
select {
case <-verack:
case <-time.After(time.Second * 2):
return nil, nil, verackErr
}
}
return inPeer, outPeer, nil
},
},
{
"handshake with legacy outbound peer",
false,
func() (*peer.Peer, *peer.Peer, error) {
inPeer := peer.NewInboundPeer(peer1Cfg)
legacyVersion := wire.AddrV2Version - 1
peer2Cfg.ProtocolVersion = legacyVersion
outPeer, err := peer.NewOutboundPeer(
peer2Cfg, "10.0.0.2:8333",
)
if err != nil {
return nil, nil, err
}
err = setupPeerConnection(inPeer, outPeer)
if err != nil {
return nil, nil, err
}
for i := 0; i < 2; i++ {
select {
case <-verack:
case <-time.After(time.Second * 2):
return nil, nil, verackErr
}
}
return inPeer, outPeer, nil
},
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
inPeer, outPeer, err := test.setup()
if err != nil {
t.Fatalf("TestSendAddrV2Handshake setup #%d: "+
"unexpected err: %v", i, err)
}
if inPeer.WantsAddrV2() != test.expectsV2 {
t.Fatalf("TestSendAddrV2Handshake #%d expected "+
"wantsAddrV2 to be %v instead was %v", i,
test.expectsV2, inPeer.WantsAddrV2())
} else if outPeer.WantsAddrV2() != test.expectsV2 {
t.Fatalf("TestSendAddrV2Handshake #%d expected "+
"wantsAddrV2 to be %v instead was %v", i,
test.expectsV2, outPeer.WantsAddrV2())
}
inPeer.Disconnect()
outPeer.Disconnect()
inPeer.WaitForDisconnect()
outPeer.WaitForDisconnect()
}
}

View File

@ -228,7 +228,7 @@ func (cm *rpcConnManager) RelayTransactions(txns []*mempool.TxDesc) {
//
// This function is safe for concurrent access and is part of the
// rpcserverConnManager interface implementation.
func (cm *rpcConnManager) NodeAddresses() []*wire.NetAddress {
func (cm *rpcConnManager) NodeAddresses() []*wire.NetAddressV2 {
return cm.server.addrManager.AddressCache()
}

View File

@ -2521,7 +2521,7 @@ func handleGetNodeAddresses(s *rpcServer, cmd interface{}, closeChan <-chan stru
address := &btcjson.GetNodeAddressesResult{
Time: node.Timestamp.Unix(),
Services: uint64(node.Services),
Address: node.IP.String(),
Address: node.Addr.String(),
Port: node.Port,
}
addresses = append(addresses, address)
@ -4527,7 +4527,7 @@ type rpcserverConnManager interface {
// NodeAddresses returns an array consisting node addresses which can
// potentially be used to find new nodes in the network.
NodeAddresses() []*wire.NetAddress
NodeAddresses() []*wire.NetAddressV2
}
// rpcserverSyncManager represents a sync manager for use with the RPC server.

148
server.go
View File

@ -25,6 +25,8 @@ import (
"github.com/btcsuite/btcd/addrmgr"
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/blockchain/indexers"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/btcutil/bloom"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/connmgr"
@ -36,8 +38,7 @@ import (
"github.com/btcsuite/btcd/peer"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/btcutil/bloom"
"github.com/decred/dcrd/lru"
)
const (
@ -274,7 +275,7 @@ type serverPeer struct {
isWhitelisted bool
filter *bloom.Filter
addressesMtx sync.RWMutex
knownAddresses map[string]struct{}
knownAddresses lru.Cache
banScore connmgr.DynamicBanScore
quit chan struct{}
// The following chans are used to sync blockmanager and server.
@ -289,7 +290,7 @@ func newServerPeer(s *server, isPersistent bool) *serverPeer {
server: s,
persistent: isPersistent,
filter: bloom.LoadFilter(nil),
knownAddresses: make(map[string]struct{}),
knownAddresses: lru.NewCache(5000),
quit: make(chan struct{}),
txProcessed: make(chan struct{}, 1),
blockProcessed: make(chan struct{}, 1),
@ -305,18 +306,18 @@ func (sp *serverPeer) newestBlock() (*chainhash.Hash, int32, error) {
// addKnownAddresses adds the given addresses to the set of known addresses to
// the peer to prevent sending duplicate addresses.
func (sp *serverPeer) addKnownAddresses(addresses []*wire.NetAddress) {
func (sp *serverPeer) addKnownAddresses(addresses []*wire.NetAddressV2) {
sp.addressesMtx.Lock()
for _, na := range addresses {
sp.knownAddresses[addrmgr.NetAddressKey(na)] = struct{}{}
sp.knownAddresses.Add(addrmgr.NetAddressKey(na))
}
sp.addressesMtx.Unlock()
}
// addressKnown true if the given address is already known to the peer.
func (sp *serverPeer) addressKnown(na *wire.NetAddress) bool {
func (sp *serverPeer) addressKnown(na *wire.NetAddressV2) bool {
sp.addressesMtx.RLock()
_, exists := sp.knownAddresses[addrmgr.NetAddressKey(na)]
exists := sp.knownAddresses.Contains(addrmgr.NetAddressKey(na))
sp.addressesMtx.RUnlock()
return exists
}
@ -340,23 +341,73 @@ func (sp *serverPeer) relayTxDisabled() bool {
return isDisabled
}
// pushAddrMsg sends an addr message to the connected peer using the provided
// addresses.
func (sp *serverPeer) pushAddrMsg(addresses []*wire.NetAddress) {
// Filter addresses already known to the peer.
addrs := make([]*wire.NetAddress, 0, len(addresses))
for _, addr := range addresses {
if !sp.addressKnown(addr) {
// pushAddrMsg sends a legacy addr message to the connected peer using the
// provided addresses.
func (sp *serverPeer) pushAddrMsg(addresses []*wire.NetAddressV2) {
if sp.WantsAddrV2() {
// If the peer supports addrv2, we'll be pushing an addrv2
// message instead. The logic is otherwise identical to the
// addr case below.
addrs := make([]*wire.NetAddressV2, 0, len(addresses))
for _, addr := range addresses {
// Filter addresses already known to the peer.
if sp.addressKnown(addr) {
continue
}
addrs = append(addrs, addr)
}
known, err := sp.PushAddrV2Msg(addrs)
if err != nil {
peerLog.Errorf("Can't push addrv2 message to %s: %v",
sp.Peer, err)
sp.Disconnect()
return
}
// Add the final set of addresses sent to the set the peer
// knows of.
sp.addKnownAddresses(known)
return
}
addrs := make([]*wire.NetAddress, 0, len(addresses))
for _, addr := range addresses {
// Filter addresses already known to the peer.
if sp.addressKnown(addr) {
continue
}
// Must skip the V3 addresses for legacy ADDR messages.
if addr.IsTorV3() {
continue
}
// Convert the NetAddressV2 to a legacy address.
addrs = append(addrs, addr.ToLegacy())
}
known, err := sp.PushAddrMsg(addrs)
if err != nil {
peerLog.Errorf("Can't push address message to %s: %v", sp.Peer, err)
peerLog.Errorf(
"Can't push address message to %s: %v", sp.Peer, err,
)
sp.Disconnect()
return
}
sp.addKnownAddresses(known)
// Convert all of the known addresses to NetAddressV2 to add them to
// the set of known addresses.
knownAddrs := make([]*wire.NetAddressV2, 0, len(known))
for _, knownAddr := range known {
currentKna := wire.NetAddressV2FromBytes(
knownAddr.Timestamp, knownAddr.Services,
knownAddr.IP, knownAddr.Port,
)
knownAddrs = append(knownAddrs, currentKna)
}
sp.addKnownAddresses(knownAddrs)
}
// addBanScore increases the persistent and decaying ban score fields by the
@ -1272,6 +1323,7 @@ func (sp *serverPeer) OnAddr(_ *peer.Peer, msg *wire.MsgAddr) {
return
}
addrs := make([]*wire.NetAddressV2, 0, len(msg.AddrList))
for _, na := range msg.AddrList {
// Don't add more address if we're disconnecting.
if !sp.Connected() {
@ -1286,8 +1338,14 @@ func (sp *serverPeer) OnAddr(_ *peer.Peer, msg *wire.MsgAddr) {
na.Timestamp = now.Add(-1 * time.Hour * 24 * 5)
}
// Add address to known addresses for this peer.
sp.addKnownAddresses([]*wire.NetAddress{na})
// Add address to known addresses for this peer. This is
// converted to NetAddressV2 since that's what the address
// manager uses.
currentNa := wire.NetAddressV2FromBytes(
na.Timestamp, na.Services, na.IP, na.Port,
)
addrs = append(addrs, currentNa)
sp.addKnownAddresses([]*wire.NetAddressV2{currentNa})
}
// Add addresses to server address manager. The address manager handles
@ -1295,6 +1353,45 @@ func (sp *serverPeer) OnAddr(_ *peer.Peer, msg *wire.MsgAddr) {
// addresses, and last seen updates.
// XXX bitcoind gives a 2 hour time penalty here, do we want to do the
// same?
sp.server.addrManager.AddAddresses(addrs, sp.NA())
}
// OnAddrV2 is invoked when a peer receives an addrv2 bitcoin message and is
// used to notify the server about advertised addresses.
func (sp *serverPeer) OnAddrV2(_ *peer.Peer, msg *wire.MsgAddrV2) {
// Ignore if simnet for the same reasons as the regular addr message.
if cfg.SimNet {
return
}
// An empty AddrV2 message is invalid.
if len(msg.AddrList) == 0 {
peerLog.Errorf("Command [%s] from %s does not contain any "+
"addresses", msg.Command(), sp.Peer)
sp.Disconnect()
return
}
for _, na := range msg.AddrList {
// Don't add more to the set of known addresses if we're
// disconnecting.
if !sp.Connected() {
return
}
// Set the timestamp to 5 days ago if the timestamp received is
// more than 10 minutes in the future so this address is one of
// the first to be removed.
now := time.Now()
if na.Timestamp.After(now.Add(time.Minute * 10)) {
na.Timestamp = now.Add(-1 * time.Hour * 24 * 5)
}
// Add to the set of known addresses.
sp.addKnownAddresses([]*wire.NetAddressV2{na})
}
// Add the addresses to the addrmanager.
sp.server.addrManager.AddAddresses(msg.AddrList, sp.NA())
}
@ -1700,7 +1797,7 @@ func (s *server) handleAddPeerMsg(state *peerState, sp *serverPeer) bool {
lna := s.addrManager.GetBestLocalAddress(sp.NA())
if addrmgr.IsRoutable(lna) {
// Filter addresses the peer already knows about.
addresses := []*wire.NetAddress{lna}
addresses := []*wire.NetAddressV2{lna}
sp.pushAddrMsg(addresses)
}
}
@ -2044,6 +2141,7 @@ func newPeerConfig(sp *serverPeer) *peer.Config {
OnFilterLoad: sp.OnFilterLoad,
OnGetAddr: sp.OnGetAddr,
OnAddr: sp.OnAddr,
OnAddrV2: sp.OnAddrV2,
OnRead: sp.OnRead,
OnWrite: sp.OnWrite,
OnNotFound: sp.OnNotFound,
@ -2152,7 +2250,7 @@ func (s *server) peerHandler() {
if !cfg.DisableDNSSeed {
// Add peers discovered through DNS to the address manager.
connmgr.SeedFromDNS(activeNetParams.Params, defaultRequiredServices,
btcdLookup, func(addrs []*wire.NetAddress) {
btcdLookup, func(addrs []*wire.NetAddressV2) {
// Bitcoind uses a lookup of the dns seeder here. This
// is rather strange since the values looked up by the
// DNS seed lookups will vary quite a lot.
@ -2543,8 +2641,8 @@ out:
srvrLog.Warnf("UPnP can't get external address: %v", err)
continue out
}
na := wire.NewNetAddressIPPort(externalip, uint16(listenPort),
s.services)
na := wire.NetAddressV2FromBytes(time.Now(), s.services,
externalip, uint16(listenPort))
err = s.addrManager.AddLocalAddress(na, addrmgr.UpnpPrio)
if err != nil {
// XXX DeletePortMapping?
@ -3117,7 +3215,9 @@ func addLocalAddress(addrMgr *addrmgr.AddrManager, addr string, services wire.Se
continue
}
netAddr := wire.NewNetAddressIPPort(ifaceIP, uint16(port), services)
netAddr := wire.NetAddressV2FromBytes(
time.Now(), services, ifaceIP, uint16(port),
)
addrMgr.AddLocalAddress(netAddr, addrmgr.BoundPrio)
}
} else {

View File

@ -32,6 +32,7 @@ const (
CmdVerAck = "verack"
CmdGetAddr = "getaddr"
CmdAddr = "addr"
CmdAddrV2 = "addrv2"
CmdGetBlocks = "getblocks"
CmdInv = "inv"
CmdGetData = "getdata"
@ -78,6 +79,13 @@ const (
// protocol.
var LatestEncoding = WitnessEncoding
// ErrUnknownMessage is the error returned when decoding an unknown message.
var ErrUnknownMessage = fmt.Errorf("received unknown message")
// ErrInvalidHandshake is the error returned when a peer sends us a known
// message that does not belong in the version-verack handshake.
var ErrInvalidHandshake = fmt.Errorf("invalid message during handshake")
// Message is an interface that describes a bitcoin message. A type that
// implements Message has complete control over the representation of its data
// and may therefore contain additional or fewer fields than those which
@ -109,6 +117,9 @@ func makeEmptyMessage(command string) (Message, error) {
case CmdAddr:
msg = &MsgAddr{}
case CmdAddrV2:
msg = &MsgAddrV2{}
case CmdGetBlocks:
msg = &MsgGetBlocks{}
@ -185,7 +196,7 @@ func makeEmptyMessage(command string) (Message, error) {
msg = &MsgCFCheckpt{}
default:
return nil, fmt.Errorf("unhandled command [%s]", command)
return nil, ErrUnknownMessage
}
return msg, nil
}
@ -378,9 +389,10 @@ func ReadMessageWithEncodingN(r io.Reader, pver uint32, btcnet BitcoinNet,
// Create struct of appropriate message type based on the command.
msg, err := makeEmptyMessage(command)
if err != nil {
// makeEmptyMessage can only return ErrUnknownMessage and it is
// important that we bubble it up to the caller.
discardInput(r, hdr.length)
return totalBytes, nil, nil, messageError("ReadMessage",
err.Error())
return totalBytes, nil, nil, err
}
// Check for maximum length based on the message type as a malicious client

View File

@ -295,7 +295,7 @@ func TestReadMessageWireErrors(t *testing.T) {
pver,
btcnet,
len(unsupportedCommandBytes),
&MessageError{},
ErrUnknownMessage,
24,
},
@ -345,7 +345,7 @@ func TestReadMessageWireErrors(t *testing.T) {
pver,
btcnet,
len(discardBytes),
&MessageError{},
ErrUnknownMessage,
24,
},
}

102
wire/msgaddrv2.go Normal file
View File

@ -0,0 +1,102 @@
package wire
import (
"fmt"
"io"
)
// MaxV2AddrPerMsg is the maximum number of version 2 addresses that will exist
// in a single addrv2 message (MsgAddrV2).
const MaxV2AddrPerMsg = 1000
// MsgAddrV2 implements the Message interface and represents a bitcoin addrv2
// message that can support longer-length addresses like torv3, cjdns, and i2p.
// It is used to gossip addresses on the network. Each message is limited to
// MaxV2AddrPerMsg addresses. This is the same limit as MsgAddr.
type MsgAddrV2 struct {
AddrList []*NetAddressV2
}
// BtcDecode decodes r using the bitcoin protocol into a MsgAddrV2.
func (m *MsgAddrV2) BtcDecode(r io.Reader, pver uint32,
enc MessageEncoding) error {
count, err := ReadVarInt(r, pver)
if err != nil {
return err
}
// Limit to max addresses per message.
if count > MaxV2AddrPerMsg {
str := fmt.Sprintf("too many addresses for message [count %v,"+
" max %v]", count, MaxV2AddrPerMsg)
return messageError("MsgAddrV2.BtcDecode", str)
}
addrList := make([]NetAddressV2, count)
m.AddrList = make([]*NetAddressV2, 0, count)
for i := uint64(0); i < count; i++ {
na := &addrList[i]
err := readNetAddressV2(r, pver, na)
switch err {
case ErrSkippedNetworkID:
// This may be a network ID we don't know of, but is
// still valid. We can safely skip those.
continue
case ErrInvalidAddressSize:
// The encoding used by the peer does not follow
// BIP-155 and we should stop processing this message.
return err
}
m.AddrList = append(m.AddrList, na)
}
return nil
}
// BtcEncode encodes the MsgAddrV2 into a writer w.
func (m *MsgAddrV2) BtcEncode(w io.Writer, pver uint32,
enc MessageEncoding) error {
count := len(m.AddrList)
if count > MaxV2AddrPerMsg {
str := fmt.Sprintf("too many addresses for message [count %v,"+
" max %v]", count, MaxV2AddrPerMsg)
return messageError("MsgAddrV2.BtcEncode", str)
}
err := WriteVarInt(w, pver, uint64(count))
if err != nil {
return err
}
for _, na := range m.AddrList {
err = writeNetAddressV2(w, pver, na)
if err != nil {
return err
}
}
return nil
}
// Command returns the protocol command string for MsgAddrV2.
func (m *MsgAddrV2) Command() string {
return CmdAddrV2
}
// MaxPayloadLength returns the maximum length payload possible for MsgAddrV2.
func (m *MsgAddrV2) MaxPayloadLength(pver uint32) uint32 {
// The varint that can store the maximum number of addresses is 3 bytes
// long. The maximum payload is then 3 + 1000 * maxNetAddressV2Payload.
return 3 + (MaxV2AddrPerMsg * maxNetAddressV2Payload())
}
// NewMsgAddrV2 returns a new bitcoin addrv2 message that conforms to the
// Message interface.
func NewMsgAddrV2() *MsgAddrV2 {
return &MsgAddrV2{
AddrList: make([]*NetAddressV2, 0, MaxV2AddrPerMsg),
}
}

73
wire/msgaddrv2_test.go Normal file
View File

@ -0,0 +1,73 @@
package wire
import (
"bytes"
"io"
"testing"
)
// TestAddrV2Decode checks that decoding an addrv2 message off the wire behaves
// as expected. This means ignoring certain addresses, and failing in certain
// failure scenarios.
func TestAddrV2Decode(t *testing.T) {
tests := []struct {
buf []byte
expectedError bool
expectedAddrs int
}{
// Exceeding max addresses.
{
[]byte{0xfd, 0xff, 0xff},
true,
0,
},
// Invalid address size.
{
[]byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x05},
true,
0,
},
// One valid address and one skipped address
{
[]byte{
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04,
0x7f, 0x00, 0x00, 0x01, 0x22, 0x22, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x10, 0xfd, 0x87, 0xd8,
0x7e, 0xeb, 0x43, 0xff, 0xfe, 0xcc, 0x39, 0xa8,
0x73, 0x69, 0x15, 0xff, 0xff, 0x22, 0x22,
},
false,
1,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
r := bytes.NewReader(test.buf)
m := &MsgAddrV2{}
err := m.BtcDecode(r, 0, LatestEncoding)
if test.expectedError {
if err == nil {
t.Errorf("Test #%d expected error", i)
}
continue
} else if err != nil {
t.Errorf("Test #%d unexpected error %v", i, err)
}
// Trying to read more should give EOF.
var b [1]byte
if _, err := r.Read(b[:]); err != io.EOF {
t.Errorf("Test #%d did not cleanly finish reading", i)
}
if len(m.AddrList) != test.expectedAddrs {
t.Errorf("Test #%d expected %d addrs, instead of %d",
i, test.expectedAddrs, len(m.AddrList))
}
}
}

588
wire/netaddressv2.go Normal file
View File

@ -0,0 +1,588 @@
package wire
import (
"encoding/base32"
"encoding/binary"
"fmt"
"io"
"net"
"strings"
"time"
"golang.org/x/crypto/sha3"
)
const (
// maxAddrV2Size is the maximum size an address may be in the addrv2
// message.
maxAddrV2Size = 512
)
var (
// ErrInvalidAddressSize is an error that means an incorrect address
// size was decoded for a networkID or that the address exceeded the
// maximum size for an unknown networkID.
ErrInvalidAddressSize = fmt.Errorf("invalid address size")
// ErrSkippedNetworkID is returned when the cjdns, i2p, or unknown
// networks are encountered during decoding. btcd does not support i2p
// or cjdns addresses. In the case of an unknown networkID, this is so
// that a future BIP reserving a new networkID does not cause older
// addrv2-supporting btcd software to disconnect upon receiving the new
// addresses. This error can also be returned when an OnionCat-encoded
// torv2 address is received with the ipv6 networkID. This error
// signals to the caller to continue reading.
ErrSkippedNetworkID = fmt.Errorf("skipped networkID")
)
// maxNetAddressV2Payload returns the max payload size for an address used in
// the addrv2 message.
func maxNetAddressV2Payload() uint32 {
// The timestamp takes up four bytes.
plen := uint32(4)
// The ServiceFlag is a varint and its maximum size is 9 bytes.
plen += 9
// The netID is a single byte.
plen += 1
// The largest address is 512 bytes. Even though it will not be a valid
// address, we should read and ignore it. The preceeding varint to
// store 512 bytes is 3 bytes long. This gives us a total of 515 bytes.
plen += 515
// The port is 2 bytes.
plen += 2
return plen
}
// isOnionCatTor returns whether a given ip address is actually an encoded tor
// v2 address. The wire package is unable to use the addrmgr's IsOnionCatTor as
// doing so would give an import cycle.
func isOnionCatTor(ip net.IP) bool {
onionCatNet := net.IPNet{
IP: net.ParseIP("fd87:d87e:eb43::"),
Mask: net.CIDRMask(48, 128),
}
return onionCatNet.Contains(ip)
}
// NetAddressV2 defines information about a peer on the network including the
// last time it was seen, the services it supports, its address, and port. This
// struct is used in the addrv2 message (MsgAddrV2) and can contain larger
// addresses, like Tor. Additionally, it can contain any NetAddress address.
type NetAddressV2 struct {
// Last time the address was seen. This is, unfortunately, encoded as a
// uint32 on the wire and therefore is limited to 2106. This field is
// not present in the bitcoin version message (MsgVersion) nor was it
// added until protocol version >= NetAddressTimeVersion.
Timestamp time.Time
// Services is a bitfield which identifies the services supported by
// the address. This is encoded in CompactSize.
Services ServiceFlag
// Addr is the network address of the peer. This is a variable-length
// address. Network() returns the BIP-155 networkID which is a uint8
// encoded as a string. String() returns the address as a string.
Addr net.Addr
// Port is the port of the address. This is 0 if the network doesn't
// use ports.
Port uint16
}
// HasService returns whether the specified service is supported by the
// address.
func (na *NetAddressV2) HasService(service ServiceFlag) bool {
return na.Services&service == service
}
// AddService adds a service to the Services bitfield.
func (na *NetAddressV2) AddService(service ServiceFlag) {
na.Services |= service
}
// ToLegacy attempts to convert a NetAddressV2 to a legacy NetAddress. This
// only works for ipv4, ipv6, or torv2 addresses as they can be encoded with
// the OnionCat encoding. If this method is called on a torv3 address, nil will
// be returned.
func (na *NetAddressV2) ToLegacy() *NetAddress {
legacyNa := &NetAddress{
Timestamp: na.Timestamp,
Services: na.Services,
Port: na.Port,
}
switch a := na.Addr.(type) {
case *ipv4Addr:
legacyNa.IP = a.addr[:]
case *ipv6Addr:
legacyNa.IP = a.addr[:]
case *torv2Addr:
legacyNa.IP = a.onionCatEncoding()
case *torv3Addr:
return nil
}
return legacyNa
}
// IsTorV3 returns a bool that signals to the caller whether or not this is a
// torv3 address.
func (na *NetAddressV2) IsTorV3() bool {
_, ok := na.Addr.(*torv3Addr)
return ok
}
// TorV3Key returns the first byte of the v3 public key. This is used in the
// addrmgr to calculate a key from a network group.
func (na *NetAddressV2) TorV3Key() byte {
// This should never be called on a non-torv3 address.
addr, ok := na.Addr.(*torv3Addr)
if !ok {
panic("unexpected TorV3Key call on non-torv3 address")
}
return addr.addr[0]
}
// NetAddressV2FromBytes creates a NetAddressV2 from a byte slice. It will
// also handle a torv2 address using the OnionCat encoding.
func NetAddressV2FromBytes(timestamp time.Time, services ServiceFlag,
addrBytes []byte, port uint16) *NetAddressV2 {
var netAddr net.Addr
switch len(addrBytes) {
case ipv4Size:
addr := &ipv4Addr{}
addr.netID = ipv4
copy(addr.addr[:], addrBytes)
netAddr = addr
case ipv6Size:
if isOnionCatTor(addrBytes) {
addr := &torv2Addr{}
addr.netID = torv2
copy(addr.addr[:], addrBytes[6:])
netAddr = addr
break
}
addr := &ipv6Addr{}
addr.netID = ipv6
copy(addr.addr[:], addrBytes)
netAddr = addr
case torv2Size:
addr := &torv2Addr{}
addr.netID = torv2
copy(addr.addr[:], addrBytes)
netAddr = addr
case TorV3Size:
addr := &torv3Addr{}
addr.netID = torv3
copy(addr.addr[:], addrBytes)
netAddr = addr
}
return &NetAddressV2{
Timestamp: timestamp,
Services: services,
Addr: netAddr,
Port: port,
}
}
// writeNetAddressV2 writes a NetAddressV2 to a writer.
func writeNetAddressV2(w io.Writer, pver uint32, na *NetAddressV2) error {
err := writeElement(w, uint32(na.Timestamp.Unix()))
if err != nil {
return err
}
if err := WriteVarInt(w, pver, uint64(na.Services)); err != nil {
return err
}
var (
netID networkID
address []byte
)
switch a := na.Addr.(type) {
case *ipv4Addr:
netID = a.netID
address = a.addr[:]
case *ipv6Addr:
netID = a.netID
address = a.addr[:]
case *torv2Addr:
netID = a.netID
address = a.addr[:]
case *torv3Addr:
netID = a.netID
address = a.addr[:]
default:
// This should not occur.
return fmt.Errorf("unexpected address type")
}
if err := writeElement(w, netID); err != nil {
return err
}
addressSize := uint64(len(address))
if err := WriteVarInt(w, pver, addressSize); err != nil {
return err
}
if err := writeElement(w, address); err != nil {
return err
}
return binary.Write(w, bigEndian, na.Port)
}
// readNetAddressV2 reads a NetAddressV2 from a reader. This function has
// checks that the corresponding write function doesn't. This is because
// reading from the peer is untrusted whereas writing assumes we have already
// validated the NetAddressV2.
func readNetAddressV2(r io.Reader, pver uint32, na *NetAddressV2) error {
err := readElement(r, (*uint32Time)(&na.Timestamp))
if err != nil {
return err
}
// Services is encoded as a variable length integer in addrv2.
services, err := ReadVarInt(r, pver)
if err != nil {
return err
}
na.Services = ServiceFlag(services)
var netID uint8
if err := readElement(r, &netID); err != nil {
return err
}
decodedSize, err := ReadVarInt(r, pver)
if err != nil {
return err
}
if !isKnownNetworkID(netID) {
// In the case of an unknown networkID, we'll read the address
// size and error with ErrInvalidAddressSize if it's greater
// than maxAddrV2Size. If the address size is within the valid
// range, we'll just read and discard the address. In this
// case, ErrSkippedNetworkID will be returned to signal to the
// caller to continue reading.
if decodedSize > maxAddrV2Size {
return ErrInvalidAddressSize
}
// The +2 is the port field.
discardedAddrPort := make([]byte, decodedSize+2)
if err := readElement(r, &discardedAddrPort); err != nil {
return err
}
return ErrSkippedNetworkID
}
// If the netID is an i2p or cjdns address, we'll advance the reader
// and return a special error to signal to the caller to not use the
// passed NetAddressV2 struct. Otherwise, we'll just read the address
// and port without returning an error.
switch networkID(netID) {
case ipv4:
addr := &ipv4Addr{}
addr.netID = ipv4
if decodedSize != uint64(ipv4Size) {
return ErrInvalidAddressSize
}
if err := readElement(r, &addr.addr); err != nil {
return err
}
na.Port, err = binarySerializer.Uint16(r, bigEndian)
if err != nil {
return err
}
na.Addr = addr
case ipv6:
addr := &ipv6Addr{}
addr.netID = ipv6
if decodedSize != uint64(ipv6Size) {
return ErrInvalidAddressSize
}
if err := readElement(r, &addr.addr); err != nil {
return err
}
na.Port, err = binarySerializer.Uint16(r, bigEndian)
if err != nil {
return err
}
na.Addr = addr
// BIP-155 says to ignore OnionCat addresses in addrv2
// messages.
if isOnionCatTor(addr.addr[:]) {
return ErrSkippedNetworkID
}
case torv2:
addr := &torv2Addr{}
addr.netID = torv2
if decodedSize != uint64(torv2Size) {
return ErrInvalidAddressSize
}
if err := readElement(r, &addr.addr); err != nil {
return err
}
na.Port, err = binarySerializer.Uint16(r, bigEndian)
if err != nil {
return err
}
na.Addr = addr
case torv3:
addr := &torv3Addr{}
addr.netID = torv3
if decodedSize != uint64(TorV3Size) {
return ErrInvalidAddressSize
}
if err := readElement(r, &addr.addr); err != nil {
return err
}
na.Port, err = binarySerializer.Uint16(r, bigEndian)
if err != nil {
return err
}
// BIP-155 does not specify to validate the public key here.
// bitcoind does not validate the ed25519 pubkey.
na.Addr = addr
case i2p:
addr := &i2pAddr{}
addr.netID = i2p
if decodedSize != uint64(i2pSize) {
return ErrInvalidAddressSize
}
if err := readElement(r, &addr.addr); err != nil {
return err
}
na.Port, err = binarySerializer.Uint16(r, bigEndian)
if err != nil {
return err
}
return ErrSkippedNetworkID
case cjdns:
addr := &cjdnsAddr{}
addr.netID = cjdns
if decodedSize != uint64(cjdnsSize) {
return ErrInvalidAddressSize
}
if err := readElement(r, &addr.addr); err != nil {
return err
}
na.Port, err = binarySerializer.Uint16(r, bigEndian)
if err != nil {
return err
}
return ErrSkippedNetworkID
}
return nil
}
// networkID represents the network that a given address is in. CJDNS and I2P
// addresses are not included.
type networkID uint8
const (
// ipv4 means the following address is ipv4.
ipv4 networkID = iota + 1
// ipv6 means the following address is ipv6.
ipv6
// torv2 means the following address is a torv2 hidden service address.
torv2
// torv3 means the following address is a torv3 hidden service address.
torv3
// i2p means the following address is an i2p address.
i2p
// cjdns means the following address is a cjdns address.
cjdns
)
const (
// ipv4Size is the size of an ipv4 address.
ipv4Size = 4
// ipv6Size is the size of an ipv6 address.
ipv6Size = 16
// torv2Size is the size of a torv2 address.
torv2Size = 10
// TorV3Size is the size of a torv3 address in bytes.
TorV3Size = 32
// i2pSize is the size of an i2p address.
i2pSize = 32
// cjdnsSize is the size of a cjdns address.
cjdnsSize = 16
)
const (
// TorV2EncodedSize is the size of a torv2 address encoded in base32
// with the ".onion" suffix.
TorV2EncodedSize = 22
// TorV3EncodedSize is the size of a torv3 address encoded in base32
// with the ".onion" suffix.
TorV3EncodedSize = 62
)
// isKnownNetworkID returns true if the networkID is one listed above and false
// otherwise.
func isKnownNetworkID(netID uint8) bool {
return uint8(ipv4) <= netID && netID <= uint8(cjdns)
}
type ipv4Addr struct {
addr [ipv4Size]byte
netID networkID
}
// Part of the net.Addr interface.
func (a *ipv4Addr) String() string {
return net.IP(a.addr[:]).String()
}
// Part of the net.Addr interface.
func (a *ipv4Addr) Network() string {
return string(a.netID)
}
// Compile-time constraints to check that ipv4Addr meets the net.Addr
// interface.
var _ net.Addr = (*ipv4Addr)(nil)
type ipv6Addr struct {
addr [ipv6Size]byte
netID networkID
}
// Part of the net.Addr interface.
func (a *ipv6Addr) String() string {
return net.IP(a.addr[:]).String()
}
// Part of the net.Addr interface.
func (a *ipv6Addr) Network() string {
return string(a.netID)
}
// Compile-time constraints to check that ipv6Addr meets the net.Addr
// interface.
var _ net.Addr = (*ipv4Addr)(nil)
type torv2Addr struct {
addr [torv2Size]byte
netID networkID
}
// Part of the net.Addr interface.
func (a *torv2Addr) String() string {
base32Hash := base32.StdEncoding.EncodeToString(a.addr[:])
return strings.ToLower(base32Hash) + ".onion"
}
// Part of the net.Addr interface.
func (a *torv2Addr) Network() string {
return string(a.netID)
}
// onionCatEncoding returns a torv2 address as an ipv6 address.
func (a *torv2Addr) onionCatEncoding() net.IP {
prefix := []byte{0xfd, 0x87, 0xd8, 0x7e, 0xeb, 0x43}
return net.IP(append(prefix, a.addr[:]...))
}
// Compile-time constraints to check that torv2Addr meets the net.Addr
// interface.
var _ net.Addr = (*torv2Addr)(nil)
type torv3Addr struct {
addr [TorV3Size]byte
netID networkID
}
// Part of the net.Addr interface.
func (a *torv3Addr) String() string {
// BIP-155 describes the torv3 address format:
// onion_address = base32(PUBKEY | CHECKSUM | VERSION) + ".onion"
// CHECKSUM = H(".onion checksum" | PUBKEY | VERSION)[:2]
// PUBKEY = addr, which is the ed25519 pubkey of the hidden service.
// VERSION = '\x03'
// H() is the SHA3-256 cryptographic hash function.
torV3Version := []byte("\x03")
checksumConst := []byte(".onion checksum")
// Write never returns an error so there is no need to handle it.
h := sha3.New256()
h.Write(checksumConst)
h.Write(a.addr[:])
h.Write(torV3Version)
truncatedChecksum := h.Sum(nil)[:2]
var base32Input [35]byte
copy(base32Input[:32], a.addr[:])
copy(base32Input[32:34], truncatedChecksum)
copy(base32Input[34:], torV3Version)
base32Hash := base32.StdEncoding.EncodeToString(base32Input[:])
return strings.ToLower(base32Hash) + ".onion"
}
// Part of the net.Addr interface.
func (a *torv3Addr) Network() string {
return string(a.netID)
}
// Compile-time constraints to check that torv3Addr meets the net.Addr
// interface.
var _ net.Addr = (*torv3Addr)(nil)
type i2pAddr struct {
addr [i2pSize]byte
netID networkID
}
type cjdnsAddr struct {
addr [cjdnsSize]byte
netID networkID
}

285
wire/netaddressv2_test.go Normal file
View File

@ -0,0 +1,285 @@
package wire
import (
"bytes"
"io"
"testing"
"time"
)
// TestNetAddressV2FromBytes tests that NetAddressV2FromBytes works as
// expected.
func TestNetAddressV2FromBytes(t *testing.T) {
tests := []struct {
addrBytes []byte
expectedString string
expectedNetwork string
}{
// Ipv4 encoding
{
[]byte{0x7f, 0x00, 0x00, 0x01},
"127.0.0.1",
string(ipv4),
},
// Ipv6 encoding
{
[]byte{
0x20, 0x01, 0x09, 0xe8, 0x26, 0x15, 0x73, 0x00,
0x09, 0x54, 0x12, 0x63, 0xef, 0xc8, 0x2e, 0x34,
},
"2001:9e8:2615:7300:954:1263:efc8:2e34",
string(ipv6),
},
// OnionCat encoding
{
[]byte{
0xfd, 0x87, 0xd8, 0x7e, 0xeb, 0x43, 0xff, 0xfe,
0xcc, 0x39, 0xa8, 0x73, 0x69, 0x15, 0xff, 0xff,
},
"777myonionurl777.onion",
string(torv2),
},
// Torv2 encoding
{
[]byte{
0xff, 0xfe, 0xcc, 0x39, 0xa8, 0x73, 0x69, 0x15,
0xff, 0xff,
},
"777myonionurl777.onion",
string(torv2),
},
// Torv3 encoding
{
[]byte{
0xca, 0xd2, 0xd3, 0xc8, 0xdc, 0x9c, 0xc4, 0xd3,
0x70, 0x33, 0x30, 0xc5, 0x23, 0xaf, 0x02, 0xed,
0xc4, 0x9d, 0xf8, 0xc6, 0xb0, 0x4e, 0x74, 0x6d,
0x3b, 0x51, 0x57, 0xa7, 0x15, 0xfe, 0x98, 0x35,
},
"zljnhsg4ttcng4btgdcshlyc5xcj36ggwbhhi3j3kfl2ofp6ta26jlid.onion",
string(torv3),
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
na := NetAddressV2FromBytes(time.Time{}, 0, test.addrBytes, 0)
if test.expectedNetwork != string(torv3) {
if na.ToLegacy() == nil {
t.Errorf("Test #%d has nil legacy encoding", i)
}
} else {
if !na.IsTorV3() {
t.Errorf("Test #%d is not torv3 address", i)
}
}
if na.Addr.String() != test.expectedString {
t.Errorf("Test #%d did not match expected string", i)
}
if na.Addr.Network() != test.expectedNetwork {
t.Errorf("Test #%d did not match expected network", i)
}
var b bytes.Buffer
if err := writeNetAddressV2(&b, 0, na); err != nil {
t.Errorf("Test #%d failed writing address %v", i, err)
}
// Assert that the written netID is equivalent to the above.
if string(b.Bytes()[5]) != test.expectedNetwork {
t.Errorf("Test #%d did not match expected network", i)
}
}
}
// TestReadNetAddressV2 tests that readNetAddressV2 behaves as expected in
// different scenarios.
func TestReadNetAddressV2(t *testing.T) {
tests := []struct {
buf []byte
expectedNetwork string
expectedError error
}{
// Invalid address size for unknown netID.
{
[]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfd, 0xff,
0xff,
},
"",
ErrInvalidAddressSize,
},
// Valid address size for unknown netID.
{
[]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x20, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x22,
0x22,
},
"",
ErrSkippedNetworkID,
},
// Invalid ipv4 size.
{
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x05},
"",
ErrInvalidAddressSize,
},
// Valid ipv4 encoding.
{
[]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x7f,
0x00, 0x00, 0x01, 0x22, 0x22,
},
string(ipv4),
nil,
},
// Invalid ipv6 size.
{
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xfc},
"",
ErrInvalidAddressSize,
},
// OnionCat encoding is skipped.
{
[]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0xfd,
0x87, 0xd8, 0x7e, 0xeb, 0x43, 0xff, 0xfe, 0xcc,
0x39, 0xa8, 0x73, 0x69, 0x15, 0xff, 0xff, 0x22,
0x22,
},
"",
ErrSkippedNetworkID,
},
// Valid ipv6 encoding.
{
[]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x22,
0x22,
},
string(ipv6),
nil,
},
// Invalid torv2 size.
{
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02},
"",
ErrInvalidAddressSize,
},
// Valid torv2 encoding.
{
[]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x22, 0x22,
},
string(torv2),
nil,
},
// Invalid torv3 size.
{
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02},
"",
ErrInvalidAddressSize,
},
// Valid torv3 encoding.
{
[]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x22,
0x22,
},
string(torv3),
nil,
},
// Invalid i2p size.
{
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x02},
"",
ErrInvalidAddressSize,
},
// Valid i2p encoding.
{
[]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x22,
0x22,
},
string(i2p),
ErrSkippedNetworkID,
},
// Invalid cjdns size.
{
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x02},
"",
ErrInvalidAddressSize,
},
// Valid cjdns encoding.
{
[]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22,
0x22,
},
string(cjdns),
ErrSkippedNetworkID,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
r := bytes.NewReader(test.buf)
na := &NetAddressV2{}
err := readNetAddressV2(r, 0, na)
if err != test.expectedError {
t.Errorf("Test #%d had unexpected error %v", i, err)
} else if err != nil {
continue
}
// Trying to read more should give EOF.
var b [1]byte
if _, err := r.Read(b[:]); err != io.EOF {
t.Errorf("Test #%d did not cleanly finish reading", i)
}
if na.Addr.Network() != test.expectedNetwork {
t.Errorf("Test #%d had unexpected network %v", i,
na.Addr.Network())
}
}
}

View File

@ -13,7 +13,7 @@ import (
// XXX pedro: we will probably need to bump this.
const (
// ProtocolVersion is the latest protocol version this package supports.
ProtocolVersion uint32 = 70013
ProtocolVersion uint32 = 70016
// MultipleAddressVersion is the protocol version which added multiple
// addresses per message (pver >= MultipleAddressVersion).
@ -51,6 +51,13 @@ const (
// FeeFilterVersion is the protocol version which added a new
// feefilter message.
FeeFilterVersion uint32 = 70013
// AddrV2Version is the protocol version which added two new messages.
// sendaddrv2 is sent during the version-verack handshake and signals
// support for sending and receiving the addrv2 message. In the future,
// new messages that occur during the version-verack handshake will not
// come with a protocol version bump.
AddrV2Version uint32 = 70016
)
// ServiceFlag identifies services supported by a bitcoin peer.