1use std::collections::HashSet;
6use std::io::{self, Read};
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
51static NEXT_RESOURCE_TRANSFER_ID: AtomicU64 = AtomicU64::new(1);
52
53#[cfg(test)]
54const DEFAULT_KNOWN_DESTINATIONS_TTL: Duration = Duration::from_secs(48 * 60 * 60);
55const DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES: usize = 8192;
56
57fn parse_interface_mode(mode: &str) -> u8 {
60 match mode.to_lowercase().as_str() {
61 "full" => rns_core::constants::MODE_FULL,
62 "access_point" | "accesspoint" | "ap" => rns_core::constants::MODE_ACCESS_POINT,
63 "pointtopoint" | "ptp" => rns_core::constants::MODE_POINT_TO_POINT,
64 "roaming" => rns_core::constants::MODE_ROAMING,
65 "boundary" => rns_core::constants::MODE_BOUNDARY,
66 "gateway" | "gw" => rns_core::constants::MODE_GATEWAY,
67 "internal" => rns_core::constants::MODE_INTERNAL,
68 _ => rns_core::constants::MODE_FULL,
69 }
70}
71
72fn parse_optional_interface_mode(mode: &str) -> Option<u8> {
73 match mode.to_lowercase().as_str() {
74 "full" => Some(rns_core::constants::MODE_FULL),
75 "access_point" | "accesspoint" | "ap" => Some(rns_core::constants::MODE_ACCESS_POINT),
76 "pointtopoint" | "ptp" => Some(rns_core::constants::MODE_POINT_TO_POINT),
77 "roaming" => Some(rns_core::constants::MODE_ROAMING),
78 "boundary" => Some(rns_core::constants::MODE_BOUNDARY),
79 "gateway" | "gw" => Some(rns_core::constants::MODE_GATEWAY),
80 "internal" => Some(rns_core::constants::MODE_INTERNAL),
81 _ => None,
82 }
83}
84
85fn parse_interface_gravity(value: Option<&str>, default_gravity: i64) -> i64 {
86 value
87 .and_then(|value| value.parse().ok())
88 .unwrap_or(default_gravity)
89}
90
91fn normalize_discovery_mode(
93 interface_type: &str,
94 configured_mode: u8,
95 discoverable: bool,
96 ignore_config_warnings: bool,
97) -> u8 {
98 if !discoverable
99 || ignore_config_warnings
100 || matches!(
101 configured_mode,
102 rns_core::constants::MODE_ACCESS_POINT
103 | rns_core::constants::MODE_GATEWAY
104 | rns_core::constants::MODE_INTERNAL
105 )
106 {
107 return configured_mode;
108 }
109
110 if matches!(interface_type, "RNodeInterface" | "RNodeMultiInterface") {
111 rns_core::constants::MODE_ACCESS_POINT
112 } else {
113 rns_core::constants::MODE_GATEWAY
114 }
115}
116
117fn default_ingress_control_for_type(
118 iface_type: &str,
119) -> rns_core::transport::types::IngressControlConfig {
120 match iface_type {
121 "AutoInterface" | "BackboneInterface" | "TCPClientInterface" | "TCPServerInterface"
122 | "UDPInterface" | "I2PInterface" => {
123 rns_core::transport::types::IngressControlConfig::enabled()
124 }
125 _ => rns_core::transport::types::IngressControlConfig::disabled(),
126 }
127}
128
129fn parse_ingress_control_config(
130 iface_type: &str,
131 params: &std::collections::HashMap<String, String>,
132) -> Result<rns_core::transport::types::IngressControlConfig, String> {
133 let mut config = default_ingress_control_for_type(iface_type);
134
135 if let Some(v) = params.get("ingress_control") {
136 config.enabled = config::parse_bool_pub(v)
137 .ok_or_else(|| format!("ingress_control must be a boolean, got '{}'", v))?;
138 }
139 if let Some(v) = params.get("ic_max_held_announces") {
140 config.max_held_announces = v
141 .parse::<usize>()
142 .map_err(|_| format!("ic_max_held_announces must be an integer, got '{}'", v))?;
143 }
144 if let Some(v) = params.get("ic_burst_hold") {
145 config.burst_hold = parse_nonnegative_f64("ic_burst_hold", v)?;
146 }
147 if let Some(v) = params.get("ic_burst_freq_new") {
148 config.burst_freq_new = parse_nonnegative_f64("ic_burst_freq_new", v)?;
149 }
150 if let Some(v) = params.get("ic_burst_freq") {
151 config.burst_freq = parse_nonnegative_f64("ic_burst_freq", v)?;
152 }
153 if let Some(v) = params.get("ic_pr_burst_freq_new") {
154 config.pr_burst_freq_new = parse_nonnegative_f64("ic_pr_burst_freq_new", v)?;
155 }
156 if let Some(v) = params.get("ic_pr_burst_freq") {
157 config.pr_burst_freq = parse_nonnegative_f64("ic_pr_burst_freq", v)?;
158 }
159 if let Some(v) = params.get("egress_control") {
160 config.egress_enabled = config::parse_bool_pub(v)
161 .ok_or_else(|| format!("egress_control must be a boolean, got '{}'", v))?;
162 }
163 if let Some(v) = params.get("ec_pr_freq") {
164 config.egress_pr_freq = parse_nonnegative_f64("ec_pr_freq", v)?;
165 }
166 if let Some(v) = params.get("ic_new_time") {
167 config.new_time = parse_nonnegative_f64("ic_new_time", v)?;
168 }
169 if let Some(v) = params.get("ic_burst_penalty") {
170 config.burst_penalty = parse_nonnegative_f64("ic_burst_penalty", v)?;
171 }
172 if let Some(v) = params.get("ic_held_release_interval") {
173 config.held_release_interval = parse_nonnegative_f64("ic_held_release_interval", v)?;
174 }
175
176 Ok(config)
177}
178
179fn parse_nonnegative_f64(key: &str, value: &str) -> Result<f64, String> {
180 let parsed = value
181 .parse::<f64>()
182 .map_err(|_| format!("{} must be numeric, got '{}'", key, value))?;
183 if parsed < 0.0 {
184 return Err(format!("{} must be >= 0, got '{}'", key, value));
185 }
186 Ok(parsed)
187}
188
189fn extract_ifac_config(
192 params: &std::collections::HashMap<String, String>,
193 default_size: usize,
194) -> Option<IfacConfig> {
195 let netname = params
196 .get("networkname")
197 .or_else(|| params.get("network_name"))
198 .cloned();
199 let netkey = params
200 .get("passphrase")
201 .or_else(|| params.get("pass_phrase"))
202 .cloned();
203
204 if netname.is_none() && netkey.is_none() {
205 return None;
206 }
207
208 let size = params
210 .get("ifac_size")
211 .and_then(|v| v.parse::<usize>().ok())
212 .map(|bits| (bits / 8).max(1))
213 .unwrap_or(default_size);
214
215 Some(IfacConfig {
216 netname,
217 netkey,
218 size,
219 })
220}
221
222fn extract_discovery_config(
224 iface_name: &str,
225 iface_type: &str,
226 params: &std::collections::HashMap<String, String>,
227) -> Option<crate::discovery::DiscoveryConfig> {
228 let discoverable = params
229 .get("discoverable")
230 .and_then(|v| config::parse_bool_pub(v))
231 .unwrap_or(false);
232 if !discoverable {
233 return None;
234 }
235
236 if iface_type == "TCPClientInterface" {
237 log::error!(
238 "Invalid interface discovery configuration for {}, aborting discovery announce",
239 iface_name
240 );
241 return None;
242 }
243
244 let discovery_name = params
245 .get("discovery_name")
246 .cloned()
247 .unwrap_or_else(|| iface_name.to_string());
248
249 let announce_interval = params
251 .get("announce_interval")
252 .and_then(|v| v.parse::<u64>().ok())
253 .map(|secs| secs.max(300))
254 .unwrap_or(21600);
255
256 let stamp_value = params
257 .get("discovery_stamp_value")
258 .and_then(|v| v.parse::<u8>().ok())
259 .unwrap_or(crate::discovery::DEFAULT_STAMP_VALUE);
260
261 let reachable_on = params.get("reachable_on").cloned();
262
263 let listen_port = params
264 .get("listen_port")
265 .or_else(|| params.get("port"))
266 .and_then(|v| v.parse().ok());
267
268 let location_cmd = params.get("location_cmd").cloned();
269
270 let latitude = params
271 .get("latitude")
272 .or_else(|| params.get("lat"))
273 .and_then(|v| v.parse().ok());
274 let longitude = params
275 .get("longitude")
276 .or_else(|| params.get("lon"))
277 .and_then(|v| v.parse().ok());
278 let height = params.get("height").and_then(|v| v.parse().ok());
279 let operator_lxmf_address = params.get("discovery_lxmf_address").and_then(|value| {
280 let parsed = parse_discovery_lxmf_address(value);
281 if parsed.is_none() {
282 log::error!(
283 "Invalid interface discovery LXMF address for '{}': {}",
284 iface_name,
285 value
286 );
287 }
288 parsed
289 });
290
291 Some(crate::discovery::DiscoveryConfig {
292 discovery_name,
293 announce_interval,
294 stamp_value,
295 reachable_on,
296 interface_type: iface_type.to_string(),
297 listen_port,
298 location_cmd,
299 latitude,
300 longitude,
301 height,
302 operator_lxmf_address,
303 })
304}
305
306fn default_discovery_runtime_config(
307 interface_name: &str,
308 interface_type: &str,
309 listen_port: Option<u16>,
310) -> crate::discovery::DiscoveryConfig {
311 crate::discovery::DiscoveryConfig {
312 discovery_name: interface_name.to_string(),
313 announce_interval: 21600,
314 stamp_value: crate::discovery::DEFAULT_STAMP_VALUE,
315 reachable_on: None,
316 interface_type: interface_type.to_string(),
317 listen_port,
318 location_cmd: None,
319 latitude: None,
320 longitude: None,
321 height: None,
322 operator_lxmf_address: None,
323 }
324}
325
326fn parse_discovery_lxmf_address(value: &str) -> Option<[u8; 16]> {
327 if value.len() != 32 || !value.bytes().all(|byte| byte.is_ascii_hexdigit()) {
328 return None;
329 }
330 let mut address = [0u8; 16];
331 for (index, byte) in address.iter_mut().enumerate() {
332 *byte = u8::from_str_radix(&value[index * 2..index * 2 + 2], 16).ok()?;
333 }
334 Some(address)
335}
336
337fn discovery_runtime_ifac_fields(ifac: Option<&IfacConfig>) -> (Option<String>, Option<String>) {
338 (
339 ifac.and_then(|cfg| cfg.netname.clone()),
340 ifac.and_then(|cfg| cfg.netkey.clone()),
341 )
342}
343
344#[cfg(feature = "iface-backbone")]
345fn backbone_discovery_runtime_from_interface(
346 interface_name: &str,
347 mode: &BackboneMode,
348 discovery: Option<&crate::discovery::DiscoveryConfig>,
349 transport_enabled: bool,
350 ifac: Option<&IfacConfig>,
351) -> Option<crate::driver::BackboneDiscoveryRuntimeHandle> {
352 let config = match mode {
353 BackboneMode::Server(config) => config,
354 BackboneMode::Client(_, _) => return None,
355 };
356
357 let startup_config = discovery.cloned().unwrap_or_else(|| {
358 default_discovery_runtime_config(
359 interface_name,
360 "BackboneInterface",
361 Some(config.listen_port),
362 )
363 });
364 let (ifac_netname, ifac_netkey) = discovery_runtime_ifac_fields(ifac);
365
366 Some(crate::driver::BackboneDiscoveryRuntimeHandle::from_parts(
367 config.name.clone(),
368 startup_config,
369 transport_enabled,
370 ifac_netname,
371 ifac_netkey,
372 discovery.is_some(),
373 ))
374}
375
376#[cfg(feature = "iface-tcp")]
377fn tcp_server_discovery_runtime_from_interface(
378 interface_name: &str,
379 config: &crate::interface::tcp_server::TcpServerConfig,
380 discovery: Option<&crate::discovery::DiscoveryConfig>,
381 transport_enabled: bool,
382 ifac: Option<&IfacConfig>,
383) -> crate::driver::TcpServerDiscoveryRuntimeHandle {
384 let startup_config = discovery.cloned().unwrap_or_else(|| {
385 default_discovery_runtime_config(
386 interface_name,
387 "TCPServerInterface",
388 Some(config.listen_port),
389 )
390 });
391 let (ifac_netname, ifac_netkey) = discovery_runtime_ifac_fields(ifac);
392
393 crate::driver::TcpServerDiscoveryRuntimeHandle::from_parts(
394 config.name.clone(),
395 startup_config,
396 transport_enabled,
397 ifac_netname,
398 ifac_netkey,
399 discovery.is_some(),
400 )
401}
402
403fn ifac_runtime_from_config(
404 ifac: Option<&IfacConfig>,
405 default_size: usize,
406) -> crate::driver::IfacRuntimeConfig {
407 crate::driver::IfacRuntimeConfig::from_parts(
408 ifac.and_then(|cfg| cfg.netname.clone()),
409 ifac.and_then(|cfg| cfg.netkey.clone()),
410 ifac.map(|cfg| cfg.size).unwrap_or(default_size),
411 )
412}
413
414fn discoverable_interface_from_config(
415 interface_name: &str,
416 discovery: &crate::discovery::DiscoveryConfig,
417 transport_enabled: bool,
418 ifac: Option<&IfacConfig>,
419) -> crate::discovery::DiscoverableInterface {
420 crate::discovery::DiscoverableInterface {
421 interface_name: interface_name.to_string(),
422 config: discovery.clone(),
423 transport_enabled,
424 ifac_netname: ifac.and_then(|cfg| cfg.netname.clone()),
425 ifac_netkey: ifac.and_then(|cfg| cfg.netkey.clone()),
426 }
427}
428
429fn derive_ifac_state(
430 ifac: Option<&IfacConfig>,
431 interface_name: &str,
432) -> io::Result<Option<crate::ifac::IfacState>> {
433 let Some(ifac) = ifac else {
434 return Ok(None);
435 };
436 if ifac.netname.is_none() && ifac.netkey.is_none() {
437 return Ok(None);
438 }
439
440 ifac::derive_ifac(ifac.netname.as_deref(), ifac.netkey.as_deref(), ifac.size)
441 .map(Some)
442 .map_err(|err| {
443 io::Error::other(format!(
444 "failed to derive IFAC for {}: {}",
445 interface_name, err
446 ))
447 })
448}
449
450struct StartedInterface<'a> {
451 driver: &'a mut Driver,
452 tx: &'a EventSender,
453 queue_capacity: usize,
454 id: rns_core::transport::types::InterfaceId,
455 info: rns_core::transport::types::InterfaceInfo,
456 writer: Box<dyn crate::interface::Writer>,
457 interface_type_name: String,
458 ifac_state: Option<crate::ifac::IfacState>,
459 ifac_runtime: &'a crate::driver::IfacRuntimeConfig,
460}
461
462fn register_started_interface(params: StartedInterface<'_>) {
463 let StartedInterface {
464 driver,
465 tx,
466 queue_capacity,
467 id,
468 mut info,
469 writer,
470 interface_type_name,
471 ifac_state,
472 ifac_runtime,
473 } = params;
474 driver.apply_announce_rate_defaults(&mut info);
475 driver.apply_ingress_control_defaults(&mut info);
476 let (writer, async_writer_metrics) =
477 crate::interface::wrap_async_writer(writer, id, &info.name, tx.clone(), queue_capacity);
478 driver.register_interface_runtime_defaults(&info);
479 driver.register_interface_ifac_runtime(&info.name, ifac_runtime.clone());
480 driver.engine.register_interface(info.clone());
481 driver.interfaces.insert(
482 id,
483 InterfaceEntry {
484 id,
485 info,
486 writer,
487 async_writer_metrics: Some(async_writer_metrics),
488 enabled: true,
489 online: false,
490 dynamic: false,
491 ifac: ifac_state,
492 stats: InterfaceStats {
493 started: time::now(),
494 ..Default::default()
495 },
496 interface_type: interface_type_name,
497 send_retry_at: None,
498 send_retry_backoff: Duration::ZERO,
499 },
500 );
501}
502
503pub struct NodeConfig {
505 pub transport_enabled: bool,
506 pub static_transport_identity: bool,
507 pub local_hops_delta: bool,
508 pub identity: Option<Identity>,
509 pub interfaces: Vec<InterfaceConfig>,
511 pub share_instance: bool,
513 pub instance_name: String,
515 pub shared_instance_port: u16,
517 pub rpc_port: u16,
519 pub cache_dir: Option<std::path::PathBuf>,
521 pub ratchet_store: Option<Arc<dyn storage::RatchetStore>>,
523 pub ratchet_expiry: Duration,
525 pub management: crate::management::ManagementConfig,
527 pub probe_port: Option<u16>,
529 pub probe_addrs: Vec<std::net::SocketAddr>,
531 pub probe_protocol: rns_core::holepunch::ProbeProtocol,
533 pub direct_connect_policy: crate::event::HolePunchPolicy,
535 pub device: Option<String>,
537 pub underlay_mark: Option<u32>,
539 pub hooks: Vec<config::ParsedHook>,
541 pub discover_interfaces: bool,
543 pub autoconnect_interface_mode: Option<u8>,
545 pub autoconnect_interface_gravity: i64,
547 pub autoconnect_announces_to_internal: bool,
549 pub discovery_required_value: Option<u8>,
551 pub respond_to_probes: bool,
553 pub prefer_shorter_path: bool,
557 pub max_paths_per_destination: usize,
560 pub packet_hashlist_max_entries: usize,
562 pub packet_hashlist_allocation: rns_core::transport::types::PacketHashlistAllocation,
564 pub max_discovery_pr_tags: usize,
566 pub max_path_destinations: usize,
568 pub max_tunnel_destinations_total: usize,
570 pub known_destinations_ttl: Duration,
572 pub known_destinations_max_entries: usize,
574 pub announce_table_ttl: Duration,
576 pub announce_table_max_bytes: usize,
578 pub driver_event_queue_capacity: usize,
580 pub interface_writer_queue_capacity: usize,
582 pub announce_rate_defaults: AnnounceRateDefaults,
584 pub ingress_control_defaults: rns_core::transport::types::IngressControlConfig,
586 #[cfg(feature = "iface-backbone")]
588 pub backbone_peer_pool: Option<BackbonePeerPoolSettings>,
589 pub announce_sig_cache_enabled: bool,
591 pub announce_sig_cache_max_entries: usize,
593 pub announce_sig_cache_ttl: Duration,
595 pub registry: Option<crate::interface::registry::InterfaceRegistry>,
597 pub panic_on_interface_error: bool,
600 #[cfg(feature = "hooks")]
602 pub provider_bridge: Option<crate::provider_bridge::ProviderBridgeConfig>,
603}
604
605struct NodeQueueConfig {
606 announce_max_entries: usize,
607 announce_max_interfaces: usize,
608 announce_max_bytes: usize,
609 announce_ttl_secs: f64,
610 announce_overflow_policy: AnnounceQueueOverflowPolicy,
611 inbound_capacities: crate::event::InboundQueueCapacities,
612 link_mtu_discovery: bool,
613}
614
615impl Default for NodeQueueConfig {
616 fn default() -> Self {
617 Self {
618 announce_max_entries: 256,
619 announce_max_interfaces: 1024,
620 announce_max_bytes: 256 * 1024,
621 announce_ttl_secs: 30.0,
622 announce_overflow_policy: AnnounceQueueOverflowPolicy::DropWorst,
623 inbound_capacities: crate::event::InboundQueueCapacities::default(),
624 link_mtu_discovery: true,
625 }
626 }
627}
628
629impl Default for NodeConfig {
630 fn default() -> Self {
631 Self {
632 transport_enabled: false,
633 static_transport_identity: false,
634 local_hops_delta: false,
635 identity: None,
636 interfaces: Vec::new(),
637 share_instance: false,
638 instance_name: "default".into(),
639 shared_instance_port: 37428,
640 rpc_port: 0,
641 cache_dir: None,
642 ratchet_store: None,
643 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
644 management: Default::default(),
645 probe_port: None,
646 probe_addrs: vec![],
647 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
648 direct_connect_policy: crate::event::HolePunchPolicy::AcceptAll,
649 device: None,
650 underlay_mark: None,
651 hooks: Vec::new(),
652 discover_interfaces: false,
653 autoconnect_interface_mode: None,
654 autoconnect_interface_gravity: 0,
655 autoconnect_announces_to_internal: false,
656 discovery_required_value: None,
657 respond_to_probes: false,
658 prefer_shorter_path: false,
659 max_paths_per_destination: 1,
660 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
661 packet_hashlist_allocation: rns_core::transport::types::PacketHashlistAllocation::Eager,
662 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
663 max_path_destinations: rns_core::transport::types::DEFAULT_MAX_PATH_DESTINATIONS,
664 max_tunnel_destinations_total: usize::MAX,
665 known_destinations_ttl: Duration::from_secs(48 * 60 * 60),
666 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
667 announce_table_ttl: Duration::from_secs(rns_core::constants::ANNOUNCE_TABLE_TTL as u64),
668 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
669 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
670 interface_writer_queue_capacity: crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
671 announce_rate_defaults: AnnounceRateDefaults::default(),
672 ingress_control_defaults: rns_core::transport::types::IngressControlConfig::enabled(),
673 #[cfg(feature = "iface-backbone")]
674 backbone_peer_pool: None,
675 announce_sig_cache_enabled: true,
676 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
677 announce_sig_cache_ttl: Duration::from_secs(
678 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
679 ),
680 registry: None,
681 panic_on_interface_error: false,
682 #[cfg(feature = "hooks")]
683 provider_bridge: None,
684 }
685 }
686}
687
688pub struct IfacConfig {
690 pub netname: Option<String>,
691 pub netkey: Option<String>,
692 pub size: usize,
693}
694
695pub struct InterfaceConfig {
697 pub name: String,
698 pub type_name: String,
699 pub config_data: Box<dyn crate::interface::InterfaceConfigData>,
700 pub mode: u8,
701 pub gravity: i64,
702 pub recursive_prs: bool,
703 pub announces_from_internal: bool,
704 pub announces_to_internal: Option<bool>,
705 pub ingress_control: rns_core::transport::types::IngressControlConfig,
706 pub ifac: Option<IfacConfig>,
707 pub discovery: Option<crate::discovery::DiscoveryConfig>,
708}
709
710use crate::event::{QueryRequest, QueryResponse};
711
712#[derive(Debug)]
714pub struct SendError;
715
716impl std::fmt::Display for SendError {
717 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
718 write!(f, "driver shut down")
719 }
720}
721
722impl std::error::Error for SendError {}
723
724pub struct RnsNode {
726 tx: EventSender,
727 driver_handle: Option<JoinHandle<()>>,
728 verify_handle: Option<JoinHandle<()>>,
729 verify_shutdown: Arc<AtomicBool>,
730 persist_handle: Option<JoinHandle<()>>,
731 persist_stop: Option<std::sync::mpsc::Sender<()>>,
732 rpc_server: Option<crate::rpc::RpcServer>,
733 tick_interval_ms: Arc<AtomicU64>,
734 #[allow(dead_code)]
735 probe_server: Option<crate::holepunch::probe::ProbeServerHandle>,
736 known_destinations_path: Option<std::path::PathBuf>,
737 transport_state_dir: Option<std::path::PathBuf>,
738 ratchet_store: Option<Arc<dyn storage::RatchetStore>>,
739 ratchet_expiry: Duration,
740}
741
742impl RnsNode {
743 pub fn from_config(
746 config_path: Option<&Path>,
747 callbacks: Box<dyn Callbacks>,
748 ) -> io::Result<Self> {
749 let config_dir = storage::resolve_config_dir(config_path);
750 Self::from_config_dirs(&config_dir, &config_dir, None, false, callbacks)
751 }
752
753 pub fn from_private_config(
761 config_dir: &Path,
762 state_dir: &Path,
763 underlay_mark: Option<u32>,
764 callbacks: Box<dyn Callbacks>,
765 ) -> io::Result<Self> {
766 Self::from_config_dirs(config_dir, state_dir, underlay_mark, true, callbacks)
767 }
768
769 fn from_config_dirs(
770 config_dir: &Path,
771 state_dir: &Path,
772 underlay_mark: Option<u32>,
773 private: bool,
774 callbacks: Box<dyn Callbacks>,
775 ) -> io::Result<Self> {
776 let paths = storage::ensure_storage_dirs(state_dir)?;
777 let known_destinations_path = paths.storage.join("known_destinations");
778 let ratchet_store: Arc<dyn storage::RatchetStore> =
779 Arc::new(storage::FsRatchetStore::new(paths.ratchets.clone()));
780
781 let config_file = config_dir.join("config");
783 let config_content = if config_file.exists() {
784 std::fs::read_to_string(&config_file)?
785 } else {
786 String::new()
787 };
788 let rns_config = config::parse(&config_content)
789 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, format!("{}", e)))?;
790 let link_mtu_discovery = config::parse_link_mtu_discovery(&config_content)
791 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, format!("{}", e)))?;
792
793 let identity = if let Some(ref id_path_str) = rns_config.reticulum.network_identity {
795 let id_path = std::path::PathBuf::from(id_path_str);
796 if id_path.exists() {
797 storage::load_identity(&id_path)?
798 } else {
799 let id = Identity::new(&mut OsRng);
800 storage::save_identity(&id, &id_path)?;
801 id
802 }
803 } else {
804 storage::load_or_create_identity(&paths.identities)?
805 };
806
807 let registry = crate::interface::registry::InterfaceRegistry::with_builtins();
809 let mut interface_configs = Vec::new();
810 let mut next_id_val = 1u64;
811
812 for iface in &rns_config.interfaces {
813 if !iface.enabled {
814 continue;
815 }
816
817 let iface_id = rns_core::transport::types::InterfaceId(next_id_val);
818 next_id_val += 1;
819
820 let factory = match registry.get(&iface.interface_type) {
821 Some(f) => f,
822 None => {
823 if private {
824 return Err(io::Error::new(
825 io::ErrorKind::InvalidData,
826 format!(
827 "unsupported enabled interface type '{}' for '{}'",
828 iface.interface_type, iface.name
829 ),
830 ));
831 }
832 log::warn!(
833 "Unsupported interface type '{}' for '{}'",
834 iface.interface_type,
835 iface.name,
836 );
837 continue;
838 }
839 };
840
841 let mut iface_mode = parse_interface_mode(&iface.mode);
842
843 let discoverable = iface
844 .params
845 .get("discoverable")
846 .and_then(|value| config::parse_bool_pub(value))
847 .unwrap_or(false);
848 let ignore_config_warnings = iface
849 .params
850 .get("ignore_config_warnings")
851 .and_then(|value| config::parse_bool_pub(value))
852 .unwrap_or(false);
853 let normalized_mode = normalize_discovery_mode(
854 &iface.interface_type,
855 iface_mode,
856 discoverable,
857 ignore_config_warnings,
858 );
859 if normalized_mode != iface_mode {
860 log::info!(
861 "Interface '{}' has discovery enabled, auto-configuring mode to {}",
862 iface.name,
863 if normalized_mode == rns_core::constants::MODE_ACCESS_POINT {
864 "ACCESS_POINT"
865 } else {
866 "GATEWAY"
867 }
868 );
869 iface_mode = normalized_mode;
870 }
871
872 let default_ifac_size = factory.default_ifac_size();
873 let ifac_config = extract_ifac_config(&iface.params, default_ifac_size);
874 let discovery_config =
875 extract_discovery_config(&iface.name, &iface.interface_type, &iface.params);
876 let ingress_control =
877 match parse_ingress_control_config(&iface.interface_type, &iface.params) {
878 Ok(config) => config,
879 Err(e) => {
880 if private {
881 return Err(io::Error::new(
882 io::ErrorKind::InvalidData,
883 format!("invalid ingress control for '{}': {e}", iface.name),
884 ));
885 }
886 log::warn!(
887 "Failed to parse ingress control config for '{}': {}",
888 iface.name,
889 e
890 );
891 continue;
892 }
893 };
894 let recursive_prs = iface
895 .params
896 .get("recursive_prs")
897 .and_then(|v| config::parse_bool_pub(v))
898 .unwrap_or(false);
899 let announces_from_internal = iface
900 .params
901 .get("announces_from_internal")
902 .and_then(|v| config::parse_bool_pub(v))
903 .unwrap_or(true);
904 let gravity = parse_interface_gravity(
905 iface.params.get("gravity").map(String::as_str),
906 rns_config.reticulum.default_gravity,
907 );
908 let announces_to_internal = iface
909 .params
910 .get("announces_to_internal")
911 .and_then(|v| config::parse_bool_pub(v));
912
913 let mut params = iface.params.clone();
915 if !params.contains_key("storage_dir") {
916 params.insert(
917 "storage_dir".to_string(),
918 paths.storage.to_string_lossy().to_string(),
919 );
920 }
921 if let Some(ref device) = rns_config.reticulum.device {
923 if !params.contains_key("device") {
924 params.insert("device".to_string(), device.clone());
925 }
926 }
927
928 let config_data = match factory.parse_config_section(
929 &iface.name,
930 iface_id,
931 crate::interface::ConfigSection {
932 params: ¶ms,
933 children: &iface.subinterfaces,
934 },
935 ) {
936 Ok(data) => data,
937 Err(e) => {
938 if private {
939 return Err(io::Error::new(
940 io::ErrorKind::InvalidData,
941 format!("invalid interface '{}': {e}", iface.name),
942 ));
943 }
944 log::warn!("Failed to parse config for '{}': {}", iface.name, e);
945 continue;
946 }
947 };
948
949 interface_configs.push(InterfaceConfig {
950 name: iface.name.clone(),
951 type_name: iface.interface_type.clone(),
952 config_data,
953 mode: iface_mode,
954 gravity,
955 recursive_prs,
956 announces_from_internal,
957 announces_to_internal,
958 ingress_control,
959 ifac: ifac_config,
960 discovery: discovery_config,
961 });
962 }
963
964 let mut mgmt_allowed = Vec::new();
966 for hex_hash in &rns_config.reticulum.remote_management_allowed {
967 if hex_hash.len() == 32 {
968 if let Ok(bytes) = (0..hex_hash.len())
969 .step_by(2)
970 .map(|i| u8::from_str_radix(&hex_hash[i..i + 2], 16))
971 .collect::<Result<Vec<u8>, _>>()
972 {
973 if bytes.len() == 16 {
974 let mut h = [0u8; 16];
975 h.copy_from_slice(&bytes);
976 mgmt_allowed.push(h);
977 }
978 } else {
979 log::warn!("Invalid hex in remote_management_allowed: {}", hex_hash);
980 }
981 } else {
982 log::warn!(
983 "Invalid entry in remote_management_allowed (expected 32 hex chars, got {}): {}",
984 hex_hash.len(), hex_hash,
985 );
986 }
987 }
988
989 let probe_addrs: Vec<std::net::SocketAddr> = rns_config
991 .reticulum
992 .probe_addr
993 .as_ref()
994 .map(|s| {
995 s.split(',')
996 .filter_map(|entry| {
997 let trimmed = entry.trim();
998 if trimmed.is_empty() {
999 return None;
1000 }
1001 trimmed
1002 .parse::<std::net::SocketAddr>()
1003 .map_err(|e| {
1004 log::warn!("Invalid probe_addr entry '{}': {}", trimmed, e);
1005 e
1006 })
1007 .ok()
1008 })
1009 .collect()
1010 })
1011 .unwrap_or_default();
1012
1013 let probe_protocol = match rns_config
1015 .reticulum
1016 .probe_protocol
1017 .as_deref()
1018 .map(|s| s.to_lowercase())
1019 {
1020 Some(ref s) if s == "stun" => rns_core::holepunch::ProbeProtocol::Stun,
1021 _ => rns_core::holepunch::ProbeProtocol::Rnsp,
1022 };
1023
1024 let known_destinations = match storage::load_known_destinations(&known_destinations_path) {
1025 Ok(destinations) => destinations,
1026 Err(err) if err.kind() == io::ErrorKind::NotFound => Default::default(),
1027 Err(err) => {
1028 log::warn!("failed to load known destinations: {}", err);
1029 Default::default()
1030 }
1031 };
1032 let known_destination_hashes: HashSet<[u8; 16]> =
1033 known_destinations.keys().copied().collect();
1034 match ratchet_store.cleanup(
1035 &known_destination_hashes,
1036 time::now(),
1037 rns_config.reticulum.ratchet_expiry as f64,
1038 ) {
1039 Ok(stats) if stats.processed > 0 => log::debug!(
1040 "Processed {} ratchets, not in use {}, removed {}",
1041 stats.processed,
1042 stats.not_known,
1043 stats.removed
1044 ),
1045 Ok(_) => {}
1046 Err(err) => log::warn!("failed to clean ratchets: {}", err),
1047 }
1048
1049 let node_config = NodeConfig {
1050 transport_enabled: rns_config.reticulum.enable_transport,
1051 static_transport_identity: rns_config.reticulum.static_transport_identity,
1052 local_hops_delta: rns_config.reticulum.local_hops_delta,
1053 identity: Some(identity),
1054 share_instance: !private && rns_config.reticulum.share_instance,
1055 instance_name: rns_config.reticulum.instance_name.clone(),
1056 shared_instance_port: rns_config.reticulum.shared_instance_port,
1057 rpc_port: rns_config.reticulum.instance_control_port,
1058 cache_dir: Some(paths.cache),
1059 ratchet_store: Some(Arc::clone(&ratchet_store)),
1060 ratchet_expiry: Duration::from_secs(rns_config.reticulum.ratchet_expiry),
1061 management: if private {
1062 crate::management::ManagementConfig::default()
1063 } else {
1064 crate::management::ManagementConfig {
1065 enable_remote_management: rns_config.reticulum.enable_remote_management,
1066 remote_management_allowed: mgmt_allowed,
1067 publish_blackhole: rns_config.reticulum.publish_blackhole,
1068 }
1069 },
1070 probe_port: rns_config.reticulum.probe_port,
1071 probe_addrs,
1072 probe_protocol,
1073 direct_connect_policy: rns_config.reticulum.direct_connect_policy,
1074 device: rns_config.reticulum.device.clone(),
1075 underlay_mark: underlay_mark.or(rns_config.reticulum.underlay_mark),
1076 hooks: if private {
1077 Vec::new()
1078 } else {
1079 rns_config.hooks.clone()
1080 },
1081 discover_interfaces: rns_config.reticulum.discover_interfaces,
1082 autoconnect_interface_mode: rns_config
1083 .reticulum
1084 .autoconnect_interface_mode
1085 .as_deref()
1086 .and_then(parse_optional_interface_mode),
1087 autoconnect_interface_gravity: rns_config.reticulum.autoconnect_interface_gravity,
1088 autoconnect_announces_to_internal: rns_config
1089 .reticulum
1090 .autoconnect_announces_to_internal,
1091 discovery_required_value: rns_config.reticulum.required_discovery_value,
1092 respond_to_probes: rns_config.reticulum.respond_to_probes,
1093 prefer_shorter_path: rns_config.reticulum.prefer_shorter_path,
1094 max_paths_per_destination: rns_config.reticulum.max_paths_per_destination,
1095 packet_hashlist_max_entries: rns_config.reticulum.packet_hashlist_max_entries,
1096 packet_hashlist_allocation: rns_config.reticulum.packet_hashlist_allocation,
1097 max_discovery_pr_tags: rns_config.reticulum.max_discovery_pr_tags,
1098 max_path_destinations: rns_config.reticulum.max_path_destinations,
1099 max_tunnel_destinations_total: rns_config.reticulum.max_tunnel_destinations_total,
1100 known_destinations_ttl: Duration::from_secs(
1101 rns_config.reticulum.known_destinations_ttl,
1102 ),
1103 known_destinations_max_entries: rns_config.reticulum.known_destinations_max_entries,
1104 announce_table_ttl: Duration::from_secs(rns_config.reticulum.announce_table_ttl),
1105 announce_table_max_bytes: rns_config.reticulum.announce_table_max_bytes,
1106 driver_event_queue_capacity: rns_config.reticulum.driver_event_queue_capacity,
1107 interface_writer_queue_capacity: rns_config.reticulum.interface_writer_queue_capacity,
1108 announce_rate_defaults: AnnounceRateDefaults {
1109 target: rns_config.reticulum.default_ar_target,
1110 penalty: rns_config.reticulum.default_ar_penalty,
1111 grace: rns_config.reticulum.default_ar_grace,
1112 },
1113 ingress_control_defaults: rns_core::transport::types::IngressControlConfig {
1114 enabled: true,
1115 egress_enabled: rns_config.reticulum.default_egress_control,
1116 max_held_announces: rns_config.reticulum.default_ic_max_held_announces,
1117 burst_freq_new: rns_config.reticulum.default_ic_burst_freq_new,
1118 burst_freq: rns_config.reticulum.default_ic_burst_freq,
1119 pr_burst_freq_new: rns_config.reticulum.default_ic_pr_burst_freq_new,
1120 pr_burst_freq: rns_config.reticulum.default_ic_pr_burst_freq,
1121 egress_pr_freq: rns_config.reticulum.default_ec_pr_freq,
1122 new_time: rns_config.reticulum.default_ic_new_time,
1123 burst_hold: rns_config.reticulum.default_ic_burst_hold,
1124 burst_penalty: rns_config.reticulum.default_ic_burst_penalty,
1125 held_release_interval: rns_config.reticulum.default_ic_held_release_interval,
1126 },
1127 #[cfg(feature = "iface-backbone")]
1128 backbone_peer_pool: if rns_config.reticulum.backbone_peer_pool_max_connected > 0 {
1129 Some(BackbonePeerPoolSettings {
1130 max_connected: rns_config.reticulum.backbone_peer_pool_max_connected,
1131 failure_threshold: rns_config.reticulum.backbone_peer_pool_failure_threshold,
1132 failure_window: Duration::from_secs(
1133 rns_config.reticulum.backbone_peer_pool_failure_window,
1134 ),
1135 cooldown: Duration::from_secs(rns_config.reticulum.backbone_peer_pool_cooldown),
1136 })
1137 } else {
1138 None
1139 },
1140 announce_sig_cache_enabled: rns_config.reticulum.announce_sig_cache_enabled,
1141 announce_sig_cache_max_entries: rns_config.reticulum.announce_sig_cache_max_entries,
1142 announce_sig_cache_ttl: Duration::from_secs(
1143 rns_config.reticulum.announce_sig_cache_ttl,
1144 ),
1145 interfaces: interface_configs,
1146 registry: None,
1147 panic_on_interface_error: private || rns_config.reticulum.panic_on_interface_error,
1148 #[cfg(feature = "hooks")]
1149 provider_bridge: if !private && rns_config.reticulum.provider_bridge {
1150 Some(crate::provider_bridge::ProviderBridgeConfig {
1151 enabled: true,
1152 socket_path: rns_config
1153 .reticulum
1154 .provider_socket_path
1155 .as_ref()
1156 .map(std::path::PathBuf::from)
1157 .unwrap_or_else(|| config_dir.join("provider.sock")),
1158 queue_max_events: rns_config.reticulum.provider_queue_max_events,
1159 queue_max_bytes: rns_config.reticulum.provider_queue_max_bytes,
1160 overflow_policy: match rns_config.reticulum.provider_overflow_policy.as_str() {
1161 "drop_oldest" => crate::provider_bridge::OverflowPolicy::DropOldest,
1162 _ => crate::provider_bridge::OverflowPolicy::DropNewest,
1163 },
1164 node_instance: rns_config.reticulum.instance_name.clone(),
1165 })
1166 } else {
1167 None
1168 },
1169 };
1170
1171 let mut node = Self::start_with_queue_config(
1172 node_config,
1173 callbacks,
1174 NodeQueueConfig {
1175 announce_max_entries: rns_config.reticulum.announce_queue_max_entries,
1176 announce_max_interfaces: rns_config.reticulum.announce_queue_max_interfaces,
1177 announce_max_bytes: rns_config.reticulum.announce_queue_max_bytes,
1178 announce_ttl_secs: rns_config.reticulum.announce_queue_ttl as f64,
1179 announce_overflow_policy: match rns_config
1180 .reticulum
1181 .announce_queue_overflow_policy
1182 .as_str()
1183 {
1184 "drop_newest" => AnnounceQueueOverflowPolicy::DropNewest,
1185 "drop_oldest" => AnnounceQueueOverflowPolicy::DropOldest,
1186 _ => AnnounceQueueOverflowPolicy::DropWorst,
1187 },
1188 inbound_capacities: rns_config.reticulum.inbound_queue_capacities,
1189 link_mtu_discovery,
1190 },
1191 )?;
1192
1193 node.known_destinations_path = Some(known_destinations_path.clone());
1194 for (dest_hash, known) in known_destinations {
1195 let _ = node.query(QueryRequest::RestoreKnownDestination(
1196 crate::event::KnownDestinationEntry {
1197 dest_hash,
1198 identity_hash: known.identity_hash,
1199 public_key: known.public_key,
1200 app_data: known.app_data,
1201 hops: known.hops,
1202 received_at: known.received_at,
1203 receiving_interface: rns_core::transport::types::InterfaceId(
1204 known.receiving_interface,
1205 ),
1206 was_used: known.was_used,
1207 last_used_at: known.last_used_at,
1208 retained: known.retained,
1209 },
1210 ));
1211 }
1212
1213 Ok(node)
1214 }
1215
1216 pub fn start(config: NodeConfig, callbacks: Box<dyn Callbacks>) -> io::Result<Self> {
1218 crate::interface::validate_underlay_mark(config.underlay_mark)?;
1219 let inbound_queue_capacities = crate::event::InboundQueueCapacities::from_shared_capacity(
1220 config.driver_event_queue_capacity,
1221 );
1222 Self::start_with_queue_config(
1223 config,
1224 callbacks,
1225 NodeQueueConfig {
1226 inbound_capacities: inbound_queue_capacities,
1227 ..NodeQueueConfig::default()
1228 },
1229 )
1230 }
1231
1232 fn start_with_queue_config(
1233 config: NodeConfig,
1234 callbacks: Box<dyn Callbacks>,
1235 queue_config: NodeQueueConfig,
1236 ) -> io::Result<Self> {
1237 let transport_state_dir = if config.transport_enabled {
1238 config.cache_dir.as_ref().map(|cache_dir| {
1239 cache_dir
1240 .parent()
1241 .unwrap_or_else(|| std::path::Path::new("."))
1242 .join("storage")
1243 })
1244 } else {
1245 None
1246 };
1247 let identity = config.identity.unwrap_or_else(|| Identity::new(&mut OsRng));
1248 let transport_identity = if config.transport_enabled || config.static_transport_identity {
1249 Identity::from_private_key(
1250 &identity
1251 .get_private_key()
1252 .expect("node identity must expose a private key"),
1253 )
1254 } else {
1255 Identity::new(&mut OsRng)
1256 };
1257
1258 let local_hops_delta = if config.local_hops_delta {
1259 let mut byte = [0u8; 1];
1260 OsRng.fill_bytes(&mut byte);
1261 byte[0] % 6 + 2
1262 } else {
1263 0
1264 };
1265
1266 let transport_config = TransportConfig {
1267 transport_enabled: config.transport_enabled,
1268 identity_hash: Some(*transport_identity.hash()),
1269 local_hops_delta,
1270 prefer_shorter_path: config.prefer_shorter_path,
1271 max_paths_per_destination: config.max_paths_per_destination,
1272 packet_hashlist_max_entries: config.packet_hashlist_max_entries,
1273 packet_hashlist_allocation: config.packet_hashlist_allocation,
1274 max_discovery_pr_tags: config.max_discovery_pr_tags,
1275 max_path_destinations: config.max_path_destinations,
1276 max_tunnel_destinations_total: config.max_tunnel_destinations_total,
1277 destination_timeout_secs: config.known_destinations_ttl.as_secs_f64(),
1278 announce_table_ttl_secs: config.announce_table_ttl.as_secs_f64(),
1279 announce_table_max_bytes: config.announce_table_max_bytes,
1280 announce_sig_cache_enabled: config.announce_sig_cache_enabled,
1281 announce_sig_cache_max_entries: config.announce_sig_cache_max_entries,
1282 announce_sig_cache_ttl_secs: config.announce_sig_cache_ttl.as_secs_f64(),
1283 announce_queue_max_entries: queue_config.announce_max_entries,
1284 announce_queue_max_interfaces: queue_config.announce_max_interfaces,
1285 };
1286
1287 let (tx, rx) = event::channel_with_queue_capacities(
1288 config.driver_event_queue_capacity,
1289 queue_config.inbound_capacities,
1290 );
1291 let tick_interval_ms = Arc::new(AtomicU64::new(1000));
1292 let mut driver = Driver::new(transport_config, rx, tx.clone(), callbacks);
1293 driver.underlay_mark = config.underlay_mark;
1294 driver
1295 .link_manager
1296 .set_link_mtu_discovery(queue_config.link_mtu_discovery);
1297 driver.set_announce_verify_queue_config(
1298 queue_config.announce_max_entries,
1299 queue_config.announce_max_bytes,
1300 queue_config.announce_ttl_secs,
1301 queue_config.announce_overflow_policy,
1302 );
1303 driver.async_announce_verification = true;
1304 driver.set_tick_interval_handle(Arc::clone(&tick_interval_ms));
1305 driver.known_destinations_ttl = config.known_destinations_ttl.as_secs_f64();
1306 driver.known_destinations_max_entries = config.known_destinations_max_entries;
1307 driver.ratchet_store = config.ratchet_store.clone();
1308 driver.ratchet_expiry_secs = config.ratchet_expiry.as_secs_f64();
1309 driver.interface_writer_queue_capacity = config.interface_writer_queue_capacity;
1310 driver.set_announce_rate_defaults(config.announce_rate_defaults);
1311 driver.set_ingress_control_defaults(config.ingress_control_defaults);
1312 driver.runtime_config_defaults.known_destinations_ttl =
1313 config.known_destinations_ttl.as_secs_f64();
1314 driver
1315 .holepunch_manager
1316 .set_policy(config.direct_connect_policy);
1317 driver.runtime_config_defaults.direct_connect_policy = config.direct_connect_policy;
1318 #[cfg(feature = "hooks")]
1319 if let Some(provider_config) = config.provider_bridge.clone() {
1320 driver.runtime_config_defaults.provider_queue_max_events =
1321 provider_config.queue_max_events;
1322 driver.runtime_config_defaults.provider_queue_max_bytes =
1323 provider_config.queue_max_bytes;
1324 if provider_config.enabled {
1325 match crate::provider_bridge::ProviderBridge::start(provider_config) {
1326 Ok(bridge) => driver.provider_bridge = Some(bridge),
1327 Err(err) => log::warn!("failed to start provider bridge: {}", err),
1328 }
1329 }
1330 }
1331
1332 if let Some(ref cache_dir) = config.cache_dir {
1334 let announces_dir = cache_dir.join("announces");
1335 let _ = std::fs::create_dir_all(&announces_dir);
1336 driver.announce_cache = Some(crate::announce_cache::AnnounceCache::new(announces_dir));
1337 }
1338
1339 if !config.probe_addrs.is_empty()
1341 || config.device.is_some()
1342 || config.underlay_mark.is_some()
1343 {
1344 driver.set_probe_config(
1345 config.probe_addrs.clone(),
1346 config.probe_protocol,
1347 config.device.clone(),
1348 );
1349 }
1350
1351 let probe_server = if let Some(port) = config.probe_port {
1353 let listen_addr: std::net::SocketAddr = ([0, 0, 0, 0], port).into();
1354 match crate::holepunch::probe::start_probe_server_with_mark(
1355 listen_addr,
1356 config.underlay_mark,
1357 ) {
1358 Ok(handle) => {
1359 log::info!("Probe server started on 0.0.0.0:{}", port);
1360 Some(handle)
1361 }
1362 Err(e) => {
1363 log::error!("Failed to start probe server on port {}: {}", port, e);
1364 None
1365 }
1366 }
1367 } else {
1368 None
1369 };
1370
1371 driver.management_config = config.management.clone();
1373
1374 driver.transport_identity = Some(transport_identity);
1376
1377 #[cfg(feature = "hooks")]
1379 {
1380 for hook_cfg in &config.hooks {
1381 if !hook_cfg.enabled {
1382 continue;
1383 }
1384 let point_idx = match config::parse_hook_point(&hook_cfg.attach_point) {
1385 Some(idx) => idx,
1386 None => {
1387 log::warn!(
1388 "Unknown hook point '{}' for hook '{}'",
1389 hook_cfg.attach_point,
1390 hook_cfg.name,
1391 );
1392 continue;
1393 }
1394 };
1395 let mgr = match driver.hook_manager.as_ref() {
1396 Some(m) => m,
1397 None => {
1398 log::warn!(
1399 "Hook manager not available, skipping hook '{}'",
1400 hook_cfg.name
1401 );
1402 continue;
1403 }
1404 };
1405 let hook_backend = match config::parse_hook_backend(&hook_cfg.hook_type) {
1406 Ok(backend) => backend,
1407 Err(e) => {
1408 log::warn!(
1409 "Invalid hook type '{}' for hook '{}': {}",
1410 hook_cfg.hook_type,
1411 hook_cfg.name,
1412 e,
1413 );
1414 continue;
1415 }
1416 };
1417 let load_result = if hook_backend == rns_hooks::HookBackend::Builtin {
1418 let builtin_id = hook_cfg
1419 .builtin_id
1420 .as_deref()
1421 .filter(|id| !id.is_empty())
1422 .or_else(|| (!hook_cfg.path.is_empty()).then_some(hook_cfg.path.as_str()));
1423 match builtin_id {
1424 Some(id) => mgr.load_builtin(hook_cfg.name.clone(), id, hook_cfg.priority),
1425 None => Err(rns_hooks::HookError::CompileError(
1426 "built-in hook requires builtin/id or path".to_string(),
1427 )),
1428 }
1429 } else {
1430 mgr.load_file_backend(
1431 hook_cfg.name.clone(),
1432 std::path::Path::new(&hook_cfg.path),
1433 hook_cfg.priority,
1434 hook_backend,
1435 )
1436 };
1437 match load_result {
1438 Ok(program) => {
1439 driver.hook_slots[point_idx].attach(program);
1440 log::info!(
1441 "Loaded hook '{}' at point {} (priority {})",
1442 hook_cfg.name,
1443 hook_cfg.attach_point,
1444 hook_cfg.priority,
1445 );
1446 }
1447 Err(e) => {
1448 log::error!(
1449 "Failed to load hook '{}' from '{}': {}",
1450 hook_cfg.name,
1451 hook_cfg.path,
1452 e,
1453 );
1454 }
1455 }
1456 }
1457 }
1458
1459 driver.discover_interfaces = config.discover_interfaces;
1461 driver.autoconnect_interface_mode = config.autoconnect_interface_mode;
1462 driver.autoconnect_interface_gravity = config.autoconnect_interface_gravity;
1463 driver.autoconnect_announces_to_internal =
1464 config.autoconnect_announces_to_internal.then_some(true);
1465 if let Some(val) = config.discovery_required_value {
1466 driver.discovery_required_value = val;
1467 }
1468
1469 let next_dynamic_id = Arc::new(AtomicU64::new(10000));
1471 #[cfg(feature = "iface-backbone")]
1472 {
1473 driver.next_dynamic_interface_id = Arc::clone(&next_dynamic_id);
1474 }
1475
1476 let mut discoverable_interfaces = Vec::new();
1478 #[cfg(feature = "iface-backbone")]
1479 let mut backbone_peer_pool_candidates = Vec::new();
1480
1481 let registry = config
1483 .registry
1484 .unwrap_or_else(crate::interface::registry::InterfaceRegistry::with_builtins);
1485 for iface_config in config.interfaces {
1486 #[cfg(feature = "iface-backbone")]
1487 if iface_config.type_name == "BackboneInterface" {
1488 if let Some(mode) = iface_config
1489 .config_data
1490 .as_any()
1491 .downcast_ref::<BackboneMode>()
1492 {
1493 if let Some(handle) = runtime_handle_from_mode(mode) {
1494 driver.register_backbone_runtime(handle);
1495 }
1496 if let Some(handle) = peer_state_handle_from_mode(mode) {
1497 driver.register_backbone_peer_state(handle);
1498 }
1499 if let Some(handle) = client_runtime_handle_from_mode(mode) {
1500 driver.register_backbone_client_runtime(handle);
1501 }
1502 if let Some(handle) = backbone_discovery_runtime_from_interface(
1503 &iface_config.name,
1504 mode,
1505 iface_config.discovery.as_ref(),
1506 config.transport_enabled,
1507 iface_config.ifac.as_ref(),
1508 ) {
1509 driver.register_backbone_discovery_runtime(handle);
1510 }
1511 }
1512 }
1513 #[cfg(feature = "iface-tcp")]
1514 if iface_config.type_name == "TCPClientInterface" {
1515 if let Some(tcp_config) = iface_config
1516 .config_data
1517 .as_any()
1518 .downcast_ref::<TcpClientConfig>()
1519 {
1520 driver.register_tcp_client_runtime(tcp_client_runtime_handle_from_config(
1521 tcp_config,
1522 ));
1523 }
1524 }
1525 #[cfg(feature = "iface-tcp")]
1526 if iface_config.type_name == "TCPServerInterface" {
1527 if let Some(tcp_config) = iface_config
1528 .config_data
1529 .as_any()
1530 .downcast_ref::<TcpServerConfig>()
1531 {
1532 driver.register_tcp_server_runtime(tcp_runtime_handle_from_config(tcp_config));
1533 driver.register_tcp_server_discovery_runtime(
1534 tcp_server_discovery_runtime_from_interface(
1535 &iface_config.name,
1536 tcp_config,
1537 iface_config.discovery.as_ref(),
1538 config.transport_enabled,
1539 iface_config.ifac.as_ref(),
1540 ),
1541 );
1542 }
1543 }
1544 #[cfg(feature = "iface-udp")]
1545 if iface_config.type_name == "UDPInterface" {
1546 if let Some(udp_config) = iface_config
1547 .config_data
1548 .as_any()
1549 .downcast_ref::<UdpConfig>()
1550 {
1551 driver.register_udp_runtime(udp_runtime_handle_from_config(udp_config));
1552 }
1553 }
1554 #[cfg(feature = "iface-auto")]
1555 if iface_config.type_name == "AutoInterface" {
1556 if let Some(auto_config) = iface_config
1557 .config_data
1558 .as_any()
1559 .downcast_ref::<AutoConfig>()
1560 {
1561 driver.register_auto_runtime(auto_runtime_handle_from_config(auto_config));
1562 }
1563 }
1564 #[cfg(feature = "iface-i2p")]
1565 if iface_config.type_name == "I2PInterface" {
1566 if let Some(i2p_config) = iface_config
1567 .config_data
1568 .as_any()
1569 .downcast_ref::<I2pConfig>()
1570 {
1571 driver.register_i2p_runtime(i2p_runtime_handle_from_config(i2p_config));
1572 }
1573 }
1574 #[cfg(feature = "iface-pipe")]
1575 if iface_config.type_name == "PipeInterface" {
1576 if let Some(pipe_config) = iface_config
1577 .config_data
1578 .as_any()
1579 .downcast_ref::<PipeConfig>()
1580 {
1581 driver.register_pipe_runtime(pipe_runtime_handle_from_config(pipe_config));
1582 }
1583 }
1584 #[cfg(feature = "iface-rnode")]
1585 if iface_config.type_name == "RNodeInterface" {
1586 if let Some(rnode_config) = iface_config
1587 .config_data
1588 .as_any()
1589 .downcast_ref::<RNodeConfig>()
1590 {
1591 driver.register_rnode_runtime(rnode_runtime_handle_from_config(rnode_config));
1592 }
1593 }
1594
1595 let factory = match registry.get(&iface_config.type_name) {
1596 Some(f) => f,
1597 None => {
1598 log::warn!(
1599 "No factory registered for interface type '{}'",
1600 iface_config.type_name
1601 );
1602 continue;
1603 }
1604 };
1605
1606 let mut ifac_state = derive_ifac_state(iface_config.ifac.as_ref(), &iface_config.name)?;
1607 let ifac_runtime =
1608 ifac_runtime_from_config(iface_config.ifac.as_ref(), factory.default_ifac_size());
1609
1610 #[cfg(feature = "iface-backbone")]
1611 if config.backbone_peer_pool.is_some() && iface_config.type_name == "BackboneInterface"
1612 {
1613 if let Some(mode) = iface_config
1614 .config_data
1615 .as_any()
1616 .downcast_ref::<BackboneMode>()
1617 {
1618 if let Some(client) = client_config_from_mode(mode) {
1619 let priority = client_priority_from_mode(mode).unwrap_or(
1620 crate::driver::BACKBONE_PEER_POOL_CONFIGURED_DEFAULT_PRIORITY,
1621 );
1622 backbone_peer_pool_candidates.push(BackbonePeerPoolCandidateConfig {
1623 client,
1624 mode: iface_config.mode,
1625 gravity: iface_config.gravity,
1626 recursive_prs: iface_config.recursive_prs,
1627 announces_from_internal: iface_config.announces_from_internal,
1628 announces_to_internal: iface_config.announces_to_internal,
1629 ingress_control: iface_config.ingress_control,
1630 ifac_runtime: ifac_runtime.clone(),
1631 ifac_enabled: ifac_state.is_some(),
1632 interface_type_name: iface_config.type_name.clone(),
1633 source: crate::driver::BackbonePeerPoolCandidateSource::Configured,
1634 priority,
1635 discovery: None,
1636 });
1637 if let Some(ref disc) = iface_config.discovery {
1638 discoverable_interfaces.push(discoverable_interface_from_config(
1639 &iface_config.name,
1640 disc,
1641 config.transport_enabled,
1642 iface_config.ifac.as_ref(),
1643 ));
1644 }
1645 continue;
1646 }
1647 }
1648 }
1649
1650 let ctx = crate::interface::StartContext {
1651 tx: tx.clone(),
1652 next_dynamic_id: next_dynamic_id.clone(),
1653 mode: iface_config.mode,
1654 gravity: iface_config.gravity,
1655 recursive_prs: iface_config.recursive_prs,
1656 announces_from_internal: iface_config.announces_from_internal,
1657 announces_to_internal: iface_config.announces_to_internal,
1658 ingress_control: iface_config.ingress_control,
1659 ifac: ifac_state.clone(),
1660 underlay_mark: config.underlay_mark,
1661 };
1662
1663 let result = match factory.start(iface_config.config_data, ctx) {
1664 Ok(r) => r,
1665 Err(e) => {
1666 if config.panic_on_interface_error {
1667 return Err(e);
1668 }
1669 log::error!(
1670 "Interface '{}' ({}) failed to start: {}",
1671 iface_config.name,
1672 iface_config.type_name,
1673 e
1674 );
1675 continue;
1676 }
1677 };
1678
1679 if let Some(ref disc) = iface_config.discovery {
1680 discoverable_interfaces.push(discoverable_interface_from_config(
1681 &iface_config.name,
1682 disc,
1683 config.transport_enabled,
1684 iface_config.ifac.as_ref(),
1685 ));
1686 }
1687
1688 match result {
1689 crate::interface::StartResult::Simple {
1690 id,
1691 info,
1692 writer,
1693 interface_type_name,
1694 } => {
1695 register_started_interface(StartedInterface {
1696 driver: &mut driver,
1697 tx: &tx,
1698 queue_capacity: config.interface_writer_queue_capacity,
1699 id,
1700 info,
1701 writer,
1702 interface_type_name,
1703 ifac_state,
1704 ifac_runtime: &ifac_runtime,
1705 });
1706 }
1707 crate::interface::StartResult::Listener { control } => {
1708 if let Some(control) = control {
1711 driver.register_listener_control(control);
1712 }
1713 }
1714 crate::interface::StartResult::Multi(subs) => {
1715 let ifac_cfg = &iface_config.ifac;
1716 let mut first = true;
1717 for sub in subs {
1718 let sub_ifac = if first {
1719 first = false;
1720 ifac_state.take()
1721 } else {
1722 derive_ifac_state(ifac_cfg.as_ref(), &sub.info.name)?
1723 };
1724 register_started_interface(StartedInterface {
1725 driver: &mut driver,
1726 tx: &tx,
1727 queue_capacity: config.interface_writer_queue_capacity,
1728 id: sub.id,
1729 info: sub.info,
1730 writer: sub.writer,
1731 interface_type_name: sub.interface_type_name,
1732 ifac_state: sub_ifac,
1733 ifac_runtime: &ifac_runtime,
1734 });
1735 }
1736 }
1737 }
1738 }
1739
1740 if !discoverable_interfaces.is_empty() {
1742 let transport_id = *identity.hash();
1743 let announcer =
1744 crate::discovery::InterfaceAnnouncer::new(transport_id, discoverable_interfaces);
1745 log::info!("Interface discovery announcer initialized");
1746 driver.interface_announcer = Some(announcer);
1747 }
1748
1749 if let Some(ref cache_dir) = config.cache_dir {
1751 let disc_path = std::path::PathBuf::from(cache_dir)
1752 .parent()
1753 .unwrap_or(std::path::Path::new("."))
1754 .join("storage")
1755 .join("discovery")
1756 .join("interfaces");
1757 let _ = std::fs::create_dir_all(&disc_path);
1758 driver.discovered_interfaces =
1759 crate::discovery::DiscoveredInterfaceStorage::new(disc_path);
1760 }
1761
1762 if let Some(ref storage_dir) = transport_state_dir {
1765 match storage::load_transport_state(storage_dir) {
1766 Ok(snapshot) => {
1767 let announce_cache = driver.announce_cache.as_ref();
1768 let stats = driver.engine.restore_persistence_snapshot(
1769 snapshot,
1770 time::now(),
1771 |packet_hash| {
1772 announce_cache
1773 .and_then(|cache| cache.get(packet_hash).ok().flatten())
1774 .map(|(raw, _)| raw)
1775 },
1776 );
1777 log::debug!(
1778 "restored transport state: {} packet hashes, {} paths, {} tunnels ({} paths skipped)",
1779 stats.packet_hashes,
1780 stats.paths,
1781 stats.tunnels,
1782 stats.skipped_paths,
1783 );
1784 }
1785 Err(err) => log::warn!("failed to restore transport state: {}", err),
1786 }
1787 }
1788
1789 #[cfg(feature = "iface-backbone")]
1790 if let Some(settings) = config.backbone_peer_pool.clone() {
1791 driver.configure_backbone_peer_pool(settings, backbone_peer_pool_candidates);
1792 driver.seed_backbone_peer_pool_from_discovery_cache();
1793 }
1794
1795 if config.management.enable_remote_management {
1797 if let Some(prv_key) = identity.get_private_key() {
1798 let identity_hash = *identity.hash();
1799 let mgmt_dest = crate::management::management_dest_hash(&identity_hash);
1800
1801 let sig_prv = rns_crypto::ed25519::Ed25519PrivateKey::from_bytes(
1803 &prv_key[32..64].try_into().unwrap(),
1804 );
1805 let sig_pub_bytes: [u8; 32] = identity.get_public_key().unwrap()[32..64]
1806 .try_into()
1807 .unwrap();
1808
1809 driver
1811 .engine
1812 .register_destination(mgmt_dest, rns_core::constants::DESTINATION_SINGLE);
1813 driver
1814 .local_destinations
1815 .insert(mgmt_dest, rns_core::constants::DESTINATION_SINGLE);
1816
1817 driver.link_manager.register_link_destination(
1819 mgmt_dest,
1820 sig_prv,
1821 sig_pub_bytes,
1822 crate::link_manager::ResourceStrategy::AcceptNone,
1823 );
1824
1825 driver
1827 .link_manager
1828 .register_management_path(crate::management::status_path_hash());
1829 driver
1830 .link_manager
1831 .register_management_path(crate::management::path_path_hash());
1832
1833 log::info!("Remote management enabled on {:02x?}", &mgmt_dest[..4],);
1834
1835 if !config.management.remote_management_allowed.is_empty() {
1837 log::info!(
1838 "Remote management allowed for {} identities",
1839 config.management.remote_management_allowed.len(),
1840 );
1841 }
1842 }
1843 }
1844
1845 if config.management.publish_blackhole {
1846 if let Some(prv_key) = identity.get_private_key() {
1847 let identity_hash = *identity.hash();
1848 let bh_dest = crate::management::blackhole_dest_hash(&identity_hash);
1849
1850 let sig_prv = rns_crypto::ed25519::Ed25519PrivateKey::from_bytes(
1851 &prv_key[32..64].try_into().unwrap(),
1852 );
1853 let sig_pub_bytes: [u8; 32] = identity.get_public_key().unwrap()[32..64]
1854 .try_into()
1855 .unwrap();
1856
1857 driver
1858 .engine
1859 .register_destination(bh_dest, rns_core::constants::DESTINATION_SINGLE);
1860 driver.link_manager.register_link_destination(
1861 bh_dest,
1862 sig_prv,
1863 sig_pub_bytes,
1864 crate::link_manager::ResourceStrategy::AcceptNone,
1865 );
1866 driver
1867 .link_manager
1868 .register_management_path(crate::management::list_path_hash());
1869
1870 log::info!(
1871 "Blackhole list publishing enabled on {:02x?}",
1872 &bh_dest[..4],
1873 );
1874 }
1875 }
1876
1877 if config.respond_to_probes && config.transport_enabled {
1879 let identity_hash = *identity.hash();
1880 let probe_dest = crate::management::probe_dest_hash(&identity_hash);
1881
1882 driver
1884 .engine
1885 .register_destination(probe_dest, rns_core::constants::DESTINATION_SINGLE);
1886 driver
1887 .local_destinations
1888 .insert(probe_dest, rns_core::constants::DESTINATION_SINGLE);
1889
1890 let probe_identity = rns_crypto::identity::Identity::from_private_key(
1892 &identity.get_private_key().unwrap(),
1893 );
1894 driver.proof_strategies.insert(
1895 probe_dest,
1896 (
1897 rns_core::types::ProofStrategy::ProveAll,
1898 Some(probe_identity),
1899 ),
1900 );
1901
1902 driver.probe_responder_hash = Some(probe_dest);
1903
1904 log::info!("Probe responder enabled on {:02x?}", &probe_dest[..4],);
1905 }
1906
1907 let timer_tx = tx.clone();
1909 let timer_interval = Arc::clone(&tick_interval_ms);
1910 thread::Builder::new()
1911 .name("rns-timer".into())
1912 .spawn(move || {
1913 loop {
1914 let ms = timer_interval.load(Ordering::Relaxed);
1915 thread::sleep(Duration::from_millis(ms));
1916 if timer_tx.send(Event::Tick).is_err() {
1917 break; }
1919 }
1920 })?;
1921
1922 #[cfg(feature = "iface-local")]
1924 if config.share_instance {
1925 let local_server_config = LocalServerConfig {
1926 instance_name: config.instance_name.clone(),
1927 port: config.shared_instance_port,
1928 interface_id: rns_core::transport::types::InterfaceId(0), };
1930 match crate::interface::local::start_server(
1931 local_server_config,
1932 tx.clone(),
1933 next_dynamic_id.clone(),
1934 ) {
1935 Ok(control) => {
1936 driver.register_listener_control(control);
1937 log::info!(
1938 "Local shared instance server started (instance={}, port={})",
1939 config.instance_name,
1940 config.shared_instance_port
1941 );
1942 }
1943 Err(e) => {
1944 log::error!("Failed to start local shared instance server: {}", e);
1945 }
1946 }
1947 }
1948
1949 let rpc_server = if config.share_instance {
1951 let auth_key =
1952 crate::rpc::derive_auth_key(&identity.get_private_key().unwrap_or([0u8; 64]));
1953 let rpc_addr = crate::rpc::RpcAddr::Tcp("127.0.0.1".into(), config.rpc_port);
1954 match crate::rpc::RpcServer::start(&rpc_addr, auth_key, tx.clone()) {
1955 Ok(server) => {
1956 log::info!("RPC server started on 127.0.0.1:{}", config.rpc_port);
1957 Some(server)
1958 }
1959 Err(e) => {
1960 log::error!("Failed to start RPC server: {}", e);
1961 None
1962 }
1963 }
1964 } else {
1965 None
1966 };
1967
1968 let announce_verify_queue = Arc::clone(&driver.announce_verify_queue);
1969 let verify_shutdown = Arc::new(AtomicBool::new(false));
1970 let verify_shutdown_thread = Arc::clone(&verify_shutdown);
1971 let verify_tx = tx.clone();
1972 let verify_handle = thread::Builder::new()
1973 .name("rns-verify".into())
1974 .spawn(move || {
1975 #[cfg(target_family = "unix")]
1976 {
1977 unsafe {
1978 libc::nice(5);
1979 }
1980 }
1981
1982 while !verify_shutdown_thread.load(Ordering::Relaxed) {
1983 let batch = {
1984 let mut queue = announce_verify_queue
1985 .lock()
1986 .unwrap_or_else(|poisoned| poisoned.into_inner());
1987 queue.take_pending(time::now())
1988 };
1989
1990 if batch.is_empty() {
1991 thread::sleep(Duration::from_millis(50));
1992 continue;
1993 }
1994
1995 for (key, pending) in batch {
1996 if verify_shutdown_thread.load(Ordering::Relaxed) {
1997 break;
1998 }
1999 let has_ratchet =
2000 pending.packet.flags.context_flag == rns_core::constants::FLAG_SET;
2001 let announce = match rns_core::announce::AnnounceData::unpack(
2002 &pending.packet.data,
2003 has_ratchet,
2004 ) {
2005 Ok(announce) => announce,
2006 Err(_) => {
2007 let signature = [0u8; 64];
2008 let sig_cache_key = {
2009 let mut material = [0u8; 80];
2010 material[..16]
2011 .copy_from_slice(&pending.packet.destination_hash);
2012 material[16..].copy_from_slice(&signature);
2013 rns_core::hash::full_hash(&material)
2014 };
2015 if verify_tx
2016 .send(Event::AnnounceVerifyFailed { key, sig_cache_key })
2017 .is_err()
2018 {
2019 return;
2020 }
2021 continue;
2022 }
2023 };
2024 let mut material = [0u8; 80];
2025 material[..16].copy_from_slice(&pending.packet.destination_hash);
2026 material[16..].copy_from_slice(&announce.signature);
2027 let sig_cache_key = rns_core::hash::full_hash(&material);
2028 match announce.validate(&pending.packet.destination_hash) {
2029 Ok(validated) => {
2030 if verify_tx
2031 .send(Event::AnnounceVerified {
2032 key,
2033 validated,
2034 sig_cache_key,
2035 })
2036 .is_err()
2037 {
2038 return;
2039 }
2040 }
2041 Err(_) => {
2042 if verify_tx
2043 .send(Event::AnnounceVerifyFailed { key, sig_cache_key })
2044 .is_err()
2045 {
2046 return;
2047 }
2048 }
2049 }
2050 }
2051 }
2052 })?;
2053
2054 let driver_handle = thread::Builder::new()
2058 .name("rns-driver".into())
2059 .spawn(move || {
2060 driver.run();
2061 })?;
2062
2063 let (persist_handle, persist_stop) = if let Some(storage_dir) = transport_state_dir.clone()
2067 {
2068 let persist_tx = tx.clone();
2069 let (stop_tx, stop_rx) = std::sync::mpsc::channel();
2070 let handle = thread::Builder::new()
2071 .name("rns-persist".into())
2072 .spawn(move || {
2073 #[cfg(target_family = "unix")]
2074 unsafe {
2075 libc::nice(5);
2076 }
2077
2078 loop {
2079 match stop_rx.recv_timeout(Duration::from_secs(12 * 60 * 60)) {
2080 Ok(()) | Err(std::sync::mpsc::RecvTimeoutError::Disconnected) => break,
2081 Err(std::sync::mpsc::RecvTimeoutError::Timeout) => {}
2082 }
2083 let (response_tx, response_rx) = std::sync::mpsc::channel();
2084 if persist_tx
2085 .send(Event::Query(
2086 QueryRequest::TransportStateSnapshot,
2087 response_tx,
2088 ))
2089 .is_err()
2090 {
2091 break;
2092 }
2093 let Ok(QueryResponse::TransportStateSnapshot(snapshot)) =
2094 response_rx.recv()
2095 else {
2096 continue;
2097 };
2098 if let Err(err) =
2099 storage::save_transport_state(&snapshot, &storage_dir, true)
2100 {
2101 log::warn!("failed to persist transport state: {}", err);
2102 }
2103 }
2104 })?;
2105 (Some(handle), Some(stop_tx))
2106 } else {
2107 (None, None)
2108 };
2109
2110 Ok(RnsNode {
2111 tx,
2112 driver_handle: Some(driver_handle),
2113 verify_handle: Some(verify_handle),
2114 verify_shutdown,
2115 persist_handle,
2116 persist_stop,
2117 rpc_server,
2118 tick_interval_ms,
2119 probe_server,
2120 known_destinations_path: None,
2121 transport_state_dir,
2122 ratchet_store: config.ratchet_store,
2123 ratchet_expiry: config.ratchet_expiry,
2124 })
2125 }
2126
2127 pub fn query(&self, request: QueryRequest) -> Result<QueryResponse, SendError> {
2129 let (resp_tx, resp_rx) = std::sync::mpsc::channel();
2130 self.tx
2131 .send(Event::Query(request, resp_tx))
2132 .map_err(|_| SendError)?;
2133 resp_rx.recv().map_err(|_| SendError)
2134 }
2135
2136 pub fn links(&self) -> Result<Vec<crate::event::LinkInfoEntry>, SendError> {
2138 match self.query(QueryRequest::Links)? {
2139 QueryResponse::Links(links) => Ok(links),
2140 _ => Err(SendError),
2141 }
2142 }
2143
2144 pub fn lowest_interface_bitrate(&self) -> Result<Option<u64>, SendError> {
2149 match self.query(QueryRequest::LowestInterfaceBitrate)? {
2150 QueryResponse::LowestInterfaceBitrate(bitrate) => Ok(bitrate),
2151 _ => Err(SendError),
2152 }
2153 }
2154
2155 pub fn medium_path_timeout(&self) -> Result<f64, SendError> {
2161 match self.query(QueryRequest::MediumPathTimeout)? {
2162 QueryResponse::MediumPathTimeout(timeout) => Ok(timeout),
2163 _ => Err(SendError),
2164 }
2165 }
2166
2167 pub fn begin_drain(&self, timeout: Duration) -> Result<(), SendError> {
2169 self.tx
2170 .send(Event::BeginDrain { timeout })
2171 .map_err(|_| SendError)
2172 }
2173
2174 pub fn drain_status(&self) -> Result<crate::event::DrainStatus, SendError> {
2176 match self.query(QueryRequest::DrainStatus)? {
2177 QueryResponse::DrainStatus(status) => Ok(status),
2178 _ => Err(SendError),
2179 }
2180 }
2181
2182 fn reject_new_work_if_draining(&self) -> Result<(), SendError> {
2183 let status = self.drain_status()?;
2184 if matches!(status.state, crate::event::LifecycleState::Active) {
2185 Ok(())
2186 } else {
2187 Err(SendError)
2188 }
2189 }
2190
2191 pub fn send_raw(
2193 &self,
2194 raw: Vec<u8>,
2195 dest_type: u8,
2196 attached_interface: Option<rns_core::transport::types::InterfaceId>,
2197 ) -> Result<(), SendError> {
2198 self.tx
2199 .send(Event::SendOutbound {
2200 raw,
2201 dest_type,
2202 attached_interface,
2203 })
2204 .map_err(|_| SendError)
2205 }
2206
2207 pub fn register_destination(
2209 &self,
2210 dest_hash: [u8; 16],
2211 dest_type: u8,
2212 ) -> Result<(), SendError> {
2213 self.tx
2214 .send(Event::RegisterDestination {
2215 dest_hash,
2216 dest_type,
2217 })
2218 .map_err(|_| SendError)
2219 }
2220
2221 pub fn deregister_destination(&self, dest_hash: [u8; 16]) -> Result<(), SendError> {
2223 self.tx
2224 .send(Event::DeregisterDestination { dest_hash })
2225 .map_err(|_| SendError)
2226 }
2227
2228 pub fn deregister_link_destination(&self, dest_hash: [u8; 16]) -> Result<(), SendError> {
2230 self.tx
2231 .send(Event::DeregisterLinkDestination { dest_hash })
2232 .map_err(|_| SendError)
2233 }
2234
2235 pub fn register_link_destination(
2241 &self,
2242 dest_hash: [u8; 16],
2243 sig_prv_bytes: [u8; 32],
2244 sig_pub_bytes: [u8; 32],
2245 resource_strategy: u8,
2246 ) -> Result<(), SendError> {
2247 self.register_link_destination_with_max_request_size(
2248 dest_hash,
2249 sig_prv_bytes,
2250 sig_pub_bytes,
2251 resource_strategy,
2252 None,
2253 )
2254 }
2255
2256 pub fn register_link_destination_with_max_request_size(
2258 &self,
2259 dest_hash: [u8; 16],
2260 sig_prv_bytes: [u8; 32],
2261 sig_pub_bytes: [u8; 32],
2262 resource_strategy: u8,
2263 max_request_size: Option<usize>,
2264 ) -> Result<(), SendError> {
2265 self.tx
2266 .send(Event::RegisterLinkDestination {
2267 dest_hash,
2268 sig_prv_bytes,
2269 sig_pub_bytes,
2270 resource_strategy,
2271 max_request_size,
2272 })
2273 .map_err(|_| SendError)
2274 }
2275
2276 pub fn register_request_handler<F>(
2278 &self,
2279 path: &str,
2280 allowed_list: Option<Vec<[u8; 16]>>,
2281 handler: F,
2282 ) -> Result<(), SendError>
2283 where
2284 F: Fn([u8; 16], &str, &[u8], Option<&([u8; 16], [u8; 64])>) -> Option<Vec<u8>>
2285 + Send
2286 + 'static,
2287 {
2288 self.tx
2289 .send(Event::RegisterRequestHandler {
2290 path: path.to_string(),
2291 allowed_list,
2292 handler: Box::new(handler),
2293 })
2294 .map_err(|_| SendError)
2295 }
2296
2297 pub fn register_request_handler_response<F>(
2299 &self,
2300 path: &str,
2301 allowed_list: Option<Vec<[u8; 16]>>,
2302 handler: F,
2303 ) -> Result<(), SendError>
2304 where
2305 F: Fn(
2306 [u8; 16],
2307 &str,
2308 &[u8],
2309 Option<&([u8; 16], [u8; 64])>,
2310 ) -> Option<crate::link_manager::RequestResponse>
2311 + Send
2312 + 'static,
2313 {
2314 self.tx
2315 .send(Event::RegisterRequestHandlerResponse {
2316 path: path.to_string(),
2317 allowed_list,
2318 handler: Box::new(handler),
2319 })
2320 .map_err(|_| SendError)
2321 }
2322
2323 pub fn register_deferred_request_handler<F>(
2326 &self,
2327 path: &str,
2328 allowed_list: Option<Vec<[u8; 16]>>,
2329 handler: F,
2330 ) -> Result<(), SendError>
2331 where
2332 F: Fn([u8; 16], &str, [u8; 16], &[u8], Option<&([u8; 16], [u8; 64])>) + Send + 'static,
2333 {
2334 self.tx
2335 .send(Event::RegisterDeferredRequestHandler {
2336 path: path.to_string(),
2337 allowed_list,
2338 handler: Box::new(handler),
2339 })
2340 .map_err(|_| SendError)
2341 }
2342
2343 pub fn send_deferred_response(
2345 &self,
2346 link_id: [u8; 16],
2347 request_id: [u8; 16],
2348 data: Vec<u8>,
2349 ) -> Result<(), SendError> {
2350 self.tx
2351 .send(Event::SendDeferredResponse {
2352 link_id,
2353 request_id,
2354 data,
2355 })
2356 .map_err(|_| SendError)
2357 }
2358
2359 pub fn create_link(
2363 &self,
2364 dest_hash: [u8; 16],
2365 dest_sig_pub_bytes: [u8; 32],
2366 ) -> Result<[u8; 16], SendError> {
2367 self.reject_new_work_if_draining()?;
2368 let (response_tx, response_rx) = std::sync::mpsc::channel();
2369 self.tx
2370 .send(Event::CreateLink {
2371 dest_hash,
2372 dest_sig_pub_bytes,
2373 response_tx,
2374 })
2375 .map_err(|_| SendError)?;
2376 let link_id = response_rx.recv().map_err(|_| SendError)?;
2377 if link_id == [0u8; 16] {
2378 Err(SendError)
2379 } else {
2380 Ok(link_id)
2381 }
2382 }
2383
2384 pub fn send_request(
2386 &self,
2387 link_id: [u8; 16],
2388 path: &str,
2389 data: &[u8],
2390 ) -> Result<(), SendError> {
2391 self.send_request_with_max_response_size(link_id, path, data, None)
2392 }
2393
2394 pub fn send_request_with_max_response_size(
2396 &self,
2397 link_id: [u8; 16],
2398 path: &str,
2399 data: &[u8],
2400 max_response_size: Option<usize>,
2401 ) -> Result<(), SendError> {
2402 self.reject_new_work_if_draining()?;
2403 self.tx
2404 .send(Event::SendRequest {
2405 link_id,
2406 path: path.to_string(),
2407 data: data.to_vec(),
2408 max_response_size,
2409 })
2410 .map_err(|_| SendError)
2411 }
2412
2413 pub fn identify_on_link(
2415 &self,
2416 link_id: [u8; 16],
2417 identity_prv_key: [u8; 64],
2418 ) -> Result<(), SendError> {
2419 self.reject_new_work_if_draining()?;
2420 self.tx
2421 .send(Event::IdentifyOnLink {
2422 link_id,
2423 identity_prv_key,
2424 })
2425 .map_err(|_| SendError)
2426 }
2427
2428 pub fn teardown_link(&self, link_id: [u8; 16]) -> Result<(), SendError> {
2430 self.tx
2431 .send(Event::TeardownLink { link_id })
2432 .map_err(|_| SendError)
2433 }
2434
2435 pub fn send_resource(
2437 &self,
2438 link_id: [u8; 16],
2439 data: Vec<u8>,
2440 metadata: Option<Vec<u8>>,
2441 ) -> Result<(), SendError> {
2442 self.send_resource_with_auto_compress(link_id, data, metadata, true)
2443 }
2444
2445 pub fn send_resource_with_auto_compress(
2447 &self,
2448 link_id: [u8; 16],
2449 data: Vec<u8>,
2450 metadata: Option<Vec<u8>>,
2451 auto_compress: bool,
2452 ) -> Result<(), SendError> {
2453 self.reject_new_work_if_draining()?;
2454 self.tx
2455 .send(Event::SendResource {
2456 link_id,
2457 data,
2458 metadata,
2459 auto_compress,
2460 })
2461 .map_err(|_| SendError)
2462 }
2463
2464 pub fn send_resource_reader<R: Read + Send + 'static>(
2467 &self,
2468 link_id: [u8; 16],
2469 reader: R,
2470 declared_length: u64,
2471 metadata: Option<Vec<u8>>,
2472 auto_compress: bool,
2473 ) -> Result<crate::resource::ResourceTransferId, SendError> {
2474 self.reject_new_work_if_draining()?;
2475 let transfer_id = crate::resource::ResourceTransferId(
2476 NEXT_RESOURCE_TRANSFER_ID.fetch_add(1, Ordering::Relaxed),
2477 );
2478 self.tx
2479 .send(Event::SendResourceStream {
2480 link_id,
2481 transfer_id,
2482 reader: Box::new(reader),
2483 declared_length,
2484 metadata,
2485 auto_compress,
2486 })
2487 .map_err(|_| SendError)?;
2488 Ok(transfer_id)
2489 }
2490
2491 pub fn send_resource_file(
2493 &self,
2494 link_id: [u8; 16],
2495 path: impl AsRef<Path>,
2496 metadata: Option<Vec<u8>>,
2497 auto_compress: bool,
2498 ) -> io::Result<crate::resource::ResourceTransferId> {
2499 let file = std::fs::File::open(path.as_ref())?;
2500 let length = file.metadata()?.len();
2501 self.send_resource_reader(link_id, file, length, metadata, auto_compress)
2502 .map_err(|_| io::Error::new(io::ErrorKind::BrokenPipe, "RNS driver shut down"))
2503 }
2504
2505 pub fn set_resource_receive_mode(
2507 &self,
2508 link_id: [u8; 16],
2509 mode: crate::resource::ResourceReceiveMode,
2510 ) -> Result<(), SendError> {
2511 self.tx
2512 .send(Event::SetResourceReceiveMode { link_id, mode })
2513 .map_err(|_| SendError)
2514 }
2515
2516 pub fn set_resource_strategy(&self, link_id: [u8; 16], strategy: u8) -> Result<(), SendError> {
2520 self.tx
2521 .send(Event::SetResourceStrategy { link_id, strategy })
2522 .map_err(|_| SendError)
2523 }
2524
2525 pub fn accept_resource(
2527 &self,
2528 link_id: [u8; 16],
2529 resource_hash: Vec<u8>,
2530 accept: bool,
2531 ) -> Result<(), SendError> {
2532 if accept {
2533 self.reject_new_work_if_draining()?;
2534 }
2535 self.tx
2536 .send(Event::AcceptResource {
2537 link_id,
2538 resource_hash,
2539 accept,
2540 })
2541 .map_err(|_| SendError)
2542 }
2543
2544 pub fn send_channel_message(
2546 &self,
2547 link_id: [u8; 16],
2548 msgtype: u16,
2549 payload: Vec<u8>,
2550 ) -> Result<(), SendError> {
2551 self.reject_new_work_if_draining()?;
2552 let (response_tx, response_rx) = std::sync::mpsc::channel();
2553 self.tx
2554 .send(Event::SendChannelMessage {
2555 link_id,
2556 msgtype,
2557 payload,
2558 response_tx,
2559 })
2560 .map_err(|_| SendError)?;
2561 response_rx
2562 .recv()
2563 .map_err(|_| SendError)?
2564 .map_err(|_| SendError)
2565 }
2566
2567 pub fn propose_direct_connect(&self, link_id: [u8; 16]) -> Result<(), SendError> {
2572 self.reject_new_work_if_draining()?;
2573 self.tx
2574 .send(Event::ProposeDirectConnect { link_id })
2575 .map_err(|_| SendError)
2576 }
2577
2578 pub fn set_direct_connect_policy(
2580 &self,
2581 policy: crate::holepunch::orchestrator::HolePunchPolicy,
2582 ) -> Result<(), SendError> {
2583 self.tx
2584 .send(Event::SetDirectConnectPolicy { policy })
2585 .map_err(|_| SendError)
2586 }
2587
2588 pub fn send_on_link(
2590 &self,
2591 link_id: [u8; 16],
2592 data: Vec<u8>,
2593 context: u8,
2594 ) -> Result<(), SendError> {
2595 self.reject_new_work_if_draining()?;
2596 self.tx
2597 .send(Event::SendOnLink {
2598 link_id,
2599 data,
2600 context,
2601 response_tx: None,
2602 })
2603 .map_err(|_| SendError)
2604 }
2605
2606 pub fn try_send_link_datagram(
2611 &self,
2612 link_id: [u8; 16],
2613 data: Vec<u8>,
2614 context: u8,
2615 timeout: Duration,
2616 ) -> Result<(), crate::event::LinkDatagramError> {
2617 use crate::event::LinkDatagramError;
2618 if self.reject_new_work_if_draining().is_err() {
2619 return Err(LinkDatagramError::DriverStopped);
2620 }
2621 let (response_tx, response_rx) = std::sync::mpsc::channel();
2622 self.tx
2623 .try_send(Event::SendOnLink {
2624 link_id,
2625 data,
2626 context,
2627 response_tx: Some(response_tx),
2628 })
2629 .map_err(|error| match error {
2630 std::sync::mpsc::TrySendError::Full(_) => LinkDatagramError::DriverQueueFull,
2631 std::sync::mpsc::TrySendError::Disconnected(_) => LinkDatagramError::DriverStopped,
2632 })?;
2633 response_rx
2634 .recv_timeout(timeout)
2635 .map_err(|error| match error {
2636 std::sync::mpsc::RecvTimeoutError::Timeout => LinkDatagramError::AdmissionTimedOut,
2637 std::sync::mpsc::RecvTimeoutError::Disconnected => LinkDatagramError::DriverStopped,
2638 })?
2639 }
2640
2641 pub fn announce(
2646 &self,
2647 dest: &crate::destination::Destination,
2648 identity: &Identity,
2649 app_data: Option<&[u8]>,
2650 ) -> Result<(), SendError> {
2651 self.reject_new_work_if_draining()?;
2652 let name_hash = rns_core::destination::name_hash(
2653 &dest.app_name,
2654 &dest.aspects.iter().map(|s| s.as_str()).collect::<Vec<_>>(),
2655 );
2656
2657 let mut random_hash = [0u8; 10];
2658 OsRng.fill_bytes(&mut random_hash[..5]);
2659 let now_secs = std::time::SystemTime::now()
2663 .duration_since(std::time::UNIX_EPOCH)
2664 .unwrap_or_default()
2665 .as_secs();
2666 random_hash[5..10].copy_from_slice(&now_secs.to_be_bytes()[3..8]);
2667
2668 let (announce_data, _has_ratchet) = rns_core::announce::AnnounceData::pack(
2669 identity,
2670 &dest.hash.0,
2671 &name_hash,
2672 &random_hash,
2673 None, app_data,
2675 )
2676 .map_err(|_| SendError)?;
2677
2678 let context_flag = rns_core::constants::FLAG_UNSET;
2679
2680 let flags = rns_core::packet::PacketFlags {
2681 header_type: rns_core::constants::HEADER_1,
2682 context_flag,
2683 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
2684 destination_type: rns_core::constants::DESTINATION_SINGLE,
2685 packet_type: rns_core::constants::PACKET_TYPE_ANNOUNCE,
2686 };
2687
2688 let packet = rns_core::packet::RawPacket::pack(
2689 flags,
2690 0,
2691 &dest.hash.0,
2692 None,
2693 rns_core::constants::CONTEXT_NONE,
2694 &announce_data,
2695 )
2696 .map_err(|_| SendError)?;
2697
2698 if dest.dest_type == rns_core::types::DestinationType::Single {
2699 if let Some(identity_prv_key) = identity.get_private_key() {
2700 self.tx
2701 .send(Event::StoreSharedAnnounce {
2702 dest_hash: dest.hash.0,
2703 name_hash,
2704 identity_prv_key,
2705 app_data: app_data.map(|d| d.to_vec()),
2706 })
2707 .map_err(|_| SendError)?;
2708 }
2709 }
2710
2711 self.send_raw(packet.raw, dest.dest_type.to_wire_constant(), None)
2712 }
2713
2714 pub fn send_packet(
2719 &self,
2720 dest: &crate::destination::Destination,
2721 data: &[u8],
2722 ) -> Result<rns_core::types::PacketHash, SendError> {
2723 self.reject_new_work_if_draining()?;
2724 use rns_core::types::DestinationType;
2725
2726 let payload = match dest.dest_type {
2727 DestinationType::Single => self.encrypt_single_payload(dest, data)?,
2728 DestinationType::Plain => data.to_vec(),
2729 DestinationType::Group => dest.encrypt(data).map_err(|_| SendError)?,
2730 };
2731
2732 let flags = rns_core::packet::PacketFlags {
2733 header_type: rns_core::constants::HEADER_1,
2734 context_flag: rns_core::constants::FLAG_UNSET,
2735 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
2736 destination_type: dest.dest_type.to_wire_constant(),
2737 packet_type: rns_core::constants::PACKET_TYPE_DATA,
2738 };
2739
2740 let packet = rns_core::packet::RawPacket::pack(
2741 flags,
2742 0,
2743 &dest.hash.0,
2744 None,
2745 rns_core::constants::CONTEXT_NONE,
2746 &payload,
2747 )
2748 .map_err(|_| SendError)?;
2749
2750 let packet_hash = rns_core::types::PacketHash(packet.packet_hash);
2751
2752 self.tx
2753 .send(Event::SendOutbound {
2754 raw: packet.raw,
2755 dest_type: dest.dest_type.to_wire_constant(),
2756 attached_interface: None,
2757 })
2758 .map_err(|_| SendError)?;
2759
2760 Ok(packet_hash)
2761 }
2762
2763 fn encrypt_single_payload(
2764 &self,
2765 dest: &crate::destination::Destination,
2766 data: &[u8],
2767 ) -> Result<Vec<u8>, SendError> {
2768 let pub_key = dest.public_key.ok_or(SendError)?;
2769 let remote_id = rns_crypto::identity::Identity::from_public_key(&pub_key);
2770 let ratchet = self.ratchet_store.as_ref().and_then(|store| {
2771 match store.current(&dest.hash.0, time::now(), self.ratchet_expiry.as_secs_f64()) {
2772 Ok(entry) => entry.map(|entry| entry.ratchet),
2773 Err(err) => {
2774 log::warn!(
2775 "failed to load ratchet for {:02x}{:02x}{:02x}{:02x}..: {}",
2776 dest.hash.0[0],
2777 dest.hash.0[1],
2778 dest.hash.0[2],
2779 dest.hash.0[3],
2780 err
2781 );
2782 None
2783 }
2784 }
2785 });
2786 remote_id
2787 .encrypt_with_ratchet(data, ratchet.as_ref(), &mut OsRng)
2788 .map_err(|_| SendError)
2789 }
2790
2791 pub fn register_destination_with_proof(
2796 &self,
2797 dest: &crate::destination::Destination,
2798 signing_key: Option<[u8; 64]>,
2799 ) -> Result<(), SendError> {
2800 self.register_destination(dest.hash.0, dest.dest_type.to_wire_constant())?;
2802
2803 if dest.proof_strategy != rns_core::types::ProofStrategy::ProveNone {
2805 self.tx
2806 .send(Event::RegisterProofStrategy {
2807 dest_hash: dest.hash.0,
2808 strategy: dest.proof_strategy,
2809 signing_key,
2810 })
2811 .map_err(|_| SendError)?;
2812 }
2813
2814 Ok(())
2815 }
2816
2817 pub fn request_path(&self, dest_hash: &rns_core::types::DestHash) -> Result<(), SendError> {
2819 self.reject_new_work_if_draining()?;
2820 self.tx
2821 .send(Event::RequestPath {
2822 dest_hash: dest_hash.0,
2823 })
2824 .map_err(|_| SendError)
2825 }
2826
2827 pub fn has_path(&self, dest_hash: &rns_core::types::DestHash) -> Result<bool, SendError> {
2829 match self.query(QueryRequest::HasPath {
2830 dest_hash: dest_hash.0,
2831 })? {
2832 QueryResponse::HasPath(v) => Ok(v),
2833 _ => Ok(false),
2834 }
2835 }
2836
2837 pub fn hops_to(&self, dest_hash: &rns_core::types::DestHash) -> Result<Option<u8>, SendError> {
2839 match self.query(QueryRequest::HopsTo {
2840 dest_hash: dest_hash.0,
2841 })? {
2842 QueryResponse::HopsTo(v) => Ok(v),
2843 _ => Ok(None),
2844 }
2845 }
2846
2847 pub fn recall_identity(
2849 &self,
2850 dest_hash: &rns_core::types::DestHash,
2851 ) -> Result<Option<crate::destination::AnnouncedIdentity>, SendError> {
2852 match self.query(QueryRequest::RecallIdentity {
2853 dest_hash: dest_hash.0,
2854 })? {
2855 QueryResponse::RecallIdentity(v) => Ok(v),
2856 _ => Ok(None),
2857 }
2858 }
2859
2860 pub fn known_destinations(
2862 &self,
2863 ) -> Result<Vec<crate::event::KnownDestinationEntry>, SendError> {
2864 match self.query(QueryRequest::KnownDestinations)? {
2865 QueryResponse::KnownDestinations(entries) => Ok(entries),
2866 _ => Ok(Vec::new()),
2867 }
2868 }
2869
2870 pub fn retain_known_destination(
2872 &self,
2873 dest_hash: &rns_core::types::DestHash,
2874 ) -> Result<bool, SendError> {
2875 match self.query(QueryRequest::RetainKnownDestination {
2876 dest_hash: dest_hash.0,
2877 })? {
2878 QueryResponse::RetainKnownDestination(ok) => Ok(ok),
2879 _ => Ok(false),
2880 }
2881 }
2882
2883 pub fn retain_identity(
2885 &self,
2886 identity_hash: &rns_core::types::IdentityHash,
2887 ) -> Result<bool, SendError> {
2888 match self.query(QueryRequest::RetainIdentity {
2889 identity_hash: identity_hash.0,
2890 })? {
2891 QueryResponse::RetainIdentity(ok) => Ok(ok),
2892 _ => Ok(false),
2893 }
2894 }
2895
2896 pub fn unretain_known_destination(
2898 &self,
2899 dest_hash: &rns_core::types::DestHash,
2900 ) -> Result<bool, SendError> {
2901 match self.query(QueryRequest::UnretainKnownDestination {
2902 dest_hash: dest_hash.0,
2903 })? {
2904 QueryResponse::UnretainKnownDestination(ok) => Ok(ok),
2905 _ => Ok(false),
2906 }
2907 }
2908
2909 pub fn mark_known_destination_used(
2911 &self,
2912 dest_hash: &rns_core::types::DestHash,
2913 ) -> Result<bool, SendError> {
2914 match self.query(QueryRequest::MarkKnownDestinationUsed {
2915 dest_hash: dest_hash.0,
2916 })? {
2917 QueryResponse::MarkKnownDestinationUsed(ok) => Ok(ok),
2918 _ => Ok(false),
2919 }
2920 }
2921
2922 fn persist_known_destinations(&self) {
2923 let Some(path) = self.known_destinations_path.as_ref() else {
2924 return;
2925 };
2926
2927 let Ok(entries) = self.known_destinations() else {
2928 return;
2929 };
2930
2931 let destinations: std::collections::HashMap<[u8; 16], storage::KnownDestination> = entries
2932 .into_iter()
2933 .map(|entry| {
2934 (
2935 entry.dest_hash,
2936 storage::KnownDestination {
2937 identity_hash: entry.identity_hash,
2938 public_key: entry.public_key,
2939 app_data: entry.app_data,
2940 hops: entry.hops,
2941 received_at: entry.received_at,
2942 receiving_interface: entry.receiving_interface.0,
2943 was_used: entry.was_used,
2944 last_used_at: entry.last_used_at,
2945 retained: entry.retained,
2946 },
2947 )
2948 })
2949 .collect();
2950
2951 if let Err(err) = storage::save_known_destinations(&destinations, path) {
2952 log::warn!("failed to persist known destinations: {}", err);
2953 }
2954 }
2955
2956 fn persist_transport_state(&self) {
2957 let Some(storage_dir) = self.transport_state_dir.as_ref() else {
2958 return;
2959 };
2960 let Ok(QueryResponse::TransportStateSnapshot(snapshot)) =
2961 self.query(QueryRequest::TransportStateSnapshot)
2962 else {
2963 return;
2964 };
2965 if let Err(err) = storage::save_transport_state(&snapshot, storage_dir, true) {
2966 log::warn!("failed to persist transport state: {}", err);
2967 }
2968 }
2969
2970 pub fn load_hook(
2972 &self,
2973 name: String,
2974 wasm_bytes: Vec<u8>,
2975 attach_point: String,
2976 priority: i32,
2977 ) -> Result<Result<(), String>, SendError> {
2978 let (response_tx, response_rx) = std::sync::mpsc::channel();
2979 self.tx
2980 .send(Event::LoadHook {
2981 name,
2982 wasm_bytes,
2983 attach_point,
2984 priority,
2985 response_tx,
2986 })
2987 .map_err(|_| SendError)?;
2988 response_rx.recv().map_err(|_| SendError)
2989 }
2990
2991 pub fn load_hook_file(
2993 &self,
2994 name: String,
2995 path: String,
2996 hook_type: String,
2997 attach_point: String,
2998 priority: i32,
2999 ) -> Result<Result<(), String>, SendError> {
3000 let (response_tx, response_rx) = std::sync::mpsc::channel();
3001 self.tx
3002 .send(Event::LoadHookFile {
3003 name,
3004 path,
3005 hook_type,
3006 attach_point,
3007 priority,
3008 response_tx,
3009 })
3010 .map_err(|_| SendError)?;
3011 response_rx.recv().map_err(|_| SendError)
3012 }
3013
3014 pub fn load_builtin_hook(
3016 &self,
3017 name: String,
3018 builtin_id: String,
3019 attach_point: String,
3020 priority: i32,
3021 ) -> Result<Result<(), String>, SendError> {
3022 let (response_tx, response_rx) = std::sync::mpsc::channel();
3023 self.tx
3024 .send(Event::LoadBuiltinHook {
3025 name,
3026 builtin_id,
3027 attach_point,
3028 priority,
3029 response_tx,
3030 })
3031 .map_err(|_| SendError)?;
3032 response_rx.recv().map_err(|_| SendError)
3033 }
3034
3035 pub fn unload_hook(
3037 &self,
3038 name: String,
3039 attach_point: String,
3040 ) -> Result<Result<(), String>, SendError> {
3041 let (response_tx, response_rx) = std::sync::mpsc::channel();
3042 self.tx
3043 .send(Event::UnloadHook {
3044 name,
3045 attach_point,
3046 response_tx,
3047 })
3048 .map_err(|_| SendError)?;
3049 response_rx.recv().map_err(|_| SendError)
3050 }
3051
3052 pub fn reload_hook(
3054 &self,
3055 name: String,
3056 attach_point: String,
3057 wasm_bytes: Vec<u8>,
3058 ) -> Result<Result<(), String>, SendError> {
3059 let (response_tx, response_rx) = std::sync::mpsc::channel();
3060 self.tx
3061 .send(Event::ReloadHook {
3062 name,
3063 attach_point,
3064 wasm_bytes,
3065 response_tx,
3066 })
3067 .map_err(|_| SendError)?;
3068 response_rx.recv().map_err(|_| SendError)
3069 }
3070
3071 pub fn reload_hook_file(
3073 &self,
3074 name: String,
3075 attach_point: String,
3076 path: String,
3077 hook_type: String,
3078 ) -> Result<Result<(), String>, SendError> {
3079 let (response_tx, response_rx) = std::sync::mpsc::channel();
3080 self.tx
3081 .send(Event::ReloadHookFile {
3082 name,
3083 attach_point,
3084 path,
3085 hook_type,
3086 response_tx,
3087 })
3088 .map_err(|_| SendError)?;
3089 response_rx.recv().map_err(|_| SendError)
3090 }
3091
3092 pub fn reload_builtin_hook(
3094 &self,
3095 name: String,
3096 attach_point: String,
3097 builtin_id: String,
3098 ) -> Result<Result<(), String>, SendError> {
3099 let (response_tx, response_rx) = std::sync::mpsc::channel();
3100 self.tx
3101 .send(Event::ReloadBuiltinHook {
3102 name,
3103 attach_point,
3104 builtin_id,
3105 response_tx,
3106 })
3107 .map_err(|_| SendError)?;
3108 response_rx.recv().map_err(|_| SendError)
3109 }
3110
3111 pub fn set_hook_enabled(
3113 &self,
3114 name: String,
3115 attach_point: String,
3116 enabled: bool,
3117 ) -> Result<Result<(), String>, SendError> {
3118 let (response_tx, response_rx) = std::sync::mpsc::channel();
3119 self.tx
3120 .send(Event::SetHookEnabled {
3121 name,
3122 attach_point,
3123 enabled,
3124 response_tx,
3125 })
3126 .map_err(|_| SendError)?;
3127 response_rx.recv().map_err(|_| SendError)
3128 }
3129
3130 pub fn set_hook_priority(
3132 &self,
3133 name: String,
3134 attach_point: String,
3135 priority: i32,
3136 ) -> Result<Result<(), String>, SendError> {
3137 let (response_tx, response_rx) = std::sync::mpsc::channel();
3138 self.tx
3139 .send(Event::SetHookPriority {
3140 name,
3141 attach_point,
3142 priority,
3143 response_tx,
3144 })
3145 .map_err(|_| SendError)?;
3146 response_rx.recv().map_err(|_| SendError)
3147 }
3148
3149 pub fn list_hooks(&self) -> Result<Vec<crate::event::HookInfo>, SendError> {
3151 let (response_tx, response_rx) = std::sync::mpsc::channel();
3152 self.tx
3153 .send(Event::ListHooks { response_tx })
3154 .map_err(|_| SendError)?;
3155 response_rx.recv().map_err(|_| SendError)
3156 }
3157
3158 pub(crate) fn from_parts(
3161 tx: EventSender,
3162 driver_handle: thread::JoinHandle<()>,
3163 rpc_server: Option<crate::rpc::RpcServer>,
3164 tick_interval_ms: Arc<AtomicU64>,
3165 ) -> Self {
3166 RnsNode {
3167 tx,
3168 driver_handle: Some(driver_handle),
3169 verify_handle: None,
3170 verify_shutdown: Arc::new(AtomicBool::new(false)),
3171 persist_handle: None,
3172 persist_stop: None,
3173 rpc_server,
3174 tick_interval_ms,
3175 probe_server: None,
3176 known_destinations_path: None,
3177 transport_state_dir: None,
3178 ratchet_store: None,
3179 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
3180 }
3181 }
3182
3183 pub fn event_sender(&self) -> &EventSender {
3185 &self.tx
3186 }
3187
3188 pub fn set_tick_interval(&self, ms: u64) -> u64 {
3193 let clamped = ms.clamp(100, 10_000);
3194 if clamped != ms {
3195 log::warn!(
3196 "tick interval {}ms out of range, clamped to {}ms",
3197 ms,
3198 clamped
3199 );
3200 }
3201 self.tick_interval_ms.store(clamped, Ordering::Relaxed);
3202 clamped
3203 }
3204
3205 pub fn tick_interval(&self) -> u64 {
3207 self.tick_interval_ms.load(Ordering::Relaxed)
3208 }
3209
3210 pub fn shutdown(mut self) {
3212 self.shutdown_inner();
3213 }
3214
3215 fn shutdown_inner(&mut self) {
3216 if self.driver_handle.is_none()
3217 && self.verify_handle.is_none()
3218 && self.persist_handle.is_none()
3219 && self.rpc_server.is_none()
3220 {
3221 return;
3222 }
3223 if let Some(mut rpc) = self.rpc_server.take() {
3225 rpc.stop();
3226 }
3227 self.persist_known_destinations();
3228 self.persist_transport_state();
3229 if let Some(stop) = self.persist_stop.take() {
3230 let _ = stop.send(());
3231 }
3232 if let Some(handle) = self.persist_handle.take() {
3233 let _ = handle.join();
3234 }
3235 self.verify_shutdown.store(true, Ordering::Relaxed);
3236 let _ = self.tx.send(Event::Shutdown);
3237 if let Some(handle) = self.driver_handle.take() {
3238 let _ = handle.join();
3239 }
3240 if let Some(handle) = self.verify_handle.take() {
3241 let _ = handle.join();
3242 }
3243 }
3244}
3245
3246impl Drop for RnsNode {
3247 fn drop(&mut self) {
3248 self.shutdown_inner();
3249 }
3250}
3251
3252#[cfg(test)]
3253mod tests {
3254 use super::*;
3255 use crate::driver::IfacRuntimeConfig;
3256 use crate::storage::RatchetStore;
3257 use std::fs;
3258 use tempfile::tempdir;
3259
3260 struct NoopCallbacks;
3261
3262 impl Callbacks for NoopCallbacks {
3263 fn on_announce(&mut self, _: crate::destination::AnnouncedIdentity) {}
3264 fn on_path_updated(&mut self, _: rns_core::types::DestHash, _: u8) {}
3265 fn on_local_delivery(
3266 &mut self,
3267 _: rns_core::types::DestHash,
3268 _: Vec<u8>,
3269 _: rns_core::types::PacketHash,
3270 ) {
3271 }
3272 }
3273
3274 struct TestWriter;
3275
3276 impl crate::interface::Writer for TestWriter {
3277 fn send_frame(&mut self, _data: &[u8]) -> io::Result<()> {
3278 Ok(())
3279 }
3280 }
3281
3282 fn test_transport_config(transport_enabled: bool) -> TransportConfig {
3283 TransportConfig {
3284 transport_enabled,
3285 identity_hash: None,
3286 local_hops_delta: 0,
3287 prefer_shorter_path: false,
3288 max_paths_per_destination: 1,
3289 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
3290 packet_hashlist_allocation: rns_core::transport::types::PacketHashlistAllocation::Eager,
3291 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
3292 max_path_destinations: usize::MAX,
3293 max_tunnel_destinations_total: usize::MAX,
3294 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
3295 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
3296 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
3297 announce_sig_cache_enabled: true,
3298 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
3299 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
3300 announce_queue_max_entries: 256,
3301 announce_queue_max_interfaces: 1024,
3302 }
3303 }
3304
3305 fn test_interface_info(id: u64) -> rns_core::transport::types::InterfaceInfo {
3306 rns_core::transport::types::InterfaceInfo {
3307 id: rns_core::transport::types::InterfaceId(id),
3308 name: format!("test-{id}"),
3309 mode: rns_core::constants::MODE_FULL,
3310 gravity: 0,
3311 recursive_prs: false,
3312 announces_from_internal: true,
3313 announces_to_internal: None,
3314 out_capable: true,
3315 in_capable: true,
3316 bitrate: None,
3317 airtime_profile: None,
3318 announce_rate_target: None,
3319 announce_rate_grace: 0,
3320 announce_rate_penalty: 0.0,
3321 announce_cap: rns_core::constants::ANNOUNCE_CAP,
3322 is_local_client: false,
3323 wants_tunnel: false,
3324 tunnel_id: None,
3325 mtu: rns_core::constants::MTU as u32,
3326 ingress_control: rns_core::transport::types::IngressControlConfig::disabled(),
3327 ia_freq: 0.0,
3328 ip_freq: 0.0,
3329 op_freq: 0.0,
3330 op_samples: 0,
3331 started: 0.0,
3332 }
3333 }
3334
3335 #[test]
3336 fn static_interface_registration_applies_transport_announce_rate_defaults() {
3337 let (tx, rx) = crate::event::channel();
3338 let mut driver = Driver::new(
3339 test_transport_config(true),
3340 rx,
3341 tx.clone(),
3342 Box::new(NoopCallbacks),
3343 );
3344 driver.set_announce_rate_defaults(AnnounceRateDefaults {
3345 target: Some(7200.0),
3346 penalty: 15.0,
3347 grace: 7,
3348 });
3349
3350 let id = rns_core::transport::types::InterfaceId(7);
3351 register_started_interface(StartedInterface {
3352 driver: &mut driver,
3353 tx: &tx,
3354 queue_capacity: crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
3355 id,
3356 info: test_interface_info(id.0),
3357 writer: Box::new(TestWriter),
3358 interface_type_name: "TestInterface".to_string(),
3359 ifac_state: None,
3360 ifac_runtime: &IfacRuntimeConfig {
3361 netname: None,
3362 netkey: None,
3363 size: 16,
3364 },
3365 });
3366
3367 let info = &driver.interfaces[&id].info;
3368 assert_eq!(info.announce_rate_target, Some(7200.0));
3369 assert_eq!(info.announce_rate_penalty, 15.0);
3370 assert_eq!(info.announce_rate_grace, 7);
3371 }
3372
3373 #[test]
3374 fn static_interface_registration_applies_transport_ingress_control_defaults() {
3375 let (tx, rx) = crate::event::channel();
3376 let mut driver = Driver::new(
3377 test_transport_config(true),
3378 rx,
3379 tx.clone(),
3380 Box::new(NoopCallbacks),
3381 );
3382 let mut defaults = rns_core::transport::types::IngressControlConfig::enabled();
3383 defaults.burst_hold = 1.5;
3384 defaults.pr_burst_freq = 5.5;
3385 defaults.egress_enabled = true;
3386 defaults.egress_pr_freq = 9.5;
3387 driver.set_ingress_control_defaults(defaults);
3388
3389 let id = rns_core::transport::types::InterfaceId(8);
3390 register_started_interface(StartedInterface {
3391 driver: &mut driver,
3392 tx: &tx,
3393 queue_capacity: crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
3394 id,
3395 info: test_interface_info(id.0),
3396 writer: Box::new(TestWriter),
3397 interface_type_name: "TestInterface".to_string(),
3398 ifac_state: None,
3399 ifac_runtime: &IfacRuntimeConfig {
3400 netname: None,
3401 netkey: None,
3402 size: 16,
3403 },
3404 });
3405
3406 let ic = driver.interfaces[&id].info.ingress_control;
3407 assert_eq!(ic.burst_hold, 1.5);
3408 assert_eq!(ic.pr_burst_freq, 5.5);
3409 assert!(ic.egress_enabled);
3410 assert_eq!(ic.egress_pr_freq, 9.5);
3411 }
3412
3413 struct TestNodeRatchetStore {
3414 entry: storage::RatchetEntry,
3415 current_calls: std::sync::Mutex<Vec<[u8; 16]>>,
3416 }
3417
3418 impl storage::RatchetStore for TestNodeRatchetStore {
3419 fn remember(&self, _dest_hash: [u8; 16], _entry: storage::RatchetEntry) -> io::Result<()> {
3420 Ok(())
3421 }
3422
3423 fn current(
3424 &self,
3425 dest_hash: &[u8; 16],
3426 _now: f64,
3427 _expiry_secs: f64,
3428 ) -> io::Result<Option<storage::RatchetEntry>> {
3429 self.current_calls.lock().unwrap().push(*dest_hash);
3430 Ok(Some(self.entry))
3431 }
3432
3433 fn cleanup(
3434 &self,
3435 _known_destinations: &HashSet<[u8; 16]>,
3436 _now: f64,
3437 _expiry_secs: f64,
3438 ) -> io::Result<storage::RatchetCleanupStats> {
3439 Ok(Default::default())
3440 }
3441 }
3442
3443 #[test]
3444 fn send_packet_checks_ratchet_store_for_single_destinations() {
3445 let store = Arc::new(TestNodeRatchetStore {
3446 entry: storage::RatchetEntry {
3447 ratchet: [0x55; 32],
3448 received_at: time::now(),
3449 },
3450 current_calls: std::sync::Mutex::new(Vec::new()),
3451 });
3452 let ratchet_store: Arc<dyn storage::RatchetStore> = store.clone();
3453 let node = RnsNode::start(
3454 NodeConfig {
3455 ratchet_store: Some(ratchet_store),
3456 ..Default::default()
3457 },
3458 Box::new(NoopCallbacks),
3459 )
3460 .unwrap();
3461
3462 let mut rng = OsRng;
3463 let remote_identity = Identity::new(&mut rng);
3464 let public_key = remote_identity.get_public_key().unwrap();
3465 let announced = crate::destination::AnnouncedIdentity {
3466 dest_hash: rns_core::types::DestHash([0x22; 16]),
3467 identity_hash: rns_core::types::IdentityHash(*remote_identity.hash()),
3468 public_key,
3469 app_data: None,
3470 hops: 1,
3471 received_at: time::now(),
3472 receiving_interface: rns_core::transport::types::InterfaceId(0),
3473 rssi: Some(-100),
3474 snr: Some(10.5),
3475 };
3476 let dest = crate::destination::Destination::single_out("test", &["ratchet"], &announced);
3477
3478 node.send_packet(&dest, b"hello").unwrap();
3479 assert_eq!(
3480 store.current_calls.lock().unwrap().as_slice(),
3481 &[dest.hash.0]
3482 );
3483
3484 node.shutdown();
3485 }
3486
3487 #[test]
3488 fn single_payload_uses_stored_ratchet_key_material() {
3489 let ratchet_prv = rns_crypto::x25519::X25519PrivateKey::from_bytes(&[0x42; 32]);
3490 let ratchet_pub = ratchet_prv.public_key().public_bytes();
3491 let store = Arc::new(TestNodeRatchetStore {
3492 entry: storage::RatchetEntry {
3493 ratchet: ratchet_pub,
3494 received_at: time::now(),
3495 },
3496 current_calls: std::sync::Mutex::new(Vec::new()),
3497 });
3498 let ratchet_store: Arc<dyn storage::RatchetStore> = store.clone();
3499 let (tx, _rx) = crate::event::channel();
3500 let node = RnsNode {
3501 tx,
3502 driver_handle: None,
3503 verify_handle: None,
3504 verify_shutdown: Arc::new(AtomicBool::new(false)),
3505 persist_handle: None,
3506 persist_stop: None,
3507 rpc_server: None,
3508 tick_interval_ms: Arc::new(AtomicU64::new(1000)),
3509 probe_server: None,
3510 known_destinations_path: None,
3511 transport_state_dir: None,
3512 ratchet_store: Some(ratchet_store),
3513 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
3514 };
3515
3516 let mut rng = OsRng;
3517 let remote_identity = Identity::new(&mut rng);
3518 let public_key = remote_identity.get_public_key().unwrap();
3519 let announced = crate::destination::AnnouncedIdentity {
3520 dest_hash: rns_core::types::DestHash([0x33; 16]),
3521 identity_hash: rns_core::types::IdentityHash(*remote_identity.hash()),
3522 public_key,
3523 app_data: None,
3524 hops: 1,
3525 received_at: time::now(),
3526 receiving_interface: rns_core::transport::types::InterfaceId(0),
3527 rssi: Some(-100),
3528 snr: Some(10.5),
3529 };
3530 let dest = crate::destination::Destination::single_out("test", &["ratchet"], &announced);
3531 let payload = node
3532 .encrypt_single_payload(&dest, b"ratchet message")
3533 .unwrap();
3534
3535 assert_eq!(
3536 store.current_calls.lock().unwrap().as_slice(),
3537 &[dest.hash.0]
3538 );
3539 assert!(remote_identity.decrypt(&payload).is_err());
3540
3541 let peer_pub_bytes: [u8; 32] = payload[..32].try_into().unwrap();
3542 let peer_pub = rns_crypto::x25519::X25519PublicKey::from_bytes(&peer_pub_bytes);
3543 let shared_key = ratchet_prv.exchange(&peer_pub);
3544 let derived_key = rns_crypto::hkdf::hkdf(
3545 rns_crypto::identity::DERIVED_KEY_LENGTH,
3546 &shared_key,
3547 Some(remote_identity.hash()),
3548 None,
3549 )
3550 .unwrap();
3551 let token = rns_crypto::token::Token::new(&derived_key).unwrap();
3552 let plaintext = token.decrypt(&payload[32..]).unwrap();
3553 assert_eq!(plaintext, b"ratchet message");
3554 }
3555
3556 #[test]
3557 fn tcp_client_interface_is_not_discoverable_without_kiss_framing() {
3558 let mut params = std::collections::HashMap::new();
3559 params.insert("discoverable".to_string(), "yes".to_string());
3560 params.insert(
3561 "discovery_name".to_string(),
3562 "invalid-tcp-client".to_string(),
3563 );
3564 params.insert("reachable_on".to_string(), "example.com".to_string());
3565 params.insert("target_port".to_string(), "4242".to_string());
3566
3567 let discovery =
3568 super::extract_discovery_config("tcp-client", "TCPClientInterface", ¶ms);
3569
3570 assert!(
3571 discovery.is_none(),
3572 "TCPClientInterface discovery must be rejected unless KISS framing is supported"
3573 );
3574 }
3575
3576 #[test]
3577 fn discovery_config_parses_location_command() {
3578 let mut params = std::collections::HashMap::new();
3579 params.insert("discoverable".into(), "yes".into());
3580 params.insert("location_cmd".into(), "~/bin/reticulum-location".into());
3581
3582 let discovery =
3583 super::extract_discovery_config("public", "BackboneInterface", ¶ms).unwrap();
3584 assert_eq!(
3585 discovery.location_cmd.as_deref(),
3586 Some("~/bin/reticulum-location")
3587 );
3588 }
3589
3590 #[test]
3591 fn discovery_config_parses_valid_operator_lxmf_address() {
3592 let mut params = std::collections::HashMap::new();
3593 params.insert("discoverable".into(), "yes".into());
3594 params.insert("discovery_lxmf_address".into(), "ab".repeat(16));
3595
3596 let discovery =
3597 super::extract_discovery_config("public", "BackboneInterface", ¶ms).unwrap();
3598
3599 assert_eq!(discovery.operator_lxmf_address, Some([0xab; 16]));
3600 }
3601
3602 #[test]
3603 fn discovery_config_ignores_invalid_operator_lxmf_addresses() {
3604 for address in ["ab".repeat(15), "zz".repeat(16)] {
3605 let mut params = std::collections::HashMap::new();
3606 params.insert("discoverable".into(), "yes".into());
3607 params.insert("discovery_lxmf_address".into(), address);
3608
3609 let discovery =
3610 super::extract_discovery_config("public", "BackboneInterface", ¶ms).unwrap();
3611
3612 assert_eq!(discovery.operator_lxmf_address, None);
3613 }
3614 }
3615
3616 #[test]
3617 fn ingress_control_config_defaults_by_interface_type() {
3618 let params = std::collections::HashMap::new();
3619
3620 let tcp = super::parse_ingress_control_config("TCPServerInterface", ¶ms).unwrap();
3621 assert!(tcp.enabled);
3622 assert_eq!(
3623 tcp.max_held_announces,
3624 rns_core::constants::IC_MAX_HELD_ANNOUNCES
3625 );
3626 assert_eq!(tcp.burst_hold, rns_core::constants::IC_BURST_HOLD);
3627
3628 let pipe = super::parse_ingress_control_config("PipeInterface", ¶ms).unwrap();
3629 assert!(!pipe.enabled);
3630 assert_eq!(
3631 pipe.held_release_interval,
3632 rns_core::constants::IC_HELD_RELEASE_INTERVAL
3633 );
3634 }
3635
3636 #[test]
3637 fn ingress_control_config_parses_python_ic_keys() {
3638 let mut params = std::collections::HashMap::new();
3639 params.insert("ingress_control".to_string(), "No".to_string());
3640 params.insert("ic_max_held_announces".to_string(), "17".to_string());
3641 params.insert("ic_burst_hold".to_string(), "1.5".to_string());
3642 params.insert("ic_burst_freq_new".to_string(), "2.5".to_string());
3643 params.insert("ic_burst_freq".to_string(), "3.5".to_string());
3644 params.insert("ic_pr_burst_freq_new".to_string(), "3.25".to_string());
3645 params.insert("ic_pr_burst_freq".to_string(), "8.25".to_string());
3646 params.insert("egress_control".to_string(), "Yes".to_string());
3647 params.insert("ec_pr_freq".to_string(), "5.25".to_string());
3648 params.insert("ic_new_time".to_string(), "4.5".to_string());
3649 params.insert("ic_burst_penalty".to_string(), "5.5".to_string());
3650 params.insert("ic_held_release_interval".to_string(), "6.5".to_string());
3651
3652 let config = super::parse_ingress_control_config("TCPServerInterface", ¶ms).unwrap();
3653
3654 assert!(!config.enabled);
3655 assert_eq!(config.max_held_announces, 17);
3656 assert_eq!(config.burst_hold, 1.5);
3657 assert_eq!(config.burst_freq_new, 2.5);
3658 assert_eq!(config.burst_freq, 3.5);
3659 assert_eq!(config.pr_burst_freq_new, 3.25);
3660 assert_eq!(config.pr_burst_freq, 8.25);
3661 assert!(config.egress_enabled);
3662 assert_eq!(config.egress_pr_freq, 5.25);
3663 assert_eq!(config.new_time, 4.5);
3664 assert_eq!(config.burst_penalty, 5.5);
3665 assert_eq!(config.held_release_interval, 6.5);
3666 }
3667
3668 #[test]
3669 fn ingress_control_config_rejects_invalid_values() {
3670 let mut params = std::collections::HashMap::new();
3671 params.insert("ic_burst_hold".to_string(), "-1".to_string());
3672
3673 let err = super::parse_ingress_control_config("TCPServerInterface", ¶ms).unwrap_err();
3674
3675 assert!(err.contains("ic_burst_hold"));
3676 }
3677
3678 #[test]
3679 fn discovery_stamp_default_is_16_but_explicit_override_is_preserved() {
3680 let mut params = std::collections::HashMap::new();
3681 params.insert("discoverable".to_string(), "yes".to_string());
3682 let defaults =
3683 super::extract_discovery_config("Discovery Test", "BackboneInterface", ¶ms)
3684 .unwrap();
3685 assert_eq!(defaults.stamp_value, 16);
3686
3687 params.insert("discovery_stamp_value".to_string(), "14".to_string());
3688 let overridden =
3689 super::extract_discovery_config("Discovery Test", "BackboneInterface", ¶ms)
3690 .unwrap();
3691 assert_eq!(overridden.stamp_value, 14);
3692 }
3693
3694 #[test]
3695 fn start_and_shutdown() {
3696 let node = RnsNode::start(
3697 NodeConfig {
3698 panic_on_interface_error: false,
3699 transport_enabled: false,
3700 static_transport_identity: false,
3701 local_hops_delta: false,
3702 identity: None,
3703 interfaces: vec![],
3704 share_instance: false,
3705 instance_name: "default".into(),
3706 shared_instance_port: 37428,
3707 rpc_port: 0,
3708 cache_dir: None,
3709 ratchet_store: None,
3710 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
3711 management: Default::default(),
3712 probe_port: None,
3713 probe_addrs: vec![],
3714 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
3715 direct_connect_policy: Default::default(),
3716 device: None,
3717 underlay_mark: None,
3718 hooks: Vec::new(),
3719 discover_interfaces: false,
3720 autoconnect_interface_mode: None,
3721 autoconnect_interface_gravity: 0,
3722 autoconnect_announces_to_internal: false,
3723 discovery_required_value: None,
3724 respond_to_probes: false,
3725 prefer_shorter_path: false,
3726 max_paths_per_destination: 1,
3727 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
3728 packet_hashlist_allocation:
3729 rns_core::transport::types::PacketHashlistAllocation::Eager,
3730 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
3731 max_path_destinations: usize::MAX,
3732 max_tunnel_destinations_total: usize::MAX,
3733 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
3734 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
3735 announce_table_ttl: Duration::from_secs(
3736 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
3737 ),
3738 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
3739 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
3740 interface_writer_queue_capacity:
3741 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
3742 announce_rate_defaults: AnnounceRateDefaults::default(),
3743 ingress_control_defaults: rns_core::transport::types::IngressControlConfig::enabled(
3744 ),
3745 #[cfg(feature = "iface-backbone")]
3746 backbone_peer_pool: None,
3747 announce_sig_cache_enabled: true,
3748 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
3749 announce_sig_cache_ttl: Duration::from_secs(
3750 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
3751 ),
3752 registry: None,
3753 #[cfg(feature = "hooks")]
3754 provider_bridge: None,
3755 },
3756 Box::new(NoopCallbacks),
3757 )
3758 .unwrap();
3759 node.shutdown();
3760 }
3761
3762 #[test]
3763 fn known_destinations_persist_across_restart() {
3764 let dir = tempdir().unwrap();
3765 let dest_hash = [0x91; 16];
3766 let identity = Identity::new(&mut OsRng);
3767 let last_used_at = 77.0;
3768 let receiving_interface = rns_core::transport::types::InterfaceId(42);
3769
3770 let node = RnsNode::from_config(Some(dir.path()), Box::new(NoopCallbacks)).unwrap();
3771 let (response_tx, response_rx) = std::sync::mpsc::channel();
3772 node.event_sender()
3773 .send(crate::event::Event::Query(
3774 QueryRequest::RestoreKnownDestination(crate::event::KnownDestinationEntry {
3775 dest_hash,
3776 identity_hash: *identity.hash(),
3777 public_key: identity.get_public_key().unwrap(),
3778 app_data: Some(b"persisted".to_vec()),
3779 hops: 2,
3780 received_at: 55.0,
3781 receiving_interface,
3782 was_used: true,
3783 last_used_at: Some(last_used_at),
3784 retained: true,
3785 }),
3786 response_tx,
3787 ))
3788 .unwrap();
3789 assert!(matches!(
3790 response_rx.recv().unwrap(),
3791 QueryResponse::RestoreKnownDestination(true)
3792 ));
3793 node.shutdown();
3794
3795 let restarted = RnsNode::from_config(Some(dir.path()), Box::new(NoopCallbacks)).unwrap();
3796 let entries = restarted.known_destinations().unwrap();
3797 let entry = entries
3798 .iter()
3799 .find(|entry| entry.dest_hash == dest_hash)
3800 .expect("reloaded destination should appear in lifecycle listing");
3801 assert!(entry.retained);
3802 assert!(entry.was_used);
3803 assert_eq!(entry.hops, 2);
3804 assert_eq!(entry.receiving_interface, receiving_interface);
3805 assert_eq!(entry.last_used_at, Some(last_used_at));
3806
3807 let recalled = restarted
3808 .recall_identity(&rns_core::types::DestHash(dest_hash))
3809 .unwrap()
3810 .expect("known destination should reload from storage");
3811 assert_eq!(recalled.identity_hash.0, *identity.hash());
3812 assert_eq!(recalled.app_data, Some(b"persisted".to_vec()));
3813 restarted.shutdown();
3814 }
3815
3816 #[test]
3817 fn transport_paths_hashes_and_detached_tunnels_restore_from_upstream_files() {
3818 use rns_core::packet::{PacketFlags, RawPacket};
3819 use rns_core::transport::persistence::{
3820 PersistedPath, PersistedTunnel, TransportStateSnapshot,
3821 };
3822
3823 let dir = tempdir().unwrap();
3824 fs::write(
3825 dir.path().join("config"),
3826 r#"[reticulum]
3827enable_transport = True
3828share_instance = False
3829
3830[interfaces]
3831 [[Persistence Test]]
3832 type = UDPInterface
3833 forward_ip = 127.0.0.1
3834 forward_port = 9
3835 interface_mode = full
3836"#,
3837 )
3838 .unwrap();
3839 let paths = storage::ensure_storage_dirs(dir.path()).unwrap();
3840 let destination_hash = [0x31; 16];
3841 let packet = RawPacket::pack(
3842 PacketFlags {
3843 header_type: rns_core::constants::HEADER_1,
3844 context_flag: rns_core::constants::FLAG_UNSET,
3845 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
3846 destination_type: rns_core::constants::DESTINATION_SINGLE,
3847 packet_type: rns_core::constants::PACKET_TYPE_ANNOUNCE,
3848 },
3849 0,
3850 &destination_hash,
3851 None,
3852 rns_core::constants::CONTEXT_NONE,
3853 &[0xA5; 32],
3854 )
3855 .unwrap();
3856 crate::announce_cache::AnnounceCache::new(paths.cache.join("announces"))
3857 .store(&packet.packet_hash, &packet.raw, None)
3858 .unwrap();
3859 let now = time::now();
3860 let route = PersistedPath {
3861 destination_hash,
3862 timestamp: now,
3863 received_from: [0x41; 16],
3864 hops: 2,
3865 expires: now + 600.0,
3866 random_blobs: vec![[0x51; 10]],
3867 interface_hash: Some(rns_core::hash::full_hash(b"Persistence Test")),
3868 packet_hash: packet.packet_hash,
3869 };
3870 storage::save_transport_state(
3871 &TransportStateSnapshot {
3872 packet_hashes: vec![[0x61; 32], [0x62; 32]],
3873 paths: vec![route.clone()],
3874 tunnels: vec![PersistedTunnel {
3875 tunnel_id: [0x71; 32],
3876 interface_hash: None,
3877 paths: vec![PersistedPath {
3878 interface_hash: None,
3879 ..route
3880 }],
3881 expires: now + 1_200.0,
3882 }],
3883 },
3884 &paths.storage,
3885 false,
3886 )
3887 .unwrap();
3888
3889 let node = RnsNode::from_config(Some(dir.path()), Box::new(NoopCallbacks)).unwrap();
3890 let QueryResponse::TransportStateSnapshot(restored) =
3891 node.query(QueryRequest::TransportStateSnapshot).unwrap()
3892 else {
3893 panic!("unexpected transport snapshot response");
3894 };
3895 assert_eq!(restored.packet_hashes, vec![[0x61; 32], [0x62; 32]]);
3896 assert_eq!(restored.paths.len(), 1);
3897 assert_eq!(restored.paths[0].destination_hash, destination_hash);
3898 assert_eq!(restored.paths[0].received_from, [0x41; 16]);
3899 assert_eq!(restored.tunnels.len(), 1);
3900 assert_eq!(restored.tunnels[0].tunnel_id, [0x71; 32]);
3901 assert_eq!(restored.tunnels[0].interface_hash, None);
3902 node.shutdown();
3903 }
3904
3905 #[test]
3906 fn from_config_cleans_persistent_ratchets_after_loading_known_destinations() {
3907 fn hex(bytes: &[u8]) -> String {
3908 bytes.iter().map(|byte| format!("{:02x}", byte)).collect()
3909 }
3910
3911 let dir = tempdir().unwrap();
3912 fs::write(
3913 dir.path().join("config"),
3914 "[reticulum]\nenable_transport = False\nshare_instance = False\nratchet_expiry = 300\n",
3915 )
3916 .unwrap();
3917 let paths = storage::ensure_storage_dirs(dir.path()).unwrap();
3918 let known_live = [0x01; 16];
3919 let known_expired = [0x02; 16];
3920 let unknown = [0x03; 16];
3921 let known_corrupt = [0x04; 16];
3922 let now = time::now();
3923
3924 let identity = Identity::new(&mut OsRng);
3925 let known = std::collections::HashMap::from([
3926 (
3927 known_live,
3928 storage::KnownDestination {
3929 identity_hash: *identity.hash(),
3930 public_key: identity.get_public_key().unwrap(),
3931 app_data: None,
3932 hops: 1,
3933 received_at: now,
3934 receiving_interface: 0,
3935 was_used: false,
3936 last_used_at: None,
3937 retained: false,
3938 },
3939 ),
3940 (
3941 known_expired,
3942 storage::KnownDestination {
3943 identity_hash: *identity.hash(),
3944 public_key: identity.get_public_key().unwrap(),
3945 app_data: None,
3946 hops: 1,
3947 received_at: now,
3948 receiving_interface: 0,
3949 was_used: false,
3950 last_used_at: None,
3951 retained: false,
3952 },
3953 ),
3954 (
3955 known_corrupt,
3956 storage::KnownDestination {
3957 identity_hash: *identity.hash(),
3958 public_key: identity.get_public_key().unwrap(),
3959 app_data: None,
3960 hops: 1,
3961 received_at: now,
3962 receiving_interface: 0,
3963 was_used: false,
3964 last_used_at: None,
3965 retained: false,
3966 },
3967 ),
3968 ]);
3969 storage::save_known_destinations(&known, &paths.storage.join("known_destinations"))
3970 .unwrap();
3971
3972 let store = storage::FsRatchetStore::new(paths.ratchets.clone());
3973 store
3974 .remember(
3975 known_live,
3976 storage::RatchetEntry {
3977 ratchet: [0x11; 32],
3978 received_at: now,
3979 },
3980 )
3981 .unwrap();
3982 store
3983 .remember(
3984 known_expired,
3985 storage::RatchetEntry {
3986 ratchet: [0x22; 32],
3987 received_at: now - 1000.0,
3988 },
3989 )
3990 .unwrap();
3991 store
3992 .remember(
3993 unknown,
3994 storage::RatchetEntry {
3995 ratchet: [0x33; 32],
3996 received_at: now,
3997 },
3998 )
3999 .unwrap();
4000 fs::write(paths.ratchets.join(hex(&known_corrupt)), b"not msgpack").unwrap();
4001 fs::write(paths.ratchets.join("0102.out"), b"temp").unwrap();
4002
4003 let node = RnsNode::from_config(Some(dir.path()), Box::new(NoopCallbacks)).unwrap();
4004
4005 assert!(paths.ratchets.join(hex(&known_live)).exists());
4006 assert!(!paths.ratchets.join(hex(&known_expired)).exists());
4007 assert!(!paths.ratchets.join(hex(&unknown)).exists());
4008 assert!(!paths.ratchets.join(hex(&known_corrupt)).exists());
4009 assert!(!paths.ratchets.join("0102.out").exists());
4010
4011 node.shutdown();
4012 }
4013
4014 #[test]
4015 fn start_with_identity() {
4016 let identity = Identity::new(&mut OsRng);
4017 let hash = *identity.hash();
4018 let node = RnsNode::start(
4019 NodeConfig {
4020 panic_on_interface_error: false,
4021 transport_enabled: true,
4022 static_transport_identity: false,
4023 local_hops_delta: false,
4024 identity: Some(identity),
4025 interfaces: vec![],
4026 share_instance: false,
4027 instance_name: "default".into(),
4028 shared_instance_port: 37428,
4029 rpc_port: 0,
4030 cache_dir: None,
4031 ratchet_store: None,
4032 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
4033 management: Default::default(),
4034 probe_port: None,
4035 probe_addrs: vec![],
4036 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
4037 direct_connect_policy: Default::default(),
4038 device: None,
4039 underlay_mark: None,
4040 hooks: Vec::new(),
4041 discover_interfaces: false,
4042 autoconnect_interface_mode: None,
4043 autoconnect_interface_gravity: 0,
4044 autoconnect_announces_to_internal: false,
4045 discovery_required_value: None,
4046 respond_to_probes: false,
4047 prefer_shorter_path: false,
4048 max_paths_per_destination: 1,
4049 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
4050 packet_hashlist_allocation:
4051 rns_core::transport::types::PacketHashlistAllocation::Eager,
4052 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
4053 max_path_destinations: usize::MAX,
4054 max_tunnel_destinations_total: usize::MAX,
4055 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
4056 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
4057 announce_table_ttl: Duration::from_secs(
4058 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
4059 ),
4060 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
4061 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
4062 interface_writer_queue_capacity:
4063 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
4064 announce_rate_defaults: AnnounceRateDefaults::default(),
4065 ingress_control_defaults: rns_core::transport::types::IngressControlConfig::enabled(
4066 ),
4067 #[cfg(feature = "iface-backbone")]
4068 backbone_peer_pool: None,
4069 announce_sig_cache_enabled: true,
4070 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
4071 announce_sig_cache_ttl: Duration::from_secs(
4072 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
4073 ),
4074 registry: None,
4075 #[cfg(feature = "hooks")]
4076 provider_bridge: None,
4077 },
4078 Box::new(NoopCallbacks),
4079 )
4080 .unwrap();
4081 let _ = hash;
4083 node.shutdown();
4084 }
4085
4086 #[test]
4087 fn start_generates_identity() {
4088 let node = RnsNode::start(
4089 NodeConfig {
4090 panic_on_interface_error: false,
4091 transport_enabled: false,
4092 static_transport_identity: false,
4093 local_hops_delta: false,
4094 identity: None,
4095 interfaces: vec![],
4096 share_instance: false,
4097 instance_name: "default".into(),
4098 shared_instance_port: 37428,
4099 rpc_port: 0,
4100 cache_dir: None,
4101 ratchet_store: None,
4102 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
4103 management: Default::default(),
4104 probe_port: None,
4105 probe_addrs: vec![],
4106 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
4107 direct_connect_policy: Default::default(),
4108 device: None,
4109 underlay_mark: None,
4110 hooks: Vec::new(),
4111 discover_interfaces: false,
4112 autoconnect_interface_mode: None,
4113 autoconnect_interface_gravity: 0,
4114 autoconnect_announces_to_internal: false,
4115 discovery_required_value: None,
4116 respond_to_probes: false,
4117 prefer_shorter_path: false,
4118 max_paths_per_destination: 1,
4119 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
4120 packet_hashlist_allocation:
4121 rns_core::transport::types::PacketHashlistAllocation::Eager,
4122 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
4123 max_path_destinations: usize::MAX,
4124 max_tunnel_destinations_total: usize::MAX,
4125 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
4126 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
4127 announce_table_ttl: Duration::from_secs(
4128 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
4129 ),
4130 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
4131 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
4132 interface_writer_queue_capacity:
4133 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
4134 announce_rate_defaults: AnnounceRateDefaults::default(),
4135 ingress_control_defaults: rns_core::transport::types::IngressControlConfig::enabled(
4136 ),
4137 #[cfg(feature = "iface-backbone")]
4138 backbone_peer_pool: None,
4139 announce_sig_cache_enabled: true,
4140 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
4141 announce_sig_cache_ttl: Duration::from_secs(
4142 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
4143 ),
4144 registry: None,
4145 #[cfg(feature = "hooks")]
4146 provider_bridge: None,
4147 },
4148 Box::new(NoopCallbacks),
4149 )
4150 .unwrap();
4151 node.shutdown();
4153 }
4154
4155 #[test]
4156 fn from_config_creates_identity() {
4157 let dir = std::env::temp_dir().join(format!("rns-test-fc-{}", std::process::id()));
4158 let _ = fs::remove_dir_all(&dir);
4159 fs::create_dir_all(&dir).unwrap();
4160
4161 fs::write(
4163 dir.join("config"),
4164 "[reticulum]\nenable_transport = False\n",
4165 )
4166 .unwrap();
4167
4168 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
4169
4170 assert!(dir.join("storage/identities/identity").exists());
4172
4173 node.shutdown();
4174 let _ = fs::remove_dir_all(&dir);
4175 }
4176
4177 #[test]
4178 fn private_config_keeps_state_out_of_config_directory() {
4179 let root = tempfile::tempdir().unwrap();
4180 let config_dir = root.path().join("config");
4181 let state_dir = root.path().join("state");
4182 fs::create_dir_all(&config_dir).unwrap();
4183 fs::write(
4184 config_dir.join("config"),
4185 "[reticulum]\nenable_transport = False\nshare_instance = Yes\n",
4186 )
4187 .unwrap();
4188 let node =
4189 RnsNode::from_private_config(&config_dir, &state_dir, None, Box::new(NoopCallbacks))
4190 .unwrap();
4191 assert!(state_dir.join("storage/identities/identity").exists());
4192 assert!(!config_dir.join("storage").exists());
4193 node.shutdown();
4194 }
4195
4196 #[test]
4197 fn from_config_loads_identity() {
4198 let dir = std::env::temp_dir().join(format!("rns-test-fl-{}", std::process::id()));
4199 let _ = fs::remove_dir_all(&dir);
4200 fs::create_dir_all(dir.join("storage/identities")).unwrap();
4201
4202 let identity = Identity::new(&mut OsRng);
4204 let hash = *identity.hash();
4205 storage::save_identity(&identity, &dir.join("storage/identities/identity")).unwrap();
4206
4207 fs::write(
4208 dir.join("config"),
4209 "[reticulum]\nenable_transport = False\n",
4210 )
4211 .unwrap();
4212
4213 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
4214
4215 let loaded = storage::load_identity(&dir.join("storage/identities/identity")).unwrap();
4217 assert_eq!(*loaded.hash(), hash);
4218
4219 node.shutdown();
4220 let _ = fs::remove_dir_all(&dir);
4221 }
4222
4223 #[test]
4224 fn from_config_tcp_server() {
4225 let dir = std::env::temp_dir().join(format!("rns-test-fts-{}", std::process::id()));
4226 let _ = fs::remove_dir_all(&dir);
4227 fs::create_dir_all(&dir).unwrap();
4228
4229 let port = crate::test_support::port();
4230
4231 let config = format!(
4232 r#"
4233[reticulum]
4234enable_transport = False
4235
4236[interfaces]
4237 [[Test TCP Server]]
4238 type = TCPServerInterface
4239 listen_ip = 127.0.0.1
4240 listen_port = {}
4241"#,
4242 port
4243 );
4244
4245 fs::write(dir.join("config"), config).unwrap();
4246
4247 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
4248
4249 thread::sleep(Duration::from_millis(100));
4251
4252 let _client = std::net::TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
4254
4255 node.shutdown();
4256 let _ = fs::remove_dir_all(&dir);
4257 }
4258
4259 #[test]
4260 fn from_config_starts_rpc_when_share_instance_enabled() {
4261 let dir = std::env::temp_dir().join(format!("rns-test-rpc-{}", std::process::id()));
4262 let _ = fs::remove_dir_all(&dir);
4263 fs::create_dir_all(&dir).unwrap();
4264
4265 let rpc_port = crate::test_support::port();
4266
4267 let config = format!(
4268 r#"
4269[reticulum]
4270enable_transport = False
4271share_instance = Yes
4272instance_control_port = {}
4273
4274[interfaces]
4275"#,
4276 rpc_port
4277 );
4278
4279 fs::write(dir.join("config"), config).unwrap();
4280
4281 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
4282
4283 thread::sleep(Duration::from_millis(100));
4284
4285 let _client = std::net::TcpStream::connect(format!("127.0.0.1:{}", rpc_port)).unwrap();
4286
4287 node.shutdown();
4288 let _ = fs::remove_dir_all(&dir);
4289 }
4290
4291 #[test]
4292 fn from_config_starts_rpc_when_transport_enabled() {
4293 let dir =
4294 std::env::temp_dir().join(format!("rns-test-rpc-transport-{}", std::process::id()));
4295 let _ = fs::remove_dir_all(&dir);
4296 fs::create_dir_all(&dir).unwrap();
4297
4298 let rpc_port = crate::test_support::port();
4299
4300 let config = format!(
4301 r#"
4302[reticulum]
4303enable_transport = True
4304share_instance = Yes
4305instance_control_port = {}
4306
4307[interfaces]
4308"#,
4309 rpc_port
4310 );
4311
4312 fs::write(dir.join("config"), config).unwrap();
4313
4314 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
4315
4316 thread::sleep(Duration::from_millis(100));
4317
4318 let _client = std::net::TcpStream::connect(format!("127.0.0.1:{}", rpc_port)).unwrap();
4319
4320 node.shutdown();
4321 let _ = fs::remove_dir_all(&dir);
4322 }
4323
4324 #[test]
4325 fn from_config_starts_rpc_when_tcp_client_is_unreachable() {
4326 let dir =
4327 std::env::temp_dir().join(format!("rns-test-rpc-unreachable-{}", std::process::id()));
4328 let _ = fs::remove_dir_all(&dir);
4329 fs::create_dir_all(&dir).unwrap();
4330
4331 let rpc_port = crate::test_support::port();
4332 let unreachable_port = crate::test_support::port();
4333
4334 let config = format!(
4335 r#"
4336[reticulum]
4337enable_transport = True
4338share_instance = Yes
4339instance_control_port = {}
4340
4341[interfaces]
4342 [[Unreachable Upstream]]
4343 type = TCPClientInterface
4344 target_host = 127.0.0.1
4345 target_port = {}
4346"#,
4347 rpc_port, unreachable_port
4348 );
4349
4350 fs::write(dir.join("config"), config).unwrap();
4351
4352 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
4353
4354 thread::sleep(Duration::from_millis(100));
4355
4356 let _client = std::net::TcpStream::connect(format!("127.0.0.1:{}", rpc_port)).unwrap();
4357
4358 node.shutdown();
4359 let _ = fs::remove_dir_all(&dir);
4360 }
4361
4362 #[test]
4363 fn test_parse_interface_mode() {
4364 use rns_core::constants::*;
4365
4366 assert_eq!(parse_interface_mode("full"), MODE_FULL);
4367 assert_eq!(parse_interface_mode("Full"), MODE_FULL);
4368 assert_eq!(parse_interface_mode("access_point"), MODE_ACCESS_POINT);
4369 assert_eq!(parse_interface_mode("accesspoint"), MODE_ACCESS_POINT);
4370 assert_eq!(parse_interface_mode("ap"), MODE_ACCESS_POINT);
4371 assert_eq!(parse_interface_mode("AP"), MODE_ACCESS_POINT);
4372 assert_eq!(parse_interface_mode("pointtopoint"), MODE_POINT_TO_POINT);
4373 assert_eq!(parse_interface_mode("ptp"), MODE_POINT_TO_POINT);
4374 assert_eq!(parse_interface_mode("roaming"), MODE_ROAMING);
4375 assert_eq!(parse_interface_mode("boundary"), MODE_BOUNDARY);
4376 assert_eq!(parse_interface_mode("gateway"), MODE_GATEWAY);
4377 assert_eq!(parse_interface_mode("gw"), MODE_GATEWAY);
4378 assert_eq!(parse_interface_mode("internal"), MODE_INTERNAL);
4379 assert_eq!(parse_interface_mode("Internal"), MODE_INTERNAL);
4380 assert_eq!(parse_interface_mode("invalid"), MODE_FULL);
4382 }
4383
4384 #[test]
4385 fn discovery_mode_normalization_matrix() {
4386 use rns_core::constants::*;
4387
4388 let interface_types = [
4389 "RNodeInterface",
4390 "RNodeMultiInterface",
4391 "BackboneInterface",
4392 "TCPServerInterface",
4393 "AutoInterface",
4394 ];
4395 let modes = [
4396 MODE_FULL,
4397 MODE_ACCESS_POINT,
4398 MODE_POINT_TO_POINT,
4399 MODE_ROAMING,
4400 MODE_BOUNDARY,
4401 MODE_GATEWAY,
4402 MODE_INTERNAL,
4403 ];
4404
4405 for interface_type in interface_types {
4406 for configured_mode in modes {
4407 for discoverable in [false, true] {
4408 for ignore_warnings in [false, true] {
4409 let expected = if !discoverable
4410 || ignore_warnings
4411 || matches!(
4412 configured_mode,
4413 MODE_ACCESS_POINT | MODE_GATEWAY | MODE_INTERNAL
4414 ) {
4415 configured_mode
4416 } else if matches!(interface_type, "RNodeInterface" | "RNodeMultiInterface")
4417 {
4418 MODE_ACCESS_POINT
4419 } else {
4420 MODE_GATEWAY
4421 };
4422 assert_eq!(
4423 normalize_discovery_mode(
4424 interface_type,
4425 configured_mode,
4426 discoverable,
4427 ignore_warnings,
4428 ),
4429 expected,
4430 "type={interface_type}, mode={configured_mode}, discoverable={discoverable}, ignore={ignore_warnings}",
4431 );
4432 }
4433 }
4434 }
4435 }
4436 }
4437
4438 #[test]
4439 fn to_node_config_serial() {
4440 let dir = std::env::temp_dir().join(format!("rns-test-serial-{}", std::process::id()));
4444 let _ = fs::remove_dir_all(&dir);
4445 fs::create_dir_all(&dir).unwrap();
4446
4447 let config = r#"
4448[reticulum]
4449enable_transport = False
4450
4451[interfaces]
4452 [[Test Serial Port]]
4453 type = SerialInterface
4454 port = /dev/nonexistent_rns_test_serial
4455 speed = 115200
4456 databits = 8
4457 parity = E
4458 stopbits = 1
4459 interface_mode = ptp
4460 networkname = testnet
4461"#;
4462 fs::write(dir.join("config"), config).unwrap();
4463
4464 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks))
4466 .expect("Config should parse; interface failure is non-fatal");
4467 node.shutdown();
4468
4469 let _ = fs::remove_dir_all(&dir);
4470 }
4471
4472 #[test]
4473 fn to_node_config_kiss() {
4474 let dir = std::env::temp_dir().join(format!("rns-test-kiss-{}", std::process::id()));
4476 let _ = fs::remove_dir_all(&dir);
4477 fs::create_dir_all(&dir).unwrap();
4478
4479 let config = r#"
4480[reticulum]
4481enable_transport = False
4482
4483[interfaces]
4484 [[Test KISS TNC]]
4485 type = KISSInterface
4486 port = /dev/nonexistent_rns_test_kiss
4487 speed = 9600
4488 preamble = 500
4489 txtail = 30
4490 persistence = 128
4491 slottime = 40
4492 flow_control = True
4493 id_interval = 600
4494 id_callsign = TEST0
4495 interface_mode = full
4496 passphrase = secretkey
4497"#;
4498 fs::write(dir.join("config"), config).unwrap();
4499
4500 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks))
4502 .expect("Config should parse; interface failure is non-fatal");
4503 node.shutdown();
4504
4505 let _ = fs::remove_dir_all(&dir);
4506 }
4507
4508 #[test]
4509 fn config_parser_accepts_recursive_prs_interface_option() {
4510 let config = r#"
4511[reticulum]
4512enable_transport = True
4513
4514[interfaces]
4515 [[Recursive Client]]
4516 type = TCPClientInterface
4517 target_host = 127.0.0.1
4518 target_port = 4242
4519 interface_mode = full
4520 recursive_prs = yes
4521
4522 [[Default Client]]
4523 type = TCPClientInterface
4524 target_host = 127.0.0.1
4525 target_port = 4243
4526 interface_mode = gateway
4527"#;
4528
4529 let parsed = config::parse(config).unwrap();
4530
4531 assert_eq!(parsed.interfaces.len(), 2);
4532 assert_eq!(parsed.interfaces[0].name, "Recursive Client");
4533 assert_eq!(parsed.interfaces[0].mode, "full");
4534 assert_eq!(
4535 parsed.interfaces[0]
4536 .params
4537 .get("recursive_prs")
4538 .map(String::as_str),
4539 Some("yes")
4540 );
4541 assert_eq!(parsed.interfaces[1].name, "Default Client");
4542 assert_eq!(parsed.interfaces[1].mode, "gateway");
4543 assert!(!parsed.interfaces[1].params.contains_key("recursive_prs"));
4544 }
4545
4546 #[test]
4547 fn config_parser_accepts_announces_from_internal_interface_option() {
4548 let config = r#"
4549[reticulum]
4550enable_transport = True
4551
4552[interfaces]
4553 [[Filtered Client]]
4554 type = TCPClientInterface
4555 target_host = 127.0.0.1
4556 target_port = 4242
4557 interface_mode = gateway
4558 announces_from_internal = no
4559
4560 [[Default Client]]
4561 type = TCPClientInterface
4562 target_host = 127.0.0.1
4563 target_port = 4243
4564 interface_mode = full
4565"#;
4566
4567 let parsed = config::parse(config).unwrap();
4568
4569 assert_eq!(parsed.interfaces.len(), 2);
4570 assert_eq!(parsed.interfaces[0].name, "Filtered Client");
4571 assert_eq!(parsed.interfaces[0].mode, "gateway");
4572 assert_eq!(
4573 parsed.interfaces[0]
4574 .params
4575 .get("announces_from_internal")
4576 .map(String::as_str),
4577 Some("no")
4578 );
4579 assert_eq!(parsed.interfaces[1].name, "Default Client");
4580 assert_eq!(parsed.interfaces[1].mode, "full");
4581 assert!(!parsed.interfaces[1]
4582 .params
4583 .contains_key("announces_from_internal"));
4584 }
4585
4586 #[test]
4587 fn config_parser_preserves_announces_to_internal_tristate() {
4588 let config = r#"
4589[interfaces]
4590 [[Allowed Boundary]]
4591 type = TCPClientInterface
4592 target_host = 127.0.0.1
4593 target_port = 4242
4594 announces_to_internal = yes
4595
4596 [[Blocked Boundary]]
4597 type = TCPClientInterface
4598 target_host = 127.0.0.1
4599 target_port = 4243
4600 announces_to_internal = no
4601
4602 [[Default Boundary]]
4603 type = TCPClientInterface
4604 target_host = 127.0.0.1
4605 target_port = 4244
4606"#;
4607
4608 let parsed = config::parse(config).unwrap();
4609 let parsed_value = |index: usize| {
4610 parsed.interfaces[index]
4611 .params
4612 .get("announces_to_internal")
4613 .and_then(|value| config::parse_bool_pub(value))
4614 };
4615
4616 assert_eq!(parsed_value(0), Some(true));
4617 assert_eq!(parsed_value(1), Some(false));
4618 assert_eq!(parsed_value(2), None);
4619 }
4620
4621 #[test]
4622 fn interface_gravity_parser_accepts_signed_values_and_uses_global_default() {
4623 assert_eq!(parse_interface_gravity(Some("7"), -2), 7);
4624 assert_eq!(parse_interface_gravity(Some("-4"), 2), -4);
4625 assert_eq!(parse_interface_gravity(None, -6), -6);
4626 }
4627
4628 #[test]
4629 fn config_parser_accepts_local_hops_delta_option() {
4630 let cfg = r#"
4631[reticulum]
4632local_hops_delta = yes
4633"#;
4634 let parsed = config::parse(cfg).unwrap();
4635 assert!(parsed.reticulum.local_hops_delta);
4636 }
4637
4638 #[test]
4639 fn config_parser_accepts_static_transport_identity_option() {
4640 let config = r#"
4641[reticulum]
4642enable_transport = false
4643static_transport_identity = yes
4644"#;
4645
4646 let parsed = config::parse(config).unwrap();
4647
4648 assert!(!parsed.reticulum.enable_transport);
4649 assert!(parsed.reticulum.static_transport_identity);
4650 }
4651
4652 #[test]
4653 fn non_transport_node_uses_ephemeral_transport_identity_by_default() {
4654 let identity = Identity::new(&mut rns_crypto::FixedRng::new(&[0x41; 32]));
4655 let stored_hash = *identity.hash();
4656 let node = RnsNode::start(
4657 NodeConfig {
4658 transport_enabled: false,
4659 static_transport_identity: false,
4660 local_hops_delta: false,
4661 identity: Some(identity),
4662 share_instance: false,
4663 ..Default::default()
4664 },
4665 Box::new(NoopCallbacks),
4666 )
4667 .unwrap();
4668
4669 let response = node.query(QueryRequest::TransportIdentity).unwrap();
4670 let QueryResponse::TransportIdentity(Some(hash)) = response else {
4671 panic!("transport identity query returned {response:?}");
4672 };
4673 assert_ne!(hash, stored_hash);
4674
4675 node.shutdown();
4676 }
4677
4678 #[test]
4679 fn non_transport_node_can_use_static_transport_identity() {
4680 let identity = Identity::new(&mut rns_crypto::FixedRng::new(&[0x42; 32]));
4681 let stored_hash = *identity.hash();
4682 let node = RnsNode::start(
4683 NodeConfig {
4684 transport_enabled: false,
4685 static_transport_identity: true,
4686 local_hops_delta: false,
4687 identity: Some(identity),
4688 share_instance: false,
4689 ..Default::default()
4690 },
4691 Box::new(NoopCallbacks),
4692 )
4693 .unwrap();
4694
4695 let response = node.query(QueryRequest::TransportIdentity).unwrap();
4696 let QueryResponse::TransportIdentity(Some(hash)) = response else {
4697 panic!("transport identity query returned {response:?}");
4698 };
4699 assert_eq!(hash, stored_hash);
4700
4701 node.shutdown();
4702 }
4703
4704 #[test]
4705 fn test_extract_ifac_config() {
4706 use std::collections::HashMap;
4707
4708 let params: HashMap<String, String> = HashMap::new();
4710 assert!(extract_ifac_config(¶ms, 16).is_none());
4711
4712 let mut params = HashMap::new();
4714 params.insert("networkname".into(), "testnet".into());
4715 let ifac = extract_ifac_config(¶ms, 16).unwrap();
4716 assert_eq!(ifac.netname.as_deref(), Some("testnet"));
4717 assert!(ifac.netkey.is_none());
4718 assert_eq!(ifac.size, 16);
4719
4720 let mut params = HashMap::new();
4722 params.insert("passphrase".into(), "secret".into());
4723 params.insert("ifac_size".into(), "64".into()); let ifac = extract_ifac_config(¶ms, 16).unwrap();
4725 assert!(ifac.netname.is_none());
4726 assert_eq!(ifac.netkey.as_deref(), Some("secret"));
4727 assert_eq!(ifac.size, 8);
4728
4729 let mut params = HashMap::new();
4731 params.insert("network_name".into(), "mynet".into());
4732 params.insert("pass_phrase".into(), "mykey".into());
4733 let ifac = extract_ifac_config(¶ms, 8).unwrap();
4734 assert_eq!(ifac.netname.as_deref(), Some("mynet"));
4735 assert_eq!(ifac.netkey.as_deref(), Some("mykey"));
4736 assert_eq!(ifac.size, 8);
4737 }
4738
4739 #[test]
4740 fn to_node_config_rnode() {
4741 let dir = std::env::temp_dir().join(format!("rns-test-rnode-{}", std::process::id()));
4744 let _ = fs::remove_dir_all(&dir);
4745 fs::create_dir_all(&dir).unwrap();
4746
4747 let config = r#"
4748[reticulum]
4749enable_transport = False
4750
4751[interfaces]
4752 [[Test RNode]]
4753 type = RNodeInterface
4754 port = /dev/nonexistent_rns_test_rnode
4755 frequency = 867200000
4756 bandwidth = 125000
4757 txpower = 7
4758 spreadingfactor = 8
4759 codingrate = 5
4760 flow_control = True
4761 st_alock = 5.0
4762 lt_alock = 2.5
4763 interface_mode = full
4764 networkname = testnet
4765"#;
4766 fs::write(dir.join("config"), config).unwrap();
4767
4768 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks))
4770 .expect("Config should parse; interface failure is non-fatal");
4771 node.shutdown();
4772
4773 let _ = fs::remove_dir_all(&dir);
4774 }
4775
4776 #[test]
4777 fn to_node_config_pipe() {
4778 let dir = std::env::temp_dir().join(format!("rns-test-pipe-{}", std::process::id()));
4781 let _ = fs::remove_dir_all(&dir);
4782 fs::create_dir_all(&dir).unwrap();
4783
4784 let config = r#"
4785[reticulum]
4786enable_transport = False
4787
4788[interfaces]
4789 [[Test Pipe]]
4790 type = PipeInterface
4791 command = cat
4792 respawn_delay = 5000
4793 interface_mode = full
4794"#;
4795 fs::write(dir.join("config"), config).unwrap();
4796
4797 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
4798 node.shutdown();
4800
4801 let _ = fs::remove_dir_all(&dir);
4802 }
4803
4804 #[test]
4805 fn to_node_config_backbone() {
4806 let dir = std::env::temp_dir().join(format!("rns-test-backbone-{}", std::process::id()));
4808 let _ = fs::remove_dir_all(&dir);
4809 fs::create_dir_all(&dir).unwrap();
4810
4811 let port = crate::test_support::port();
4812
4813 let config = format!(
4814 r#"
4815[reticulum]
4816enable_transport = False
4817
4818[interfaces]
4819 [[Test Backbone]]
4820 type = BackboneInterface
4821 listen_ip = 127.0.0.1
4822 listen_port = {}
4823 interface_mode = full
4824"#,
4825 port
4826 );
4827
4828 fs::write(dir.join("config"), config).unwrap();
4829
4830 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
4831
4832 thread::sleep(Duration::from_millis(100));
4834
4835 {
4837 let _client = std::net::TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
4838 }
4840
4841 thread::sleep(Duration::from_millis(50));
4843
4844 node.shutdown();
4845 let _ = fs::remove_dir_all(&dir);
4846 }
4847
4848 #[test]
4849 fn rnode_config_defaults() {
4850 use crate::interface::rnode::{RNodeConfig, RNodeSubConfig};
4851
4852 let config = RNodeConfig::default();
4853 assert_eq!(config.speed, 115200);
4854 assert!(config.subinterfaces.is_empty());
4855 assert!(config.id_interval.is_none());
4856 assert!(config.id_callsign.is_none());
4857
4858 let sub = RNodeSubConfig {
4859 name: "test".into(),
4860 vport: 0,
4861 outgoing: true,
4862 frequency: 868_000_000,
4863 bandwidth: 125_000,
4864 txpower: 7,
4865 spreading_factor: 8,
4866 coding_rate: 5,
4867 flow_control: false,
4868 st_alock: None,
4869 lt_alock: None,
4870 };
4871 assert_eq!(sub.frequency, 868_000_000);
4872 assert_eq!(sub.bandwidth, 125_000);
4873 assert!(!sub.flow_control);
4874 }
4875
4876 #[test]
4881 fn announce_builds_valid_packet() {
4882 let identity = Identity::new(&mut OsRng);
4883 let identity_hash = rns_core::types::IdentityHash(*identity.hash());
4884
4885 let node = RnsNode::start(
4886 NodeConfig {
4887 panic_on_interface_error: false,
4888 transport_enabled: false,
4889 static_transport_identity: false,
4890 local_hops_delta: false,
4891 identity: None,
4892 interfaces: vec![],
4893 share_instance: false,
4894 instance_name: "default".into(),
4895 shared_instance_port: 37428,
4896 rpc_port: 0,
4897 cache_dir: None,
4898 ratchet_store: None,
4899 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
4900 management: Default::default(),
4901 probe_port: None,
4902 probe_addrs: vec![],
4903 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
4904 direct_connect_policy: Default::default(),
4905 device: None,
4906 underlay_mark: None,
4907 hooks: Vec::new(),
4908 discover_interfaces: false,
4909 autoconnect_interface_mode: None,
4910 autoconnect_interface_gravity: 0,
4911 autoconnect_announces_to_internal: false,
4912 discovery_required_value: None,
4913 respond_to_probes: false,
4914 prefer_shorter_path: false,
4915 max_paths_per_destination: 1,
4916 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
4917 packet_hashlist_allocation:
4918 rns_core::transport::types::PacketHashlistAllocation::Eager,
4919 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
4920 max_path_destinations: usize::MAX,
4921 max_tunnel_destinations_total: usize::MAX,
4922 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
4923 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
4924 announce_table_ttl: Duration::from_secs(
4925 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
4926 ),
4927 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
4928 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
4929 interface_writer_queue_capacity:
4930 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
4931 announce_rate_defaults: AnnounceRateDefaults::default(),
4932 ingress_control_defaults: rns_core::transport::types::IngressControlConfig::enabled(
4933 ),
4934 #[cfg(feature = "iface-backbone")]
4935 backbone_peer_pool: None,
4936 announce_sig_cache_enabled: true,
4937 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
4938 announce_sig_cache_ttl: Duration::from_secs(
4939 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
4940 ),
4941 registry: None,
4942 #[cfg(feature = "hooks")]
4943 provider_bridge: None,
4944 },
4945 Box::new(NoopCallbacks),
4946 )
4947 .unwrap();
4948
4949 let dest = crate::destination::Destination::single_in("test", &["echo"], identity_hash);
4950
4951 node.register_destination(dest.hash.0, dest.dest_type.to_wire_constant())
4953 .unwrap();
4954
4955 let result = node.announce(&dest, &identity, Some(b"hello"));
4957 assert!(result.is_ok());
4958
4959 node.shutdown();
4960 }
4961
4962 #[test]
4963 fn has_path_and_hops_to() {
4964 let node = RnsNode::start(
4965 NodeConfig {
4966 panic_on_interface_error: false,
4967 transport_enabled: false,
4968 static_transport_identity: false,
4969 local_hops_delta: false,
4970 identity: None,
4971 interfaces: vec![],
4972 share_instance: false,
4973 instance_name: "default".into(),
4974 shared_instance_port: 37428,
4975 rpc_port: 0,
4976 cache_dir: None,
4977 ratchet_store: None,
4978 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
4979 management: Default::default(),
4980 probe_port: None,
4981 probe_addrs: vec![],
4982 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
4983 direct_connect_policy: Default::default(),
4984 device: None,
4985 underlay_mark: None,
4986 hooks: Vec::new(),
4987 discover_interfaces: false,
4988 autoconnect_interface_mode: None,
4989 autoconnect_interface_gravity: 0,
4990 autoconnect_announces_to_internal: false,
4991 discovery_required_value: None,
4992 respond_to_probes: false,
4993 prefer_shorter_path: false,
4994 max_paths_per_destination: 1,
4995 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
4996 packet_hashlist_allocation:
4997 rns_core::transport::types::PacketHashlistAllocation::Eager,
4998 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
4999 max_path_destinations: usize::MAX,
5000 max_tunnel_destinations_total: usize::MAX,
5001 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
5002 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
5003 announce_table_ttl: Duration::from_secs(
5004 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
5005 ),
5006 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
5007 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
5008 interface_writer_queue_capacity:
5009 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
5010 announce_rate_defaults: AnnounceRateDefaults::default(),
5011 ingress_control_defaults: rns_core::transport::types::IngressControlConfig::enabled(
5012 ),
5013 #[cfg(feature = "iface-backbone")]
5014 backbone_peer_pool: None,
5015 announce_sig_cache_enabled: true,
5016 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
5017 announce_sig_cache_ttl: Duration::from_secs(
5018 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
5019 ),
5020 registry: None,
5021 #[cfg(feature = "hooks")]
5022 provider_bridge: None,
5023 },
5024 Box::new(NoopCallbacks),
5025 )
5026 .unwrap();
5027
5028 let dh = rns_core::types::DestHash([0xAA; 16]);
5029
5030 assert!(!node.has_path(&dh).unwrap());
5032 assert_eq!(node.hops_to(&dh).unwrap(), None);
5033
5034 node.shutdown();
5035 }
5036
5037 #[test]
5038 fn recall_identity_none_when_unknown() {
5039 let node = RnsNode::start(
5040 NodeConfig {
5041 panic_on_interface_error: false,
5042 transport_enabled: false,
5043 static_transport_identity: false,
5044 local_hops_delta: false,
5045 identity: None,
5046 interfaces: vec![],
5047 share_instance: false,
5048 instance_name: "default".into(),
5049 shared_instance_port: 37428,
5050 rpc_port: 0,
5051 cache_dir: None,
5052 ratchet_store: None,
5053 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
5054 management: Default::default(),
5055 probe_port: None,
5056 probe_addrs: vec![],
5057 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
5058 direct_connect_policy: Default::default(),
5059 device: None,
5060 underlay_mark: None,
5061 hooks: Vec::new(),
5062 discover_interfaces: false,
5063 autoconnect_interface_mode: None,
5064 autoconnect_interface_gravity: 0,
5065 autoconnect_announces_to_internal: false,
5066 discovery_required_value: None,
5067 respond_to_probes: false,
5068 prefer_shorter_path: false,
5069 max_paths_per_destination: 1,
5070 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
5071 packet_hashlist_allocation:
5072 rns_core::transport::types::PacketHashlistAllocation::Eager,
5073 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
5074 max_path_destinations: usize::MAX,
5075 max_tunnel_destinations_total: usize::MAX,
5076 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
5077 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
5078 announce_table_ttl: Duration::from_secs(
5079 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
5080 ),
5081 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
5082 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
5083 interface_writer_queue_capacity:
5084 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
5085 announce_rate_defaults: AnnounceRateDefaults::default(),
5086 ingress_control_defaults: rns_core::transport::types::IngressControlConfig::enabled(
5087 ),
5088 #[cfg(feature = "iface-backbone")]
5089 backbone_peer_pool: None,
5090 announce_sig_cache_enabled: true,
5091 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
5092 announce_sig_cache_ttl: Duration::from_secs(
5093 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
5094 ),
5095 registry: None,
5096 #[cfg(feature = "hooks")]
5097 provider_bridge: None,
5098 },
5099 Box::new(NoopCallbacks),
5100 )
5101 .unwrap();
5102
5103 let dh = rns_core::types::DestHash([0xBB; 16]);
5104 assert!(node.recall_identity(&dh).unwrap().is_none());
5105
5106 node.shutdown();
5107 }
5108
5109 #[test]
5110 fn request_path_does_not_crash() {
5111 let node = RnsNode::start(
5112 NodeConfig {
5113 panic_on_interface_error: false,
5114 transport_enabled: false,
5115 static_transport_identity: false,
5116 local_hops_delta: false,
5117 identity: None,
5118 interfaces: vec![],
5119 share_instance: false,
5120 instance_name: "default".into(),
5121 shared_instance_port: 37428,
5122 rpc_port: 0,
5123 cache_dir: None,
5124 ratchet_store: None,
5125 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
5126 management: Default::default(),
5127 probe_port: None,
5128 probe_addrs: vec![],
5129 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
5130 direct_connect_policy: Default::default(),
5131 device: None,
5132 underlay_mark: None,
5133 hooks: Vec::new(),
5134 discover_interfaces: false,
5135 autoconnect_interface_mode: None,
5136 autoconnect_interface_gravity: 0,
5137 autoconnect_announces_to_internal: false,
5138 discovery_required_value: None,
5139 respond_to_probes: false,
5140 prefer_shorter_path: false,
5141 max_paths_per_destination: 1,
5142 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
5143 packet_hashlist_allocation:
5144 rns_core::transport::types::PacketHashlistAllocation::Eager,
5145 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
5146 max_path_destinations: usize::MAX,
5147 max_tunnel_destinations_total: usize::MAX,
5148 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
5149 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
5150 announce_table_ttl: Duration::from_secs(
5151 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
5152 ),
5153 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
5154 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
5155 interface_writer_queue_capacity:
5156 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
5157 announce_rate_defaults: AnnounceRateDefaults::default(),
5158 ingress_control_defaults: rns_core::transport::types::IngressControlConfig::enabled(
5159 ),
5160 #[cfg(feature = "iface-backbone")]
5161 backbone_peer_pool: None,
5162 announce_sig_cache_enabled: true,
5163 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
5164 announce_sig_cache_ttl: Duration::from_secs(
5165 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
5166 ),
5167 registry: None,
5168 #[cfg(feature = "hooks")]
5169 provider_bridge: None,
5170 },
5171 Box::new(NoopCallbacks),
5172 )
5173 .unwrap();
5174
5175 let dh = rns_core::types::DestHash([0xCC; 16]);
5176 assert!(node.request_path(&dh).is_ok());
5177
5178 thread::sleep(Duration::from_millis(50));
5180
5181 node.shutdown();
5182 }
5183
5184 #[test]
5185 fn create_link_returns_error_while_draining() {
5186 let node = RnsNode::start(NodeConfig::default(), Box::new(NoopCallbacks)).unwrap();
5187
5188 node.begin_drain(Duration::from_secs(1)).unwrap();
5189 assert!(node.create_link([0xAB; 16], [0xCD; 32]).is_err());
5190
5191 node.shutdown();
5192 }
5193
5194 #[test]
5195 fn request_path_returns_error_while_draining() {
5196 let node = RnsNode::start(NodeConfig::default(), Box::new(NoopCallbacks)).unwrap();
5197
5198 node.begin_drain(Duration::from_secs(1)).unwrap();
5199 assert!(node
5200 .request_path(&rns_core::types::DestHash([0xAB; 16]))
5201 .is_err());
5202
5203 node.shutdown();
5204 }
5205
5206 #[test]
5211 fn send_packet_returns_error_while_draining() {
5212 let node = RnsNode::start(NodeConfig::default(), Box::new(NoopCallbacks)).unwrap();
5213 let dest = crate::destination::Destination::plain("drain-test", &["send"]);
5214
5215 node.begin_drain(Duration::from_secs(1)).unwrap();
5216 assert!(node.send_packet(&dest, b"hello").is_err());
5217
5218 node.shutdown();
5219 }
5220
5221 #[test]
5222 fn send_packet_plain() {
5223 let node = RnsNode::start(
5224 NodeConfig {
5225 panic_on_interface_error: false,
5226 transport_enabled: false,
5227 static_transport_identity: false,
5228 local_hops_delta: false,
5229 identity: None,
5230 interfaces: vec![],
5231 share_instance: false,
5232 instance_name: "default".into(),
5233 shared_instance_port: 37428,
5234 rpc_port: 0,
5235 cache_dir: None,
5236 ratchet_store: None,
5237 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
5238 management: Default::default(),
5239 probe_port: None,
5240 probe_addrs: vec![],
5241 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
5242 direct_connect_policy: Default::default(),
5243 device: None,
5244 underlay_mark: None,
5245 hooks: Vec::new(),
5246 discover_interfaces: false,
5247 autoconnect_interface_mode: None,
5248 autoconnect_interface_gravity: 0,
5249 autoconnect_announces_to_internal: false,
5250 discovery_required_value: None,
5251 respond_to_probes: false,
5252 prefer_shorter_path: false,
5253 max_paths_per_destination: 1,
5254 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
5255 packet_hashlist_allocation:
5256 rns_core::transport::types::PacketHashlistAllocation::Eager,
5257 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
5258 max_path_destinations: usize::MAX,
5259 max_tunnel_destinations_total: usize::MAX,
5260 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
5261 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
5262 announce_table_ttl: Duration::from_secs(
5263 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
5264 ),
5265 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
5266 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
5267 interface_writer_queue_capacity:
5268 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
5269 announce_rate_defaults: AnnounceRateDefaults::default(),
5270 ingress_control_defaults: rns_core::transport::types::IngressControlConfig::enabled(
5271 ),
5272 #[cfg(feature = "iface-backbone")]
5273 backbone_peer_pool: None,
5274 announce_sig_cache_enabled: true,
5275 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
5276 announce_sig_cache_ttl: Duration::from_secs(
5277 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
5278 ),
5279 registry: None,
5280 #[cfg(feature = "hooks")]
5281 provider_bridge: None,
5282 },
5283 Box::new(NoopCallbacks),
5284 )
5285 .unwrap();
5286
5287 let dest = crate::destination::Destination::plain("test", &["echo"]);
5288 let result = node.send_packet(&dest, b"hello world");
5289 assert!(result.is_ok());
5290
5291 let packet_hash = result.unwrap();
5292 assert_ne!(packet_hash.0, [0u8; 32]);
5294
5295 thread::sleep(Duration::from_millis(50));
5297
5298 node.shutdown();
5299 }
5300
5301 #[test]
5302 fn send_packet_single_requires_public_key() {
5303 let node = RnsNode::start(
5304 NodeConfig {
5305 panic_on_interface_error: false,
5306 transport_enabled: false,
5307 static_transport_identity: false,
5308 local_hops_delta: false,
5309 identity: None,
5310 interfaces: vec![],
5311 share_instance: false,
5312 instance_name: "default".into(),
5313 shared_instance_port: 37428,
5314 rpc_port: 0,
5315 cache_dir: None,
5316 ratchet_store: None,
5317 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
5318 management: Default::default(),
5319 probe_port: None,
5320 probe_addrs: vec![],
5321 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
5322 direct_connect_policy: Default::default(),
5323 device: None,
5324 underlay_mark: None,
5325 hooks: Vec::new(),
5326 discover_interfaces: false,
5327 autoconnect_interface_mode: None,
5328 autoconnect_interface_gravity: 0,
5329 autoconnect_announces_to_internal: false,
5330 discovery_required_value: None,
5331 respond_to_probes: false,
5332 prefer_shorter_path: false,
5333 max_paths_per_destination: 1,
5334 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
5335 packet_hashlist_allocation:
5336 rns_core::transport::types::PacketHashlistAllocation::Eager,
5337 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
5338 max_path_destinations: usize::MAX,
5339 max_tunnel_destinations_total: usize::MAX,
5340 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
5341 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
5342 announce_table_ttl: Duration::from_secs(
5343 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
5344 ),
5345 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
5346 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
5347 interface_writer_queue_capacity:
5348 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
5349 announce_rate_defaults: AnnounceRateDefaults::default(),
5350 ingress_control_defaults: rns_core::transport::types::IngressControlConfig::enabled(
5351 ),
5352 #[cfg(feature = "iface-backbone")]
5353 backbone_peer_pool: None,
5354 announce_sig_cache_enabled: true,
5355 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
5356 announce_sig_cache_ttl: Duration::from_secs(
5357 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
5358 ),
5359 registry: None,
5360 #[cfg(feature = "hooks")]
5361 provider_bridge: None,
5362 },
5363 Box::new(NoopCallbacks),
5364 )
5365 .unwrap();
5366
5367 let dest = crate::destination::Destination::single_in(
5369 "test",
5370 &["echo"],
5371 rns_core::types::IdentityHash([0x42; 16]),
5372 );
5373 let result = node.send_packet(&dest, b"hello");
5374 assert!(result.is_err(), "single_in has no public_key, should fail");
5375
5376 node.shutdown();
5377 }
5378
5379 #[test]
5380 fn send_packet_single_encrypts() {
5381 let node = RnsNode::start(
5382 NodeConfig {
5383 panic_on_interface_error: false,
5384 transport_enabled: false,
5385 static_transport_identity: false,
5386 local_hops_delta: false,
5387 identity: None,
5388 interfaces: vec![],
5389 share_instance: false,
5390 instance_name: "default".into(),
5391 shared_instance_port: 37428,
5392 rpc_port: 0,
5393 cache_dir: None,
5394 ratchet_store: None,
5395 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
5396 management: Default::default(),
5397 probe_port: None,
5398 probe_addrs: vec![],
5399 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
5400 direct_connect_policy: Default::default(),
5401 device: None,
5402 underlay_mark: None,
5403 hooks: Vec::new(),
5404 discover_interfaces: false,
5405 autoconnect_interface_mode: None,
5406 autoconnect_interface_gravity: 0,
5407 autoconnect_announces_to_internal: false,
5408 discovery_required_value: None,
5409 respond_to_probes: false,
5410 prefer_shorter_path: false,
5411 max_paths_per_destination: 1,
5412 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
5413 packet_hashlist_allocation:
5414 rns_core::transport::types::PacketHashlistAllocation::Eager,
5415 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
5416 max_path_destinations: usize::MAX,
5417 max_tunnel_destinations_total: usize::MAX,
5418 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
5419 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
5420 announce_table_ttl: Duration::from_secs(
5421 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
5422 ),
5423 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
5424 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
5425 interface_writer_queue_capacity:
5426 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
5427 announce_rate_defaults: AnnounceRateDefaults::default(),
5428 ingress_control_defaults: rns_core::transport::types::IngressControlConfig::enabled(
5429 ),
5430 #[cfg(feature = "iface-backbone")]
5431 backbone_peer_pool: None,
5432 announce_sig_cache_enabled: true,
5433 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
5434 announce_sig_cache_ttl: Duration::from_secs(
5435 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
5436 ),
5437 registry: None,
5438 #[cfg(feature = "hooks")]
5439 provider_bridge: None,
5440 },
5441 Box::new(NoopCallbacks),
5442 )
5443 .unwrap();
5444
5445 let remote_identity = Identity::new(&mut OsRng);
5447 let recalled = crate::destination::AnnouncedIdentity {
5448 dest_hash: rns_core::types::DestHash([0xAA; 16]),
5449 identity_hash: rns_core::types::IdentityHash(*remote_identity.hash()),
5450 public_key: remote_identity.get_public_key().unwrap(),
5451 app_data: None,
5452 hops: 1,
5453 received_at: 0.0,
5454 receiving_interface: rns_core::transport::types::InterfaceId(0),
5455 rssi: Some(-100),
5456 snr: Some(10.5),
5457 };
5458 let dest = crate::destination::Destination::single_out("test", &["echo"], &recalled);
5459
5460 let result = node.send_packet(&dest, b"secret message");
5461 assert!(result.is_ok());
5462
5463 let packet_hash = result.unwrap();
5464 assert_ne!(packet_hash.0, [0u8; 32]);
5465
5466 thread::sleep(Duration::from_millis(50));
5467 node.shutdown();
5468 }
5469
5470 #[test]
5471 fn register_destination_with_proof_prove_all() {
5472 let node = RnsNode::start(
5473 NodeConfig {
5474 panic_on_interface_error: false,
5475 transport_enabled: false,
5476 static_transport_identity: false,
5477 local_hops_delta: false,
5478 identity: None,
5479 interfaces: vec![],
5480 share_instance: false,
5481 instance_name: "default".into(),
5482 shared_instance_port: 37428,
5483 rpc_port: 0,
5484 cache_dir: None,
5485 ratchet_store: None,
5486 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
5487 management: Default::default(),
5488 probe_port: None,
5489 probe_addrs: vec![],
5490 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
5491 direct_connect_policy: Default::default(),
5492 device: None,
5493 underlay_mark: None,
5494 hooks: Vec::new(),
5495 discover_interfaces: false,
5496 autoconnect_interface_mode: None,
5497 autoconnect_interface_gravity: 0,
5498 autoconnect_announces_to_internal: false,
5499 discovery_required_value: None,
5500 respond_to_probes: false,
5501 prefer_shorter_path: false,
5502 max_paths_per_destination: 1,
5503 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
5504 packet_hashlist_allocation:
5505 rns_core::transport::types::PacketHashlistAllocation::Eager,
5506 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
5507 max_path_destinations: usize::MAX,
5508 max_tunnel_destinations_total: usize::MAX,
5509 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
5510 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
5511 announce_table_ttl: Duration::from_secs(
5512 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
5513 ),
5514 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
5515 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
5516 interface_writer_queue_capacity:
5517 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
5518 announce_rate_defaults: AnnounceRateDefaults::default(),
5519 ingress_control_defaults: rns_core::transport::types::IngressControlConfig::enabled(
5520 ),
5521 #[cfg(feature = "iface-backbone")]
5522 backbone_peer_pool: None,
5523 announce_sig_cache_enabled: true,
5524 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
5525 announce_sig_cache_ttl: Duration::from_secs(
5526 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
5527 ),
5528 registry: None,
5529 #[cfg(feature = "hooks")]
5530 provider_bridge: None,
5531 },
5532 Box::new(NoopCallbacks),
5533 )
5534 .unwrap();
5535
5536 let identity = Identity::new(&mut OsRng);
5537 let ih = rns_core::types::IdentityHash(*identity.hash());
5538 let dest = crate::destination::Destination::single_in("echo", &["request"], ih)
5539 .set_proof_strategy(rns_core::types::ProofStrategy::ProveAll);
5540 let prv_key = identity.get_private_key().unwrap();
5541
5542 let result = node.register_destination_with_proof(&dest, Some(prv_key));
5543 assert!(result.is_ok());
5544
5545 thread::sleep(Duration::from_millis(50));
5547
5548 node.shutdown();
5549 }
5550
5551 #[test]
5552 fn register_destination_with_proof_prove_none() {
5553 let node = RnsNode::start(
5554 NodeConfig {
5555 panic_on_interface_error: false,
5556 transport_enabled: false,
5557 static_transport_identity: false,
5558 local_hops_delta: false,
5559 identity: None,
5560 interfaces: vec![],
5561 share_instance: false,
5562 instance_name: "default".into(),
5563 shared_instance_port: 37428,
5564 rpc_port: 0,
5565 cache_dir: None,
5566 ratchet_store: None,
5567 ratchet_expiry: Duration::from_secs(rns_core::constants::RATCHET_EXPIRY),
5568 management: Default::default(),
5569 probe_port: None,
5570 probe_addrs: vec![],
5571 probe_protocol: rns_core::holepunch::ProbeProtocol::Rnsp,
5572 direct_connect_policy: Default::default(),
5573 device: None,
5574 underlay_mark: None,
5575 hooks: Vec::new(),
5576 discover_interfaces: false,
5577 autoconnect_interface_mode: None,
5578 autoconnect_interface_gravity: 0,
5579 autoconnect_announces_to_internal: false,
5580 discovery_required_value: None,
5581 respond_to_probes: false,
5582 prefer_shorter_path: false,
5583 max_paths_per_destination: 1,
5584 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
5585 packet_hashlist_allocation:
5586 rns_core::transport::types::PacketHashlistAllocation::Eager,
5587 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
5588 max_path_destinations: usize::MAX,
5589 max_tunnel_destinations_total: usize::MAX,
5590 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
5591 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
5592 announce_table_ttl: Duration::from_secs(
5593 rns_core::constants::ANNOUNCE_TABLE_TTL as u64,
5594 ),
5595 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
5596 driver_event_queue_capacity: crate::event::DEFAULT_EVENT_QUEUE_CAPACITY,
5597 interface_writer_queue_capacity:
5598 crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
5599 announce_rate_defaults: AnnounceRateDefaults::default(),
5600 ingress_control_defaults: rns_core::transport::types::IngressControlConfig::enabled(
5601 ),
5602 #[cfg(feature = "iface-backbone")]
5603 backbone_peer_pool: None,
5604 announce_sig_cache_enabled: true,
5605 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
5606 announce_sig_cache_ttl: Duration::from_secs(
5607 rns_core::constants::ANNOUNCE_SIG_CACHE_TTL as u64,
5608 ),
5609 registry: None,
5610 #[cfg(feature = "hooks")]
5611 provider_bridge: None,
5612 },
5613 Box::new(NoopCallbacks),
5614 )
5615 .unwrap();
5616
5617 let dest = crate::destination::Destination::plain("test", &["data"])
5619 .set_proof_strategy(rns_core::types::ProofStrategy::ProveNone);
5620
5621 let result = node.register_destination_with_proof(&dest, None);
5622 assert!(result.is_ok());
5623
5624 thread::sleep(Duration::from_millis(50));
5625 node.shutdown();
5626 }
5627}