p2p: handle both disconnect encodings

And update some unit tests, since they were still using the wrong format.
This commit is contained in:
Felix Lange 2024-12-10 13:50:50 +01:00
parent 9950b419ad
commit d109446f86
4 changed files with 36 additions and 11 deletions

View file

@ -345,9 +345,7 @@ func (p *Peer) handle(msg Msg) error {
case msg.Code == discMsg: case msg.Code == discMsg:
// This is the last message. We don't need to discard or // This is the last message. We don't need to discard or
// check errors because, the connection will be closed after it. // check errors because, the connection will be closed after it.
var m struct{ R DiscReason } return decodeDisconnectMessage(msg.Payload)
rlp.Decode(msg.Payload, &m)
return m.R
case msg.Code < baseProtocolLength: case msg.Code < baseProtocolLength:
// ignore other base protocol messages // ignore other base protocol messages
return msg.Discard() return msg.Discard()
@ -372,6 +370,27 @@ func (p *Peer) handle(msg Msg) error {
return nil return nil
} }
// decodeDisconnectMessage decodes the payload of discMsg.
func decodeDisconnectMessage(r io.Reader) (reason DiscReason) {
s := rlp.NewStream(r, 100)
k, _, err := s.Kind()
if err != nil {
return DiscInvalid
}
if k == rlp.List {
s.List()
err = s.Decode(&reason)
} else {
// Legacy path: some implementations, including geth, used to send the disconnect
// reason as a byte array by accident.
err = s.Decode(&reason)
}
if err != nil {
reason = DiscInvalid
}
return reason
}
func countMatchingProtocols(protocols []Protocol, caps []Cap) int { func countMatchingProtocols(protocols []Protocol, caps []Cap) int {
n := 0 n := 0
for _, cap := range caps { for _, cap := range caps {

View file

@ -70,6 +70,8 @@ const (
DiscSelf DiscSelf
DiscReadTimeout DiscReadTimeout
DiscSubprotocolError = DiscReason(0x10) DiscSubprotocolError = DiscReason(0x10)
DiscInvalid = 0xff
) )
var discReasonToString = [...]string{ var discReasonToString = [...]string{
@ -86,6 +88,7 @@ var discReasonToString = [...]string{
DiscSelf: "connected to self", DiscSelf: "connected to self",
DiscReadTimeout: "read timeout", DiscReadTimeout: "read timeout",
DiscSubprotocolError: "subprotocol error", DiscSubprotocolError: "subprotocol error",
DiscInvalid: "invalid disconnect reason",
} }
func (d DiscReason) String() string { func (d DiscReason) String() string {

View file

@ -120,7 +120,7 @@ func (t *rlpxTransport) close(err error) {
if err := t.conn.SetWriteDeadline(deadline); err == nil { if err := t.conn.SetWriteDeadline(deadline); err == nil {
// Connection supports write deadline. // Connection supports write deadline.
t.wbuf.Reset() t.wbuf.Reset()
rlp.Encode(&t.wbuf, []interface{}{reason}) rlp.Encode(&t.wbuf, []any{reason})
t.conn.Write(discMsg, t.wbuf.Bytes()) t.conn.Write(discMsg, t.wbuf.Bytes())
} }
} }
@ -164,11 +164,8 @@ func readProtocolHandshake(rw MsgReader) (*protoHandshake, error) {
if msg.Code == discMsg { if msg.Code == discMsg {
// Disconnect before protocol handshake is valid according to the // Disconnect before protocol handshake is valid according to the
// spec and we send it ourself if the post-handshake checks fail. // spec and we send it ourself if the post-handshake checks fail.
// We can't return the reason directly, though, because it is echoed r := decodeDisconnectMessage(msg.Payload)
// back otherwise. Wrap it in a string instead. return nil, r
var m struct{ R DiscReason }
rlp.Decode(msg.Payload, &m)
return nil, m.R
} }
if msg.Code != handshakeMsg { if msg.Code != handshakeMsg {
return nil, fmt.Errorf("expected handshake, got %x", msg.Code) return nil, fmt.Errorf("expected handshake, got %x", msg.Code)

View file

@ -97,7 +97,7 @@ func TestProtocolHandshake(t *testing.T) {
return return
} }
if err := ExpectMsg(rlpx, discMsg, []DiscReason{DiscQuitting}); err != nil { if err := ExpectMsg(rlpx, discMsg, []any{DiscQuitting}); err != nil {
t.Errorf("error receiving disconnect: %v", err) t.Errorf("error receiving disconnect: %v", err)
} }
}() }()
@ -112,7 +112,13 @@ func TestProtocolHandshakeErrors(t *testing.T) {
}{ }{
{ {
code: discMsg, code: discMsg,
msg: []DiscReason{DiscQuitting}, msg: []any{DiscQuitting},
err: DiscQuitting,
},
{
// legacy disconnect encoding as byte array
code: discMsg,
msg: []byte{byte(DiscQuitting)},
err: DiscQuitting, err: DiscQuitting,
}, },
{ {