mirror of
https://github.com/btcsuite/btcd.git
synced 2024-11-19 09:50:08 +01:00
589 lines
14 KiB
Go
589 lines
14 KiB
Go
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
|
|
}
|