From 5435967d09800c92d185f28c0bcdec73ba04fc96 Mon Sep 17 00:00:00 2001 From: delbonis Date: Thu, 1 Nov 2018 14:51:34 -0400 Subject: [PATCH 1/4] Merged some of the new connection handling code (to create Peer object) into new shared function. --- lnp2p/listen.go | 56 +++++++++--------------------------------------- lnp2p/peermgr.go | 46 ++++++++++++++++++++++++++++----------- 2 files changed, 44 insertions(+), 58 deletions(-) diff --git a/lnp2p/listen.go b/lnp2p/listen.go index 6a974c88b..954462dca 100644 --- a/lnp2p/listen.go +++ b/lnp2p/listen.go @@ -2,7 +2,6 @@ package lnp2p import ( "github.com/mit-dci/lit/eventbus" - "github.com/mit-dci/lit/lncore" "github.com/mit-dci/lit/lndc" "github.com/mit-dci/lit/logging" ) @@ -48,58 +47,23 @@ func acceptConnections(listener *lndc.Listener, port int, pm *PeerManager) { rlitaddr := convertPubkeyToLitAddr(rpk) rnetaddr := lndcConn.RemoteAddr() - logging.Infof("New connection from %s at %s\n", rlitaddr, rnetaddr.String()) - - // Read the peer info from the DB. - pi, err := pm.peerdb.GetPeerInfo(rlitaddr) - if err != nil { - logging.Warnf("problem loading peer info in DB (maybe this is ok?): %s\n", err.Error()) - netConn.Close() + // Make sure we can't let ourself connect to ourself. + if string(rlitaddr) == pm.GetExternalAddress() { + logging.Infof("peermgr: Got a connection from ourselves? Dropping.") + lndcConn.Close() continue } - // Create the actual peer object. - npeer := &Peer{ - lnaddr: rlitaddr, - nickname: nil, - conn: lndcConn, - idpubkey: rpk, + logging.Infof("peermgr: New connection from %s at %s\n", rlitaddr, rnetaddr.String()) - // TEMP - idx: nil, - } - - // Add the peer data to the DB if we don't have it. - if pi == nil { - raddr := rnetaddr.String() - pidx, err := pm.peerdb.GetUniquePeerIdx() - if err != nil { - logging.Errorf("problem getting unique peeridx: %s\n", err.Error()) - } - pi = &lncore.PeerInfo{ - LnAddr: &rlitaddr, - Nickname: nil, - NetAddr: &raddr, - PeerIdx: pidx, - } - err = pm.peerdb.AddPeer(rlitaddr, *pi) - npeer.idx = &pidx - if err != nil { - // don't close it, I guess - logging.Errorf("problem saving peer info to DB: %s\n", err.Error()) - } - } else { - npeer.nickname = pi.Nickname - // TEMP - npeer.idx = &pi.PeerIdx + p, err := pm.handleNewConnection(lndcConn, rlitaddr) + if err != nil { + logging.Warnf("%s\n", err.Error()) + continue } - // Don't do any locking here since registerPeer takes a lock and Go's - // mutex isn't reentrant. - pm.registerPeer(npeer) - // Start a goroutine to process inbound traffic for this peer. - go processConnectionInboundTraffic(npeer, pm) + go processConnectionInboundTraffic(p, pm) } diff --git a/lnp2p/peermgr.go b/lnp2p/peermgr.go index b9c54774f..e2cdd1cc1 100644 --- a/lnp2p/peermgr.go +++ b/lnp2p/peermgr.go @@ -211,26 +211,52 @@ func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr, set dialer = d } - // Set up the connection. + // Create the connection. lndcconn, err := lndc.Dial(pm.idkey, netaddr, string(*lnaddr), dialer) if err != nil { return nil, err } - pi, err := pm.peerdb.GetPeerInfo(*lnaddr) + // Try to set up the new connection. + p, err := pm.handleNewConnection(lndcconn, *lnaddr) if err != nil { - logging.Errorf("Problem loading peer info from DB: %s\n", err.Error()) - // don't kill the connection? + return nil, err } + // Now start listening for inbound traffic. + // (it *also* took me a while to realize I forgot *this*) + go processConnectionInboundTraffic(p, pm) + + // Return + return p, nil + +} + +func (pm *PeerManager) handleNewConnection(conn *lndc.Conn, expectedAddr lncore.LnAddr) (*Peer, error) { + // Now that we've got the connection, actually create the peer object. - pk := pubkey(lndcconn.RemotePub()) + pk := pubkey(conn.RemotePub()) rlitaddr := convertPubkeyToLitAddr(pk) + + if rlitaddr != expectedAddr { + conn.Close() + return nil, fmt.Errorf("peermgr: Connection init error, expected addr %s got addr %s", expectedAddr, rlitaddr) + } + p := &Peer{ lnaddr: rlitaddr, nickname: nil, - conn: lndcconn, + conn: conn, idpubkey: pk, + + // TEMP + idx: nil, + } + + pi, err := pm.peerdb.GetPeerInfo(expectedAddr) + if err != nil { + logging.Errorf("peermgr: Problem loading peer info from DB: %s\n", err.Error()) + // don't kill the connection? } if pi == nil { @@ -240,7 +266,7 @@ func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr, set } else { p.idx = &pidx } - raddr := lndcconn.RemoteAddr().String() + raddr := conn.RemoteAddr().String() pi = &lncore.PeerInfo{ LnAddr: &rlitaddr, Nickname: nil, @@ -261,11 +287,7 @@ func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr, set // (it took me a while to realize I forgot this) pm.registerPeer(p) - // Now start listening for inbound traffic. - // (it *also* took me a while to realize I forgot *this*) - go processConnectionInboundTraffic(p, pm) - - // Return + // Now actually return the peer. return p, nil } From 51e44944572284da15d52a31a2a8d69a7f46f561 Mon Sep 17 00:00:00 2001 From: delbonis Date: Thu, 1 Nov 2018 15:23:35 -0400 Subject: [PATCH 2/4] Refactored peermgr so that you only have to provide the NetSettings when creating it. --- lnp2p/peermgr.go | 87 +++++++++++++++++++++++------------------------- qln/init.go | 2 +- qln/netio.go | 2 +- 3 files changed, 44 insertions(+), 47 deletions(-) diff --git a/lnp2p/peermgr.go b/lnp2p/peermgr.go index e2cdd1cc1..912e97943 100644 --- a/lnp2p/peermgr.go +++ b/lnp2p/peermgr.go @@ -5,7 +5,6 @@ import ( "crypto/ecdsa" "fmt" "net" - "strconv" "sync" "time" @@ -31,10 +30,11 @@ const MaxNodeCount = 1024 type PeerManager struct { // Biographical. - idkey privkey - peerdb lncore.LitPeerStorage - ebus *eventbus.EventBus - mproc MessageProcessor + idkey privkey + peerdb lncore.LitPeerStorage + ebus *eventbus.EventBus + mproc MessageProcessor + netsettings *NetSettings // Peer tracking. peers []lncore.LnAddr // compatibility @@ -66,7 +66,7 @@ type NetSettings struct { } // NewPeerManager creates a peer manager from a root key -func NewPeerManager(rootkey *hdkeychain.ExtendedKey, pdb lncore.LitPeerStorage, trackerURL string, bus *eventbus.EventBus) (*PeerManager, error) { +func NewPeerManager(rootkey *hdkeychain.ExtendedKey, pdb lncore.LitPeerStorage, trackerURL string, bus *eventbus.EventBus, ns *NetSettings) (*PeerManager, error) { k, err := computeIdentKeyFromRoot(rootkey) if err != nil { return nil, err @@ -77,6 +77,7 @@ func NewPeerManager(rootkey *hdkeychain.ExtendedKey, pdb lncore.LitPeerStorage, peerdb: pdb, ebus: bus, mproc: NewMessageProcessor(), + netsettings: ns, peers: make([]lncore.LnAddr, MaxNodeCount), peerMap: map[lncore.LnAddr]*Peer{}, listeningPorts: map[int]*listeningthread{}, @@ -128,7 +129,6 @@ func (pm *PeerManager) GetPeerIdx(peer *Peer) uint32 { // GetPeer returns the peer with the given lnaddr. func (pm *PeerManager) GetPeer(lnaddr lncore.LnAddr) *Peer { p, ok := pm.peerMap[lnaddr] - logging.Errorf("%v -> %v (%t)\n", lnaddr, p, ok) if !ok { return nil } @@ -144,7 +144,7 @@ func (pm *PeerManager) GetPeerByIdx(id int32) *Peer { } // TryConnectAddress attempts to connect to the specified LN address. -func (pm *PeerManager) TryConnectAddress(addr string, settings *NetSettings) (*Peer, error) { +func (pm *PeerManager) TryConnectAddress(addr string) (*Peer, error) { // Figure out who we're trying to connect to. who, where := splitAdrString(addr) @@ -157,54 +157,24 @@ func (pm *PeerManager) TryConnectAddress(addr string, settings *NetSettings) (*P } lnwho := lncore.LnAddr(who) - x, y := pm.tryConnectPeer(where, &lnwho, settings) + x, y := pm.tryConnectPeer(where, &lnwho) return x, y } -func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr, settings *NetSettings) (*Peer, error) { +func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr) (*Peer, error) { // lnaddr check, to make sure that we do the right thing. if lnaddr == nil { return nil, fmt.Errorf("connection to a peer with unknown lnaddr not supported yet") } - // Do NAT setup stuff. - if settings != nil && settings.NatMode != nil { - - // Do some type juggling. - x, err := strconv.Atoi(netaddr[1:]) - if err != nil { - return nil, err - } - lisPort := uint16(x) // if only Atoi could infer which type we wanted to parse as! - - // Actually figure out what we're going to do. - if *settings.NatMode == "upnp" { - // Universal Plug-n-Play - logging.Infof("Attempting port forwarding via UPnP...") - err = nat.SetupUpnp(lisPort) - if err != nil { - return nil, err - } - } else if *settings.NatMode == "pmp" { - // NAT Port Mapping Protocol - timeout := time.Duration(10 * time.Second) - logging.Infof("Attempting port forwarding via PMP...") - _, err = nat.SetupPmp(timeout, lisPort) - if err != nil { - return nil, err - } - } else { - return nil, fmt.Errorf("invalid NAT type: %s", *settings.NatMode) - } - } - dialer := net.Dial // Use a proxy server if applicable. - if settings != nil && settings.ProxyAddr != nil { - d, err := connectToProxyTCP(*settings.ProxyAddr, settings.ProxyAuth) + ns := pm.netsettings + if ns != nil && ns.ProxyAddr != nil { + d, err := connectToProxyTCP(*ns.ProxyAddr, ns.ProxyAuth) if err != nil { return nil, err } @@ -361,6 +331,34 @@ func (pm *PeerManager) DisconnectPeer(peer *Peer) error { // ListenOnPort attempts to start a goroutine lisening on the port. func (pm *PeerManager) ListenOnPort(port int) error { + // Do NAT setup stuff. + ns := pm.netsettings + if ns != nil && ns.NatMode != nil { + + // Do some type juggling. + lisPort := uint16(port) + + // Actually figure out what we're going to do. + if *ns.NatMode == "upnp" { + // Universal Plug-n-Play + logging.Infof("Attempting port forwarding via UPnP...") + err := nat.SetupUpnp(lisPort) + if err != nil { + return err + } + } else if *ns.NatMode == "pmp" { + // NAT Port Mapping Protocol + timeout := time.Duration(10 * time.Second) + logging.Infof("Attempting port forwarding via PMP...") + _, err := nat.SetupPmp(timeout, lisPort) + if err != nil { + return err + } + } else { + return fmt.Errorf("invalid NAT type: %s", *ns.NatMode) + } + } + threadobj := &listeningthread{ listener: nil, } @@ -375,9 +373,8 @@ func (pm *PeerManager) ListenOnPort(port int) error { return fmt.Errorf("listen cancelled by event handler") } - // TODO UPnP and PMP NAT traversal. - // Try to start listening. + // TODO Listen on proxy if possible? logging.Info("PORT: ", port) listener, err := lndc.NewListener(pm.idkey, port) if err != nil { diff --git a/qln/init.go b/qln/init.go index a730dc78d..fe9cee9f0 100644 --- a/qln/init.go +++ b/qln/init.go @@ -60,7 +60,7 @@ func NewLitNode(privKey *[32]byte, path string, trackerURL string, proxyURL stri nd.Events = &ebus // Peer manager - nd.PeerMan, err = lnp2p.NewPeerManager(rootPrivKey, nd.NewLitDB.GetPeerDB(), trackerURL, &ebus) + nd.PeerMan, err = lnp2p.NewPeerManager(rootPrivKey, nd.NewLitDB.GetPeerDB(), trackerURL, &ebus, nil) // TODO proxy/nat stuff if err != nil { return nil, err } diff --git a/qln/netio.go b/qln/netio.go index 36b89c662..f375f1bfa 100644 --- a/qln/netio.go +++ b/qln/netio.go @@ -80,7 +80,7 @@ func splitAdrString(adr string) (string, string) { // TODO Remove this. func (nd *LitNode) DialPeer(connectAdr string) error { - _, err := nd.PeerMan.TryConnectAddress(connectAdr, nil) + _, err := nd.PeerMan.TryConnectAddress(connectAdr) if err != nil { return err } From 9e699d32d93485549e35a77a0a6756fcd6db4024 Mon Sep 17 00:00:00 2001 From: delbonis Date: Thu, 1 Nov 2018 15:40:31 -0400 Subject: [PATCH 3/4] Changed force-casts into LnAddr type into calls to a parser function that ensures that it's well-formed. I also added a .ToString() function on the LnAddr type but that's less important so I didn't seek out all the manual conversions there, espcially since it can't fail. --- db/lnbolt/peerdb.go | 14 ++++++++++++-- litrpc/netcmds.go | 9 +++++++-- lncore/peers.go | 37 +++++++++++++++++++++++++++++++++++++ lnp2p/peermgr.go | 6 +++++- lnp2p/util.go | 10 +++++++++- qln/netio.go | 7 ++++++- 6 files changed, 76 insertions(+), 7 deletions(-) diff --git a/db/lnbolt/peerdb.go b/db/lnbolt/peerdb.go index c1ef6e35f..2ebb81501 100644 --- a/db/lnbolt/peerdb.go +++ b/db/lnbolt/peerdb.go @@ -55,7 +55,13 @@ func (pdb *peerboltdb) GetPeerAddrs() ([]lncore.LnAddr, error) { if k == nil { break } - atmp = append(atmp, lncore.LnAddr(string(k))) + lnaddr, err := lncore.ParseLnAddr(string(k)) + if err != nil { + logging.Warnf("lnbolt/peerdb: found invalid key in DB as lnaddr: %s (error: %s)", string(k), err.Error()) + continue + } + + atmp = append(atmp, lnaddr) } // Now that we have the final array return it. @@ -126,7 +132,11 @@ func (pdb *peerboltdb) GetPeerInfos() (map[lncore.LnAddr]lncore.PeerInfo, error) return err2 } - ka := lncore.LnAddr(string(k)) + ka, err2 := lncore.ParseLnAddr(string(k)) + if err2 != nil { + logging.Warnf("lnbolt/peerdb: found invalid key in DB as lnaddr: %s (error: %s)", string(k), err.Error()) + continue + } mtmp[ka] = pi } diff --git a/litrpc/netcmds.go b/litrpc/netcmds.go index 6e760dba4..c759a5d5e 100644 --- a/litrpc/netcmds.go +++ b/litrpc/netcmds.go @@ -95,8 +95,13 @@ func (r *LitRPC) Connect(args ConnectArgs, reply *ConnectReply) error { paddr = strings.SplitN(paddr, "@", 2)[0] } - var pm *lnp2p.PeerManager = r.Node.PeerMan - p := pm.GetPeer(lncore.LnAddr(paddr)) + pm := r.Node.PeerMan + lnaddr, err := lncore.ParseLnAddr(paddr) + if err != nil { + return err + } + + p := pm.GetPeer(lnaddr) if p == nil { return fmt.Errorf("couldn't find peer in manager after connecting") } diff --git a/lncore/peers.go b/lncore/peers.go index e4930d662..567c84f45 100644 --- a/lncore/peers.go +++ b/lncore/peers.go @@ -1,9 +1,46 @@ package lncore +import ( + "fmt" + + "github.com/mit-dci/lit/bech32" +) + // LnAddr is just a bech32-encoded pubkey. // TODO Move this to another package so it's more obviously not *just* IO-related. type LnAddr string +// ParseLnAddr will verify that the string passed is a valid LN address, as in +// ln1pmclh89haeswrw0unf8awuyqeu4t2uell58nea. +func ParseLnAddr(m string) (LnAddr, error) { + + prefix, raw, err := bech32.Decode(m) + + // Check it's valid bech32. + if err != nil { + return "", err + } + + // Check it has the right prefix. + if prefix != "ln" { + return "", fmt.Errorf("prefix is not 'ln'") + } + + // Check the length of the content bytes is right. + if len(raw) > 20 { + return "", fmt.Errorf("address too long to be pubkey") + } + + return LnAddr(m), nil // should be the only place we cast to this type + +} + +// ToString returns the LnAddr as a string. Right now it just unwraps it but it +// might do something more eventually. +func (lnaddr LnAddr) ToString() string { + return string(lnaddr) +} + // LitPeerStorage is storage for peer data. type LitPeerStorage interface { GetPeerAddrs() ([]LnAddr, error) diff --git a/lnp2p/peermgr.go b/lnp2p/peermgr.go index 912e97943..f367ddd83 100644 --- a/lnp2p/peermgr.go +++ b/lnp2p/peermgr.go @@ -156,7 +156,11 @@ func (pm *PeerManager) TryConnectAddress(addr string) (*Peer, error) { where = fmt.Sprintf("%s:2448", ipv4) } - lnwho := lncore.LnAddr(who) + lnwho, err := lncore.ParseLnAddr(who) + if err != nil { + return nil, err + } + x, y := pm.tryConnectPeer(where, &lnwho) return x, y diff --git a/lnp2p/util.go b/lnp2p/util.go index 49e09f30c..0af8df4df 100644 --- a/lnp2p/util.go +++ b/lnp2p/util.go @@ -32,7 +32,15 @@ func splitAdrString(adr string) (string, string) { func convertPubkeyToLitAddr(pk pubkey) lncore.LnAddr { b := (*koblitz.PublicKey)(pk).SerializeCompressed() doubleSha := fastsha256.Sum256(b[:]) - return lncore.LnAddr(bech32.Encode("ln", doubleSha[:20])) + + lnaddr, e := lncore.ParseLnAddr(bech32.Encode("ln", doubleSha[:20])) + + // Should never happen. + if e != nil { + panic("this should never happen, lit addr gen code has a bug: " + e.Error()) + } + + return lnaddr } func connectToProxyTCP(addr string, auth *string) (func(string, string) (net.Conn, error), error) { diff --git a/qln/netio.go b/qln/netio.go index f375f1bfa..39a021ae4 100644 --- a/qln/netio.go +++ b/qln/netio.go @@ -20,7 +20,12 @@ func (nd *LitNode) GetLisAddressAndPorts() (string, []string) { // TODO Remove this function. func (nd *LitNode) FindPeerIndexByAddress(lnAdr string) (uint32, error) { pm := nd.PeerMan - p := pm.GetPeer(lncore.LnAddr(lnAdr)) + lnaddr, err := lncore.ParseLnAddr(lnAdr) + if err != nil { + return 0, err + } + + p := pm.GetPeer(lnaddr) if p != nil { return p.GetIdx(), nil } From ea17df4053b7ef4fbeec0596a338236e281eac72 Mon Sep 17 00:00:00 2001 From: delbonis Date: Thu, 1 Nov 2018 15:44:10 -0400 Subject: [PATCH 4/4] Removed unused import. --- litrpc/netcmds.go | 1 - 1 file changed, 1 deletion(-) diff --git a/litrpc/netcmds.go b/litrpc/netcmds.go index c759a5d5e..c82a7c0a3 100644 --- a/litrpc/netcmds.go +++ b/litrpc/netcmds.go @@ -8,7 +8,6 @@ import ( "github.com/mit-dci/lit/bech32" "github.com/mit-dci/lit/crypto/koblitz" "github.com/mit-dci/lit/lncore" - "github.com/mit-dci/lit/lnp2p" "github.com/mit-dci/lit/lnutil" "github.com/mit-dci/lit/logging" "github.com/mit-dci/lit/qln"