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