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