peer: fix panic due to err in handleVersionMsg

In case of an error during protocol negotiation in handleVersionMsg,
immediately break out and prevent further processing of OnVersion
listener which generally depends upon peer attributes like NA to be set
during the negotiation. Fixes #579.
This commit is contained in:
Javed Khan 2015-12-10 00:24:19 +05:30
parent 2f6aeacfab
commit 542844832e

View file

@ -7,6 +7,7 @@ package peer
import ( import (
"bytes" "bytes"
"container/list" "container/list"
"errors"
"fmt" "fmt"
"io" "io"
prand "math/rand" prand "math/rand"
@ -974,12 +975,10 @@ func (p *Peer) PushRejectMsg(command string, code wire.RejectCode, reason string
// handleVersionMsg is invoked when a peer receives a version bitcoin message // handleVersionMsg is invoked when a peer receives a version bitcoin message
// and is used to negotiate the protocol version details as well as kick start // and is used to negotiate the protocol version details as well as kick start
// the communications. // the communications.
func (p *Peer) handleVersionMsg(msg *wire.MsgVersion) { func (p *Peer) handleVersionMsg(msg *wire.MsgVersion) error {
// Detect self connections. // Detect self connections.
if !allowSelfConns && sentNonces.Exists(msg.Nonce) { if !allowSelfConns && sentNonces.Exists(msg.Nonce) {
log.Debugf("Disconnecting peer connected to self %s", p) return errors.New("disconnecting peer connected to self")
p.Disconnect()
return
} }
// Notify and disconnect clients that have a protocol version that is // Notify and disconnect clients that have a protocol version that is
@ -992,25 +991,19 @@ func (p *Peer) handleVersionMsg(msg *wire.MsgVersion) {
wire.MultipleAddressVersion) wire.MultipleAddressVersion)
p.PushRejectMsg(msg.Command(), wire.RejectObsolete, reason, p.PushRejectMsg(msg.Command(), wire.RejectObsolete, reason,
nil, true) nil, true)
p.Disconnect() return errors.New(reason)
return
} }
// Limit to one version message per peer. // Limit to one version message per peer.
// No read lock is necessary because versionKnown is not written to in any // No read lock is necessary because versionKnown is not written to in any
// other goroutine // other goroutine
if p.versionKnown { if p.versionKnown {
log.Errorf("Only one version message per peer is allowed %s.",
p)
// Send an reject message indicating the version message was // Send an reject message indicating the version message was
// incorrectly sent twice and wait for the message to be sent // incorrectly sent twice and wait for the message to be sent
// before disconnecting. // before disconnecting.
p.PushRejectMsg(msg.Command(), wire.RejectDuplicate, p.PushRejectMsg(msg.Command(), wire.RejectDuplicate,
"duplicate version message", nil, true) "duplicate version message", nil, true)
return errors.New("only one version message per peer is allowed")
p.Disconnect()
return
} }
// Updating a bunch of stats. // Updating a bunch of stats.
@ -1043,24 +1036,20 @@ func (p *Peer) handleVersionMsg(msg *wire.MsgVersion) {
// at connection time and no point recomputing. // at connection time and no point recomputing.
na, err := newNetAddress(p.conn.RemoteAddr(), p.services) na, err := newNetAddress(p.conn.RemoteAddr(), p.services)
if err != nil { if err != nil {
log.Errorf("Can't get remote address: %v", err) return err
p.Disconnect()
return
} }
p.na = na p.na = na
// Send version. // Send version.
err = p.pushVersionMsg() err = p.pushVersionMsg()
if err != nil { if err != nil {
log.Errorf("Can't send version message to %s: %v", return err
p, err)
p.Disconnect()
return
} }
} }
// Send verack. // Send verack.
p.QueueMessage(wire.NewMsgVerAck(), nil) p.QueueMessage(wire.NewMsgVerAck(), nil)
return nil
} }
// isValidBIP0111 is a helper function for the bloom filter commands to check // isValidBIP0111 is a helper function for the bloom filter commands to check
@ -1527,7 +1516,13 @@ out:
p.stallControl <- stallControlMsg{sccHandlerStart, rmsg} p.stallControl <- stallControlMsg{sccHandlerStart, rmsg}
switch msg := rmsg.(type) { switch msg := rmsg.(type) {
case *wire.MsgVersion: case *wire.MsgVersion:
p.handleVersionMsg(msg) err := p.handleVersionMsg(msg)
if err != nil {
log.Debugf("New peer %v - error negotiating protocol: %v",
p, err)
p.Disconnect()
break out
}
if p.cfg.Listeners.OnVersion != nil { if p.cfg.Listeners.OnVersion != nil {
p.cfg.Listeners.OnVersion(p, msg) p.cfg.Listeners.OnVersion(p, msg)
} }