1use crate::config::{NostrDiscoveryPolicy, TransportInstances, UdpConfig};
7#[cfg(test)]
8use crate::node::ENDPOINT_EVENT_TEST_PAYLOAD_LEN;
9use crate::node::{
10 EndpointDataBatchTx, EndpointDataPayload, EndpointDirectSink, EndpointEventSender,
11 EndpointServiceEventSender, NodeEndpointControlCommand, NodeEndpointDataBatch,
12 NodeEndpointEvent,
13};
14use crate::upper::tun::TunOutboundTx;
15use crate::{
16 Config, FipsAddress, IdentityConfig, Node, NodeAddr, NodeDeliveredPacket, NodeError,
17 PeerIdentity,
18};
19use std::collections::HashMap;
20use std::sync::{Arc, Mutex as StdMutex};
21use std::time::Duration;
22use thiserror::Error;
23use tokio::sync::{Mutex, mpsc, oneshot};
24use tokio::task::JoinHandle;
25
26const ENDPOINT_DATA_BATCH_MAX: usize = 128;
27const ENDPOINT_RECV_BATCH_MAX: usize = 128;
28const ENDPOINT_OPERATION_TIMEOUT: Duration = Duration::from_secs(5);
29
30mod builder;
31#[path = "endpoint/nostr.rs"]
32mod nostr_api;
33mod receive;
34mod service_receiver;
35mod status;
36
37#[cfg(test)]
38mod tests;
39
40pub use crate::node::{
41 FIPS_ENDPOINT_DIRECT_PACKET_QUEUE_MAX_PACKETS, FIPS_ENDPOINT_DIRECT_PACKET_RUN_MAX_PACKETS,
42 FipsEndpointDirectDeliveryError, FipsEndpointDirectPacketBatch, FipsEndpointDirectPacketRun,
43 FipsEndpointDirectReceiver, FipsEndpointDirectSink,
44};
45pub use builder::FipsEndpointBuilder;
46use receive::{EndpointReceiveState, ServiceReceiveState};
47pub use status::{FipsEndpointPeer, FipsEndpointRelayStatus};
48
49pub type FipsEndpointData = crate::transport::PacketBuffer;
54
55#[derive(Debug, Error)]
57pub enum FipsEndpointError {
58 #[error("node error: {0}")]
59 Node(#[from] NodeError),
60
61 #[error("endpoint task failed: {0}")]
62 TaskJoin(#[from] tokio::task::JoinError),
63
64 #[error("endpoint is closed")]
65 Closed,
66
67 #[error("endpoint {operation} timed out")]
68 Timeout { operation: &'static str },
69
70 #[error("endpoint data payload is too large: {len} bytes exceeds max {max} bytes")]
71 EndpointDataTooLarge { len: usize, max: usize },
72
73 #[error("service datagram payload is too large: {len} bytes exceeds max {max} bytes")]
74 ServiceDatagramTooLarge { len: usize, max: usize },
75
76 #[error("FSP service port {port} is reserved")]
77 ServicePortReserved { port: u16 },
78
79 #[error("FSP service port {port} is already registered")]
80 ServicePortAlreadyRegistered { port: u16 },
81
82 #[cfg(feature = "host-ble-transport")]
83 #[error("host BLE adapter was already consumed by another endpoint bind")]
84 HostBleAdapterConsumed,
85}
86
87#[derive(Debug, Error)]
89pub enum LocalServiceRegistrationError {
90 #[error("local FSP service capability must include a port")]
91 ServiceCapabilityMissingPort,
92
93 #[error("local FSP service capability name must not be empty")]
94 ServiceCapabilityNameEmpty,
95
96 #[error("local FSP service capability name exceeds {max} bytes")]
97 ServiceCapabilityNameTooLong { max: usize },
98
99 #[error(transparent)]
100 Endpoint(#[from] FipsEndpointError),
101}
102
103#[derive(Debug, Clone, PartialEq, Eq)]
105pub struct FipsEndpointMessage {
106 pub source_peer: PeerIdentity,
108 pub data: FipsEndpointData,
110 pub enqueued_at_ms: u64,
112}
113
114#[derive(Debug, Clone, PartialEq, Eq)]
116pub struct FipsEndpointOutboundDatagram {
117 pub source_port: u16,
118 pub destination_port: u16,
119 pub data: Vec<u8>,
120}
121
122impl FipsEndpointOutboundDatagram {
123 pub fn new(source_port: u16, destination_port: u16, data: Vec<u8>) -> Self {
124 Self {
125 source_port,
126 destination_port,
127 data,
128 }
129 }
130}
131
132#[derive(Debug, Clone, PartialEq, Eq)]
134pub struct FipsEndpointServiceDatagram {
135 pub source_peer: PeerIdentity,
136 pub source_port: u16,
137 pub destination_port: u16,
138 pub data: FipsEndpointData,
139 pub enqueued_at_ms: u64,
140}
141
142pub struct FipsEndpointServiceReceiver {
147 state: Mutex<ServiceReceiveState>,
148}
149
150#[derive(Debug, Clone, Default, PartialEq, Eq)]
152pub struct UpdatePeersOutcome {
153 pub added: usize,
156 pub removed: usize,
160 pub updated: usize,
165 pub unchanged: usize,
167}
168
169impl From<crate::node::UpdatePeersOutcome> for UpdatePeersOutcome {
170 fn from(value: crate::node::UpdatePeersOutcome) -> Self {
171 Self {
172 added: value.added,
173 removed: value.removed,
174 updated: value.updated,
175 unchanged: value.unchanged,
176 }
177 }
178}
179
180fn apply_default_scoped_discovery(config: &mut Config, scope: &str) {
181 if config.node.discovery.nostr.enabled || !config.transports.is_empty() {
182 return;
183 }
184
185 config.node.discovery.nostr.enabled = true;
186 config.node.discovery.nostr.advertise = true;
187 config.node.discovery.nostr.policy = NostrDiscoveryPolicy::Open;
188 config.node.discovery.nostr.share_local_candidates = true;
189 config.node.discovery.nostr.app = scope.to_string();
190 config.node.discovery.lan.scope = Some(scope.to_string());
191 config.node.discovery.local.enabled = true;
192 config.transports.udp = TransportInstances::Single(UdpConfig {
193 bind_addr: Some("0.0.0.0:0".to_string()),
194 advertise_on_nostr: Some(true),
195 public: Some(false),
196 outbound_only: Some(false),
197 accept_connections: Some(true),
198 ..UdpConfig::default()
199 });
200}
201
202fn endpoint_data_payloads_from_vecs(
203 payloads: Vec<Vec<u8>>,
204) -> Result<Vec<EndpointDataPayload>, FipsEndpointError> {
205 let mut converted = Vec::with_capacity(payloads.len());
206 for payload in payloads {
207 let len = payload.len();
208 let Some(payload) = EndpointDataPayload::from_packet_payload(payload) else {
209 let max = crate::node::session_wire::fsp_endpoint_data_max_body_len();
210 return Err(FipsEndpointError::EndpointDataTooLarge { len, max });
211 };
212 converted.push(payload);
213 }
214 Ok(converted)
215}
216
217fn service_datagram_payloads(
218 datagrams: Vec<FipsEndpointOutboundDatagram>,
219) -> Result<Vec<EndpointDataPayload>, FipsEndpointError> {
220 let max = crate::node::session_wire::fsp_service_datagram_max_body_len();
221 let mut payloads = Vec::with_capacity(datagrams.len());
222 for datagram in datagrams {
223 let len = datagram.data.len();
224 let Some(payload) = EndpointDataPayload::from_service_datagram(
225 datagram.source_port,
226 datagram.destination_port,
227 datagram.data,
228 ) else {
229 return Err(FipsEndpointError::ServiceDatagramTooLarge { len, max });
230 };
231 payloads.push(payload);
232 }
233 Ok(payloads)
234}
235
236fn spawn_node_task(
237 mut node: Node,
238 shutdown_rx: oneshot::Receiver<()>,
239) -> JoinHandle<Result<(), NodeError>> {
240 tokio::spawn(async move {
241 tokio::pin!(shutdown_rx);
242 let loop_result = tokio::select! {
243 result = node.run_rx_loop() => result,
244 _ = &mut shutdown_rx => Ok(()),
245 };
246 let stop_result = if node.state().can_stop() {
247 node.stop().await
248 } else {
249 Ok(())
250 };
251 loop_result?;
252 stop_result
253 })
254}
255
256pub struct FipsEndpoint {
258 identity: PeerIdentity,
259 npub: String,
260 node_addr: NodeAddr,
261 address: FipsAddress,
262 discovery_scope: Option<String>,
263 local_capability_directory: crate::discovery::local::LocalCapabilityDirectory,
264 outbound_packets: TunOutboundTx,
265 delivered_packets: Arc<Mutex<mpsc::Receiver<NodeDeliveredPacket>>>,
266 endpoint_control_tx: mpsc::Sender<NodeEndpointControlCommand>,
267 endpoint_data_batches: EndpointDataBatchTx,
268 inbound_endpoint_tx: EndpointEventSender,
274 inbound_endpoint_rx: Arc<Mutex<EndpointReceiveState>>,
281 inbound_service_tx: EndpointServiceEventSender,
282 inbound_service_rx: Arc<Mutex<ServiceReceiveState>>,
283 registered_services: Arc<StdMutex<HashMap<u16, EndpointServiceEventSender>>>,
284 service_channel_capacity: usize,
285 shutdown_tx: StdMutex<Option<oneshot::Sender<()>>>,
286 task: StdMutex<Option<JoinHandle<Result<(), NodeError>>>>,
287}
288
289impl FipsEndpoint {
290 pub fn builder() -> FipsEndpointBuilder {
292 FipsEndpointBuilder::default()
293 }
294
295 async fn control<T>(
296 &self,
297 operation: &'static str,
298 command: NodeEndpointControlCommand,
299 response_rx: oneshot::Receiver<T>,
300 ) -> Result<T, FipsEndpointError> {
301 tokio::time::timeout(ENDPOINT_OPERATION_TIMEOUT, async {
302 self.endpoint_control_tx
303 .send(command)
304 .await
305 .map_err(|_| FipsEndpointError::Closed)?;
306 response_rx.await.map_err(|_| FipsEndpointError::Closed)
307 })
308 .await
309 .map_err(|_| FipsEndpointError::Timeout { operation })?
310 }
311
312 pub fn npub(&self) -> &str {
314 &self.npub
315 }
316
317 pub fn node_addr(&self) -> &NodeAddr {
319 &self.node_addr
320 }
321
322 pub fn address(&self) -> FipsAddress {
324 self.address
325 }
326
327 pub fn discovery_scope(&self) -> Option<&str> {
329 self.discovery_scope.as_deref()
330 }
331
332 pub fn local_instance_advertisements(
336 &self,
337 ) -> Result<
338 Vec<crate::discovery::local::LocalInstanceAdvertisement>,
339 crate::discovery::local::LocalInstanceRegistryError,
340 > {
341 Ok(self.local_capability_directory.snapshot())
342 }
343
344 pub async fn send_batch_to_peer(
352 &self,
353 remote: PeerIdentity,
354 payloads: Vec<Vec<u8>>,
355 ) -> Result<(), FipsEndpointError> {
356 self.send_payloads_to_peer(remote, payloads)
357 }
358
359 pub async fn register_service(&self, port: u16) -> Result<(), FipsEndpointError> {
364 self.register_service_with_sender(port, self.inbound_service_tx.clone(), None)
365 .await
366 }
367
368 pub async fn register_service_receiver(
370 &self,
371 port: u16,
372 ) -> Result<FipsEndpointServiceReceiver, FipsEndpointError> {
373 let (sender, receiver) = EndpointServiceEventSender::channel(self.service_channel_capacity);
374 self.register_service_with_sender(port, sender, None)
375 .await?;
376 Ok(FipsEndpointServiceReceiver {
377 state: Mutex::new(ServiceReceiveState::new(receiver)),
378 })
379 }
380
381 pub async fn register_service_receiver_with_capability(
384 &self,
385 mut capability: crate::discovery::local::LocalInstanceCapability,
386 ) -> Result<FipsEndpointServiceReceiver, LocalServiceRegistrationError> {
387 let Some(port) = capability.fsp_port else {
388 return Err(LocalServiceRegistrationError::ServiceCapabilityMissingPort);
389 };
390 if capability.name.trim().is_empty() {
391 return Err(LocalServiceRegistrationError::ServiceCapabilityNameEmpty);
392 }
393 capability.name = capability.name.trim().to_string();
394 if !crate::discovery::local_udp::local_capability_name_is_valid(&capability.name) {
395 return Err(
396 LocalServiceRegistrationError::ServiceCapabilityNameTooLong {
397 max: crate::discovery::local_udp::LOCAL_CAPABILITY_MAX_NAME_BYTES,
398 },
399 );
400 }
401 let (sender, receiver) = EndpointServiceEventSender::channel(self.service_channel_capacity);
402 self.register_service_with_sender(port, sender, Some(capability))
403 .await?;
404 Ok(FipsEndpointServiceReceiver {
405 state: Mutex::new(ServiceReceiveState::new(receiver)),
406 })
407 }
408
409 async fn register_service_with_sender(
410 &self,
411 port: u16,
412 sender: EndpointServiceEventSender,
413 capability: Option<crate::discovery::local::LocalInstanceCapability>,
414 ) -> Result<(), FipsEndpointError> {
415 if port == crate::node::session_wire::FSP_PORT_IPV6_SHIM
416 || port == crate::transport::link_negotiation::LINK_NEGOTIATION_SERVICE_PORT
417 || port == crate::discovery::local_udp::LOCAL_CAPABILITY_FSP_PORT
418 {
419 return Err(FipsEndpointError::ServicePortReserved { port });
420 }
421
422 let (response_tx, response_rx) = oneshot::channel();
423 if !self
424 .control(
425 "service registration",
426 NodeEndpointControlCommand::RegisterService {
427 port,
428 sender: sender.clone(),
429 capability,
430 response_tx,
431 },
432 response_rx,
433 )
434 .await?
435 {
436 return Err(FipsEndpointError::ServicePortAlreadyRegistered { port });
437 }
438 self.registered_services
439 .lock()
440 .map_err(|_| FipsEndpointError::Closed)?
441 .insert(port, sender);
442 Ok(())
443 }
444
445 pub async fn send_datagram(
447 &self,
448 remote: PeerIdentity,
449 source_port: u16,
450 destination_port: u16,
451 payload: Vec<u8>,
452 ) -> Result<(), FipsEndpointError> {
453 self.send_service_datagrams_to_peer(
454 remote,
455 vec![FipsEndpointOutboundDatagram::new(
456 source_port,
457 destination_port,
458 payload,
459 )],
460 )
461 }
462
463 pub async fn send_datagram_batch_to_peer(
465 &self,
466 remote: PeerIdentity,
467 datagrams: Vec<FipsEndpointOutboundDatagram>,
468 ) -> Result<(), FipsEndpointError> {
469 self.send_service_datagrams_to_peer(remote, datagrams)
470 }
471
472 fn send_service_datagrams_to_peer(
473 &self,
474 remote: PeerIdentity,
475 datagrams: Vec<FipsEndpointOutboundDatagram>,
476 ) -> Result<(), FipsEndpointError> {
477 let max = crate::node::session_wire::fsp_service_datagram_max_body_len();
478 if let Some(datagram) = datagrams.iter().find(|datagram| datagram.data.len() > max) {
479 return Err(FipsEndpointError::ServiceDatagramTooLarge {
480 len: datagram.data.len(),
481 max,
482 });
483 }
484 if datagrams.is_empty() {
485 return Ok(());
486 }
487
488 if *remote.node_addr() == self.node_addr {
489 let deliveries_by_port = {
490 let mut registered = self
491 .registered_services
492 .lock()
493 .map_err(|_| FipsEndpointError::Closed)?;
494 registered.retain(|_, sender| !sender.is_closed());
495 let mut grouped: HashMap<
496 u16,
497 (
498 EndpointServiceEventSender,
499 Vec<crate::node::EndpointServiceDatagramDelivery>,
500 ),
501 > = HashMap::new();
502 for datagram in datagrams {
503 let Some(sender) = registered.get(&datagram.destination_port) else {
504 continue;
505 };
506 grouped
507 .entry(datagram.destination_port)
508 .or_insert_with(|| (sender.clone(), Vec::new()))
509 .1
510 .push(crate::node::EndpointServiceDatagramDelivery::new(
511 self.identity,
512 datagram.source_port,
513 datagram.destination_port,
514 crate::transport::PacketBuffer::new(datagram.data),
515 ));
516 }
517 grouped
518 };
519 for (_, (sender, deliveries)) in deliveries_by_port {
520 sender
521 .send(deliveries)
522 .map_err(|_| FipsEndpointError::Closed)?;
523 }
524 return Ok(());
525 }
526
527 self.send_endpoint_data_batch(remote, service_datagram_payloads(datagrams)?)
528 }
529
530 fn send_payloads_to_peer(
531 &self,
532 remote: PeerIdentity,
533 payloads: Vec<Vec<u8>>,
534 ) -> Result<(), FipsEndpointError> {
535 let payloads = endpoint_data_payloads_from_vecs(payloads)?;
536 if *remote.node_addr() == self.node_addr {
537 for payload in payloads {
538 self.send_loopback(payload)?;
539 }
540 return Ok(());
541 }
542
543 self.send_endpoint_data_batch(remote, payloads)
544 }
545
546 fn send_endpoint_data_batch(
547 &self,
548 remote: PeerIdentity,
549 payloads: Vec<EndpointDataPayload>,
550 ) -> Result<(), FipsEndpointError> {
551 if payloads.is_empty() {
552 return Ok(());
553 }
554
555 if payloads.len() <= ENDPOINT_DATA_BATCH_MAX {
556 self.enqueue_endpoint_data_batch(remote, payloads)?;
557 return Ok(());
558 }
559
560 let mut payloads = payloads.into_iter();
561 loop {
562 let payload_batch: Vec<_> = payloads.by_ref().take(ENDPOINT_DATA_BATCH_MAX).collect();
563 if payload_batch.is_empty() {
564 break;
565 }
566 self.enqueue_endpoint_data_batch(remote, payload_batch)?;
567 }
568 Ok(())
569 }
570
571 fn enqueue_endpoint_data_batch(
572 &self,
573 remote: PeerIdentity,
574 payload_batch: Vec<EndpointDataPayload>,
575 ) -> Result<(), FipsEndpointError> {
576 if let Some(batch) = NodeEndpointDataBatch::from_payloads(
581 remote,
582 payload_batch,
583 crate::perf_profile::stamp(),
584 ) {
585 self.endpoint_data_batches
586 .send_or_drop(batch)
587 .map_err(|_| FipsEndpointError::Closed)?;
588 }
589 Ok(())
590 }
591
592 fn send_loopback(&self, payload: EndpointDataPayload) -> Result<(), FipsEndpointError> {
593 self.inbound_endpoint_tx
594 .send(NodeEndpointEvent {
595 messages: vec![crate::node::EndpointDataDelivery::new(
596 self.identity,
597 payload.into_body(),
598 )],
599 queued_at: crate::perf_profile::stamp(),
600 })
601 .map_err(|_| FipsEndpointError::Closed)
602 }
603
604 pub async fn recv_batch_into(
611 &self,
612 messages: &mut Vec<FipsEndpointMessage>,
613 max: usize,
614 ) -> Option<usize> {
615 let max = max.clamp(1, ENDPOINT_RECV_BATCH_MAX);
616 messages.clear();
617
618 let mut state = self.inbound_endpoint_rx.lock().await;
619 state.drain_pending_into(messages, max);
620
621 while messages.len() < max {
622 let event = if messages.is_empty() {
623 state.rx.recv().await?
624 } else {
625 match state.rx.try_recv() {
626 Ok(event) => event,
627 Err(_) => break,
628 }
629 };
630 state.push_event_into(event, messages, max);
631 }
632
633 Some(messages.len())
634 }
635
636 pub async fn recv_service_datagram_batch_into(
638 &self,
639 datagrams: &mut Vec<FipsEndpointServiceDatagram>,
640 max: usize,
641 ) -> Option<usize> {
642 let max = max.clamp(1, ENDPOINT_RECV_BATCH_MAX);
643 datagrams.clear();
644
645 let mut state = self.inbound_service_rx.lock().await;
646 state.drain_pending_into(datagrams, max);
647 while datagrams.len() < max {
648 let event = if datagrams.is_empty() {
649 state.rx.recv().await?
650 } else {
651 match state.rx.try_recv() {
652 Ok(event) => event,
653 Err(_) => break,
654 }
655 };
656 state.push_event_into(event, datagrams, max);
657 }
658 Some(datagrams.len())
659 }
660
661 pub fn blocking_send_batch_to_peer(
667 &self,
668 remote: PeerIdentity,
669 payloads: Vec<Vec<u8>>,
670 ) -> Result<(), FipsEndpointError> {
671 self.send_payloads_to_peer(remote, payloads)
672 }
673
674 pub fn blocking_send_datagram(
676 &self,
677 remote: PeerIdentity,
678 source_port: u16,
679 destination_port: u16,
680 payload: Vec<u8>,
681 ) -> Result<(), FipsEndpointError> {
682 self.send_service_datagrams_to_peer(
683 remote,
684 vec![FipsEndpointOutboundDatagram::new(
685 source_port,
686 destination_port,
687 payload,
688 )],
689 )
690 }
691
692 pub fn blocking_send_datagram_batch_to_peer(
694 &self,
695 remote: PeerIdentity,
696 datagrams: Vec<FipsEndpointOutboundDatagram>,
697 ) -> Result<(), FipsEndpointError> {
698 self.send_service_datagrams_to_peer(remote, datagrams)
699 }
700
701 pub fn blocking_recv_batch_into(
709 &self,
710 messages: &mut Vec<FipsEndpointMessage>,
711 max: usize,
712 ) -> Option<usize> {
713 let max = max.clamp(1, ENDPOINT_RECV_BATCH_MAX);
714 messages.clear();
715
716 let mut state = self.inbound_endpoint_rx.blocking_lock();
717 state.drain_pending_into(messages, max);
718
719 while messages.len() < max {
720 let event = if messages.is_empty() {
721 state.rx.blocking_recv()?
722 } else {
723 match state.rx.try_recv() {
724 Ok(event) => event,
725 Err(_) => break,
726 }
727 };
728 state.push_event_into(event, messages, max);
729 }
730
731 Some(messages.len())
732 }
733
734 pub fn blocking_recv_service_datagram_batch_into(
736 &self,
737 datagrams: &mut Vec<FipsEndpointServiceDatagram>,
738 max: usize,
739 ) -> Option<usize> {
740 let max = max.clamp(1, ENDPOINT_RECV_BATCH_MAX);
741 datagrams.clear();
742
743 let mut state = self.inbound_service_rx.blocking_lock();
744 state.drain_pending_into(datagrams, max);
745 while datagrams.len() < max {
746 let event = if datagrams.is_empty() {
747 state.rx.blocking_recv()?
748 } else {
749 match state.rx.try_recv() {
750 Ok(event) => event,
751 Err(_) => break,
752 }
753 };
754 state.push_event_into(event, datagrams, max);
755 }
756 Some(datagrams.len())
757 }
758
759 pub async fn update_peers(
769 &self,
770 peers: Vec<crate::config::PeerConfig>,
771 ) -> Result<UpdatePeersOutcome, FipsEndpointError> {
772 let (response_tx, response_rx) = oneshot::channel();
773 match self
774 .control(
775 "peer update",
776 NodeEndpointControlCommand::UpdatePeers { peers, response_tx },
777 response_rx,
778 )
779 .await?
780 {
781 Ok(outcome) => Ok(UpdatePeersOutcome::from(outcome)),
782 Err(error) => Err(FipsEndpointError::Node(error)),
783 }
784 }
785
786 pub async fn refresh_peer_paths(
794 &self,
795 peers: Vec<PeerIdentity>,
796 ) -> Result<usize, FipsEndpointError> {
797 let (response_tx, response_rx) = oneshot::channel();
798 let npubs = peers.into_iter().map(|peer| peer.npub()).collect();
799 match self
800 .control(
801 "peer path refresh",
802 NodeEndpointControlCommand::RefreshPeerPaths { npubs, response_tx },
803 response_rx,
804 )
805 .await?
806 {
807 Ok(refreshed) => Ok(refreshed),
808 Err(error) => Err(FipsEndpointError::Node(error)),
809 }
810 }
811
812 pub async fn register_peer_identity(
818 &self,
819 identity: PeerIdentity,
820 ) -> Result<bool, FipsEndpointError> {
821 let (response_tx, response_rx) = oneshot::channel();
822 self.control(
823 "identity registration",
824 NodeEndpointControlCommand::RegisterIdentity {
825 identity,
826 response_tx,
827 },
828 response_rx,
829 )
830 .await
831 }
832
833 pub async fn peers(&self) -> Result<Vec<FipsEndpointPeer>, FipsEndpointError> {
835 let (response_tx, response_rx) = oneshot::channel();
836 self.control(
837 "peer snapshot",
838 NodeEndpointControlCommand::PeerSnapshot { response_tx },
839 response_rx,
840 )
841 .await
842 .map(|peers| peers.into_iter().map(FipsEndpointPeer::from).collect())
843 }
844
845 pub async fn send_ip_packet(
847 &self,
848 packet: impl Into<Vec<u8>>,
849 ) -> Result<(), FipsEndpointError> {
850 self.outbound_packets
851 .send(packet.into())
852 .await
853 .map_err(|_| FipsEndpointError::Closed)
854 }
855
856 pub fn blocking_send_ip_packet(
858 &self,
859 packet: impl Into<Vec<u8>>,
860 ) -> Result<(), FipsEndpointError> {
861 self.outbound_packets
862 .blocking_send(packet.into())
863 .map_err(|_| FipsEndpointError::Closed)
864 }
865
866 pub async fn recv_ip_packet(&self) -> Option<NodeDeliveredPacket> {
868 self.delivered_packets.lock().await.recv().await
869 }
870
871 pub async fn shutdown(&self) -> Result<(), FipsEndpointError> {
873 let shutdown_tx = self
874 .shutdown_tx
875 .lock()
876 .map_err(|_| FipsEndpointError::Closed)?
877 .take();
878 if let Some(shutdown_tx) = shutdown_tx {
879 let _ = shutdown_tx.send(());
880 }
881 let task = self
882 .task
883 .lock()
884 .map_err(|_| FipsEndpointError::Closed)?
885 .take();
886 if let Some(mut task) = task {
887 match tokio::time::timeout(ENDPOINT_OPERATION_TIMEOUT, &mut task).await {
888 Ok(result) => result??,
889 Err(_) => {
890 task.abort();
891 let _ = task.await;
892 return Err(FipsEndpointError::Timeout {
893 operation: "shutdown",
894 });
895 }
896 }
897 }
898 Ok(())
899 }
900}
901
902impl Drop for FipsEndpoint {
903 fn drop(&mut self) {
904 if let Ok(mut shutdown_tx) = self.shutdown_tx.lock()
905 && let Some(shutdown_tx) = shutdown_tx.take()
906 {
907 let _ = shutdown_tx.send(());
908 }
909 if let Ok(mut task) = self.task.lock()
910 && task.is_some()
911 {
912 drop(task.take());
916 }
917 }
918}