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