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
83#[derive(Debug, Error)]
85pub enum LocalServiceRegistrationError {
86 #[error("local FSP service capability must include a port")]
87 ServiceCapabilityMissingPort,
88
89 #[error("local FSP service capability name must not be empty")]
90 ServiceCapabilityNameEmpty,
91
92 #[error("local FSP service capability name exceeds {max} bytes")]
93 ServiceCapabilityNameTooLong { max: usize },
94
95 #[error(transparent)]
96 Endpoint(#[from] FipsEndpointError),
97}
98
99#[derive(Debug, Clone, PartialEq, Eq)]
101pub struct FipsEndpointMessage {
102 pub source_peer: PeerIdentity,
104 pub data: FipsEndpointData,
106 pub enqueued_at_ms: u64,
108}
109
110#[derive(Debug, Clone, PartialEq, Eq)]
112pub struct FipsEndpointOutboundDatagram {
113 pub source_port: u16,
114 pub destination_port: u16,
115 pub data: Vec<u8>,
116}
117
118impl FipsEndpointOutboundDatagram {
119 pub fn new(source_port: u16, destination_port: u16, data: Vec<u8>) -> Self {
120 Self {
121 source_port,
122 destination_port,
123 data,
124 }
125 }
126}
127
128#[derive(Debug, Clone, PartialEq, Eq)]
130pub struct FipsEndpointServiceDatagram {
131 pub source_peer: PeerIdentity,
132 pub source_port: u16,
133 pub destination_port: u16,
134 pub data: FipsEndpointData,
135 pub enqueued_at_ms: u64,
136}
137
138pub struct FipsEndpointServiceReceiver {
143 state: Mutex<ServiceReceiveState>,
144}
145
146#[derive(Debug, Clone, Default, PartialEq, Eq)]
148pub struct UpdatePeersOutcome {
149 pub added: usize,
152 pub removed: usize,
156 pub updated: usize,
161 pub unchanged: usize,
163}
164
165impl From<crate::node::UpdatePeersOutcome> for UpdatePeersOutcome {
166 fn from(value: crate::node::UpdatePeersOutcome) -> Self {
167 Self {
168 added: value.added,
169 removed: value.removed,
170 updated: value.updated,
171 unchanged: value.unchanged,
172 }
173 }
174}
175
176fn apply_default_scoped_discovery(config: &mut Config, scope: &str) {
177 if config.node.discovery.nostr.enabled || !config.transports.is_empty() {
178 return;
179 }
180
181 config.node.discovery.nostr.enabled = true;
182 config.node.discovery.nostr.advertise = true;
183 config.node.discovery.nostr.policy = NostrDiscoveryPolicy::Open;
184 config.node.discovery.nostr.share_local_candidates = true;
185 config.node.discovery.nostr.app = scope.to_string();
186 config.node.discovery.lan.scope = Some(scope.to_string());
187 config.node.discovery.local.enabled = true;
188 config.transports.udp = TransportInstances::Single(UdpConfig {
189 bind_addr: Some("0.0.0.0:0".to_string()),
190 advertise_on_nostr: Some(true),
191 public: Some(false),
192 outbound_only: Some(false),
193 accept_connections: Some(true),
194 ..UdpConfig::default()
195 });
196}
197
198fn endpoint_data_payloads_from_vecs(
199 payloads: Vec<Vec<u8>>,
200) -> Result<Vec<EndpointDataPayload>, FipsEndpointError> {
201 let mut converted = Vec::with_capacity(payloads.len());
202 for payload in payloads {
203 let len = payload.len();
204 let Some(payload) = EndpointDataPayload::from_packet_payload(payload) else {
205 let max = crate::node::session_wire::fsp_endpoint_data_max_body_len();
206 return Err(FipsEndpointError::EndpointDataTooLarge { len, max });
207 };
208 converted.push(payload);
209 }
210 Ok(converted)
211}
212
213fn service_datagram_payloads(
214 datagrams: Vec<FipsEndpointOutboundDatagram>,
215) -> Result<Vec<EndpointDataPayload>, FipsEndpointError> {
216 let max = crate::node::session_wire::fsp_service_datagram_max_body_len();
217 let mut payloads = Vec::with_capacity(datagrams.len());
218 for datagram in datagrams {
219 let len = datagram.data.len();
220 let Some(payload) = EndpointDataPayload::from_service_datagram(
221 datagram.source_port,
222 datagram.destination_port,
223 datagram.data,
224 ) else {
225 return Err(FipsEndpointError::ServiceDatagramTooLarge { len, max });
226 };
227 payloads.push(payload);
228 }
229 Ok(payloads)
230}
231
232fn spawn_node_task(
233 mut node: Node,
234 shutdown_rx: oneshot::Receiver<()>,
235) -> JoinHandle<Result<(), NodeError>> {
236 tokio::spawn(async move {
237 tokio::pin!(shutdown_rx);
238 let loop_result = tokio::select! {
239 result = node.run_rx_loop() => result,
240 _ = &mut shutdown_rx => Ok(()),
241 };
242 let stop_result = if node.state().can_stop() {
243 node.stop().await
244 } else {
245 Ok(())
246 };
247 loop_result?;
248 stop_result
249 })
250}
251
252pub struct FipsEndpoint {
254 identity: PeerIdentity,
255 npub: String,
256 node_addr: NodeAddr,
257 address: FipsAddress,
258 discovery_scope: Option<String>,
259 local_capability_directory: crate::discovery::local::LocalCapabilityDirectory,
260 outbound_packets: TunOutboundTx,
261 delivered_packets: Arc<Mutex<mpsc::Receiver<NodeDeliveredPacket>>>,
262 endpoint_control_tx: mpsc::Sender<NodeEndpointControlCommand>,
263 endpoint_data_batches: EndpointDataBatchTx,
264 inbound_endpoint_tx: EndpointEventSender,
270 inbound_endpoint_rx: Arc<Mutex<EndpointReceiveState>>,
277 inbound_service_tx: EndpointServiceEventSender,
278 inbound_service_rx: Arc<Mutex<ServiceReceiveState>>,
279 registered_services: Arc<StdMutex<HashMap<u16, EndpointServiceEventSender>>>,
280 service_channel_capacity: usize,
281 shutdown_tx: StdMutex<Option<oneshot::Sender<()>>>,
282 task: StdMutex<Option<JoinHandle<Result<(), NodeError>>>>,
283}
284
285impl FipsEndpoint {
286 pub fn builder() -> FipsEndpointBuilder {
288 FipsEndpointBuilder::default()
289 }
290
291 async fn control<T>(
292 &self,
293 operation: &'static str,
294 command: NodeEndpointControlCommand,
295 response_rx: oneshot::Receiver<T>,
296 ) -> Result<T, FipsEndpointError> {
297 tokio::time::timeout(ENDPOINT_OPERATION_TIMEOUT, async {
298 self.endpoint_control_tx
299 .send(command)
300 .await
301 .map_err(|_| FipsEndpointError::Closed)?;
302 response_rx.await.map_err(|_| FipsEndpointError::Closed)
303 })
304 .await
305 .map_err(|_| FipsEndpointError::Timeout { operation })?
306 }
307
308 pub fn npub(&self) -> &str {
310 &self.npub
311 }
312
313 pub fn node_addr(&self) -> &NodeAddr {
315 &self.node_addr
316 }
317
318 pub fn address(&self) -> FipsAddress {
320 self.address
321 }
322
323 pub fn discovery_scope(&self) -> Option<&str> {
325 self.discovery_scope.as_deref()
326 }
327
328 pub fn local_instance_advertisements(
332 &self,
333 ) -> Result<
334 Vec<crate::discovery::local::LocalInstanceAdvertisement>,
335 crate::discovery::local::LocalInstanceRegistryError,
336 > {
337 Ok(self.local_capability_directory.snapshot())
338 }
339
340 pub async fn send_batch_to_peer(
348 &self,
349 remote: PeerIdentity,
350 payloads: Vec<Vec<u8>>,
351 ) -> Result<(), FipsEndpointError> {
352 self.send_payloads_to_peer(remote, payloads)
353 }
354
355 pub async fn register_service(&self, port: u16) -> Result<(), FipsEndpointError> {
360 self.register_service_with_sender(port, self.inbound_service_tx.clone(), None)
361 .await
362 }
363
364 pub async fn register_service_receiver(
366 &self,
367 port: u16,
368 ) -> Result<FipsEndpointServiceReceiver, FipsEndpointError> {
369 let (sender, receiver) = EndpointServiceEventSender::channel(self.service_channel_capacity);
370 self.register_service_with_sender(port, sender, None)
371 .await?;
372 Ok(FipsEndpointServiceReceiver {
373 state: Mutex::new(ServiceReceiveState::new(receiver)),
374 })
375 }
376
377 pub async fn register_service_receiver_with_capability(
380 &self,
381 mut capability: crate::discovery::local::LocalInstanceCapability,
382 ) -> Result<FipsEndpointServiceReceiver, LocalServiceRegistrationError> {
383 let Some(port) = capability.fsp_port else {
384 return Err(LocalServiceRegistrationError::ServiceCapabilityMissingPort);
385 };
386 if capability.name.trim().is_empty() {
387 return Err(LocalServiceRegistrationError::ServiceCapabilityNameEmpty);
388 }
389 capability.name = capability.name.trim().to_string();
390 if !crate::discovery::local_udp::local_capability_name_is_valid(&capability.name) {
391 return Err(
392 LocalServiceRegistrationError::ServiceCapabilityNameTooLong {
393 max: crate::discovery::local_udp::LOCAL_CAPABILITY_MAX_NAME_BYTES,
394 },
395 );
396 }
397 let (sender, receiver) = EndpointServiceEventSender::channel(self.service_channel_capacity);
398 self.register_service_with_sender(port, sender, Some(capability))
399 .await?;
400 Ok(FipsEndpointServiceReceiver {
401 state: Mutex::new(ServiceReceiveState::new(receiver)),
402 })
403 }
404
405 async fn register_service_with_sender(
406 &self,
407 port: u16,
408 sender: EndpointServiceEventSender,
409 capability: Option<crate::discovery::local::LocalInstanceCapability>,
410 ) -> Result<(), FipsEndpointError> {
411 if port == crate::node::session_wire::FSP_PORT_IPV6_SHIM
412 || port == crate::transport::link_negotiation::LINK_NEGOTIATION_SERVICE_PORT
413 || port == crate::discovery::local_udp::LOCAL_CAPABILITY_FSP_PORT
414 {
415 return Err(FipsEndpointError::ServicePortReserved { port });
416 }
417
418 let (response_tx, response_rx) = oneshot::channel();
419 if !self
420 .control(
421 "service registration",
422 NodeEndpointControlCommand::RegisterService {
423 port,
424 sender: sender.clone(),
425 capability,
426 response_tx,
427 },
428 response_rx,
429 )
430 .await?
431 {
432 return Err(FipsEndpointError::ServicePortAlreadyRegistered { port });
433 }
434 self.registered_services
435 .lock()
436 .map_err(|_| FipsEndpointError::Closed)?
437 .insert(port, sender);
438 Ok(())
439 }
440
441 pub async fn send_datagram(
443 &self,
444 remote: PeerIdentity,
445 source_port: u16,
446 destination_port: u16,
447 payload: Vec<u8>,
448 ) -> Result<(), FipsEndpointError> {
449 self.send_service_datagrams_to_peer(
450 remote,
451 vec![FipsEndpointOutboundDatagram::new(
452 source_port,
453 destination_port,
454 payload,
455 )],
456 )
457 }
458
459 pub async fn send_datagram_batch_to_peer(
461 &self,
462 remote: PeerIdentity,
463 datagrams: Vec<FipsEndpointOutboundDatagram>,
464 ) -> Result<(), FipsEndpointError> {
465 self.send_service_datagrams_to_peer(remote, datagrams)
466 }
467
468 fn send_service_datagrams_to_peer(
469 &self,
470 remote: PeerIdentity,
471 datagrams: Vec<FipsEndpointOutboundDatagram>,
472 ) -> Result<(), FipsEndpointError> {
473 let max = crate::node::session_wire::fsp_service_datagram_max_body_len();
474 if let Some(datagram) = datagrams.iter().find(|datagram| datagram.data.len() > max) {
475 return Err(FipsEndpointError::ServiceDatagramTooLarge {
476 len: datagram.data.len(),
477 max,
478 });
479 }
480 if datagrams.is_empty() {
481 return Ok(());
482 }
483
484 if *remote.node_addr() == self.node_addr {
485 let deliveries_by_port = {
486 let mut registered = self
487 .registered_services
488 .lock()
489 .map_err(|_| FipsEndpointError::Closed)?;
490 registered.retain(|_, sender| !sender.is_closed());
491 let mut grouped: HashMap<
492 u16,
493 (
494 EndpointServiceEventSender,
495 Vec<crate::node::EndpointServiceDatagramDelivery>,
496 ),
497 > = HashMap::new();
498 for datagram in datagrams {
499 let Some(sender) = registered.get(&datagram.destination_port) else {
500 continue;
501 };
502 grouped
503 .entry(datagram.destination_port)
504 .or_insert_with(|| (sender.clone(), Vec::new()))
505 .1
506 .push(crate::node::EndpointServiceDatagramDelivery::new(
507 self.identity,
508 datagram.source_port,
509 datagram.destination_port,
510 crate::transport::PacketBuffer::new(datagram.data),
511 ));
512 }
513 grouped
514 };
515 for (_, (sender, deliveries)) in deliveries_by_port {
516 sender
517 .send(deliveries)
518 .map_err(|_| FipsEndpointError::Closed)?;
519 }
520 return Ok(());
521 }
522
523 self.send_endpoint_data_batch(remote, service_datagram_payloads(datagrams)?)
524 }
525
526 fn send_payloads_to_peer(
527 &self,
528 remote: PeerIdentity,
529 payloads: Vec<Vec<u8>>,
530 ) -> Result<(), FipsEndpointError> {
531 let payloads = endpoint_data_payloads_from_vecs(payloads)?;
532 if *remote.node_addr() == self.node_addr {
533 for payload in payloads {
534 self.send_loopback(payload)?;
535 }
536 return Ok(());
537 }
538
539 self.send_endpoint_data_batch(remote, payloads)
540 }
541
542 fn send_endpoint_data_batch(
543 &self,
544 remote: PeerIdentity,
545 payloads: Vec<EndpointDataPayload>,
546 ) -> Result<(), FipsEndpointError> {
547 if payloads.is_empty() {
548 return Ok(());
549 }
550
551 if payloads.len() <= ENDPOINT_DATA_BATCH_MAX {
552 self.enqueue_endpoint_data_batch(remote, payloads)?;
553 return Ok(());
554 }
555
556 let mut payloads = payloads.into_iter();
557 loop {
558 let payload_batch: Vec<_> = payloads.by_ref().take(ENDPOINT_DATA_BATCH_MAX).collect();
559 if payload_batch.is_empty() {
560 break;
561 }
562 self.enqueue_endpoint_data_batch(remote, payload_batch)?;
563 }
564 Ok(())
565 }
566
567 fn enqueue_endpoint_data_batch(
568 &self,
569 remote: PeerIdentity,
570 payload_batch: Vec<EndpointDataPayload>,
571 ) -> Result<(), FipsEndpointError> {
572 if let Some(batch) = NodeEndpointDataBatch::from_payloads(
577 remote,
578 payload_batch,
579 crate::perf_profile::stamp(),
580 ) {
581 self.endpoint_data_batches
582 .send_or_drop(batch)
583 .map_err(|_| FipsEndpointError::Closed)?;
584 }
585 Ok(())
586 }
587
588 fn send_loopback(&self, payload: EndpointDataPayload) -> Result<(), FipsEndpointError> {
589 self.inbound_endpoint_tx
590 .send(NodeEndpointEvent {
591 messages: vec![crate::node::EndpointDataDelivery::new(
592 self.identity,
593 payload.into_body(),
594 )],
595 queued_at: crate::perf_profile::stamp(),
596 })
597 .map_err(|_| FipsEndpointError::Closed)
598 }
599
600 pub async fn recv_batch_into(
607 &self,
608 messages: &mut Vec<FipsEndpointMessage>,
609 max: usize,
610 ) -> Option<usize> {
611 let max = max.clamp(1, ENDPOINT_RECV_BATCH_MAX);
612 messages.clear();
613
614 let mut state = self.inbound_endpoint_rx.lock().await;
615 state.drain_pending_into(messages, max);
616
617 while messages.len() < max {
618 let event = if messages.is_empty() {
619 state.rx.recv().await?
620 } else {
621 match state.rx.try_recv() {
622 Ok(event) => event,
623 Err(_) => break,
624 }
625 };
626 state.push_event_into(event, messages, max);
627 }
628
629 Some(messages.len())
630 }
631
632 pub async fn recv_service_datagram_batch_into(
634 &self,
635 datagrams: &mut Vec<FipsEndpointServiceDatagram>,
636 max: usize,
637 ) -> Option<usize> {
638 let max = max.clamp(1, ENDPOINT_RECV_BATCH_MAX);
639 datagrams.clear();
640
641 let mut state = self.inbound_service_rx.lock().await;
642 state.drain_pending_into(datagrams, max);
643 while datagrams.len() < max {
644 let event = if datagrams.is_empty() {
645 state.rx.recv().await?
646 } else {
647 match state.rx.try_recv() {
648 Ok(event) => event,
649 Err(_) => break,
650 }
651 };
652 state.push_event_into(event, datagrams, max);
653 }
654 Some(datagrams.len())
655 }
656
657 pub fn blocking_send_batch_to_peer(
663 &self,
664 remote: PeerIdentity,
665 payloads: Vec<Vec<u8>>,
666 ) -> Result<(), FipsEndpointError> {
667 self.send_payloads_to_peer(remote, payloads)
668 }
669
670 pub fn blocking_send_datagram(
672 &self,
673 remote: PeerIdentity,
674 source_port: u16,
675 destination_port: u16,
676 payload: Vec<u8>,
677 ) -> Result<(), FipsEndpointError> {
678 self.send_service_datagrams_to_peer(
679 remote,
680 vec![FipsEndpointOutboundDatagram::new(
681 source_port,
682 destination_port,
683 payload,
684 )],
685 )
686 }
687
688 pub fn blocking_send_datagram_batch_to_peer(
690 &self,
691 remote: PeerIdentity,
692 datagrams: Vec<FipsEndpointOutboundDatagram>,
693 ) -> Result<(), FipsEndpointError> {
694 self.send_service_datagrams_to_peer(remote, datagrams)
695 }
696
697 pub fn blocking_recv_batch_into(
705 &self,
706 messages: &mut Vec<FipsEndpointMessage>,
707 max: usize,
708 ) -> Option<usize> {
709 let max = max.clamp(1, ENDPOINT_RECV_BATCH_MAX);
710 messages.clear();
711
712 let mut state = self.inbound_endpoint_rx.blocking_lock();
713 state.drain_pending_into(messages, max);
714
715 while messages.len() < max {
716 let event = if messages.is_empty() {
717 state.rx.blocking_recv()?
718 } else {
719 match state.rx.try_recv() {
720 Ok(event) => event,
721 Err(_) => break,
722 }
723 };
724 state.push_event_into(event, messages, max);
725 }
726
727 Some(messages.len())
728 }
729
730 pub fn blocking_recv_service_datagram_batch_into(
732 &self,
733 datagrams: &mut Vec<FipsEndpointServiceDatagram>,
734 max: usize,
735 ) -> Option<usize> {
736 let max = max.clamp(1, ENDPOINT_RECV_BATCH_MAX);
737 datagrams.clear();
738
739 let mut state = self.inbound_service_rx.blocking_lock();
740 state.drain_pending_into(datagrams, max);
741 while datagrams.len() < max {
742 let event = if datagrams.is_empty() {
743 state.rx.blocking_recv()?
744 } else {
745 match state.rx.try_recv() {
746 Ok(event) => event,
747 Err(_) => break,
748 }
749 };
750 state.push_event_into(event, datagrams, max);
751 }
752 Some(datagrams.len())
753 }
754
755 pub async fn update_peers(
765 &self,
766 peers: Vec<crate::config::PeerConfig>,
767 ) -> Result<UpdatePeersOutcome, FipsEndpointError> {
768 let (response_tx, response_rx) = oneshot::channel();
769 match self
770 .control(
771 "peer update",
772 NodeEndpointControlCommand::UpdatePeers { peers, response_tx },
773 response_rx,
774 )
775 .await?
776 {
777 Ok(outcome) => Ok(UpdatePeersOutcome::from(outcome)),
778 Err(error) => Err(FipsEndpointError::Node(error)),
779 }
780 }
781
782 pub async fn refresh_peer_paths(
790 &self,
791 peers: Vec<PeerIdentity>,
792 ) -> Result<usize, FipsEndpointError> {
793 let (response_tx, response_rx) = oneshot::channel();
794 let npubs = peers.into_iter().map(|peer| peer.npub()).collect();
795 match self
796 .control(
797 "peer path refresh",
798 NodeEndpointControlCommand::RefreshPeerPaths { npubs, response_tx },
799 response_rx,
800 )
801 .await?
802 {
803 Ok(refreshed) => Ok(refreshed),
804 Err(error) => Err(FipsEndpointError::Node(error)),
805 }
806 }
807
808 pub async fn peers(&self) -> Result<Vec<FipsEndpointPeer>, FipsEndpointError> {
810 let (response_tx, response_rx) = oneshot::channel();
811 self.control(
812 "peer snapshot",
813 NodeEndpointControlCommand::PeerSnapshot { response_tx },
814 response_rx,
815 )
816 .await
817 .map(|peers| peers.into_iter().map(FipsEndpointPeer::from).collect())
818 }
819
820 pub async fn send_ip_packet(
822 &self,
823 packet: impl Into<Vec<u8>>,
824 ) -> Result<(), FipsEndpointError> {
825 self.outbound_packets
826 .send(packet.into())
827 .await
828 .map_err(|_| FipsEndpointError::Closed)
829 }
830
831 pub async fn recv_ip_packet(&self) -> Option<NodeDeliveredPacket> {
833 self.delivered_packets.lock().await.recv().await
834 }
835
836 pub async fn shutdown(&self) -> Result<(), FipsEndpointError> {
838 let shutdown_tx = self
839 .shutdown_tx
840 .lock()
841 .map_err(|_| FipsEndpointError::Closed)?
842 .take();
843 if let Some(shutdown_tx) = shutdown_tx {
844 let _ = shutdown_tx.send(());
845 }
846 let task = self
847 .task
848 .lock()
849 .map_err(|_| FipsEndpointError::Closed)?
850 .take();
851 if let Some(mut task) = task {
852 match tokio::time::timeout(ENDPOINT_OPERATION_TIMEOUT, &mut task).await {
853 Ok(result) => result??,
854 Err(_) => {
855 task.abort();
856 let _ = task.await;
857 return Err(FipsEndpointError::Timeout {
858 operation: "shutdown",
859 });
860 }
861 }
862 }
863 Ok(())
864 }
865}
866
867impl Drop for FipsEndpoint {
868 fn drop(&mut self) {
869 if let Ok(mut shutdown_tx) = self.shutdown_tx.lock()
870 && let Some(shutdown_tx) = shutdown_tx.take()
871 {
872 let _ = shutdown_tx.send(());
873 }
874 if let Ok(mut task) = self.task.lock()
875 && task.is_some()
876 {
877 drop(task.take());
881 }
882 }
883}