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 { if ka == nil {
return return
} }
// ka.Timestamp is not updated here to avoid leaking information
// about currently connected peers.
now := time.Now() now := time.Now()
ka.lastsuccess = now ka.lastsuccess = now
ka.lastattempt = now ka.lastattempt = now
naCopy := *ka.na
naCopy.Timestamp = time.Now()
ka.na = &naCopy
ka.attempts = 0 ka.attempts = 0
// move to tried set, optionally evicting other addresses if neeed. // 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. StatsMtx sync.Mutex // protects all statistics below here.
versionKnown bool versionKnown bool
protocolVersion uint32 protocolVersion uint32
versionSent bool
verAckReceived bool
services wire.ServiceFlag services wire.ServiceFlag
timeOffset int64 timeOffset int64
timeConnected time.Time timeConnected time.Time
@ -1480,25 +1482,36 @@ out:
} }
// Handle each supported message type. // Handle each supported message type.
markConnected := false
switch msg := rmsg.(type) { switch msg := rmsg.(type) {
case *wire.MsgVersion: case *wire.MsgVersion:
p.handleVersionMsg(msg) p.handleVersionMsg(msg)
markConnected = true
case *wire.MsgVerAck: 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: case *wire.MsgGetAddr:
p.handleGetAddrMsg(msg) p.handleGetAddrMsg(msg)
case *wire.MsgAddr: case *wire.MsgAddr:
p.handleAddrMsg(msg) p.handleAddrMsg(msg)
markConnected = true
case *wire.MsgPing: case *wire.MsgPing:
p.handlePingMsg(msg) p.handlePingMsg(msg)
markConnected = true
case *wire.MsgPong: case *wire.MsgPong:
p.handlePongMsg(msg) p.handlePongMsg(msg)
@ -1524,7 +1537,6 @@ out:
case *wire.MsgInv: case *wire.MsgInv:
p.handleInvMsg(msg) p.handleInvMsg(msg)
markConnected = true
case *wire.MsgHeaders: case *wire.MsgHeaders:
p.handleHeadersMsg(msg) p.handleHeadersMsg(msg)
@ -1537,7 +1549,6 @@ out:
case *wire.MsgGetData: case *wire.MsgGetData:
p.handleGetDataMsg(msg) p.handleGetDataMsg(msg)
markConnected = true
case *wire.MsgGetBlocks: case *wire.MsgGetBlocks:
p.handleGetBlocksMsg(msg) p.handleGetBlocksMsg(msg)
@ -1563,16 +1574,6 @@ out:
rmsg.Command()) 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. // ok we got a message, reset the timer.
// timer just calls p.Disconnect() after logging. // timer just calls p.Disconnect() after logging.
idleTimer.Reset(idleTimeoutMinutes * time.Minute) idleTimer.Reset(idleTimeoutMinutes * time.Minute)
@ -1756,7 +1757,10 @@ out:
reset := true reset := true
switch m := msg.msg.(type) { switch m := msg.msg.(type) {
case *wire.MsgVersion: case *wire.MsgVersion:
// should get an ack // should get a verack
p.StatsMtx.Lock()
p.versionSent = true
p.StatsMtx.Unlock()
case *wire.MsgGetAddr: case *wire.MsgGetAddr:
// should get addresses // should get addresses
case *wire.MsgPing: case *wire.MsgPing:
@ -1875,6 +1879,15 @@ func (p *peer) Disconnect() {
if atomic.AddInt32(&p.disconnect, 1) != 1 { if atomic.AddInt32(&p.disconnect, 1) != 1 {
return 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) peerLog.Tracef("disconnecting %s", p)
close(p.quit) close(p.quit)
if atomic.LoadInt32(&p.connected) != 0 { if atomic.LoadInt32(&p.connected) != 0 {