github.com/shyftnetwork/go-empyrean@v1.8.3-0.20191127201940-fbfca9338f04/p2p/discover/udp.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package discover 18 19 import ( 20 "bytes" 21 "container/list" 22 "crypto/ecdsa" 23 "errors" 24 "fmt" 25 "net" 26 "sync" 27 "time" 28 29 "github.com/ShyftNetwork/go-empyrean/crypto" 30 "github.com/ShyftNetwork/go-empyrean/log" 31 "github.com/ShyftNetwork/go-empyrean/p2p/enode" 32 "github.com/ShyftNetwork/go-empyrean/p2p/netutil" 33 "github.com/ShyftNetwork/go-empyrean/rlp" 34 ) 35 36 // Errors 37 var ( 38 errPacketTooSmall = errors.New("too small") 39 errBadHash = errors.New("bad hash") 40 errExpired = errors.New("expired") 41 errUnsolicitedReply = errors.New("unsolicited reply") 42 errUnknownNode = errors.New("unknown node") 43 errTimeout = errors.New("RPC timeout") 44 errClockWarp = errors.New("reply deadline too far in the future") 45 errClosed = errors.New("socket closed") 46 ) 47 48 // Timeouts 49 const ( 50 respTimeout = 500 * time.Millisecond 51 expiration = 20 * time.Second 52 bondExpiration = 24 * time.Hour 53 54 ntpFailureThreshold = 32 // Continuous timeouts after which to check NTP 55 ntpWarningCooldown = 10 * time.Minute // Minimum amount of time to pass before repeating NTP warning 56 driftThreshold = 10 * time.Second // Allowed clock drift before warning user 57 ) 58 59 // RPC packet types 60 const ( 61 pingPacket = iota + 1 // zero is 'reserved' 62 pongPacket 63 findnodePacket 64 neighborsPacket 65 ) 66 67 // RPC request structures 68 type ( 69 ping struct { 70 Version uint 71 From, To rpcEndpoint 72 Expiration uint64 73 // Ignore additional fields (for forward compatibility). 74 Rest []rlp.RawValue `rlp:"tail"` 75 } 76 77 // pong is the reply to ping. 78 pong struct { 79 // This field should mirror the UDP envelope address 80 // of the ping packet, which provides a way to discover the 81 // the external address (after NAT). 82 To rpcEndpoint 83 84 ReplyTok []byte // This contains the hash of the ping packet. 85 Expiration uint64 // Absolute timestamp at which the packet becomes invalid. 86 // Ignore additional fields (for forward compatibility). 87 Rest []rlp.RawValue `rlp:"tail"` 88 } 89 90 // findnode is a query for nodes close to the given target. 91 findnode struct { 92 Target encPubkey 93 Expiration uint64 94 // Ignore additional fields (for forward compatibility). 95 Rest []rlp.RawValue `rlp:"tail"` 96 } 97 98 // reply to findnode 99 neighbors struct { 100 Nodes []rpcNode 101 Expiration uint64 102 // Ignore additional fields (for forward compatibility). 103 Rest []rlp.RawValue `rlp:"tail"` 104 } 105 106 rpcNode struct { 107 IP net.IP // len 4 for IPv4 or 16 for IPv6 108 UDP uint16 // for discovery protocol 109 TCP uint16 // for RLPx protocol 110 ID encPubkey 111 } 112 113 rpcEndpoint struct { 114 IP net.IP // len 4 for IPv4 or 16 for IPv6 115 UDP uint16 // for discovery protocol 116 TCP uint16 // for RLPx protocol 117 } 118 ) 119 120 func makeEndpoint(addr *net.UDPAddr, tcpPort uint16) rpcEndpoint { 121 ip := net.IP{} 122 if ip4 := addr.IP.To4(); ip4 != nil { 123 ip = ip4 124 } else if ip6 := addr.IP.To16(); ip6 != nil { 125 ip = ip6 126 } 127 return rpcEndpoint{IP: ip, UDP: uint16(addr.Port), TCP: tcpPort} 128 } 129 130 func (t *udp) nodeFromRPC(sender *net.UDPAddr, rn rpcNode) (*node, error) { 131 if rn.UDP <= 1024 { 132 return nil, errors.New("low port") 133 } 134 if err := netutil.CheckRelayIP(sender.IP, rn.IP); err != nil { 135 return nil, err 136 } 137 if t.netrestrict != nil && !t.netrestrict.Contains(rn.IP) { 138 return nil, errors.New("not contained in netrestrict whitelist") 139 } 140 key, err := decodePubkey(rn.ID) 141 if err != nil { 142 return nil, err 143 } 144 n := wrapNode(enode.NewV4(key, rn.IP, int(rn.TCP), int(rn.UDP))) 145 err = n.ValidateComplete() 146 return n, err 147 } 148 149 func nodeToRPC(n *node) rpcNode { 150 var key ecdsa.PublicKey 151 var ekey encPubkey 152 if err := n.Load((*enode.Secp256k1)(&key)); err == nil { 153 ekey = encodePubkey(&key) 154 } 155 return rpcNode{ID: ekey, IP: n.IP(), UDP: uint16(n.UDP()), TCP: uint16(n.TCP())} 156 } 157 158 type packet interface { 159 handle(t *udp, from *net.UDPAddr, fromKey encPubkey, mac []byte) error 160 name() string 161 } 162 163 type conn interface { 164 ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) 165 WriteToUDP(b []byte, addr *net.UDPAddr) (n int, err error) 166 Close() error 167 LocalAddr() net.Addr 168 } 169 170 // udp implements the discovery v4 UDP wire protocol. 171 type udp struct { 172 conn conn 173 netrestrict *netutil.Netlist 174 priv *ecdsa.PrivateKey 175 localNode *enode.LocalNode 176 db *enode.DB 177 tab *Table 178 wg sync.WaitGroup 179 180 addpending chan *pending 181 gotreply chan reply 182 closing chan struct{} 183 } 184 185 // pending represents a pending reply. 186 // 187 // some implementations of the protocol wish to send more than one 188 // reply packet to findnode. in general, any neighbors packet cannot 189 // be matched up with a specific findnode packet. 190 // 191 // our implementation handles this by storing a callback function for 192 // each pending reply. incoming packets from a node are dispatched 193 // to all the callback functions for that node. 194 type pending struct { 195 // these fields must match in the reply. 196 from enode.ID 197 ptype byte 198 199 // time when the request must complete 200 deadline time.Time 201 202 // callback is called when a matching reply arrives. if it returns 203 // true, the callback is removed from the pending reply queue. 204 // if it returns false, the reply is considered incomplete and 205 // the callback will be invoked again for the next matching reply. 206 callback func(resp interface{}) (done bool) 207 208 // errc receives nil when the callback indicates completion or an 209 // error if no further reply is received within the timeout. 210 errc chan<- error 211 } 212 213 type reply struct { 214 from enode.ID 215 ptype byte 216 data interface{} 217 // loop indicates whether there was 218 // a matching request by sending on this channel. 219 matched chan<- bool 220 } 221 222 // ReadPacket is sent to the unhandled channel when it could not be processed 223 type ReadPacket struct { 224 Data []byte 225 Addr *net.UDPAddr 226 } 227 228 // Config holds Table-related settings. 229 type Config struct { 230 // These settings are required and configure the UDP listener: 231 PrivateKey *ecdsa.PrivateKey 232 233 // These settings are optional: 234 NetRestrict *netutil.Netlist // network whitelist 235 Bootnodes []*enode.Node // list of bootstrap nodes 236 Unhandled chan<- ReadPacket // unhandled packets are sent on this channel 237 } 238 239 // ListenUDP returns a new table that listens for UDP packets on laddr. 240 func ListenUDP(c conn, ln *enode.LocalNode, cfg Config) (*Table, error) { 241 tab, _, err := newUDP(c, ln, cfg) 242 if err != nil { 243 return nil, err 244 } 245 return tab, nil 246 } 247 248 func newUDP(c conn, ln *enode.LocalNode, cfg Config) (*Table, *udp, error) { 249 udp := &udp{ 250 conn: c, 251 priv: cfg.PrivateKey, 252 netrestrict: cfg.NetRestrict, 253 localNode: ln, 254 db: ln.Database(), 255 closing: make(chan struct{}), 256 gotreply: make(chan reply), 257 addpending: make(chan *pending), 258 } 259 tab, err := newTable(udp, ln.Database(), cfg.Bootnodes) 260 if err != nil { 261 return nil, nil, err 262 } 263 udp.tab = tab 264 265 udp.wg.Add(2) 266 go udp.loop() 267 go udp.readLoop(cfg.Unhandled) 268 return udp.tab, udp, nil 269 } 270 271 func (t *udp) self() *enode.Node { 272 return t.localNode.Node() 273 } 274 275 func (t *udp) close() { 276 close(t.closing) 277 t.conn.Close() 278 t.wg.Wait() 279 } 280 281 func (t *udp) ourEndpoint() rpcEndpoint { 282 n := t.self() 283 a := &net.UDPAddr{IP: n.IP(), Port: n.UDP()} 284 return makeEndpoint(a, uint16(n.TCP())) 285 } 286 287 // ping sends a ping message to the given node and waits for a reply. 288 func (t *udp) ping(toid enode.ID, toaddr *net.UDPAddr) error { 289 return <-t.sendPing(toid, toaddr, nil) 290 } 291 292 // sendPing sends a ping message to the given node and invokes the callback 293 // when the reply arrives. 294 func (t *udp) sendPing(toid enode.ID, toaddr *net.UDPAddr, callback func()) <-chan error { 295 req := &ping{ 296 Version: 4, 297 From: t.ourEndpoint(), 298 To: makeEndpoint(toaddr, 0), // TODO: maybe use known TCP port from DB 299 Expiration: uint64(time.Now().Add(expiration).Unix()), 300 } 301 packet, hash, err := encodePacket(t.priv, pingPacket, req) 302 if err != nil { 303 errc := make(chan error, 1) 304 errc <- err 305 return errc 306 } 307 errc := t.pending(toid, pongPacket, func(p interface{}) bool { 308 ok := bytes.Equal(p.(*pong).ReplyTok, hash) 309 if ok && callback != nil { 310 callback() 311 } 312 return ok 313 }) 314 t.localNode.UDPContact(toaddr) 315 t.write(toaddr, req.name(), packet) 316 return errc 317 } 318 319 func (t *udp) waitping(from enode.ID) error { 320 return <-t.pending(from, pingPacket, func(interface{}) bool { return true }) 321 } 322 323 // findnode sends a findnode request to the given node and waits until 324 // the node has sent up to k neighbors. 325 func (t *udp) findnode(toid enode.ID, toaddr *net.UDPAddr, target encPubkey) ([]*node, error) { 326 // If we haven't seen a ping from the destination node for a while, it won't remember 327 // our endpoint proof and reject findnode. Solicit a ping first. 328 if time.Since(t.db.LastPingReceived(toid)) > bondExpiration { 329 t.ping(toid, toaddr) 330 t.waitping(toid) 331 } 332 333 nodes := make([]*node, 0, bucketSize) 334 nreceived := 0 335 errc := t.pending(toid, neighborsPacket, func(r interface{}) bool { 336 reply := r.(*neighbors) 337 for _, rn := range reply.Nodes { 338 nreceived++ 339 n, err := t.nodeFromRPC(toaddr, rn) 340 if err != nil { 341 log.Trace("Invalid neighbor node received", "ip", rn.IP, "addr", toaddr, "err", err) 342 continue 343 } 344 nodes = append(nodes, n) 345 } 346 return nreceived >= bucketSize 347 }) 348 t.send(toaddr, findnodePacket, &findnode{ 349 Target: target, 350 Expiration: uint64(time.Now().Add(expiration).Unix()), 351 }) 352 return nodes, <-errc 353 } 354 355 // pending adds a reply callback to the pending reply queue. 356 // see the documentation of type pending for a detailed explanation. 357 func (t *udp) pending(id enode.ID, ptype byte, callback func(interface{}) bool) <-chan error { 358 ch := make(chan error, 1) 359 p := &pending{from: id, ptype: ptype, callback: callback, errc: ch} 360 select { 361 case t.addpending <- p: 362 // loop will handle it 363 case <-t.closing: 364 ch <- errClosed 365 } 366 return ch 367 } 368 369 func (t *udp) handleReply(from enode.ID, ptype byte, req packet) bool { 370 matched := make(chan bool, 1) 371 select { 372 case t.gotreply <- reply{from, ptype, req, matched}: 373 // loop will handle it 374 return <-matched 375 case <-t.closing: 376 return false 377 } 378 } 379 380 // loop runs in its own goroutine. it keeps track of 381 // the refresh timer and the pending reply queue. 382 func (t *udp) loop() { 383 defer t.wg.Done() 384 385 var ( 386 plist = list.New() 387 timeout = time.NewTimer(0) 388 nextTimeout *pending // head of plist when timeout was last reset 389 contTimeouts = 0 // number of continuous timeouts to do NTP checks 390 ntpWarnTime = time.Unix(0, 0) 391 ) 392 <-timeout.C // ignore first timeout 393 defer timeout.Stop() 394 395 resetTimeout := func() { 396 if plist.Front() == nil || nextTimeout == plist.Front().Value { 397 return 398 } 399 // Start the timer so it fires when the next pending reply has expired. 400 now := time.Now() 401 for el := plist.Front(); el != nil; el = el.Next() { 402 nextTimeout = el.Value.(*pending) 403 if dist := nextTimeout.deadline.Sub(now); dist < 2*respTimeout { 404 timeout.Reset(dist) 405 return 406 } 407 // Remove pending replies whose deadline is too far in the 408 // future. These can occur if the system clock jumped 409 // backwards after the deadline was assigned. 410 nextTimeout.errc <- errClockWarp 411 plist.Remove(el) 412 } 413 nextTimeout = nil 414 timeout.Stop() 415 } 416 417 for { 418 resetTimeout() 419 420 select { 421 case <-t.closing: 422 for el := plist.Front(); el != nil; el = el.Next() { 423 el.Value.(*pending).errc <- errClosed 424 } 425 return 426 427 case p := <-t.addpending: 428 p.deadline = time.Now().Add(respTimeout) 429 plist.PushBack(p) 430 431 case r := <-t.gotreply: 432 var matched bool 433 for el := plist.Front(); el != nil; el = el.Next() { 434 p := el.Value.(*pending) 435 if p.from == r.from && p.ptype == r.ptype { 436 matched = true 437 // Remove the matcher if its callback indicates 438 // that all replies have been received. This is 439 // required for packet types that expect multiple 440 // reply packets. 441 if p.callback(r.data) { 442 p.errc <- nil 443 plist.Remove(el) 444 } 445 // Reset the continuous timeout counter (time drift detection) 446 contTimeouts = 0 447 } 448 } 449 r.matched <- matched 450 451 case now := <-timeout.C: 452 nextTimeout = nil 453 454 // Notify and remove callbacks whose deadline is in the past. 455 for el := plist.Front(); el != nil; el = el.Next() { 456 p := el.Value.(*pending) 457 if now.After(p.deadline) || now.Equal(p.deadline) { 458 p.errc <- errTimeout 459 plist.Remove(el) 460 contTimeouts++ 461 } 462 } 463 // If we've accumulated too many timeouts, do an NTP time sync check 464 if contTimeouts > ntpFailureThreshold { 465 if time.Since(ntpWarnTime) >= ntpWarningCooldown { 466 ntpWarnTime = time.Now() 467 go checkClockDrift() 468 } 469 contTimeouts = 0 470 } 471 } 472 } 473 } 474 475 const ( 476 macSize = 256 / 8 477 sigSize = 520 / 8 478 headSize = macSize + sigSize // space of packet frame data 479 ) 480 481 var ( 482 headSpace = make([]byte, headSize) 483 484 // Neighbors replies are sent across multiple packets to 485 // stay below the 1280 byte limit. We compute the maximum number 486 // of entries by stuffing a packet until it grows too large. 487 maxNeighbors int 488 ) 489 490 func init() { 491 p := neighbors{Expiration: ^uint64(0)} 492 maxSizeNode := rpcNode{IP: make(net.IP, 16), UDP: ^uint16(0), TCP: ^uint16(0)} 493 for n := 0; ; n++ { 494 p.Nodes = append(p.Nodes, maxSizeNode) 495 size, _, err := rlp.EncodeToReader(p) 496 if err != nil { 497 // If this ever happens, it will be caught by the unit tests. 498 panic("cannot encode: " + err.Error()) 499 } 500 if headSize+size+1 >= 1280 { 501 maxNeighbors = n 502 break 503 } 504 } 505 } 506 507 func (t *udp) send(toaddr *net.UDPAddr, ptype byte, req packet) ([]byte, error) { 508 packet, hash, err := encodePacket(t.priv, ptype, req) 509 if err != nil { 510 return hash, err 511 } 512 return hash, t.write(toaddr, req.name(), packet) 513 } 514 515 func (t *udp) write(toaddr *net.UDPAddr, what string, packet []byte) error { 516 _, err := t.conn.WriteToUDP(packet, toaddr) 517 log.Trace(">> "+what, "addr", toaddr, "err", err) 518 return err 519 } 520 521 func encodePacket(priv *ecdsa.PrivateKey, ptype byte, req interface{}) (packet, hash []byte, err error) { 522 b := new(bytes.Buffer) 523 b.Write(headSpace) 524 b.WriteByte(ptype) 525 if err := rlp.Encode(b, req); err != nil { 526 log.Error("Can't encode discv4 packet", "err", err) 527 return nil, nil, err 528 } 529 packet = b.Bytes() 530 sig, err := crypto.Sign(crypto.Keccak256(packet[headSize:]), priv) 531 if err != nil { 532 log.Error("Can't sign discv4 packet", "err", err) 533 return nil, nil, err 534 } 535 copy(packet[macSize:], sig) 536 // add the hash to the front. Note: this doesn't protect the 537 // packet in any way. Our public key will be part of this hash in 538 // The future. 539 hash = crypto.Keccak256(packet[macSize:]) 540 copy(packet, hash) 541 return packet, hash, nil 542 } 543 544 // readLoop runs in its own goroutine. it handles incoming UDP packets. 545 func (t *udp) readLoop(unhandled chan<- ReadPacket) { 546 defer t.wg.Done() 547 if unhandled != nil { 548 defer close(unhandled) 549 } 550 551 // Discovery packets are defined to be no larger than 1280 bytes. 552 // Packets larger than this size will be cut at the end and treated 553 // as invalid because their hash won't match. 554 buf := make([]byte, 1280) 555 for { 556 nbytes, from, err := t.conn.ReadFromUDP(buf) 557 if netutil.IsTemporaryError(err) { 558 // Ignore temporary read errors. 559 log.Debug("Temporary UDP read error", "err", err) 560 continue 561 } else if err != nil { 562 // Shut down the loop for permament errors. 563 log.Debug("UDP read error", "err", err) 564 return 565 } 566 if t.handlePacket(from, buf[:nbytes]) != nil && unhandled != nil { 567 select { 568 case unhandled <- ReadPacket{buf[:nbytes], from}: 569 default: 570 } 571 } 572 } 573 } 574 575 func (t *udp) handlePacket(from *net.UDPAddr, buf []byte) error { 576 packet, fromID, hash, err := decodePacket(buf) 577 if err != nil { 578 log.Debug("Bad discv4 packet", "addr", from, "err", err) 579 return err 580 } 581 err = packet.handle(t, from, fromID, hash) 582 log.Trace("<< "+packet.name(), "addr", from, "err", err) 583 return err 584 } 585 586 func decodePacket(buf []byte) (packet, encPubkey, []byte, error) { 587 if len(buf) < headSize+1 { 588 return nil, encPubkey{}, nil, errPacketTooSmall 589 } 590 hash, sig, sigdata := buf[:macSize], buf[macSize:headSize], buf[headSize:] 591 shouldhash := crypto.Keccak256(buf[macSize:]) 592 if !bytes.Equal(hash, shouldhash) { 593 return nil, encPubkey{}, nil, errBadHash 594 } 595 fromKey, err := recoverNodeKey(crypto.Keccak256(buf[headSize:]), sig) 596 if err != nil { 597 return nil, fromKey, hash, err 598 } 599 600 var req packet 601 switch ptype := sigdata[0]; ptype { 602 case pingPacket: 603 req = new(ping) 604 case pongPacket: 605 req = new(pong) 606 case findnodePacket: 607 req = new(findnode) 608 case neighborsPacket: 609 req = new(neighbors) 610 default: 611 return nil, fromKey, hash, fmt.Errorf("unknown type: %d", ptype) 612 } 613 s := rlp.NewStream(bytes.NewReader(sigdata[1:]), 0) 614 err = s.Decode(req) 615 return req, fromKey, hash, err 616 } 617 618 func (req *ping) handle(t *udp, from *net.UDPAddr, fromKey encPubkey, mac []byte) error { 619 if expired(req.Expiration) { 620 return errExpired 621 } 622 key, err := decodePubkey(fromKey) 623 if err != nil { 624 return fmt.Errorf("invalid public key: %v", err) 625 } 626 t.send(from, pongPacket, &pong{ 627 To: makeEndpoint(from, req.From.TCP), 628 ReplyTok: mac, 629 Expiration: uint64(time.Now().Add(expiration).Unix()), 630 }) 631 n := wrapNode(enode.NewV4(key, from.IP, int(req.From.TCP), from.Port)) 632 t.handleReply(n.ID(), pingPacket, req) 633 if time.Since(t.db.LastPongReceived(n.ID())) > bondExpiration { 634 t.sendPing(n.ID(), from, func() { t.tab.addThroughPing(n) }) 635 } else { 636 t.tab.addThroughPing(n) 637 } 638 t.localNode.UDPEndpointStatement(from, &net.UDPAddr{IP: req.To.IP, Port: int(req.To.UDP)}) 639 t.db.UpdateLastPingReceived(n.ID(), time.Now()) 640 return nil 641 } 642 643 func (req *ping) name() string { return "PING/v4" } 644 645 func (req *pong) handle(t *udp, from *net.UDPAddr, fromKey encPubkey, mac []byte) error { 646 if expired(req.Expiration) { 647 return errExpired 648 } 649 fromID := fromKey.id() 650 if !t.handleReply(fromID, pongPacket, req) { 651 return errUnsolicitedReply 652 } 653 t.localNode.UDPEndpointStatement(from, &net.UDPAddr{IP: req.To.IP, Port: int(req.To.UDP)}) 654 t.db.UpdateLastPongReceived(fromID, time.Now()) 655 return nil 656 } 657 658 func (req *pong) name() string { return "PONG/v4" } 659 660 func (req *findnode) handle(t *udp, from *net.UDPAddr, fromKey encPubkey, mac []byte) error { 661 if expired(req.Expiration) { 662 return errExpired 663 } 664 fromID := fromKey.id() 665 if time.Since(t.db.LastPongReceived(fromID)) > bondExpiration { 666 // No endpoint proof pong exists, we don't process the packet. This prevents an 667 // attack vector where the discovery protocol could be used to amplify traffic in a 668 // DDOS attack. A malicious actor would send a findnode request with the IP address 669 // and UDP port of the target as the source address. The recipient of the findnode 670 // packet would then send a neighbors packet (which is a much bigger packet than 671 // findnode) to the victim. 672 return errUnknownNode 673 } 674 target := enode.ID(crypto.Keccak256Hash(req.Target[:])) 675 t.tab.mutex.Lock() 676 closest := t.tab.closest(target, bucketSize).entries 677 t.tab.mutex.Unlock() 678 679 p := neighbors{Expiration: uint64(time.Now().Add(expiration).Unix())} 680 var sent bool 681 // Send neighbors in chunks with at most maxNeighbors per packet 682 // to stay below the 1280 byte limit. 683 for _, n := range closest { 684 if netutil.CheckRelayIP(from.IP, n.IP()) == nil { 685 p.Nodes = append(p.Nodes, nodeToRPC(n)) 686 } 687 if len(p.Nodes) == maxNeighbors { 688 t.send(from, neighborsPacket, &p) 689 p.Nodes = p.Nodes[:0] 690 sent = true 691 } 692 } 693 if len(p.Nodes) > 0 || !sent { 694 t.send(from, neighborsPacket, &p) 695 } 696 return nil 697 } 698 699 func (req *findnode) name() string { return "FINDNODE/v4" } 700 701 func (req *neighbors) handle(t *udp, from *net.UDPAddr, fromKey encPubkey, mac []byte) error { 702 if expired(req.Expiration) { 703 return errExpired 704 } 705 if !t.handleReply(fromKey.id(), neighborsPacket, req) { 706 return errUnsolicitedReply 707 } 708 return nil 709 } 710 711 func (req *neighbors) name() string { return "NEIGHBORS/v4" } 712 713 func expired(ts uint64) bool { 714 return time.Unix(int64(ts), 0).Before(time.Now()) 715 }