channeldb: implement serialization of onion addresses

Co-Authored-By: Eugene <crypt-iq@users.noreply.github.com>
This commit is contained in:
Wilmer Paulino 2018-04-25 14:00:26 -04:00
parent e5987a1ef1
commit 3738e68ae2
No known key found for this signature in database
GPG Key ID: 6DF57B9F9514972F

@ -1,10 +1,12 @@
package channeldb package channeldb
import ( import (
"encoding/binary"
"errors"
"io" "io"
"net" "net"
"github.com/btcsuite/go-socks/socks" "github.com/lightningnetwork/lnd/tor"
) )
// addressType specifies the network protocol and version that should be used // 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 denotes a version 2 Tor onion service address.
v2OnionAddr addressType = 2 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 v3OnionAddr addressType = 3
) )
// encodeTCPAddr serializes a TCP address into its compact raw bytes
// representation.
func encodeTCPAddr(w io.Writer, addr *net.TCPAddr) error { func encodeTCPAddr(w io.Writer, addr *net.TCPAddr) error {
var scratch [16]byte var (
addrType byte
ip []byte
)
if addr.IP.To4() != nil { if addr.IP.To4() != nil {
scratch[0] = uint8(tcp4Addr) addrType = byte(tcp4Addr)
if _, err := w.Write(scratch[:1]); err != nil { ip = addr.IP.To4()
return err
}
copy(scratch[:4], addr.IP.To4())
if _, err := w.Write(scratch[:4]); err != nil {
return err
}
} else { } else {
scratch[0] = uint8(tcp6Addr) addrType = byte(tcp6Addr)
if _, err := w.Write(scratch[:1]); err != nil { ip = addr.IP.To16()
return err
}
copy(scratch[:], addr.IP.To16())
if _, err := w.Write(scratch[:]); err != nil {
return err
}
} }
byteOrder.PutUint16(scratch[:2], uint16(addr.Port)) if _, err := w.Write([]byte{addrType}); err != nil {
if _, err := w.Write(scratch[:2]); 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 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 // deserializeAddr reads the serialized raw representation of an address and
// deserializes it into the actual address, to avoid performing address // deserializes it into the actual address. This allows us to avoid address
// resolution in the database module // resolution within the channeldb package.
func deserializeAddr(r io.Reader) (net.Addr, error) { func deserializeAddr(r io.Reader) (net.Addr, error) {
var scratch [8]byte var addrType [1]byte
var address net.Addr if _, err := r.Read(addrType[:]); err != nil {
if _, err := r.Read(scratch[:1]); err != nil {
return nil, err return nil, err
} }
// TODO(roasbeef): also add onion addrs var address net.Addr
switch addressType(scratch[0]) { switch addressType(addrType[0]) {
case tcp4Addr: case tcp4Addr:
addr := &net.TCPAddr{}
var ip [4]byte var ip [4]byte
if _, err := r.Read(ip[:]); err != nil { if _, err := r.Read(ip[:]); err != nil {
return nil, err 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 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: case tcp6Addr:
addr := &net.TCPAddr{}
var ip [16]byte var ip [16]byte
if _, err := r.Read(ip[:]); err != nil { if _, err := r.Read(ip[:]); err != nil {
return nil, err 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 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: default:
return nil, ErrUnknownAddressType return nil, ErrUnknownAddressType
} }
@ -103,34 +184,14 @@ func deserializeAddr(r io.Reader) (net.Addr, error) {
return address, nil return address, nil
} }
// serializeAddr serializes an address into a raw byte representation so it // serializeAddr serializes an address into its raw bytes representation so that
// can be deserialized without requiring address resolution // it can be deserialized without requiring address resolution.
func serializeAddr(w io.Writer, address net.Addr) error { func serializeAddr(w io.Writer, address net.Addr) error {
switch addr := address.(type) { switch addr := address.(type) {
case *net.TCPAddr: case *net.TCPAddr:
return encodeTCPAddr(w, addr) return encodeTCPAddr(w, addr)
case *tor.OnionAddr:
// If this is a proxied address (due to the connection being return encodeOnionAddr(w, addr)
// 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)
} }
return nil return nil