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