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