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
This commit is contained in:
David Hill 2015-03-17 13:05:11 -04:00
parent 0eef96e1c8
commit 2cc85ef428
2 changed files with 34 additions and 21 deletions

View file

@ -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.

49
peer.go
View file

@ -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 {