btcd/wire/netaddressv2.go
2023-11-20 12:04:31 -05:00

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 preceding 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
}