p2p/discover: WIP node refactoring part 2

This commit is contained in:
Felix Lange 2024-05-26 11:43:49 +02:00
parent b168d82446
commit c0a1f1dfe1
8 changed files with 183 additions and 176 deletions

View file

@ -22,6 +22,7 @@ import (
"encoding/binary" "encoding/binary"
"math/rand" "math/rand"
"net" "net"
"net/netip"
"sync" "sync"
"time" "time"
@ -34,8 +35,8 @@ import (
// UDPConn is a network connection on which discovery can operate. // UDPConn is a network connection on which discovery can operate.
type UDPConn interface { type UDPConn interface {
ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) ReadFromUDPAddrPort(b []byte) (n int, addr netip.AddrPort, err error)
WriteToUDP(b []byte, addr *net.UDPAddr) (n int, err error) WriteToUDPAddrPort(b []byte, addr netip.AddrPort) (n int, err error)
Close() error Close() error
LocalAddr() net.Addr LocalAddr() net.Addr
} }
@ -94,7 +95,7 @@ func ListenUDP(c UDPConn, ln *enode.LocalNode, cfg Config) (*UDPv4, error) {
// channel if configured. // channel if configured.
type ReadPacket struct { type ReadPacket struct {
Data []byte Data []byte
Addr *net.UDPAddr Addr netip.AddrPort
} }
type randomSource interface { type randomSource interface {

View file

@ -18,7 +18,7 @@ package discover
import ( import (
"fmt" "fmt"
"net" "net/netip"
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
) )
@ -59,15 +59,15 @@ func newMeteredConn(conn UDPConn) UDPConn {
} }
// ReadFromUDP delegates a network read to the underlying connection, bumping the udp ingress traffic meter along the way. // ReadFromUDP delegates a network read to the underlying connection, bumping the udp ingress traffic meter along the way.
func (c *meteredUdpConn) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) { func (c *meteredUdpConn) ReadFromUDPAddrPort(b []byte) (n int, addr netip.AddrPort, err error) {
n, addr, err = c.UDPConn.ReadFromUDP(b) n, addr, err = c.UDPConn.ReadFromUDPAddrPort(b)
ingressTrafficMeter.Mark(int64(n)) ingressTrafficMeter.Mark(int64(n))
return n, addr, err return n, addr, err
} }
// Write delegates a network write to the underlying connection, bumping the udp egress traffic meter along the way. // Write delegates a network write to the underlying connection, bumping the udp egress traffic meter along the way.
func (c *meteredUdpConn) WriteToUDP(b []byte, addr *net.UDPAddr) (n int, err error) { func (c *meteredUdpConn) WriteToUDP(b []byte, addr netip.AddrPort) (n int, err error) {
n, err = c.UDPConn.WriteToUDP(b, addr) n, err = c.UDPConn.WriteToUDPAddrPort(b, addr)
egressTrafficMeter.Mark(int64(n)) egressTrafficMeter.Mark(int64(n))
return n, err return n, err
} }

View file

@ -95,7 +95,7 @@ type UDPv4 struct {
type replyMatcher struct { type replyMatcher struct {
// these fields must match in the reply. // these fields must match in the reply.
from enode.ID from enode.ID
ip net.IP ip netip.Addr
ptype byte ptype byte
// time when the request must complete // time when the request must complete
@ -121,7 +121,7 @@ type replyMatchFunc func(v4wire.Packet) (matched bool, requestDone bool)
// reply is a reply packet from a certain node. // reply is a reply packet from a certain node.
type reply struct { type reply struct {
from enode.ID from enode.ID
ip net.IP ip netip.Addr
data v4wire.Packet data v4wire.Packet
// loop indicates whether there was // loop indicates whether there was
// a matching request by sending on this channel. // a matching request by sending on this channel.
@ -204,8 +204,8 @@ func (t *UDPv4) Resolve(n *enode.Node) *enode.Node {
func (t *UDPv4) ourEndpoint() v4wire.Endpoint { func (t *UDPv4) ourEndpoint() v4wire.Endpoint {
n := t.Self() n := t.Self()
a := &net.UDPAddr{IP: n.IP(), Port: n.UDP()} addr, _ := n.UDPEndpoint()
return v4wire.NewEndpoint(a, uint16(n.TCP())) return v4wire.NewEndpoint(addr, uint16(n.TCP()))
} }
// Ping sends a ping message to the given node. // Ping sends a ping message to the given node.
@ -216,7 +216,11 @@ func (t *UDPv4) Ping(n *enode.Node) error {
// ping sends a ping message to the given node and waits for a reply. // ping sends a ping message to the given node and waits for a reply.
func (t *UDPv4) ping(n *enode.Node) (seq uint64, err error) { func (t *UDPv4) ping(n *enode.Node) (seq uint64, err error) {
rm := t.sendPing(n.ID(), &net.UDPAddr{IP: n.IP(), Port: n.UDP()}, nil) addr, ok := n.UDPEndpoint()
if !ok {
return 0, errNoUDPEndpoint
}
rm := t.sendPing(n.ID(), addr, nil)
if err = <-rm.errc; err == nil { if err = <-rm.errc; err == nil {
seq = rm.reply.(*v4wire.Pong).ENRSeq seq = rm.reply.(*v4wire.Pong).ENRSeq
} }
@ -225,7 +229,7 @@ func (t *UDPv4) ping(n *enode.Node) (seq uint64, err error) {
// sendPing sends a ping message to the given node and invokes the callback // sendPing sends a ping message to the given node and invokes the callback
// when the reply arrives. // when the reply arrives.
func (t *UDPv4) sendPing(toid enode.ID, toaddr *net.UDPAddr, callback func()) *replyMatcher { func (t *UDPv4) sendPing(toid enode.ID, toaddr netip.AddrPort, callback func()) *replyMatcher {
req := t.makePing(toaddr) req := t.makePing(toaddr)
packet, hash, err := v4wire.Encode(t.priv, req) packet, hash, err := v4wire.Encode(t.priv, req)
if err != nil { if err != nil {
@ -235,7 +239,7 @@ func (t *UDPv4) sendPing(toid enode.ID, toaddr *net.UDPAddr, callback func()) *r
} }
// Add a matcher for the reply to the pending reply queue. Pongs are matched if they // Add a matcher for the reply to the pending reply queue. Pongs are matched if they
// reference the ping we're about to send. // reference the ping we're about to send.
rm := t.pending(toid, toaddr.IP, v4wire.PongPacket, func(p v4wire.Packet) (matched bool, requestDone bool) { rm := t.pending(toid, toaddr.Addr(), v4wire.PongPacket, func(p v4wire.Packet) (matched bool, requestDone bool) {
matched = bytes.Equal(p.(*v4wire.Pong).ReplyTok, hash) matched = bytes.Equal(p.(*v4wire.Pong).ReplyTok, hash)
if matched && callback != nil { if matched && callback != nil {
callback() callback()
@ -243,12 +247,13 @@ func (t *UDPv4) sendPing(toid enode.ID, toaddr *net.UDPAddr, callback func()) *r
return matched, matched return matched, matched
}) })
// Send the packet. // Send the packet.
t.localNode.UDPContact(toaddr) udpAddr := &net.UDPAddr{IP: toaddr.Addr().AsSlice()}
t.localNode.UDPContact(udpAddr)
t.write(toaddr, toid, req.Name(), packet) t.write(toaddr, toid, req.Name(), packet)
return rm return rm
} }
func (t *UDPv4) makePing(toaddr *net.UDPAddr) *v4wire.Ping { func (t *UDPv4) makePing(toaddr netip.AddrPort) *v4wire.Ping {
return &v4wire.Ping{ return &v4wire.Ping{
Version: 4, Version: 4,
From: t.ourEndpoint(), From: t.ourEndpoint(),
@ -305,27 +310,26 @@ func (t *UDPv4) newLookup(ctx context.Context, targetKey encPubkey) *lookup {
// findnode sends a findnode request to the given node and waits until // findnode sends a findnode request to the given node and waits until
// the node has sent up to k neighbors. // the node has sent up to k neighbors.
func (t *UDPv4) findnode(toid enode.ID, toAddrPort netip.AddrPort, target v4wire.Pubkey) ([]*enode.Node, error) { func (t *UDPv4) findnode(toid enode.ID, toAddrPort netip.AddrPort, target v4wire.Pubkey) ([]*enode.Node, error) {
toaddr := &net.UDPAddr{IP: toAddrPort.Addr().AsSlice(), Port: int(toAddrPort.Port())} t.ensureBond(toid, toAddrPort)
t.ensureBond(toid, toaddr)
// Add a matcher for 'neighbours' replies to the pending reply queue. The matcher is // Add a matcher for 'neighbours' replies to the pending reply queue. The matcher is
// active until enough nodes have been received. // active until enough nodes have been received.
nodes := make([]*enode.Node, 0, bucketSize) nodes := make([]*enode.Node, 0, bucketSize)
nreceived := 0 nreceived := 0
rm := t.pending(toid, toaddr.IP, v4wire.NeighborsPacket, func(r v4wire.Packet) (matched bool, requestDone bool) { rm := t.pending(toid, toAddrPort.Addr(), v4wire.NeighborsPacket, func(r v4wire.Packet) (matched bool, requestDone bool) {
reply := r.(*v4wire.Neighbors) reply := r.(*v4wire.Neighbors)
for _, rn := range reply.Nodes { for _, rn := range reply.Nodes {
nreceived++ nreceived++
n, err := t.nodeFromRPC(toaddr, rn) n, err := t.nodeFromRPC(toAddrPort, rn)
if err != nil { if err != nil {
t.log.Trace("Invalid neighbor node received", "ip", rn.IP, "addr", toaddr, "err", err) t.log.Trace("Invalid neighbor node received", "ip", rn.IP, "addr", toAddrPort, "err", err)
continue continue
} }
nodes = append(nodes, n) nodes = append(nodes, n)
} }
return true, nreceived >= bucketSize return true, nreceived >= bucketSize
}) })
t.send(toaddr, toid, &v4wire.Findnode{ t.send(toAddrPort, toid, &v4wire.Findnode{
Target: target, Target: target,
Expiration: uint64(time.Now().Add(expiration).Unix()), Expiration: uint64(time.Now().Add(expiration).Unix()),
}) })
@ -343,7 +347,7 @@ func (t *UDPv4) findnode(toid enode.ID, toAddrPort netip.AddrPort, target v4wire
// RequestENR sends ENRRequest to the given node and waits for a response. // RequestENR sends ENRRequest to the given node and waits for a response.
func (t *UDPv4) RequestENR(n *enode.Node) (*enode.Node, error) { func (t *UDPv4) RequestENR(n *enode.Node) (*enode.Node, error) {
addr := &net.UDPAddr{IP: n.IP(), Port: n.UDP()} addr, _ := n.UDPEndpoint()
t.ensureBond(n.ID(), addr) t.ensureBond(n.ID(), addr)
req := &v4wire.ENRRequest{ req := &v4wire.ENRRequest{
@ -356,7 +360,7 @@ func (t *UDPv4) RequestENR(n *enode.Node) (*enode.Node, error) {
// Add a matcher for the reply to the pending reply queue. Responses are matched if // Add a matcher for the reply to the pending reply queue. Responses are matched if
// they reference the request we're about to send. // they reference the request we're about to send.
rm := t.pending(n.ID(), addr.IP, v4wire.ENRResponsePacket, func(r v4wire.Packet) (matched bool, requestDone bool) { rm := t.pending(n.ID(), addr.Addr(), v4wire.ENRResponsePacket, func(r v4wire.Packet) (matched bool, requestDone bool) {
matched = bytes.Equal(r.(*v4wire.ENRResponse).ReplyTok, hash) matched = bytes.Equal(r.(*v4wire.ENRResponse).ReplyTok, hash)
return matched, matched return matched, matched
}) })
@ -376,7 +380,7 @@ func (t *UDPv4) RequestENR(n *enode.Node) (*enode.Node, error) {
if respN.Seq() < n.Seq() { if respN.Seq() < n.Seq() {
return n, nil // response record is older return n, nil // response record is older
} }
if err := netutil.CheckRelayIP(addr.IP, respN.IP()); err != nil { if err := netutil.CheckRelayIP(addr.Addr().AsSlice(), respN.IP()); err != nil {
return nil, fmt.Errorf("invalid IP in response record: %v", err) return nil, fmt.Errorf("invalid IP in response record: %v", err)
} }
return respN, nil return respN, nil
@ -388,7 +392,7 @@ func (t *UDPv4) TableBuckets() [][]BucketNode {
// pending adds a reply matcher to the pending reply queue. // pending adds a reply matcher to the pending reply queue.
// see the documentation of type replyMatcher for a detailed explanation. // see the documentation of type replyMatcher for a detailed explanation.
func (t *UDPv4) pending(id enode.ID, ip net.IP, ptype byte, callback replyMatchFunc) *replyMatcher { func (t *UDPv4) pending(id enode.ID, ip netip.Addr, ptype byte, callback replyMatchFunc) *replyMatcher {
ch := make(chan error, 1) ch := make(chan error, 1)
p := &replyMatcher{from: id, ip: ip, ptype: ptype, callback: callback, errc: ch} p := &replyMatcher{from: id, ip: ip, ptype: ptype, callback: callback, errc: ch}
select { select {
@ -402,7 +406,7 @@ func (t *UDPv4) pending(id enode.ID, ip net.IP, ptype byte, callback replyMatchF
// handleReply dispatches a reply packet, invoking reply matchers. It returns // handleReply dispatches a reply packet, invoking reply matchers. It returns
// whether any matcher considered the packet acceptable. // whether any matcher considered the packet acceptable.
func (t *UDPv4) handleReply(from enode.ID, fromIP net.IP, req v4wire.Packet) bool { func (t *UDPv4) handleReply(from enode.ID, fromIP netip.Addr, req v4wire.Packet) bool {
matched := make(chan bool, 1) matched := make(chan bool, 1)
select { select {
case t.gotreply <- reply{from, fromIP, req, matched}: case t.gotreply <- reply{from, fromIP, req, matched}:
@ -468,7 +472,7 @@ func (t *UDPv4) loop() {
var matched bool // whether any replyMatcher considered the reply acceptable. var matched bool // whether any replyMatcher considered the reply acceptable.
for el := plist.Front(); el != nil; el = el.Next() { for el := plist.Front(); el != nil; el = el.Next() {
p := el.Value.(*replyMatcher) p := el.Value.(*replyMatcher)
if p.from == r.from && p.ptype == r.data.Kind() && p.ip.Equal(r.ip) { if p.from == r.from && p.ptype == r.data.Kind() && p.ip == r.ip {
ok, requestDone := p.callback(r.data) ok, requestDone := p.callback(r.data)
matched = matched || ok matched = matched || ok
p.reply = r.data p.reply = r.data
@ -507,7 +511,7 @@ func (t *UDPv4) loop() {
} }
} }
func (t *UDPv4) send(toaddr *net.UDPAddr, toid enode.ID, req v4wire.Packet) ([]byte, error) { func (t *UDPv4) send(toaddr netip.AddrPort, toid enode.ID, req v4wire.Packet) ([]byte, error) {
packet, hash, err := v4wire.Encode(t.priv, req) packet, hash, err := v4wire.Encode(t.priv, req)
if err != nil { if err != nil {
return hash, err return hash, err
@ -515,8 +519,8 @@ func (t *UDPv4) send(toaddr *net.UDPAddr, toid enode.ID, req v4wire.Packet) ([]b
return hash, t.write(toaddr, toid, req.Name(), packet) return hash, t.write(toaddr, toid, req.Name(), packet)
} }
func (t *UDPv4) write(toaddr *net.UDPAddr, toid enode.ID, what string, packet []byte) error { func (t *UDPv4) write(toaddr netip.AddrPort, toid enode.ID, what string, packet []byte) error {
_, err := t.conn.WriteToUDP(packet, toaddr) _, err := t.conn.WriteToUDPAddrPort(packet, toaddr)
t.log.Trace(">> "+what, "id", toid, "addr", toaddr, "err", err) t.log.Trace(">> "+what, "id", toid, "addr", toaddr, "err", err)
return err return err
} }
@ -530,7 +534,7 @@ func (t *UDPv4) readLoop(unhandled chan<- ReadPacket) {
buf := make([]byte, maxPacketSize) buf := make([]byte, maxPacketSize)
for { for {
nbytes, from, err := t.conn.ReadFromUDP(buf) nbytes, from, err := t.conn.ReadFromUDPAddrPort(buf)
if netutil.IsTemporaryError(err) { if netutil.IsTemporaryError(err) {
// Ignore temporary read errors. // Ignore temporary read errors.
t.log.Debug("Temporary UDP read error", "err", err) t.log.Debug("Temporary UDP read error", "err", err)
@ -551,7 +555,7 @@ func (t *UDPv4) readLoop(unhandled chan<- ReadPacket) {
} }
} }
func (t *UDPv4) handlePacket(from *net.UDPAddr, buf []byte) error { func (t *UDPv4) handlePacket(from netip.AddrPort, buf []byte) error {
rawpacket, fromKey, hash, err := v4wire.Decode(buf) rawpacket, fromKey, hash, err := v4wire.Decode(buf)
if err != nil { if err != nil {
t.log.Debug("Bad discv4 packet", "addr", from, "err", err) t.log.Debug("Bad discv4 packet", "addr", from, "err", err)
@ -570,15 +574,16 @@ func (t *UDPv4) handlePacket(from *net.UDPAddr, buf []byte) error {
} }
// checkBond checks if the given node has a recent enough endpoint proof. // checkBond checks if the given node has a recent enough endpoint proof.
func (t *UDPv4) checkBond(id enode.ID, ip net.IP) bool { func (t *UDPv4) checkBond(id enode.ID, ip netip.AddrPort) bool {
return time.Since(t.db.LastPongReceived(id, ip)) < bondExpiration return time.Since(t.db.LastPongReceived(id, ip.Addr().AsSlice())) < bondExpiration
} }
// ensureBond solicits a ping from a node if we haven't seen a ping from it for a while. // ensureBond solicits a ping from a node if we haven't seen a ping from it for a while.
// This ensures there is a valid endpoint proof on the remote end. // This ensures there is a valid endpoint proof on the remote end.
func (t *UDPv4) ensureBond(toid enode.ID, toaddr *net.UDPAddr) { func (t *UDPv4) ensureBond(toid enode.ID, toaddr netip.AddrPort) {
tooOld := time.Since(t.db.LastPingReceived(toid, toaddr.IP)) > bondExpiration ip := toaddr.Addr().AsSlice()
if tooOld || t.db.FindFails(toid, toaddr.IP) > maxFindnodeFailures { tooOld := time.Since(t.db.LastPingReceived(toid, ip)) > bondExpiration
if tooOld || t.db.FindFails(toid, ip) > maxFindnodeFailures {
rm := t.sendPing(toid, toaddr, nil) rm := t.sendPing(toid, toaddr, nil)
<-rm.errc <-rm.errc
// Wait for them to ping back and process our pong. // Wait for them to ping back and process our pong.
@ -586,11 +591,11 @@ func (t *UDPv4) ensureBond(toid enode.ID, toaddr *net.UDPAddr) {
} }
} }
func (t *UDPv4) nodeFromRPC(sender *net.UDPAddr, rn v4wire.Node) (*enode.Node, error) { func (t *UDPv4) nodeFromRPC(sender netip.AddrPort, rn v4wire.Node) (*enode.Node, error) {
if rn.UDP <= 1024 { if rn.UDP <= 1024 {
return nil, errLowPort return nil, errLowPort
} }
if err := netutil.CheckRelayIP(sender.IP, rn.IP); err != nil { if err := netutil.CheckRelayIP(sender.Addr().AsSlice(), rn.IP); err != nil {
return nil, err return nil, err
} }
if t.netrestrict != nil && !t.netrestrict.Contains(rn.IP) { if t.netrestrict != nil && !t.netrestrict.Contains(rn.IP) {
@ -644,14 +649,14 @@ type packetHandlerV4 struct {
senderKey *ecdsa.PublicKey // used for ping senderKey *ecdsa.PublicKey // used for ping
// preverify checks whether the packet is valid and should be handled at all. // preverify checks whether the packet is valid and should be handled at all.
preverify func(p *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, fromKey v4wire.Pubkey) error preverify func(p *packetHandlerV4, from netip.AddrPort, fromID enode.ID, fromKey v4wire.Pubkey) error
// handle handles the packet. // handle handles the packet.
handle func(req *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, mac []byte) handle func(req *packetHandlerV4, from netip.AddrPort, fromID enode.ID, mac []byte)
} }
// PING/v4 // PING/v4
func (t *UDPv4) verifyPing(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, fromKey v4wire.Pubkey) error { func (t *UDPv4) verifyPing(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, fromKey v4wire.Pubkey) error {
req := h.Packet.(*v4wire.Ping) req := h.Packet.(*v4wire.Ping)
if v4wire.Expired(req.Expiration) { if v4wire.Expired(req.Expiration) {
@ -665,7 +670,7 @@ func (t *UDPv4) verifyPing(h *packetHandlerV4, from *net.UDPAddr, fromID enode.I
return nil return nil
} }
func (t *UDPv4) handlePing(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, mac []byte) { func (t *UDPv4) handlePing(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, mac []byte) {
req := h.Packet.(*v4wire.Ping) req := h.Packet.(*v4wire.Ping)
// Reply. // Reply.
@ -677,8 +682,9 @@ func (t *UDPv4) handlePing(h *packetHandlerV4, from *net.UDPAddr, fromID enode.I
}) })
// Ping back if our last pong on file is too far in the past. // Ping back if our last pong on file is too far in the past.
n := enode.NewV4(h.senderKey, from.IP, int(req.From.TCP), from.Port) ip := from.Addr().AsSlice()
if time.Since(t.db.LastPongReceived(n.ID(), from.IP)) > bondExpiration { n := enode.NewV4(h.senderKey, ip, int(req.From.TCP), int(from.Port()))
if time.Since(t.db.LastPongReceived(n.ID(), ip)) > bondExpiration {
t.sendPing(fromID, from, func() { t.sendPing(fromID, from, func() {
t.tab.addInboundNode(n) t.tab.addInboundNode(n)
}) })
@ -687,35 +693,40 @@ func (t *UDPv4) handlePing(h *packetHandlerV4, from *net.UDPAddr, fromID enode.I
} }
// Update node database and endpoint predictor. // Update node database and endpoint predictor.
t.db.UpdateLastPingReceived(n.ID(), from.IP, time.Now()) t.db.UpdateLastPingReceived(n.ID(), ip, time.Now())
t.localNode.UDPEndpointStatement(from, &net.UDPAddr{IP: req.To.IP, Port: int(req.To.UDP)}) fromUDPAddr := &net.UDPAddr{IP: ip, Port: int(from.Port())}
toUDPAddr := &net.UDPAddr{IP: req.To.IP, Port: int(req.To.UDP)}
t.localNode.UDPEndpointStatement(fromUDPAddr, toUDPAddr)
} }
// PONG/v4 // PONG/v4
func (t *UDPv4) verifyPong(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, fromKey v4wire.Pubkey) error { func (t *UDPv4) verifyPong(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, fromKey v4wire.Pubkey) error {
req := h.Packet.(*v4wire.Pong) req := h.Packet.(*v4wire.Pong)
if v4wire.Expired(req.Expiration) { if v4wire.Expired(req.Expiration) {
return errExpired return errExpired
} }
if !t.handleReply(fromID, from.IP, req) { if !t.handleReply(fromID, from.Addr(), req) {
return errUnsolicitedReply return errUnsolicitedReply
} }
t.localNode.UDPEndpointStatement(from, &net.UDPAddr{IP: req.To.IP, Port: int(req.To.UDP)}) fromIP := from.Addr().AsSlice()
t.db.UpdateLastPongReceived(fromID, from.IP, time.Now()) fromUDPAddr := &net.UDPAddr{IP: fromIP, Port: int(from.Port())}
toUDPAddr := &net.UDPAddr{IP: req.To.IP, Port: int(req.To.UDP)}
t.localNode.UDPEndpointStatement(fromUDPAddr, toUDPAddr)
t.db.UpdateLastPongReceived(fromID, fromIP, time.Now())
return nil return nil
} }
// FINDNODE/v4 // FINDNODE/v4
func (t *UDPv4) verifyFindnode(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, fromKey v4wire.Pubkey) error { func (t *UDPv4) verifyFindnode(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, fromKey v4wire.Pubkey) error {
req := h.Packet.(*v4wire.Findnode) req := h.Packet.(*v4wire.Findnode)
if v4wire.Expired(req.Expiration) { if v4wire.Expired(req.Expiration) {
return errExpired return errExpired
} }
if !t.checkBond(fromID, from.IP) { if !t.checkBond(fromID, from) {
// No endpoint proof pong exists, we don't process the packet. This prevents an // No endpoint proof pong exists, we don't process the packet. This prevents an
// attack vector where the discovery protocol could be used to amplify traffic in a // attack vector where the discovery protocol could be used to amplify traffic in a
// DDOS attack. A malicious actor would send a findnode request with the IP address // DDOS attack. A malicious actor would send a findnode request with the IP address
@ -727,7 +738,7 @@ func (t *UDPv4) verifyFindnode(h *packetHandlerV4, from *net.UDPAddr, fromID eno
return nil return nil
} }
func (t *UDPv4) handleFindnode(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, mac []byte) { func (t *UDPv4) handleFindnode(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, mac []byte) {
req := h.Packet.(*v4wire.Findnode) req := h.Packet.(*v4wire.Findnode)
// Determine closest nodes. // Determine closest nodes.
@ -739,7 +750,8 @@ func (t *UDPv4) handleFindnode(h *packetHandlerV4, from *net.UDPAddr, fromID eno
p := v4wire.Neighbors{Expiration: uint64(time.Now().Add(expiration).Unix())} p := v4wire.Neighbors{Expiration: uint64(time.Now().Add(expiration).Unix())}
var sent bool var sent bool
for _, n := range closest { for _, n := range closest {
if netutil.CheckRelayIP(from.IP, n.IP()) == nil { fromIP := from.Addr().AsSlice()
if netutil.CheckRelayIP(fromIP, n.IP()) == nil {
p.Nodes = append(p.Nodes, nodeToRPC(n)) p.Nodes = append(p.Nodes, nodeToRPC(n))
} }
if len(p.Nodes) == v4wire.MaxNeighbors { if len(p.Nodes) == v4wire.MaxNeighbors {
@ -755,13 +767,13 @@ func (t *UDPv4) handleFindnode(h *packetHandlerV4, from *net.UDPAddr, fromID eno
// NEIGHBORS/v4 // NEIGHBORS/v4
func (t *UDPv4) verifyNeighbors(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, fromKey v4wire.Pubkey) error { func (t *UDPv4) verifyNeighbors(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, fromKey v4wire.Pubkey) error {
req := h.Packet.(*v4wire.Neighbors) req := h.Packet.(*v4wire.Neighbors)
if v4wire.Expired(req.Expiration) { if v4wire.Expired(req.Expiration) {
return errExpired return errExpired
} }
if !t.handleReply(fromID, from.IP, h.Packet) { if !t.handleReply(fromID, from.Addr(), h.Packet) {
return errUnsolicitedReply return errUnsolicitedReply
} }
return nil return nil
@ -769,19 +781,19 @@ func (t *UDPv4) verifyNeighbors(h *packetHandlerV4, from *net.UDPAddr, fromID en
// ENRREQUEST/v4 // ENRREQUEST/v4
func (t *UDPv4) verifyENRRequest(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, fromKey v4wire.Pubkey) error { func (t *UDPv4) verifyENRRequest(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, fromKey v4wire.Pubkey) error {
req := h.Packet.(*v4wire.ENRRequest) req := h.Packet.(*v4wire.ENRRequest)
if v4wire.Expired(req.Expiration) { if v4wire.Expired(req.Expiration) {
return errExpired return errExpired
} }
if !t.checkBond(fromID, from.IP) { if !t.checkBond(fromID, from) {
return errUnknownNode return errUnknownNode
} }
return nil return nil
} }
func (t *UDPv4) handleENRRequest(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, mac []byte) { func (t *UDPv4) handleENRRequest(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, mac []byte) {
t.send(from, fromID, &v4wire.ENRResponse{ t.send(from, fromID, &v4wire.ENRResponse{
ReplyTok: mac, ReplyTok: mac,
Record: *t.localNode.Node().Record(), Record: *t.localNode.Node().Record(),
@ -790,8 +802,8 @@ func (t *UDPv4) handleENRRequest(h *packetHandlerV4, from *net.UDPAddr, fromID e
// ENRRESPONSE/v4 // ENRRESPONSE/v4
func (t *UDPv4) verifyENRResponse(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, fromKey v4wire.Pubkey) error { func (t *UDPv4) verifyENRResponse(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, fromKey v4wire.Pubkey) error {
if !t.handleReply(fromID, from.IP, h.Packet) { if !t.handleReply(fromID, from.Addr(), h.Packet) {
return errUnsolicitedReply return errUnsolicitedReply
} }
return nil return nil

View file

@ -65,7 +65,7 @@ func newUDPTest(t *testing.T) *udpTest {
pipe: newpipe(), pipe: newpipe(),
localkey: newkey(), localkey: newkey(),
remotekey: newkey(), remotekey: newkey(),
remoteaddr: netip.AddrPortFrom(netip.MustParseAddr("10.0.1.99"), 30303), remoteaddr: netip.MustParseAddrPort("10.0.1.99:30303"),
} }
test.db, _ = enode.OpenDB("") test.db, _ = enode.OpenDB("")
@ -101,14 +101,13 @@ func (test *udpTest) packetInFrom(wantError error, key *ecdsa.PrivateKey, addr n
test.t.Errorf("%s encode error: %v", data.Name(), err) test.t.Errorf("%s encode error: %v", data.Name(), err)
} }
test.sent = append(test.sent, enc) test.sent = append(test.sent, enc)
udpaddr := &net.UDPAddr{IP: addr.Addr().AsSlice(), Port: int(addr.Port())} if err = test.udp.handlePacket(addr, enc); err != wantError {
if err = test.udp.handlePacket(udpaddr, enc); err != wantError {
test.t.Errorf("error mismatch: got %q, want %q", err, wantError) test.t.Errorf("error mismatch: got %q, want %q", err, wantError)
} }
} }
// waits for a packet to be sent by the transport. // waits for a packet to be sent by the transport.
// validate should have type func(X, *net.UDPAddr, []byte), where X is a packet type. // validate should have type func(X, netip.AddrPort, []byte), where X is a packet type.
func (test *udpTest) waitPacketOut(validate interface{}) (closed bool) { func (test *udpTest) waitPacketOut(validate interface{}) (closed bool) {
test.t.Helper() test.t.Helper()
@ -130,7 +129,7 @@ func (test *udpTest) waitPacketOut(validate interface{}) (closed bool) {
test.t.Errorf("sent packet type mismatch, got: %v, want: %v", reflect.TypeOf(p), exptype) test.t.Errorf("sent packet type mismatch, got: %v, want: %v", reflect.TypeOf(p), exptype)
return false return false
} }
fn.Call([]reflect.Value{reflect.ValueOf(p), reflect.ValueOf(&dgram.to), reflect.ValueOf(hash)}) fn.Call([]reflect.Value{reflect.ValueOf(p), reflect.ValueOf(dgram.to), reflect.ValueOf(hash)})
return false return false
} }
@ -281,7 +280,7 @@ func TestUDPv4_findnode(t *testing.T) {
expected := test.table.findnodeByID(testTarget.ID(), bucketSize, true) expected := test.table.findnodeByID(testTarget.ID(), bucketSize, true)
test.packetIn(nil, &v4wire.Findnode{Target: testTarget, Expiration: futureExp}) test.packetIn(nil, &v4wire.Findnode{Target: testTarget, Expiration: futureExp})
waitNeighbors := func(want []*enode.Node) { waitNeighbors := func(want []*enode.Node) {
test.waitPacketOut(func(p *v4wire.Neighbors, to *net.UDPAddr, hash []byte) { test.waitPacketOut(func(p *v4wire.Neighbors, to netip.AddrPort, hash []byte) {
if len(p.Nodes) != len(want) { if len(p.Nodes) != len(want) {
t.Errorf("wrong number of results: got %d, want %d", len(p.Nodes), len(want)) t.Errorf("wrong number of results: got %d, want %d", len(p.Nodes), len(want))
return return
@ -326,7 +325,7 @@ func TestUDPv4_findnodeMultiReply(t *testing.T) {
// wait for the findnode to be sent. // wait for the findnode to be sent.
// after it is sent, the transport is waiting for a reply // after it is sent, the transport is waiting for a reply
test.waitPacketOut(func(p *v4wire.Findnode, to *net.UDPAddr, hash []byte) { test.waitPacketOut(func(p *v4wire.Findnode, to netip.AddrPort, hash []byte) {
if p.Target != testTarget { if p.Target != testTarget {
t.Errorf("wrong target: got %v, want %v", p.Target, testTarget) t.Errorf("wrong target: got %v, want %v", p.Target, testTarget)
} }
@ -369,8 +368,8 @@ func TestUDPv4_pingMatch(t *testing.T) {
crand.Read(randToken) crand.Read(randToken)
test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp}) test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp})
test.waitPacketOut(func(*v4wire.Pong, *net.UDPAddr, []byte) {}) test.waitPacketOut(func(*v4wire.Pong, netip.AddrPort, []byte) {})
test.waitPacketOut(func(*v4wire.Ping, *net.UDPAddr, []byte) {}) test.waitPacketOut(func(*v4wire.Ping, netip.AddrPort, []byte) {})
test.packetIn(errUnsolicitedReply, &v4wire.Pong{ReplyTok: randToken, To: testLocalAnnounced, Expiration: futureExp}) test.packetIn(errUnsolicitedReply, &v4wire.Pong{ReplyTok: randToken, To: testLocalAnnounced, Expiration: futureExp})
} }
@ -380,7 +379,7 @@ func TestUDPv4_pingMatchIP(t *testing.T) {
defer test.close() defer test.close()
test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp}) test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp})
test.waitPacketOut(func(*v4wire.Pong, *net.UDPAddr, []byte) {}) test.waitPacketOut(func(*v4wire.Pong, netip.AddrPort, []byte) {})
test.waitPacketOut(func(p *v4wire.Ping, to netip.AddrPort, hash []byte) { test.waitPacketOut(func(p *v4wire.Ping, to netip.AddrPort, hash []byte) {
wrongAddr := netip.MustParseAddrPort("33.44.1.2:30000") wrongAddr := netip.MustParseAddrPort("33.44.1.2:30000")
@ -402,34 +401,26 @@ func TestUDPv4_successfulPing(t *testing.T) {
go test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp}) go test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp})
// The ping is replied to. // The ping is replied to.
test.waitPacketOut(func(p *v4wire.Pong, to *net.UDPAddr, hash []byte) { test.waitPacketOut(func(p *v4wire.Pong, to netip.AddrPort, hash []byte) {
pinghash := test.sent[0][:32] pinghash := test.sent[0][:32]
if !bytes.Equal(p.ReplyTok, pinghash) { if !bytes.Equal(p.ReplyTok, pinghash) {
t.Errorf("got pong.ReplyTok %x, want %x", p.ReplyTok, pinghash) t.Errorf("got pong.ReplyTok %x, want %x", p.ReplyTok, pinghash)
} }
wantTo := v4wire.Endpoint{ // The mirrored UDP address is the UDP packet sender.
// The mirrored UDP address is the UDP packet sender // The mirrored TCP port is the one from the ping packet.
IP: test.remoteaddr.Addr().AsSlice(), wantTo := v4wire.NewEndpoint(test.remoteaddr, testRemote.TCP)
UDP: test.remoteaddr.Port(),
// The mirrored TCP port is the one from the ping packet
TCP: testRemote.TCP,
}
if !reflect.DeepEqual(p.To, wantTo) { if !reflect.DeepEqual(p.To, wantTo) {
t.Errorf("got pong.To %v, want %v", p.To, wantTo) t.Errorf("got pong.To %v, want %v", p.To, wantTo)
} }
}) })
// Remote is unknown, the table pings back. // Remote is unknown, the table pings back.
test.waitPacketOut(func(p *v4wire.Ping, to *net.UDPAddr, hash []byte) { test.waitPacketOut(func(p *v4wire.Ping, to netip.AddrPort, hash []byte) {
if !reflect.DeepEqual(p.From, test.udp.ourEndpoint()) { if !reflect.DeepEqual(p.From, test.udp.ourEndpoint()) {
t.Errorf("got ping.From %#v, want %#v", p.From, test.udp.ourEndpoint()) t.Errorf("got ping.From %#v, want %#v", p.From, test.udp.ourEndpoint())
} }
wantTo := v4wire.Endpoint{
// The mirrored UDP address is the UDP packet sender. // The mirrored UDP address is the UDP packet sender.
IP: test.remoteaddr.Addr().AsSlice(), wantTo := v4wire.NewEndpoint(test.remoteaddr, 0)
UDP: test.remoteaddr.Port(),
TCP: 0,
}
if !reflect.DeepEqual(p.To, wantTo) { if !reflect.DeepEqual(p.To, wantTo) {
t.Errorf("got ping.To %v, want %v", p.To, wantTo) t.Errorf("got ping.To %v, want %v", p.To, wantTo)
} }
@ -448,7 +439,7 @@ func TestUDPv4_successfulPing(t *testing.T) {
t.Errorf("node has wrong IP: got %v, want: %v", n.IP(), test.remoteaddr.Addr()) t.Errorf("node has wrong IP: got %v, want: %v", n.IP(), test.remoteaddr.Addr())
} }
if n.UDP() != int(test.remoteaddr.Port()) { if n.UDP() != int(test.remoteaddr.Port()) {
t.Errorf("node has wrong UDP port: got %v, want: %v", n.UDP(), test.remoteaddr.Port) t.Errorf("node has wrong UDP port: got %v, want: %v", n.UDP(), test.remoteaddr.Port())
} }
if n.TCP() != int(testRemote.TCP) { if n.TCP() != int(testRemote.TCP) {
t.Errorf("node has wrong TCP port: got %v, want: %v", n.TCP(), testRemote.TCP) t.Errorf("node has wrong TCP port: got %v, want: %v", n.TCP(), testRemote.TCP)
@ -471,12 +462,12 @@ func TestUDPv4_EIP868(t *testing.T) {
// Perform endpoint proof and check for sequence number in packet tail. // Perform endpoint proof and check for sequence number in packet tail.
test.packetIn(nil, &v4wire.Ping{Expiration: futureExp}) test.packetIn(nil, &v4wire.Ping{Expiration: futureExp})
test.waitPacketOut(func(p *v4wire.Pong, addr *net.UDPAddr, hash []byte) { test.waitPacketOut(func(p *v4wire.Pong, addr netip.AddrPort, hash []byte) {
if p.ENRSeq != wantNode.Seq() { if p.ENRSeq != wantNode.Seq() {
t.Errorf("wrong sequence number in pong: %d, want %d", p.ENRSeq, wantNode.Seq()) t.Errorf("wrong sequence number in pong: %d, want %d", p.ENRSeq, wantNode.Seq())
} }
}) })
test.waitPacketOut(func(p *v4wire.Ping, addr *net.UDPAddr, hash []byte) { test.waitPacketOut(func(p *v4wire.Ping, addr netip.AddrPort, hash []byte) {
if p.ENRSeq != wantNode.Seq() { if p.ENRSeq != wantNode.Seq() {
t.Errorf("wrong sequence number in ping: %d, want %d", p.ENRSeq, wantNode.Seq()) t.Errorf("wrong sequence number in ping: %d, want %d", p.ENRSeq, wantNode.Seq())
} }
@ -485,7 +476,7 @@ func TestUDPv4_EIP868(t *testing.T) {
// Request should work now. // Request should work now.
test.packetIn(nil, &v4wire.ENRRequest{Expiration: futureExp}) test.packetIn(nil, &v4wire.ENRRequest{Expiration: futureExp})
test.waitPacketOut(func(p *v4wire.ENRResponse, addr *net.UDPAddr, hash []byte) { test.waitPacketOut(func(p *v4wire.ENRResponse, addr netip.AddrPort, hash []byte) {
n, err := enode.New(enode.ValidSchemes, &p.Record) n, err := enode.New(enode.ValidSchemes, &p.Record)
if err != nil { if err != nil {
t.Fatalf("invalid record: %v", err) t.Fatalf("invalid record: %v", err)
@ -600,7 +591,7 @@ func newpipe() *dgramPipe {
} }
// WriteToUDP queues a datagram. // WriteToUDP queues a datagram.
func (c *dgramPipe) WriteToUDP(b []byte, to *net.UDPAddr) (n int, err error) { func (c *dgramPipe) WriteToUDPAddrPort(b []byte, to netip.AddrPort) (n int, err error) {
msg := make([]byte, len(b)) msg := make([]byte, len(b))
copy(msg, b) copy(msg, b)
c.mu.Lock() c.mu.Lock()
@ -608,20 +599,15 @@ func (c *dgramPipe) WriteToUDP(b []byte, to *net.UDPAddr) (n int, err error) {
if c.closed { if c.closed {
return 0, errors.New("closed") return 0, errors.New("closed")
} }
addr, ok := netip.AddrFromSlice(to.IP) c.queue = append(c.queue, dgram{to, b})
if !ok {
panic(fmt.Errorf("invalid destination IP addr %v", to.IP))
}
addrPort := netip.AddrPortFrom(addr, uint16(to.Port))
c.queue = append(c.queue, dgram{addrPort, b})
c.cond.Signal() c.cond.Signal()
return len(b), nil return len(b), nil
} }
// ReadFromUDP just hangs until the pipe is closed. // ReadFromUDP just hangs until the pipe is closed.
func (c *dgramPipe) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) { func (c *dgramPipe) ReadFromUDPAddrPort(b []byte) (n int, addr netip.AddrPort, err error) {
<-c.closing <-c.closing
return 0, nil, io.EOF return 0, netip.AddrPort{}, io.EOF
} }
func (c *dgramPipe) Close() error { func (c *dgramPipe) Close() error {

View file

@ -25,6 +25,7 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"net" "net"
"net/netip"
"time" "time"
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
@ -150,14 +151,15 @@ type Endpoint struct {
} }
// NewEndpoint creates an endpoint. // NewEndpoint creates an endpoint.
func NewEndpoint(addr *net.UDPAddr, tcpPort uint16) Endpoint { func NewEndpoint(addr netip.AddrPort, tcpPort uint16) Endpoint {
ip := net.IP{} var ip net.IP
if ip4 := addr.IP.To4(); ip4 != nil { if addr.Addr().Is4() || addr.Addr().Is4In6() {
ip = ip4 ip4 := addr.Addr().As4()
} else if ip6 := addr.IP.To16(); ip6 != nil { ip = ip4[:]
ip = ip6 } else {
ip = addr.Addr().AsSlice()
} }
return Endpoint{IP: ip, UDP: uint16(addr.Port), TCP: tcpPort} return Endpoint{IP: ip, UDP: addr.Port(), TCP: tcpPort}
} }
type Packet interface { type Packet interface {

View file

@ -18,6 +18,7 @@ package discover
import ( import (
"net" "net"
"net/netip"
"sync" "sync"
"time" "time"
@ -70,7 +71,7 @@ func (t *talkSystem) register(protocol string, handler TalkRequestHandler) {
} }
// handleRequest handles a talk request. // handleRequest handles a talk request.
func (t *talkSystem) handleRequest(id enode.ID, addr *net.UDPAddr, req *v5wire.TalkRequest) { func (t *talkSystem) handleRequest(id enode.ID, addr netip.AddrPort, req *v5wire.TalkRequest) {
t.mutex.Lock() t.mutex.Lock()
handler, ok := t.handlers[req.Protocol] handler, ok := t.handlers[req.Protocol]
t.mutex.Unlock() t.mutex.Unlock()
@ -88,7 +89,8 @@ func (t *talkSystem) handleRequest(id enode.ID, addr *net.UDPAddr, req *v5wire.T
case <-t.slots: case <-t.slots:
go func() { go func() {
defer func() { t.slots <- struct{}{} }() defer func() { t.slots <- struct{}{} }()
respMessage := handler(id, addr, req.Message) udpAddr := &net.UDPAddr{IP: addr.Addr().AsSlice(), Port: int(addr.Port())}
respMessage := handler(id, udpAddr, req.Message)
resp := &v5wire.TalkResponse{ReqID: req.ReqID, Message: respMessage} resp := &v5wire.TalkResponse{ReqID: req.ReqID, Message: respMessage}
t.transport.sendFromAnotherThread(id, addr, resp) t.transport.sendFromAnotherThread(id, addr, resp)
}() }()

View file

@ -25,6 +25,7 @@ import (
"fmt" "fmt"
"io" "io"
"net" "net"
"net/netip"
"slices" "slices"
"sync" "sync"
"time" "time"
@ -101,14 +102,14 @@ type UDPv5 struct {
type sendRequest struct { type sendRequest struct {
destID enode.ID destID enode.ID
destAddr *net.UDPAddr destAddr netip.AddrPort
msg v5wire.Packet msg v5wire.Packet
} }
// callV5 represents a remote procedure call against another node. // callV5 represents a remote procedure call against another node.
type callV5 struct { type callV5 struct {
id enode.ID id enode.ID
addr *net.UDPAddr addr netip.AddrPort
node *enode.Node // This is required to perform handshakes. node *enode.Node // This is required to perform handshakes.
packet v5wire.Packet packet v5wire.Packet
@ -266,7 +267,7 @@ func (t *UDPv5) TalkRequest(n *enode.Node, protocol string, request []byte) ([]b
} }
// TalkRequestToID sends a talk request to a node and waits for a response. // TalkRequestToID sends a talk request to a node and waits for a response.
func (t *UDPv5) TalkRequestToID(id enode.ID, addr *net.UDPAddr, protocol string, request []byte) ([]byte, error) { func (t *UDPv5) TalkRequestToID(id enode.ID, addr netip.AddrPort, protocol string, request []byte) ([]byte, error) {
req := &v5wire.TalkRequest{Protocol: protocol, Message: request} req := &v5wire.TalkRequest{Protocol: protocol, Message: request}
resp := t.callToID(id, addr, v5wire.TalkResponseMsg, req) resp := t.callToID(id, addr, v5wire.TalkResponseMsg, req)
defer t.callDone(resp) defer t.callDone(resp)
@ -427,7 +428,7 @@ func (t *UDPv5) verifyResponseNode(c *callV5, r *enr.Record, distances []uint, s
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := netutil.CheckRelayIP(c.addr.IP, node.IP()); err != nil { if err := netutil.CheckRelayIP(c.addr.Addr().AsSlice(), node.IP()); err != nil {
return nil, err return nil, err
} }
if t.netrestrict != nil && !t.netrestrict.Contains(node.IP()) { if t.netrestrict != nil && !t.netrestrict.Contains(node.IP()) {
@ -452,14 +453,14 @@ func (t *UDPv5) verifyResponseNode(c *callV5, r *enr.Record, distances []uint, s
// callToNode sends the given call and sets up a handler for response packets (of message // callToNode sends the given call and sets up a handler for response packets (of message
// type responseType). Responses are dispatched to the call's response channel. // type responseType). Responses are dispatched to the call's response channel.
func (t *UDPv5) callToNode(n *enode.Node, responseType byte, req v5wire.Packet) *callV5 { func (t *UDPv5) callToNode(n *enode.Node, responseType byte, req v5wire.Packet) *callV5 {
addr := &net.UDPAddr{IP: n.IP(), Port: n.UDP()} addr, _ := n.UDPEndpoint()
c := &callV5{id: n.ID(), addr: addr, node: n} c := &callV5{id: n.ID(), addr: addr, node: n}
t.initCall(c, responseType, req) t.initCall(c, responseType, req)
return c return c
} }
// callToID is like callToNode, but for cases where the node record is not available. // callToID is like callToNode, but for cases where the node record is not available.
func (t *UDPv5) callToID(id enode.ID, addr *net.UDPAddr, responseType byte, req v5wire.Packet) *callV5 { func (t *UDPv5) callToID(id enode.ID, addr netip.AddrPort, responseType byte, req v5wire.Packet) *callV5 {
c := &callV5{id: id, addr: addr} c := &callV5{id: id, addr: addr}
t.initCall(c, responseType, req) t.initCall(c, responseType, req)
return c return c
@ -619,12 +620,12 @@ func (t *UDPv5) sendCall(c *callV5) {
// sendResponse sends a response packet to the given node. // sendResponse sends a response packet to the given node.
// This doesn't trigger a handshake even if no keys are available. // This doesn't trigger a handshake even if no keys are available.
func (t *UDPv5) sendResponse(toID enode.ID, toAddr *net.UDPAddr, packet v5wire.Packet) error { func (t *UDPv5) sendResponse(toID enode.ID, toAddr netip.AddrPort, packet v5wire.Packet) error {
_, err := t.send(toID, toAddr, packet, nil) _, err := t.send(toID, toAddr, packet, nil)
return err return err
} }
func (t *UDPv5) sendFromAnotherThread(toID enode.ID, toAddr *net.UDPAddr, packet v5wire.Packet) { func (t *UDPv5) sendFromAnotherThread(toID enode.ID, toAddr netip.AddrPort, packet v5wire.Packet) {
select { select {
case t.sendCh <- sendRequest{toID, toAddr, packet}: case t.sendCh <- sendRequest{toID, toAddr, packet}:
case <-t.closeCtx.Done(): case <-t.closeCtx.Done():
@ -632,7 +633,7 @@ func (t *UDPv5) sendFromAnotherThread(toID enode.ID, toAddr *net.UDPAddr, packet
} }
// send sends a packet to the given node. // send sends a packet to the given node.
func (t *UDPv5) send(toID enode.ID, toAddr *net.UDPAddr, packet v5wire.Packet, c *v5wire.Whoareyou) (v5wire.Nonce, error) { func (t *UDPv5) send(toID enode.ID, toAddr netip.AddrPort, packet v5wire.Packet, c *v5wire.Whoareyou) (v5wire.Nonce, error) {
addr := toAddr.String() addr := toAddr.String()
t.logcontext = append(t.logcontext[:0], "id", toID, "addr", addr) t.logcontext = append(t.logcontext[:0], "id", toID, "addr", addr)
t.logcontext = packet.AppendLogInfo(t.logcontext) t.logcontext = packet.AppendLogInfo(t.logcontext)
@ -644,7 +645,7 @@ func (t *UDPv5) send(toID enode.ID, toAddr *net.UDPAddr, packet v5wire.Packet, c
return nonce, err return nonce, err
} }
_, err = t.conn.WriteToUDP(enc, toAddr) _, err = t.conn.WriteToUDPAddrPort(enc, toAddr)
t.log.Trace(">> "+packet.Name(), t.logcontext...) t.log.Trace(">> "+packet.Name(), t.logcontext...)
return nonce, err return nonce, err
} }
@ -655,7 +656,7 @@ func (t *UDPv5) readLoop() {
buf := make([]byte, maxPacketSize) buf := make([]byte, maxPacketSize)
for range t.readNextCh { for range t.readNextCh {
nbytes, from, err := t.conn.ReadFromUDP(buf) nbytes, from, err := t.conn.ReadFromUDPAddrPort(buf)
if netutil.IsTemporaryError(err) { if netutil.IsTemporaryError(err) {
// Ignore temporary read errors. // Ignore temporary read errors.
t.log.Debug("Temporary UDP read error", "err", err) t.log.Debug("Temporary UDP read error", "err", err)
@ -672,7 +673,7 @@ func (t *UDPv5) readLoop() {
} }
// dispatchReadPacket sends a packet into the dispatch loop. // dispatchReadPacket sends a packet into the dispatch loop.
func (t *UDPv5) dispatchReadPacket(from *net.UDPAddr, content []byte) bool { func (t *UDPv5) dispatchReadPacket(from netip.AddrPort, content []byte) bool {
select { select {
case t.packetInCh <- ReadPacket{content, from}: case t.packetInCh <- ReadPacket{content, from}:
return true return true
@ -682,7 +683,7 @@ func (t *UDPv5) dispatchReadPacket(from *net.UDPAddr, content []byte) bool {
} }
// handlePacket decodes and processes an incoming packet from the network. // handlePacket decodes and processes an incoming packet from the network.
func (t *UDPv5) handlePacket(rawpacket []byte, fromAddr *net.UDPAddr) error { func (t *UDPv5) handlePacket(rawpacket []byte, fromAddr netip.AddrPort) error {
addr := fromAddr.String() addr := fromAddr.String()
fromID, fromNode, packet, err := t.codec.Decode(rawpacket, addr) fromID, fromNode, packet, err := t.codec.Decode(rawpacket, addr)
if err != nil { if err != nil {
@ -712,13 +713,13 @@ func (t *UDPv5) handlePacket(rawpacket []byte, fromAddr *net.UDPAddr) error {
} }
// handleCallResponse dispatches a response packet to the call waiting for it. // handleCallResponse dispatches a response packet to the call waiting for it.
func (t *UDPv5) handleCallResponse(fromID enode.ID, fromAddr *net.UDPAddr, p v5wire.Packet) bool { func (t *UDPv5) handleCallResponse(fromID enode.ID, fromAddr netip.AddrPort, p v5wire.Packet) bool {
ac := t.activeCallByNode[fromID] ac := t.activeCallByNode[fromID]
if ac == nil || !bytes.Equal(p.RequestID(), ac.reqid) { if ac == nil || !bytes.Equal(p.RequestID(), ac.reqid) {
t.log.Debug(fmt.Sprintf("Unsolicited/late %s response", p.Name()), "id", fromID, "addr", fromAddr) t.log.Debug(fmt.Sprintf("Unsolicited/late %s response", p.Name()), "id", fromID, "addr", fromAddr)
return false return false
} }
if !fromAddr.IP.Equal(ac.addr.IP) || fromAddr.Port != ac.addr.Port { if fromAddr != ac.addr {
t.log.Debug(fmt.Sprintf("%s from wrong endpoint", p.Name()), "id", fromID, "addr", fromAddr) t.log.Debug(fmt.Sprintf("%s from wrong endpoint", p.Name()), "id", fromID, "addr", fromAddr)
return false return false
} }
@ -743,7 +744,7 @@ func (t *UDPv5) getNode(id enode.ID) *enode.Node {
} }
// handle processes incoming packets according to their message type. // handle processes incoming packets according to their message type.
func (t *UDPv5) handle(p v5wire.Packet, fromID enode.ID, fromAddr *net.UDPAddr) { func (t *UDPv5) handle(p v5wire.Packet, fromID enode.ID, fromAddr netip.AddrPort) {
switch p := p.(type) { switch p := p.(type) {
case *v5wire.Unknown: case *v5wire.Unknown:
t.handleUnknown(p, fromID, fromAddr) t.handleUnknown(p, fromID, fromAddr)
@ -753,7 +754,9 @@ func (t *UDPv5) handle(p v5wire.Packet, fromID enode.ID, fromAddr *net.UDPAddr)
t.handlePing(p, fromID, fromAddr) t.handlePing(p, fromID, fromAddr)
case *v5wire.Pong: case *v5wire.Pong:
if t.handleCallResponse(fromID, fromAddr, p) { if t.handleCallResponse(fromID, fromAddr, p) {
t.localNode.UDPEndpointStatement(fromAddr, &net.UDPAddr{IP: p.ToIP, Port: int(p.ToPort)}) fromUDPAddr := &net.UDPAddr{IP: fromAddr.Addr().AsSlice(), Port: int(fromAddr.Port())}
toUDPAddr := &net.UDPAddr{IP: p.ToIP, Port: int(p.ToPort)}
t.localNode.UDPEndpointStatement(fromUDPAddr, toUDPAddr)
} }
case *v5wire.Findnode: case *v5wire.Findnode:
t.handleFindnode(p, fromID, fromAddr) t.handleFindnode(p, fromID, fromAddr)
@ -767,7 +770,7 @@ func (t *UDPv5) handle(p v5wire.Packet, fromID enode.ID, fromAddr *net.UDPAddr)
} }
// handleUnknown initiates a handshake by responding with WHOAREYOU. // handleUnknown initiates a handshake by responding with WHOAREYOU.
func (t *UDPv5) handleUnknown(p *v5wire.Unknown, fromID enode.ID, fromAddr *net.UDPAddr) { func (t *UDPv5) handleUnknown(p *v5wire.Unknown, fromID enode.ID, fromAddr netip.AddrPort) {
challenge := &v5wire.Whoareyou{Nonce: p.Nonce} challenge := &v5wire.Whoareyou{Nonce: p.Nonce}
crand.Read(challenge.IDNonce[:]) crand.Read(challenge.IDNonce[:])
if n := t.getNode(fromID); n != nil { if n := t.getNode(fromID); n != nil {
@ -783,7 +786,7 @@ var (
) )
// handleWhoareyou resends the active call as a handshake packet. // handleWhoareyou resends the active call as a handshake packet.
func (t *UDPv5) handleWhoareyou(p *v5wire.Whoareyou, fromID enode.ID, fromAddr *net.UDPAddr) { func (t *UDPv5) handleWhoareyou(p *v5wire.Whoareyou, fromID enode.ID, fromAddr netip.AddrPort) {
c, err := t.matchWithCall(fromID, p.Nonce) c, err := t.matchWithCall(fromID, p.Nonce)
if err != nil { if err != nil {
t.log.Debug("Invalid "+p.Name(), "addr", fromAddr, "err", err) t.log.Debug("Invalid "+p.Name(), "addr", fromAddr, "err", err)
@ -817,32 +820,35 @@ func (t *UDPv5) matchWithCall(fromID enode.ID, nonce v5wire.Nonce) (*callV5, err
} }
// handlePing sends a PONG response. // handlePing sends a PONG response.
func (t *UDPv5) handlePing(p *v5wire.Ping, fromID enode.ID, fromAddr *net.UDPAddr) { func (t *UDPv5) handlePing(p *v5wire.Ping, fromID enode.ID, fromAddr netip.AddrPort) {
remoteIP := fromAddr.IP var remoteIP net.IP
// Handle IPv4 mapped IPv6 addresses in the // Handle IPv4 mapped IPv6 addresses in the event the local node is binded
// event the local node is binded to an // to an ipv6 interface.
// ipv6 interface. if fromAddr.Addr().Is4() || fromAddr.Addr().Is4In6() {
if remoteIP.To4() != nil { ip4 := fromAddr.Addr().As4()
remoteIP = remoteIP.To4() remoteIP = ip4[:]
} else {
remoteIP = fromAddr.Addr().AsSlice()
} }
t.sendResponse(fromID, fromAddr, &v5wire.Pong{ t.sendResponse(fromID, fromAddr, &v5wire.Pong{
ReqID: p.ReqID, ReqID: p.ReqID,
ToIP: remoteIP, ToIP: remoteIP,
ToPort: uint16(fromAddr.Port), ToPort: fromAddr.Port(),
ENRSeq: t.localNode.Node().Seq(), ENRSeq: t.localNode.Node().Seq(),
}) })
} }
// handleFindnode returns nodes to the requester. // handleFindnode returns nodes to the requester.
func (t *UDPv5) handleFindnode(p *v5wire.Findnode, fromID enode.ID, fromAddr *net.UDPAddr) { func (t *UDPv5) handleFindnode(p *v5wire.Findnode, fromID enode.ID, fromAddr netip.AddrPort) {
nodes := t.collectTableNodes(fromAddr.IP, p.Distances, findnodeResultLimit) nodes := t.collectTableNodes(fromAddr.Addr(), p.Distances, findnodeResultLimit)
for _, resp := range packNodes(p.ReqID, nodes) { for _, resp := range packNodes(p.ReqID, nodes) {
t.sendResponse(fromID, fromAddr, resp) t.sendResponse(fromID, fromAddr, resp)
} }
} }
// collectTableNodes creates a FINDNODE result set for the given distances. // collectTableNodes creates a FINDNODE result set for the given distances.
func (t *UDPv5) collectTableNodes(rip net.IP, distances []uint, limit int) []*enode.Node { func (t *UDPv5) collectTableNodes(rip netip.Addr, distances []uint, limit int) []*enode.Node {
ripSlice := rip.AsSlice()
var bn []*enode.Node var bn []*enode.Node
var nodes []*enode.Node var nodes []*enode.Node
var processed = make(map[uint]struct{}) var processed = make(map[uint]struct{})
@ -857,7 +863,7 @@ func (t *UDPv5) collectTableNodes(rip net.IP, distances []uint, limit int) []*en
for _, n := range t.tab.appendLiveNodes(dist, bn[:0]) { for _, n := range t.tab.appendLiveNodes(dist, bn[:0]) {
// Apply some pre-checks to avoid sending invalid nodes. // Apply some pre-checks to avoid sending invalid nodes.
// Note liveness is checked by appendLiveNodes. // Note liveness is checked by appendLiveNodes.
if netutil.CheckRelayIP(rip, n.IP()) != nil { if netutil.CheckRelayIP(ripSlice, n.IP()) != nil {
continue continue
} }
nodes = append(nodes, n) nodes = append(nodes, n)

View file

@ -104,7 +104,7 @@ func TestUDPv5_pingHandling(t *testing.T) {
defer test.close() defer test.close()
test.packetIn(&v5wire.Ping{ReqID: []byte("foo")}) test.packetIn(&v5wire.Ping{ReqID: []byte("foo")})
test.waitPacketOut(func(p *v5wire.Pong, addr *net.UDPAddr, _ v5wire.Nonce) { test.waitPacketOut(func(p *v5wire.Pong, addr netip.AddrPort, _ v5wire.Nonce) {
if !bytes.Equal(p.ReqID, []byte("foo")) { if !bytes.Equal(p.ReqID, []byte("foo")) {
t.Error("wrong request ID in response:", p.ReqID) t.Error("wrong request ID in response:", p.ReqID)
} }
@ -136,7 +136,7 @@ func TestUDPv5_unknownPacket(t *testing.T) {
// Unknown packet from unknown node. // Unknown packet from unknown node.
test.packetIn(&v5wire.Unknown{Nonce: nonce}) test.packetIn(&v5wire.Unknown{Nonce: nonce})
test.waitPacketOut(func(p *v5wire.Whoareyou, addr *net.UDPAddr, _ v5wire.Nonce) { test.waitPacketOut(func(p *v5wire.Whoareyou, addr netip.AddrPort, _ v5wire.Nonce) {
check(p, 0) check(p, 0)
}) })
@ -145,7 +145,7 @@ func TestUDPv5_unknownPacket(t *testing.T) {
test.table.addFoundNode(n, false) test.table.addFoundNode(n, false)
test.packetIn(&v5wire.Unknown{Nonce: nonce}) test.packetIn(&v5wire.Unknown{Nonce: nonce})
test.waitPacketOut(func(p *v5wire.Whoareyou, addr *net.UDPAddr, _ v5wire.Nonce) { test.waitPacketOut(func(p *v5wire.Whoareyou, addr netip.AddrPort, _ v5wire.Nonce) {
check(p, n.Seq()) check(p, n.Seq())
}) })
} }
@ -200,7 +200,7 @@ func (test *udpV5Test) expectNodes(wantReqID []byte, wantTotal uint8, wantNodes
} }
for { for {
test.waitPacketOut(func(p *v5wire.Nodes, addr *net.UDPAddr, _ v5wire.Nonce) { test.waitPacketOut(func(p *v5wire.Nodes, addr netip.AddrPort, _ v5wire.Nonce) {
if !bytes.Equal(p.ReqID, wantReqID) { if !bytes.Equal(p.ReqID, wantReqID) {
test.t.Fatalf("wrong request ID %v in response, want %v", p.ReqID, wantReqID) test.t.Fatalf("wrong request ID %v in response, want %v", p.ReqID, wantReqID)
} }
@ -239,7 +239,7 @@ func TestUDPv5_pingCall(t *testing.T) {
_, err := test.udp.ping(remote) _, err := test.udp.ping(remote)
done <- err done <- err
}() }()
test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) {}) test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, _ v5wire.Nonce) {})
if err := <-done; err != errTimeout { if err := <-done; err != errTimeout {
t.Fatalf("want errTimeout, got %q", err) t.Fatalf("want errTimeout, got %q", err)
} }
@ -249,7 +249,7 @@ func TestUDPv5_pingCall(t *testing.T) {
_, err := test.udp.ping(remote) _, err := test.udp.ping(remote)
done <- err done <- err
}() }()
test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) { test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, _ v5wire.Nonce) {
test.packetInFrom(test.remotekey, test.remoteaddr, &v5wire.Pong{ReqID: p.ReqID}) test.packetInFrom(test.remotekey, test.remoteaddr, &v5wire.Pong{ReqID: p.ReqID})
}) })
if err := <-done; err != nil { if err := <-done; err != nil {
@ -261,8 +261,8 @@ func TestUDPv5_pingCall(t *testing.T) {
_, err := test.udp.ping(remote) _, err := test.udp.ping(remote)
done <- err done <- err
}() }()
test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) { test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, _ v5wire.Nonce) {
wrongAddr := &net.UDPAddr{IP: net.IP{33, 44, 55, 22}, Port: 10101} wrongAddr := netip.MustParseAddrPort("33.44.55.22:10101")
test.packetInFrom(test.remotekey, wrongAddr, &v5wire.Pong{ReqID: p.ReqID}) test.packetInFrom(test.remotekey, wrongAddr, &v5wire.Pong{ReqID: p.ReqID})
}) })
if err := <-done; err != errTimeout { if err := <-done; err != errTimeout {
@ -292,7 +292,7 @@ func TestUDPv5_findnodeCall(t *testing.T) {
}() }()
// Serve the responses: // Serve the responses:
test.waitPacketOut(func(p *v5wire.Findnode, addr *net.UDPAddr, _ v5wire.Nonce) { test.waitPacketOut(func(p *v5wire.Findnode, addr netip.AddrPort, _ v5wire.Nonce) {
if !reflect.DeepEqual(p.Distances, distances) { if !reflect.DeepEqual(p.Distances, distances) {
t.Fatalf("wrong distances in request: %v", p.Distances) t.Fatalf("wrong distances in request: %v", p.Distances)
} }
@ -338,15 +338,15 @@ func TestUDPv5_callResend(t *testing.T) {
}() }()
// Ping answered by WHOAREYOU. // Ping answered by WHOAREYOU.
test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, nonce v5wire.Nonce) { test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, nonce v5wire.Nonce) {
test.packetIn(&v5wire.Whoareyou{Nonce: nonce}) test.packetIn(&v5wire.Whoareyou{Nonce: nonce})
}) })
// Ping should be re-sent. // Ping should be re-sent.
test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) { test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, _ v5wire.Nonce) {
test.packetIn(&v5wire.Pong{ReqID: p.ReqID}) test.packetIn(&v5wire.Pong{ReqID: p.ReqID})
}) })
// Answer the other ping. // Answer the other ping.
test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) { test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, _ v5wire.Nonce) {
test.packetIn(&v5wire.Pong{ReqID: p.ReqID}) test.packetIn(&v5wire.Pong{ReqID: p.ReqID})
}) })
if err := <-done; err != nil { if err := <-done; err != nil {
@ -371,11 +371,11 @@ func TestUDPv5_multipleHandshakeRounds(t *testing.T) {
}() }()
// Ping answered by WHOAREYOU. // Ping answered by WHOAREYOU.
test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, nonce v5wire.Nonce) { test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, nonce v5wire.Nonce) {
test.packetIn(&v5wire.Whoareyou{Nonce: nonce}) test.packetIn(&v5wire.Whoareyou{Nonce: nonce})
}) })
// Ping answered by WHOAREYOU again. // Ping answered by WHOAREYOU again.
test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, nonce v5wire.Nonce) { test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, nonce v5wire.Nonce) {
test.packetIn(&v5wire.Whoareyou{Nonce: nonce}) test.packetIn(&v5wire.Whoareyou{Nonce: nonce})
}) })
if err := <-done; err != errTimeout { if err := <-done; err != errTimeout {
@ -402,7 +402,7 @@ func TestUDPv5_callTimeoutReset(t *testing.T) {
}() }()
// Serve two responses, slowly. // Serve two responses, slowly.
test.waitPacketOut(func(p *v5wire.Findnode, addr *net.UDPAddr, _ v5wire.Nonce) { test.waitPacketOut(func(p *v5wire.Findnode, addr netip.AddrPort, _ v5wire.Nonce) {
time.Sleep(respTimeout - 50*time.Millisecond) time.Sleep(respTimeout - 50*time.Millisecond)
test.packetIn(&v5wire.Nodes{ test.packetIn(&v5wire.Nodes{
ReqID: p.ReqID, ReqID: p.ReqID,
@ -440,7 +440,7 @@ func TestUDPv5_talkHandling(t *testing.T) {
Protocol: "test", Protocol: "test",
Message: []byte("test request"), Message: []byte("test request"),
}) })
test.waitPacketOut(func(p *v5wire.TalkResponse, addr *net.UDPAddr, _ v5wire.Nonce) { test.waitPacketOut(func(p *v5wire.TalkResponse, addr netip.AddrPort, _ v5wire.Nonce) {
if !bytes.Equal(p.ReqID, []byte("foo")) { if !bytes.Equal(p.ReqID, []byte("foo")) {
t.Error("wrong request ID in response:", p.ReqID) t.Error("wrong request ID in response:", p.ReqID)
} }
@ -459,7 +459,7 @@ func TestUDPv5_talkHandling(t *testing.T) {
Protocol: "wrong", Protocol: "wrong",
Message: []byte("test request"), Message: []byte("test request"),
}) })
test.waitPacketOut(func(p *v5wire.TalkResponse, addr *net.UDPAddr, _ v5wire.Nonce) { test.waitPacketOut(func(p *v5wire.TalkResponse, addr netip.AddrPort, _ v5wire.Nonce) {
if !bytes.Equal(p.ReqID, []byte("2")) { if !bytes.Equal(p.ReqID, []byte("2")) {
t.Error("wrong request ID in response:", p.ReqID) t.Error("wrong request ID in response:", p.ReqID)
} }
@ -486,7 +486,7 @@ func TestUDPv5_talkRequest(t *testing.T) {
_, err := test.udp.TalkRequest(remote, "test", []byte("test request")) _, err := test.udp.TalkRequest(remote, "test", []byte("test request"))
done <- err done <- err
}() }()
test.waitPacketOut(func(p *v5wire.TalkRequest, addr *net.UDPAddr, _ v5wire.Nonce) {}) test.waitPacketOut(func(p *v5wire.TalkRequest, addr netip.AddrPort, _ v5wire.Nonce) {})
if err := <-done; err != errTimeout { if err := <-done; err != errTimeout {
t.Fatalf("want errTimeout, got %q", err) t.Fatalf("want errTimeout, got %q", err)
} }
@ -496,7 +496,7 @@ func TestUDPv5_talkRequest(t *testing.T) {
_, err := test.udp.TalkRequest(remote, "test", []byte("test request")) _, err := test.udp.TalkRequest(remote, "test", []byte("test request"))
done <- err done <- err
}() }()
test.waitPacketOut(func(p *v5wire.TalkRequest, addr *net.UDPAddr, _ v5wire.Nonce) { test.waitPacketOut(func(p *v5wire.TalkRequest, addr netip.AddrPort, _ v5wire.Nonce) {
if p.Protocol != "test" { if p.Protocol != "test" {
t.Errorf("wrong protocol ID in talk request: %q", p.Protocol) t.Errorf("wrong protocol ID in talk request: %q", p.Protocol)
} }
@ -517,7 +517,7 @@ func TestUDPv5_talkRequest(t *testing.T) {
_, err := test.udp.TalkRequestToID(remote.ID(), test.remoteaddr, "test", []byte("test request 2")) _, err := test.udp.TalkRequestToID(remote.ID(), test.remoteaddr, "test", []byte("test request 2"))
done <- err done <- err
}() }()
test.waitPacketOut(func(p *v5wire.TalkRequest, addr *net.UDPAddr, _ v5wire.Nonce) { test.waitPacketOut(func(p *v5wire.TalkRequest, addr netip.AddrPort, _ v5wire.Nonce) {
if p.Protocol != "test" { if p.Protocol != "test" {
t.Errorf("wrong protocol ID in talk request: %q", p.Protocol) t.Errorf("wrong protocol ID in talk request: %q", p.Protocol)
} }
@ -584,7 +584,8 @@ func TestUDPv5_lookup(t *testing.T) {
for d, nn := range lookupTestnet.dists { for d, nn := range lookupTestnet.dists {
for i, key := range nn { for i, key := range nn {
n := lookupTestnet.node(d, i) n := lookupTestnet.node(d, i)
test.getNode(key, &net.UDPAddr{IP: n.IP(), Port: n.UDP()}) addr, _ := n.UDPEndpoint()
test.getNode(key, addr)
} }
} }
@ -653,11 +654,8 @@ func TestUDPv5_PingWithIPV4MappedAddress(t *testing.T) {
test := newUDPV5Test(t) test := newUDPV5Test(t)
defer test.close() defer test.close()
rawIP := net.IPv4(0xFF, 0x12, 0x33, 0xE5) rawIP := netip.AddrFrom4([4]byte{0xFF, 0x12, 0x33, 0xE5})
test.remoteaddr = &net.UDPAddr{ test.remoteaddr = netip.AddrPortFrom(netip.AddrFrom16(rawIP.As16()), 0)
IP: rawIP.To16(),
Port: 0,
}
remote := test.getNode(test.remotekey, test.remoteaddr).Node() remote := test.getNode(test.remotekey, test.remoteaddr).Node()
done := make(chan struct{}, 1) done := make(chan struct{}, 1)
@ -666,14 +664,14 @@ func TestUDPv5_PingWithIPV4MappedAddress(t *testing.T) {
test.udp.handlePing(&v5wire.Ping{ENRSeq: 1}, remote.ID(), test.remoteaddr) test.udp.handlePing(&v5wire.Ping{ENRSeq: 1}, remote.ID(), test.remoteaddr)
done <- struct{}{} done <- struct{}{}
}() }()
test.waitPacketOut(func(p *v5wire.Pong, addr *net.UDPAddr, _ v5wire.Nonce) { test.waitPacketOut(func(p *v5wire.Pong, addr netip.AddrPort, _ v5wire.Nonce) {
if len(p.ToIP) == net.IPv6len { if len(p.ToIP) == net.IPv6len {
t.Error("Received untruncated ip address") t.Error("Received untruncated ip address")
} }
if len(p.ToIP) != net.IPv4len { if len(p.ToIP) != net.IPv4len {
t.Errorf("Received ip address with incorrect length: %d", len(p.ToIP)) t.Errorf("Received ip address with incorrect length: %d", len(p.ToIP))
} }
if !p.ToIP.Equal(rawIP) { if !p.ToIP.Equal(rawIP.AsSlice()) {
t.Errorf("Received incorrect ip address: wanted %s but received %s", rawIP.String(), p.ToIP.String()) t.Errorf("Received incorrect ip address: wanted %s but received %s", rawIP.String(), p.ToIP.String())
} }
}) })
@ -751,9 +749,9 @@ func newUDPV5Test(t *testing.T) *udpV5Test {
pipe: newpipe(), pipe: newpipe(),
localkey: newkey(), localkey: newkey(),
remotekey: newkey(), remotekey: newkey(),
remoteaddr: &net.UDPAddr{IP: net.IP{10, 0, 1, 99}, Port: 30303}, remoteaddr: netip.MustParseAddrPort("10.0.1.99:30303"),
nodesByID: make(map[enode.ID]*enode.LocalNode), nodesByID: make(map[enode.ID]*enode.LocalNode),
nodesByIP: make(map[string]*enode.LocalNode), nodesByIP: make(map[netip.Addr]*enode.LocalNode),
} }
test.db, _ = enode.OpenDB("") test.db, _ = enode.OpenDB("")
ln := enode.NewLocalNode(test.db, test.localkey) ln := enode.NewLocalNode(test.db, test.localkey)
@ -804,12 +802,12 @@ func (test *udpV5Test) getNode(key *ecdsa.PrivateKey, addr netip.AddrPort) *enod
ln.Set(enr.UDP(addr.Port())) ln.Set(enr.UDP(addr.Port()))
test.nodesByID[id] = ln test.nodesByID[id] = ln
} }
test.nodesByIP[string(addr.Addr().String())] = ln test.nodesByIP[addr.Addr()] = ln
return ln return ln
} }
// waitPacketOut waits for the next output packet and handles it using the given 'validate' // waitPacketOut waits for the next output packet and handles it using the given 'validate'
// function. The function must be of type func (X, *net.UDPAddr, v5wire.Nonce) where X is // function. The function must be of type func (X, netip.AddrPort, v5wire.Nonce) where X is
// assignable to packetV5. // assignable to packetV5.
func (test *udpV5Test) waitPacketOut(validate interface{}) (closed bool) { func (test *udpV5Test) waitPacketOut(validate interface{}) (closed bool) {
test.t.Helper() test.t.Helper()
@ -825,7 +823,7 @@ func (test *udpV5Test) waitPacketOut(validate interface{}) (closed bool) {
test.t.Fatalf("timed out waiting for %v", exptype) test.t.Fatalf("timed out waiting for %v", exptype)
return false return false
} }
ln := test.nodesByIP[string(dgram.to.IP)] ln := test.nodesByIP[dgram.to.Addr()]
if ln == nil { if ln == nil {
test.t.Fatalf("attempt to send to non-existing node %v", &dgram.to) test.t.Fatalf("attempt to send to non-existing node %v", &dgram.to)
return false return false
@ -840,7 +838,7 @@ func (test *udpV5Test) waitPacketOut(validate interface{}) (closed bool) {
test.t.Errorf("sent packet type mismatch, got: %v, want: %v", reflect.TypeOf(p), exptype) test.t.Errorf("sent packet type mismatch, got: %v, want: %v", reflect.TypeOf(p), exptype)
return false return false
} }
fn.Call([]reflect.Value{reflect.ValueOf(p), reflect.ValueOf(&dgram.to), reflect.ValueOf(frame.AuthTag)}) fn.Call([]reflect.Value{reflect.ValueOf(p), reflect.ValueOf(dgram.to), reflect.ValueOf(frame.AuthTag)})
return false return false
} }