1use std::io;
6use std::path::Path;
7use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
8use std::sync::Arc;
9use std::thread::{self, JoinHandle};
10use std::time::Duration;
11
12use rns_core::transport::announce_verify_queue::OverflowPolicy as AnnounceQueueOverflowPolicy;
13use rns_core::transport::types::TransportConfig;
14use rns_crypto::identity::Identity;
15use rns_crypto::{OsRng, Rng};
16
17use crate::config;
18use crate::driver::{Callbacks, Driver};
19use crate::event::{self, Event, EventSender};
20use crate::ifac;
21#[cfg(feature = "iface-auto")]
22use crate::interface::auto::{auto_runtime_handle_from_config, AutoConfig};
23#[cfg(feature = "iface-backbone")]
24use crate::interface::backbone::{
25 client_runtime_handle_from_mode, peer_state_handle_from_mode, runtime_handle_from_mode,
26 BackboneMode,
27};
28#[cfg(feature = "iface-i2p")]
29use crate::interface::i2p::{i2p_runtime_handle_from_config, I2pConfig};
30#[cfg(feature = "iface-local")]
31use crate::interface::local::LocalServerConfig;
32#[cfg(feature = "iface-pipe")]
33use crate::interface::pipe::{pipe_runtime_handle_from_config, PipeConfig};
34#[cfg(feature = "iface-rnode")]
35use crate::interface::rnode::{rnode_runtime_handle_from_config, RNodeConfig};
36#[cfg(feature = "iface-tcp")]
37use crate::interface::tcp::{tcp_client_runtime_handle_from_config, TcpClientConfig};
38#[cfg(feature = "iface-tcp")]
39use crate::interface::tcp_server::{
40 runtime_handle_from_config as tcp_runtime_handle_from_config, TcpServerConfig,
41};
42#[cfg(feature = "iface-udp")]
43use crate::interface::udp::{udp_runtime_handle_from_config, UdpConfig};
44use crate::interface::{InterfaceEntry, InterfaceStats};
45use crate::storage;
46use crate::time;
47
48#[cfg(test)]
49const DEFAULT_KNOWN_DESTINATIONS_TTL: Duration = Duration::from_secs(48 * 60 * 60);
50const DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES: usize = 8192;
51
52fn parse_interface_mode(mode: &str) -> u8 {
55 match mode.to_lowercase().as_str() {
56 "full" => rns_core::constants::MODE_FULL,
57 "access_point" | "accesspoint" | "ap" => rns_core::constants::MODE_ACCESS_POINT,
58 "pointtopoint" | "ptp" => rns_core::constants::MODE_POINT_TO_POINT,
59 "roaming" => rns_core::constants::MODE_ROAMING,
60 "boundary" => rns_core::constants::MODE_BOUNDARY,
61 "gateway" | "gw" => rns_core::constants::MODE_GATEWAY,
62 _ => rns_core::constants::MODE_FULL,
63 }
64}
65
66fn default_ingress_control_for_type(
67 iface_type: &str,
68) -> rns_core::transport::types::IngressControlConfig {
69 match iface_type {
70 "AutoInterface" | "BackboneInterface" | "TCPClientInterface" | "TCPServerInterface"
71 | "UDPInterface" | "I2PInterface" => {
72 rns_core::transport::types::IngressControlConfig::enabled()
73 }
74 _ => rns_core::transport::types::IngressControlConfig::disabled(),
75 }
76}
77
78fn parse_ingress_control_config(
79 iface_type: &str,
80 params: &std::collections::HashMap<String, String>,
81) -> Result<rns_core::transport::types::IngressControlConfig, String> {
82 let mut config = default_ingress_control_for_type(iface_type);
83
84 if let Some(v) = params.get("ingress_control") {
85 config.enabled = config::parse_bool_pub(v)
86 .ok_or_else(|| format!("ingress_control must be a boolean, got '{}'", v))?;
87 }
88 if let Some(v) = params.get("ic_max_held_announces") {
89 config.max_held_announces = v
90 .parse::<usize>()
91 .map_err(|_| format!("ic_max_held_announces must be an integer, got '{}'", v))?;
92 }
93 if let Some(v) = params.get("ic_burst_hold") {
94 config.burst_hold = parse_nonnegative_f64("ic_burst_hold", v)?;
95 }
96 if let Some(v) = params.get("ic_burst_freq_new") {
97 config.burst_freq_new = parse_nonnegative_f64("ic_burst_freq_new", v)?;
98 }
99 if let Some(v) = params.get("ic_burst_freq") {
100 config.burst_freq = parse_nonnegative_f64("ic_burst_freq", v)?;
101 }
102 if let Some(v) = params.get("ic_new_time") {
103 config.new_time = parse_nonnegative_f64("ic_new_time", v)?;
104 }
105 if let Some(v) = params.get("ic_burst_penalty") {
106 config.burst_penalty = parse_nonnegative_f64("ic_burst_penalty", v)?;
107 }
108 if let Some(v) = params.get("ic_held_release_interval") {
109 config.held_release_interval = parse_nonnegative_f64("ic_held_release_interval", v)?;
110 }
111
112 Ok(config)
113}
114
115fn parse_nonnegative_f64(key: &str, value: &str) -> Result<f64, String> {
116 let parsed = value
117 .parse::<f64>()
118 .map_err(|_| format!("{} must be numeric, got '{}'", key, value))?;
119 if parsed < 0.0 {
120 return Err(format!("{} must be >= 0, got '{}'", key, value));
121 }
122 Ok(parsed)
123}
124
125fn extract_ifac_config(
128 params: &std::collections::HashMap<String, String>,
129 default_size: usize,
130) -> Option<IfacConfig> {
131 let netname = params
132 .get("networkname")
133 .or_else(|| params.get("network_name"))
134 .cloned();
135 let netkey = params
136 .get("passphrase")
137 .or_else(|| params.get("pass_phrase"))
138 .cloned();
139
140 if netname.is_none() && netkey.is_none() {
141 return None;
142 }
143
144 let size = params
146 .get("ifac_size")
147 .and_then(|v| v.parse::<usize>().ok())
148 .map(|bits| (bits / 8).max(1))
149 .unwrap_or(default_size);
150
151 Some(IfacConfig {
152 netname,
153 netkey,
154 size,
155 })
156}
157
158fn extract_discovery_config(
160 iface_name: &str,
161 iface_type: &str,
162 params: &std::collections::HashMap<String, String>,
163) -> Option<crate::discovery::DiscoveryConfig> {
164 let discoverable = params
165 .get("discoverable")
166 .and_then(|v| config::parse_bool_pub(v))
167 .unwrap_or(false);
168 if !discoverable {
169 return None;
170 }
171
172 if iface_type == "TCPClientInterface" {
173 log::error!(
174 "Invalid interface discovery configuration for {}, aborting discovery announce",
175 iface_name
176 );
177 return None;
178 }
179
180 let discovery_name = params
181 .get("discovery_name")
182 .cloned()
183 .unwrap_or_else(|| iface_name.to_string());
184
185 let announce_interval = params
187 .get("announce_interval")
188 .and_then(|v| v.parse::<u64>().ok())
189 .map(|secs| secs.max(300))
190 .unwrap_or(21600);
191
192 let stamp_value = params
193 .get("discovery_stamp_value")
194 .and_then(|v| v.parse::<u8>().ok())
195 .unwrap_or(crate::discovery::DEFAULT_STAMP_VALUE);
196
197 let reachable_on = params.get("reachable_on").cloned();
198
199 let listen_port = params
200 .get("listen_port")
201 .or_else(|| params.get("port"))
202 .and_then(|v| v.parse().ok());
203
204 let latitude = params
205 .get("latitude")
206 .or_else(|| params.get("lat"))
207 .and_then(|v| v.parse().ok());
208 let longitude = params
209 .get("longitude")
210 .or_else(|| params.get("lon"))
211 .and_then(|v| v.parse().ok());
212 let height = params.get("height").and_then(|v| v.parse().ok());
213
214 Some(crate::discovery::DiscoveryConfig {
215 discovery_name,
216 announce_interval,
217 stamp_value,
218 reachable_on,
219 interface_type: iface_type.to_string(),
220 listen_port,
221 latitude,
222 longitude,
223 height,
224 })
225}
226
227#[cfg(feature = "iface-backbone")]
228fn backbone_discovery_runtime_from_interface(
229 interface_name: &str,
230 mode: &BackboneMode,
231 discovery: Option<&crate::discovery::DiscoveryConfig>,
232 transport_enabled: bool,
233 ifac: Option<&IfacConfig>,
234) -> Option<crate::driver::BackboneDiscoveryRuntimeHandle> {
235 let config = match mode {
236 BackboneMode::Server(config) => config,
237 BackboneMode::Client(_) => return None,
238 };
239
240 let startup_config = discovery
241 .cloned()
242 .unwrap_or(crate::discovery::DiscoveryConfig {
243 discovery_name: interface_name.to_string(),
244 announce_interval: 21600,
245 stamp_value: crate::discovery::DEFAULT_STAMP_VALUE,
246 reachable_on: None,
247 interface_type: "BackboneInterface".to_string(),
248 listen_port: Some(config.listen_port),
249 latitude: None,
250 longitude: None,
251 height: None,
252 });
253 let startup = crate::driver::BackboneDiscoveryRuntime {
254 discoverable: discovery.is_some(),
255 config: startup_config,
256 transport_enabled,
257 ifac_netname: ifac.and_then(|cfg| cfg.netname.clone()),
258 ifac_netkey: ifac.and_then(|cfg| cfg.netkey.clone()),
259 };
260
261 Some(crate::driver::BackboneDiscoveryRuntimeHandle {
262 interface_name: config.name.clone(),
263 current: startup.clone(),
264 startup,
265 })
266}
267
268#[cfg(feature = "iface-tcp")]
269fn tcp_server_discovery_runtime_from_interface(
270 interface_name: &str,
271 config: &crate::interface::tcp_server::TcpServerConfig,
272 discovery: Option<&crate::discovery::DiscoveryConfig>,
273 transport_enabled: bool,
274 ifac: Option<&IfacConfig>,
275) -> crate::driver::TcpServerDiscoveryRuntimeHandle {
276 let startup_config = discovery
277 .cloned()
278 .unwrap_or(crate::discovery::DiscoveryConfig {
279 discovery_name: interface_name.to_string(),
280 announce_interval: 21600,
281 stamp_value: crate::discovery::DEFAULT_STAMP_VALUE,
282 reachable_on: None,
283 interface_type: "TCPServerInterface".to_string(),
284 listen_port: Some(config.listen_port),
285 latitude: None,
286 longitude: None,
287 height: None,
288 });
289 let startup = crate::driver::TcpServerDiscoveryRuntime {
290 discoverable: discovery.is_some(),
291 config: startup_config,
292 transport_enabled,
293 ifac_netname: ifac.and_then(|cfg| cfg.netname.clone()),
294 ifac_netkey: ifac.and_then(|cfg| cfg.netkey.clone()),
295 };
296
297 crate::driver::TcpServerDiscoveryRuntimeHandle {
298 interface_name: config.name.clone(),
299 current: startup.clone(),
300 startup,
301 }
302}
303
304pub struct NodeConfig {
306 pub transport_enabled: bool,
307 pub identity: Option<Identity>,
308 pub interfaces: Vec<InterfaceConfig>,
310 pub share_instance: bool,
312 pub instance_name: String,
314 pub shared_instance_port: u16,
316 pub rpc_port: u16,
318 pub cache_dir: Option<std::path::PathBuf>,
320 pub management: crate::management::ManagementConfig,
322 pub probe_port: Option<u16>,
324 pub probe_addrs: Vec<std::net::SocketAddr>,
326 pub probe_protocol: rns_core::holepunch::ProbeProtocol,
328 pub device: Option<String>,
330 pub hooks: Vec<config::ParsedHook>,
332 pub discover_interfaces: bool,
334 pub discovery_required_value: Option<u8>,
336 pub respond_to_probes: bool,
338 pub prefer_shorter_path: bool,
342 pub max_paths_per_destination: usize,
345 pub packet_hashlist_max_entries: usize,
347 pub max_discovery_pr_tags: usize,
349 pub max_path_destinations: usize,
351 pub max_tunnel_destinations_total: usize,
353 pub known_destinations_ttl: Duration,
355 pub known_destinations_max_entries: usize,
357 pub announce_table_ttl: Duration,
359 pub announce_table_max_bytes: usize,
361 pub driver_event_queue_capacity: usize,
363 pub interface_writer_queue_capacity: usize,
365 pub announce_sig_cache_enabled: bool,
367 pub announce_sig_cache_max_entries: usize,
369 pub announce_sig_cache_ttl: Duration,
371 pub registry: Option<crate::interface::registry::InterfaceRegistry>,
373 pub panic_on_interface_error: bool,
376 #[cfg(feature = "rns-hooks")]
378 pub provider_bridge: Option<crate::provider_bridge::ProviderBridgeConfig>,
379}
380
381impl Default for NodeConfig {
382 fn default() -> Self {
383 Self {
384 transport_enabled: false,
385 identity: None,
386 interfaces: Vec::new(),
387 share_instance: false,
388 instance_name: "default".into(),
389 shared_instance_port: 37428,
390 rpc_port: 0,
391 cache_dir: None,
392 management: Default::default(),
393 probe_port: None,
394 probe_addrs: vec![],
395 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
396 device: None,
397 hooks: Vec::new(),
398 discover_interfaces: false,
399 discovery_required_value: None,
400 respond_to_probes: false,
401 prefer_shorter_path: false,
402 max_paths_per_destination: 1,
403 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
404 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
405 max_path_destinations: rns_core::transport::types::DEFAULT_MAX_PATH_DESTINATIONS,
406 max_tunnel_destinations_total: usize::MAX,
407 known_destinations_ttl: Duration::from_secs(48 * 60 * 60),
408 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
409 announce_table_ttl: Duration::from_secs(rns_core::constants::ANNOUNCE_TABLE_TTL as u64),
410 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
411 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
412 interface_writer_queue_capacity: crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
413 announce_sig_cache_enabled: true,
414 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
415 announce_sig_cache_ttl: Duration::from_secs(
416 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
417 ),
418 registry: None,
419 panic_on_interface_error: false,
420 #[cfg(feature = "rns-hooks")]
421 provider_bridge: None,
422 }
423 }
424}
425
426pub struct IfacConfig {
428 pub netname: Option<String>,
429 pub netkey: Option<String>,
430 pub size: usize,
431}
432
433pub struct InterfaceConfig {
435 pub name: String,
436 pub type_name: String,
437 pub config_data: Box<dyn crate::interface::InterfaceConfigData>,
438 pub mode: u8,
439 pub ingress_control: rns_core::transport::types::IngressControlConfig,
440 pub ifac: Option<IfacConfig>,
441 pub discovery: Option<crate::discovery::DiscoveryConfig>,
442}
443
444use crate::event::{QueryRequest, QueryResponse};
445
446#[derive(Debug)]
448pub struct SendError;
449
450impl std::fmt::Display for SendError {
451 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
452 write!(f, "driver shut down")
453 }
454}
455
456impl std::error::Error for SendError {}
457
458pub struct RnsNode {
460 tx: EventSender,
461 driver_handle: Option<JoinHandle<()>>,
462 verify_handle: Option<JoinHandle<()>>,
463 verify_shutdown: Arc<AtomicBool>,
464 rpc_server: Option<crate::rpc::RpcServer>,
465 tick_interval_ms: Arc<AtomicU64>,
466 #[allow(dead_code)]
467 probe_server: Option<crate::holepunch::probe::ProbeServerHandle>,
468}
469
470impl RnsNode {
471 pub fn from_config(
474 config_path: Option<&Path>,
475 callbacks: Box<dyn Callbacks>,
476 ) -> io::Result<Self> {
477 let config_dir = storage::resolve_config_dir(config_path);
478 let paths = storage::ensure_storage_dirs(&config_dir)?;
479
480 let config_file = config_dir.join("config");
482 let rns_config = if config_file.exists() {
483 config::parse_file(&config_file)
484 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, format!("{}", e)))?
485 } else {
486 config::parse("")
488 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, format!("{}", e)))?
489 };
490
491 let identity = if let Some(ref id_path_str) = rns_config.reticulum.network_identity {
493 let id_path = std::path::PathBuf::from(id_path_str);
494 if id_path.exists() {
495 storage::load_identity(&id_path)?
496 } else {
497 let id = Identity::new(&mut OsRng);
498 storage::save_identity(&id, &id_path)?;
499 id
500 }
501 } else {
502 storage::load_or_create_identity(&paths.identities)?
503 };
504
505 let registry = crate::interface::registry::InterfaceRegistry::with_builtins();
507 let mut interface_configs = Vec::new();
508 let mut next_id_val = 1u64;
509
510 for iface in &rns_config.interfaces {
511 if !iface.enabled {
512 continue;
513 }
514
515 let iface_id = rns_core::transport::types::InterfaceId(next_id_val);
516 next_id_val += 1;
517
518 let factory = match registry.get(&iface.interface_type) {
519 Some(f) => f,
520 None => {
521 log::warn!(
522 "Unsupported interface type '{}' for '{}'",
523 iface.interface_type,
524 iface.name,
525 );
526 continue;
527 }
528 };
529
530 let mut iface_mode = parse_interface_mode(&iface.mode);
531
532 let has_discovery = match iface.interface_type.as_str() {
534 "AutoInterface" => true,
535 "RNodeInterface" => iface
536 .params
537 .get("discoverable")
538 .and_then(|v| config::parse_bool_pub(v))
539 .unwrap_or(false),
540 _ => false,
541 };
542 if has_discovery
543 && iface_mode != rns_core::constants::MODE_ACCESS_POINT
544 && iface_mode != rns_core::constants::MODE_GATEWAY
545 {
546 let new_mode = if iface.interface_type == "RNodeInterface" {
547 rns_core::constants::MODE_ACCESS_POINT
548 } else {
549 rns_core::constants::MODE_GATEWAY
550 };
551 log::info!(
552 "Interface '{}' has discovery enabled, auto-configuring mode to {}",
553 iface.name,
554 if new_mode == rns_core::constants::MODE_ACCESS_POINT {
555 "ACCESS_POINT"
556 } else {
557 "GATEWAY"
558 }
559 );
560 iface_mode = new_mode;
561 }
562
563 let default_ifac_size = factory.default_ifac_size();
564 let ifac_config = extract_ifac_config(&iface.params, default_ifac_size);
565 let discovery_config =
566 extract_discovery_config(&iface.name, &iface.interface_type, &iface.params);
567 let ingress_control =
568 match parse_ingress_control_config(&iface.interface_type, &iface.params) {
569 Ok(config) => config,
570 Err(e) => {
571 log::warn!(
572 "Failed to parse ingress control config for '{}': {}",
573 iface.name,
574 e
575 );
576 continue;
577 }
578 };
579
580 let mut params = iface.params.clone();
582 if !params.contains_key("storage_dir") {
583 params.insert(
584 "storage_dir".to_string(),
585 paths.storage.to_string_lossy().to_string(),
586 );
587 }
588 if let Some(ref device) = rns_config.reticulum.device {
590 if !params.contains_key("device") {
591 params.insert("device".to_string(), device.clone());
592 }
593 }
594
595 let config_data = match factory.parse_config(&iface.name, iface_id, ¶ms) {
596 Ok(data) => data,
597 Err(e) => {
598 log::warn!("Failed to parse config for '{}': {}", iface.name, e);
599 continue;
600 }
601 };
602
603 interface_configs.push(InterfaceConfig {
604 name: iface.name.clone(),
605 type_name: iface.interface_type.clone(),
606 config_data,
607 mode: iface_mode,
608 ingress_control,
609 ifac: ifac_config,
610 discovery: discovery_config,
611 });
612 }
613
614 let mut mgmt_allowed = Vec::new();
616 for hex_hash in &rns_config.reticulum.remote_management_allowed {
617 if hex_hash.len() == 32 {
618 if let Ok(bytes) = (0..hex_hash.len())
619 .step_by(2)
620 .map(|i| u8::from_str_radix(&hex_hash[i..i + 2], 16))
621 .collect::<Result<Vec<u8>, _>>()
622 {
623 if bytes.len() == 16 {
624 let mut h = [0u8; 16];
625 h.copy_from_slice(&bytes);
626 mgmt_allowed.push(h);
627 }
628 } else {
629 log::warn!("Invalid hex in remote_management_allowed: {}", hex_hash);
630 }
631 } else {
632 log::warn!(
633 "Invalid entry in remote_management_allowed (expected 32 hex chars, got {}): {}",
634 hex_hash.len(), hex_hash,
635 );
636 }
637 }
638
639 let probe_addrs: Vec<std::net::SocketAddr> = rns_config
641 .reticulum
642 .probe_addr
643 .as_ref()
644 .map(|s| {
645 s.split(',')
646 .filter_map(|entry| {
647 let trimmed = entry.trim();
648 if trimmed.is_empty() {
649 return None;
650 }
651 trimmed
652 .parse::<std::net::SocketAddr>()
653 .map_err(|e| {
654 log::warn!("Invalid probe_addr entry '{}': {}", trimmed, e);
655 e
656 })
657 .ok()
658 })
659 .collect()
660 })
661 .unwrap_or_default();
662
663 let probe_protocol = match rns_config
665 .reticulum
666 .probe_protocol
667 .as_deref()
668 .map(|s| s.to_lowercase())
669 {
670 Some(ref s) if s == "stun" => rns_core::holepunch::ProbeProtocol::Stun,
671 _ => rns_core::holepunch::ProbeProtocol::Rnsp,
672 };
673
674 let node_config = NodeConfig {
675 transport_enabled: rns_config.reticulum.enable_transport,
676 identity: Some(identity),
677 share_instance: rns_config.reticulum.share_instance,
678 instance_name: rns_config.reticulum.instance_name.clone(),
679 shared_instance_port: rns_config.reticulum.shared_instance_port,
680 rpc_port: rns_config.reticulum.instance_control_port,
681 cache_dir: Some(paths.cache),
682 management: crate::management::ManagementConfig {
683 enable_remote_management: rns_config.reticulum.enable_remote_management,
684 remote_management_allowed: mgmt_allowed,
685 publish_blackhole: rns_config.reticulum.publish_blackhole,
686 },
687 probe_port: rns_config.reticulum.probe_port,
688 probe_addrs,
689 probe_protocol,
690 device: rns_config.reticulum.device.clone(),
691 hooks: rns_config.hooks.clone(),
692 discover_interfaces: rns_config.reticulum.discover_interfaces,
693 discovery_required_value: rns_config.reticulum.required_discovery_value,
694 respond_to_probes: rns_config.reticulum.respond_to_probes,
695 prefer_shorter_path: rns_config.reticulum.prefer_shorter_path,
696 max_paths_per_destination: rns_config.reticulum.max_paths_per_destination,
697 packet_hashlist_max_entries: rns_config.reticulum.packet_hashlist_max_entries,
698 max_discovery_pr_tags: rns_config.reticulum.max_discovery_pr_tags,
699 max_path_destinations: rns_config.reticulum.max_path_destinations,
700 max_tunnel_destinations_total: rns_config.reticulum.max_tunnel_destinations_total,
701 known_destinations_ttl: Duration::from_secs(
702 rns_config.reticulum.known_destinations_ttl,
703 ),
704 known_destinations_max_entries: rns_config.reticulum.known_destinations_max_entries,
705 announce_table_ttl: Duration::from_secs(rns_config.reticulum.announce_table_ttl),
706 announce_table_max_bytes: rns_config.reticulum.announce_table_max_bytes,
707 driver_event_queue_capacity: rns_config.reticulum.driver_event_queue_capacity,
708 interface_writer_queue_capacity: rns_config.reticulum.interface_writer_queue_capacity,
709 announce_sig_cache_enabled: rns_config.reticulum.announce_sig_cache_enabled,
710 announce_sig_cache_max_entries: rns_config.reticulum.announce_sig_cache_max_entries,
711 announce_sig_cache_ttl: Duration::from_secs(
712 rns_config.reticulum.announce_sig_cache_ttl,
713 ),
714 interfaces: interface_configs,
715 registry: None,
716 panic_on_interface_error: rns_config.reticulum.panic_on_interface_error,
717 #[cfg(feature = "rns-hooks")]
718 provider_bridge: if rns_config.reticulum.provider_bridge {
719 Some(crate::provider_bridge::ProviderBridgeConfig {
720 enabled: true,
721 socket_path: rns_config
722 .reticulum
723 .provider_socket_path
724 .as_ref()
725 .map(std::path::PathBuf::from)
726 .unwrap_or_else(|| config_dir.join("provider.sock")),
727 queue_max_events: rns_config.reticulum.provider_queue_max_events,
728 queue_max_bytes: rns_config.reticulum.provider_queue_max_bytes,
729 overflow_policy: match rns_config.reticulum.provider_overflow_policy.as_str() {
730 "drop_oldest" => crate::provider_bridge::OverflowPolicy::DropOldest,
731 _ => crate::provider_bridge::OverflowPolicy::DropNewest,
732 },
733 node_instance: rns_config.reticulum.instance_name.clone(),
734 })
735 } else {
736 None
737 },
738 };
739
740 Self::start_with_announce_queue_max_entries(
741 node_config,
742 callbacks,
743 rns_config.reticulum.announce_queue_max_entries,
744 rns_config.reticulum.announce_queue_max_interfaces,
745 rns_config.reticulum.announce_queue_max_bytes,
746 rns_config.reticulum.announce_queue_ttl as f64,
747 match rns_config.reticulum.announce_queue_overflow_policy.as_str() {
748 "drop_newest" => AnnounceQueueOverflowPolicy::DropNewest,
749 "drop_oldest" => AnnounceQueueOverflowPolicy::DropOldest,
750 _ => AnnounceQueueOverflowPolicy::DropWorst,
751 },
752 )
753 }
754
755 pub fn start(config: NodeConfig, callbacks: Box<dyn Callbacks>) -> io::Result<Self> {
757 Self::start_with_announce_queue_max_entries(
758 config,
759 callbacks,
760 256,
761 1024,
762 256 * 1024,
763 30.0,
764 AnnounceQueueOverflowPolicy::DropWorst,
765 )
766 }
767
768 fn start_with_announce_queue_max_entries(
769 config: NodeConfig,
770 callbacks: Box<dyn Callbacks>,
771 announce_queue_max_entries: usize,
772 announce_queue_max_interfaces: usize,
773 announce_queue_max_bytes: usize,
774 announce_queue_ttl_secs: f64,
775 announce_queue_overflow_policy: AnnounceQueueOverflowPolicy,
776 ) -> io::Result<Self> {
777 let identity = config.identity.unwrap_or_else(|| Identity::new(&mut OsRng));
778
779 let transport_config = TransportConfig {
780 transport_enabled: config.transport_enabled,
781 identity_hash: Some(*identity.hash()),
782 prefer_shorter_path: config.prefer_shorter_path,
783 max_paths_per_destination: config.max_paths_per_destination,
784 packet_hashlist_max_entries: config.packet_hashlist_max_entries,
785 max_discovery_pr_tags: config.max_discovery_pr_tags,
786 max_path_destinations: config.max_path_destinations,
787 max_tunnel_destinations_total: config.max_tunnel_destinations_total,
788 destination_timeout_secs: config.known_destinations_ttl.as_secs_f64(),
789 announce_table_ttl_secs: config.announce_table_ttl.as_secs_f64(),
790 announce_table_max_bytes: config.announce_table_max_bytes,
791 announce_sig_cache_enabled: config.announce_sig_cache_enabled,
792 announce_sig_cache_max_entries: config.announce_sig_cache_max_entries,
793 announce_sig_cache_ttl_secs: config.announce_sig_cache_ttl.as_secs_f64(),
794 announce_queue_max_entries,
795 announce_queue_max_interfaces,
796 };
797
798 let (tx, rx) = event::channel_with_capacity(config.driver_event_queue_capacity);
799 let tick_interval_ms = Arc::new(AtomicU64::new(1000));
800 let mut driver = Driver::new(transport_config, rx, tx.clone(), callbacks);
801 driver.set_announce_verify_queue_config(
802 announce_queue_max_entries,
803 announce_queue_max_bytes,
804 announce_queue_ttl_secs,
805 announce_queue_overflow_policy,
806 );
807 driver.async_announce_verification = true;
808 driver.set_tick_interval_handle(Arc::clone(&tick_interval_ms));
809 driver.set_packet_hashlist_max_entries(config.packet_hashlist_max_entries);
810 driver.known_destinations_ttl = config.known_destinations_ttl.as_secs_f64();
811 driver.known_destinations_max_entries = config.known_destinations_max_entries;
812 driver.interface_writer_queue_capacity = config.interface_writer_queue_capacity;
813 driver.runtime_config_defaults.known_destinations_ttl =
814 config.known_destinations_ttl.as_secs_f64();
815
816 #[cfg(feature = "rns-hooks")]
817 if let Some(provider_config) = config.provider_bridge.clone() {
818 driver.runtime_config_defaults.provider_queue_max_events =
819 provider_config.queue_max_events;
820 driver.runtime_config_defaults.provider_queue_max_bytes =
821 provider_config.queue_max_bytes;
822 if provider_config.enabled {
823 match crate::provider_bridge::ProviderBridge::start(provider_config) {
824 Ok(bridge) => driver.provider_bridge = Some(bridge),
825 Err(err) => log::warn!("failed to start provider bridge: {}", err),
826 }
827 }
828 }
829
830 if let Some(ref cache_dir) = config.cache_dir {
832 let announces_dir = cache_dir.join("announces");
833 let _ = std::fs::create_dir_all(&announces_dir);
834 driver.announce_cache = Some(crate::announce_cache::AnnounceCache::new(announces_dir));
835 }
836
837 if !config.probe_addrs.is_empty() || config.device.is_some() {
839 driver.set_probe_config(
840 config.probe_addrs.clone(),
841 config.probe_protocol,
842 config.device.clone(),
843 );
844 }
845
846 let probe_server = if let Some(port) = config.probe_port {
848 let listen_addr: std::net::SocketAddr = ([0, 0, 0, 0], port).into();
849 match crate::holepunch::probe::start_probe_server(listen_addr) {
850 Ok(handle) => {
851 log::info!("Probe server started on 0.0.0.0:{}", port);
852 Some(handle)
853 }
854 Err(e) => {
855 log::error!("Failed to start probe server on port {}: {}", port, e);
856 None
857 }
858 }
859 } else {
860 None
861 };
862
863 driver.management_config = config.management.clone();
865
866 if let Some(prv_key) = identity.get_private_key() {
868 driver.transport_identity = Some(Identity::from_private_key(&prv_key));
869 }
870
871 #[cfg(feature = "rns-hooks")]
873 {
874 for hook_cfg in &config.hooks {
875 if !hook_cfg.enabled {
876 continue;
877 }
878 let point_idx = match config::parse_hook_point(&hook_cfg.attach_point) {
879 Some(idx) => idx,
880 None => {
881 log::warn!(
882 "Unknown hook point '{}' for hook '{}'",
883 hook_cfg.attach_point,
884 hook_cfg.name,
885 );
886 continue;
887 }
888 };
889 let mgr = match driver.hook_manager.as_ref() {
890 Some(m) => m,
891 None => {
892 log::warn!(
893 "Hook manager not available, skipping hook '{}'",
894 hook_cfg.name
895 );
896 continue;
897 }
898 };
899 match mgr.load_file(
900 hook_cfg.name.clone(),
901 std::path::Path::new(&hook_cfg.path),
902 hook_cfg.priority,
903 ) {
904 Ok(program) => {
905 driver.hook_slots[point_idx].attach(program);
906 log::info!(
907 "Loaded hook '{}' at point {} (priority {})",
908 hook_cfg.name,
909 hook_cfg.attach_point,
910 hook_cfg.priority,
911 );
912 }
913 Err(e) => {
914 log::error!(
915 "Failed to load hook '{}' from '{}': {}",
916 hook_cfg.name,
917 hook_cfg.path,
918 e,
919 );
920 }
921 }
922 }
923 }
924
925 driver.discover_interfaces = config.discover_interfaces;
927 if let Some(val) = config.discovery_required_value {
928 driver.discovery_required_value = val;
929 }
930
931 let next_dynamic_id = Arc::new(AtomicU64::new(10000));
933
934 let mut discoverable_interfaces = Vec::new();
936
937 let registry = config
939 .registry
940 .unwrap_or_else(crate::interface::registry::InterfaceRegistry::with_builtins);
941 for iface_config in config.interfaces {
942 #[cfg(feature = "iface-backbone")]
943 if iface_config.type_name == "BackboneInterface" {
944 if let Some(mode) = iface_config
945 .config_data
946 .as_any()
947 .downcast_ref::<BackboneMode>()
948 {
949 if let Some(handle) = runtime_handle_from_mode(mode) {
950 driver.register_backbone_runtime(handle);
951 }
952 if let Some(handle) = peer_state_handle_from_mode(mode) {
953 driver.register_backbone_peer_state(handle);
954 }
955 if let Some(handle) = client_runtime_handle_from_mode(mode) {
956 driver.register_backbone_client_runtime(handle);
957 }
958 if let Some(handle) = backbone_discovery_runtime_from_interface(
959 &iface_config.name,
960 mode,
961 iface_config.discovery.as_ref(),
962 config.transport_enabled,
963 iface_config.ifac.as_ref(),
964 ) {
965 driver.register_backbone_discovery_runtime(handle);
966 }
967 }
968 }
969 #[cfg(feature = "iface-tcp")]
970 if iface_config.type_name == "TCPClientInterface" {
971 if let Some(tcp_config) = iface_config
972 .config_data
973 .as_any()
974 .downcast_ref::<TcpClientConfig>()
975 {
976 driver.register_tcp_client_runtime(tcp_client_runtime_handle_from_config(
977 tcp_config,
978 ));
979 }
980 }
981 #[cfg(feature = "iface-tcp")]
982 if iface_config.type_name == "TCPServerInterface" {
983 if let Some(tcp_config) = iface_config
984 .config_data
985 .as_any()
986 .downcast_ref::<TcpServerConfig>()
987 {
988 driver.register_tcp_server_runtime(tcp_runtime_handle_from_config(tcp_config));
989 driver.register_tcp_server_discovery_runtime(
990 tcp_server_discovery_runtime_from_interface(
991 &iface_config.name,
992 tcp_config,
993 iface_config.discovery.as_ref(),
994 config.transport_enabled,
995 iface_config.ifac.as_ref(),
996 ),
997 );
998 }
999 }
1000 #[cfg(feature = "iface-udp")]
1001 if iface_config.type_name == "UDPInterface" {
1002 if let Some(udp_config) = iface_config
1003 .config_data
1004 .as_any()
1005 .downcast_ref::<UdpConfig>()
1006 {
1007 driver.register_udp_runtime(udp_runtime_handle_from_config(udp_config));
1008 }
1009 }
1010 #[cfg(feature = "iface-auto")]
1011 if iface_config.type_name == "AutoInterface" {
1012 if let Some(auto_config) = iface_config
1013 .config_data
1014 .as_any()
1015 .downcast_ref::<AutoConfig>()
1016 {
1017 driver.register_auto_runtime(auto_runtime_handle_from_config(auto_config));
1018 }
1019 }
1020 #[cfg(feature = "iface-i2p")]
1021 if iface_config.type_name == "I2PInterface" {
1022 if let Some(i2p_config) = iface_config
1023 .config_data
1024 .as_any()
1025 .downcast_ref::<I2pConfig>()
1026 {
1027 driver.register_i2p_runtime(i2p_runtime_handle_from_config(i2p_config));
1028 }
1029 }
1030 #[cfg(feature = "iface-pipe")]
1031 if iface_config.type_name == "PipeInterface" {
1032 if let Some(pipe_config) = iface_config
1033 .config_data
1034 .as_any()
1035 .downcast_ref::<PipeConfig>()
1036 {
1037 driver.register_pipe_runtime(pipe_runtime_handle_from_config(pipe_config));
1038 }
1039 }
1040 #[cfg(feature = "iface-rnode")]
1041 if iface_config.type_name == "RNodeInterface" {
1042 if let Some(rnode_config) = iface_config
1043 .config_data
1044 .as_any()
1045 .downcast_ref::<RNodeConfig>()
1046 {
1047 driver.register_rnode_runtime(rnode_runtime_handle_from_config(rnode_config));
1048 }
1049 }
1050
1051 let factory = match registry.get(&iface_config.type_name) {
1052 Some(f) => f,
1053 None => {
1054 log::warn!(
1055 "No factory registered for interface type '{}'",
1056 iface_config.type_name
1057 );
1058 continue;
1059 }
1060 };
1061
1062 let mut ifac_state = iface_config.ifac.as_ref().and_then(|ic| {
1063 if ic.netname.is_some() || ic.netkey.is_some() {
1064 Some(ifac::derive_ifac(
1065 ic.netname.as_deref(),
1066 ic.netkey.as_deref(),
1067 ic.size,
1068 ))
1069 } else {
1070 None
1071 }
1072 });
1073 let ifac_runtime = crate::driver::IfacRuntimeConfig {
1074 netname: iface_config.ifac.as_ref().and_then(|ic| ic.netname.clone()),
1075 netkey: iface_config.ifac.as_ref().and_then(|ic| ic.netkey.clone()),
1076 size: iface_config
1077 .ifac
1078 .as_ref()
1079 .map(|ic| ic.size)
1080 .unwrap_or(factory.default_ifac_size()),
1081 };
1082
1083 let ctx = crate::interface::StartContext {
1084 tx: tx.clone(),
1085 next_dynamic_id: next_dynamic_id.clone(),
1086 mode: iface_config.mode,
1087 ingress_control: iface_config.ingress_control,
1088 };
1089
1090 let result = match factory.start(iface_config.config_data, ctx) {
1091 Ok(r) => r,
1092 Err(e) => {
1093 if config.panic_on_interface_error {
1094 return Err(e);
1095 }
1096 log::error!(
1097 "Interface '{}' ({}) failed to start: {}",
1098 iface_config.name,
1099 iface_config.type_name,
1100 e
1101 );
1102 continue;
1103 }
1104 };
1105
1106 if let Some(ref disc) = iface_config.discovery {
1107 discoverable_interfaces.push(crate::discovery::DiscoverableInterface {
1108 interface_name: iface_config.name.clone(),
1109 config: disc.clone(),
1110 transport_enabled: config.transport_enabled,
1111 ifac_netname: iface_config.ifac.as_ref().and_then(|ic| ic.netname.clone()),
1112 ifac_netkey: iface_config.ifac.as_ref().and_then(|ic| ic.netkey.clone()),
1113 });
1114 }
1115
1116 match result {
1117 crate::interface::StartResult::Simple {
1118 id,
1119 info,
1120 writer,
1121 interface_type_name,
1122 } => {
1123 let (writer, async_writer_metrics) = crate::interface::wrap_async_writer(
1124 writer,
1125 id,
1126 &info.name,
1127 tx.clone(),
1128 config.interface_writer_queue_capacity,
1129 );
1130 driver.register_interface_runtime_defaults(&info);
1131 driver.register_interface_ifac_runtime(&info.name, ifac_runtime.clone());
1132 driver.engine.register_interface(info.clone());
1133 driver.interfaces.insert(
1134 id,
1135 InterfaceEntry {
1136 id,
1137 info,
1138 writer,
1139 async_writer_metrics: Some(async_writer_metrics),
1140 enabled: true,
1141 online: false,
1142 dynamic: false,
1143 ifac: ifac_state,
1144 stats: InterfaceStats {
1145 started: time::now(),
1146 ..Default::default()
1147 },
1148 interface_type: interface_type_name,
1149 send_retry_at: None,
1150 send_retry_backoff: Duration::ZERO,
1151 },
1152 );
1153 }
1154 crate::interface::StartResult::Listener { control } => {
1155 if let Some(control) = control {
1158 driver.register_listener_control(control);
1159 }
1160 }
1161 crate::interface::StartResult::Multi(subs) => {
1162 let ifac_cfg = &iface_config.ifac;
1163 let mut first = true;
1164 for sub in subs {
1165 let (writer, async_writer_metrics) = crate::interface::wrap_async_writer(
1166 sub.writer,
1167 sub.id,
1168 &sub.info.name,
1169 tx.clone(),
1170 config.interface_writer_queue_capacity,
1171 );
1172 let sub_ifac = if first {
1173 first = false;
1174 ifac_state.take()
1175 } else if let Some(ref ic) = ifac_cfg {
1176 Some(ifac::derive_ifac(
1177 ic.netname.as_deref(),
1178 ic.netkey.as_deref(),
1179 ic.size,
1180 ))
1181 } else {
1182 None
1183 };
1184
1185 driver.register_interface_runtime_defaults(&sub.info);
1186 driver
1187 .register_interface_ifac_runtime(&sub.info.name, ifac_runtime.clone());
1188 driver.engine.register_interface(sub.info.clone());
1189 driver.interfaces.insert(
1190 sub.id,
1191 InterfaceEntry {
1192 id: sub.id,
1193 info: sub.info,
1194 writer,
1195 async_writer_metrics: Some(async_writer_metrics),
1196 enabled: true,
1197 online: false,
1198 dynamic: false,
1199 ifac: sub_ifac,
1200 stats: InterfaceStats {
1201 started: time::now(),
1202 ..Default::default()
1203 },
1204 interface_type: sub.interface_type_name,
1205 send_retry_at: None,
1206 send_retry_backoff: Duration::ZERO,
1207 },
1208 );
1209 }
1210 }
1211 }
1212 }
1213
1214 if !discoverable_interfaces.is_empty() {
1216 let transport_id = *identity.hash();
1217 let announcer =
1218 crate::discovery::InterfaceAnnouncer::new(transport_id, discoverable_interfaces);
1219 log::info!("Interface discovery announcer initialized");
1220 driver.interface_announcer = Some(announcer);
1221 }
1222
1223 if let Some(ref cache_dir) = config.cache_dir {
1225 let disc_path = std::path::PathBuf::from(cache_dir)
1226 .parent()
1227 .unwrap_or(std::path::Path::new("."))
1228 .join("storage")
1229 .join("discovery")
1230 .join("interfaces");
1231 let _ = std::fs::create_dir_all(&disc_path);
1232 driver.discovered_interfaces =
1233 crate::discovery::DiscoveredInterfaceStorage::new(disc_path);
1234 }
1235
1236 if config.management.enable_remote_management {
1238 if let Some(prv_key) = identity.get_private_key() {
1239 let identity_hash = *identity.hash();
1240 let mgmt_dest = crate::management::management_dest_hash(&identity_hash);
1241
1242 let sig_prv = rns_crypto::ed25519::Ed25519PrivateKey::from_bytes(
1244 &prv_key[32..64].try_into().unwrap(),
1245 );
1246 let sig_pub_bytes: [u8; 32] = identity.get_public_key().unwrap()[32..64]
1247 .try_into()
1248 .unwrap();
1249
1250 driver
1252 .engine
1253 .register_destination(mgmt_dest, rns_core::constants::DESTINATION_SINGLE);
1254 driver
1255 .local_destinations
1256 .insert(mgmt_dest, rns_core::constants::DESTINATION_SINGLE);
1257
1258 driver.link_manager.register_link_destination(
1260 mgmt_dest,
1261 sig_prv,
1262 sig_pub_bytes,
1263 crate::link_manager::ResourceStrategy::AcceptNone,
1264 );
1265
1266 driver
1268 .link_manager
1269 .register_management_path(crate::management::status_path_hash());
1270 driver
1271 .link_manager
1272 .register_management_path(crate::management::path_path_hash());
1273
1274 log::info!("Remote management enabled on {:02x?}", &mgmt_dest[..4],);
1275
1276 if !config.management.remote_management_allowed.is_empty() {
1278 log::info!(
1279 "Remote management allowed for {} identities",
1280 config.management.remote_management_allowed.len(),
1281 );
1282 }
1283 }
1284 }
1285
1286 if config.management.publish_blackhole {
1287 if let Some(prv_key) = identity.get_private_key() {
1288 let identity_hash = *identity.hash();
1289 let bh_dest = crate::management::blackhole_dest_hash(&identity_hash);
1290
1291 let sig_prv = rns_crypto::ed25519::Ed25519PrivateKey::from_bytes(
1292 &prv_key[32..64].try_into().unwrap(),
1293 );
1294 let sig_pub_bytes: [u8; 32] = identity.get_public_key().unwrap()[32..64]
1295 .try_into()
1296 .unwrap();
1297
1298 driver
1299 .engine
1300 .register_destination(bh_dest, rns_core::constants::DESTINATION_SINGLE);
1301 driver.link_manager.register_link_destination(
1302 bh_dest,
1303 sig_prv,
1304 sig_pub_bytes,
1305 crate::link_manager::ResourceStrategy::AcceptNone,
1306 );
1307 driver
1308 .link_manager
1309 .register_management_path(crate::management::list_path_hash());
1310
1311 log::info!(
1312 "Blackhole list publishing enabled on {:02x?}",
1313 &bh_dest[..4],
1314 );
1315 }
1316 }
1317
1318 if config.respond_to_probes && config.transport_enabled {
1320 let identity_hash = *identity.hash();
1321 let probe_dest = crate::management::probe_dest_hash(&identity_hash);
1322
1323 driver
1325 .engine
1326 .register_destination(probe_dest, rns_core::constants::DESTINATION_SINGLE);
1327 driver
1328 .local_destinations
1329 .insert(probe_dest, rns_core::constants::DESTINATION_SINGLE);
1330
1331 let probe_identity = rns_crypto::identity::Identity::from_private_key(
1333 &identity.get_private_key().unwrap(),
1334 );
1335 driver.proof_strategies.insert(
1336 probe_dest,
1337 (
1338 rns_core::types::ProofStrategy::ProveAll,
1339 Some(probe_identity),
1340 ),
1341 );
1342
1343 driver.probe_responder_hash = Some(probe_dest);
1344
1345 log::info!("Probe responder enabled on {:02x?}", &probe_dest[..4],);
1346 }
1347
1348 let timer_tx = tx.clone();
1350 let timer_interval = Arc::clone(&tick_interval_ms);
1351 thread::Builder::new()
1352 .name("rns-timer".into())
1353 .spawn(move || {
1354 loop {
1355 let ms = timer_interval.load(Ordering::Relaxed);
1356 thread::sleep(Duration::from_millis(ms));
1357 if timer_tx.send(Event::Tick).is_err() {
1358 break; }
1360 }
1361 })?;
1362
1363 #[cfg(feature = "iface-local")]
1365 if config.share_instance {
1366 let local_server_config = LocalServerConfig {
1367 instance_name: config.instance_name.clone(),
1368 port: config.shared_instance_port,
1369 interface_id: rns_core::transport::types::InterfaceId(0), };
1371 match crate::interface::local::start_server(
1372 local_server_config,
1373 tx.clone(),
1374 next_dynamic_id.clone(),
1375 ) {
1376 Ok(control) => {
1377 driver.register_listener_control(control);
1378 log::info!(
1379 "Local shared instance server started (instance={}, port={})",
1380 config.instance_name,
1381 config.shared_instance_port
1382 );
1383 }
1384 Err(e) => {
1385 log::error!("Failed to start local shared instance server: {}", e);
1386 }
1387 }
1388 }
1389
1390 let rpc_server = if config.share_instance {
1392 let auth_key =
1393 crate::rpc::derive_auth_key(&identity.get_private_key().unwrap_or([0u8; 64]));
1394 let rpc_addr = crate::rpc::RpcAddr::Tcp("127.0.0.1".into(), config.rpc_port);
1395 match crate::rpc::RpcServer::start(&rpc_addr, auth_key, tx.clone()) {
1396 Ok(server) => {
1397 log::info!("RPC server started on 127.0.0.1:{}", config.rpc_port);
1398 Some(server)
1399 }
1400 Err(e) => {
1401 log::error!("Failed to start RPC server: {}", e);
1402 None
1403 }
1404 }
1405 } else {
1406 None
1407 };
1408
1409 let announce_verify_queue = Arc::clone(&driver.announce_verify_queue);
1410 let verify_shutdown = Arc::new(AtomicBool::new(false));
1411 let verify_shutdown_thread = Arc::clone(&verify_shutdown);
1412 let verify_tx = tx.clone();
1413 let verify_handle = thread::Builder::new()
1414 .name("rns-verify".into())
1415 .spawn(move || {
1416 #[cfg(target_family = "unix")]
1417 {
1418 unsafe {
1419 libc::nice(5);
1420 }
1421 }
1422
1423 while !verify_shutdown_thread.load(Ordering::Relaxed) {
1424 let batch = {
1425 let mut queue = announce_verify_queue
1426 .lock()
1427 .unwrap_or_else(|poisoned| poisoned.into_inner());
1428 queue.take_pending(time::now())
1429 };
1430
1431 if batch.is_empty() {
1432 thread::sleep(Duration::from_millis(50));
1433 continue;
1434 }
1435
1436 for (key, pending) in batch {
1437 if verify_shutdown_thread.load(Ordering::Relaxed) {
1438 break;
1439 }
1440 let has_ratchet =
1441 pending.packet.flags.context_flag == rns_core::constants::FLAG_SET;
1442 let announce = match rns_core::announce::AnnounceData::unpack(
1443 &pending.packet.data,
1444 has_ratchet,
1445 ) {
1446 Ok(announce) => announce,
1447 Err(_) => {
1448 let signature = [0u8; 64];
1449 let sig_cache_key = {
1450 let mut material = [0u8; 80];
1451 material[..16]
1452 .copy_from_slice(&pending.packet.destination_hash);
1453 material[16..].copy_from_slice(&signature);
1454 rns_core::hash::full_hash(&material)
1455 };
1456 if verify_tx
1457 .send(Event::AnnounceVerifyFailed { key, sig_cache_key })
1458 .is_err()
1459 {
1460 return;
1461 }
1462 continue;
1463 }
1464 };
1465 let mut material = [0u8; 80];
1466 material[..16].copy_from_slice(&pending.packet.destination_hash);
1467 material[16..].copy_from_slice(&announce.signature);
1468 let sig_cache_key = rns_core::hash::full_hash(&material);
1469 match announce.validate(&pending.packet.destination_hash) {
1470 Ok(validated) => {
1471 if verify_tx
1472 .send(Event::AnnounceVerified {
1473 key,
1474 validated,
1475 sig_cache_key,
1476 })
1477 .is_err()
1478 {
1479 return;
1480 }
1481 }
1482 Err(_) => {
1483 if verify_tx
1484 .send(Event::AnnounceVerifyFailed { key, sig_cache_key })
1485 .is_err()
1486 {
1487 return;
1488 }
1489 }
1490 }
1491 }
1492 }
1493 })?;
1494
1495 let driver_handle = thread::Builder::new()
1497 .name("rns-driver".into())
1498 .spawn(move || {
1499 driver.run();
1500 })?;
1501
1502 Ok(RnsNode {
1503 tx,
1504 driver_handle: Some(driver_handle),
1505 verify_handle: Some(verify_handle),
1506 verify_shutdown,
1507 rpc_server,
1508 tick_interval_ms,
1509 probe_server,
1510 })
1511 }
1512
1513 pub fn query(&self, request: QueryRequest) -> Result<QueryResponse, SendError> {
1515 let (resp_tx, resp_rx) = std::sync::mpsc::channel();
1516 self.tx
1517 .send(Event::Query(request, resp_tx))
1518 .map_err(|_| SendError)?;
1519 resp_rx.recv().map_err(|_| SendError)
1520 }
1521
1522 pub fn begin_drain(&self, timeout: Duration) -> Result<(), SendError> {
1524 self.tx
1525 .send(Event::BeginDrain { timeout })
1526 .map_err(|_| SendError)
1527 }
1528
1529 pub fn drain_status(&self) -> Result<crate::event::DrainStatus, SendError> {
1531 match self.query(QueryRequest::DrainStatus)? {
1532 QueryResponse::DrainStatus(status) => Ok(status),
1533 _ => Err(SendError),
1534 }
1535 }
1536
1537 fn reject_new_work_if_draining(&self) -> Result<(), SendError> {
1538 let status = self.drain_status()?;
1539 if matches!(status.state, crate::event::LifecycleState::Active) {
1540 Ok(())
1541 } else {
1542 Err(SendError)
1543 }
1544 }
1545
1546 pub fn send_raw(
1548 &self,
1549 raw: Vec<u8>,
1550 dest_type: u8,
1551 attached_interface: Option<rns_core::transport::types::InterfaceId>,
1552 ) -> Result<(), SendError> {
1553 self.tx
1554 .send(Event::SendOutbound {
1555 raw,
1556 dest_type,
1557 attached_interface,
1558 })
1559 .map_err(|_| SendError)
1560 }
1561
1562 pub fn register_destination(
1564 &self,
1565 dest_hash: [u8; 16],
1566 dest_type: u8,
1567 ) -> Result<(), SendError> {
1568 self.tx
1569 .send(Event::RegisterDestination {
1570 dest_hash,
1571 dest_type,
1572 })
1573 .map_err(|_| SendError)
1574 }
1575
1576 pub fn deregister_destination(&self, dest_hash: [u8; 16]) -> Result<(), SendError> {
1578 self.tx
1579 .send(Event::DeregisterDestination { dest_hash })
1580 .map_err(|_| SendError)
1581 }
1582
1583 pub fn deregister_link_destination(&self, dest_hash: [u8; 16]) -> Result<(), SendError> {
1585 self.tx
1586 .send(Event::DeregisterLinkDestination { dest_hash })
1587 .map_err(|_| SendError)
1588 }
1589
1590 pub fn register_link_destination(
1596 &self,
1597 dest_hash: [u8; 16],
1598 sig_prv_bytes: [u8; 32],
1599 sig_pub_bytes: [u8; 32],
1600 resource_strategy: u8,
1601 ) -> Result<(), SendError> {
1602 self.tx
1603 .send(Event::RegisterLinkDestination {
1604 dest_hash,
1605 sig_prv_bytes,
1606 sig_pub_bytes,
1607 resource_strategy,
1608 })
1609 .map_err(|_| SendError)
1610 }
1611
1612 pub fn register_request_handler<F>(
1614 &self,
1615 path: &str,
1616 allowed_list: Option<Vec<[u8; 16]>>,
1617 handler: F,
1618 ) -> Result<(), SendError>
1619 where
1620 F: Fn([u8; 16], &str, &[u8], Option<&([u8; 16], [u8; 64])>) -> Option<Vec<u8>>
1621 + Send
1622 + 'static,
1623 {
1624 self.tx
1625 .send(Event::RegisterRequestHandler {
1626 path: path.to_string(),
1627 allowed_list,
1628 handler: Box::new(handler),
1629 })
1630 .map_err(|_| SendError)
1631 }
1632
1633 pub fn create_link(
1637 &self,
1638 dest_hash: [u8; 16],
1639 dest_sig_pub_bytes: [u8; 32],
1640 ) -> Result<[u8; 16], SendError> {
1641 self.reject_new_work_if_draining()?;
1642 let (response_tx, response_rx) = std::sync::mpsc::channel();
1643 self.tx
1644 .send(Event::CreateLink {
1645 dest_hash,
1646 dest_sig_pub_bytes,
1647 response_tx,
1648 })
1649 .map_err(|_| SendError)?;
1650 let link_id = response_rx.recv().map_err(|_| SendError)?;
1651 if link_id == [0u8; 16] {
1652 Err(SendError)
1653 } else {
1654 Ok(link_id)
1655 }
1656 }
1657
1658 pub fn send_request(
1660 &self,
1661 link_id: [u8; 16],
1662 path: &str,
1663 data: &[u8],
1664 ) -> Result<(), SendError> {
1665 self.reject_new_work_if_draining()?;
1666 self.tx
1667 .send(Event::SendRequest {
1668 link_id,
1669 path: path.to_string(),
1670 data: data.to_vec(),
1671 })
1672 .map_err(|_| SendError)
1673 }
1674
1675 pub fn identify_on_link(
1677 &self,
1678 link_id: [u8; 16],
1679 identity_prv_key: [u8; 64],
1680 ) -> Result<(), SendError> {
1681 self.reject_new_work_if_draining()?;
1682 self.tx
1683 .send(Event::IdentifyOnLink {
1684 link_id,
1685 identity_prv_key,
1686 })
1687 .map_err(|_| SendError)
1688 }
1689
1690 pub fn teardown_link(&self, link_id: [u8; 16]) -> Result<(), SendError> {
1692 self.tx
1693 .send(Event::TeardownLink { link_id })
1694 .map_err(|_| SendError)
1695 }
1696
1697 pub fn send_resource(
1699 &self,
1700 link_id: [u8; 16],
1701 data: Vec<u8>,
1702 metadata: Option<Vec<u8>>,
1703 ) -> Result<(), SendError> {
1704 self.reject_new_work_if_draining()?;
1705 self.tx
1706 .send(Event::SendResource {
1707 link_id,
1708 data,
1709 metadata,
1710 })
1711 .map_err(|_| SendError)
1712 }
1713
1714 pub fn set_resource_strategy(&self, link_id: [u8; 16], strategy: u8) -> Result<(), SendError> {
1718 self.tx
1719 .send(Event::SetResourceStrategy { link_id, strategy })
1720 .map_err(|_| SendError)
1721 }
1722
1723 pub fn accept_resource(
1725 &self,
1726 link_id: [u8; 16],
1727 resource_hash: Vec<u8>,
1728 accept: bool,
1729 ) -> Result<(), SendError> {
1730 if accept {
1731 self.reject_new_work_if_draining()?;
1732 }
1733 self.tx
1734 .send(Event::AcceptResource {
1735 link_id,
1736 resource_hash,
1737 accept,
1738 })
1739 .map_err(|_| SendError)
1740 }
1741
1742 pub fn send_channel_message(
1744 &self,
1745 link_id: [u8; 16],
1746 msgtype: u16,
1747 payload: Vec<u8>,
1748 ) -> Result<(), SendError> {
1749 self.reject_new_work_if_draining()?;
1750 let (response_tx, response_rx) = std::sync::mpsc::channel();
1751 self.tx
1752 .send(Event::SendChannelMessage {
1753 link_id,
1754 msgtype,
1755 payload,
1756 response_tx,
1757 })
1758 .map_err(|_| SendError)?;
1759 response_rx
1760 .recv()
1761 .map_err(|_| SendError)?
1762 .map_err(|_| SendError)
1763 }
1764
1765 pub fn propose_direct_connect(&self, link_id: [u8; 16]) -> Result<(), SendError> {
1770 self.reject_new_work_if_draining()?;
1771 self.tx
1772 .send(Event::ProposeDirectConnect { link_id })
1773 .map_err(|_| SendError)
1774 }
1775
1776 pub fn set_direct_connect_policy(
1778 &self,
1779 policy: crate::holepunch::orchestrator::HolePunchPolicy,
1780 ) -> Result<(), SendError> {
1781 self.tx
1782 .send(Event::SetDirectConnectPolicy { policy })
1783 .map_err(|_| SendError)
1784 }
1785
1786 pub fn send_on_link(
1788 &self,
1789 link_id: [u8; 16],
1790 data: Vec<u8>,
1791 context: u8,
1792 ) -> Result<(), SendError> {
1793 self.reject_new_work_if_draining()?;
1794 self.tx
1795 .send(Event::SendOnLink {
1796 link_id,
1797 data,
1798 context,
1799 })
1800 .map_err(|_| SendError)
1801 }
1802
1803 pub fn announce(
1808 &self,
1809 dest: &crate::destination::Destination,
1810 identity: &Identity,
1811 app_data: Option<&[u8]>,
1812 ) -> Result<(), SendError> {
1813 self.reject_new_work_if_draining()?;
1814 let name_hash = rns_core::destination::name_hash(
1815 &dest.app_name,
1816 &dest.aspects.iter().map(|s| s.as_str()).collect::<Vec<_>>(),
1817 );
1818
1819 let mut random_hash = [0u8; 10];
1820 OsRng.fill_bytes(&mut random_hash[..5]);
1821 let now_secs = std::time::SystemTime::now()
1825 .duration_since(std::time::UNIX_EPOCH)
1826 .unwrap_or_default()
1827 .as_secs();
1828 random_hash[5..10].copy_from_slice(&now_secs.to_be_bytes()[3..8]);
1829
1830 let (announce_data, _has_ratchet) = rns_core::announce::AnnounceData::pack(
1831 identity,
1832 &dest.hash.0,
1833 &name_hash,
1834 &random_hash,
1835 None, app_data,
1837 )
1838 .map_err(|_| SendError)?;
1839
1840 let context_flag = rns_core::constants::FLAG_UNSET;
1841
1842 let flags = rns_core::packet::PacketFlags {
1843 header_type: rns_core::constants::HEADER_1,
1844 context_flag,
1845 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
1846 destination_type: rns_core::constants::DESTINATION_SINGLE,
1847 packet_type: rns_core::constants::PACKET_TYPE_ANNOUNCE,
1848 };
1849
1850 let packet = rns_core::packet::RawPacket::pack(
1851 flags,
1852 0,
1853 &dest.hash.0,
1854 None,
1855 rns_core::constants::CONTEXT_NONE,
1856 &announce_data,
1857 )
1858 .map_err(|_| SendError)?;
1859
1860 if dest.dest_type == rns_core::types::DestinationType::Single {
1861 if let Some(identity_prv_key) = identity.get_private_key() {
1862 self.tx
1863 .send(Event::StoreSharedAnnounce {
1864 dest_hash: dest.hash.0,
1865 name_hash,
1866 identity_prv_key,
1867 app_data: app_data.map(|d| d.to_vec()),
1868 })
1869 .map_err(|_| SendError)?;
1870 }
1871 }
1872
1873 self.send_raw(packet.raw, dest.dest_type.to_wire_constant(), None)
1874 }
1875
1876 pub fn send_packet(
1881 &self,
1882 dest: &crate::destination::Destination,
1883 data: &[u8],
1884 ) -> Result<rns_core::types::PacketHash, SendError> {
1885 self.reject_new_work_if_draining()?;
1886 use rns_core::types::DestinationType;
1887
1888 let payload = match dest.dest_type {
1889 DestinationType::Single => {
1890 let pub_key = dest.public_key.ok_or(SendError)?;
1891 let remote_id = rns_crypto::identity::Identity::from_public_key(&pub_key);
1892 remote_id.encrypt(data, &mut OsRng).map_err(|_| SendError)?
1893 }
1894 DestinationType::Plain => data.to_vec(),
1895 DestinationType::Group => dest.encrypt(data).map_err(|_| SendError)?,
1896 };
1897
1898 let flags = rns_core::packet::PacketFlags {
1899 header_type: rns_core::constants::HEADER_1,
1900 context_flag: rns_core::constants::FLAG_UNSET,
1901 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
1902 destination_type: dest.dest_type.to_wire_constant(),
1903 packet_type: rns_core::constants::PACKET_TYPE_DATA,
1904 };
1905
1906 let packet = rns_core::packet::RawPacket::pack(
1907 flags,
1908 0,
1909 &dest.hash.0,
1910 None,
1911 rns_core::constants::CONTEXT_NONE,
1912 &payload,
1913 )
1914 .map_err(|_| SendError)?;
1915
1916 let packet_hash = rns_core::types::PacketHash(packet.packet_hash);
1917
1918 self.tx
1919 .send(Event::SendOutbound {
1920 raw: packet.raw,
1921 dest_type: dest.dest_type.to_wire_constant(),
1922 attached_interface: None,
1923 })
1924 .map_err(|_| SendError)?;
1925
1926 Ok(packet_hash)
1927 }
1928
1929 pub fn register_destination_with_proof(
1934 &self,
1935 dest: &crate::destination::Destination,
1936 signing_key: Option<[u8; 64]>,
1937 ) -> Result<(), SendError> {
1938 self.register_destination(dest.hash.0, dest.dest_type.to_wire_constant())?;
1940
1941 if dest.proof_strategy != rns_core::types::ProofStrategy::ProveNone {
1943 self.tx
1944 .send(Event::RegisterProofStrategy {
1945 dest_hash: dest.hash.0,
1946 strategy: dest.proof_strategy,
1947 signing_key,
1948 })
1949 .map_err(|_| SendError)?;
1950 }
1951
1952 Ok(())
1953 }
1954
1955 pub fn request_path(&self, dest_hash: &rns_core::types::DestHash) -> Result<(), SendError> {
1957 self.reject_new_work_if_draining()?;
1958 self.tx
1959 .send(Event::RequestPath {
1960 dest_hash: dest_hash.0,
1961 })
1962 .map_err(|_| SendError)
1963 }
1964
1965 pub fn has_path(&self, dest_hash: &rns_core::types::DestHash) -> Result<bool, SendError> {
1967 match self.query(QueryRequest::HasPath {
1968 dest_hash: dest_hash.0,
1969 })? {
1970 QueryResponse::HasPath(v) => Ok(v),
1971 _ => Ok(false),
1972 }
1973 }
1974
1975 pub fn hops_to(&self, dest_hash: &rns_core::types::DestHash) -> Result<Option<u8>, SendError> {
1977 match self.query(QueryRequest::HopsTo {
1978 dest_hash: dest_hash.0,
1979 })? {
1980 QueryResponse::HopsTo(v) => Ok(v),
1981 _ => Ok(None),
1982 }
1983 }
1984
1985 pub fn recall_identity(
1987 &self,
1988 dest_hash: &rns_core::types::DestHash,
1989 ) -> Result<Option<crate::destination::AnnouncedIdentity>, SendError> {
1990 match self.query(QueryRequest::RecallIdentity {
1991 dest_hash: dest_hash.0,
1992 })? {
1993 QueryResponse::RecallIdentity(v) => Ok(v),
1994 _ => Ok(None),
1995 }
1996 }
1997
1998 pub fn load_hook(
2000 &self,
2001 name: String,
2002 wasm_bytes: Vec<u8>,
2003 attach_point: String,
2004 priority: i32,
2005 ) -> Result<Result<(), String>, SendError> {
2006 let (response_tx, response_rx) = std::sync::mpsc::channel();
2007 self.tx
2008 .send(Event::LoadHook {
2009 name,
2010 wasm_bytes,
2011 attach_point,
2012 priority,
2013 response_tx,
2014 })
2015 .map_err(|_| SendError)?;
2016 response_rx.recv().map_err(|_| SendError)
2017 }
2018
2019 pub fn unload_hook(
2021 &self,
2022 name: String,
2023 attach_point: String,
2024 ) -> Result<Result<(), String>, SendError> {
2025 let (response_tx, response_rx) = std::sync::mpsc::channel();
2026 self.tx
2027 .send(Event::UnloadHook {
2028 name,
2029 attach_point,
2030 response_tx,
2031 })
2032 .map_err(|_| SendError)?;
2033 response_rx.recv().map_err(|_| SendError)
2034 }
2035
2036 pub fn reload_hook(
2038 &self,
2039 name: String,
2040 attach_point: String,
2041 wasm_bytes: Vec<u8>,
2042 ) -> Result<Result<(), String>, SendError> {
2043 let (response_tx, response_rx) = std::sync::mpsc::channel();
2044 self.tx
2045 .send(Event::ReloadHook {
2046 name,
2047 attach_point,
2048 wasm_bytes,
2049 response_tx,
2050 })
2051 .map_err(|_| SendError)?;
2052 response_rx.recv().map_err(|_| SendError)
2053 }
2054
2055 pub fn set_hook_enabled(
2057 &self,
2058 name: String,
2059 attach_point: String,
2060 enabled: bool,
2061 ) -> Result<Result<(), String>, SendError> {
2062 let (response_tx, response_rx) = std::sync::mpsc::channel();
2063 self.tx
2064 .send(Event::SetHookEnabled {
2065 name,
2066 attach_point,
2067 enabled,
2068 response_tx,
2069 })
2070 .map_err(|_| SendError)?;
2071 response_rx.recv().map_err(|_| SendError)
2072 }
2073
2074 pub fn set_hook_priority(
2076 &self,
2077 name: String,
2078 attach_point: String,
2079 priority: i32,
2080 ) -> Result<Result<(), String>, SendError> {
2081 let (response_tx, response_rx) = std::sync::mpsc::channel();
2082 self.tx
2083 .send(Event::SetHookPriority {
2084 name,
2085 attach_point,
2086 priority,
2087 response_tx,
2088 })
2089 .map_err(|_| SendError)?;
2090 response_rx.recv().map_err(|_| SendError)
2091 }
2092
2093 pub fn list_hooks(&self) -> Result<Vec<crate::event::HookInfo>, SendError> {
2095 let (response_tx, response_rx) = std::sync::mpsc::channel();
2096 self.tx
2097 .send(Event::ListHooks { response_tx })
2098 .map_err(|_| SendError)?;
2099 response_rx.recv().map_err(|_| SendError)
2100 }
2101
2102 pub(crate) fn from_parts(
2105 tx: EventSender,
2106 driver_handle: thread::JoinHandle<()>,
2107 rpc_server: Option<crate::rpc::RpcServer>,
2108 tick_interval_ms: Arc<AtomicU64>,
2109 ) -> Self {
2110 RnsNode {
2111 tx,
2112 driver_handle: Some(driver_handle),
2113 verify_handle: None,
2114 verify_shutdown: Arc::new(AtomicBool::new(false)),
2115 rpc_server,
2116 tick_interval_ms,
2117 probe_server: None,
2118 }
2119 }
2120
2121 pub fn event_sender(&self) -> &EventSender {
2123 &self.tx
2124 }
2125
2126 pub fn set_tick_interval(&self, ms: u64) -> u64 {
2131 let clamped = ms.clamp(100, 10_000);
2132 if clamped != ms {
2133 log::warn!(
2134 "tick interval {}ms out of range, clamped to {}ms",
2135 ms,
2136 clamped
2137 );
2138 }
2139 self.tick_interval_ms.store(clamped, Ordering::Relaxed);
2140 clamped
2141 }
2142
2143 pub fn tick_interval(&self) -> u64 {
2145 self.tick_interval_ms.load(Ordering::Relaxed)
2146 }
2147
2148 pub fn shutdown(mut self) {
2150 if let Some(mut rpc) = self.rpc_server.take() {
2152 rpc.stop();
2153 }
2154 self.verify_shutdown.store(true, Ordering::Relaxed);
2155 let _ = self.tx.send(Event::Shutdown);
2156 if let Some(handle) = self.driver_handle.take() {
2157 let _ = handle.join();
2158 }
2159 if let Some(handle) = self.verify_handle.take() {
2160 let _ = handle.join();
2161 }
2162 }
2163}
2164
2165#[cfg(test)]
2166mod tests {
2167 use super::*;
2168 use std::fs;
2169
2170 struct NoopCallbacks;
2171
2172 impl Callbacks for NoopCallbacks {
2173 fn on_announce(&mut self, _: crate::destination::AnnouncedIdentity) {}
2174 fn on_path_updated(&mut self, _: rns_core::types::DestHash, _: u8) {}
2175 fn on_local_delivery(
2176 &mut self,
2177 _: rns_core::types::DestHash,
2178 _: Vec<u8>,
2179 _: rns_core::types::PacketHash,
2180 ) {
2181 }
2182 }
2183
2184 #[test]
2185 fn tcp_client_interface_is_not_discoverable_without_kiss_framing() {
2186 let mut params = std::collections::HashMap::new();
2187 params.insert("discoverable".to_string(), "yes".to_string());
2188 params.insert(
2189 "discovery_name".to_string(),
2190 "invalid-tcp-client".to_string(),
2191 );
2192 params.insert("reachable_on".to_string(), "example.com".to_string());
2193 params.insert("target_port".to_string(), "4242".to_string());
2194
2195 let discovery =
2196 super::extract_discovery_config("tcp-client", "TCPClientInterface", ¶ms);
2197
2198 assert!(
2199 discovery.is_none(),
2200 "TCPClientInterface discovery must be rejected unless KISS framing is supported"
2201 );
2202 }
2203
2204 #[test]
2205 fn ingress_control_config_defaults_by_interface_type() {
2206 let params = std::collections::HashMap::new();
2207
2208 let tcp = super::parse_ingress_control_config("TCPServerInterface", ¶ms).unwrap();
2209 assert!(tcp.enabled);
2210 assert_eq!(
2211 tcp.max_held_announces,
2212 rns_core::constants::IC_MAX_HELD_ANNOUNCES
2213 );
2214 assert_eq!(tcp.burst_hold, rns_core::constants::IC_BURST_HOLD);
2215
2216 let pipe = super::parse_ingress_control_config("PipeInterface", ¶ms).unwrap();
2217 assert!(!pipe.enabled);
2218 assert_eq!(
2219 pipe.held_release_interval,
2220 rns_core::constants::IC_HELD_RELEASE_INTERVAL
2221 );
2222 }
2223
2224 #[test]
2225 fn ingress_control_config_parses_python_ic_keys() {
2226 let mut params = std::collections::HashMap::new();
2227 params.insert("ingress_control".to_string(), "No".to_string());
2228 params.insert("ic_max_held_announces".to_string(), "17".to_string());
2229 params.insert("ic_burst_hold".to_string(), "1.5".to_string());
2230 params.insert("ic_burst_freq_new".to_string(), "2.5".to_string());
2231 params.insert("ic_burst_freq".to_string(), "3.5".to_string());
2232 params.insert("ic_new_time".to_string(), "4.5".to_string());
2233 params.insert("ic_burst_penalty".to_string(), "5.5".to_string());
2234 params.insert("ic_held_release_interval".to_string(), "6.5".to_string());
2235
2236 let config = super::parse_ingress_control_config("TCPServerInterface", ¶ms).unwrap();
2237
2238 assert!(!config.enabled);
2239 assert_eq!(config.max_held_announces, 17);
2240 assert_eq!(config.burst_hold, 1.5);
2241 assert_eq!(config.burst_freq_new, 2.5);
2242 assert_eq!(config.burst_freq, 3.5);
2243 assert_eq!(config.new_time, 4.5);
2244 assert_eq!(config.burst_penalty, 5.5);
2245 assert_eq!(config.held_release_interval, 6.5);
2246 }
2247
2248 #[test]
2249 fn ingress_control_config_rejects_invalid_values() {
2250 let mut params = std::collections::HashMap::new();
2251 params.insert("ic_burst_hold".to_string(), "-1".to_string());
2252
2253 let err = super::parse_ingress_control_config("TCPServerInterface", ¶ms).unwrap_err();
2254
2255 assert!(err.contains("ic_burst_hold"));
2256 }
2257
2258 #[test]
2259 fn start_and_shutdown() {
2260 let node = RnsNode::start(
2261 NodeConfig {
2262 panic_on_interface_error: false,
2263 transport_enabled: false,
2264 identity: None,
2265 interfaces: vec![],
2266 share_instance: false,
2267 instance_name: "default".into(),
2268 shared_instance_port: 37428,
2269 rpc_port: 0,
2270 cache_dir: None,
2271 management: Default::default(),
2272 probe_port: None,
2273 probe_addrs: vec![],
2274 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
2275 device: None,
2276 hooks: Vec::new(),
2277 discover_interfaces: false,
2278 discovery_required_value: None,
2279 respond_to_probes: false,
2280 prefer_shorter_path: false,
2281 max_paths_per_destination: 1,
2282 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
2283 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
2284 max_path_destinations: usize::MAX,
2285 max_tunnel_destinations_total: usize::MAX,
2286 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
2287 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
2288 announce_table_ttl: Duration::from_secs(
2289 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
2290 ),
2291 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
2292 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
2293 interface_writer_queue_capacity:
2294 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
2295 announce_sig_cache_enabled: true,
2296 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
2297 announce_sig_cache_ttl: Duration::from_secs(
2298 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
2299 ),
2300 registry: None,
2301 #[cfg(feature = "rns-hooks")]
2302 provider_bridge: None,
2303 },
2304 Box::new(NoopCallbacks),
2305 )
2306 .unwrap();
2307 node.shutdown();
2308 }
2309
2310 #[test]
2311 fn start_with_identity() {
2312 let identity = Identity::new(&mut OsRng);
2313 let hash = *identity.hash();
2314 let node = RnsNode::start(
2315 NodeConfig {
2316 panic_on_interface_error: false,
2317 transport_enabled: true,
2318 identity: Some(identity),
2319 interfaces: vec![],
2320 share_instance: false,
2321 instance_name: "default".into(),
2322 shared_instance_port: 37428,
2323 rpc_port: 0,
2324 cache_dir: None,
2325 management: Default::default(),
2326 probe_port: None,
2327 probe_addrs: vec![],
2328 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
2329 device: None,
2330 hooks: Vec::new(),
2331 discover_interfaces: false,
2332 discovery_required_value: None,
2333 respond_to_probes: false,
2334 prefer_shorter_path: false,
2335 max_paths_per_destination: 1,
2336 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
2337 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
2338 max_path_destinations: usize::MAX,
2339 max_tunnel_destinations_total: usize::MAX,
2340 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
2341 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
2342 announce_table_ttl: Duration::from_secs(
2343 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
2344 ),
2345 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
2346 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
2347 interface_writer_queue_capacity:
2348 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
2349 announce_sig_cache_enabled: true,
2350 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
2351 announce_sig_cache_ttl: Duration::from_secs(
2352 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
2353 ),
2354 registry: None,
2355 #[cfg(feature = "rns-hooks")]
2356 provider_bridge: None,
2357 },
2358 Box::new(NoopCallbacks),
2359 )
2360 .unwrap();
2361 let _ = hash;
2363 node.shutdown();
2364 }
2365
2366 #[test]
2367 fn start_generates_identity() {
2368 let node = RnsNode::start(
2369 NodeConfig {
2370 panic_on_interface_error: false,
2371 transport_enabled: false,
2372 identity: None,
2373 interfaces: vec![],
2374 share_instance: false,
2375 instance_name: "default".into(),
2376 shared_instance_port: 37428,
2377 rpc_port: 0,
2378 cache_dir: None,
2379 management: Default::default(),
2380 probe_port: None,
2381 probe_addrs: vec![],
2382 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
2383 device: None,
2384 hooks: Vec::new(),
2385 discover_interfaces: false,
2386 discovery_required_value: None,
2387 respond_to_probes: false,
2388 prefer_shorter_path: false,
2389 max_paths_per_destination: 1,
2390 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
2391 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
2392 max_path_destinations: usize::MAX,
2393 max_tunnel_destinations_total: usize::MAX,
2394 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
2395 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
2396 announce_table_ttl: Duration::from_secs(
2397 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
2398 ),
2399 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
2400 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
2401 interface_writer_queue_capacity:
2402 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
2403 announce_sig_cache_enabled: true,
2404 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
2405 announce_sig_cache_ttl: Duration::from_secs(
2406 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
2407 ),
2408 registry: None,
2409 #[cfg(feature = "rns-hooks")]
2410 provider_bridge: None,
2411 },
2412 Box::new(NoopCallbacks),
2413 )
2414 .unwrap();
2415 node.shutdown();
2417 }
2418
2419 #[test]
2420 fn from_config_creates_identity() {
2421 let dir = std::env::temp_dir().join(format!("rns-test-fc-{}", std::process::id()));
2422 let _ = fs::remove_dir_all(&dir);
2423 fs::create_dir_all(&dir).unwrap();
2424
2425 fs::write(
2427 dir.join("config"),
2428 "[reticulum]\nenable_transport = False\n",
2429 )
2430 .unwrap();
2431
2432 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
2433
2434 assert!(dir.join("storage/identities/identity").exists());
2436
2437 node.shutdown();
2438 let _ = fs::remove_dir_all(&dir);
2439 }
2440
2441 #[test]
2442 fn from_config_loads_identity() {
2443 let dir = std::env::temp_dir().join(format!("rns-test-fl-{}", std::process::id()));
2444 let _ = fs::remove_dir_all(&dir);
2445 fs::create_dir_all(dir.join("storage/identities")).unwrap();
2446
2447 let identity = Identity::new(&mut OsRng);
2449 let hash = *identity.hash();
2450 storage::save_identity(&identity, &dir.join("storage/identities/identity")).unwrap();
2451
2452 fs::write(
2453 dir.join("config"),
2454 "[reticulum]\nenable_transport = False\n",
2455 )
2456 .unwrap();
2457
2458 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
2459
2460 let loaded = storage::load_identity(&dir.join("storage/identities/identity")).unwrap();
2462 assert_eq!(*loaded.hash(), hash);
2463
2464 node.shutdown();
2465 let _ = fs::remove_dir_all(&dir);
2466 }
2467
2468 #[test]
2469 fn from_config_tcp_server() {
2470 let dir = std::env::temp_dir().join(format!("rns-test-fts-{}", std::process::id()));
2471 let _ = fs::remove_dir_all(&dir);
2472 fs::create_dir_all(&dir).unwrap();
2473
2474 let port = std::net::TcpListener::bind("127.0.0.1:0")
2476 .unwrap()
2477 .local_addr()
2478 .unwrap()
2479 .port();
2480
2481 let config = format!(
2482 r#"
2483[reticulum]
2484enable_transport = False
2485
2486[interfaces]
2487 [[Test TCP Server]]
2488 type = TCPServerInterface
2489 listen_ip = 127.0.0.1
2490 listen_port = {}
2491"#,
2492 port
2493 );
2494
2495 fs::write(dir.join("config"), config).unwrap();
2496
2497 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
2498
2499 thread::sleep(Duration::from_millis(100));
2501
2502 let _client = std::net::TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
2504
2505 node.shutdown();
2506 let _ = fs::remove_dir_all(&dir);
2507 }
2508
2509 #[test]
2510 fn from_config_starts_rpc_when_share_instance_enabled() {
2511 let dir = std::env::temp_dir().join(format!("rns-test-rpc-{}", std::process::id()));
2512 let _ = fs::remove_dir_all(&dir);
2513 fs::create_dir_all(&dir).unwrap();
2514
2515 let rpc_port = std::net::TcpListener::bind("127.0.0.1:0")
2516 .unwrap()
2517 .local_addr()
2518 .unwrap()
2519 .port();
2520
2521 let config = format!(
2522 r#"
2523[reticulum]
2524enable_transport = False
2525share_instance = Yes
2526instance_control_port = {}
2527
2528[interfaces]
2529"#,
2530 rpc_port
2531 );
2532
2533 fs::write(dir.join("config"), config).unwrap();
2534
2535 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
2536
2537 thread::sleep(Duration::from_millis(100));
2538
2539 let _client = std::net::TcpStream::connect(format!("127.0.0.1:{}", rpc_port)).unwrap();
2540
2541 node.shutdown();
2542 let _ = fs::remove_dir_all(&dir);
2543 }
2544
2545 #[test]
2546 fn from_config_starts_rpc_when_transport_enabled() {
2547 let dir =
2548 std::env::temp_dir().join(format!("rns-test-rpc-transport-{}", std::process::id()));
2549 let _ = fs::remove_dir_all(&dir);
2550 fs::create_dir_all(&dir).unwrap();
2551
2552 let rpc_port = std::net::TcpListener::bind("127.0.0.1:0")
2553 .unwrap()
2554 .local_addr()
2555 .unwrap()
2556 .port();
2557
2558 let config = format!(
2559 r#"
2560[reticulum]
2561enable_transport = True
2562share_instance = Yes
2563instance_control_port = {}
2564
2565[interfaces]
2566"#,
2567 rpc_port
2568 );
2569
2570 fs::write(dir.join("config"), config).unwrap();
2571
2572 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
2573
2574 thread::sleep(Duration::from_millis(100));
2575
2576 let _client = std::net::TcpStream::connect(format!("127.0.0.1:{}", rpc_port)).unwrap();
2577
2578 node.shutdown();
2579 let _ = fs::remove_dir_all(&dir);
2580 }
2581
2582 #[test]
2583 fn from_config_starts_rpc_when_tcp_client_is_unreachable() {
2584 let dir =
2585 std::env::temp_dir().join(format!("rns-test-rpc-unreachable-{}", std::process::id()));
2586 let _ = fs::remove_dir_all(&dir);
2587 fs::create_dir_all(&dir).unwrap();
2588
2589 let rpc_port = std::net::TcpListener::bind("127.0.0.1:0")
2590 .unwrap()
2591 .local_addr()
2592 .unwrap()
2593 .port();
2594 let unreachable_port = std::net::TcpListener::bind("127.0.0.1:0")
2595 .unwrap()
2596 .local_addr()
2597 .unwrap()
2598 .port();
2599
2600 let config = format!(
2601 r#"
2602[reticulum]
2603enable_transport = True
2604share_instance = Yes
2605instance_control_port = {}
2606
2607[interfaces]
2608 [[Unreachable Upstream]]
2609 type = TCPClientInterface
2610 target_host = 127.0.0.1
2611 target_port = {}
2612"#,
2613 rpc_port, unreachable_port
2614 );
2615
2616 fs::write(dir.join("config"), config).unwrap();
2617
2618 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
2619
2620 thread::sleep(Duration::from_millis(100));
2621
2622 let _client = std::net::TcpStream::connect(format!("127.0.0.1:{}", rpc_port)).unwrap();
2623
2624 node.shutdown();
2625 let _ = fs::remove_dir_all(&dir);
2626 }
2627
2628 #[test]
2629 fn test_parse_interface_mode() {
2630 use rns_core::constants::*;
2631
2632 assert_eq!(parse_interface_mode("full"), MODE_FULL);
2633 assert_eq!(parse_interface_mode("Full"), MODE_FULL);
2634 assert_eq!(parse_interface_mode("access_point"), MODE_ACCESS_POINT);
2635 assert_eq!(parse_interface_mode("accesspoint"), MODE_ACCESS_POINT);
2636 assert_eq!(parse_interface_mode("ap"), MODE_ACCESS_POINT);
2637 assert_eq!(parse_interface_mode("AP"), MODE_ACCESS_POINT);
2638 assert_eq!(parse_interface_mode("pointtopoint"), MODE_POINT_TO_POINT);
2639 assert_eq!(parse_interface_mode("ptp"), MODE_POINT_TO_POINT);
2640 assert_eq!(parse_interface_mode("roaming"), MODE_ROAMING);
2641 assert_eq!(parse_interface_mode("boundary"), MODE_BOUNDARY);
2642 assert_eq!(parse_interface_mode("gateway"), MODE_GATEWAY);
2643 assert_eq!(parse_interface_mode("gw"), MODE_GATEWAY);
2644 assert_eq!(parse_interface_mode("invalid"), MODE_FULL);
2646 }
2647
2648 #[test]
2649 fn to_node_config_serial() {
2650 let dir = std::env::temp_dir().join(format!("rns-test-serial-{}", std::process::id()));
2654 let _ = fs::remove_dir_all(&dir);
2655 fs::create_dir_all(&dir).unwrap();
2656
2657 let config = r#"
2658[reticulum]
2659enable_transport = False
2660
2661[interfaces]
2662 [[Test Serial Port]]
2663 type = SerialInterface
2664 port = /dev/nonexistent_rns_test_serial
2665 speed = 115200
2666 databits = 8
2667 parity = E
2668 stopbits = 1
2669 interface_mode = ptp
2670 networkname = testnet
2671"#;
2672 fs::write(dir.join("config"), config).unwrap();
2673
2674 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks))
2676 .expect("Config should parse; interface failure is non-fatal");
2677 node.shutdown();
2678
2679 let _ = fs::remove_dir_all(&dir);
2680 }
2681
2682 #[test]
2683 fn to_node_config_kiss() {
2684 let dir = std::env::temp_dir().join(format!("rns-test-kiss-{}", std::process::id()));
2686 let _ = fs::remove_dir_all(&dir);
2687 fs::create_dir_all(&dir).unwrap();
2688
2689 let config = r#"
2690[reticulum]
2691enable_transport = False
2692
2693[interfaces]
2694 [[Test KISS TNC]]
2695 type = KISSInterface
2696 port = /dev/nonexistent_rns_test_kiss
2697 speed = 9600
2698 preamble = 500
2699 txtail = 30
2700 persistence = 128
2701 slottime = 40
2702 flow_control = True
2703 id_interval = 600
2704 id_callsign = TEST0
2705 interface_mode = full
2706 passphrase = secretkey
2707"#;
2708 fs::write(dir.join("config"), config).unwrap();
2709
2710 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks))
2712 .expect("Config should parse; interface failure is non-fatal");
2713 node.shutdown();
2714
2715 let _ = fs::remove_dir_all(&dir);
2716 }
2717
2718 #[test]
2719 fn test_extract_ifac_config() {
2720 use std::collections::HashMap;
2721
2722 let params: HashMap<String, String> = HashMap::new();
2724 assert!(extract_ifac_config(¶ms, 16).is_none());
2725
2726 let mut params = HashMap::new();
2728 params.insert("networkname".into(), "testnet".into());
2729 let ifac = extract_ifac_config(¶ms, 16).unwrap();
2730 assert_eq!(ifac.netname.as_deref(), Some("testnet"));
2731 assert!(ifac.netkey.is_none());
2732 assert_eq!(ifac.size, 16);
2733
2734 let mut params = HashMap::new();
2736 params.insert("passphrase".into(), "secret".into());
2737 params.insert("ifac_size".into(), "64".into()); let ifac = extract_ifac_config(¶ms, 16).unwrap();
2739 assert!(ifac.netname.is_none());
2740 assert_eq!(ifac.netkey.as_deref(), Some("secret"));
2741 assert_eq!(ifac.size, 8);
2742
2743 let mut params = HashMap::new();
2745 params.insert("network_name".into(), "mynet".into());
2746 params.insert("pass_phrase".into(), "mykey".into());
2747 let ifac = extract_ifac_config(¶ms, 8).unwrap();
2748 assert_eq!(ifac.netname.as_deref(), Some("mynet"));
2749 assert_eq!(ifac.netkey.as_deref(), Some("mykey"));
2750 assert_eq!(ifac.size, 8);
2751 }
2752
2753 #[test]
2754 fn to_node_config_rnode() {
2755 let dir = std::env::temp_dir().join(format!("rns-test-rnode-{}", std::process::id()));
2758 let _ = fs::remove_dir_all(&dir);
2759 fs::create_dir_all(&dir).unwrap();
2760
2761 let config = r#"
2762[reticulum]
2763enable_transport = False
2764
2765[interfaces]
2766 [[Test RNode]]
2767 type = RNodeInterface
2768 port = /dev/nonexistent_rns_test_rnode
2769 frequency = 867200000
2770 bandwidth = 125000
2771 txpower = 7
2772 spreadingfactor = 8
2773 codingrate = 5
2774 flow_control = True
2775 st_alock = 5.0
2776 lt_alock = 2.5
2777 interface_mode = full
2778 networkname = testnet
2779"#;
2780 fs::write(dir.join("config"), config).unwrap();
2781
2782 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks))
2784 .expect("Config should parse; interface failure is non-fatal");
2785 node.shutdown();
2786
2787 let _ = fs::remove_dir_all(&dir);
2788 }
2789
2790 #[test]
2791 fn to_node_config_pipe() {
2792 let dir = std::env::temp_dir().join(format!("rns-test-pipe-{}", std::process::id()));
2795 let _ = fs::remove_dir_all(&dir);
2796 fs::create_dir_all(&dir).unwrap();
2797
2798 let config = r#"
2799[reticulum]
2800enable_transport = False
2801
2802[interfaces]
2803 [[Test Pipe]]
2804 type = PipeInterface
2805 command = cat
2806 respawn_delay = 5000
2807 interface_mode = full
2808"#;
2809 fs::write(dir.join("config"), config).unwrap();
2810
2811 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
2812 node.shutdown();
2814
2815 let _ = fs::remove_dir_all(&dir);
2816 }
2817
2818 #[test]
2819 fn to_node_config_backbone() {
2820 let dir = std::env::temp_dir().join(format!("rns-test-backbone-{}", std::process::id()));
2822 let _ = fs::remove_dir_all(&dir);
2823 fs::create_dir_all(&dir).unwrap();
2824
2825 let port = std::net::TcpListener::bind("127.0.0.1:0")
2826 .unwrap()
2827 .local_addr()
2828 .unwrap()
2829 .port();
2830
2831 let config = format!(
2832 r#"
2833[reticulum]
2834enable_transport = False
2835
2836[interfaces]
2837 [[Test Backbone]]
2838 type = BackboneInterface
2839 listen_ip = 127.0.0.1
2840 listen_port = {}
2841 interface_mode = full
2842"#,
2843 port
2844 );
2845
2846 fs::write(dir.join("config"), config).unwrap();
2847
2848 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
2849
2850 thread::sleep(Duration::from_millis(100));
2852
2853 {
2855 let _client = std::net::TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
2856 }
2858
2859 thread::sleep(Duration::from_millis(50));
2861
2862 node.shutdown();
2863 let _ = fs::remove_dir_all(&dir);
2864 }
2865
2866 #[test]
2867 fn rnode_config_defaults() {
2868 use crate::interface::rnode::{RNodeConfig, RNodeSubConfig};
2869
2870 let config = RNodeConfig::default();
2871 assert_eq!(config.speed, 115200);
2872 assert!(config.subinterfaces.is_empty());
2873 assert!(config.id_interval.is_none());
2874 assert!(config.id_callsign.is_none());
2875
2876 let sub = RNodeSubConfig {
2877 name: "test".into(),
2878 frequency: 868_000_000,
2879 bandwidth: 125_000,
2880 txpower: 7,
2881 spreading_factor: 8,
2882 coding_rate: 5,
2883 flow_control: false,
2884 st_alock: None,
2885 lt_alock: None,
2886 };
2887 assert_eq!(sub.frequency, 868_000_000);
2888 assert_eq!(sub.bandwidth, 125_000);
2889 assert!(!sub.flow_control);
2890 }
2891
2892 #[test]
2897 fn announce_builds_valid_packet() {
2898 let identity = Identity::new(&mut OsRng);
2899 let identity_hash = rns_core::types::IdentityHash(*identity.hash());
2900
2901 let node = RnsNode::start(
2902 NodeConfig {
2903 panic_on_interface_error: false,
2904 transport_enabled: false,
2905 identity: None,
2906 interfaces: vec![],
2907 share_instance: false,
2908 instance_name: "default".into(),
2909 shared_instance_port: 37428,
2910 rpc_port: 0,
2911 cache_dir: None,
2912 management: Default::default(),
2913 probe_port: None,
2914 probe_addrs: vec![],
2915 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
2916 device: None,
2917 hooks: Vec::new(),
2918 discover_interfaces: false,
2919 discovery_required_value: None,
2920 respond_to_probes: false,
2921 prefer_shorter_path: false,
2922 max_paths_per_destination: 1,
2923 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
2924 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
2925 max_path_destinations: usize::MAX,
2926 max_tunnel_destinations_total: usize::MAX,
2927 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
2928 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
2929 announce_table_ttl: Duration::from_secs(
2930 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
2931 ),
2932 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
2933 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
2934 interface_writer_queue_capacity:
2935 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
2936 announce_sig_cache_enabled: true,
2937 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
2938 announce_sig_cache_ttl: Duration::from_secs(
2939 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
2940 ),
2941 registry: None,
2942 #[cfg(feature = "rns-hooks")]
2943 provider_bridge: None,
2944 },
2945 Box::new(NoopCallbacks),
2946 )
2947 .unwrap();
2948
2949 let dest = crate::destination::Destination::single_in("test", &["echo"], identity_hash);
2950
2951 node.register_destination(dest.hash.0, dest.dest_type.to_wire_constant())
2953 .unwrap();
2954
2955 let result = node.announce(&dest, &identity, Some(b"hello"));
2957 assert!(result.is_ok());
2958
2959 node.shutdown();
2960 }
2961
2962 #[test]
2963 fn has_path_and_hops_to() {
2964 let node = RnsNode::start(
2965 NodeConfig {
2966 panic_on_interface_error: false,
2967 transport_enabled: false,
2968 identity: None,
2969 interfaces: vec![],
2970 share_instance: false,
2971 instance_name: "default".into(),
2972 shared_instance_port: 37428,
2973 rpc_port: 0,
2974 cache_dir: None,
2975 management: Default::default(),
2976 probe_port: None,
2977 probe_addrs: vec![],
2978 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
2979 device: None,
2980 hooks: Vec::new(),
2981 discover_interfaces: false,
2982 discovery_required_value: None,
2983 respond_to_probes: false,
2984 prefer_shorter_path: false,
2985 max_paths_per_destination: 1,
2986 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
2987 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
2988 max_path_destinations: usize::MAX,
2989 max_tunnel_destinations_total: usize::MAX,
2990 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
2991 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
2992 announce_table_ttl: Duration::from_secs(
2993 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
2994 ),
2995 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
2996 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
2997 interface_writer_queue_capacity:
2998 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
2999 announce_sig_cache_enabled: true,
3000 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
3001 announce_sig_cache_ttl: Duration::from_secs(
3002 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
3003 ),
3004 registry: None,
3005 #[cfg(feature = "rns-hooks")]
3006 provider_bridge: None,
3007 },
3008 Box::new(NoopCallbacks),
3009 )
3010 .unwrap();
3011
3012 let dh = rns_core::types::DestHash([0xAA; 16]);
3013
3014 assert_eq!(node.has_path(&dh).unwrap(), false);
3016 assert_eq!(node.hops_to(&dh).unwrap(), None);
3017
3018 node.shutdown();
3019 }
3020
3021 #[test]
3022 fn recall_identity_none_when_unknown() {
3023 let node = RnsNode::start(
3024 NodeConfig {
3025 panic_on_interface_error: false,
3026 transport_enabled: false,
3027 identity: None,
3028 interfaces: vec![],
3029 share_instance: false,
3030 instance_name: "default".into(),
3031 shared_instance_port: 37428,
3032 rpc_port: 0,
3033 cache_dir: None,
3034 management: Default::default(),
3035 probe_port: None,
3036 probe_addrs: vec![],
3037 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
3038 device: None,
3039 hooks: Vec::new(),
3040 discover_interfaces: false,
3041 discovery_required_value: None,
3042 respond_to_probes: false,
3043 prefer_shorter_path: false,
3044 max_paths_per_destination: 1,
3045 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
3046 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
3047 max_path_destinations: usize::MAX,
3048 max_tunnel_destinations_total: usize::MAX,
3049 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
3050 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
3051 announce_table_ttl: Duration::from_secs(
3052 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
3053 ),
3054 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
3055 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
3056 interface_writer_queue_capacity:
3057 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
3058 announce_sig_cache_enabled: true,
3059 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
3060 announce_sig_cache_ttl: Duration::from_secs(
3061 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
3062 ),
3063 registry: None,
3064 #[cfg(feature = "rns-hooks")]
3065 provider_bridge: None,
3066 },
3067 Box::new(NoopCallbacks),
3068 )
3069 .unwrap();
3070
3071 let dh = rns_core::types::DestHash([0xBB; 16]);
3072 assert!(node.recall_identity(&dh).unwrap().is_none());
3073
3074 node.shutdown();
3075 }
3076
3077 #[test]
3078 fn request_path_does_not_crash() {
3079 let node = RnsNode::start(
3080 NodeConfig {
3081 panic_on_interface_error: false,
3082 transport_enabled: false,
3083 identity: None,
3084 interfaces: vec![],
3085 share_instance: false,
3086 instance_name: "default".into(),
3087 shared_instance_port: 37428,
3088 rpc_port: 0,
3089 cache_dir: None,
3090 management: Default::default(),
3091 probe_port: None,
3092 probe_addrs: vec![],
3093 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
3094 device: None,
3095 hooks: Vec::new(),
3096 discover_interfaces: false,
3097 discovery_required_value: None,
3098 respond_to_probes: false,
3099 prefer_shorter_path: false,
3100 max_paths_per_destination: 1,
3101 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
3102 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
3103 max_path_destinations: usize::MAX,
3104 max_tunnel_destinations_total: usize::MAX,
3105 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
3106 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
3107 announce_table_ttl: Duration::from_secs(
3108 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
3109 ),
3110 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
3111 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
3112 interface_writer_queue_capacity:
3113 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
3114 announce_sig_cache_enabled: true,
3115 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
3116 announce_sig_cache_ttl: Duration::from_secs(
3117 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
3118 ),
3119 registry: None,
3120 #[cfg(feature = "rns-hooks")]
3121 provider_bridge: None,
3122 },
3123 Box::new(NoopCallbacks),
3124 )
3125 .unwrap();
3126
3127 let dh = rns_core::types::DestHash([0xCC; 16]);
3128 assert!(node.request_path(&dh).is_ok());
3129
3130 thread::sleep(Duration::from_millis(50));
3132
3133 node.shutdown();
3134 }
3135
3136 #[test]
3137 fn create_link_returns_error_while_draining() {
3138 let node = RnsNode::start(NodeConfig::default(), Box::new(NoopCallbacks)).unwrap();
3139
3140 node.begin_drain(Duration::from_secs(1)).unwrap();
3141 assert!(node.create_link([0xAB; 16], [0xCD; 32]).is_err());
3142
3143 node.shutdown();
3144 }
3145
3146 #[test]
3147 fn request_path_returns_error_while_draining() {
3148 let node = RnsNode::start(NodeConfig::default(), Box::new(NoopCallbacks)).unwrap();
3149
3150 node.begin_drain(Duration::from_secs(1)).unwrap();
3151 assert!(node
3152 .request_path(&rns_core::types::DestHash([0xAB; 16]))
3153 .is_err());
3154
3155 node.shutdown();
3156 }
3157
3158 #[test]
3163 fn send_packet_returns_error_while_draining() {
3164 let node = RnsNode::start(NodeConfig::default(), Box::new(NoopCallbacks)).unwrap();
3165 let dest = crate::destination::Destination::plain("drain-test", &["send"]);
3166
3167 node.begin_drain(Duration::from_secs(1)).unwrap();
3168 assert!(node.send_packet(&dest, b"hello").is_err());
3169
3170 node.shutdown();
3171 }
3172
3173 #[test]
3174 fn send_packet_plain() {
3175 let node = RnsNode::start(
3176 NodeConfig {
3177 panic_on_interface_error: false,
3178 transport_enabled: false,
3179 identity: None,
3180 interfaces: vec![],
3181 share_instance: false,
3182 instance_name: "default".into(),
3183 shared_instance_port: 37428,
3184 rpc_port: 0,
3185 cache_dir: None,
3186 management: Default::default(),
3187 probe_port: None,
3188 probe_addrs: vec![],
3189 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
3190 device: None,
3191 hooks: Vec::new(),
3192 discover_interfaces: false,
3193 discovery_required_value: None,
3194 respond_to_probes: false,
3195 prefer_shorter_path: false,
3196 max_paths_per_destination: 1,
3197 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
3198 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
3199 max_path_destinations: usize::MAX,
3200 max_tunnel_destinations_total: usize::MAX,
3201 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
3202 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
3203 announce_table_ttl: Duration::from_secs(
3204 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
3205 ),
3206 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
3207 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
3208 interface_writer_queue_capacity:
3209 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
3210 announce_sig_cache_enabled: true,
3211 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
3212 announce_sig_cache_ttl: Duration::from_secs(
3213 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
3214 ),
3215 registry: None,
3216 #[cfg(feature = "rns-hooks")]
3217 provider_bridge: None,
3218 },
3219 Box::new(NoopCallbacks),
3220 )
3221 .unwrap();
3222
3223 let dest = crate::destination::Destination::plain("test", &["echo"]);
3224 let result = node.send_packet(&dest, b"hello world");
3225 assert!(result.is_ok());
3226
3227 let packet_hash = result.unwrap();
3228 assert_ne!(packet_hash.0, [0u8; 32]);
3230
3231 thread::sleep(Duration::from_millis(50));
3233
3234 node.shutdown();
3235 }
3236
3237 #[test]
3238 fn send_packet_single_requires_public_key() {
3239 let node = RnsNode::start(
3240 NodeConfig {
3241 panic_on_interface_error: false,
3242 transport_enabled: false,
3243 identity: None,
3244 interfaces: vec![],
3245 share_instance: false,
3246 instance_name: "default".into(),
3247 shared_instance_port: 37428,
3248 rpc_port: 0,
3249 cache_dir: None,
3250 management: Default::default(),
3251 probe_port: None,
3252 probe_addrs: vec![],
3253 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
3254 device: None,
3255 hooks: Vec::new(),
3256 discover_interfaces: false,
3257 discovery_required_value: None,
3258 respond_to_probes: false,
3259 prefer_shorter_path: false,
3260 max_paths_per_destination: 1,
3261 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
3262 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
3263 max_path_destinations: usize::MAX,
3264 max_tunnel_destinations_total: usize::MAX,
3265 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
3266 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
3267 announce_table_ttl: Duration::from_secs(
3268 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
3269 ),
3270 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
3271 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
3272 interface_writer_queue_capacity:
3273 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
3274 announce_sig_cache_enabled: true,
3275 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
3276 announce_sig_cache_ttl: Duration::from_secs(
3277 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
3278 ),
3279 registry: None,
3280 #[cfg(feature = "rns-hooks")]
3281 provider_bridge: None,
3282 },
3283 Box::new(NoopCallbacks),
3284 )
3285 .unwrap();
3286
3287 let dest = crate::destination::Destination::single_in(
3289 "test",
3290 &["echo"],
3291 rns_core::types::IdentityHash([0x42; 16]),
3292 );
3293 let result = node.send_packet(&dest, b"hello");
3294 assert!(result.is_err(), "single_in has no public_key, should fail");
3295
3296 node.shutdown();
3297 }
3298
3299 #[test]
3300 fn send_packet_single_encrypts() {
3301 let node = RnsNode::start(
3302 NodeConfig {
3303 panic_on_interface_error: false,
3304 transport_enabled: false,
3305 identity: None,
3306 interfaces: vec![],
3307 share_instance: false,
3308 instance_name: "default".into(),
3309 shared_instance_port: 37428,
3310 rpc_port: 0,
3311 cache_dir: None,
3312 management: Default::default(),
3313 probe_port: None,
3314 probe_addrs: vec![],
3315 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
3316 device: None,
3317 hooks: Vec::new(),
3318 discover_interfaces: false,
3319 discovery_required_value: None,
3320 respond_to_probes: false,
3321 prefer_shorter_path: false,
3322 max_paths_per_destination: 1,
3323 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
3324 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
3325 max_path_destinations: usize::MAX,
3326 max_tunnel_destinations_total: usize::MAX,
3327 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
3328 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
3329 announce_table_ttl: Duration::from_secs(
3330 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
3331 ),
3332 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
3333 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
3334 interface_writer_queue_capacity:
3335 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
3336 announce_sig_cache_enabled: true,
3337 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
3338 announce_sig_cache_ttl: Duration::from_secs(
3339 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
3340 ),
3341 registry: None,
3342 #[cfg(feature = "rns-hooks")]
3343 provider_bridge: None,
3344 },
3345 Box::new(NoopCallbacks),
3346 )
3347 .unwrap();
3348
3349 let remote_identity = Identity::new(&mut OsRng);
3351 let recalled = crate::destination::AnnouncedIdentity {
3352 dest_hash: rns_core::types::DestHash([0xAA; 16]),
3353 identity_hash: rns_core::types::IdentityHash(*remote_identity.hash()),
3354 public_key: remote_identity.get_public_key().unwrap(),
3355 app_data: None,
3356 hops: 1,
3357 received_at: 0.0,
3358 receiving_interface: rns_core::transport::types::InterfaceId(0),
3359 };
3360 let dest = crate::destination::Destination::single_out("test", &["echo"], &recalled);
3361
3362 let result = node.send_packet(&dest, b"secret message");
3363 assert!(result.is_ok());
3364
3365 let packet_hash = result.unwrap();
3366 assert_ne!(packet_hash.0, [0u8; 32]);
3367
3368 thread::sleep(Duration::from_millis(50));
3369 node.shutdown();
3370 }
3371
3372 #[test]
3373 fn register_destination_with_proof_prove_all() {
3374 let node = RnsNode::start(
3375 NodeConfig {
3376 panic_on_interface_error: false,
3377 transport_enabled: false,
3378 identity: None,
3379 interfaces: vec![],
3380 share_instance: false,
3381 instance_name: "default".into(),
3382 shared_instance_port: 37428,
3383 rpc_port: 0,
3384 cache_dir: None,
3385 management: Default::default(),
3386 probe_port: None,
3387 probe_addrs: vec![],
3388 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
3389 device: None,
3390 hooks: Vec::new(),
3391 discover_interfaces: false,
3392 discovery_required_value: None,
3393 respond_to_probes: false,
3394 prefer_shorter_path: false,
3395 max_paths_per_destination: 1,
3396 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
3397 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
3398 max_path_destinations: usize::MAX,
3399 max_tunnel_destinations_total: usize::MAX,
3400 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
3401 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
3402 announce_table_ttl: Duration::from_secs(
3403 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
3404 ),
3405 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
3406 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
3407 interface_writer_queue_capacity:
3408 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
3409 announce_sig_cache_enabled: true,
3410 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
3411 announce_sig_cache_ttl: Duration::from_secs(
3412 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
3413 ),
3414 registry: None,
3415 #[cfg(feature = "rns-hooks")]
3416 provider_bridge: None,
3417 },
3418 Box::new(NoopCallbacks),
3419 )
3420 .unwrap();
3421
3422 let identity = Identity::new(&mut OsRng);
3423 let ih = rns_core::types::IdentityHash(*identity.hash());
3424 let dest = crate::destination::Destination::single_in("echo", &["request"], ih)
3425 .set_proof_strategy(rns_core::types::ProofStrategy::ProveAll);
3426 let prv_key = identity.get_private_key().unwrap();
3427
3428 let result = node.register_destination_with_proof(&dest, Some(prv_key));
3429 assert!(result.is_ok());
3430
3431 thread::sleep(Duration::from_millis(50));
3433
3434 node.shutdown();
3435 }
3436
3437 #[test]
3438 fn register_destination_with_proof_prove_none() {
3439 let node = RnsNode::start(
3440 NodeConfig {
3441 panic_on_interface_error: false,
3442 transport_enabled: false,
3443 identity: None,
3444 interfaces: vec![],
3445 share_instance: false,
3446 instance_name: "default".into(),
3447 shared_instance_port: 37428,
3448 rpc_port: 0,
3449 cache_dir: None,
3450 management: Default::default(),
3451 probe_port: None,
3452 probe_addrs: vec![],
3453 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
3454 device: None,
3455 hooks: Vec::new(),
3456 discover_interfaces: false,
3457 discovery_required_value: None,
3458 respond_to_probes: false,
3459 prefer_shorter_path: false,
3460 max_paths_per_destination: 1,
3461 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
3462 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
3463 max_path_destinations: usize::MAX,
3464 max_tunnel_destinations_total: usize::MAX,
3465 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
3466 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
3467 announce_table_ttl: Duration::from_secs(
3468 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
3469 ),
3470 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
3471 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
3472 interface_writer_queue_capacity:
3473 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
3474 announce_sig_cache_enabled: true,
3475 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
3476 announce_sig_cache_ttl: Duration::from_secs(
3477 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
3478 ),
3479 registry: None,
3480 #[cfg(feature = "rns-hooks")]
3481 provider_bridge: None,
3482 },
3483 Box::new(NoopCallbacks),
3484 )
3485 .unwrap();
3486
3487 let dest = crate::destination::Destination::plain("test", &["data"])
3489 .set_proof_strategy(rns_core::types::ProofStrategy::ProveNone);
3490
3491 let result = node.register_destination_with_proof(&dest, None);
3492 assert!(result.is_ok());
3493
3494 thread::sleep(Duration::from_millis(50));
3495 node.shutdown();
3496 }
3497}