From 2cc85ef428077085f479c6e05cdf11be29494dd7 Mon Sep 17 00:00:00 2001 From: David Hill Date: Tue, 17 Mar 2015 13:05:11 -0400 Subject: [PATCH] Reduce fingerprinting. This change reduces fingerprinting via timestamps in addr messages. Previously, the last seen time for an address was updated when certain protocol commands were received. Now, the last seen time is set when the peer disconnects if the peer had sent a verack message and was connected for more than 20 minutes. This mimics Bitcoin Core commit: 9c2737901b5203f267d21d728019d64b46f1d9f3 Also, add additional sanity checking before updating the peer's timestamp. These include: - Do not mark a peer as connected if we never received a version message. - Disconnect a peer for sending a verack before btcd sent a version - Disconnect a peer for sending multiple verack's --- addrmgr/addrmanager.go | 6 +++--- peer.go | 49 ++++++++++++++++++++++++++---------------- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/addrmgr/addrmanager.go b/addrmgr/addrmanager.go index 318fd6d8..c4b6fcd9 100644 --- a/addrmgr/addrmanager.go +++ b/addrmgr/addrmanager.go @@ -862,12 +862,12 @@ func (a *AddrManager) Good(addr *wire.NetAddress) { if ka == nil { return } + + // ka.Timestamp is not updated here to avoid leaking information + // about currently connected peers. now := time.Now() ka.lastsuccess = now ka.lastattempt = now - naCopy := *ka.na - naCopy.Timestamp = time.Now() - ka.na = &naCopy ka.attempts = 0 // move to tried set, optionally evicting other addresses if neeed. diff --git a/peer.go b/peer.go index 8b05f60f..2bce1838 100644 --- a/peer.go +++ b/peer.go @@ -183,6 +183,8 @@ type peer struct { StatsMtx sync.Mutex // protects all statistics below here. versionKnown bool protocolVersion uint32 + versionSent bool + verAckReceived bool services wire.ServiceFlag timeOffset int64 timeConnected time.Time @@ -1480,25 +1482,36 @@ out: } // Handle each supported message type. - markConnected := false switch msg := rmsg.(type) { case *wire.MsgVersion: p.handleVersionMsg(msg) - markConnected = true case *wire.MsgVerAck: - // Do nothing. + p.StatsMtx.Lock() + versionSent := p.versionSent + verAckReceived := p.verAckReceived + p.StatsMtx.Unlock() + + if !versionSent { + peerLog.Infof("Received 'verack' from peer %v "+ + "before version was sent -- disconnecting", p) + break out + } + if verAckReceived { + peerLog.Infof("Already received 'verack' from "+ + "peer %v -- disconnecting", p) + break out + } + p.verAckReceived = true case *wire.MsgGetAddr: p.handleGetAddrMsg(msg) case *wire.MsgAddr: p.handleAddrMsg(msg) - markConnected = true case *wire.MsgPing: p.handlePingMsg(msg) - markConnected = true case *wire.MsgPong: p.handlePongMsg(msg) @@ -1524,7 +1537,6 @@ out: case *wire.MsgInv: p.handleInvMsg(msg) - markConnected = true case *wire.MsgHeaders: p.handleHeadersMsg(msg) @@ -1537,7 +1549,6 @@ out: case *wire.MsgGetData: p.handleGetDataMsg(msg) - markConnected = true case *wire.MsgGetBlocks: p.handleGetBlocksMsg(msg) @@ -1563,16 +1574,6 @@ out: rmsg.Command()) } - // Mark the address as currently connected and working as of - // now if one of the messages that trigger it was processed. - if markConnected && atomic.LoadInt32(&p.disconnect) == 0 { - if p.na == nil { - peerLog.Warnf("we're getting stuff before we " + - "got a version message. that's bad") - continue - } - p.server.addrManager.Connected(p.na) - } // ok we got a message, reset the timer. // timer just calls p.Disconnect() after logging. idleTimer.Reset(idleTimeoutMinutes * time.Minute) @@ -1756,7 +1757,10 @@ out: reset := true switch m := msg.msg.(type) { case *wire.MsgVersion: - // should get an ack + // should get a verack + p.StatsMtx.Lock() + p.versionSent = true + p.StatsMtx.Unlock() case *wire.MsgGetAddr: // should get addresses case *wire.MsgPing: @@ -1875,6 +1879,15 @@ func (p *peer) Disconnect() { if atomic.AddInt32(&p.disconnect, 1) != 1 { return } + + // Update the address' last seen time if the peer has acknowledged + // our version and has sent us its version as well. + p.StatsMtx.Lock() + if p.verAckReceived && p.versionKnown && p.na != nil { + p.server.addrManager.Connected(p.na) + } + p.StatsMtx.Unlock() + peerLog.Tracef("disconnecting %s", p) close(p.quit) if atomic.LoadInt32(&p.connected) != 0 {