Skip to content

Commit

Permalink
Fixes #3522
Browse files Browse the repository at this point in the history
* OriginalAddr -> SocketAddr

OriginalAddr records the originally dialed address for outbound peers,
rather than the peer's self reported address. For inbound peers, it was
nil. Here, we rename it to SocketAddr and for inbound peers, set it to
the RemoteAddr of the connection.

* use SocketAddr

Numerous places in the code call peer.NodeInfo().NetAddress().
However, this call to NetAddress() may perform a DNS lookup if the
reported NodeInfo.ListenAddr includes a name. Failure of this lookup
returns a nil address, which can lead to panics in the code.

Instead, call peer.SocketAddr() to return the static address of the
connection.

* remove nodeInfo.NetAddress()

Expose `transport.NetAddress()`, a static result determined
when the transport is created. Removing NetAddress() from the nodeInfo
prevents accidental DNS lookups.

* fixes from review

* linter

* fixes from review
  • Loading branch information
ebuchman committed Apr 3, 2019
1 parent 853dd34 commit 79da813
Show file tree
Hide file tree
Showing 14 changed files with 187 additions and 105 deletions.
2 changes: 1 addition & 1 deletion node/node.go
Expand Up @@ -488,7 +488,7 @@ func NewNode(config *cfg.Config,
addrBook := pex.NewAddrBook(config.P2P.AddrBookFile(), config.P2P.AddrBookStrict)

// Add ourselves to addrbook to prevent dialing ourselves
addrBook.AddOurAddress(nodeInfo.NetAddress())
addrBook.AddOurAddress(sw.NetAddress())

addrBook.SetLogger(p2pLogger.With("book", config.P2P.AddrBookFile()))
if config.P2P.PexReactor {
Expand Down
68 changes: 68 additions & 0 deletions p2p/mock/peer.go
@@ -0,0 +1,68 @@
package mock

import (
"net"

"github.com/tendermint/tendermint/crypto/ed25519"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/p2p/conn"
)

type Peer struct {
*cmn.BaseService
ip net.IP
id p2p.ID
addr *p2p.NetAddress
kv map[string]interface{}
Outbound, Persistent bool
}

// NewPeer creates and starts a new mock peer. If the ip
// is nil, random routable address is used.
func NewPeer(ip net.IP) *Peer {
var netAddr *p2p.NetAddress
if ip == nil {
_, netAddr = p2p.CreateRoutableAddr()
} else {
netAddr = p2p.NewNetAddressIPPort(ip, 26656)
}
nodeKey := p2p.NodeKey{PrivKey: ed25519.GenPrivKey()}
netAddr.ID = nodeKey.ID()
mp := &Peer{
ip: ip,
id: nodeKey.ID(),
addr: netAddr,
kv: make(map[string]interface{}),
}
mp.BaseService = cmn.NewBaseService(nil, "MockPeer", mp)
mp.Start()
return mp
}

func (mp *Peer) FlushStop() { mp.Stop() }
func (mp *Peer) TrySend(chID byte, msgBytes []byte) bool { return true }
func (mp *Peer) Send(chID byte, msgBytes []byte) bool { return true }
func (mp *Peer) NodeInfo() p2p.NodeInfo {
return p2p.DefaultNodeInfo{
ID_: mp.addr.ID,
ListenAddr: mp.addr.DialString(),
}
}
func (mp *Peer) Status() conn.ConnectionStatus { return conn.ConnectionStatus{} }
func (mp *Peer) ID() p2p.ID { return mp.id }
func (mp *Peer) IsOutbound() bool { return mp.Outbound }
func (mp *Peer) IsPersistent() bool { return mp.Persistent }
func (mp *Peer) Get(key string) interface{} {
if value, ok := mp.kv[key]; ok {
return value
}
return nil
}
func (mp *Peer) Set(key string, value interface{}) {
mp.kv[key] = value
}
func (mp *Peer) RemoteIP() net.IP { return mp.ip }
func (mp *Peer) SocketAddr() *p2p.NetAddress { return mp.addr }
func (mp *Peer) RemoteAddr() net.Addr { return &net.TCPAddr{IP: mp.ip, Port: 8800} }
func (mp *Peer) CloseConn() error { return nil }
10 changes: 2 additions & 8 deletions p2p/node_info.go
Expand Up @@ -23,14 +23,8 @@ func MaxNodeInfoSize() int {
// NodeInfo exposes basic info of a node
// and determines if we're compatible.
type NodeInfo interface {
nodeInfoAddress
nodeInfoTransport
}

// nodeInfoAddress exposes just the core info of a node.
type nodeInfoAddress interface {
ID() ID
NetAddress() *NetAddress
nodeInfoTransport
}

// nodeInfoTransport validates a nodeInfo and checks
Expand Down Expand Up @@ -221,7 +215,7 @@ func (info DefaultNodeInfo) NetAddress() *NetAddress {
if err != nil {
switch err.(type) {
case ErrNetAddressLookup:
// XXX If the peer provided a host name and the lookup fails here
// XXX If the peer provided a host name and the lookup fails here
// we're out of luck.
// TODO: use a NetAddress in DefaultNodeInfo
default:
Expand Down
27 changes: 13 additions & 14 deletions p2p/peer.go
Expand Up @@ -29,7 +29,7 @@ type Peer interface {

NodeInfo() NodeInfo // peer's info
Status() tmconn.ConnectionStatus
OriginalAddr() *NetAddress // original address for outbound peers
SocketAddr() *NetAddress // actual address of the socket

Send(byte, []byte) bool
TrySend(byte, []byte) bool
Expand All @@ -46,7 +46,7 @@ type peerConn struct {
persistent bool
conn net.Conn // source connection

originalAddr *NetAddress // nil for inbound connections
socketAddr *NetAddress

// cached RemoteIP()
ip net.IP
Expand All @@ -55,14 +55,14 @@ type peerConn struct {
func newPeerConn(
outbound, persistent bool,
conn net.Conn,
originalAddr *NetAddress,
socketAddr *NetAddress,
) peerConn {

return peerConn{
outbound: outbound,
persistent: persistent,
conn: conn,
originalAddr: originalAddr,
outbound: outbound,
persistent: persistent,
conn: conn,
socketAddr: socketAddr,
}
}

Expand Down Expand Up @@ -223,13 +223,12 @@ func (p *peer) NodeInfo() NodeInfo {
return p.nodeInfo
}

// OriginalAddr returns the original address, which was used to connect with
// the peer. Returns nil for inbound peers.
func (p *peer) OriginalAddr() *NetAddress {
if p.peerConn.outbound {
return p.peerConn.originalAddr
}
return nil
// SocketAddr returns the address of the socket.
// For outbound peers, it's the address dialed (after DNS resolution).
// For inbound peers, it's the address returned by the underlying connection
// (not what's reported in the peer's NodeInfo).
func (p *peer) SocketAddr() *NetAddress {
return p.peerConn.socketAddr
}

// Status returns the peer's ConnectionStatus.
Expand Down
2 changes: 1 addition & 1 deletion p2p/peer_set_test.go
Expand Up @@ -29,7 +29,7 @@ func (mp *mockPeer) IsPersistent() bool { return true }
func (mp *mockPeer) Get(s string) interface{} { return s }
func (mp *mockPeer) Set(string, interface{}) {}
func (mp *mockPeer) RemoteIP() net.IP { return mp.ip }
func (mp *mockPeer) OriginalAddr() *NetAddress { return nil }
func (mp *mockPeer) SocketAddr() *NetAddress { return nil }
func (mp *mockPeer) RemoteAddr() net.Addr { return &net.TCPAddr{IP: mp.ip, Port: 8800} }
func (mp *mockPeer) CloseConn() error { return nil }

Expand Down
14 changes: 8 additions & 6 deletions p2p/peer_test.go
Expand Up @@ -109,25 +109,27 @@ func testOutboundPeerConn(
persistent bool,
ourNodePrivKey crypto.PrivKey,
) (peerConn, error) {

var pc peerConn
conn, err := testDial(addr, config)
if err != nil {
return peerConn{}, cmn.ErrorWrap(err, "Error creating peer")
return pc, cmn.ErrorWrap(err, "Error creating peer")
}

pc, err := testPeerConn(conn, config, true, persistent, ourNodePrivKey, addr)
pc, err = testPeerConn(conn, config, true, persistent, ourNodePrivKey, addr)
if err != nil {
if cerr := conn.Close(); cerr != nil {
return peerConn{}, cmn.ErrorWrap(err, cerr.Error())
return pc, cmn.ErrorWrap(err, cerr.Error())
}
return peerConn{}, err
return pc, err
}

// ensure dialed ID matches connection ID
if addr.ID != pc.ID() {
if cerr := conn.Close(); cerr != nil {
return peerConn{}, cmn.ErrorWrap(err, cerr.Error())
return pc, cmn.ErrorWrap(err, cerr.Error())
}
return peerConn{}, ErrSwitchAuthenticationFailure{addr, pc.ID()}
return pc, ErrSwitchAuthenticationFailure{addr, pc.ID()}
}

return pc, nil
Expand Down
4 changes: 2 additions & 2 deletions p2p/pex/pex_reactor.go
Expand Up @@ -167,7 +167,7 @@ func (r *PEXReactor) AddPeer(p Peer) {
}
} else {
// inbound peer is its own source
addr := p.NodeInfo().NetAddress()
addr := p.SocketAddr()
src := addr

// add to book. dont RequestAddrs right away because
Expand Down Expand Up @@ -309,7 +309,7 @@ func (r *PEXReactor) ReceiveAddrs(addrs []*p2p.NetAddress, src Peer) error {
}
r.requestsSent.Delete(id)

srcAddr := src.NodeInfo().NetAddress()
srcAddr := src.SocketAddr()
for _, netAddr := range addrs {
// Validate netAddr. Disconnect from a peer if it sends us invalid data.
if netAddr == nil {
Expand Down
27 changes: 14 additions & 13 deletions p2p/pex/pex_reactor_test.go
Expand Up @@ -101,7 +101,7 @@ func TestPEXReactorRunning(t *testing.T) {
}

addOtherNodeAddrToAddrBook := func(switchIndex, otherSwitchIndex int) {
addr := switches[otherSwitchIndex].NodeInfo().NetAddress()
addr := switches[otherSwitchIndex].NetAddress()
books[switchIndex].AddAddress(addr, addr)
}

Expand Down Expand Up @@ -132,7 +132,7 @@ func TestPEXReactorReceive(t *testing.T) {
r.RequestAddrs(peer)

size := book.Size()
addrs := []*p2p.NetAddress{peer.NodeInfo().NetAddress()}
addrs := []*p2p.NetAddress{peer.SocketAddr()}
msg := cdc.MustMarshalBinaryBare(&pexAddrsMessage{Addrs: addrs})
r.Receive(PexChannel, peer, msg)
assert.Equal(t, size+1, book.Size())
Expand Down Expand Up @@ -189,7 +189,7 @@ func TestPEXReactorAddrsMessageAbuse(t *testing.T) {
assert.True(t, r.requestsSent.Has(id))
assert.True(t, sw.Peers().Has(peer.ID()))

addrs := []*p2p.NetAddress{peer.NodeInfo().NetAddress()}
addrs := []*p2p.NetAddress{peer.SocketAddr()}
msg := cdc.MustMarshalBinaryBare(&pexAddrsMessage{Addrs: addrs})

// receive some addrs. should clear the request
Expand Down Expand Up @@ -234,7 +234,7 @@ func TestCheckSeeds(t *testing.T) {
badPeerConfig = &PEXReactorConfig{
Seeds: []string{"ed3dfd27bfc4af18f67a49862f04cc100696e84d@bad.network.addr:26657",
"d824b13cb5d40fa1d8a614e089357c7eff31b670@anotherbad.network.addr:26657",
seed.NodeInfo().NetAddress().String()},
seed.NetAddress().String()},
}
peer = testCreatePeerWithConfig(dir, 2, badPeerConfig)
require.Nil(t, peer.Start())
Expand Down Expand Up @@ -268,12 +268,13 @@ func TestConnectionSpeedForPeerReceivedFromSeed(t *testing.T) {
defer os.RemoveAll(dir) // nolint: errcheck

// 1. create peer
peer := testCreateDefaultPeer(dir, 1)
require.Nil(t, peer.Start())
defer peer.Stop()
peerSwitch := testCreateDefaultPeer(dir, 1)
require.Nil(t, peerSwitch.Start())
defer peerSwitch.Stop()

// 2. Create seed which knows about the peer
seed := testCreateSeed(dir, 2, []*p2p.NetAddress{peer.NodeInfo().NetAddress()}, []*p2p.NetAddress{peer.NodeInfo().NetAddress()})
peerAddr := peerSwitch.NetAddress()
seed := testCreateSeed(dir, 2, []*p2p.NetAddress{peerAddr}, []*p2p.NetAddress{peerAddr})
require.Nil(t, seed.Start())
defer seed.Stop()

Expand All @@ -300,7 +301,7 @@ func TestPEXReactorCrawlStatus(t *testing.T) {
// Create a peer, add it to the peer set and the addrbook.
peer := p2p.CreateRandomPeer(false)
p2p.AddPeerToSwitch(pexR.Switch, peer)
addr1 := peer.NodeInfo().NetAddress()
addr1 := peer.SocketAddr()
pexR.book.AddAddress(addr1, addr1)

// Add a non-connected address to the book.
Expand Down Expand Up @@ -364,7 +365,7 @@ func TestPEXReactorSeedModeFlushStop(t *testing.T) {
reactor := switches[0].Reactors()["pex"].(*PEXReactor)
peerID := switches[1].NodeInfo().ID()

err = switches[1].DialPeerWithAddress(switches[0].NodeInfo().NetAddress(), false)
err = switches[1].DialPeerWithAddress(switches[0].NetAddress(), false)
assert.NoError(t, err)

// sleep up to a second while waiting for the peer to send us a message.
Expand Down Expand Up @@ -402,7 +403,7 @@ func TestPEXReactorDoesNotAddPrivatePeersToAddrBook(t *testing.T) {
pexR.RequestAddrs(peer)

size := book.Size()
addrs := []*p2p.NetAddress{peer.NodeInfo().NetAddress()}
addrs := []*p2p.NetAddress{peer.SocketAddr()}
msg := cdc.MustMarshalBinaryBare(&pexAddrsMessage{Addrs: addrs})
pexR.Receive(PexChannel, peer, msg)
assert.Equal(t, size, book.Size())
Expand All @@ -419,7 +420,7 @@ func TestPEXReactorDialPeer(t *testing.T) {
sw.SetAddrBook(book)

peer := newMockPeer()
addr := peer.NodeInfo().NetAddress()
addr := peer.SocketAddr()

assert.Equal(t, 0, pexR.AttemptsToDial(addr))

Expand Down Expand Up @@ -590,7 +591,7 @@ func testCreateSeed(dir string, id int, knownAddrs, srcAddrs []*p2p.NetAddress)
// Starting and stopping the peer is left to the caller
func testCreatePeerWithSeed(dir string, id int, seed *p2p.Switch) *p2p.Switch {
conf := &PEXReactorConfig{
Seeds: []string{seed.NodeInfo().NetAddress().String()},
Seeds: []string{seed.NetAddress().String()},
}
return testCreatePeerWithConfig(dir, id, conf)
}
Expand Down
22 changes: 11 additions & 11 deletions p2p/switch.go
Expand Up @@ -86,6 +86,12 @@ type Switch struct {
metrics *Metrics
}

// NetAddress returns the address the switch is listening on.
func (sw *Switch) NetAddress() *NetAddress {
addr := sw.transport.NetAddress()
return &addr
}

// SwitchOption sets an optional parameter on the Switch.
type SwitchOption func(*Switch)

Expand Down Expand Up @@ -284,13 +290,7 @@ func (sw *Switch) StopPeerForError(peer Peer, reason interface{}) {
sw.stopAndRemovePeer(peer, reason)

if peer.IsPersistent() {
addr := peer.OriginalAddr()
if addr == nil {
// FIXME: persistent peers can't be inbound right now.
// self-reported address for inbound persistent peers
addr = peer.NodeInfo().NetAddress()
}
go sw.reconnectToPeer(addr)
go sw.reconnectToPeer(peer.SocketAddr())
}
}

Expand Down Expand Up @@ -378,7 +378,7 @@ func (sw *Switch) SetAddrBook(addrBook AddrBook) {
// like contributed to consensus.
func (sw *Switch) MarkPeerAsGood(peer Peer) {
if sw.addrBook != nil {
sw.addrBook.MarkGood(peer.NodeInfo().NetAddress())
sw.addrBook.MarkGood(peer.SocketAddr())
}
}

Expand All @@ -395,7 +395,7 @@ func (sw *Switch) DialPeersAsync(addrBook AddrBook, peers []string, persistent b
sw.Logger.Error("Error in peer's address", "err", err)
}

ourAddr := sw.nodeInfo.NetAddress()
ourAddr := sw.NetAddress()

// TODO: this code feels like it's in the wrong place.
// The integration tests depend on the addrBook being saved
Expand Down Expand Up @@ -524,7 +524,7 @@ func (sw *Switch) acceptRoutine() {
if in >= sw.config.MaxNumInboundPeers {
sw.Logger.Info(
"Ignoring inbound connection: already have enough inbound peers",
"address", p.NodeInfo().NetAddress().String(),
"address", p.SocketAddr(),
"have", in,
"max", sw.config.MaxNumInboundPeers,
)
Expand Down Expand Up @@ -641,7 +641,7 @@ func (sw *Switch) addPeer(p Peer) error {
return err
}

p.SetLogger(sw.Logger.With("peer", p.NodeInfo().NetAddress()))
p.SetLogger(sw.Logger.With("peer", p.SocketAddr()))

// Handle the shut down case where the switch has stopped but we're
// concurrently trying to add a peer.
Expand Down

0 comments on commit 79da813

Please sign in to comment.