1use crate::{
2 discovery::lookup::{IterativeLookup, LOOKUP_ALPHA, LOOKUP_BUCKET_SIZE},
3 discv5::{
4 messages::{
5 DISTANCES_PER_FIND_NODE_MSG, FindNodeMessage, Handshake, HandshakeAuthdata, Message,
6 NodesMessage, Ordinary, Packet, PacketTrait as _, PingMessage, PongMessage,
7 TalkResMessage, WhoAreYou, decrypt_message,
8 },
9 server::{Discv5Message, Discv5State, SessionSource, update_local_ip},
10 session::{
11 build_challenge_data, create_id_signature, derive_session_keys, verify_id_signature,
12 },
13 },
14 metrics::METRICS,
15 peer_table::{ContactValidation, DiscoveryProtocol, PeerTableServerProtocol as _},
16 rlpx::utils::compress_pubkey,
17 types::{Node, NodeRecord},
18 utils::{distance, node_id},
19};
20use bytes::{Bytes, BytesMut};
21use ethrex_common::{H256, H512};
22use rand::{Rng, rngs::OsRng};
23use secp256k1::{PublicKey, SecretKey, ecdsa::Signature};
24use std::{
25 net::SocketAddr,
26 time::{Duration, Instant},
27};
28use tracing::{debug, trace, warn};
29
30use super::server::{DiscoveryServer, DiscoveryServerError};
31
32const MAX_ENRS_PER_MESSAGE: usize = 3;
34const REVALIDATION_INTERVAL: Duration = Duration::from_secs(12 * 60 * 60); const WHOAREYOU_RATE_LIMIT: Duration = Duration::from_secs(1);
38const GLOBAL_WHOAREYOU_RATE_LIMIT: u32 = 100;
40
41impl DiscoveryServer {
42 pub(crate) async fn discv5_handle_packet(
43 &mut self,
44 Discv5Message { packet, from }: Discv5Message,
45 ) -> Result<(), DiscoveryServerError> {
46 #[cfg(feature = "metrics")]
47 {
48 use ethrex_metrics::p2p::METRICS_P2P;
49 match packet.header.flag {
50 0x01 => METRICS_P2P.inc_discv5_incoming("WhoAreYou"),
51 0x02 => METRICS_P2P.inc_discv5_incoming("Handshake"),
52 _ => {}
53 }
54 }
55 match packet.header.flag {
56 0x00 => self.discv5_handle_ordinary(packet, from).await,
57 0x01 => self.discv5_handle_who_are_you(packet, from).await,
58 0x02 => self.discv5_handle_handshake(packet, from).await,
59 f => {
60 tracing::trace!(protocol = "discv5", "Unexpected flag {f}");
61 Err(crate::discv5::messages::PacketCodecError::MalformedData.into())
62 }
63 }
64 }
65
66 async fn discv5_handle_ordinary(
67 &mut self,
68 packet: Packet,
69 addr: SocketAddr,
70 ) -> Result<(), DiscoveryServerError> {
71 let src_id = H256::from_slice(&packet.header.authdata);
72
73 let decrypt_key = self
74 .peer_table
75 .get_session_info(src_id)
76 .await?
77 .map(|s| s.inbound_key);
78
79 let discv5 = self.discv5.as_mut().expect("discv5 state must exist");
80
81 let ordinary = match decrypt_key {
82 Some(key) => match Ordinary::decode(&packet, &key) {
83 Ok(ordinary) => {
84 if let Some(SessionSource { ip: session_ip, .. }) =
85 discv5.session_ips.get(&src_id)
86 && addr.ip() != *session_ip
87 {
88 trace!(
89 protocol = "discv5",
90 from = %src_id,
91 %addr,
92 expected_ip = %session_ip,
93 "IP mismatch for existing session, sending WhoAreYou"
94 );
95 discv5.whoareyou_rate_limit.pop(&(addr.ip(), src_id));
96 return self
97 .discv5_send_who_are_you(packet.header.nonce, src_id, addr)
98 .await;
99 }
100 ordinary
101 }
102 Err(_) => {
103 trace!(protocol = "discv5", from = %src_id, %addr, "Decryption failed, sending WhoAreYou");
104 return self
105 .discv5_send_who_are_you(packet.header.nonce, src_id, addr)
106 .await;
107 }
108 },
109 None => {
110 trace!(protocol = "discv5", from = %src_id, %addr, "No session, sending WhoAreYou");
111 return self
112 .discv5_send_who_are_you(packet.header.nonce, src_id, addr)
113 .await;
114 }
115 };
116
117 tracing::trace!(protocol = "discv5", received = %ordinary.message, from = %src_id, %addr);
118
119 self.discv5_handle_message(ordinary, addr, None).await
120 }
121
122 async fn discv5_handle_who_are_you(
123 &mut self,
124 packet: Packet,
125 addr: SocketAddr,
126 ) -> Result<(), DiscoveryServerError> {
127 let nonce = packet.header.nonce;
128 let discv5 = self.discv5.as_mut().expect("discv5 state must exist");
129 let Some((node, message, _)) = discv5.pending_by_nonce.remove(&nonce) else {
130 tracing::trace!(
131 protocol = "discv5",
132 "Received unexpected WhoAreYou packet. Ignoring it"
133 );
134 return Ok(());
135 };
136 tracing::trace!(protocol = "discv5", received = "WhoAreYou", from = %node.node_id(), %addr);
137
138 let challenge_data = build_challenge_data(
139 &packet.masking_iv,
140 &packet.header.static_header,
141 &packet.header.authdata,
142 );
143
144 let ephemeral_key = SecretKey::new(&mut OsRng);
145 let ephemeral_pubkey = ephemeral_key.public_key(secp256k1::SECP256K1).serialize();
146
147 let Some(dest_pubkey) = compress_pubkey(node.public_key) else {
148 return Err(DiscoveryServerError::CryptographyError(
149 "Invalid public key".to_string(),
150 ));
151 };
152
153 let session = derive_session_keys(
154 &ephemeral_key,
155 &dest_pubkey,
156 &self.local_node.node_id(),
157 &node.node_id(),
158 &challenge_data,
159 true,
160 );
161
162 let signature = create_id_signature(
163 &self.signer,
164 &challenge_data,
165 &ephemeral_pubkey,
166 &node.node_id(),
167 );
168
169 self.peer_table.set_session_info(node.node_id(), session)?;
170
171 let whoareyou = WhoAreYou::decode(&packet)?;
172 let record = (self.local_node_record.seq != whoareyou.enr_seq)
173 .then(|| self.local_node_record.clone());
174 self.discv5_send_handshake(message, signature, &ephemeral_pubkey, node, record)
175 .await
176 }
177
178 async fn discv5_handle_handshake(
179 &mut self,
180 packet: Packet,
181 addr: SocketAddr,
182 ) -> Result<(), DiscoveryServerError> {
183 let authdata = HandshakeAuthdata::decode(&packet.header.authdata)?;
184 let src_id = authdata.src_id;
185
186 let discv5 = self.discv5.as_mut().expect("discv5 state must exist");
187 let Some((challenge_data, _, _)) = discv5.pending_challenges.remove(&src_id) else {
188 trace!(protocol = "discv5", from = %src_id, %addr, "Received unexpected Handshake packet");
189 return Ok(());
190 };
191
192 let eph_pubkey = PublicKey::from_slice(&authdata.eph_pubkey).map_err(|_| {
193 DiscoveryServerError::CryptographyError("Invalid ephemeral pubkey".into())
194 })?;
195
196 let src_pubkey = if let Some(contact) = self.peer_table.get_contact(src_id).await? {
197 compress_pubkey(contact.node.public_key)
198 } else if let Some(record) = &authdata.record {
199 if !record.verify_signature() {
200 trace!(from = %src_id, "Handshake ENR signature verification failed");
201 return Ok(());
202 }
203 let pairs = record.pairs();
204 let pubkey = pairs
205 .secp256k1
206 .and_then(|pk| PublicKey::from_slice(pk.as_bytes()).ok());
207
208 if let Some(pk) = &pubkey {
209 let uncompressed = pk.serialize_uncompressed();
210 let derived_node_id = node_id(&H512::from_slice(&uncompressed[1..]));
211 if derived_node_id != src_id {
212 trace!(from = %src_id, "Handshake ENR node_id mismatch");
213 return Ok(());
214 }
215 }
216
217 pubkey
218 } else {
219 None
220 };
221
222 let Some(src_pubkey) = src_pubkey else {
223 trace!(protocol = "discv5", from = %src_id, "Cannot verify handshake: unknown sender public key");
224 return Ok(());
225 };
226
227 let signature = Signature::from_compact(&authdata.id_signature).map_err(|_| {
228 DiscoveryServerError::CryptographyError("Invalid signature format".into())
229 })?;
230
231 if !verify_id_signature(
232 &src_pubkey,
233 &challenge_data,
234 &authdata.eph_pubkey,
235 &self.local_node.node_id(),
236 &signature,
237 ) {
238 trace!(protocol = "discv5", from = %src_id, "Handshake signature verification failed");
239 return Ok(());
240 }
241
242 if let Some(record) = &authdata.record {
243 self.peer_table.new_contact_records(vec![record.clone()])?;
244 }
245
246 let session = derive_session_keys(
247 &self.signer,
248 &eph_pubkey,
249 &src_id,
250 &self.local_node.node_id(),
251 &challenge_data,
252 false,
253 );
254
255 self.peer_table.set_session_info(src_id, session.clone())?;
256 let discv5 = self.discv5.as_mut().expect("discv5 state must exist");
257 discv5.session_ips.insert(
258 src_id,
259 SessionSource {
260 ip: addr.ip(),
261 established_at: std::time::Instant::now(),
262 },
263 );
264
265 let mut encrypted = packet.encrypted_message.clone();
266 decrypt_message(&session.inbound_key, &packet, &mut encrypted)?;
267 let message = Message::decode(&encrypted)?;
268 trace!(protocol = "discv5", received = %message, from = %src_id, %addr, "Handshake completed");
269
270 let ordinary = Ordinary { src_id, message };
271 self.discv5_handle_message(ordinary, addr, Some(session.outbound_key))
272 .await
273 }
274
275 pub(crate) async fn discv5_revalidate(&mut self) -> Result<(), DiscoveryServerError> {
276 if let Some(contact) = self
277 .peer_table
278 .get_contact_to_revalidate(REVALIDATION_INTERVAL, DiscoveryProtocol::Discv5)
279 .await?
280 && let Err(e) = self.discv5_send_ping(&contact.node).await
281 {
282 trace!(protocol = "discv5", node = %contact.node.node_id(), err = ?e, "Failed to send revalidation PING");
283 }
284 Ok(())
285 }
286
287 pub(crate) async fn discv5_lookup(&mut self) -> Result<(), DiscoveryServerError> {
288 if self.discv5.is_none() {
289 return Ok(());
290 }
291
292 self.discv5
294 .as_mut()
295 .expect("discv5 state must exist")
296 .active_lookups
297 .retain(|l| !l.is_finished());
298
299 if !self
304 .discv5
305 .as_ref()
306 .expect("discv5 state must exist")
307 .active_lookups
308 .is_empty()
309 {
310 return self.advance_v5_lookup().await;
311 }
312
313 let mut rng = OsRng;
314 let target_id: H256 = rng.r#gen();
315
316 let seed = self
318 .peer_table
319 .get_closest_from_pool(target_id, LOOKUP_BUCKET_SIZE)
320 .await?;
321 if seed.is_empty() {
322 trace!(
323 protocol = "discv5",
324 "No seeds for lookup, connection pool empty"
325 );
326 return Ok(());
327 }
328
329 trace!(
330 protocol = "discv5",
331 seeds = seed.len(),
332 "Starting new iterative lookup"
333 );
334 let lookup = IterativeLookup::new(target_id, seed);
335 let discv5 = self.discv5.as_mut().expect("discv5 state must exist");
336 discv5.active_lookups.push(lookup);
337
338 self.advance_v5_lookup().await
340 }
341
342 async fn advance_v5_lookup(&mut self) -> Result<(), DiscoveryServerError> {
343 let discv5 = match &mut self.discv5 {
344 Some(s) => s,
345 None => return Ok(()),
346 };
347
348 if discv5.active_lookups.is_empty() {
349 return Ok(());
350 }
351
352 let mut queries: Vec<(usize, H256, H256, Node)> = Vec::new();
354 for (idx, lookup) in discv5.active_lookups.iter_mut().enumerate() {
355 let target = lookup.target;
356 for (node_id, node) in lookup.next_to_query(LOOKUP_ALPHA) {
357 queries.push((idx, target, node_id, node));
358 }
359 }
360
361 for (idx, target, node_id, node) in queries {
362 let find_node_msg = self.discv5_build_find_node_for_target(target, &node);
363 if let Err(e) = self.discv5_send_ordinary(find_node_msg, &node).await {
364 debug!(protocol = "discv5", sending = "FindNode", addr = ?node.udp_addr(), err=?e, "Error sending message");
365 self.peer_table.set_disposable(node_id)?;
366 METRICS.record_new_discarded_node();
367 if let Some(discv5) = &mut self.discv5
368 && let Some(lookup) = discv5.active_lookups.get_mut(idx)
369 {
370 lookup.record_timeout();
371 }
372 }
373 }
374 Ok(())
375 }
376
377 fn discv5_build_find_node_for_target(&self, target: H256, node: &Node) -> Message {
378 let center_distance = distance(&target, &node.node_id()) as u8;
379 let mut distances = Vec::new();
380 distances.push(center_distance as u32);
381 for i in 0..DISTANCES_PER_FIND_NODE_MSG / 2 {
382 if let Some(d) = center_distance.checked_add(i + 1) {
383 distances.push(d as u32)
384 }
385 if let Some(d) = center_distance.checked_sub(i + 1) {
386 distances.push(d as u32)
387 }
388 }
389 Message::FindNode(FindNodeMessage {
390 req_id: generate_req_id(),
391 distances,
392 })
393 }
394
395 async fn discv5_handle_ping(
396 &mut self,
397 ping_message: PingMessage,
398 sender_id: H256,
399 sender_addr: SocketAddr,
400 outbound_key: Option<[u8; 16]>,
401 ) -> Result<(), DiscoveryServerError> {
402 trace!(protocol = "discv5", from = %sender_id, enr_seq = ping_message.enr_seq, "Received PING");
403
404 let pong = Message::Pong(PongMessage {
405 req_id: ping_message.req_id,
406 enr_seq: self.local_node_record.seq,
407 recipient_addr: sender_addr,
408 });
409
410 if outbound_key.is_none()
411 && let Some(contact) = self.peer_table.get_contact(sender_id).await?
412 {
413 return self.discv5_send_ordinary(pong, &contact.node).await;
414 }
415 let key = self
416 .discv5_resolve_outbound_key(&sender_id, outbound_key)
417 .await?;
418 self.discv5_send_ordinary_to(pong, &sender_id, sender_addr, &key)
419 .await?;
420
421 Ok(())
422 }
423
424 pub async fn discv5_handle_pong(
425 &mut self,
426 pong_message: PongMessage,
427 sender_id: H256,
428 ) -> Result<(), DiscoveryServerError> {
429 self.peer_table
430 .record_pong_received(sender_id, pong_message.req_id)?;
431
432 if let Some(contact) = self.peer_table.get_contact(sender_id).await? {
433 let cached_seq = contact.record.as_ref().map_or(0, |r| r.seq);
434 if pong_message.enr_seq > cached_seq {
435 trace!(
436 protocol = "discv5",
437 from = %sender_id,
438 cached_seq,
439 pong_seq = pong_message.enr_seq,
440 "ENR seq mismatch, requesting updated ENR (FINDNODE distance 0)"
441 );
442 let find_node = Message::FindNode(FindNodeMessage {
443 req_id: generate_req_id(),
444 distances: vec![0],
445 });
446 self.discv5_send_ordinary(find_node, &contact.node).await?;
447 }
448 }
449
450 let discv5 = self.discv5.as_mut().expect("discv5 state must exist");
451 if let Some(winning_ip) = discv5.record_ip_vote(pong_message.recipient_addr.ip(), sender_id)
452 && winning_ip != self.local_node.ip
453 {
454 tracing::info!(
455 protocol = "discv5",
456 old_ip = %self.local_node.ip,
457 new_ip = %winning_ip,
458 "External IP detected via PONG voting, updating local ENR"
459 );
460 update_local_ip(
461 &mut self.local_node,
462 &mut self.local_node_record,
463 &self.signer,
464 winning_ip,
465 );
466 }
467
468 Ok(())
469 }
470
471 async fn discv5_handle_find_node(
472 &mut self,
473 find_node_message: FindNodeMessage,
474 sender_id: H256,
475 sender_addr: SocketAddr,
476 outbound_key: Option<[u8; 16]>,
477 ) -> Result<(), DiscoveryServerError> {
478 let send_to_contact = match self
479 .peer_table
480 .validate_contact(sender_id, sender_addr.ip())
481 .await?
482 {
483 ContactValidation::Valid(contact) => Some(*contact),
484 ContactValidation::UnknownContact => None,
485 reason => {
486 trace!(from = %sender_id, ?reason, "Rejected FINDNODE");
487 return Ok(());
488 }
489 };
490
491 let mut nodes = self
492 .peer_table
493 .get_nodes_at_distances(find_node_message.distances.clone())
494 .await?;
495 if find_node_message.distances.contains(&0) {
496 nodes.push(self.local_node_record.clone());
497 }
498
499 let key = self
500 .discv5_resolve_outbound_key(&sender_id, outbound_key)
501 .await?;
502
503 let chunks: Vec<_> = nodes.chunks(MAX_ENRS_PER_MESSAGE).collect();
504 if chunks.is_empty() {
505 let nodes_message = Message::Nodes(NodesMessage {
506 req_id: find_node_message.req_id,
507 total: 1,
508 nodes: vec![],
509 });
510 if let Some(contact) = &send_to_contact {
511 self.discv5_send_ordinary(nodes_message, &contact.node)
512 .await?;
513 } else {
514 self.discv5_send_ordinary_to(nodes_message, &sender_id, sender_addr, &key)
515 .await?;
516 }
517 } else {
518 for chunk in &chunks {
519 let nodes_message = Message::Nodes(NodesMessage {
520 req_id: find_node_message.req_id.clone(),
521 total: chunks.len() as u64,
522 nodes: chunk.to_vec(),
523 });
524 if let Some(contact) = &send_to_contact {
525 self.discv5_send_ordinary(nodes_message, &contact.node)
526 .await?;
527 } else {
528 self.discv5_send_ordinary_to(nodes_message, &sender_id, sender_addr, &key)
529 .await?;
530 }
531 }
532 }
533
534 Ok(())
535 }
536
537 async fn discv5_handle_nodes_message(
538 &mut self,
539 nodes_message: NodesMessage,
540 ) -> Result<(), DiscoveryServerError> {
541 self.peer_table
542 .new_contact_records(nodes_message.nodes.clone())?;
543
544 if let Some(discv5) = &mut self.discv5 {
547 let entries: Vec<(H256, Node)> = nodes_message
548 .nodes
549 .iter()
550 .filter_map(|r| Node::from_enr(r).ok().map(|n| (n.node_id(), n)))
551 .collect();
552 for lookup in &mut discv5.active_lookups {
553 lookup.feed_results(entries.clone());
554 }
555 if let Some(lookup) = discv5.active_lookups.first_mut() {
556 lookup.record_response();
557 }
558 }
559
560 Ok(())
561 }
562
563 async fn discv5_send_ping(&mut self, node: &Node) -> Result<(), DiscoveryServerError> {
564 let req_id = generate_req_id();
565
566 let ping = Message::Ping(PingMessage {
567 req_id: req_id.clone(),
568 enr_seq: self.local_node_record.seq,
569 });
570
571 self.discv5_send_ordinary(ping, node).await?;
572 self.peer_table.record_ping_sent(node.node_id(), req_id)?;
573
574 Ok(())
575 }
576
577 async fn discv5_send_ordinary(
578 &mut self,
579 message: Message,
580 node: &Node,
581 ) -> Result<(), DiscoveryServerError> {
582 #[cfg(feature = "metrics")]
583 {
584 use ethrex_metrics::p2p::METRICS_P2P;
585 METRICS_P2P.inc_discv5_outgoing(message.metric_label());
586 }
587 let ordinary = Ordinary {
588 src_id: self.local_node.node_id(),
589 message: message.clone(),
590 };
591 let encrypt_key = match self.peer_table.get_session_info(node.node_id()).await? {
592 Some(s) => s.outbound_key,
593 None => {
594 trace!(
595 protocol = "discv5",
596 node = %node.node_id(),
597 "No session found in send_ordinary, using zeroed key to trigger handshake"
598 );
599 [0; 16]
600 }
601 };
602
603 let discv5 = self.discv5.as_mut().expect("discv5 state must exist");
604 let mut rng = OsRng;
605 let masking_iv: u128 = rng.r#gen();
606 let nonce = discv5.next_nonce(&mut rng);
607
608 let packet = ordinary.encode(&nonce, masking_iv.to_be_bytes(), &encrypt_key)?;
609
610 self.discv5_send_packet(&packet, &node.node_id(), node.udp_addr())
611 .await?;
612 let discv5 = self.discv5.as_mut().expect("discv5 state must exist");
613 discv5
614 .pending_by_nonce
615 .insert(nonce, (node.clone(), message, Instant::now()));
616 Ok(())
617 }
618
619 async fn discv5_resolve_outbound_key(
620 &self,
621 node_id: &H256,
622 key: Option<[u8; 16]>,
623 ) -> Result<[u8; 16], DiscoveryServerError> {
624 if let Some(key) = key {
625 return Ok(key);
626 }
627 match self.peer_table.get_session_info(*node_id).await? {
628 Some(s) => Ok(s.outbound_key),
629 None => {
630 trace!(
631 protocol = "discv5",
632 node = %node_id,
633 "No session found in resolve_outbound_key, using zeroed key"
634 );
635 Ok([0; 16])
636 }
637 }
638 }
639
640 async fn discv5_send_ordinary_to(
641 &mut self,
642 message: Message,
643 dest_id: &H256,
644 addr: SocketAddr,
645 encrypt_key: &[u8; 16],
646 ) -> Result<(), DiscoveryServerError> {
647 #[cfg(feature = "metrics")]
648 {
649 use ethrex_metrics::p2p::METRICS_P2P;
650 METRICS_P2P.inc_discv5_outgoing(message.metric_label());
651 }
652 let ordinary = Ordinary {
653 src_id: self.local_node.node_id(),
654 message,
655 };
656
657 let discv5 = self.discv5.as_mut().expect("discv5 state must exist");
658 let mut rng = OsRng;
659 let masking_iv: u128 = rng.r#gen();
660 let nonce = discv5.next_nonce(&mut rng);
661
662 let packet = ordinary.encode(&nonce, masking_iv.to_be_bytes(), encrypt_key)?;
663
664 self.discv5_send_packet(&packet, dest_id, addr).await?;
665 Ok(())
666 }
667
668 async fn discv5_send_handshake(
669 &mut self,
670 message: Message,
671 signature: Signature,
672 eph_pubkey: &[u8],
673 node: Node,
674 record: Option<NodeRecord>,
675 ) -> Result<(), DiscoveryServerError> {
676 #[cfg(feature = "metrics")]
677 {
678 use ethrex_metrics::p2p::METRICS_P2P;
679 METRICS_P2P.inc_discv5_outgoing("Handshake");
680 }
681 let handshake = Handshake {
682 src_id: self.local_node.node_id(),
683 id_signature: signature.serialize_compact().to_vec(),
684 eph_pubkey: eph_pubkey.to_vec(),
685 record,
686 message: message.clone(),
687 };
688 let encrypt_key = match self.peer_table.get_session_info(node.node_id()).await? {
689 Some(s) => s.outbound_key,
690 None => {
691 trace!(
692 protocol = "discv5",
693 node = %node.node_id(),
694 "No session found in send_handshake, using zeroed key"
695 );
696 [0; 16]
697 }
698 };
699
700 let discv5 = self.discv5.as_mut().expect("discv5 state must exist");
701 let mut rng = OsRng;
702 let masking_iv: u128 = rng.r#gen();
703 let nonce = discv5.next_nonce(&mut rng);
704
705 let packet = handshake.encode(&nonce, masking_iv.to_be_bytes(), &encrypt_key)?;
706
707 self.discv5_send_packet(&packet, &node.node_id(), node.udp_addr())
708 .await?;
709 let discv5 = self.discv5.as_mut().expect("discv5 state must exist");
710 discv5
711 .pending_by_nonce
712 .insert(nonce, (node, message, Instant::now()));
713 Ok(())
714 }
715
716 pub async fn discv5_send_who_are_you(
717 &mut self,
718 nonce: [u8; 12],
719 src_id: H256,
720 addr: SocketAddr,
721 ) -> Result<(), DiscoveryServerError> {
722 #[cfg(feature = "metrics")]
723 {
724 use ethrex_metrics::p2p::METRICS_P2P;
725 METRICS_P2P.inc_discv5_outgoing("WhoAreYou");
726 }
727 let discv5 = self.discv5.as_mut().expect("discv5 state must exist");
728
729 let rate_key = (addr.ip(), src_id);
730 let now = Instant::now();
731
732 if now.duration_since(discv5.whoareyou_global_window_start) >= Duration::from_secs(1) {
734 discv5.whoareyou_global_count = 0;
735 discv5.whoareyou_global_window_start = now;
736 }
737 if discv5.whoareyou_global_count >= GLOBAL_WHOAREYOU_RATE_LIMIT {
738 if discv5.whoareyou_global_count == GLOBAL_WHOAREYOU_RATE_LIMIT {
739 discv5.whoareyou_global_count = GLOBAL_WHOAREYOU_RATE_LIMIT + 1;
740 warn!(
741 protocol = "discv5",
742 "Global WHOAREYOU rate limit reached ({GLOBAL_WHOAREYOU_RATE_LIMIT}/s), \
743 dropping excess packets. This is normal during initial discovery or \
744 network churn; persistent occurrences may indicate a DoS attempt"
745 );
746 }
747 return Ok(());
748 }
749
750 if let Some((_, _, raw_bytes)) = discv5.pending_challenges.get(&src_id) {
752 trace!(
753 protocol = "discv5",
754 to = %src_id,
755 %addr,
756 "Resending existing WhoAreYou challenge"
757 );
758 self.udp_socket.send_to(raw_bytes, addr).await?;
759 return Ok(());
760 }
761
762 if !Discv5State::is_private_ip(addr.ip())
764 && let Some(last_sent) = discv5.whoareyou_rate_limit.get(&rate_key)
765 && now.duration_since(*last_sent) < WHOAREYOU_RATE_LIMIT
766 {
767 trace!(
768 protocol = "discv5",
769 to_ip = %addr.ip(),
770 "Rate limiting WHOAREYOU packet (amplification attack prevention)"
771 );
772 return Ok(());
773 }
774
775 discv5.whoareyou_rate_limit.push(rate_key, now);
776 discv5.whoareyou_global_count += 1;
777
778 let mut rng = OsRng;
779
780 let enr_seq = self
781 .peer_table
782 .get_contact(src_id)
783 .await?
784 .map_or(0, |c| c.record.as_ref().map_or(0, |r| r.seq));
785
786 let who_are_you = WhoAreYou {
787 id_nonce: rng.r#gen(),
788 enr_seq,
789 };
790
791 let masking_iv: u128 = rng.r#gen();
792 let packet = who_are_you.encode(&nonce, masking_iv.to_be_bytes(), &[0; 16])?;
793
794 let mut raw_buf = BytesMut::new();
795 packet.encode(&mut raw_buf, &src_id)?;
796 let raw_bytes = raw_buf.to_vec();
797
798 let challenge_data = build_challenge_data(
799 &masking_iv.to_be_bytes(),
800 &packet.header.static_header,
801 &packet.header.authdata,
802 );
803 let discv5 = self.discv5.as_mut().expect("discv5 state must exist");
804 discv5
805 .pending_challenges
806 .insert(src_id, (challenge_data, Instant::now(), raw_bytes.clone()));
807
808 self.udp_socket.send_to(&raw_bytes, addr).await?;
809 trace!(protocol = "discv5", to = %src_id, %addr, flag = packet.header.flag, "Sent packet");
810
811 Ok(())
812 }
813
814 async fn discv5_send_packet(
815 &self,
816 packet: &Packet,
817 dest_id: &H256,
818 addr: SocketAddr,
819 ) -> Result<(), DiscoveryServerError> {
820 let mut buf = BytesMut::new();
821 packet.encode(&mut buf, dest_id)?;
822 self.udp_socket.send_to(&buf, addr).await?;
823 trace!(protocol = "discv5", to = %dest_id, %addr, flag = packet.header.flag, "Sent packet");
824 Ok(())
825 }
826
827 async fn discv5_handle_message(
828 &mut self,
829 ordinary: Ordinary,
830 sender_addr: SocketAddr,
831 outbound_key: Option<[u8; 16]>,
832 ) -> Result<(), DiscoveryServerError> {
833 let sender_id = ordinary.src_id;
834 if sender_id == self.local_node.node_id() {
835 return Ok(());
836 }
837 #[cfg(feature = "metrics")]
838 {
839 use ethrex_metrics::p2p::METRICS_P2P;
840 METRICS_P2P.inc_discv5_incoming(ordinary.message.metric_label());
841 }
842 match ordinary.message {
843 Message::Ping(ping_message) => {
844 if ping_message.req_id.len() > 8 {
845 trace!(protocol = "discv5", from = %sender_id, "Dropping PING with oversized req_id");
846 return Ok(());
847 }
848 self.discv5_handle_ping(ping_message, sender_id, sender_addr, outbound_key)
849 .await?
850 }
851 Message::Pong(pong_message) => {
852 self.discv5_handle_pong(pong_message, sender_id).await?;
853 }
854 Message::FindNode(find_node_message) => {
855 if find_node_message.req_id.len() > 8 {
856 trace!(protocol = "discv5", from = %sender_id, "Dropping FINDNODE with oversized req_id");
857 return Ok(());
858 }
859 self.discv5_handle_find_node(
860 find_node_message,
861 sender_id,
862 sender_addr,
863 outbound_key,
864 )
865 .await?;
866 }
867 Message::Nodes(nodes_message) => {
868 self.discv5_handle_nodes_message(nodes_message).await?;
869 }
870 Message::TalkReq(talk_req_message) => {
871 if talk_req_message.req_id.len() > 8 {
872 trace!(protocol = "discv5", from = %sender_id, "Dropping TALKREQ with oversized req_id");
873 return Ok(());
874 }
875 let talk_res = Message::TalkRes(TalkResMessage {
876 req_id: talk_req_message.req_id,
877 response: vec![],
878 });
879 let key = self
880 .discv5_resolve_outbound_key(&sender_id, outbound_key)
881 .await?;
882 self.discv5_send_ordinary_to(talk_res, &sender_id, sender_addr, &key)
883 .await?;
884 }
885 Message::TalkRes(_talk_res_message) => (),
886 Message::Ticket(_ticket_message) => (),
887 }
888 Ok(())
889 }
890}
891
892fn generate_req_id() -> Bytes {
893 let mut rng = OsRng;
894 Bytes::from(rng.r#gen::<u64>().to_be_bytes().to_vec())
895}