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