diff --git a/docs/release-notes/release-notes-0.15.0.md b/docs/release-notes/release-notes-0.15.0.md index 23daa4b71..05c11b92f 100644 --- a/docs/release-notes/release-notes-0.15.0.md +++ b/docs/release-notes/release-notes-0.15.0.md @@ -136,6 +136,9 @@ close to continue after a peer disconnect](https://github.com/lightningnetwork/l * [A subsystem hand-off between the contractcourt and htlcswitch has been fixed by adding a persistence layer. This avoids a rare edge case from occurring that would result in an erroneous force close.](https://github.com/lightningnetwork/lnd/pull/6250) +* [Ignore addresses with unknown types in NodeAnnouncements]( + https://github.com/lightningnetwork/lnd/pull/6435) + ## Routing * [Add a new `time_pref` parameter to the QueryRoutes and SendPayment APIs](https://github.com/lightningnetwork/lnd/pull/6024) that diff --git a/lnwire/lnwire.go b/lnwire/lnwire.go index 290fc4ed0..0361d7648 100644 --- a/lnwire/lnwire.go +++ b/lnwire/lnwire.go @@ -804,7 +804,29 @@ func ReadElement(r io.Reader, element interface{}) error { addrBytesRead += aType.AddrLen() default: - return &ErrUnknownAddrType{aType} + // If we don't understand this address type, + // we just store it along with the remaining + // address bytes as type OpaqueAddrs. We need + // to hold onto the bytes so that we can still + // write them back to the wire when we + // propagate this message. + payloadLen := 1 + addrsLen - addrBytesRead + payload := make([]byte, payloadLen) + + // First write a byte for the address type that + // we already read. + payload[0] = byte(aType) + + // Now append the rest of the address bytes. + _, err := io.ReadFull(addrBuf, payload[1:]) + if err != nil { + return err + } + + address = &OpaqueAddrs{ + Payload: payload, + } + addrBytesRead = addrsLen } addresses = append(addresses, address) diff --git a/lnwire/lnwire_test.go b/lnwire/lnwire_test.go index 0e3671d85..ec528a38a 100644 --- a/lnwire/lnwire_test.go +++ b/lnwire/lnwire_test.go @@ -20,6 +20,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/tor" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var ( @@ -158,6 +159,22 @@ func randV3OnionAddr(r *rand.Rand) (*tor.OnionAddr, error) { return &tor.OnionAddr{OnionService: onionService, Port: addrPort}, nil } +func randOpaqueAddr(r *rand.Rand) (*OpaqueAddrs, error) { + payloadLen := r.Int63n(64) + 1 + payload := make([]byte, payloadLen) + + // The first byte is the address type. So set it to one that we + // definitely don't know about. + payload[0] = math.MaxUint8 + + // Generate random bytes for the rest of the payload. + if _, err := r.Read(payload[1:]); err != nil { + return nil, err + } + + return &OpaqueAddrs{Payload: payload}, nil +} + func randAddrs(r *rand.Rand) ([]net.Addr, error) { tcp4Addr, err := randTCP4Addr(r) if err != nil { @@ -179,7 +196,14 @@ func randAddrs(r *rand.Rand) ([]net.Addr, error) { return nil, err } - return []net.Addr{tcp4Addr, tcp6Addr, v2OnionAddr, v3OnionAddr}, nil + opaqueAddrs, err := randOpaqueAddr(r) + if err != nil { + return nil, err + } + + return []net.Addr{ + tcp4Addr, tcp6Addr, v2OnionAddr, v3OnionAddr, opaqueAddrs, + }, nil } // TestChanUpdateChanFlags ensures that converting the ChanUpdateChanFlags and @@ -226,6 +250,45 @@ func TestChanUpdateChanFlags(t *testing.T) { } } +// TestDecodeUnknownAddressType shows that an unknown address type is currently +// incorrectly dealt with. +func TestDecodeUnknownAddressType(t *testing.T) { + // Add a normal, clearnet address. + tcpAddr := &net.TCPAddr{ + IP: net.IP{127, 0, 0, 1}, + Port: 8080, + } + + // Add an onion address. + onionAddr := &tor.OnionAddr{ + OnionService: "abcdefghijklmnop.onion", + Port: 9065, + } + + // Now add an address with an unknown type. + var newAddrType addressType = math.MaxUint8 + data := make([]byte, 0, 16) + data = append(data, uint8(newAddrType)) + opaqueAddrs := &OpaqueAddrs{ + Payload: data, + } + + buffer := bytes.NewBuffer(make([]byte, 0, MaxMsgBody)) + err := WriteNetAddrs( + buffer, []net.Addr{tcpAddr, onionAddr, opaqueAddrs}, + ) + require.NoError(t, err) + + // Now we attempt to parse the bytes and assert that we get an error. + var addrs []net.Addr + err = ReadElement(buffer, &addrs) + require.NoError(t, err) + require.Len(t, addrs, 3) + require.Equal(t, tcpAddr.String(), addrs[0].String()) + require.Equal(t, onionAddr.String(), addrs[1].String()) + require.Equal(t, hex.EncodeToString(data), addrs[2].String()) +} + func TestMaxOutPointIndex(t *testing.T) { t.Parallel() diff --git a/lnwire/opaque_addrs.go b/lnwire/opaque_addrs.go new file mode 100644 index 000000000..92f59ea21 --- /dev/null +++ b/lnwire/opaque_addrs.go @@ -0,0 +1,34 @@ +package lnwire + +import ( + "encoding/hex" + "net" +) + +// OpaqueAddrs is used to store the address bytes for address types that are +// unknown to us. +type OpaqueAddrs struct { + Payload []byte +} + +// A compile-time assertion to ensure that OpaqueAddrs meets the net.Addr +// interface. +var _ net.Addr = (*OpaqueAddrs)(nil) + +// String returns a human-readable string describing the target OpaqueAddrs. +// Since this is an unknown address (and could even be multiple addresses), we +// just return the hex string of the payload. +// +// This part of the net.Addr interface. +func (o *OpaqueAddrs) String() string { + return hex.EncodeToString(o.Payload) +} + +// Network returns the name of the network this address is bound to. Since this +// is an unknown address, we don't know the network and so just return a string +// indicating this. +// +// This part of the net.Addr interface. +func (o *OpaqueAddrs) Network() string { + return "unknown network for unrecognized address type" +} diff --git a/lnwire/writer.go b/lnwire/writer.go index 084e7d892..5b99ab368 100644 --- a/lnwire/writer.go +++ b/lnwire/writer.go @@ -32,6 +32,9 @@ var ( // ErrNilNetAddress is returned when a nil value is used in []net.Addr. ErrNilNetAddress = errors.New("cannot write nil address") + // ErrNilOpaqueAddrs is returned when the supplied address is nil. + ErrNilOpaqueAddrs = errors.New("cannot write nil OpaqueAddrs") + // ErrNilPublicKey is returned when a nil pubkey is used. ErrNilPublicKey = errors.New("cannot write nil pubkey") @@ -357,6 +360,16 @@ func WriteOnionAddr(buf *bytes.Buffer, addr *tor.OnionAddr) error { return WriteUint16(buf, uint16(addr.Port)) } +// WriteOpaqueAddrs appends the payload of the given OpaqueAddrs to buffer. +func WriteOpaqueAddrs(buf *bytes.Buffer, addr *OpaqueAddrs) error { + if addr == nil { + return ErrNilOpaqueAddrs + } + + _, err := buf.Write(addr.Payload) + return err +} + // WriteNetAddrs appends a slice of addresses to the provided buffer with the // length info. func WriteNetAddrs(buf *bytes.Buffer, addresses []net.Addr) error { @@ -376,6 +389,10 @@ func WriteNetAddrs(buf *bytes.Buffer, addresses []net.Addr) error { if err := WriteOnionAddr(addrBuf, a); err != nil { return err } + case *OpaqueAddrs: + if err := WriteOpaqueAddrs(addrBuf, a); err != nil { + return err + } default: return ErrNilNetAddress }