Add message blocking semantics to block processing.

This commit modifies the input message handler so that when a remote peer
sends a block, no further messages from that peer are accepted until the
block has been fully processed and therefore known good or bad.  This
helps prevent a malicious peer from queueing up a bunch of bad blocks
before disconnecting (or being disconnected) and wasting memory.

Additionally, this behavior is depended on by at least the block
acceptance test tool as the reference implementation processes blocks in
the same thread and therefore blocks further messages until the block has
been fully processed as well.
This commit is contained in:
Dave Collins 2013-08-16 13:35:38 -05:00
parent 2570ecd2ac
commit 60779ea8df
2 changed files with 23 additions and 10 deletions

View file

@ -31,6 +31,7 @@ type inventoryItem struct {
// so the block handler has access to that information. // so the block handler has access to that information.
type blockMsg struct { type blockMsg struct {
block *btcutil.Block block *btcutil.Block
peer *peer
} }
// invMsg packages a bitcoin inv message and the peer it came from together // invMsg packages a bitcoin inv message and the peer it came from together
@ -209,8 +210,9 @@ out:
for !b.shutdown { for !b.shutdown {
select { select {
// Handle new block messages. // Handle new block messages.
case msg := <-b.blockQueue: case bmsg := <-b.blockQueue:
b.handleBlockMsg(msg.block) b.handleBlockMsg(bmsg.block)
bmsg.peer.blockProcessed <- true
// Handle new inventory messages. // Handle new inventory messages.
case msg := <-b.invQueue: case msg := <-b.invQueue:
@ -287,13 +289,14 @@ out:
} }
// QueueBlock adds the passed block message and peer to the block handling queue. // QueueBlock adds the passed block message and peer to the block handling queue.
func (b *blockManager) QueueBlock(block *btcutil.Block) { func (b *blockManager) QueueBlock(block *btcutil.Block, p *peer) {
// Don't accept more blocks if we're shutting down. // Don't accept more blocks if we're shutting down.
if b.shutdown { if b.shutdown {
p.blockProcessed <- false
return return
} }
bmsg := blockMsg{block: block} bmsg := blockMsg{block: block, peer: p}
b.blockQueue <- &bmsg b.blockQueue <- &bmsg
} }

22
peer.go
View file

@ -93,6 +93,7 @@ type peer struct {
lastBlock int32 lastBlock int32
wg sync.WaitGroup wg sync.WaitGroup
outputQueue chan btcwire.Message outputQueue chan btcwire.Message
blockProcessed chan bool
quit chan bool quit chan bool
} }
@ -633,11 +634,7 @@ out:
break out break out
} }
// Some messages are handled directly, while other messages // Handle each supported message type.
// are sent to a queue to be processed. Directly handling
// getdata and getblocks messages makes it impossible for a peer
// to spam with requests. However, it means that our getdata
// requests to it may not get prompt replies.
switch msg := rmsg.(type) { switch msg := rmsg.(type) {
case *btcwire.MsgVersion: case *btcwire.MsgVersion:
p.handleVersionMsg(msg) p.handleVersionMsg(msg)
@ -662,8 +659,20 @@ out:
p.server.BroadcastMessage(msg, p) p.server.BroadcastMessage(msg, p)
case *btcwire.MsgBlock: case *btcwire.MsgBlock:
// Queue the block up to be handled by the block
// manager and intentionally block further receives
// until the bitcoin block is fully processed and known
// good or bad. This helps prevent a malicious peer
// from queueing up a bunch of bad blocks before
// disconnecting (or being disconnected) and wasting
// memory. Additionally, this behavior is depended on
// by at least the block acceptance test tool as the
// reference implementation processes blocks in the same
// thread and therefore blocks further messages until
// the bitcoin block has been fully processed.
block := btcutil.NewBlockFromBlockAndBytes(msg, buf) block := btcutil.NewBlockFromBlockAndBytes(msg, buf)
p.server.blockManager.QueueBlock(block) p.server.blockManager.QueueBlock(block, p)
<-p.blockProcessed
case *btcwire.MsgInv: case *btcwire.MsgInv:
p.server.blockManager.QueueInv(msg, p) p.server.blockManager.QueueInv(msg, p)
@ -783,6 +792,7 @@ func newPeer(s *server, conn net.Conn, inbound bool, persistent bool) *peer {
persistent: persistent, persistent: persistent,
knownAddresses: make(map[string]bool), knownAddresses: make(map[string]bool),
outputQueue: make(chan btcwire.Message, outputBufferSize), outputQueue: make(chan btcwire.Message, outputBufferSize),
blockProcessed: make(chan bool, 1),
quit: make(chan bool), quit: make(chan bool),
} }
return &p return &p