peer: Don't send unsupported reject to old peers.
This commit is contained in:
parent
05bdf3b741
commit
bc36cf51c6
2 changed files with 122 additions and 12 deletions
24
peer/peer.go
24
peer/peer.go
|
@ -29,6 +29,10 @@ const (
|
||||||
// MaxProtocolVersion is the max protocol version the peer supports.
|
// MaxProtocolVersion is the max protocol version the peer supports.
|
||||||
MaxProtocolVersion = wire.FeeFilterVersion
|
MaxProtocolVersion = wire.FeeFilterVersion
|
||||||
|
|
||||||
|
// minAcceptableProtocolVersion is the lowest protocol version that a
|
||||||
|
// connected peer may support.
|
||||||
|
minAcceptableProtocolVersion = wire.MultipleAddressVersion
|
||||||
|
|
||||||
// outputBufferSize is the number of elements the output channels use.
|
// outputBufferSize is the number of elements the output channels use.
|
||||||
outputBufferSize = 50
|
outputBufferSize = 50
|
||||||
|
|
||||||
|
@ -1012,15 +1016,14 @@ func (p *Peer) handleRemoteVersionMsg(msg *wire.MsgVersion) error {
|
||||||
|
|
||||||
// Notify and disconnect clients that have a protocol version that is
|
// Notify and disconnect clients that have a protocol version that is
|
||||||
// too old.
|
// too old.
|
||||||
if msg.ProtocolVersion < int32(wire.MultipleAddressVersion) {
|
//
|
||||||
// Send a reject message indicating the protocol version is
|
// NOTE: If minAcceptableProtocolVersion is raised to be higher than
|
||||||
// obsolete and wait for the message to be sent before
|
// wire.RejectVersion, this should send a reject packet before
|
||||||
// disconnecting.
|
// disconnecting.
|
||||||
|
if uint32(msg.ProtocolVersion) < minAcceptableProtocolVersion {
|
||||||
reason := fmt.Sprintf("protocol version must be %d or greater",
|
reason := fmt.Sprintf("protocol version must be %d or greater",
|
||||||
wire.MultipleAddressVersion)
|
minAcceptableProtocolVersion)
|
||||||
rejectMsg := wire.NewMsgReject(msg.Command(), wire.RejectObsolete,
|
return errors.New(reason)
|
||||||
reason)
|
|
||||||
return p.writeMessage(rejectMsg, wire.LatestEncoding)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updating a bunch of stats including block based stats, and the
|
// Updating a bunch of stats including block based stats, and the
|
||||||
|
@ -1412,9 +1415,8 @@ cleanup:
|
||||||
// inHandler handles all incoming messages for the peer. It must be run as a
|
// inHandler handles all incoming messages for the peer. It must be run as a
|
||||||
// goroutine.
|
// goroutine.
|
||||||
func (p *Peer) inHandler() {
|
func (p *Peer) inHandler() {
|
||||||
// Peers must complete the initial version negotiation within a shorter
|
// The timer is stopped when a new message is received and reset after it
|
||||||
// timeframe than a general idle timeout. The timer is then reset below
|
// is processed.
|
||||||
// to idleTimeout for all future messages.
|
|
||||||
idleTimer := time.AfterFunc(idleTimeout, func() {
|
idleTimer := time.AfterFunc(idleTimeout, func() {
|
||||||
log.Warnf("Peer %s no answer for %s -- disconnecting", p, idleTimeout)
|
log.Warnf("Peer %s no answer for %s -- disconnecting", p, idleTimeout)
|
||||||
p.Disconnect()
|
p.Disconnect()
|
||||||
|
|
|
@ -58,7 +58,10 @@ func (c conn) RemoteAddr() net.Addr {
|
||||||
|
|
||||||
// Close handles closing the connection.
|
// Close handles closing the connection.
|
||||||
func (c conn) Close() error {
|
func (c conn) Close() error {
|
||||||
return nil
|
if c.Closer == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.Closer.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c conn) SetDeadline(t time.Time) error { return nil }
|
func (c conn) SetDeadline(t time.Time) error { return nil }
|
||||||
|
@ -80,9 +83,11 @@ func pipe(c1, c2 *conn) (*conn, *conn) {
|
||||||
r2, w2 := io.Pipe()
|
r2, w2 := io.Pipe()
|
||||||
|
|
||||||
c1.Writer = w1
|
c1.Writer = w1
|
||||||
|
c1.Closer = w1
|
||||||
c2.Reader = r1
|
c2.Reader = r1
|
||||||
c1.Reader = r2
|
c1.Reader = r2
|
||||||
c2.Writer = w2
|
c2.Writer = w2
|
||||||
|
c2.Closer = w2
|
||||||
|
|
||||||
return c1, c2
|
return c1, c2
|
||||||
}
|
}
|
||||||
|
@ -673,6 +678,109 @@ func TestOutboundPeer(t *testing.T) {
|
||||||
p2.Disconnect()
|
p2.Disconnect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests that the node disconnects from peers with an unsupported protocol
|
||||||
|
// version.
|
||||||
|
func TestUnsupportedVersionPeer(t *testing.T) {
|
||||||
|
peerCfg := &peer.Config{
|
||||||
|
UserAgentName: "peer",
|
||||||
|
UserAgentVersion: "1.0",
|
||||||
|
UserAgentComments: []string{"comment"},
|
||||||
|
ChainParams: &chaincfg.MainNetParams,
|
||||||
|
Services: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
localNA := wire.NewNetAddressIPPort(
|
||||||
|
net.ParseIP("10.0.0.1"),
|
||||||
|
uint16(8333),
|
||||||
|
wire.SFNodeNetwork,
|
||||||
|
)
|
||||||
|
remoteNA := wire.NewNetAddressIPPort(
|
||||||
|
net.ParseIP("10.0.0.2"),
|
||||||
|
uint16(8333),
|
||||||
|
wire.SFNodeNetwork,
|
||||||
|
)
|
||||||
|
localConn, remoteConn := pipe(
|
||||||
|
&conn{laddr: "10.0.0.1:8333", raddr: "10.0.0.2:8333"},
|
||||||
|
&conn{laddr: "10.0.0.2:8333", raddr: "10.0.0.1:8333"},
|
||||||
|
)
|
||||||
|
|
||||||
|
p, err := peer.NewOutboundPeer(peerCfg, "10.0.0.1:8333")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewOutboundPeer: unexpected err - %v\n", err)
|
||||||
|
}
|
||||||
|
p.AssociateConnection(localConn)
|
||||||
|
|
||||||
|
// Read outbound messages to peer into a channel
|
||||||
|
outboundMessages := make(chan wire.Message)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
_, msg, _, err := wire.ReadMessageN(
|
||||||
|
remoteConn,
|
||||||
|
p.ProtocolVersion(),
|
||||||
|
peerCfg.ChainParams.Net,
|
||||||
|
)
|
||||||
|
if err == io.EOF {
|
||||||
|
close(outboundMessages)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error reading message from local node: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
outboundMessages <- msg
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Read version message sent to remote peer
|
||||||
|
select {
|
||||||
|
case msg := <-outboundMessages:
|
||||||
|
if _, ok := msg.(*wire.MsgVersion); !ok {
|
||||||
|
t.Fatalf("Expected version message, got [%s]", msg.Command())
|
||||||
|
}
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
t.Fatal("Peer did not send version message")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remote peer writes version message advertising invalid protocol version 1
|
||||||
|
invalidVersionMsg := wire.NewMsgVersion(remoteNA, localNA, 0, 0)
|
||||||
|
invalidVersionMsg.ProtocolVersion = 1
|
||||||
|
|
||||||
|
_, err = wire.WriteMessageN(
|
||||||
|
remoteConn.Writer,
|
||||||
|
invalidVersionMsg,
|
||||||
|
uint32(invalidVersionMsg.ProtocolVersion),
|
||||||
|
peerCfg.ChainParams.Net,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("wire.WriteMessageN: unexpected err - %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expect peer to disconnect automatically
|
||||||
|
disconnected := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
p.WaitForDisconnect()
|
||||||
|
disconnected <- struct{}{}
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-disconnected:
|
||||||
|
close(disconnected)
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
t.Fatal("Peer did not automatically disconnect")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expect no further outbound messages from peer
|
||||||
|
select {
|
||||||
|
case msg, chanOpen := <-outboundMessages:
|
||||||
|
if chanOpen {
|
||||||
|
t.Fatalf("Expected no further messages, received [%s]", msg.Command())
|
||||||
|
}
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
t.Fatal("Timeout waiting for remote reader to close")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// Allow self connection when running the tests.
|
// Allow self connection when running the tests.
|
||||||
peer.TstAllowSelfConns()
|
peer.TstAllowSelfConns()
|
||||||
|
|
Loading…
Add table
Reference in a new issue