p2p/discover: repeat WHOAREYOU challenge when handshake in progress

This commit is contained in:
Felix Lange 2025-03-11 16:18:18 +01:00
parent 4cdd7c8631
commit 0fd0c83286
3 changed files with 25 additions and 0 deletions

View file

@ -55,6 +55,8 @@ type codecV5 interface {
// Decode decodes a packet. It returns a *v5wire.Unknown packet if decryption fails.
// The *enode.Node return value is non-nil when the input contains a handshake response.
Decode([]byte, string) (enode.ID, *enode.Node, v5wire.Packet, error)
CurrentChallenge(enode.ID, string) *v5wire.Whoareyou
}
// UDPv5 is the implementation of protocol version 5.
@ -774,6 +776,19 @@ func (t *UDPv5) handle(p v5wire.Packet, fromID enode.ID, fromAddr netip.AddrPort
// handleUnknown initiates a handshake by responding with WHOAREYOU.
func (t *UDPv5) handleUnknown(p *v5wire.Unknown, fromID enode.ID, fromAddr netip.AddrPort) {
currentChallenge := t.codec.CurrentChallenge(fromID, fromAddr.String())
if currentChallenge != nil {
// This case happens when the sender issues multiple concurrent requests.
// Since we only support one in-progress handshake at a time, we need to tell
// them which handshake attempt they need to complete. We tell them to use the
// existing handshake attempt since the response to that one might still be in
// transit.
t.log.Warn("Repeating discv5 handshake challenge", "id", fromID, "addr", fromAddr)
t.sendResponse(fromID, fromAddr, currentChallenge)
return
}
// Send a fresh challenge.
challenge := &v5wire.Whoareyou{Nonce: p.Nonce}
crand.Read(challenge.IDNonce[:])
if n := t.getNode(fromID); n != nil {

View file

@ -717,6 +717,10 @@ func (c *testCodec) Encode(toID enode.ID, addr string, p v5wire.Packet, _ *v5wir
return frame, authTag, err
}
func (c *testCodec) CurrentChallenge(enode.ID, string) *v5wire.Whoareyou {
return nil
}
func (c *testCodec) Decode(input []byte, addr string) (enode.ID, *enode.Node, v5wire.Packet, error) {
frame, p, err := c.decodeFrame(input)
if err != nil {

View file

@ -245,6 +245,12 @@ func (c *Codec) EncodeRaw(id enode.ID, head Header, msgdata []byte) ([]byte, err
return c.buf.Bytes(), nil
}
// CurrentChallenge returns the latest challenge sent to the given node.
// This will return non-nil while a handshake is in progress.
func (c *Codec) CurrentChallenge(id enode.ID, addr string) *Whoareyou {
return c.sc.getHandshake(id, addr)
}
func (c *Codec) writeHeaders(head *Header) {
c.buf.Reset()
c.buf.Write(head.IV[:])