@ -26,6 +26,7 @@ import (
"fmt"
"io"
"net"
"net/netip"
"sync"
"time"
@ -45,6 +46,7 @@ var (
errClockWarp = errors . New ( "reply deadline too far in the future" )
errClosed = errors . New ( "socket closed" )
errLowPort = errors . New ( "low port" )
errNoUDPEndpoint = errors . New ( "node has no UDP endpoint" )
)
const (
@ -93,7 +95,7 @@ type UDPv4 struct {
type replyMatcher struct {
// these fields must match in the reply.
from enode . ID
ip net . IP
ip netip . Addr
ptype byte
// time when the request must complete
@ -119,7 +121,7 @@ type replyMatchFunc func(v4wire.Packet) (matched bool, requestDone bool)
// reply is a reply packet from a certain node.
type reply struct {
from enode . ID
ip net . IP
ip netip . Addr
data v4wire . Packet
// loop indicates whether there was
// a matching request by sending on this channel.
@ -201,9 +203,12 @@ func (t *UDPv4) Resolve(n *enode.Node) *enode.Node {
}
func ( t * UDPv4 ) ourEndpoint ( ) v4wire . Endpoint {
n := t . Self ( )
a := & net . UDPAddr { IP : n . IP ( ) , Port : n . UDP ( ) }
return v4wire . NewEndpoint ( a , uint16 ( n . TCP ( ) ) )
node := t . Self ( )
addr , ok := node . UDPEndpoint ( )
if ! ok {
return v4wire . Endpoint { }
}
return v4wire . NewEndpoint ( addr , uint16 ( node . TCP ( ) ) )
}
// Ping sends a ping message to the given node.
@ -214,7 +219,11 @@ func (t *UDPv4) Ping(n *enode.Node) error {
// 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 ) {
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 {
seq = rm . reply . ( * v4wire . Pong ) . ENRSeq
}
@ -223,7 +232,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
// when the reply arrives.
func ( t * UDPv4 ) sendPing ( toid enode . ID , toaddr * net . UDP Addr, callback func ( ) ) * replyMatcher {
func ( t * UDPv4 ) sendPing ( toid enode . ID , toaddr netip . AddrPort , callback func ( ) ) * replyMatcher {
req := t . makePing ( toaddr )
packet , hash , err := v4wire . Encode ( t . priv , req )
if err != nil {
@ -233,7 +242,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
// 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 )
if matched && callback != nil {
callback ( )
@ -241,12 +250,13 @@ func (t *UDPv4) sendPing(toid enode.ID, toaddr *net.UDPAddr, callback func()) *r
return matched , matched
} )
// Send the packet.
t . localNode . UDPContact ( toaddr )
toUDPAddr := & net . UDPAddr { IP : toaddr . Addr ( ) . AsSlice ( ) }
t . localNode . UDPContact ( toUDPAddr )
t . write ( toaddr , toid , req . Name ( ) , packet )
return rm
}
func ( t * UDPv4 ) makePing ( toaddr * net . UDP Addr) * v4wire . Ping {
func ( t * UDPv4 ) makePing ( toaddr netip . AddrPort ) * v4wire . Ping {
return & v4wire . Ping {
Version : 4 ,
From : t . ourEndpoint ( ) ,
@ -290,35 +300,39 @@ func (t *UDPv4) newRandomLookup(ctx context.Context) *lookup {
func ( t * UDPv4 ) newLookup ( ctx context . Context , targetKey encPubkey ) * lookup {
target := enode . ID ( crypto . Keccak256Hash ( targetKey [ : ] ) )
ekey := v4wire . Pubkey ( targetKey )
it := newLookup ( ctx , t . tab , target , func ( n * node ) ( [ ] * node , error ) {
return t . findnode ( n . ID ( ) , n . addr ( ) , ekey )
it := newLookup ( ctx , t . tab , target , func ( n * enode . Node ) ( [ ] * enode . Node , error ) {
addr , ok := n . UDPEndpoint ( )
if ! ok {
return nil , errNoUDPEndpoint
}
return t . findnode ( n . ID ( ) , addr , ekey )
} )
return it
}
// findnode sends a findnode request to the given node and waits until
// the node has sent up to k neighbors.
func ( t * UDPv4 ) findnode ( toid enode . ID , toaddr * net . UDPAddr , target v4wire . Pubkey ) ( [ ] * node , error ) {
t . ensureBond ( toid , toaddr )
func ( t * UDPv4 ) findnode ( toid enode . ID , toAddrPort netip . AddrPort , target v4wire . Pubkey ) ( [ ] * e node . N ode, error ) {
t . ensureBond ( toid , toAddrPort )
// Add a matcher for 'neighbours' replies to the pending reply queue. The matcher is
// active until enough nodes have been received.
nodes := make ( [ ] * node , 0 , bucketSize )
nodes := make ( [ ] * e node . N ode, 0 , bucketSize )
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 )
for _ , rn := range reply . Nodes {
nreceived ++
n , err := t . nodeFromRPC ( toaddr , rn )
n , err := t . nodeFromRPC ( toAddrPort , rn )
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
}
nodes = append ( nodes , n )
}
return true , nreceived >= bucketSize
} )
t . send ( toaddr , toid , & v4wire . Findnode {
t . send ( toAddrPort , toid , & v4wire . Findnode {
Target : target ,
Expiration : uint64 ( time . Now ( ) . Add ( expiration ) . Unix ( ) ) ,
} )
@ -336,7 +350,7 @@ func (t *UDPv4) findnode(toid enode.ID, toaddr *net.UDPAddr, target v4wire.Pubke
// RequestENR sends ENRRequest to the given node and waits for a response.
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 )
req := & v4wire . ENRRequest {
@ -349,7 +363,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
// 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 )
return matched , matched
} )
@ -369,7 +383,7 @@ func (t *UDPv4) RequestENR(n *enode.Node) (*enode.Node, error) {
if respN . Seq ( ) < n . Seq ( ) {
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 respN , nil
@ -381,7 +395,7 @@ func (t *UDPv4) TableBuckets() [][]BucketNode {
// pending adds a reply matcher to the pending reply queue.
// 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 )
p := & replyMatcher { from : id , ip : ip , ptype : ptype , callback : callback , errc : ch }
select {
@ -395,7 +409,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
// 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 )
select {
case t . gotreply <- reply { from , fromIP , req , matched } :
@ -461,7 +475,7 @@ func (t *UDPv4) loop() {
var matched bool // whether any replyMatcher considered the reply acceptable.
for el := plist . Front ( ) ; el != nil ; el = el . Next ( ) {
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 )
matched = matched || ok
p . reply = r . data
@ -500,7 +514,7 @@ func (t *UDPv4) loop() {
}
}
func ( t * UDPv4 ) send ( toaddr * net . UDP Addr, 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 )
if err != nil {
return hash , err
@ -508,8 +522,8 @@ func (t *UDPv4) send(toaddr *net.UDPAddr, toid enode.ID, req v4wire.Packet) ([]b
return hash , t . write ( toaddr , toid , req . Name ( ) , packet )
}
func ( t * UDPv4 ) write ( toaddr * net . UDP Addr, toid enode . ID , what string , packet [ ] byte ) error {
_ , err := t . conn . WriteToUDP ( packet , toaddr )
func ( t * UDPv4 ) write ( toaddr netip . AddrPort , toid enode . ID , what string , packet [ ] byte ) error {
_ , err := t . conn . WriteToUDPAddrPort ( packet , toaddr )
t . log . Trace ( ">> " + what , "id" , toid , "addr" , toaddr , "err" , err )
return err
}
@ -523,7 +537,7 @@ func (t *UDPv4) readLoop(unhandled chan<- ReadPacket) {
buf := make ( [ ] byte , maxPacketSize )
for {
nbytes , from , err := t . conn . ReadFromUDP ( buf )
nbytes , from , err := t . conn . ReadFromUDPAddrPort ( buf )
if netutil . IsTemporaryError ( err ) {
// Ignore temporary read errors.
t . log . Debug ( "Temporary UDP read error" , "err" , err )
@ -544,7 +558,7 @@ func (t *UDPv4) readLoop(unhandled chan<- ReadPacket) {
}
}
func ( t * UDPv4 ) handlePacket ( from * net . UDP Addr, buf [ ] byte ) error {
func ( t * UDPv4 ) handlePacket ( from netip . AddrPort , buf [ ] byte ) error {
rawpacket , fromKey , hash , err := v4wire . Decode ( buf )
if err != nil {
t . log . Debug ( "Bad discv4 packet" , "addr" , from , "err" , err )
@ -563,15 +577,16 @@ func (t *UDPv4) handlePacket(from *net.UDPAddr, buf []byte) error {
}
// checkBond checks if the given node has a recent enough endpoint proof.
func ( t * UDPv4 ) checkBond ( id enode . ID , ip net . IP ) bool {
return time . Since ( t . db . LastPongReceived ( id , ip ) ) < bondExpiration
func ( t * UDPv4 ) checkBond ( id enode . ID , ip netip . AddrPort ) bool {
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.
// This ensures there is a valid endpoint proof on the remote end.
func ( t * UDPv4 ) ensureBond ( toid enode . ID , toaddr * net . UDPAddr ) {
tooOld := time . Since ( t . db . LastPingReceived ( toid , toaddr . IP ) ) > bondExpiration
if tooOld || t . db . FindFails ( toid , toaddr . IP ) > maxFindnodeFailures {
func ( t * UDPv4 ) ensureBond ( toid enode . ID , toaddr netip . AddrPort ) {
ip := toaddr . Addr ( ) . AsSlice ( )
tooOld := time . Since ( t . db . LastPingReceived ( toid , ip ) ) > bondExpiration
if tooOld || t . db . FindFails ( toid , ip ) > maxFindnodeFailures {
rm := t . sendPing ( toid , toaddr , nil )
<- rm . errc
// Wait for them to ping back and process our pong.
@ -579,11 +594,11 @@ func (t *UDPv4) ensureBond(toid enode.ID, toaddr *net.UDPAddr) {
}
}
func ( t * UDPv4 ) nodeFromRPC ( sender * net . UDP Addr, rn v4wire . Node ) ( * node , error ) {
func ( t * UDPv4 ) nodeFromRPC ( sender netip . AddrPort , rn v4wire . Node ) ( * e node . N ode, error ) {
if rn . UDP <= 1024 {
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
}
if t . netrestrict != nil && ! t . netrestrict . Contains ( rn . IP ) {
@ -593,12 +608,12 @@ func (t *UDPv4) nodeFromRPC(sender *net.UDPAddr, rn v4wire.Node) (*node, error)
if err != nil {
return nil , err
}
n := wrapNode ( enode . NewV4 ( key , rn . IP , int ( rn . TCP ) , int ( rn . UDP ) ) )
n := enode . NewV4 ( key , rn . IP , int ( rn . TCP ) , int ( rn . UDP ) )
err = n . ValidateComplete ( )
return n , err
}
func nodeToRPC ( n * node ) v4wire . Node {
func nodeToRPC ( n * e node . N ode) v4wire . Node {
var key ecdsa . PublicKey
var ekey v4wire . Pubkey
if err := n . Load ( ( * enode . Secp256k1 ) ( & key ) ) ; err == nil {
@ -637,14 +652,14 @@ type packetHandlerV4 struct {
senderKey * ecdsa . PublicKey // used for ping
// preverify checks whether the packet is valid and should be handled at all.
preverify func ( p * packetHandlerV4 , from * net . UDP Addr, 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 func ( req * packetHandlerV4 , from * net . UDP Addr, fromID enode . ID , mac [ ] byte )
handle func ( req * packetHandlerV4 , from netip . AddrPort , fromID enode . ID , mac [ ] byte )
}
// PING/v4
func ( t * UDPv4 ) verifyPing ( h * packetHandlerV4 , from * net . UDP Addr, 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 )
if v4wire . Expired ( req . Expiration ) {
@ -658,7 +673,7 @@ func (t *UDPv4) verifyPing(h *packetHandlerV4, from *net.UDPAddr, fromID enode.I
return nil
}
func ( t * UDPv4 ) handlePing ( h * packetHandlerV4 , from * net . UDP Addr, fromID enode . ID , mac [ ] byte ) {
func ( t * UDPv4 ) handlePing ( h * packetHandlerV4 , from netip . AddrPort , fromID enode . ID , mac [ ] byte ) {
req := h . Packet . ( * v4wire . Ping )
// Reply.
@ -670,8 +685,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.
n := wrapNode ( enode . NewV4 ( h . senderKey , from . IP , int ( req . From . TCP ) , from . Port ) )
if time . Since ( t . db . LastPongReceived ( n . ID ( ) , from . IP ) ) > bondExpiration {
fromIP := from . Addr ( ) . AsSlice ( )
n := enode . NewV4 ( h . senderKey , fromIP , int ( req . From . TCP ) , int ( from . Port ( ) ) )
if time . Since ( t . db . LastPongReceived ( n . ID ( ) , fromIP ) ) > bondExpiration {
t . sendPing ( fromID , from , func ( ) {
t . tab . addInboundNode ( n )
} )
@ -680,35 +696,40 @@ func (t *UDPv4) handlePing(h *packetHandlerV4, from *net.UDPAddr, fromID enode.I
}
// Update node database and endpoint predictor.
t . db . UpdateLastPingReceived ( n . ID ( ) , from . IP , time . Now ( ) )
t . localNode . UDPEndpointStatement ( from , & net . UDPAddr { IP : req . To . IP , Port : int ( req . To . UDP ) } )
t . db . UpdateLastPingReceived ( n . ID ( ) , fromIP , 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 )
}
// PONG/v4
func ( t * UDPv4 ) verifyPong ( h * packetHandlerV4 , from * net . UDP Addr, 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 )
if v4wire . Expired ( req . Expiration ) {
return errExpired
}
if ! t . handleReply ( fromID , from . IP , req ) {
if ! t . handleReply ( fromID , from . Addr ( ) , req ) {
return errUnsolicitedReply
}
t . localNode . UDPEndpointStatement ( from , & net . UDPAddr { IP : req . To . IP , Port : int ( req . To . UDP ) } )
t . db . UpdateLastPongReceived ( fromID , from . IP , time . Now ( ) )
fromIP := from . Addr ( ) . AsSlice ( )
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
}
// FINDNODE/v4
func ( t * UDPv4 ) verifyFindnode ( h * packetHandlerV4 , from * net . UDP Addr, 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 )
if v4wire . Expired ( req . Expiration ) {
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
// 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
@ -720,7 +741,7 @@ func (t *UDPv4) verifyFindnode(h *packetHandlerV4, from *net.UDPAddr, fromID eno
return nil
}
func ( t * UDPv4 ) handleFindnode ( h * packetHandlerV4 , from * net . UDP Addr, fromID enode . ID , mac [ ] byte ) {
func ( t * UDPv4 ) handleFindnode ( h * packetHandlerV4 , from netip . AddrPort , fromID enode . ID , mac [ ] byte ) {
req := h . Packet . ( * v4wire . Findnode )
// Determine closest nodes.
@ -732,7 +753,8 @@ func (t *UDPv4) handleFindnode(h *packetHandlerV4, from *net.UDPAddr, fromID eno
p := v4wire . Neighbors { Expiration : uint64 ( time . Now ( ) . Add ( expiration ) . Unix ( ) ) }
var sent bool
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 ) )
}
if len ( p . Nodes ) == v4wire . MaxNeighbors {
@ -748,13 +770,13 @@ func (t *UDPv4) handleFindnode(h *packetHandlerV4, from *net.UDPAddr, fromID eno
// NEIGHBORS/v4
func ( t * UDPv4 ) verifyNeighbors ( h * packetHandlerV4 , from * net . UDP Addr, 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 )
if v4wire . Expired ( req . Expiration ) {
return errExpired
}
if ! t . handleReply ( fromID , from . IP , h . Packet ) {
if ! t . handleReply ( fromID , from . Addr ( ) , h . Packet ) {
return errUnsolicitedReply
}
return nil
@ -762,19 +784,19 @@ func (t *UDPv4) verifyNeighbors(h *packetHandlerV4, from *net.UDPAddr, fromID en
// ENRREQUEST/v4
func ( t * UDPv4 ) verifyENRRequest ( h * packetHandlerV4 , from * net . UDP Addr, 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 )
if v4wire . Expired ( req . Expiration ) {
return errExpired
}
if ! t . checkBond ( fromID , from . IP ) {
if ! t . checkBond ( fromID , from ) {
return errUnknownNode
}
return nil
}
func ( t * UDPv4 ) handleENRRequest ( h * packetHandlerV4 , from * net . UDP Addr, 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 {
ReplyTok : mac ,
Record : * t . localNode . Node ( ) . Record ( ) ,
@ -783,8 +805,8 @@ func (t *UDPv4) handleENRRequest(h *packetHandlerV4, from *net.UDPAddr, fromID e
// ENRRESPONSE/v4
func ( t * UDPv4 ) verifyENRResponse ( h * packetHandlerV4 , from * net . UDP Addr, fromID enode . ID , fromKey v4wire . Pubkey ) error {
if ! t . handleReply ( fromID , from . IP , h . Packet ) {
func ( t * UDPv4 ) verifyENRResponse ( h * packetHandlerV4 , from netip . AddrPort , fromID enode . ID , fromKey v4wire . Pubkey ) error {
if ! t . handleReply ( fromID , from . Addr ( ) , h . Packet ) {
return errUnsolicitedReply
}
return nil