1use std::collections::HashSet;
6use std::io;
7use std::path::Path;
8use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
9use std::sync::Arc;
10use std::thread::{self, JoinHandle};
11use std::time::Duration;
12
13use rns_core::transport::announce_verify_queue::OverflowPolicy as AnnounceQueueOverflowPolicy;
14use rns_core::transport::types::TransportConfig;
15use rns_crypto::identity::Identity;
16use rns_crypto::{OsRng, Rng};
17
18use crate::config;
19#[cfg(feature = "iface-backbone")]
20use crate::driver::{BackbonePeerPoolCandidateConfig, BackbonePeerPoolSettings};
21use crate::driver::{Callbacks, Driver};
22use crate::event::{self, Event, EventSender};
23use crate::ifac;
24#[cfg(feature = "iface-auto")]
25use crate::interface::auto::{auto_runtime_handle_from_config, AutoConfig};
26#[cfg(feature = "iface-backbone")]
27use crate::interface::backbone::{
28 client_config_from_mode, client_runtime_handle_from_mode, peer_state_handle_from_mode,
29 runtime_handle_from_mode, BackboneMode,
30};
31#[cfg(feature = "iface-i2p")]
32use crate::interface::i2p::{i2p_runtime_handle_from_config, I2pConfig};
33#[cfg(feature = "iface-local")]
34use crate::interface::local::LocalServerConfig;
35#[cfg(feature = "iface-pipe")]
36use crate::interface::pipe::{pipe_runtime_handle_from_config, PipeConfig};
37#[cfg(feature = "iface-rnode")]
38use crate::interface::rnode::{rnode_runtime_handle_from_config, RNodeConfig};
39#[cfg(feature = "iface-tcp")]
40use crate::interface::tcp::{tcp_client_runtime_handle_from_config, TcpClientConfig};
41#[cfg(feature = "iface-tcp")]
42use crate::interface::tcp_server::{
43 runtime_handle_from_config as tcp_runtime_handle_from_config, TcpServerConfig,
44};
45#[cfg(feature = "iface-udp")]
46use crate::interface::udp::{udp_runtime_handle_from_config, UdpConfig};
47use crate::interface::{InterfaceEntry, InterfaceStats};
48use crate::storage;
49use crate::time;
50
51#[cfg(test)]
52const DEFAULT_KNOWN_DESTINATIONS_TTL: Duration = Duration::from_secs(48 * 60 * 60);
53const DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES: usize = 8192;
54
55fn parse_interface_mode(mode: &str) -> u8 {
58 match mode.to_lowercase().as_str() {
59 "full" => rns_core::constants::MODE_FULL,
60 "access_point" | "accesspoint" | "ap" => rns_core::constants::MODE_ACCESS_POINT,
61 "pointtopoint" | "ptp" => rns_core::constants::MODE_POINT_TO_POINT,
62 "roaming" => rns_core::constants::MODE_ROAMING,
63 "boundary" => rns_core::constants::MODE_BOUNDARY,
64 "gateway" | "gw" => rns_core::constants::MODE_GATEWAY,
65 _ => rns_core::constants::MODE_FULL,
66 }
67}
68
69fn default_ingress_control_for_type(
70 iface_type: &str,
71) -> rns_core::transport::types::IngressControlConfig {
72 match iface_type {
73 "AutoInterface" | "BackboneInterface" | "TCPClientInterface" | "TCPServerInterface"
74 | "UDPInterface" | "I2PInterface" => {
75 rns_core::transport::types::IngressControlConfig::enabled()
76 }
77 _ => rns_core::transport::types::IngressControlConfig::disabled(),
78 }
79}
80
81fn parse_ingress_control_config(
82 iface_type: &str,
83 params: &std::collections::HashMap<String, String>,
84) -> Result<rns_core::transport::types::IngressControlConfig, String> {
85 let mut config = default_ingress_control_for_type(iface_type);
86
87 if let Some(v) = params.get("ingress_control") {
88 config.enabled = config::parse_bool_pub(v)
89 .ok_or_else(|| format!("ingress_control must be a boolean, got '{}'", v))?;
90 }
91 if let Some(v) = params.get("ic_max_held_announces") {
92 config.max_held_announces = v
93 .parse::<usize>()
94 .map_err(|_| format!("ic_max_held_announces must be an integer, got '{}'", v))?;
95 }
96 if let Some(v) = params.get("ic_burst_hold") {
97 config.burst_hold = parse_nonnegative_f64("ic_burst_hold", v)?;
98 }
99 if let Some(v) = params.get("ic_burst_freq_new") {
100 config.burst_freq_new = parse_nonnegative_f64("ic_burst_freq_new", v)?;
101 }
102 if let Some(v) = params.get("ic_burst_freq") {
103 config.burst_freq = parse_nonnegative_f64("ic_burst_freq", v)?;
104 }
105 if let Some(v) = params.get("ic_new_time") {
106 config.new_time = parse_nonnegative_f64("ic_new_time", v)?;
107 }
108 if let Some(v) = params.get("ic_burst_penalty") {
109 config.burst_penalty = parse_nonnegative_f64("ic_burst_penalty", v)?;
110 }
111 if let Some(v) = params.get("ic_held_release_interval") {
112 config.held_release_interval = parse_nonnegative_f64("ic_held_release_interval", v)?;
113 }
114
115 Ok(config)
116}
117
118fn parse_nonnegative_f64(key: &str, value: &str) -> Result<f64, String> {
119 let parsed = value
120 .parse::<f64>()
121 .map_err(|_| format!("{} must be numeric, got '{}'", key, value))?;
122 if parsed < 0.0 {
123 return Err(format!("{} must be >= 0, got '{}'", key, value));
124 }
125 Ok(parsed)
126}
127
128fn extract_ifac_config(
131 params: &std::collections::HashMap<String, String>,
132 default_size: usize,
133) -> Option<IfacConfig> {
134 let netname = params
135 .get("networkname")
136 .or_else(|| params.get("network_name"))
137 .cloned();
138 let netkey = params
139 .get("passphrase")
140 .or_else(|| params.get("pass_phrase"))
141 .cloned();
142
143 if netname.is_none() && netkey.is_none() {
144 return None;
145 }
146
147 let size = params
149 .get("ifac_size")
150 .and_then(|v| v.parse::<usize>().ok())
151 .map(|bits| (bits / 8).max(1))
152 .unwrap_or(default_size);
153
154 Some(IfacConfig {
155 netname,
156 netkey,
157 size,
158 })
159}
160
161fn extract_discovery_config(
163 iface_name: &str,
164 iface_type: &str,
165 params: &std::collections::HashMap<String, String>,
166) -> Option<crate::discovery::DiscoveryConfig> {
167 let discoverable = params
168 .get("discoverable")
169 .and_then(|v| config::parse_bool_pub(v))
170 .unwrap_or(false);
171 if !discoverable {
172 return None;
173 }
174
175 if iface_type == "TCPClientInterface" {
176 log::error!(
177 "Invalid interface discovery configuration for {}, aborting discovery announce",
178 iface_name
179 );
180 return None;
181 }
182
183 let discovery_name = params
184 .get("discovery_name")
185 .cloned()
186 .unwrap_or_else(|| iface_name.to_string());
187
188 let announce_interval = params
190 .get("announce_interval")
191 .and_then(|v| v.parse::<u64>().ok())
192 .map(|secs| secs.max(300))
193 .unwrap_or(21600);
194
195 let stamp_value = params
196 .get("discovery_stamp_value")
197 .and_then(|v| v.parse::<u8>().ok())
198 .unwrap_or(crate::discovery::DEFAULT_STAMP_VALUE);
199
200 let reachable_on = params.get("reachable_on").cloned();
201
202 let listen_port = params
203 .get("listen_port")
204 .or_else(|| params.get("port"))
205 .and_then(|v| v.parse().ok());
206
207 let latitude = params
208 .get("latitude")
209 .or_else(|| params.get("lat"))
210 .and_then(|v| v.parse().ok());
211 let longitude = params
212 .get("longitude")
213 .or_else(|| params.get("lon"))
214 .and_then(|v| v.parse().ok());
215 let height = params.get("height").and_then(|v| v.parse().ok());
216
217 Some(crate::discovery::DiscoveryConfig {
218 discovery_name,
219 announce_interval,
220 stamp_value,
221 reachable_on,
222 interface_type: iface_type.to_string(),
223 listen_port,
224 latitude,
225 longitude,
226 height,
227 })
228}
229
230fn default_discovery_runtime_config(
231 interface_name: &str,
232 interface_type: &str,
233 listen_port: Option<u16>,
234) -> crate::discovery::DiscoveryConfig {
235 crate::discovery::DiscoveryConfig {
236 discovery_name: interface_name.to_string(),
237 announce_interval: 21600,
238 stamp_value: crate::discovery::DEFAULT_STAMP_VALUE,
239 reachable_on: None,
240 interface_type: interface_type.to_string(),
241 listen_port,
242 latitude: None,
243 longitude: None,
244 height: None,
245 }
246}
247
248fn discovery_runtime_ifac_fields(ifac: Option<&IfacConfig>) -> (Option<String>, Option<String>) {
249 (
250 ifac.and_then(|cfg| cfg.netname.clone()),
251 ifac.and_then(|cfg| cfg.netkey.clone()),
252 )
253}
254
255#[cfg(feature = "iface-backbone")]
256fn backbone_discovery_runtime_from_interface(
257 interface_name: &str,
258 mode: &BackboneMode,
259 discovery: Option<&crate::discovery::DiscoveryConfig>,
260 transport_enabled: bool,
261 ifac: Option<&IfacConfig>,
262) -> Option<crate::driver::BackboneDiscoveryRuntimeHandle> {
263 let config = match mode {
264 BackboneMode::Server(config) => config,
265 BackboneMode::Client(_) => return None,
266 };
267
268 let startup_config = discovery.cloned().unwrap_or_else(|| {
269 default_discovery_runtime_config(
270 interface_name,
271 "BackboneInterface",
272 Some(config.listen_port),
273 )
274 });
275 let (ifac_netname, ifac_netkey) = discovery_runtime_ifac_fields(ifac);
276
277 Some(crate::driver::BackboneDiscoveryRuntimeHandle::from_parts(
278 config.name.clone(),
279 startup_config,
280 transport_enabled,
281 ifac_netname,
282 ifac_netkey,
283 discovery.is_some(),
284 ))
285}
286
287#[cfg(feature = "iface-tcp")]
288fn tcp_server_discovery_runtime_from_interface(
289 interface_name: &str,
290 config: &crate::interface::tcp_server::TcpServerConfig,
291 discovery: Option<&crate::discovery::DiscoveryConfig>,
292 transport_enabled: bool,
293 ifac: Option<&IfacConfig>,
294) -> crate::driver::TcpServerDiscoveryRuntimeHandle {
295 let startup_config = discovery.cloned().unwrap_or_else(|| {
296 default_discovery_runtime_config(
297 interface_name,
298 "TCPServerInterface",
299 Some(config.listen_port),
300 )
301 });
302 let (ifac_netname, ifac_netkey) = discovery_runtime_ifac_fields(ifac);
303
304 crate::driver::TcpServerDiscoveryRuntimeHandle::from_parts(
305 config.name.clone(),
306 startup_config,
307 transport_enabled,
308 ifac_netname,
309 ifac_netkey,
310 discovery.is_some(),
311 )
312}
313
314fn ifac_runtime_from_config(
315 ifac: Option<&IfacConfig>,
316 default_size: usize,
317) -> crate::driver::IfacRuntimeConfig {
318 crate::driver::IfacRuntimeConfig::from_parts(
319 ifac.and_then(|cfg| cfg.netname.clone()),
320 ifac.and_then(|cfg| cfg.netkey.clone()),
321 ifac.map(|cfg| cfg.size).unwrap_or(default_size),
322 )
323}
324
325fn discoverable_interface_from_config(
326 interface_name: &str,
327 discovery: &crate::discovery::DiscoveryConfig,
328 transport_enabled: bool,
329 ifac: Option<&IfacConfig>,
330) -> crate::discovery::DiscoverableInterface {
331 crate::discovery::DiscoverableInterface {
332 interface_name: interface_name.to_string(),
333 config: discovery.clone(),
334 transport_enabled,
335 ifac_netname: ifac.and_then(|cfg| cfg.netname.clone()),
336 ifac_netkey: ifac.and_then(|cfg| cfg.netkey.clone()),
337 }
338}
339
340fn derive_ifac_state(
341 ifac: Option<&IfacConfig>,
342 interface_name: &str,
343) -> io::Result<Option<crate::ifac::IfacState>> {
344 let Some(ifac) = ifac else {
345 return Ok(None);
346 };
347 if ifac.netname.is_none() && ifac.netkey.is_none() {
348 return Ok(None);
349 }
350
351 ifac::derive_ifac(ifac.netname.as_deref(), ifac.netkey.as_deref(), ifac.size)
352 .map(Some)
353 .map_err(|err| {
354 io::Error::other(format!(
355 "failed to derive IFAC for {}: {}",
356 interface_name, err
357 ))
358 })
359}
360
361fn register_started_interface(
362 driver: &mut Driver,
363 tx: &EventSender,
364 queue_capacity: usize,
365 id: rns_core::transport::types::InterfaceId,
366 info: rns_core::transport::types::InterfaceInfo,
367 writer: Box<dyn crate::interface::Writer>,
368 interface_type_name: String,
369 ifac_state: Option<crate::ifac::IfacState>,
370 ifac_runtime: &crate::driver::IfacRuntimeConfig,
371) {
372 let (writer, async_writer_metrics) =
373 crate::interface::wrap_async_writer(writer, id, &info.name, tx.clone(), queue_capacity);
374 driver.register_interface_runtime_defaults(&info);
375 driver.register_interface_ifac_runtime(&info.name, ifac_runtime.clone());
376 driver.engine.register_interface(info.clone());
377 driver.interfaces.insert(
378 id,
379 InterfaceEntry {
380 id,
381 info,
382 writer,
383 async_writer_metrics: Some(async_writer_metrics),
384 enabled: true,
385 online: false,
386 dynamic: false,
387 ifac: ifac_state,
388 stats: InterfaceStats {
389 started: time::now(),
390 ..Default::default()
391 },
392 interface_type: interface_type_name,
393 send_retry_at: None,
394 send_retry_backoff: Duration::ZERO,
395 },
396 );
397}
398
399pub struct NodeConfig {
401 pub transport_enabled: bool,
402 pub identity: Option<Identity>,
403 pub interfaces: Vec<InterfaceConfig>,
405 pub share_instance: bool,
407 pub instance_name: String,
409 pub shared_instance_port: u16,
411 pub rpc_port: u16,
413 pub cache_dir: Option<std::path::PathBuf>,
415 pub ratchet_store: Option<Arc<dyn storage::RatchetStore>>,
417 pub ratchet_expiry: Duration,
419 pub management: crate::management::ManagementConfig,
421 pub probe_port: Option<u16>,
423 pub probe_addrs: Vec<std::net::SocketAddr>,
425 pub probe_protocol: rns_core::holepunch::ProbeProtocol,
427 pub device: Option<String>,
429 pub hooks: Vec<config::ParsedHook>,
431 pub discover_interfaces: bool,
433 pub discovery_required_value: Option<u8>,
435 pub respond_to_probes: bool,
437 pub prefer_shorter_path: bool,
441 pub max_paths_per_destination: usize,
444 pub packet_hashlist_max_entries: usize,
446 pub max_discovery_pr_tags: usize,
448 pub max_path_destinations: usize,
450 pub max_tunnel_destinations_total: usize,
452 pub known_destinations_ttl: Duration,
454 pub known_destinations_max_entries: usize,
456 pub announce_table_ttl: Duration,
458 pub announce_table_max_bytes: usize,
460 pub driver_event_queue_capacity: usize,
462 pub interface_writer_queue_capacity: usize,
464 #[cfg(feature = "iface-backbone")]
466 pub backbone_peer_pool: Option<BackbonePeerPoolSettings>,
467 pub announce_sig_cache_enabled: bool,
469 pub announce_sig_cache_max_entries: usize,
471 pub announce_sig_cache_ttl: Duration,
473 pub registry: Option<crate::interface::registry::InterfaceRegistry>,
475 pub panic_on_interface_error: bool,
478 #[cfg(feature = "hooks")]
480 pub provider_bridge: Option<crate::provider_bridge::ProviderBridgeConfig>,
481}
482
483impl Default for NodeConfig {
484 fn default() -> Self {
485 Self {
486 transport_enabled: false,
487 identity: None,
488 interfaces: Vec::new(),
489 share_instance: false,
490 instance_name: "default".into(),
491 shared_instance_port: 37428,
492 rpc_port: 0,
493 cache_dir: None,
494 ratchet_store: None,
495 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
496 management: Default::default(),
497 probe_port: None,
498 probe_addrs: vec![],
499 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
500 device: None,
501 hooks: Vec::new(),
502 discover_interfaces: false,
503 discovery_required_value: None,
504 respond_to_probes: false,
505 prefer_shorter_path: false,
506 max_paths_per_destination: 1,
507 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
508 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
509 max_path_destinations: rns_core::transport::types::DEFAULT_MAX_PATH_DESTINATIONS,
510 max_tunnel_destinations_total: usize::MAX,
511 known_destinations_ttl: Duration::from_secs(48 * 60 * 60),
512 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
513 announce_table_ttl: Duration::from_secs(rns_core::constants::ANNOUNCE_TABLE_TTL as u64),
514 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
515 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
516 interface_writer_queue_capacity: crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
517 #[cfg(feature = "iface-backbone")]
518 backbone_peer_pool: None,
519 announce_sig_cache_enabled: true,
520 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
521 announce_sig_cache_ttl: Duration::from_secs(
522 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
523 ),
524 registry: None,
525 panic_on_interface_error: false,
526 #[cfg(feature = "hooks")]
527 provider_bridge: None,
528 }
529 }
530}
531
532pub struct IfacConfig {
534 pub netname: Option<String>,
535 pub netkey: Option<String>,
536 pub size: usize,
537}
538
539pub struct InterfaceConfig {
541 pub name: String,
542 pub type_name: String,
543 pub config_data: Box<dyn crate::interface::InterfaceConfigData>,
544 pub mode: u8,
545 pub ingress_control: rns_core::transport::types::IngressControlConfig,
546 pub ifac: Option<IfacConfig>,
547 pub discovery: Option<crate::discovery::DiscoveryConfig>,
548}
549
550use crate::event::{QueryRequest, QueryResponse};
551
552#[derive(Debug)]
554pub struct SendError;
555
556impl std::fmt::Display for SendError {
557 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
558 write!(f, "driver shut down")
559 }
560}
561
562impl std::error::Error for SendError {}
563
564pub struct RnsNode {
566 tx: EventSender,
567 driver_handle: Option<JoinHandle<()>>,
568 verify_handle: Option<JoinHandle<()>>,
569 verify_shutdown: Arc<AtomicBool>,
570 rpc_server: Option<crate::rpc::RpcServer>,
571 tick_interval_ms: Arc<AtomicU64>,
572 #[allow(dead_code)]
573 probe_server: Option<crate::holepunch::probe::ProbeServerHandle>,
574 known_destinations_path: Option<std::path::PathBuf>,
575 ratchet_store: Option<Arc<dyn storage::RatchetStore>>,
576 ratchet_expiry: Duration,
577}
578
579impl RnsNode {
580 pub fn from_config(
583 config_path: Option<&Path>,
584 callbacks: Box<dyn Callbacks>,
585 ) -> io::Result<Self> {
586 let config_dir = storage::resolve_config_dir(config_path);
587 let paths = storage::ensure_storage_dirs(&config_dir)?;
588 let known_destinations_path = paths.storage.join("known_destinations");
589 let ratchet_store: Arc<dyn storage::RatchetStore> =
590 Arc::new(storage::FsRatchetStore::new(paths.ratchets.clone()));
591
592 let config_file = config_dir.join("config");
594 let rns_config = if config_file.exists() {
595 config::parse_file(&config_file)
596 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, format!("{}", e)))?
597 } else {
598 config::parse("")
600 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, format!("{}", e)))?
601 };
602
603 let identity = if let Some(ref id_path_str) = rns_config.reticulum.network_identity {
605 let id_path = std::path::PathBuf::from(id_path_str);
606 if id_path.exists() {
607 storage::load_identity(&id_path)?
608 } else {
609 let id = Identity::new(&mut OsRng);
610 storage::save_identity(&id, &id_path)?;
611 id
612 }
613 } else {
614 storage::load_or_create_identity(&paths.identities)?
615 };
616
617 let registry = crate::interface::registry::InterfaceRegistry::with_builtins();
619 let mut interface_configs = Vec::new();
620 let mut next_id_val = 1u64;
621
622 for iface in &rns_config.interfaces {
623 if !iface.enabled {
624 continue;
625 }
626
627 let iface_id = rns_core::transport::types::InterfaceId(next_id_val);
628 next_id_val += 1;
629
630 let factory = match registry.get(&iface.interface_type) {
631 Some(f) => f,
632 None => {
633 log::warn!(
634 "Unsupported interface type '{}' for '{}'",
635 iface.interface_type,
636 iface.name,
637 );
638 continue;
639 }
640 };
641
642 let mut iface_mode = parse_interface_mode(&iface.mode);
643
644 let has_discovery = match iface.interface_type.as_str() {
646 "AutoInterface" => true,
647 "RNodeInterface" => iface
648 .params
649 .get("discoverable")
650 .and_then(|v| config::parse_bool_pub(v))
651 .unwrap_or(false),
652 _ => false,
653 };
654 if has_discovery
655 && iface_mode != rns_core::constants::MODE_ACCESS_POINT
656 && iface_mode != rns_core::constants::MODE_GATEWAY
657 {
658 let new_mode = if iface.interface_type == "RNodeInterface" {
659 rns_core::constants::MODE_ACCESS_POINT
660 } else {
661 rns_core::constants::MODE_GATEWAY
662 };
663 log::info!(
664 "Interface '{}' has discovery enabled, auto-configuring mode to {}",
665 iface.name,
666 if new_mode == rns_core::constants::MODE_ACCESS_POINT {
667 "ACCESS_POINT"
668 } else {
669 "GATEWAY"
670 }
671 );
672 iface_mode = new_mode;
673 }
674
675 let default_ifac_size = factory.default_ifac_size();
676 let ifac_config = extract_ifac_config(&iface.params, default_ifac_size);
677 let discovery_config =
678 extract_discovery_config(&iface.name, &iface.interface_type, &iface.params);
679 let ingress_control =
680 match parse_ingress_control_config(&iface.interface_type, &iface.params) {
681 Ok(config) => config,
682 Err(e) => {
683 log::warn!(
684 "Failed to parse ingress control config for '{}': {}",
685 iface.name,
686 e
687 );
688 continue;
689 }
690 };
691
692 let mut params = iface.params.clone();
694 if !params.contains_key("storage_dir") {
695 params.insert(
696 "storage_dir".to_string(),
697 paths.storage.to_string_lossy().to_string(),
698 );
699 }
700 if let Some(ref device) = rns_config.reticulum.device {
702 if !params.contains_key("device") {
703 params.insert("device".to_string(), device.clone());
704 }
705 }
706
707 let config_data = match factory.parse_config(&iface.name, iface_id, ¶ms) {
708 Ok(data) => data,
709 Err(e) => {
710 log::warn!("Failed to parse config for '{}': {}", iface.name, e);
711 continue;
712 }
713 };
714
715 interface_configs.push(InterfaceConfig {
716 name: iface.name.clone(),
717 type_name: iface.interface_type.clone(),
718 config_data,
719 mode: iface_mode,
720 ingress_control,
721 ifac: ifac_config,
722 discovery: discovery_config,
723 });
724 }
725
726 let mut mgmt_allowed = Vec::new();
728 for hex_hash in &rns_config.reticulum.remote_management_allowed {
729 if hex_hash.len() == 32 {
730 if let Ok(bytes) = (0..hex_hash.len())
731 .step_by(2)
732 .map(|i| u8::from_str_radix(&hex_hash[i..i + 2], 16))
733 .collect::<Result<Vec<u8>, _>>()
734 {
735 if bytes.len() == 16 {
736 let mut h = [0u8; 16];
737 h.copy_from_slice(&bytes);
738 mgmt_allowed.push(h);
739 }
740 } else {
741 log::warn!("Invalid hex in remote_management_allowed: {}", hex_hash);
742 }
743 } else {
744 log::warn!(
745 "Invalid entry in remote_management_allowed (expected 32 hex chars, got {}): {}",
746 hex_hash.len(), hex_hash,
747 );
748 }
749 }
750
751 let probe_addrs: Vec<std::net::SocketAddr> = rns_config
753 .reticulum
754 .probe_addr
755 .as_ref()
756 .map(|s| {
757 s.split(',')
758 .filter_map(|entry| {
759 let trimmed = entry.trim();
760 if trimmed.is_empty() {
761 return None;
762 }
763 trimmed
764 .parse::<std::net::SocketAddr>()
765 .map_err(|e| {
766 log::warn!("Invalid probe_addr entry '{}': {}", trimmed, e);
767 e
768 })
769 .ok()
770 })
771 .collect()
772 })
773 .unwrap_or_default();
774
775 let probe_protocol = match rns_config
777 .reticulum
778 .probe_protocol
779 .as_deref()
780 .map(|s| s.to_lowercase())
781 {
782 Some(ref s) if s == "stun" => rns_core::holepunch::ProbeProtocol::Stun,
783 _ => rns_core::holepunch::ProbeProtocol::Rnsp,
784 };
785
786 let known_destinations = match storage::load_known_destinations(&known_destinations_path) {
787 Ok(destinations) => destinations,
788 Err(err) if err.kind() == io::ErrorKind::NotFound => Default::default(),
789 Err(err) => {
790 log::warn!("failed to load known destinations: {}", err);
791 Default::default()
792 }
793 };
794 let known_destination_hashes: HashSet<[u8; 16]> =
795 known_destinations.keys().copied().collect();
796 match ratchet_store.cleanup(
797 &known_destination_hashes,
798 time::now(),
799 rns_config.reticulum.ratchet_expiry as f64,
800 ) {
801 Ok(stats) if stats.processed > 0 => log::debug!(
802 "Processed {} ratchets, not in use {}, removed {}",
803 stats.processed,
804 stats.not_known,
805 stats.removed
806 ),
807 Ok(_) => {}
808 Err(err) => log::warn!("failed to clean ratchets: {}", err),
809 }
810
811 let node_config = NodeConfig {
812 transport_enabled: rns_config.reticulum.enable_transport,
813 identity: Some(identity),
814 share_instance: rns_config.reticulum.share_instance,
815 instance_name: rns_config.reticulum.instance_name.clone(),
816 shared_instance_port: rns_config.reticulum.shared_instance_port,
817 rpc_port: rns_config.reticulum.instance_control_port,
818 cache_dir: Some(paths.cache),
819 ratchet_store: Some(Arc::clone(&ratchet_store)),
820 ratchet_expiry: Duration::from_secs(rns_config.reticulum.ratchet_expiry),
821 management: crate::management::ManagementConfig {
822 enable_remote_management: rns_config.reticulum.enable_remote_management,
823 remote_management_allowed: mgmt_allowed,
824 publish_blackhole: rns_config.reticulum.publish_blackhole,
825 },
826 probe_port: rns_config.reticulum.probe_port,
827 probe_addrs,
828 probe_protocol,
829 device: rns_config.reticulum.device.clone(),
830 hooks: rns_config.hooks.clone(),
831 discover_interfaces: rns_config.reticulum.discover_interfaces,
832 discovery_required_value: rns_config.reticulum.required_discovery_value,
833 respond_to_probes: rns_config.reticulum.respond_to_probes,
834 prefer_shorter_path: rns_config.reticulum.prefer_shorter_path,
835 max_paths_per_destination: rns_config.reticulum.max_paths_per_destination,
836 packet_hashlist_max_entries: rns_config.reticulum.packet_hashlist_max_entries,
837 max_discovery_pr_tags: rns_config.reticulum.max_discovery_pr_tags,
838 max_path_destinations: rns_config.reticulum.max_path_destinations,
839 max_tunnel_destinations_total: rns_config.reticulum.max_tunnel_destinations_total,
840 known_destinations_ttl: Duration::from_secs(
841 rns_config.reticulum.known_destinations_ttl,
842 ),
843 known_destinations_max_entries: rns_config.reticulum.known_destinations_max_entries,
844 announce_table_ttl: Duration::from_secs(rns_config.reticulum.announce_table_ttl),
845 announce_table_max_bytes: rns_config.reticulum.announce_table_max_bytes,
846 driver_event_queue_capacity: rns_config.reticulum.driver_event_queue_capacity,
847 interface_writer_queue_capacity: rns_config.reticulum.interface_writer_queue_capacity,
848 #[cfg(feature = "iface-backbone")]
849 backbone_peer_pool: if rns_config.reticulum.backbone_peer_pool_max_connected > 0 {
850 Some(BackbonePeerPoolSettings {
851 max_connected: rns_config.reticulum.backbone_peer_pool_max_connected,
852 failure_threshold: rns_config.reticulum.backbone_peer_pool_failure_threshold,
853 failure_window: Duration::from_secs(
854 rns_config.reticulum.backbone_peer_pool_failure_window,
855 ),
856 cooldown: Duration::from_secs(rns_config.reticulum.backbone_peer_pool_cooldown),
857 })
858 } else {
859 None
860 },
861 announce_sig_cache_enabled: rns_config.reticulum.announce_sig_cache_enabled,
862 announce_sig_cache_max_entries: rns_config.reticulum.announce_sig_cache_max_entries,
863 announce_sig_cache_ttl: Duration::from_secs(
864 rns_config.reticulum.announce_sig_cache_ttl,
865 ),
866 interfaces: interface_configs,
867 registry: None,
868 panic_on_interface_error: rns_config.reticulum.panic_on_interface_error,
869 #[cfg(feature = "hooks")]
870 provider_bridge: if rns_config.reticulum.provider_bridge {
871 Some(crate::provider_bridge::ProviderBridgeConfig {
872 enabled: true,
873 socket_path: rns_config
874 .reticulum
875 .provider_socket_path
876 .as_ref()
877 .map(std::path::PathBuf::from)
878 .unwrap_or_else(|| config_dir.join("provider.sock")),
879 queue_max_events: rns_config.reticulum.provider_queue_max_events,
880 queue_max_bytes: rns_config.reticulum.provider_queue_max_bytes,
881 overflow_policy: match rns_config.reticulum.provider_overflow_policy.as_str() {
882 "drop_oldest" => crate::provider_bridge::OverflowPolicy::DropOldest,
883 _ => crate::provider_bridge::OverflowPolicy::DropNewest,
884 },
885 node_instance: rns_config.reticulum.instance_name.clone(),
886 })
887 } else {
888 None
889 },
890 };
891
892 let mut node = Self::start_with_announce_queue_max_entries(
893 node_config,
894 callbacks,
895 rns_config.reticulum.announce_queue_max_entries,
896 rns_config.reticulum.announce_queue_max_interfaces,
897 rns_config.reticulum.announce_queue_max_bytes,
898 rns_config.reticulum.announce_queue_ttl as f64,
899 match rns_config.reticulum.announce_queue_overflow_policy.as_str() {
900 "drop_newest" => AnnounceQueueOverflowPolicy::DropNewest,
901 "drop_oldest" => AnnounceQueueOverflowPolicy::DropOldest,
902 _ => AnnounceQueueOverflowPolicy::DropWorst,
903 },
904 )?;
905
906 node.known_destinations_path = Some(known_destinations_path.clone());
907 for (dest_hash, known) in known_destinations {
908 let _ = node.query(QueryRequest::RestoreKnownDestination(
909 crate::event::KnownDestinationEntry {
910 dest_hash,
911 identity_hash: known.identity_hash,
912 public_key: known.public_key,
913 app_data: known.app_data,
914 hops: known.hops,
915 received_at: known.received_at,
916 receiving_interface: rns_core::transport::types::InterfaceId(
917 known.receiving_interface,
918 ),
919 was_used: known.was_used,
920 last_used_at: known.last_used_at,
921 retained: known.retained,
922 },
923 ));
924 }
925
926 Ok(node)
927 }
928
929 pub fn start(config: NodeConfig, callbacks: Box<dyn Callbacks>) -> io::Result<Self> {
931 Self::start_with_announce_queue_max_entries(
932 config,
933 callbacks,
934 256,
935 1024,
936 256 * 1024,
937 30.0,
938 AnnounceQueueOverflowPolicy::DropWorst,
939 )
940 }
941
942 fn start_with_announce_queue_max_entries(
943 config: NodeConfig,
944 callbacks: Box<dyn Callbacks>,
945 announce_queue_max_entries: usize,
946 announce_queue_max_interfaces: usize,
947 announce_queue_max_bytes: usize,
948 announce_queue_ttl_secs: f64,
949 announce_queue_overflow_policy: AnnounceQueueOverflowPolicy,
950 ) -> io::Result<Self> {
951 let identity = config.identity.unwrap_or_else(|| Identity::new(&mut OsRng));
952
953 let transport_config = TransportConfig {
954 transport_enabled: config.transport_enabled,
955 identity_hash: Some(*identity.hash()),
956 prefer_shorter_path: config.prefer_shorter_path,
957 max_paths_per_destination: config.max_paths_per_destination,
958 packet_hashlist_max_entries: config.packet_hashlist_max_entries,
959 max_discovery_pr_tags: config.max_discovery_pr_tags,
960 max_path_destinations: config.max_path_destinations,
961 max_tunnel_destinations_total: config.max_tunnel_destinations_total,
962 destination_timeout_secs: config.known_destinations_ttl.as_secs_f64(),
963 announce_table_ttl_secs: config.announce_table_ttl.as_secs_f64(),
964 announce_table_max_bytes: config.announce_table_max_bytes,
965 announce_sig_cache_enabled: config.announce_sig_cache_enabled,
966 announce_sig_cache_max_entries: config.announce_sig_cache_max_entries,
967 announce_sig_cache_ttl_secs: config.announce_sig_cache_ttl.as_secs_f64(),
968 announce_queue_max_entries,
969 announce_queue_max_interfaces,
970 };
971
972 let (tx, rx) = event::channel_with_capacity(config.driver_event_queue_capacity);
973 let tick_interval_ms = Arc::new(AtomicU64::new(1000));
974 let mut driver = Driver::new(transport_config, rx, tx.clone(), callbacks);
975 driver.set_announce_verify_queue_config(
976 announce_queue_max_entries,
977 announce_queue_max_bytes,
978 announce_queue_ttl_secs,
979 announce_queue_overflow_policy,
980 );
981 driver.async_announce_verification = true;
982 driver.set_tick_interval_handle(Arc::clone(&tick_interval_ms));
983 driver.set_packet_hashlist_max_entries(config.packet_hashlist_max_entries);
984 driver.known_destinations_ttl = config.known_destinations_ttl.as_secs_f64();
985 driver.known_destinations_max_entries = config.known_destinations_max_entries;
986 driver.ratchet_store = config.ratchet_store.clone();
987 driver.interface_writer_queue_capacity = config.interface_writer_queue_capacity;
988 driver.runtime_config_defaults.known_destinations_ttl =
989 config.known_destinations_ttl.as_secs_f64();
990 #[cfg(feature = "hooks")]
991 if let Some(provider_config) = config.provider_bridge.clone() {
992 driver.runtime_config_defaults.provider_queue_max_events =
993 provider_config.queue_max_events;
994 driver.runtime_config_defaults.provider_queue_max_bytes =
995 provider_config.queue_max_bytes;
996 if provider_config.enabled {
997 match crate::provider_bridge::ProviderBridge::start(provider_config) {
998 Ok(bridge) => driver.provider_bridge = Some(bridge),
999 Err(err) => log::warn!("failed to start provider bridge: {}", err),
1000 }
1001 }
1002 }
1003
1004 if let Some(ref cache_dir) = config.cache_dir {
1006 let announces_dir = cache_dir.join("announces");
1007 let _ = std::fs::create_dir_all(&announces_dir);
1008 driver.announce_cache = Some(crate::announce_cache::AnnounceCache::new(announces_dir));
1009 }
1010
1011 if !config.probe_addrs.is_empty() || config.device.is_some() {
1013 driver.set_probe_config(
1014 config.probe_addrs.clone(),
1015 config.probe_protocol,
1016 config.device.clone(),
1017 );
1018 }
1019
1020 let probe_server = if let Some(port) = config.probe_port {
1022 let listen_addr: std::net::SocketAddr = ([0, 0, 0, 0], port).into();
1023 match crate::holepunch::probe::start_probe_server(listen_addr) {
1024 Ok(handle) => {
1025 log::info!("Probe server started on 0.0.0.0:{}", port);
1026 Some(handle)
1027 }
1028 Err(e) => {
1029 log::error!("Failed to start probe server on port {}: {}", port, e);
1030 None
1031 }
1032 }
1033 } else {
1034 None
1035 };
1036
1037 driver.management_config = config.management.clone();
1039
1040 if let Some(prv_key) = identity.get_private_key() {
1042 driver.transport_identity = Some(Identity::from_private_key(&prv_key));
1043 }
1044
1045 #[cfg(feature = "hooks")]
1047 {
1048 for hook_cfg in &config.hooks {
1049 if !hook_cfg.enabled {
1050 continue;
1051 }
1052 let point_idx = match config::parse_hook_point(&hook_cfg.attach_point) {
1053 Some(idx) => idx,
1054 None => {
1055 log::warn!(
1056 "Unknown hook point '{}' for hook '{}'",
1057 hook_cfg.attach_point,
1058 hook_cfg.name,
1059 );
1060 continue;
1061 }
1062 };
1063 let mgr = match driver.hook_manager.as_ref() {
1064 Some(m) => m,
1065 None => {
1066 log::warn!(
1067 "Hook manager not available, skipping hook '{}'",
1068 hook_cfg.name
1069 );
1070 continue;
1071 }
1072 };
1073 let hook_backend = match config::parse_hook_backend(&hook_cfg.hook_type) {
1074 Ok(backend) => backend,
1075 Err(e) => {
1076 log::warn!(
1077 "Invalid hook type '{}' for hook '{}': {}",
1078 hook_cfg.hook_type,
1079 hook_cfg.name,
1080 e,
1081 );
1082 continue;
1083 }
1084 };
1085 let load_result = if hook_backend == rns_hooks::HookBackend::Builtin {
1086 let builtin_id = hook_cfg
1087 .builtin_id
1088 .as_deref()
1089 .filter(|id| !id.is_empty())
1090 .or_else(|| (!hook_cfg.path.is_empty()).then_some(hook_cfg.path.as_str()));
1091 match builtin_id {
1092 Some(id) => mgr.load_builtin(hook_cfg.name.clone(), id, hook_cfg.priority),
1093 None => Err(rns_hooks::HookError::CompileError(
1094 "built-in hook requires builtin/id or path".to_string(),
1095 )),
1096 }
1097 } else {
1098 mgr.load_file_backend(
1099 hook_cfg.name.clone(),
1100 std::path::Path::new(&hook_cfg.path),
1101 hook_cfg.priority,
1102 hook_backend,
1103 )
1104 };
1105 match load_result {
1106 Ok(program) => {
1107 driver.hook_slots[point_idx].attach(program);
1108 log::info!(
1109 "Loaded hook '{}' at point {} (priority {})",
1110 hook_cfg.name,
1111 hook_cfg.attach_point,
1112 hook_cfg.priority,
1113 );
1114 }
1115 Err(e) => {
1116 log::error!(
1117 "Failed to load hook '{}' from '{}': {}",
1118 hook_cfg.name,
1119 hook_cfg.path,
1120 e,
1121 );
1122 }
1123 }
1124 }
1125 }
1126
1127 driver.discover_interfaces = config.discover_interfaces;
1129 if let Some(val) = config.discovery_required_value {
1130 driver.discovery_required_value = val;
1131 }
1132
1133 let next_dynamic_id = Arc::new(AtomicU64::new(10000));
1135
1136 let mut discoverable_interfaces = Vec::new();
1138 #[cfg(feature = "iface-backbone")]
1139 let mut backbone_peer_pool_candidates = Vec::new();
1140
1141 let registry = config
1143 .registry
1144 .unwrap_or_else(crate::interface::registry::InterfaceRegistry::with_builtins);
1145 for iface_config in config.interfaces {
1146 #[cfg(feature = "iface-backbone")]
1147 if iface_config.type_name == "BackboneInterface" {
1148 if let Some(mode) = iface_config
1149 .config_data
1150 .as_any()
1151 .downcast_ref::<BackboneMode>()
1152 {
1153 if let Some(handle) = runtime_handle_from_mode(mode) {
1154 driver.register_backbone_runtime(handle);
1155 }
1156 if let Some(handle) = peer_state_handle_from_mode(mode) {
1157 driver.register_backbone_peer_state(handle);
1158 }
1159 if let Some(handle) = client_runtime_handle_from_mode(mode) {
1160 driver.register_backbone_client_runtime(handle);
1161 }
1162 if let Some(handle) = backbone_discovery_runtime_from_interface(
1163 &iface_config.name,
1164 mode,
1165 iface_config.discovery.as_ref(),
1166 config.transport_enabled,
1167 iface_config.ifac.as_ref(),
1168 ) {
1169 driver.register_backbone_discovery_runtime(handle);
1170 }
1171 }
1172 }
1173 #[cfg(feature = "iface-tcp")]
1174 if iface_config.type_name == "TCPClientInterface" {
1175 if let Some(tcp_config) = iface_config
1176 .config_data
1177 .as_any()
1178 .downcast_ref::<TcpClientConfig>()
1179 {
1180 driver.register_tcp_client_runtime(tcp_client_runtime_handle_from_config(
1181 tcp_config,
1182 ));
1183 }
1184 }
1185 #[cfg(feature = "iface-tcp")]
1186 if iface_config.type_name == "TCPServerInterface" {
1187 if let Some(tcp_config) = iface_config
1188 .config_data
1189 .as_any()
1190 .downcast_ref::<TcpServerConfig>()
1191 {
1192 driver.register_tcp_server_runtime(tcp_runtime_handle_from_config(tcp_config));
1193 driver.register_tcp_server_discovery_runtime(
1194 tcp_server_discovery_runtime_from_interface(
1195 &iface_config.name,
1196 tcp_config,
1197 iface_config.discovery.as_ref(),
1198 config.transport_enabled,
1199 iface_config.ifac.as_ref(),
1200 ),
1201 );
1202 }
1203 }
1204 #[cfg(feature = "iface-udp")]
1205 if iface_config.type_name == "UDPInterface" {
1206 if let Some(udp_config) = iface_config
1207 .config_data
1208 .as_any()
1209 .downcast_ref::<UdpConfig>()
1210 {
1211 driver.register_udp_runtime(udp_runtime_handle_from_config(udp_config));
1212 }
1213 }
1214 #[cfg(feature = "iface-auto")]
1215 if iface_config.type_name == "AutoInterface" {
1216 if let Some(auto_config) = iface_config
1217 .config_data
1218 .as_any()
1219 .downcast_ref::<AutoConfig>()
1220 {
1221 driver.register_auto_runtime(auto_runtime_handle_from_config(auto_config));
1222 }
1223 }
1224 #[cfg(feature = "iface-i2p")]
1225 if iface_config.type_name == "I2PInterface" {
1226 if let Some(i2p_config) = iface_config
1227 .config_data
1228 .as_any()
1229 .downcast_ref::<I2pConfig>()
1230 {
1231 driver.register_i2p_runtime(i2p_runtime_handle_from_config(i2p_config));
1232 }
1233 }
1234 #[cfg(feature = "iface-pipe")]
1235 if iface_config.type_name == "PipeInterface" {
1236 if let Some(pipe_config) = iface_config
1237 .config_data
1238 .as_any()
1239 .downcast_ref::<PipeConfig>()
1240 {
1241 driver.register_pipe_runtime(pipe_runtime_handle_from_config(pipe_config));
1242 }
1243 }
1244 #[cfg(feature = "iface-rnode")]
1245 if iface_config.type_name == "RNodeInterface" {
1246 if let Some(rnode_config) = iface_config
1247 .config_data
1248 .as_any()
1249 .downcast_ref::<RNodeConfig>()
1250 {
1251 driver.register_rnode_runtime(rnode_runtime_handle_from_config(rnode_config));
1252 }
1253 }
1254
1255 let factory = match registry.get(&iface_config.type_name) {
1256 Some(f) => f,
1257 None => {
1258 log::warn!(
1259 "No factory registered for interface type '{}'",
1260 iface_config.type_name
1261 );
1262 continue;
1263 }
1264 };
1265
1266 let mut ifac_state = derive_ifac_state(iface_config.ifac.as_ref(), &iface_config.name)?;
1267 let ifac_runtime =
1268 ifac_runtime_from_config(iface_config.ifac.as_ref(), factory.default_ifac_size());
1269
1270 #[cfg(feature = "iface-backbone")]
1271 if config.backbone_peer_pool.is_some() && iface_config.type_name == "BackboneInterface"
1272 {
1273 if let Some(mode) = iface_config
1274 .config_data
1275 .as_any()
1276 .downcast_ref::<BackboneMode>()
1277 {
1278 if let Some(client) = client_config_from_mode(mode) {
1279 backbone_peer_pool_candidates.push(BackbonePeerPoolCandidateConfig {
1280 client,
1281 mode: iface_config.mode,
1282 ingress_control: iface_config.ingress_control,
1283 ifac_runtime: ifac_runtime.clone(),
1284 ifac_enabled: ifac_state.is_some(),
1285 interface_type_name: iface_config.type_name.clone(),
1286 });
1287 if let Some(ref disc) = iface_config.discovery {
1288 discoverable_interfaces.push(discoverable_interface_from_config(
1289 &iface_config.name,
1290 disc,
1291 config.transport_enabled,
1292 iface_config.ifac.as_ref(),
1293 ));
1294 }
1295 continue;
1296 }
1297 }
1298 }
1299
1300 let ctx = crate::interface::StartContext {
1301 tx: tx.clone(),
1302 next_dynamic_id: next_dynamic_id.clone(),
1303 mode: iface_config.mode,
1304 ingress_control: iface_config.ingress_control,
1305 };
1306
1307 let result = match factory.start(iface_config.config_data, ctx) {
1308 Ok(r) => r,
1309 Err(e) => {
1310 if config.panic_on_interface_error {
1311 return Err(e);
1312 }
1313 log::error!(
1314 "Interface '{}' ({}) failed to start: {}",
1315 iface_config.name,
1316 iface_config.type_name,
1317 e
1318 );
1319 continue;
1320 }
1321 };
1322
1323 if let Some(ref disc) = iface_config.discovery {
1324 discoverable_interfaces.push(discoverable_interface_from_config(
1325 &iface_config.name,
1326 disc,
1327 config.transport_enabled,
1328 iface_config.ifac.as_ref(),
1329 ));
1330 }
1331
1332 match result {
1333 crate::interface::StartResult::Simple {
1334 id,
1335 info,
1336 writer,
1337 interface_type_name,
1338 } => {
1339 register_started_interface(
1340 &mut driver,
1341 &tx,
1342 config.interface_writer_queue_capacity,
1343 id,
1344 info,
1345 writer,
1346 interface_type_name,
1347 ifac_state,
1348 &ifac_runtime,
1349 );
1350 }
1351 crate::interface::StartResult::Listener { control } => {
1352 if let Some(control) = control {
1355 driver.register_listener_control(control);
1356 }
1357 }
1358 crate::interface::StartResult::Multi(subs) => {
1359 let ifac_cfg = &iface_config.ifac;
1360 let mut first = true;
1361 for sub in subs {
1362 let sub_ifac = if first {
1363 first = false;
1364 ifac_state.take()
1365 } else {
1366 derive_ifac_state(ifac_cfg.as_ref(), &sub.info.name)?
1367 };
1368 register_started_interface(
1369 &mut driver,
1370 &tx,
1371 config.interface_writer_queue_capacity,
1372 sub.id,
1373 sub.info,
1374 sub.writer,
1375 sub.interface_type_name,
1376 sub_ifac,
1377 &ifac_runtime,
1378 );
1379 }
1380 }
1381 }
1382 }
1383
1384 #[cfg(feature = "iface-backbone")]
1385 if let Some(settings) = config.backbone_peer_pool.clone() {
1386 driver.configure_backbone_peer_pool(settings, backbone_peer_pool_candidates);
1387 }
1388
1389 if !discoverable_interfaces.is_empty() {
1391 let transport_id = *identity.hash();
1392 let announcer =
1393 crate::discovery::InterfaceAnnouncer::new(transport_id, discoverable_interfaces);
1394 log::info!("Interface discovery announcer initialized");
1395 driver.interface_announcer = Some(announcer);
1396 }
1397
1398 if let Some(ref cache_dir) = config.cache_dir {
1400 let disc_path = std::path::PathBuf::from(cache_dir)
1401 .parent()
1402 .unwrap_or(std::path::Path::new("."))
1403 .join("storage")
1404 .join("discovery")
1405 .join("interfaces");
1406 let _ = std::fs::create_dir_all(&disc_path);
1407 driver.discovered_interfaces =
1408 crate::discovery::DiscoveredInterfaceStorage::new(disc_path);
1409 }
1410
1411 if config.management.enable_remote_management {
1413 if let Some(prv_key) = identity.get_private_key() {
1414 let identity_hash = *identity.hash();
1415 let mgmt_dest = crate::management::management_dest_hash(&identity_hash);
1416
1417 let sig_prv = rns_crypto::ed25519::Ed25519PrivateKey::from_bytes(
1419 &prv_key[32..64].try_into().unwrap(),
1420 );
1421 let sig_pub_bytes: [u8; 32] = identity.get_public_key().unwrap()[32..64]
1422 .try_into()
1423 .unwrap();
1424
1425 driver
1427 .engine
1428 .register_destination(mgmt_dest, rns_core::constants::DESTINATION_SINGLE);
1429 driver
1430 .local_destinations
1431 .insert(mgmt_dest, rns_core::constants::DESTINATION_SINGLE);
1432
1433 driver.link_manager.register_link_destination(
1435 mgmt_dest,
1436 sig_prv,
1437 sig_pub_bytes,
1438 crate::link_manager::ResourceStrategy::AcceptNone,
1439 );
1440
1441 driver
1443 .link_manager
1444 .register_management_path(crate::management::status_path_hash());
1445 driver
1446 .link_manager
1447 .register_management_path(crate::management::path_path_hash());
1448
1449 log::info!("Remote management enabled on {:02x?}", &mgmt_dest[..4],);
1450
1451 if !config.management.remote_management_allowed.is_empty() {
1453 log::info!(
1454 "Remote management allowed for {} identities",
1455 config.management.remote_management_allowed.len(),
1456 );
1457 }
1458 }
1459 }
1460
1461 if config.management.publish_blackhole {
1462 if let Some(prv_key) = identity.get_private_key() {
1463 let identity_hash = *identity.hash();
1464 let bh_dest = crate::management::blackhole_dest_hash(&identity_hash);
1465
1466 let sig_prv = rns_crypto::ed25519::Ed25519PrivateKey::from_bytes(
1467 &prv_key[32..64].try_into().unwrap(),
1468 );
1469 let sig_pub_bytes: [u8; 32] = identity.get_public_key().unwrap()[32..64]
1470 .try_into()
1471 .unwrap();
1472
1473 driver
1474 .engine
1475 .register_destination(bh_dest, rns_core::constants::DESTINATION_SINGLE);
1476 driver.link_manager.register_link_destination(
1477 bh_dest,
1478 sig_prv,
1479 sig_pub_bytes,
1480 crate::link_manager::ResourceStrategy::AcceptNone,
1481 );
1482 driver
1483 .link_manager
1484 .register_management_path(crate::management::list_path_hash());
1485
1486 log::info!(
1487 "Blackhole list publishing enabled on {:02x?}",
1488 &bh_dest[..4],
1489 );
1490 }
1491 }
1492
1493 if config.respond_to_probes && config.transport_enabled {
1495 let identity_hash = *identity.hash();
1496 let probe_dest = crate::management::probe_dest_hash(&identity_hash);
1497
1498 driver
1500 .engine
1501 .register_destination(probe_dest, rns_core::constants::DESTINATION_SINGLE);
1502 driver
1503 .local_destinations
1504 .insert(probe_dest, rns_core::constants::DESTINATION_SINGLE);
1505
1506 let probe_identity = rns_crypto::identity::Identity::from_private_key(
1508 &identity.get_private_key().unwrap(),
1509 );
1510 driver.proof_strategies.insert(
1511 probe_dest,
1512 (
1513 rns_core::types::ProofStrategy::ProveAll,
1514 Some(probe_identity),
1515 ),
1516 );
1517
1518 driver.probe_responder_hash = Some(probe_dest);
1519
1520 log::info!("Probe responder enabled on {:02x?}", &probe_dest[..4],);
1521 }
1522
1523 let timer_tx = tx.clone();
1525 let timer_interval = Arc::clone(&tick_interval_ms);
1526 thread::Builder::new()
1527 .name("rns-timer".into())
1528 .spawn(move || {
1529 loop {
1530 let ms = timer_interval.load(Ordering::Relaxed);
1531 thread::sleep(Duration::from_millis(ms));
1532 if timer_tx.send(Event::Tick).is_err() {
1533 break; }
1535 }
1536 })?;
1537
1538 #[cfg(feature = "iface-local")]
1540 if config.share_instance {
1541 let local_server_config = LocalServerConfig {
1542 instance_name: config.instance_name.clone(),
1543 port: config.shared_instance_port,
1544 interface_id: rns_core::transport::types::InterfaceId(0), };
1546 match crate::interface::local::start_server(
1547 local_server_config,
1548 tx.clone(),
1549 next_dynamic_id.clone(),
1550 ) {
1551 Ok(control) => {
1552 driver.register_listener_control(control);
1553 log::info!(
1554 "Local shared instance server started (instance={}, port={})",
1555 config.instance_name,
1556 config.shared_instance_port
1557 );
1558 }
1559 Err(e) => {
1560 log::error!("Failed to start local shared instance server: {}", e);
1561 }
1562 }
1563 }
1564
1565 let rpc_server = if config.share_instance {
1567 let auth_key =
1568 crate::rpc::derive_auth_key(&identity.get_private_key().unwrap_or([0u8; 64]));
1569 let rpc_addr = crate::rpc::RpcAddr::Tcp("127.0.0.1".into(), config.rpc_port);
1570 match crate::rpc::RpcServer::start(&rpc_addr, auth_key, tx.clone()) {
1571 Ok(server) => {
1572 log::info!("RPC server started on 127.0.0.1:{}", config.rpc_port);
1573 Some(server)
1574 }
1575 Err(e) => {
1576 log::error!("Failed to start RPC server: {}", e);
1577 None
1578 }
1579 }
1580 } else {
1581 None
1582 };
1583
1584 let announce_verify_queue = Arc::clone(&driver.announce_verify_queue);
1585 let verify_shutdown = Arc::new(AtomicBool::new(false));
1586 let verify_shutdown_thread = Arc::clone(&verify_shutdown);
1587 let verify_tx = tx.clone();
1588 let verify_handle = thread::Builder::new()
1589 .name("rns-verify".into())
1590 .spawn(move || {
1591 #[cfg(target_family = "unix")]
1592 {
1593 unsafe {
1594 libc::nice(5);
1595 }
1596 }
1597
1598 while !verify_shutdown_thread.load(Ordering::Relaxed) {
1599 let batch = {
1600 let mut queue = announce_verify_queue
1601 .lock()
1602 .unwrap_or_else(|poisoned| poisoned.into_inner());
1603 queue.take_pending(time::now())
1604 };
1605
1606 if batch.is_empty() {
1607 thread::sleep(Duration::from_millis(50));
1608 continue;
1609 }
1610
1611 for (key, pending) in batch {
1612 if verify_shutdown_thread.load(Ordering::Relaxed) {
1613 break;
1614 }
1615 let has_ratchet =
1616 pending.packet.flags.context_flag == rns_core::constants::FLAG_SET;
1617 let announce = match rns_core::announce::AnnounceData::unpack(
1618 &pending.packet.data,
1619 has_ratchet,
1620 ) {
1621 Ok(announce) => announce,
1622 Err(_) => {
1623 let signature = [0u8; 64];
1624 let sig_cache_key = {
1625 let mut material = [0u8; 80];
1626 material[..16]
1627 .copy_from_slice(&pending.packet.destination_hash);
1628 material[16..].copy_from_slice(&signature);
1629 rns_core::hash::full_hash(&material)
1630 };
1631 if verify_tx
1632 .send(Event::AnnounceVerifyFailed { key, sig_cache_key })
1633 .is_err()
1634 {
1635 return;
1636 }
1637 continue;
1638 }
1639 };
1640 let mut material = [0u8; 80];
1641 material[..16].copy_from_slice(&pending.packet.destination_hash);
1642 material[16..].copy_from_slice(&announce.signature);
1643 let sig_cache_key = rns_core::hash::full_hash(&material);
1644 match announce.validate(&pending.packet.destination_hash) {
1645 Ok(validated) => {
1646 if verify_tx
1647 .send(Event::AnnounceVerified {
1648 key,
1649 validated,
1650 sig_cache_key,
1651 })
1652 .is_err()
1653 {
1654 return;
1655 }
1656 }
1657 Err(_) => {
1658 if verify_tx
1659 .send(Event::AnnounceVerifyFailed { key, sig_cache_key })
1660 .is_err()
1661 {
1662 return;
1663 }
1664 }
1665 }
1666 }
1667 }
1668 })?;
1669
1670 let driver_handle = thread::Builder::new()
1674 .name("rns-driver".into())
1675 .spawn(move || {
1676 driver.run();
1677 })?;
1678
1679 Ok(RnsNode {
1680 tx,
1681 driver_handle: Some(driver_handle),
1682 verify_handle: Some(verify_handle),
1683 verify_shutdown,
1684 rpc_server,
1685 tick_interval_ms,
1686 probe_server,
1687 known_destinations_path: None,
1688 ratchet_store: config.ratchet_store,
1689 ratchet_expiry: config.ratchet_expiry,
1690 })
1691 }
1692
1693 pub fn query(&self, request: QueryRequest) -> Result<QueryResponse, SendError> {
1695 let (resp_tx, resp_rx) = std::sync::mpsc::channel();
1696 self.tx
1697 .send(Event::Query(request, resp_tx))
1698 .map_err(|_| SendError)?;
1699 resp_rx.recv().map_err(|_| SendError)
1700 }
1701
1702 pub fn begin_drain(&self, timeout: Duration) -> Result<(), SendError> {
1704 self.tx
1705 .send(Event::BeginDrain { timeout })
1706 .map_err(|_| SendError)
1707 }
1708
1709 pub fn drain_status(&self) -> Result<crate::event::DrainStatus, SendError> {
1711 match self.query(QueryRequest::DrainStatus)? {
1712 QueryResponse::DrainStatus(status) => Ok(status),
1713 _ => Err(SendError),
1714 }
1715 }
1716
1717 fn reject_new_work_if_draining(&self) -> Result<(), SendError> {
1718 let status = self.drain_status()?;
1719 if matches!(status.state, crate::event::LifecycleState::Active) {
1720 Ok(())
1721 } else {
1722 Err(SendError)
1723 }
1724 }
1725
1726 pub fn send_raw(
1728 &self,
1729 raw: Vec<u8>,
1730 dest_type: u8,
1731 attached_interface: Option<rns_core::transport::types::InterfaceId>,
1732 ) -> Result<(), SendError> {
1733 self.tx
1734 .send(Event::SendOutbound {
1735 raw,
1736 dest_type,
1737 attached_interface,
1738 })
1739 .map_err(|_| SendError)
1740 }
1741
1742 pub fn register_destination(
1744 &self,
1745 dest_hash: [u8; 16],
1746 dest_type: u8,
1747 ) -> Result<(), SendError> {
1748 self.tx
1749 .send(Event::RegisterDestination {
1750 dest_hash,
1751 dest_type,
1752 })
1753 .map_err(|_| SendError)
1754 }
1755
1756 pub fn deregister_destination(&self, dest_hash: [u8; 16]) -> Result<(), SendError> {
1758 self.tx
1759 .send(Event::DeregisterDestination { dest_hash })
1760 .map_err(|_| SendError)
1761 }
1762
1763 pub fn deregister_link_destination(&self, dest_hash: [u8; 16]) -> Result<(), SendError> {
1765 self.tx
1766 .send(Event::DeregisterLinkDestination { dest_hash })
1767 .map_err(|_| SendError)
1768 }
1769
1770 pub fn register_link_destination(
1776 &self,
1777 dest_hash: [u8; 16],
1778 sig_prv_bytes: [u8; 32],
1779 sig_pub_bytes: [u8; 32],
1780 resource_strategy: u8,
1781 ) -> Result<(), SendError> {
1782 self.tx
1783 .send(Event::RegisterLinkDestination {
1784 dest_hash,
1785 sig_prv_bytes,
1786 sig_pub_bytes,
1787 resource_strategy,
1788 })
1789 .map_err(|_| SendError)
1790 }
1791
1792 pub fn register_request_handler<F>(
1794 &self,
1795 path: &str,
1796 allowed_list: Option<Vec<[u8; 16]>>,
1797 handler: F,
1798 ) -> Result<(), SendError>
1799 where
1800 F: Fn([u8; 16], &str, &[u8], Option<&([u8; 16], [u8; 64])>) -> Option<Vec<u8>>
1801 + Send
1802 + 'static,
1803 {
1804 self.tx
1805 .send(Event::RegisterRequestHandler {
1806 path: path.to_string(),
1807 allowed_list,
1808 handler: Box::new(handler),
1809 })
1810 .map_err(|_| SendError)
1811 }
1812
1813 pub fn register_request_handler_response<F>(
1815 &self,
1816 path: &str,
1817 allowed_list: Option<Vec<[u8; 16]>>,
1818 handler: F,
1819 ) -> Result<(), SendError>
1820 where
1821 F: Fn(
1822 [u8; 16],
1823 &str,
1824 &[u8],
1825 Option<&([u8; 16], [u8; 64])>,
1826 ) -> Option<crate::link_manager::RequestResponse>
1827 + Send
1828 + 'static,
1829 {
1830 self.tx
1831 .send(Event::RegisterRequestHandlerResponse {
1832 path: path.to_string(),
1833 allowed_list,
1834 handler: Box::new(handler),
1835 })
1836 .map_err(|_| SendError)
1837 }
1838
1839 pub fn create_link(
1843 &self,
1844 dest_hash: [u8; 16],
1845 dest_sig_pub_bytes: [u8; 32],
1846 ) -> Result<[u8; 16], SendError> {
1847 self.reject_new_work_if_draining()?;
1848 let (response_tx, response_rx) = std::sync::mpsc::channel();
1849 self.tx
1850 .send(Event::CreateLink {
1851 dest_hash,
1852 dest_sig_pub_bytes,
1853 response_tx,
1854 })
1855 .map_err(|_| SendError)?;
1856 let link_id = response_rx.recv().map_err(|_| SendError)?;
1857 if link_id == [0u8; 16] {
1858 Err(SendError)
1859 } else {
1860 Ok(link_id)
1861 }
1862 }
1863
1864 pub fn send_request(
1866 &self,
1867 link_id: [u8; 16],
1868 path: &str,
1869 data: &[u8],
1870 ) -> Result<(), SendError> {
1871 self.reject_new_work_if_draining()?;
1872 self.tx
1873 .send(Event::SendRequest {
1874 link_id,
1875 path: path.to_string(),
1876 data: data.to_vec(),
1877 })
1878 .map_err(|_| SendError)
1879 }
1880
1881 pub fn identify_on_link(
1883 &self,
1884 link_id: [u8; 16],
1885 identity_prv_key: [u8; 64],
1886 ) -> Result<(), SendError> {
1887 self.reject_new_work_if_draining()?;
1888 self.tx
1889 .send(Event::IdentifyOnLink {
1890 link_id,
1891 identity_prv_key,
1892 })
1893 .map_err(|_| SendError)
1894 }
1895
1896 pub fn teardown_link(&self, link_id: [u8; 16]) -> Result<(), SendError> {
1898 self.tx
1899 .send(Event::TeardownLink { link_id })
1900 .map_err(|_| SendError)
1901 }
1902
1903 pub fn send_resource(
1905 &self,
1906 link_id: [u8; 16],
1907 data: Vec<u8>,
1908 metadata: Option<Vec<u8>>,
1909 ) -> Result<(), SendError> {
1910 self.send_resource_with_auto_compress(link_id, data, metadata, true)
1911 }
1912
1913 pub fn send_resource_with_auto_compress(
1915 &self,
1916 link_id: [u8; 16],
1917 data: Vec<u8>,
1918 metadata: Option<Vec<u8>>,
1919 auto_compress: bool,
1920 ) -> Result<(), SendError> {
1921 self.reject_new_work_if_draining()?;
1922 self.tx
1923 .send(Event::SendResource {
1924 link_id,
1925 data,
1926 metadata,
1927 auto_compress,
1928 })
1929 .map_err(|_| SendError)
1930 }
1931
1932 pub fn set_resource_strategy(&self, link_id: [u8; 16], strategy: u8) -> Result<(), SendError> {
1936 self.tx
1937 .send(Event::SetResourceStrategy { link_id, strategy })
1938 .map_err(|_| SendError)
1939 }
1940
1941 pub fn accept_resource(
1943 &self,
1944 link_id: [u8; 16],
1945 resource_hash: Vec<u8>,
1946 accept: bool,
1947 ) -> Result<(), SendError> {
1948 if accept {
1949 self.reject_new_work_if_draining()?;
1950 }
1951 self.tx
1952 .send(Event::AcceptResource {
1953 link_id,
1954 resource_hash,
1955 accept,
1956 })
1957 .map_err(|_| SendError)
1958 }
1959
1960 pub fn send_channel_message(
1962 &self,
1963 link_id: [u8; 16],
1964 msgtype: u16,
1965 payload: Vec<u8>,
1966 ) -> Result<(), SendError> {
1967 self.reject_new_work_if_draining()?;
1968 let (response_tx, response_rx) = std::sync::mpsc::channel();
1969 self.tx
1970 .send(Event::SendChannelMessage {
1971 link_id,
1972 msgtype,
1973 payload,
1974 response_tx,
1975 })
1976 .map_err(|_| SendError)?;
1977 response_rx
1978 .recv()
1979 .map_err(|_| SendError)?
1980 .map_err(|_| SendError)
1981 }
1982
1983 pub fn propose_direct_connect(&self, link_id: [u8; 16]) -> Result<(), SendError> {
1988 self.reject_new_work_if_draining()?;
1989 self.tx
1990 .send(Event::ProposeDirectConnect { link_id })
1991 .map_err(|_| SendError)
1992 }
1993
1994 pub fn set_direct_connect_policy(
1996 &self,
1997 policy: crate::holepunch::orchestrator::HolePunchPolicy,
1998 ) -> Result<(), SendError> {
1999 self.tx
2000 .send(Event::SetDirectConnectPolicy { policy })
2001 .map_err(|_| SendError)
2002 }
2003
2004 pub fn send_on_link(
2006 &self,
2007 link_id: [u8; 16],
2008 data: Vec<u8>,
2009 context: u8,
2010 ) -> Result<(), SendError> {
2011 self.reject_new_work_if_draining()?;
2012 self.tx
2013 .send(Event::SendOnLink {
2014 link_id,
2015 data,
2016 context,
2017 })
2018 .map_err(|_| SendError)
2019 }
2020
2021 pub fn announce(
2026 &self,
2027 dest: &crate::destination::Destination,
2028 identity: &Identity,
2029 app_data: Option<&[u8]>,
2030 ) -> Result<(), SendError> {
2031 self.reject_new_work_if_draining()?;
2032 let name_hash = rns_core::destination::name_hash(
2033 &dest.app_name,
2034 &dest.aspects.iter().map(|s| s.as_str()).collect::<Vec<_>>(),
2035 );
2036
2037 let mut random_hash = [0u8; 10];
2038 OsRng.fill_bytes(&mut random_hash[..5]);
2039 let now_secs = std::time::SystemTime::now()
2043 .duration_since(std::time::UNIX_EPOCH)
2044 .unwrap_or_default()
2045 .as_secs();
2046 random_hash[5..10].copy_from_slice(&now_secs.to_be_bytes()[3..8]);
2047
2048 let (announce_data, _has_ratchet) = rns_core::announce::AnnounceData::pack(
2049 identity,
2050 &dest.hash.0,
2051 &name_hash,
2052 &random_hash,
2053 None, app_data,
2055 )
2056 .map_err(|_| SendError)?;
2057
2058 let context_flag = rns_core::constants::FLAG_UNSET;
2059
2060 let flags = rns_core::packet::PacketFlags {
2061 header_type: rns_core::constants::HEADER_1,
2062 context_flag,
2063 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
2064 destination_type: rns_core::constants::DESTINATION_SINGLE,
2065 packet_type: rns_core::constants::PACKET_TYPE_ANNOUNCE,
2066 };
2067
2068 let packet = rns_core::packet::RawPacket::pack(
2069 flags,
2070 0,
2071 &dest.hash.0,
2072 None,
2073 rns_core::constants::CONTEXT_NONE,
2074 &announce_data,
2075 )
2076 .map_err(|_| SendError)?;
2077
2078 if dest.dest_type == rns_core::types::DestinationType::Single {
2079 if let Some(identity_prv_key) = identity.get_private_key() {
2080 self.tx
2081 .send(Event::StoreSharedAnnounce {
2082 dest_hash: dest.hash.0,
2083 name_hash,
2084 identity_prv_key,
2085 app_data: app_data.map(|d| d.to_vec()),
2086 })
2087 .map_err(|_| SendError)?;
2088 }
2089 }
2090
2091 self.send_raw(packet.raw, dest.dest_type.to_wire_constant(), None)
2092 }
2093
2094 pub fn send_packet(
2099 &self,
2100 dest: &crate::destination::Destination,
2101 data: &[u8],
2102 ) -> Result<rns_core::types::PacketHash, SendError> {
2103 self.reject_new_work_if_draining()?;
2104 use rns_core::types::DestinationType;
2105
2106 let payload = match dest.dest_type {
2107 DestinationType::Single => self.encrypt_single_payload(dest, data)?,
2108 DestinationType::Plain => data.to_vec(),
2109 DestinationType::Group => dest.encrypt(data).map_err(|_| SendError)?,
2110 };
2111
2112 let flags = rns_core::packet::PacketFlags {
2113 header_type: rns_core::constants::HEADER_1,
2114 context_flag: rns_core::constants::FLAG_UNSET,
2115 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
2116 destination_type: dest.dest_type.to_wire_constant(),
2117 packet_type: rns_core::constants::PACKET_TYPE_DATA,
2118 };
2119
2120 let packet = rns_core::packet::RawPacket::pack(
2121 flags,
2122 0,
2123 &dest.hash.0,
2124 None,
2125 rns_core::constants::CONTEXT_NONE,
2126 &payload,
2127 )
2128 .map_err(|_| SendError)?;
2129
2130 let packet_hash = rns_core::types::PacketHash(packet.packet_hash);
2131
2132 self.tx
2133 .send(Event::SendOutbound {
2134 raw: packet.raw,
2135 dest_type: dest.dest_type.to_wire_constant(),
2136 attached_interface: None,
2137 })
2138 .map_err(|_| SendError)?;
2139
2140 Ok(packet_hash)
2141 }
2142
2143 fn encrypt_single_payload(
2144 &self,
2145 dest: &crate::destination::Destination,
2146 data: &[u8],
2147 ) -> Result<Vec<u8>, SendError> {
2148 let pub_key = dest.public_key.ok_or(SendError)?;
2149 let remote_id = rns_crypto::identity::Identity::from_public_key(&pub_key);
2150 let ratchet = self.ratchet_store.as_ref().and_then(|store| {
2151 match store.current(&dest.hash.0, time::now(), self.ratchet_expiry.as_secs_f64()) {
2152 Ok(entry) => entry.map(|entry| entry.ratchet),
2153 Err(err) => {
2154 log::warn!(
2155 "failed to load ratchet for {:02x}{:02x}{:02x}{:02x}..: {}",
2156 dest.hash.0[0],
2157 dest.hash.0[1],
2158 dest.hash.0[2],
2159 dest.hash.0[3],
2160 err
2161 );
2162 None
2163 }
2164 }
2165 });
2166 remote_id
2167 .encrypt_with_ratchet(data, ratchet.as_ref(), &mut OsRng)
2168 .map_err(|_| SendError)
2169 }
2170
2171 pub fn register_destination_with_proof(
2176 &self,
2177 dest: &crate::destination::Destination,
2178 signing_key: Option<[u8; 64]>,
2179 ) -> Result<(), SendError> {
2180 self.register_destination(dest.hash.0, dest.dest_type.to_wire_constant())?;
2182
2183 if dest.proof_strategy != rns_core::types::ProofStrategy::ProveNone {
2185 self.tx
2186 .send(Event::RegisterProofStrategy {
2187 dest_hash: dest.hash.0,
2188 strategy: dest.proof_strategy,
2189 signing_key,
2190 })
2191 .map_err(|_| SendError)?;
2192 }
2193
2194 Ok(())
2195 }
2196
2197 pub fn request_path(&self, dest_hash: &rns_core::types::DestHash) -> Result<(), SendError> {
2199 self.reject_new_work_if_draining()?;
2200 self.tx
2201 .send(Event::RequestPath {
2202 dest_hash: dest_hash.0,
2203 })
2204 .map_err(|_| SendError)
2205 }
2206
2207 pub fn has_path(&self, dest_hash: &rns_core::types::DestHash) -> Result<bool, SendError> {
2209 match self.query(QueryRequest::HasPath {
2210 dest_hash: dest_hash.0,
2211 })? {
2212 QueryResponse::HasPath(v) => Ok(v),
2213 _ => Ok(false),
2214 }
2215 }
2216
2217 pub fn hops_to(&self, dest_hash: &rns_core::types::DestHash) -> Result<Option<u8>, SendError> {
2219 match self.query(QueryRequest::HopsTo {
2220 dest_hash: dest_hash.0,
2221 })? {
2222 QueryResponse::HopsTo(v) => Ok(v),
2223 _ => Ok(None),
2224 }
2225 }
2226
2227 pub fn recall_identity(
2229 &self,
2230 dest_hash: &rns_core::types::DestHash,
2231 ) -> Result<Option<crate::destination::AnnouncedIdentity>, SendError> {
2232 match self.query(QueryRequest::RecallIdentity {
2233 dest_hash: dest_hash.0,
2234 })? {
2235 QueryResponse::RecallIdentity(v) => Ok(v),
2236 _ => Ok(None),
2237 }
2238 }
2239
2240 pub fn known_destinations(
2242 &self,
2243 ) -> Result<Vec<crate::event::KnownDestinationEntry>, SendError> {
2244 match self.query(QueryRequest::KnownDestinations)? {
2245 QueryResponse::KnownDestinations(entries) => Ok(entries),
2246 _ => Ok(Vec::new()),
2247 }
2248 }
2249
2250 pub fn retain_known_destination(
2252 &self,
2253 dest_hash: &rns_core::types::DestHash,
2254 ) -> Result<bool, SendError> {
2255 match self.query(QueryRequest::RetainKnownDestination {
2256 dest_hash: dest_hash.0,
2257 })? {
2258 QueryResponse::RetainKnownDestination(ok) => Ok(ok),
2259 _ => Ok(false),
2260 }
2261 }
2262
2263 pub fn unretain_known_destination(
2265 &self,
2266 dest_hash: &rns_core::types::DestHash,
2267 ) -> Result<bool, SendError> {
2268 match self.query(QueryRequest::UnretainKnownDestination {
2269 dest_hash: dest_hash.0,
2270 })? {
2271 QueryResponse::UnretainKnownDestination(ok) => Ok(ok),
2272 _ => Ok(false),
2273 }
2274 }
2275
2276 pub fn mark_known_destination_used(
2278 &self,
2279 dest_hash: &rns_core::types::DestHash,
2280 ) -> Result<bool, SendError> {
2281 match self.query(QueryRequest::MarkKnownDestinationUsed {
2282 dest_hash: dest_hash.0,
2283 })? {
2284 QueryResponse::MarkKnownDestinationUsed(ok) => Ok(ok),
2285 _ => Ok(false),
2286 }
2287 }
2288
2289 fn persist_known_destinations(&self) {
2290 let Some(path) = self.known_destinations_path.as_ref() else {
2291 return;
2292 };
2293
2294 let Ok(entries) = self.known_destinations() else {
2295 return;
2296 };
2297
2298 let destinations: std::collections::HashMap<[u8; 16], storage::KnownDestination> = entries
2299 .into_iter()
2300 .map(|entry| {
2301 (
2302 entry.dest_hash,
2303 storage::KnownDestination {
2304 identity_hash: entry.identity_hash,
2305 public_key: entry.public_key,
2306 app_data: entry.app_data,
2307 hops: entry.hops,
2308 received_at: entry.received_at,
2309 receiving_interface: entry.receiving_interface.0,
2310 was_used: entry.was_used,
2311 last_used_at: entry.last_used_at,
2312 retained: entry.retained,
2313 },
2314 )
2315 })
2316 .collect();
2317
2318 if let Err(err) = storage::save_known_destinations(&destinations, path) {
2319 log::warn!("failed to persist known destinations: {}", err);
2320 }
2321 }
2322
2323 pub fn load_hook(
2325 &self,
2326 name: String,
2327 wasm_bytes: Vec<u8>,
2328 attach_point: String,
2329 priority: i32,
2330 ) -> Result<Result<(), String>, SendError> {
2331 let (response_tx, response_rx) = std::sync::mpsc::channel();
2332 self.tx
2333 .send(Event::LoadHook {
2334 name,
2335 wasm_bytes,
2336 attach_point,
2337 priority,
2338 response_tx,
2339 })
2340 .map_err(|_| SendError)?;
2341 response_rx.recv().map_err(|_| SendError)
2342 }
2343
2344 pub fn load_hook_file(
2346 &self,
2347 name: String,
2348 path: String,
2349 hook_type: String,
2350 attach_point: String,
2351 priority: i32,
2352 ) -> Result<Result<(), String>, SendError> {
2353 let (response_tx, response_rx) = std::sync::mpsc::channel();
2354 self.tx
2355 .send(Event::LoadHookFile {
2356 name,
2357 path,
2358 hook_type,
2359 attach_point,
2360 priority,
2361 response_tx,
2362 })
2363 .map_err(|_| SendError)?;
2364 response_rx.recv().map_err(|_| SendError)
2365 }
2366
2367 pub fn load_builtin_hook(
2369 &self,
2370 name: String,
2371 builtin_id: String,
2372 attach_point: String,
2373 priority: i32,
2374 ) -> Result<Result<(), String>, SendError> {
2375 let (response_tx, response_rx) = std::sync::mpsc::channel();
2376 self.tx
2377 .send(Event::LoadBuiltinHook {
2378 name,
2379 builtin_id,
2380 attach_point,
2381 priority,
2382 response_tx,
2383 })
2384 .map_err(|_| SendError)?;
2385 response_rx.recv().map_err(|_| SendError)
2386 }
2387
2388 pub fn unload_hook(
2390 &self,
2391 name: String,
2392 attach_point: String,
2393 ) -> Result<Result<(), String>, SendError> {
2394 let (response_tx, response_rx) = std::sync::mpsc::channel();
2395 self.tx
2396 .send(Event::UnloadHook {
2397 name,
2398 attach_point,
2399 response_tx,
2400 })
2401 .map_err(|_| SendError)?;
2402 response_rx.recv().map_err(|_| SendError)
2403 }
2404
2405 pub fn reload_hook(
2407 &self,
2408 name: String,
2409 attach_point: String,
2410 wasm_bytes: Vec<u8>,
2411 ) -> Result<Result<(), String>, SendError> {
2412 let (response_tx, response_rx) = std::sync::mpsc::channel();
2413 self.tx
2414 .send(Event::ReloadHook {
2415 name,
2416 attach_point,
2417 wasm_bytes,
2418 response_tx,
2419 })
2420 .map_err(|_| SendError)?;
2421 response_rx.recv().map_err(|_| SendError)
2422 }
2423
2424 pub fn reload_hook_file(
2426 &self,
2427 name: String,
2428 attach_point: String,
2429 path: String,
2430 hook_type: String,
2431 ) -> Result<Result<(), String>, SendError> {
2432 let (response_tx, response_rx) = std::sync::mpsc::channel();
2433 self.tx
2434 .send(Event::ReloadHookFile {
2435 name,
2436 attach_point,
2437 path,
2438 hook_type,
2439 response_tx,
2440 })
2441 .map_err(|_| SendError)?;
2442 response_rx.recv().map_err(|_| SendError)
2443 }
2444
2445 pub fn reload_builtin_hook(
2447 &self,
2448 name: String,
2449 attach_point: String,
2450 builtin_id: String,
2451 ) -> Result<Result<(), String>, SendError> {
2452 let (response_tx, response_rx) = std::sync::mpsc::channel();
2453 self.tx
2454 .send(Event::ReloadBuiltinHook {
2455 name,
2456 attach_point,
2457 builtin_id,
2458 response_tx,
2459 })
2460 .map_err(|_| SendError)?;
2461 response_rx.recv().map_err(|_| SendError)
2462 }
2463
2464 pub fn set_hook_enabled(
2466 &self,
2467 name: String,
2468 attach_point: String,
2469 enabled: bool,
2470 ) -> Result<Result<(), String>, SendError> {
2471 let (response_tx, response_rx) = std::sync::mpsc::channel();
2472 self.tx
2473 .send(Event::SetHookEnabled {
2474 name,
2475 attach_point,
2476 enabled,
2477 response_tx,
2478 })
2479 .map_err(|_| SendError)?;
2480 response_rx.recv().map_err(|_| SendError)
2481 }
2482
2483 pub fn set_hook_priority(
2485 &self,
2486 name: String,
2487 attach_point: String,
2488 priority: i32,
2489 ) -> Result<Result<(), String>, SendError> {
2490 let (response_tx, response_rx) = std::sync::mpsc::channel();
2491 self.tx
2492 .send(Event::SetHookPriority {
2493 name,
2494 attach_point,
2495 priority,
2496 response_tx,
2497 })
2498 .map_err(|_| SendError)?;
2499 response_rx.recv().map_err(|_| SendError)
2500 }
2501
2502 pub fn list_hooks(&self) -> Result<Vec<crate::event::HookInfo>, SendError> {
2504 let (response_tx, response_rx) = std::sync::mpsc::channel();
2505 self.tx
2506 .send(Event::ListHooks { response_tx })
2507 .map_err(|_| SendError)?;
2508 response_rx.recv().map_err(|_| SendError)
2509 }
2510
2511 pub(crate) fn from_parts(
2514 tx: EventSender,
2515 driver_handle: thread::JoinHandle<()>,
2516 rpc_server: Option<crate::rpc::RpcServer>,
2517 tick_interval_ms: Arc<AtomicU64>,
2518 ) -> Self {
2519 RnsNode {
2520 tx,
2521 driver_handle: Some(driver_handle),
2522 verify_handle: None,
2523 verify_shutdown: Arc::new(AtomicBool::new(false)),
2524 rpc_server,
2525 tick_interval_ms,
2526 probe_server: None,
2527 known_destinations_path: None,
2528 ratchet_store: None,
2529 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
2530 }
2531 }
2532
2533 pub fn event_sender(&self) -> &EventSender {
2535 &self.tx
2536 }
2537
2538 pub fn set_tick_interval(&self, ms: u64) -> u64 {
2543 let clamped = ms.clamp(100, 10_000);
2544 if clamped != ms {
2545 log::warn!(
2546 "tick interval {}ms out of range, clamped to {}ms",
2547 ms,
2548 clamped
2549 );
2550 }
2551 self.tick_interval_ms.store(clamped, Ordering::Relaxed);
2552 clamped
2553 }
2554
2555 pub fn tick_interval(&self) -> u64 {
2557 self.tick_interval_ms.load(Ordering::Relaxed)
2558 }
2559
2560 pub fn shutdown(mut self) {
2562 if let Some(mut rpc) = self.rpc_server.take() {
2564 rpc.stop();
2565 }
2566 self.persist_known_destinations();
2567 self.verify_shutdown.store(true, Ordering::Relaxed);
2568 let _ = self.tx.send(Event::Shutdown);
2569 if let Some(handle) = self.driver_handle.take() {
2570 let _ = handle.join();
2571 }
2572 if let Some(handle) = self.verify_handle.take() {
2573 let _ = handle.join();
2574 }
2575 }
2576}
2577
2578#[cfg(test)]
2579mod tests {
2580 use super::*;
2581 use crate::storage::RatchetStore;
2582 use std::fs;
2583 use tempfile::tempdir;
2584
2585 struct NoopCallbacks;
2586
2587 impl Callbacks for NoopCallbacks {
2588 fn on_announce(&mut self, _: crate::destination::AnnouncedIdentity) {}
2589 fn on_path_updated(&mut self, _: rns_core::types::DestHash, _: u8) {}
2590 fn on_local_delivery(
2591 &mut self,
2592 _: rns_core::types::DestHash,
2593 _: Vec<u8>,
2594 _: rns_core::types::PacketHash,
2595 ) {
2596 }
2597 }
2598
2599 struct TestNodeRatchetStore {
2600 entry: storage::RatchetEntry,
2601 current_calls: std::sync::Mutex<Vec<[u8; 16]>>,
2602 }
2603
2604 impl storage::RatchetStore for TestNodeRatchetStore {
2605 fn remember(&self, _dest_hash: [u8; 16], _entry: storage::RatchetEntry) -> io::Result<()> {
2606 Ok(())
2607 }
2608
2609 fn current(
2610 &self,
2611 dest_hash: &[u8; 16],
2612 _now: f64,
2613 _expiry_secs: f64,
2614 ) -> io::Result<Option<storage::RatchetEntry>> {
2615 self.current_calls.lock().unwrap().push(*dest_hash);
2616 Ok(Some(self.entry))
2617 }
2618
2619 fn cleanup(
2620 &self,
2621 _known_destinations: &HashSet<[u8; 16]>,
2622 _now: f64,
2623 _expiry_secs: f64,
2624 ) -> io::Result<storage::RatchetCleanupStats> {
2625 Ok(Default::default())
2626 }
2627 }
2628
2629 #[test]
2630 fn send_packet_checks_ratchet_store_for_single_destinations() {
2631 let store = Arc::new(TestNodeRatchetStore {
2632 entry: storage::RatchetEntry {
2633 ratchet: [0x55; 32],
2634 received_at: time::now(),
2635 },
2636 current_calls: std::sync::Mutex::new(Vec::new()),
2637 });
2638 let ratchet_store: Arc<dyn storage::RatchetStore> = store.clone();
2639 let node = RnsNode::start(
2640 NodeConfig {
2641 ratchet_store: Some(ratchet_store),
2642 ..Default::default()
2643 },
2644 Box::new(NoopCallbacks),
2645 )
2646 .unwrap();
2647
2648 let mut rng = OsRng;
2649 let remote_identity = Identity::new(&mut rng);
2650 let public_key = remote_identity.get_public_key().unwrap();
2651 let announced = crate::destination::AnnouncedIdentity {
2652 dest_hash: rns_core::types::DestHash([0x22; 16]),
2653 identity_hash: rns_core::types::IdentityHash(*remote_identity.hash()),
2654 public_key,
2655 app_data: None,
2656 hops: 1,
2657 received_at: time::now(),
2658 receiving_interface: rns_core::transport::types::InterfaceId(0),
2659 };
2660 let dest = crate::destination::Destination::single_out("test", &["ratchet"], &announced);
2661
2662 node.send_packet(&dest, b"hello").unwrap();
2663 assert_eq!(
2664 store.current_calls.lock().unwrap().as_slice(),
2665 &[dest.hash.0]
2666 );
2667
2668 node.shutdown();
2669 }
2670
2671 #[test]
2672 fn single_payload_uses_stored_ratchet_key_material() {
2673 let ratchet_prv = rns_crypto::x25519::X25519PrivateKey::from_bytes(&[0x42; 32]);
2674 let ratchet_pub = ratchet_prv.public_key().public_bytes();
2675 let store = Arc::new(TestNodeRatchetStore {
2676 entry: storage::RatchetEntry {
2677 ratchet: ratchet_pub,
2678 received_at: time::now(),
2679 },
2680 current_calls: std::sync::Mutex::new(Vec::new()),
2681 });
2682 let ratchet_store: Arc<dyn storage::RatchetStore> = store.clone();
2683 let (tx, _rx) = crate::event::channel();
2684 let node = RnsNode {
2685 tx,
2686 driver_handle: None,
2687 verify_handle: None,
2688 verify_shutdown: Arc::new(AtomicBool::new(false)),
2689 rpc_server: None,
2690 tick_interval_ms: Arc::new(AtomicU64::new(1000)),
2691 probe_server: None,
2692 known_destinations_path: None,
2693 ratchet_store: Some(ratchet_store),
2694 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
2695 };
2696
2697 let mut rng = OsRng;
2698 let remote_identity = Identity::new(&mut rng);
2699 let public_key = remote_identity.get_public_key().unwrap();
2700 let announced = crate::destination::AnnouncedIdentity {
2701 dest_hash: rns_core::types::DestHash([0x33; 16]),
2702 identity_hash: rns_core::types::IdentityHash(*remote_identity.hash()),
2703 public_key,
2704 app_data: None,
2705 hops: 1,
2706 received_at: time::now(),
2707 receiving_interface: rns_core::transport::types::InterfaceId(0),
2708 };
2709 let dest = crate::destination::Destination::single_out("test", &["ratchet"], &announced);
2710 let payload = node
2711 .encrypt_single_payload(&dest, b"ratchet message")
2712 .unwrap();
2713
2714 assert_eq!(
2715 store.current_calls.lock().unwrap().as_slice(),
2716 &[dest.hash.0]
2717 );
2718 assert!(remote_identity.decrypt(&payload).is_err());
2719
2720 let peer_pub_bytes: [u8; 32] = payload[..32].try_into().unwrap();
2721 let peer_pub = rns_crypto::x25519::X25519PublicKey::from_bytes(&peer_pub_bytes);
2722 let shared_key = ratchet_prv.exchange(&peer_pub);
2723 let derived_key = rns_crypto::hkdf::hkdf(
2724 rns_crypto::identity::DERIVED_KEY_LENGTH,
2725 &shared_key,
2726 Some(remote_identity.hash()),
2727 None,
2728 )
2729 .unwrap();
2730 let token = rns_crypto::token::Token::new(&derived_key).unwrap();
2731 let plaintext = token.decrypt(&payload[32..]).unwrap();
2732 assert_eq!(plaintext, b"ratchet message");
2733 }
2734
2735 #[test]
2736 fn tcp_client_interface_is_not_discoverable_without_kiss_framing() {
2737 let mut params = std::collections::HashMap::new();
2738 params.insert("discoverable".to_string(), "yes".to_string());
2739 params.insert(
2740 "discovery_name".to_string(),
2741 "invalid-tcp-client".to_string(),
2742 );
2743 params.insert("reachable_on".to_string(), "example.com".to_string());
2744 params.insert("target_port".to_string(), "4242".to_string());
2745
2746 let discovery =
2747 super::extract_discovery_config("tcp-client", "TCPClientInterface", ¶ms);
2748
2749 assert!(
2750 discovery.is_none(),
2751 "TCPClientInterface discovery must be rejected unless KISS framing is supported"
2752 );
2753 }
2754
2755 #[test]
2756 fn ingress_control_config_defaults_by_interface_type() {
2757 let params = std::collections::HashMap::new();
2758
2759 let tcp = super::parse_ingress_control_config("TCPServerInterface", ¶ms).unwrap();
2760 assert!(tcp.enabled);
2761 assert_eq!(
2762 tcp.max_held_announces,
2763 rns_core::constants::IC_MAX_HELD_ANNOUNCES
2764 );
2765 assert_eq!(tcp.burst_hold, rns_core::constants::IC_BURST_HOLD);
2766
2767 let pipe = super::parse_ingress_control_config("PipeInterface", ¶ms).unwrap();
2768 assert!(!pipe.enabled);
2769 assert_eq!(
2770 pipe.held_release_interval,
2771 rns_core::constants::IC_HELD_RELEASE_INTERVAL
2772 );
2773 }
2774
2775 #[test]
2776 fn ingress_control_config_parses_python_ic_keys() {
2777 let mut params = std::collections::HashMap::new();
2778 params.insert("ingress_control".to_string(), "No".to_string());
2779 params.insert("ic_max_held_announces".to_string(), "17".to_string());
2780 params.insert("ic_burst_hold".to_string(), "1.5".to_string());
2781 params.insert("ic_burst_freq_new".to_string(), "2.5".to_string());
2782 params.insert("ic_burst_freq".to_string(), "3.5".to_string());
2783 params.insert("ic_new_time".to_string(), "4.5".to_string());
2784 params.insert("ic_burst_penalty".to_string(), "5.5".to_string());
2785 params.insert("ic_held_release_interval".to_string(), "6.5".to_string());
2786
2787 let config = super::parse_ingress_control_config("TCPServerInterface", ¶ms).unwrap();
2788
2789 assert!(!config.enabled);
2790 assert_eq!(config.max_held_announces, 17);
2791 assert_eq!(config.burst_hold, 1.5);
2792 assert_eq!(config.burst_freq_new, 2.5);
2793 assert_eq!(config.burst_freq, 3.5);
2794 assert_eq!(config.new_time, 4.5);
2795 assert_eq!(config.burst_penalty, 5.5);
2796 assert_eq!(config.held_release_interval, 6.5);
2797 }
2798
2799 #[test]
2800 fn ingress_control_config_rejects_invalid_values() {
2801 let mut params = std::collections::HashMap::new();
2802 params.insert("ic_burst_hold".to_string(), "-1".to_string());
2803
2804 let err = super::parse_ingress_control_config("TCPServerInterface", ¶ms).unwrap_err();
2805
2806 assert!(err.contains("ic_burst_hold"));
2807 }
2808
2809 #[test]
2810 fn start_and_shutdown() {
2811 let node = RnsNode::start(
2812 NodeConfig {
2813 panic_on_interface_error: false,
2814 transport_enabled: false,
2815 identity: None,
2816 interfaces: vec![],
2817 share_instance: false,
2818 instance_name: "default".into(),
2819 shared_instance_port: 37428,
2820 rpc_port: 0,
2821 cache_dir: None,
2822 ratchet_store: None,
2823 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
2824 management: Default::default(),
2825 probe_port: None,
2826 probe_addrs: vec![],
2827 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
2828 device: None,
2829 hooks: Vec::new(),
2830 discover_interfaces: false,
2831 discovery_required_value: None,
2832 respond_to_probes: false,
2833 prefer_shorter_path: false,
2834 max_paths_per_destination: 1,
2835 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
2836 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
2837 max_path_destinations: usize::MAX,
2838 max_tunnel_destinations_total: usize::MAX,
2839 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
2840 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
2841 announce_table_ttl: Duration::from_secs(
2842 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
2843 ),
2844 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
2845 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
2846 interface_writer_queue_capacity:
2847 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
2848 #[cfg(feature = "iface-backbone")]
2849 backbone_peer_pool: None,
2850 announce_sig_cache_enabled: true,
2851 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
2852 announce_sig_cache_ttl: Duration::from_secs(
2853 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
2854 ),
2855 registry: None,
2856 #[cfg(feature = "hooks")]
2857 provider_bridge: None,
2858 },
2859 Box::new(NoopCallbacks),
2860 )
2861 .unwrap();
2862 node.shutdown();
2863 }
2864
2865 #[test]
2866 fn known_destinations_persist_across_restart() {
2867 let dir = tempdir().unwrap();
2868 let dest_hash = [0x91; 16];
2869 let identity = Identity::new(&mut OsRng);
2870 let last_used_at = 77.0;
2871 let receiving_interface = rns_core::transport::types::InterfaceId(42);
2872
2873 let node = RnsNode::from_config(Some(dir.path()), Box::new(NoopCallbacks)).unwrap();
2874 let (response_tx, response_rx) = std::sync::mpsc::channel();
2875 node.event_sender()
2876 .send(crate::event::Event::Query(
2877 QueryRequest::RestoreKnownDestination(crate::event::KnownDestinationEntry {
2878 dest_hash,
2879 identity_hash: *identity.hash(),
2880 public_key: identity.get_public_key().unwrap(),
2881 app_data: Some(b"persisted".to_vec()),
2882 hops: 2,
2883 received_at: 55.0,
2884 receiving_interface,
2885 was_used: true,
2886 last_used_at: Some(last_used_at),
2887 retained: true,
2888 }),
2889 response_tx,
2890 ))
2891 .unwrap();
2892 assert!(matches!(
2893 response_rx.recv().unwrap(),
2894 QueryResponse::RestoreKnownDestination(true)
2895 ));
2896 node.shutdown();
2897
2898 let restarted = RnsNode::from_config(Some(dir.path()), Box::new(NoopCallbacks)).unwrap();
2899 let entries = restarted.known_destinations().unwrap();
2900 let entry = entries
2901 .iter()
2902 .find(|entry| entry.dest_hash == dest_hash)
2903 .expect("reloaded destination should appear in lifecycle listing");
2904 assert!(entry.retained);
2905 assert!(entry.was_used);
2906 assert_eq!(entry.hops, 2);
2907 assert_eq!(entry.receiving_interface, receiving_interface);
2908 assert_eq!(entry.last_used_at, Some(last_used_at));
2909
2910 let recalled = restarted
2911 .recall_identity(&rns_core::types::DestHash(dest_hash))
2912 .unwrap()
2913 .expect("known destination should reload from storage");
2914 assert_eq!(recalled.identity_hash.0, *identity.hash());
2915 assert_eq!(recalled.app_data, Some(b"persisted".to_vec()));
2916 restarted.shutdown();
2917 }
2918
2919 #[test]
2920 fn from_config_cleans_persistent_ratchets_after_loading_known_destinations() {
2921 fn hex(bytes: &[u8]) -> String {
2922 bytes.iter().map(|byte| format!("{:02x}", byte)).collect()
2923 }
2924
2925 let dir = tempdir().unwrap();
2926 fs::write(
2927 dir.path().join("config"),
2928 "[reticulum]\nenable_transport = False\nshare_instance = False\nratchet_expiry = 300\n",
2929 )
2930 .unwrap();
2931 let paths = storage::ensure_storage_dirs(dir.path()).unwrap();
2932 let known_live = [0x01; 16];
2933 let known_expired = [0x02; 16];
2934 let unknown = [0x03; 16];
2935 let known_corrupt = [0x04; 16];
2936 let now = time::now();
2937
2938 let identity = Identity::new(&mut OsRng);
2939 let known = std::collections::HashMap::from([
2940 (
2941 known_live,
2942 storage::KnownDestination {
2943 identity_hash: *identity.hash(),
2944 public_key: identity.get_public_key().unwrap(),
2945 app_data: None,
2946 hops: 1,
2947 received_at: now,
2948 receiving_interface: 0,
2949 was_used: false,
2950 last_used_at: None,
2951 retained: false,
2952 },
2953 ),
2954 (
2955 known_expired,
2956 storage::KnownDestination {
2957 identity_hash: *identity.hash(),
2958 public_key: identity.get_public_key().unwrap(),
2959 app_data: None,
2960 hops: 1,
2961 received_at: now,
2962 receiving_interface: 0,
2963 was_used: false,
2964 last_used_at: None,
2965 retained: false,
2966 },
2967 ),
2968 (
2969 known_corrupt,
2970 storage::KnownDestination {
2971 identity_hash: *identity.hash(),
2972 public_key: identity.get_public_key().unwrap(),
2973 app_data: None,
2974 hops: 1,
2975 received_at: now,
2976 receiving_interface: 0,
2977 was_used: false,
2978 last_used_at: None,
2979 retained: false,
2980 },
2981 ),
2982 ]);
2983 storage::save_known_destinations(&known, &paths.storage.join("known_destinations"))
2984 .unwrap();
2985
2986 let store = storage::FsRatchetStore::new(paths.ratchets.clone());
2987 store
2988 .remember(
2989 known_live,
2990 storage::RatchetEntry {
2991 ratchet: [0x11; 32],
2992 received_at: now,
2993 },
2994 )
2995 .unwrap();
2996 store
2997 .remember(
2998 known_expired,
2999 storage::RatchetEntry {
3000 ratchet: [0x22; 32],
3001 received_at: now - 1000.0,
3002 },
3003 )
3004 .unwrap();
3005 store
3006 .remember(
3007 unknown,
3008 storage::RatchetEntry {
3009 ratchet: [0x33; 32],
3010 received_at: now,
3011 },
3012 )
3013 .unwrap();
3014 fs::write(paths.ratchets.join(hex(&known_corrupt)), b"not msgpack").unwrap();
3015 fs::write(paths.ratchets.join("0102.out"), b"temp").unwrap();
3016
3017 let node = RnsNode::from_config(Some(dir.path()), Box::new(NoopCallbacks)).unwrap();
3018
3019 assert!(paths.ratchets.join(hex(&known_live)).exists());
3020 assert!(!paths.ratchets.join(hex(&known_expired)).exists());
3021 assert!(!paths.ratchets.join(hex(&unknown)).exists());
3022 assert!(!paths.ratchets.join(hex(&known_corrupt)).exists());
3023 assert!(!paths.ratchets.join("0102.out").exists());
3024
3025 node.shutdown();
3026 }
3027
3028 #[test]
3029 fn start_with_identity() {
3030 let identity = Identity::new(&mut OsRng);
3031 let hash = *identity.hash();
3032 let node = RnsNode::start(
3033 NodeConfig {
3034 panic_on_interface_error: false,
3035 transport_enabled: true,
3036 identity: Some(identity),
3037 interfaces: vec![],
3038 share_instance: false,
3039 instance_name: "default".into(),
3040 shared_instance_port: 37428,
3041 rpc_port: 0,
3042 cache_dir: None,
3043 ratchet_store: None,
3044 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
3045 management: Default::default(),
3046 probe_port: None,
3047 probe_addrs: vec![],
3048 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
3049 device: None,
3050 hooks: Vec::new(),
3051 discover_interfaces: false,
3052 discovery_required_value: None,
3053 respond_to_probes: false,
3054 prefer_shorter_path: false,
3055 max_paths_per_destination: 1,
3056 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
3057 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
3058 max_path_destinations: usize::MAX,
3059 max_tunnel_destinations_total: usize::MAX,
3060 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
3061 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
3062 announce_table_ttl: Duration::from_secs(
3063 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
3064 ),
3065 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
3066 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
3067 interface_writer_queue_capacity:
3068 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
3069 #[cfg(feature = "iface-backbone")]
3070 backbone_peer_pool: None,
3071 announce_sig_cache_enabled: true,
3072 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
3073 announce_sig_cache_ttl: Duration::from_secs(
3074 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
3075 ),
3076 registry: None,
3077 #[cfg(feature = "hooks")]
3078 provider_bridge: None,
3079 },
3080 Box::new(NoopCallbacks),
3081 )
3082 .unwrap();
3083 let _ = hash;
3085 node.shutdown();
3086 }
3087
3088 #[test]
3089 fn start_generates_identity() {
3090 let node = RnsNode::start(
3091 NodeConfig {
3092 panic_on_interface_error: false,
3093 transport_enabled: false,
3094 identity: None,
3095 interfaces: vec![],
3096 share_instance: false,
3097 instance_name: "default".into(),
3098 shared_instance_port: 37428,
3099 rpc_port: 0,
3100 cache_dir: None,
3101 ratchet_store: None,
3102 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
3103 management: Default::default(),
3104 probe_port: None,
3105 probe_addrs: vec![],
3106 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
3107 device: None,
3108 hooks: Vec::new(),
3109 discover_interfaces: false,
3110 discovery_required_value: None,
3111 respond_to_probes: false,
3112 prefer_shorter_path: false,
3113 max_paths_per_destination: 1,
3114 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
3115 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
3116 max_path_destinations: usize::MAX,
3117 max_tunnel_destinations_total: usize::MAX,
3118 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
3119 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
3120 announce_table_ttl: Duration::from_secs(
3121 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
3122 ),
3123 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
3124 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
3125 interface_writer_queue_capacity:
3126 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
3127 #[cfg(feature = "iface-backbone")]
3128 backbone_peer_pool: None,
3129 announce_sig_cache_enabled: true,
3130 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
3131 announce_sig_cache_ttl: Duration::from_secs(
3132 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
3133 ),
3134 registry: None,
3135 #[cfg(feature = "hooks")]
3136 provider_bridge: None,
3137 },
3138 Box::new(NoopCallbacks),
3139 )
3140 .unwrap();
3141 node.shutdown();
3143 }
3144
3145 #[test]
3146 fn from_config_creates_identity() {
3147 let dir = std::env::temp_dir().join(format!("rns-test-fc-{}", std::process::id()));
3148 let _ = fs::remove_dir_all(&dir);
3149 fs::create_dir_all(&dir).unwrap();
3150
3151 fs::write(
3153 dir.join("config"),
3154 "[reticulum]\nenable_transport = False\n",
3155 )
3156 .unwrap();
3157
3158 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
3159
3160 assert!(dir.join("storage/identities/identity").exists());
3162
3163 node.shutdown();
3164 let _ = fs::remove_dir_all(&dir);
3165 }
3166
3167 #[test]
3168 fn from_config_loads_identity() {
3169 let dir = std::env::temp_dir().join(format!("rns-test-fl-{}", std::process::id()));
3170 let _ = fs::remove_dir_all(&dir);
3171 fs::create_dir_all(dir.join("storage/identities")).unwrap();
3172
3173 let identity = Identity::new(&mut OsRng);
3175 let hash = *identity.hash();
3176 storage::save_identity(&identity, &dir.join("storage/identities/identity")).unwrap();
3177
3178 fs::write(
3179 dir.join("config"),
3180 "[reticulum]\nenable_transport = False\n",
3181 )
3182 .unwrap();
3183
3184 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
3185
3186 let loaded = storage::load_identity(&dir.join("storage/identities/identity")).unwrap();
3188 assert_eq!(*loaded.hash(), hash);
3189
3190 node.shutdown();
3191 let _ = fs::remove_dir_all(&dir);
3192 }
3193
3194 #[test]
3195 fn from_config_tcp_server() {
3196 let dir = std::env::temp_dir().join(format!("rns-test-fts-{}", std::process::id()));
3197 let _ = fs::remove_dir_all(&dir);
3198 fs::create_dir_all(&dir).unwrap();
3199
3200 let port = std::net::TcpListener::bind("127.0.0.1:0")
3202 .unwrap()
3203 .local_addr()
3204 .unwrap()
3205 .port();
3206
3207 let config = format!(
3208 r#"
3209[reticulum]
3210enable_transport = False
3211
3212[interfaces]
3213 [[Test TCP Server]]
3214 type = TCPServerInterface
3215 listen_ip = 127.0.0.1
3216 listen_port = {}
3217"#,
3218 port
3219 );
3220
3221 fs::write(dir.join("config"), config).unwrap();
3222
3223 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
3224
3225 thread::sleep(Duration::from_millis(100));
3227
3228 let _client = std::net::TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
3230
3231 node.shutdown();
3232 let _ = fs::remove_dir_all(&dir);
3233 }
3234
3235 #[test]
3236 fn from_config_starts_rpc_when_share_instance_enabled() {
3237 let dir = std::env::temp_dir().join(format!("rns-test-rpc-{}", std::process::id()));
3238 let _ = fs::remove_dir_all(&dir);
3239 fs::create_dir_all(&dir).unwrap();
3240
3241 let rpc_port = std::net::TcpListener::bind("127.0.0.1:0")
3242 .unwrap()
3243 .local_addr()
3244 .unwrap()
3245 .port();
3246
3247 let config = format!(
3248 r#"
3249[reticulum]
3250enable_transport = False
3251share_instance = Yes
3252instance_control_port = {}
3253
3254[interfaces]
3255"#,
3256 rpc_port
3257 );
3258
3259 fs::write(dir.join("config"), config).unwrap();
3260
3261 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
3262
3263 thread::sleep(Duration::from_millis(100));
3264
3265 let _client = std::net::TcpStream::connect(format!("127.0.0.1:{}", rpc_port)).unwrap();
3266
3267 node.shutdown();
3268 let _ = fs::remove_dir_all(&dir);
3269 }
3270
3271 #[test]
3272 fn from_config_starts_rpc_when_transport_enabled() {
3273 let dir =
3274 std::env::temp_dir().join(format!("rns-test-rpc-transport-{}", std::process::id()));
3275 let _ = fs::remove_dir_all(&dir);
3276 fs::create_dir_all(&dir).unwrap();
3277
3278 let rpc_port = std::net::TcpListener::bind("127.0.0.1:0")
3279 .unwrap()
3280 .local_addr()
3281 .unwrap()
3282 .port();
3283
3284 let config = format!(
3285 r#"
3286[reticulum]
3287enable_transport = True
3288share_instance = Yes
3289instance_control_port = {}
3290
3291[interfaces]
3292"#,
3293 rpc_port
3294 );
3295
3296 fs::write(dir.join("config"), config).unwrap();
3297
3298 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
3299
3300 thread::sleep(Duration::from_millis(100));
3301
3302 let _client = std::net::TcpStream::connect(format!("127.0.0.1:{}", rpc_port)).unwrap();
3303
3304 node.shutdown();
3305 let _ = fs::remove_dir_all(&dir);
3306 }
3307
3308 #[test]
3309 fn from_config_starts_rpc_when_tcp_client_is_unreachable() {
3310 let dir =
3311 std::env::temp_dir().join(format!("rns-test-rpc-unreachable-{}", std::process::id()));
3312 let _ = fs::remove_dir_all(&dir);
3313 fs::create_dir_all(&dir).unwrap();
3314
3315 let rpc_port = std::net::TcpListener::bind("127.0.0.1:0")
3316 .unwrap()
3317 .local_addr()
3318 .unwrap()
3319 .port();
3320 let unreachable_port = std::net::TcpListener::bind("127.0.0.1:0")
3321 .unwrap()
3322 .local_addr()
3323 .unwrap()
3324 .port();
3325
3326 let config = format!(
3327 r#"
3328[reticulum]
3329enable_transport = True
3330share_instance = Yes
3331instance_control_port = {}
3332
3333[interfaces]
3334 [[Unreachable Upstream]]
3335 type = TCPClientInterface
3336 target_host = 127.0.0.1
3337 target_port = {}
3338"#,
3339 rpc_port, unreachable_port
3340 );
3341
3342 fs::write(dir.join("config"), config).unwrap();
3343
3344 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
3345
3346 thread::sleep(Duration::from_millis(100));
3347
3348 let _client = std::net::TcpStream::connect(format!("127.0.0.1:{}", rpc_port)).unwrap();
3349
3350 node.shutdown();
3351 let _ = fs::remove_dir_all(&dir);
3352 }
3353
3354 #[test]
3355 fn test_parse_interface_mode() {
3356 use rns_core::constants::*;
3357
3358 assert_eq!(parse_interface_mode("full"), MODE_FULL);
3359 assert_eq!(parse_interface_mode("Full"), MODE_FULL);
3360 assert_eq!(parse_interface_mode("access_point"), MODE_ACCESS_POINT);
3361 assert_eq!(parse_interface_mode("accesspoint"), MODE_ACCESS_POINT);
3362 assert_eq!(parse_interface_mode("ap"), MODE_ACCESS_POINT);
3363 assert_eq!(parse_interface_mode("AP"), MODE_ACCESS_POINT);
3364 assert_eq!(parse_interface_mode("pointtopoint"), MODE_POINT_TO_POINT);
3365 assert_eq!(parse_interface_mode("ptp"), MODE_POINT_TO_POINT);
3366 assert_eq!(parse_interface_mode("roaming"), MODE_ROAMING);
3367 assert_eq!(parse_interface_mode("boundary"), MODE_BOUNDARY);
3368 assert_eq!(parse_interface_mode("gateway"), MODE_GATEWAY);
3369 assert_eq!(parse_interface_mode("gw"), MODE_GATEWAY);
3370 assert_eq!(parse_interface_mode("invalid"), MODE_FULL);
3372 }
3373
3374 #[test]
3375 fn to_node_config_serial() {
3376 let dir = std::env::temp_dir().join(format!("rns-test-serial-{}", std::process::id()));
3380 let _ = fs::remove_dir_all(&dir);
3381 fs::create_dir_all(&dir).unwrap();
3382
3383 let config = r#"
3384[reticulum]
3385enable_transport = False
3386
3387[interfaces]
3388 [[Test Serial Port]]
3389 type = SerialInterface
3390 port = /dev/nonexistent_rns_test_serial
3391 speed = 115200
3392 databits = 8
3393 parity = E
3394 stopbits = 1
3395 interface_mode = ptp
3396 networkname = testnet
3397"#;
3398 fs::write(dir.join("config"), config).unwrap();
3399
3400 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks))
3402 .expect("Config should parse; interface failure is non-fatal");
3403 node.shutdown();
3404
3405 let _ = fs::remove_dir_all(&dir);
3406 }
3407
3408 #[test]
3409 fn to_node_config_kiss() {
3410 let dir = std::env::temp_dir().join(format!("rns-test-kiss-{}", std::process::id()));
3412 let _ = fs::remove_dir_all(&dir);
3413 fs::create_dir_all(&dir).unwrap();
3414
3415 let config = r#"
3416[reticulum]
3417enable_transport = False
3418
3419[interfaces]
3420 [[Test KISS TNC]]
3421 type = KISSInterface
3422 port = /dev/nonexistent_rns_test_kiss
3423 speed = 9600
3424 preamble = 500
3425 txtail = 30
3426 persistence = 128
3427 slottime = 40
3428 flow_control = True
3429 id_interval = 600
3430 id_callsign = TEST0
3431 interface_mode = full
3432 passphrase = secretkey
3433"#;
3434 fs::write(dir.join("config"), config).unwrap();
3435
3436 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks))
3438 .expect("Config should parse; interface failure is non-fatal");
3439 node.shutdown();
3440
3441 let _ = fs::remove_dir_all(&dir);
3442 }
3443
3444 #[test]
3445 fn test_extract_ifac_config() {
3446 use std::collections::HashMap;
3447
3448 let params: HashMap<String, String> = HashMap::new();
3450 assert!(extract_ifac_config(¶ms, 16).is_none());
3451
3452 let mut params = HashMap::new();
3454 params.insert("networkname".into(), "testnet".into());
3455 let ifac = extract_ifac_config(¶ms, 16).unwrap();
3456 assert_eq!(ifac.netname.as_deref(), Some("testnet"));
3457 assert!(ifac.netkey.is_none());
3458 assert_eq!(ifac.size, 16);
3459
3460 let mut params = HashMap::new();
3462 params.insert("passphrase".into(), "secret".into());
3463 params.insert("ifac_size".into(), "64".into()); let ifac = extract_ifac_config(¶ms, 16).unwrap();
3465 assert!(ifac.netname.is_none());
3466 assert_eq!(ifac.netkey.as_deref(), Some("secret"));
3467 assert_eq!(ifac.size, 8);
3468
3469 let mut params = HashMap::new();
3471 params.insert("network_name".into(), "mynet".into());
3472 params.insert("pass_phrase".into(), "mykey".into());
3473 let ifac = extract_ifac_config(¶ms, 8).unwrap();
3474 assert_eq!(ifac.netname.as_deref(), Some("mynet"));
3475 assert_eq!(ifac.netkey.as_deref(), Some("mykey"));
3476 assert_eq!(ifac.size, 8);
3477 }
3478
3479 #[test]
3480 fn to_node_config_rnode() {
3481 let dir = std::env::temp_dir().join(format!("rns-test-rnode-{}", std::process::id()));
3484 let _ = fs::remove_dir_all(&dir);
3485 fs::create_dir_all(&dir).unwrap();
3486
3487 let config = r#"
3488[reticulum]
3489enable_transport = False
3490
3491[interfaces]
3492 [[Test RNode]]
3493 type = RNodeInterface
3494 port = /dev/nonexistent_rns_test_rnode
3495 frequency = 867200000
3496 bandwidth = 125000
3497 txpower = 7
3498 spreadingfactor = 8
3499 codingrate = 5
3500 flow_control = True
3501 st_alock = 5.0
3502 lt_alock = 2.5
3503 interface_mode = full
3504 networkname = testnet
3505"#;
3506 fs::write(dir.join("config"), config).unwrap();
3507
3508 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks))
3510 .expect("Config should parse; interface failure is non-fatal");
3511 node.shutdown();
3512
3513 let _ = fs::remove_dir_all(&dir);
3514 }
3515
3516 #[test]
3517 fn to_node_config_pipe() {
3518 let dir = std::env::temp_dir().join(format!("rns-test-pipe-{}", std::process::id()));
3521 let _ = fs::remove_dir_all(&dir);
3522 fs::create_dir_all(&dir).unwrap();
3523
3524 let config = r#"
3525[reticulum]
3526enable_transport = False
3527
3528[interfaces]
3529 [[Test Pipe]]
3530 type = PipeInterface
3531 command = cat
3532 respawn_delay = 5000
3533 interface_mode = full
3534"#;
3535 fs::write(dir.join("config"), config).unwrap();
3536
3537 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
3538 node.shutdown();
3540
3541 let _ = fs::remove_dir_all(&dir);
3542 }
3543
3544 #[test]
3545 fn to_node_config_backbone() {
3546 let dir = std::env::temp_dir().join(format!("rns-test-backbone-{}", std::process::id()));
3548 let _ = fs::remove_dir_all(&dir);
3549 fs::create_dir_all(&dir).unwrap();
3550
3551 let port = std::net::TcpListener::bind("127.0.0.1:0")
3552 .unwrap()
3553 .local_addr()
3554 .unwrap()
3555 .port();
3556
3557 let config = format!(
3558 r#"
3559[reticulum]
3560enable_transport = False
3561
3562[interfaces]
3563 [[Test Backbone]]
3564 type = BackboneInterface
3565 listen_ip = 127.0.0.1
3566 listen_port = {}
3567 interface_mode = full
3568"#,
3569 port
3570 );
3571
3572 fs::write(dir.join("config"), config).unwrap();
3573
3574 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
3575
3576 thread::sleep(Duration::from_millis(100));
3578
3579 {
3581 let _client = std::net::TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
3582 }
3584
3585 thread::sleep(Duration::from_millis(50));
3587
3588 node.shutdown();
3589 let _ = fs::remove_dir_all(&dir);
3590 }
3591
3592 #[test]
3593 fn rnode_config_defaults() {
3594 use crate::interface::rnode::{RNodeConfig, RNodeSubConfig};
3595
3596 let config = RNodeConfig::default();
3597 assert_eq!(config.speed, 115200);
3598 assert!(config.subinterfaces.is_empty());
3599 assert!(config.id_interval.is_none());
3600 assert!(config.id_callsign.is_none());
3601
3602 let sub = RNodeSubConfig {
3603 name: "test".into(),
3604 frequency: 868_000_000,
3605 bandwidth: 125_000,
3606 txpower: 7,
3607 spreading_factor: 8,
3608 coding_rate: 5,
3609 flow_control: false,
3610 st_alock: None,
3611 lt_alock: None,
3612 };
3613 assert_eq!(sub.frequency, 868_000_000);
3614 assert_eq!(sub.bandwidth, 125_000);
3615 assert!(!sub.flow_control);
3616 }
3617
3618 #[test]
3623 fn announce_builds_valid_packet() {
3624 let identity = Identity::new(&mut OsRng);
3625 let identity_hash = rns_core::types::IdentityHash(*identity.hash());
3626
3627 let node = RnsNode::start(
3628 NodeConfig {
3629 panic_on_interface_error: false,
3630 transport_enabled: false,
3631 identity: None,
3632 interfaces: vec![],
3633 share_instance: false,
3634 instance_name: "default".into(),
3635 shared_instance_port: 37428,
3636 rpc_port: 0,
3637 cache_dir: None,
3638 ratchet_store: None,
3639 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
3640 management: Default::default(),
3641 probe_port: None,
3642 probe_addrs: vec![],
3643 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
3644 device: None,
3645 hooks: Vec::new(),
3646 discover_interfaces: false,
3647 discovery_required_value: None,
3648 respond_to_probes: false,
3649 prefer_shorter_path: false,
3650 max_paths_per_destination: 1,
3651 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
3652 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
3653 max_path_destinations: usize::MAX,
3654 max_tunnel_destinations_total: usize::MAX,
3655 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
3656 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
3657 announce_table_ttl: Duration::from_secs(
3658 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
3659 ),
3660 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
3661 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
3662 interface_writer_queue_capacity:
3663 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
3664 #[cfg(feature = "iface-backbone")]
3665 backbone_peer_pool: None,
3666 announce_sig_cache_enabled: true,
3667 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
3668 announce_sig_cache_ttl: Duration::from_secs(
3669 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
3670 ),
3671 registry: None,
3672 #[cfg(feature = "hooks")]
3673 provider_bridge: None,
3674 },
3675 Box::new(NoopCallbacks),
3676 )
3677 .unwrap();
3678
3679 let dest = crate::destination::Destination::single_in("test", &["echo"], identity_hash);
3680
3681 node.register_destination(dest.hash.0, dest.dest_type.to_wire_constant())
3683 .unwrap();
3684
3685 let result = node.announce(&dest, &identity, Some(b"hello"));
3687 assert!(result.is_ok());
3688
3689 node.shutdown();
3690 }
3691
3692 #[test]
3693 fn has_path_and_hops_to() {
3694 let node = RnsNode::start(
3695 NodeConfig {
3696 panic_on_interface_error: false,
3697 transport_enabled: false,
3698 identity: None,
3699 interfaces: vec![],
3700 share_instance: false,
3701 instance_name: "default".into(),
3702 shared_instance_port: 37428,
3703 rpc_port: 0,
3704 cache_dir: None,
3705 ratchet_store: None,
3706 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
3707 management: Default::default(),
3708 probe_port: None,
3709 probe_addrs: vec![],
3710 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
3711 device: None,
3712 hooks: Vec::new(),
3713 discover_interfaces: false,
3714 discovery_required_value: None,
3715 respond_to_probes: false,
3716 prefer_shorter_path: false,
3717 max_paths_per_destination: 1,
3718 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
3719 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
3720 max_path_destinations: usize::MAX,
3721 max_tunnel_destinations_total: usize::MAX,
3722 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
3723 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
3724 announce_table_ttl: Duration::from_secs(
3725 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
3726 ),
3727 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
3728 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
3729 interface_writer_queue_capacity:
3730 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
3731 #[cfg(feature = "iface-backbone")]
3732 backbone_peer_pool: None,
3733 announce_sig_cache_enabled: true,
3734 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
3735 announce_sig_cache_ttl: Duration::from_secs(
3736 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
3737 ),
3738 registry: None,
3739 #[cfg(feature = "hooks")]
3740 provider_bridge: None,
3741 },
3742 Box::new(NoopCallbacks),
3743 )
3744 .unwrap();
3745
3746 let dh = rns_core::types::DestHash([0xAA; 16]);
3747
3748 assert_eq!(node.has_path(&dh).unwrap(), false);
3750 assert_eq!(node.hops_to(&dh).unwrap(), None);
3751
3752 node.shutdown();
3753 }
3754
3755 #[test]
3756 fn recall_identity_none_when_unknown() {
3757 let node = RnsNode::start(
3758 NodeConfig {
3759 panic_on_interface_error: false,
3760 transport_enabled: false,
3761 identity: None,
3762 interfaces: vec![],
3763 share_instance: false,
3764 instance_name: "default".into(),
3765 shared_instance_port: 37428,
3766 rpc_port: 0,
3767 cache_dir: None,
3768 ratchet_store: None,
3769 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
3770 management: Default::default(),
3771 probe_port: None,
3772 probe_addrs: vec![],
3773 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
3774 device: None,
3775 hooks: Vec::new(),
3776 discover_interfaces: false,
3777 discovery_required_value: None,
3778 respond_to_probes: false,
3779 prefer_shorter_path: false,
3780 max_paths_per_destination: 1,
3781 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
3782 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
3783 max_path_destinations: usize::MAX,
3784 max_tunnel_destinations_total: usize::MAX,
3785 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
3786 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
3787 announce_table_ttl: Duration::from_secs(
3788 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
3789 ),
3790 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
3791 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
3792 interface_writer_queue_capacity:
3793 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
3794 #[cfg(feature = "iface-backbone")]
3795 backbone_peer_pool: None,
3796 announce_sig_cache_enabled: true,
3797 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
3798 announce_sig_cache_ttl: Duration::from_secs(
3799 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
3800 ),
3801 registry: None,
3802 #[cfg(feature = "hooks")]
3803 provider_bridge: None,
3804 },
3805 Box::new(NoopCallbacks),
3806 )
3807 .unwrap();
3808
3809 let dh = rns_core::types::DestHash([0xBB; 16]);
3810 assert!(node.recall_identity(&dh).unwrap().is_none());
3811
3812 node.shutdown();
3813 }
3814
3815 #[test]
3816 fn request_path_does_not_crash() {
3817 let node = RnsNode::start(
3818 NodeConfig {
3819 panic_on_interface_error: false,
3820 transport_enabled: false,
3821 identity: None,
3822 interfaces: vec![],
3823 share_instance: false,
3824 instance_name: "default".into(),
3825 shared_instance_port: 37428,
3826 rpc_port: 0,
3827 cache_dir: None,
3828 ratchet_store: None,
3829 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
3830 management: Default::default(),
3831 probe_port: None,
3832 probe_addrs: vec![],
3833 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
3834 device: None,
3835 hooks: Vec::new(),
3836 discover_interfaces: false,
3837 discovery_required_value: None,
3838 respond_to_probes: false,
3839 prefer_shorter_path: false,
3840 max_paths_per_destination: 1,
3841 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
3842 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
3843 max_path_destinations: usize::MAX,
3844 max_tunnel_destinations_total: usize::MAX,
3845 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
3846 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
3847 announce_table_ttl: Duration::from_secs(
3848 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
3849 ),
3850 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
3851 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
3852 interface_writer_queue_capacity:
3853 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
3854 #[cfg(feature = "iface-backbone")]
3855 backbone_peer_pool: None,
3856 announce_sig_cache_enabled: true,
3857 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
3858 announce_sig_cache_ttl: Duration::from_secs(
3859 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
3860 ),
3861 registry: None,
3862 #[cfg(feature = "hooks")]
3863 provider_bridge: None,
3864 },
3865 Box::new(NoopCallbacks),
3866 )
3867 .unwrap();
3868
3869 let dh = rns_core::types::DestHash([0xCC; 16]);
3870 assert!(node.request_path(&dh).is_ok());
3871
3872 thread::sleep(Duration::from_millis(50));
3874
3875 node.shutdown();
3876 }
3877
3878 #[test]
3879 fn create_link_returns_error_while_draining() {
3880 let node = RnsNode::start(NodeConfig::default(), Box::new(NoopCallbacks)).unwrap();
3881
3882 node.begin_drain(Duration::from_secs(1)).unwrap();
3883 assert!(node.create_link([0xAB; 16], [0xCD; 32]).is_err());
3884
3885 node.shutdown();
3886 }
3887
3888 #[test]
3889 fn request_path_returns_error_while_draining() {
3890 let node = RnsNode::start(NodeConfig::default(), Box::new(NoopCallbacks)).unwrap();
3891
3892 node.begin_drain(Duration::from_secs(1)).unwrap();
3893 assert!(node
3894 .request_path(&rns_core::types::DestHash([0xAB; 16]))
3895 .is_err());
3896
3897 node.shutdown();
3898 }
3899
3900 #[test]
3905 fn send_packet_returns_error_while_draining() {
3906 let node = RnsNode::start(NodeConfig::default(), Box::new(NoopCallbacks)).unwrap();
3907 let dest = crate::destination::Destination::plain("drain-test", &["send"]);
3908
3909 node.begin_drain(Duration::from_secs(1)).unwrap();
3910 assert!(node.send_packet(&dest, b"hello").is_err());
3911
3912 node.shutdown();
3913 }
3914
3915 #[test]
3916 fn send_packet_plain() {
3917 let node = RnsNode::start(
3918 NodeConfig {
3919 panic_on_interface_error: false,
3920 transport_enabled: false,
3921 identity: None,
3922 interfaces: vec![],
3923 share_instance: false,
3924 instance_name: "default".into(),
3925 shared_instance_port: 37428,
3926 rpc_port: 0,
3927 cache_dir: None,
3928 ratchet_store: None,
3929 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
3930 management: Default::default(),
3931 probe_port: None,
3932 probe_addrs: vec![],
3933 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
3934 device: None,
3935 hooks: Vec::new(),
3936 discover_interfaces: false,
3937 discovery_required_value: None,
3938 respond_to_probes: false,
3939 prefer_shorter_path: false,
3940 max_paths_per_destination: 1,
3941 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
3942 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
3943 max_path_destinations: usize::MAX,
3944 max_tunnel_destinations_total: usize::MAX,
3945 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
3946 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
3947 announce_table_ttl: Duration::from_secs(
3948 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
3949 ),
3950 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
3951 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
3952 interface_writer_queue_capacity:
3953 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
3954 #[cfg(feature = "iface-backbone")]
3955 backbone_peer_pool: None,
3956 announce_sig_cache_enabled: true,
3957 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
3958 announce_sig_cache_ttl: Duration::from_secs(
3959 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
3960 ),
3961 registry: None,
3962 #[cfg(feature = "hooks")]
3963 provider_bridge: None,
3964 },
3965 Box::new(NoopCallbacks),
3966 )
3967 .unwrap();
3968
3969 let dest = crate::destination::Destination::plain("test", &["echo"]);
3970 let result = node.send_packet(&dest, b"hello world");
3971 assert!(result.is_ok());
3972
3973 let packet_hash = result.unwrap();
3974 assert_ne!(packet_hash.0, [0u8; 32]);
3976
3977 thread::sleep(Duration::from_millis(50));
3979
3980 node.shutdown();
3981 }
3982
3983 #[test]
3984 fn send_packet_single_requires_public_key() {
3985 let node = RnsNode::start(
3986 NodeConfig {
3987 panic_on_interface_error: false,
3988 transport_enabled: false,
3989 identity: None,
3990 interfaces: vec![],
3991 share_instance: false,
3992 instance_name: "default".into(),
3993 shared_instance_port: 37428,
3994 rpc_port: 0,
3995 cache_dir: None,
3996 ratchet_store: None,
3997 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
3998 management: Default::default(),
3999 probe_port: None,
4000 probe_addrs: vec![],
4001 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
4002 device: None,
4003 hooks: Vec::new(),
4004 discover_interfaces: false,
4005 discovery_required_value: None,
4006 respond_to_probes: false,
4007 prefer_shorter_path: false,
4008 max_paths_per_destination: 1,
4009 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
4010 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
4011 max_path_destinations: usize::MAX,
4012 max_tunnel_destinations_total: usize::MAX,
4013 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
4014 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
4015 announce_table_ttl: Duration::from_secs(
4016 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
4017 ),
4018 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
4019 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
4020 interface_writer_queue_capacity:
4021 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
4022 #[cfg(feature = "iface-backbone")]
4023 backbone_peer_pool: None,
4024 announce_sig_cache_enabled: true,
4025 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
4026 announce_sig_cache_ttl: Duration::from_secs(
4027 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
4028 ),
4029 registry: None,
4030 #[cfg(feature = "hooks")]
4031 provider_bridge: None,
4032 },
4033 Box::new(NoopCallbacks),
4034 )
4035 .unwrap();
4036
4037 let dest = crate::destination::Destination::single_in(
4039 "test",
4040 &["echo"],
4041 rns_core::types::IdentityHash([0x42; 16]),
4042 );
4043 let result = node.send_packet(&dest, b"hello");
4044 assert!(result.is_err(), "single_in has no public_key, should fail");
4045
4046 node.shutdown();
4047 }
4048
4049 #[test]
4050 fn send_packet_single_encrypts() {
4051 let node = RnsNode::start(
4052 NodeConfig {
4053 panic_on_interface_error: false,
4054 transport_enabled: false,
4055 identity: None,
4056 interfaces: vec![],
4057 share_instance: false,
4058 instance_name: "default".into(),
4059 shared_instance_port: 37428,
4060 rpc_port: 0,
4061 cache_dir: None,
4062 ratchet_store: None,
4063 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
4064 management: Default::default(),
4065 probe_port: None,
4066 probe_addrs: vec![],
4067 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
4068 device: None,
4069 hooks: Vec::new(),
4070 discover_interfaces: false,
4071 discovery_required_value: None,
4072 respond_to_probes: false,
4073 prefer_shorter_path: false,
4074 max_paths_per_destination: 1,
4075 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
4076 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
4077 max_path_destinations: usize::MAX,
4078 max_tunnel_destinations_total: usize::MAX,
4079 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
4080 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
4081 announce_table_ttl: Duration::from_secs(
4082 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
4083 ),
4084 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
4085 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
4086 interface_writer_queue_capacity:
4087 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
4088 #[cfg(feature = "iface-backbone")]
4089 backbone_peer_pool: None,
4090 announce_sig_cache_enabled: true,
4091 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
4092 announce_sig_cache_ttl: Duration::from_secs(
4093 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
4094 ),
4095 registry: None,
4096 #[cfg(feature = "hooks")]
4097 provider_bridge: None,
4098 },
4099 Box::new(NoopCallbacks),
4100 )
4101 .unwrap();
4102
4103 let remote_identity = Identity::new(&mut OsRng);
4105 let recalled = crate::destination::AnnouncedIdentity {
4106 dest_hash: rns_core::types::DestHash([0xAA; 16]),
4107 identity_hash: rns_core::types::IdentityHash(*remote_identity.hash()),
4108 public_key: remote_identity.get_public_key().unwrap(),
4109 app_data: None,
4110 hops: 1,
4111 received_at: 0.0,
4112 receiving_interface: rns_core::transport::types::InterfaceId(0),
4113 };
4114 let dest = crate::destination::Destination::single_out("test", &["echo"], &recalled);
4115
4116 let result = node.send_packet(&dest, b"secret message");
4117 assert!(result.is_ok());
4118
4119 let packet_hash = result.unwrap();
4120 assert_ne!(packet_hash.0, [0u8; 32]);
4121
4122 thread::sleep(Duration::from_millis(50));
4123 node.shutdown();
4124 }
4125
4126 #[test]
4127 fn register_destination_with_proof_prove_all() {
4128 let node = RnsNode::start(
4129 NodeConfig {
4130 panic_on_interface_error: false,
4131 transport_enabled: false,
4132 identity: None,
4133 interfaces: vec![],
4134 share_instance: false,
4135 instance_name: "default".into(),
4136 shared_instance_port: 37428,
4137 rpc_port: 0,
4138 cache_dir: None,
4139 ratchet_store: None,
4140 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
4141 management: Default::default(),
4142 probe_port: None,
4143 probe_addrs: vec![],
4144 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
4145 device: None,
4146 hooks: Vec::new(),
4147 discover_interfaces: false,
4148 discovery_required_value: None,
4149 respond_to_probes: false,
4150 prefer_shorter_path: false,
4151 max_paths_per_destination: 1,
4152 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
4153 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
4154 max_path_destinations: usize::MAX,
4155 max_tunnel_destinations_total: usize::MAX,
4156 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
4157 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
4158 announce_table_ttl: Duration::from_secs(
4159 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
4160 ),
4161 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
4162 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
4163 interface_writer_queue_capacity:
4164 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
4165 #[cfg(feature = "iface-backbone")]
4166 backbone_peer_pool: None,
4167 announce_sig_cache_enabled: true,
4168 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
4169 announce_sig_cache_ttl: Duration::from_secs(
4170 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
4171 ),
4172 registry: None,
4173 #[cfg(feature = "hooks")]
4174 provider_bridge: None,
4175 },
4176 Box::new(NoopCallbacks),
4177 )
4178 .unwrap();
4179
4180 let identity = Identity::new(&mut OsRng);
4181 let ih = rns_core::types::IdentityHash(*identity.hash());
4182 let dest = crate::destination::Destination::single_in("echo", &["request"], ih)
4183 .set_proof_strategy(rns_core::types::ProofStrategy::ProveAll);
4184 let prv_key = identity.get_private_key().unwrap();
4185
4186 let result = node.register_destination_with_proof(&dest, Some(prv_key));
4187 assert!(result.is_ok());
4188
4189 thread::sleep(Duration::from_millis(50));
4191
4192 node.shutdown();
4193 }
4194
4195 #[test]
4196 fn register_destination_with_proof_prove_none() {
4197 let node = RnsNode::start(
4198 NodeConfig {
4199 panic_on_interface_error: false,
4200 transport_enabled: false,
4201 identity: None,
4202 interfaces: vec![],
4203 share_instance: false,
4204 instance_name: "default".into(),
4205 shared_instance_port: 37428,
4206 rpc_port: 0,
4207 cache_dir: None,
4208 ratchet_store: None,
4209 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
4210 management: Default::default(),
4211 probe_port: None,
4212 probe_addrs: vec![],
4213 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
4214 device: None,
4215 hooks: Vec::new(),
4216 discover_interfaces: false,
4217 discovery_required_value: None,
4218 respond_to_probes: false,
4219 prefer_shorter_path: false,
4220 max_paths_per_destination: 1,
4221 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
4222 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
4223 max_path_destinations: usize::MAX,
4224 max_tunnel_destinations_total: usize::MAX,
4225 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
4226 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
4227 announce_table_ttl: Duration::from_secs(
4228 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
4229 ),
4230 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
4231 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
4232 interface_writer_queue_capacity:
4233 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
4234 #[cfg(feature = "iface-backbone")]
4235 backbone_peer_pool: None,
4236 announce_sig_cache_enabled: true,
4237 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
4238 announce_sig_cache_ttl: Duration::from_secs(
4239 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
4240 ),
4241 registry: None,
4242 #[cfg(feature = "hooks")]
4243 provider_bridge: None,
4244 },
4245 Box::new(NoopCallbacks),
4246 )
4247 .unwrap();
4248
4249 let dest = crate::destination::Destination::plain("test", &["data"])
4251 .set_proof_strategy(rns_core::types::ProofStrategy::ProveNone);
4252
4253 let result = node.register_destination_with_proof(&dest, None);
4254 assert!(result.is_ok());
4255
4256 thread::sleep(Duration::from_millis(50));
4257 node.shutdown();
4258 }
4259}