From 3738e68ae218428bcf95314d3c67af3c342ca791 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Wed, 25 Apr 2018 14:00:26 -0400 Subject: [PATCH] channeldb: implement serialization of onion addresses Co-Authored-By: Eugene --- channeldb/addr.go | 193 ++++++++++++++++++++++++++++++---------------- 1 file changed, 127 insertions(+), 66 deletions(-) diff --git a/channeldb/addr.go b/channeldb/addr.go index f97e8a5b..4d2f578f 100644 --- a/channeldb/addr.go +++ b/channeldb/addr.go @@ -1,10 +1,12 @@ package channeldb import ( + "encoding/binary" + "errors" "io" "net" - "github.com/btcsuite/go-socks/socks" + "github.com/lightningnetwork/lnd/tor" ) // addressType specifies the network protocol and version that should be used @@ -21,38 +23,75 @@ const ( // v2OnionAddr denotes a version 2 Tor onion service address. v2OnionAddr addressType = 2 - // v3OnionAddr denotes a version 3 Tor (prop224) onion service addresses. + // v3OnionAddr denotes a version 3 Tor (prop224) onion service address. v3OnionAddr addressType = 3 ) +// encodeTCPAddr serializes a TCP address into its compact raw bytes +// representation. func encodeTCPAddr(w io.Writer, addr *net.TCPAddr) error { - var scratch [16]byte + var ( + addrType byte + ip []byte + ) if addr.IP.To4() != nil { - scratch[0] = uint8(tcp4Addr) - if _, err := w.Write(scratch[:1]); err != nil { - return err - } - - copy(scratch[:4], addr.IP.To4()) - if _, err := w.Write(scratch[:4]); err != nil { - return err - } - + addrType = byte(tcp4Addr) + ip = addr.IP.To4() } else { - scratch[0] = uint8(tcp6Addr) - if _, err := w.Write(scratch[:1]); err != nil { - return err - } - - copy(scratch[:], addr.IP.To16()) - if _, err := w.Write(scratch[:]); err != nil { - return err - } + addrType = byte(tcp6Addr) + ip = addr.IP.To16() } - byteOrder.PutUint16(scratch[:2], uint16(addr.Port)) - if _, err := w.Write(scratch[:2]); err != nil { + if _, err := w.Write([]byte{addrType}); err != nil { + return err + } + + if _, err := w.Write(ip); err != nil { + return err + } + + var port [2]byte + byteOrder.PutUint16(port[:], uint16(addr.Port)) + if _, err := w.Write(port[:]); err != nil { + return err + } + + return nil +} + +// encodeOnionAddr serializes an onion address into its compact raw bytes +// representation. +func encodeOnionAddr(w io.Writer, addr *tor.OnionAddr) error { + var suffixIndex int + switch len(addr.OnionService) { + case tor.V2Len: + if _, err := w.Write([]byte{byte(v2OnionAddr)}); err != nil { + return err + } + suffixIndex = tor.V2Len - tor.OnionSuffixLen + case tor.V3Len: + if _, err := w.Write([]byte{byte(v3OnionAddr)}); err != nil { + return err + } + suffixIndex = tor.V3Len - tor.OnionSuffixLen + default: + return errors.New("unknown onion service length") + } + + host, err := tor.Base32Encoding.DecodeString( + addr.OnionService[:suffixIndex], + ) + if err != nil { + return err + } + if _, err := w.Write(host); err != nil { + return err + } + + var port [2]byte + byteOrder.PutUint16(port[:], uint16(addr.Port)) + if _, err := w.Write(port[:]); err != nil { return err } @@ -60,42 +99,84 @@ func encodeTCPAddr(w io.Writer, addr *net.TCPAddr) error { } // deserializeAddr reads the serialized raw representation of an address and -// deserializes it into the actual address, to avoid performing address -// resolution in the database module +// deserializes it into the actual address. This allows us to avoid address +// resolution within the channeldb package. func deserializeAddr(r io.Reader) (net.Addr, error) { - var scratch [8]byte - var address net.Addr - - if _, err := r.Read(scratch[:1]); err != nil { + var addrType [1]byte + if _, err := r.Read(addrType[:]); err != nil { return nil, err } - // TODO(roasbeef): also add onion addrs - switch addressType(scratch[0]) { + var address net.Addr + switch addressType(addrType[0]) { case tcp4Addr: - addr := &net.TCPAddr{} var ip [4]byte if _, err := r.Read(ip[:]); err != nil { return nil, err } - addr.IP = (net.IP)(ip[:]) - if _, err := r.Read(scratch[:2]); err != nil { + + var port [2]byte + if _, err := r.Read(port[:]); err != nil { return nil, err } - addr.Port = int(byteOrder.Uint16(scratch[:2])) - address = addr + + address = &net.TCPAddr{ + IP: net.IP(ip[:]), + Port: int(binary.BigEndian.Uint16(port[:])), + } case tcp6Addr: - addr := &net.TCPAddr{} var ip [16]byte if _, err := r.Read(ip[:]); err != nil { return nil, err } - addr.IP = (net.IP)(ip[:]) - if _, err := r.Read(scratch[:2]); err != nil { + + var port [2]byte + if _, err := r.Read(port[:]); err != nil { return nil, err } - addr.Port = int(byteOrder.Uint16(scratch[:2])) - address = addr + + address = &net.TCPAddr{ + IP: net.IP(ip[:]), + Port: int(binary.BigEndian.Uint16(port[:])), + } + case v2OnionAddr: + var h [tor.V2DecodedLen]byte + if _, err := r.Read(h[:]); err != nil { + return nil, err + } + + var p [2]byte + if _, err := r.Read(p[:]); err != nil { + return nil, err + } + + onionService := tor.Base32Encoding.EncodeToString(h[:]) + onionService += tor.OnionSuffix + port := int(binary.BigEndian.Uint16(p[:])) + + address = &tor.OnionAddr{ + OnionService: onionService, + Port: port, + } + case v3OnionAddr: + var h [tor.V3DecodedLen]byte + if _, err := r.Read(h[:]); err != nil { + return nil, err + } + + var p [2]byte + if _, err := r.Read(p[:]); err != nil { + return nil, err + } + + onionService := tor.Base32Encoding.EncodeToString(h[:]) + onionService += tor.OnionSuffix + port := int(binary.BigEndian.Uint16(p[:])) + + address = &tor.OnionAddr{ + OnionService: onionService, + Port: port, + } default: return nil, ErrUnknownAddressType } @@ -103,34 +184,14 @@ func deserializeAddr(r io.Reader) (net.Addr, error) { return address, nil } -// serializeAddr serializes an address into a raw byte representation so it -// can be deserialized without requiring address resolution +// serializeAddr serializes an address into its raw bytes representation so that +// it can be deserialized without requiring address resolution. func serializeAddr(w io.Writer, address net.Addr) error { - switch addr := address.(type) { case *net.TCPAddr: return encodeTCPAddr(w, addr) - - // If this is a proxied address (due to the connection being - // established over a SOCKs proxy, then we'll convert it into its - // corresponding TCP address. - case *socks.ProxiedAddr: - // If we can't parse the host as an IP (though we should be - // able to at this point), then we'll skip this address all - // together. - // - // TODO(roasbeef): would be nice to be able to store hosts - // though... - ip := net.ParseIP(addr.Host) - if ip == nil { - return nil - } - - tcpAddr := &net.TCPAddr{ - IP: ip, - Port: addr.Port, - } - return encodeTCPAddr(w, tcpAddr) + case *tor.OnionAddr: + return encodeOnionAddr(w, addr) } return nil