1use std::io;
6use std::path::Path;
7use std::sync::atomic::{AtomicU64, Ordering};
8use std::sync::Arc;
9use std::thread::{self, JoinHandle};
10use std::time::Duration;
11
12use rns_core::transport::types::{InterfaceInfo, TransportConfig};
13use rns_crypto::identity::Identity;
14use rns_crypto::{OsRng, Rng};
15
16use crate::config;
17use crate::driver::{Callbacks, Driver};
18use crate::event::{self, Event, EventSender};
19use crate::ifac;
20use crate::interface::tcp::TcpClientConfig;
21use crate::interface::tcp_server::TcpServerConfig;
22use crate::interface::udp::UdpConfig;
23use crate::interface::local::{LocalServerConfig, LocalClientConfig};
24use crate::interface::serial_iface::SerialIfaceConfig;
25use crate::interface::kiss_iface::KissIfaceConfig;
26use crate::interface::pipe::PipeConfig;
27use crate::interface::rnode::{RNodeConfig, RNodeSubConfig};
28use crate::interface::backbone::{BackboneConfig, BackboneClientConfig};
29use crate::interface::auto::AutoConfig;
30use crate::interface::i2p::I2pConfig;
31use crate::interface::{InterfaceEntry, InterfaceStats};
32use crate::time;
33use crate::serial::Parity;
34use crate::storage;
35
36fn parse_interface_mode(mode: &str) -> u8 {
39 match mode.to_lowercase().as_str() {
40 "full" => rns_core::constants::MODE_FULL,
41 "access_point" | "accesspoint" | "ap" => rns_core::constants::MODE_ACCESS_POINT,
42 "pointtopoint" | "ptp" => rns_core::constants::MODE_POINT_TO_POINT,
43 "roaming" => rns_core::constants::MODE_ROAMING,
44 "boundary" => rns_core::constants::MODE_BOUNDARY,
45 "gateway" | "gw" => rns_core::constants::MODE_GATEWAY,
46 _ => rns_core::constants::MODE_FULL,
47 }
48}
49
50fn parse_parity(s: &str) -> Parity {
52 match s.to_lowercase().as_str() {
53 "e" | "even" => Parity::Even,
54 "o" | "odd" => Parity::Odd,
55 _ => Parity::None,
56 }
57}
58
59fn extract_ifac_config(params: &std::collections::HashMap<String, String>, default_size: usize) -> Option<IfacConfig> {
62 let netname = params.get("networkname")
63 .or_else(|| params.get("network_name"))
64 .cloned();
65 let netkey = params.get("passphrase")
66 .or_else(|| params.get("pass_phrase"))
67 .cloned();
68
69 if netname.is_none() && netkey.is_none() {
70 return None;
71 }
72
73 let size = params.get("ifac_size")
75 .and_then(|v| v.parse::<usize>().ok())
76 .map(|bits| (bits / 8).max(1))
77 .unwrap_or(default_size);
78
79 Some(IfacConfig { netname, netkey, size })
80}
81
82fn extract_discovery_config(
84 iface_name: &str,
85 iface_type: &str,
86 params: &std::collections::HashMap<String, String>,
87) -> Option<crate::discovery::DiscoveryConfig> {
88 let discoverable = params.get("discoverable")
89 .and_then(|v| config::parse_bool_pub(v))
90 .unwrap_or(false);
91 if !discoverable {
92 return None;
93 }
94
95 let discovery_name = params.get("discovery_name")
96 .cloned()
97 .unwrap_or_else(|| iface_name.to_string());
98
99 let announce_interval = params.get("announce_interval")
101 .and_then(|v| v.parse::<u64>().ok())
102 .map(|secs| secs.max(300))
103 .unwrap_or(21600);
104
105 let stamp_value = params.get("discovery_stamp_value")
106 .and_then(|v| v.parse::<u8>().ok())
107 .unwrap_or(crate::discovery::DEFAULT_STAMP_VALUE);
108
109 let reachable_on = params.get("reachable_on").cloned();
110
111 let listen_port = params.get("listen_port")
112 .or_else(|| params.get("port"))
113 .and_then(|v| v.parse().ok());
114
115 let latitude = params.get("latitude")
116 .or_else(|| params.get("lat"))
117 .and_then(|v| v.parse().ok());
118 let longitude = params.get("longitude")
119 .or_else(|| params.get("lon"))
120 .and_then(|v| v.parse().ok());
121 let height = params.get("height")
122 .and_then(|v| v.parse().ok());
123
124 Some(crate::discovery::DiscoveryConfig {
125 discovery_name,
126 announce_interval,
127 stamp_value,
128 reachable_on,
129 interface_type: iface_type.to_string(),
130 listen_port,
131 latitude,
132 longitude,
133 height,
134 })
135}
136
137pub struct NodeConfig {
139 pub transport_enabled: bool,
140 pub identity: Option<Identity>,
141 pub interfaces: Vec<InterfaceConfig>,
142 pub share_instance: bool,
144 pub instance_name: String,
146 pub shared_instance_port: u16,
148 pub rpc_port: u16,
150 pub cache_dir: Option<std::path::PathBuf>,
152 pub management: crate::management::ManagementConfig,
154 pub probe_port: Option<u16>,
156 pub probe_addr: Option<std::net::SocketAddr>,
158 pub device: Option<String>,
160 pub hooks: Vec<config::ParsedHook>,
162 pub discover_interfaces: bool,
164 pub discovery_required_value: Option<u8>,
166 pub respond_to_probes: bool,
168}
169
170pub struct InterfaceConfig {
172 pub variant: InterfaceVariant,
173 pub mode: u8,
175 pub ifac: Option<IfacConfig>,
177 pub discovery: Option<crate::discovery::DiscoveryConfig>,
179}
180
181pub struct IfacConfig {
183 pub netname: Option<String>,
184 pub netkey: Option<String>,
185 pub size: usize,
186}
187
188pub enum InterfaceVariant {
190 TcpClient(TcpClientConfig),
191 TcpServer(TcpServerConfig),
192 Udp(UdpConfig),
193 LocalServer(LocalServerConfig),
194 LocalClient(LocalClientConfig),
195 Serial(SerialIfaceConfig),
196 Kiss(KissIfaceConfig),
197 Pipe(PipeConfig),
198 RNode(RNodeConfig),
199 Backbone(BackboneConfig),
200 BackboneClient(BackboneClientConfig),
201 Auto(AutoConfig),
202 I2p(I2pConfig),
203}
204
205use crate::event::{QueryRequest, QueryResponse};
206
207#[derive(Debug)]
209pub struct SendError;
210
211impl std::fmt::Display for SendError {
212 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213 write!(f, "driver shut down")
214 }
215}
216
217impl std::error::Error for SendError {}
218
219pub struct RnsNode {
221 tx: EventSender,
222 driver_handle: Option<JoinHandle<()>>,
223 rpc_server: Option<crate::rpc::RpcServer>,
224 tick_interval_ms: Arc<AtomicU64>,
225 #[allow(dead_code)]
226 probe_server: Option<crate::holepunch::probe::ProbeServerHandle>,
227}
228
229impl RnsNode {
230 pub fn from_config(
233 config_path: Option<&Path>,
234 callbacks: Box<dyn Callbacks>,
235 ) -> io::Result<Self> {
236 let config_dir = storage::resolve_config_dir(config_path);
237 let paths = storage::ensure_storage_dirs(&config_dir)?;
238
239 let config_file = config_dir.join("config");
241 let rns_config = if config_file.exists() {
242 config::parse_file(&config_file).map_err(|e| {
243 io::Error::new(io::ErrorKind::InvalidData, format!("{}", e))
244 })?
245 } else {
246 config::parse("").map_err(|e| {
248 io::Error::new(io::ErrorKind::InvalidData, format!("{}", e))
249 })?
250 };
251
252 let identity = if let Some(ref id_path_str) = rns_config.reticulum.network_identity {
254 let id_path = std::path::PathBuf::from(id_path_str);
255 if id_path.exists() {
256 storage::load_identity(&id_path)?
257 } else {
258 let id = Identity::new(&mut OsRng);
259 storage::save_identity(&id, &id_path)?;
260 id
261 }
262 } else {
263 storage::load_or_create_identity(&paths.identities)?
264 };
265
266 let mut interface_configs = Vec::new();
268 let mut next_id_val = 1u64;
269
270 for iface in &rns_config.interfaces {
271 if !iface.enabled {
272 continue;
273 }
274
275 let iface_id = rns_core::transport::types::InterfaceId(next_id_val);
276 next_id_val += 1;
277
278 let mut iface_mode = parse_interface_mode(&iface.mode);
279
280 let has_discovery = match iface.interface_type.as_str() {
283 "AutoInterface" => true,
284 "RNodeInterface" => iface.params.get("discoverable")
285 .and_then(|v| config::parse_bool_pub(v))
286 .unwrap_or(false),
287 _ => false,
288 };
289 if has_discovery
290 && iface_mode != rns_core::constants::MODE_ACCESS_POINT
291 && iface_mode != rns_core::constants::MODE_GATEWAY
292 {
293 let new_mode = if iface.interface_type == "RNodeInterface" {
294 rns_core::constants::MODE_ACCESS_POINT
295 } else {
296 rns_core::constants::MODE_GATEWAY
297 };
298 log::info!(
299 "Interface '{}' has discovery enabled, auto-configuring mode to {}",
300 iface.name,
301 if new_mode == rns_core::constants::MODE_ACCESS_POINT {
302 "ACCESS_POINT"
303 } else {
304 "GATEWAY"
305 }
306 );
307 iface_mode = new_mode;
308 }
309
310 let default_ifac_size = match iface.interface_type.as_str() {
313 "SerialInterface" | "KISSInterface" | "RNodeInterface" => 8,
314 _ => 16,
315 };
316 let ifac_config = extract_ifac_config(&iface.params, default_ifac_size);
317 let discovery_config = extract_discovery_config(
318 &iface.name, &iface.interface_type, &iface.params,
319 );
320
321 match iface.interface_type.as_str() {
322 "TCPClientInterface" => {
323 let target_host = iface
324 .params
325 .get("target_host")
326 .cloned()
327 .unwrap_or_else(|| "127.0.0.1".into());
328 let target_port = iface
329 .params
330 .get("target_port")
331 .and_then(|v| v.parse().ok())
332 .unwrap_or(4242);
333
334 interface_configs.push(InterfaceConfig {
335 variant: InterfaceVariant::TcpClient(TcpClientConfig {
336 name: iface.name.clone(),
337 target_host,
338 target_port,
339 interface_id: iface_id,
340 device: rns_config.reticulum.device.clone(),
341 ..TcpClientConfig::default()
342 }),
343 mode: iface_mode,
344 ifac: ifac_config,
345 discovery: discovery_config.clone(),
346 });
347 }
348 "TCPServerInterface" => {
349 let listen_ip = iface
350 .params
351 .get("listen_ip")
352 .cloned()
353 .unwrap_or_else(|| "0.0.0.0".into());
354 let listen_port = iface
355 .params
356 .get("listen_port")
357 .and_then(|v| v.parse().ok())
358 .unwrap_or(4242);
359
360 interface_configs.push(InterfaceConfig {
361 variant: InterfaceVariant::TcpServer(TcpServerConfig {
362 name: iface.name.clone(),
363 listen_ip,
364 listen_port,
365 interface_id: iface_id,
366 }),
367 mode: iface_mode,
368 ifac: ifac_config,
369 discovery: discovery_config.clone(),
370 });
371 }
372 "UDPInterface" => {
373 let listen_ip = iface.params.get("listen_ip").cloned();
374 let listen_port = iface
375 .params
376 .get("listen_port")
377 .and_then(|v| v.parse().ok());
378 let forward_ip = iface.params.get("forward_ip").cloned();
379 let forward_port = iface
380 .params
381 .get("forward_port")
382 .and_then(|v| v.parse().ok());
383
384 let port = iface.params.get("port").and_then(|v| v.parse::<u16>().ok());
386 let listen_port = listen_port.or(port);
387 let forward_port = forward_port.or(port);
388
389 interface_configs.push(InterfaceConfig {
390 variant: InterfaceVariant::Udp(UdpConfig {
391 name: iface.name.clone(),
392 listen_ip,
393 listen_port,
394 forward_ip,
395 forward_port,
396 interface_id: iface_id,
397 }),
398 mode: iface_mode,
399 ifac: ifac_config,
400 discovery: discovery_config.clone(),
401 });
402 }
403 "SerialInterface" => {
404 let port = match iface.params.get("port") {
405 Some(p) => p.clone(),
406 None => {
407 log::warn!("No port specified for SerialInterface '{}'", iface.name);
408 continue;
409 }
410 };
411 let speed = iface.params.get("speed")
412 .and_then(|v| v.parse().ok())
413 .unwrap_or(9600);
414 let databits = iface.params.get("databits")
415 .and_then(|v| v.parse().ok())
416 .unwrap_or(8);
417 let parity = iface.params.get("parity")
418 .map(|v| parse_parity(v))
419 .unwrap_or(Parity::None);
420 let stopbits = iface.params.get("stopbits")
421 .and_then(|v| v.parse().ok())
422 .unwrap_or(1);
423
424 interface_configs.push(InterfaceConfig {
425 variant: InterfaceVariant::Serial(SerialIfaceConfig {
426 name: iface.name.clone(),
427 port,
428 speed,
429 data_bits: databits,
430 parity,
431 stop_bits: stopbits,
432 interface_id: iface_id,
433 }),
434 mode: iface_mode,
435 ifac: ifac_config,
436 discovery: discovery_config.clone(),
437 });
438 }
439 "KISSInterface" => {
440 let port = match iface.params.get("port") {
441 Some(p) => p.clone(),
442 None => {
443 log::warn!("No port specified for KISSInterface '{}'", iface.name);
444 continue;
445 }
446 };
447 let speed = iface.params.get("speed")
448 .and_then(|v| v.parse().ok())
449 .unwrap_or(9600);
450 let databits = iface.params.get("databits")
451 .and_then(|v| v.parse().ok())
452 .unwrap_or(8);
453 let parity = iface.params.get("parity")
454 .map(|v| parse_parity(v))
455 .unwrap_or(Parity::None);
456 let stopbits = iface.params.get("stopbits")
457 .and_then(|v| v.parse().ok())
458 .unwrap_or(1);
459 let preamble = iface.params.get("preamble")
460 .and_then(|v| v.parse().ok())
461 .unwrap_or(350);
462 let txtail = iface.params.get("txtail")
463 .and_then(|v| v.parse().ok())
464 .unwrap_or(20);
465 let persistence = iface.params.get("persistence")
466 .and_then(|v| v.parse().ok())
467 .unwrap_or(64);
468 let slottime = iface.params.get("slottime")
469 .and_then(|v| v.parse().ok())
470 .unwrap_or(20);
471 let flow_control = iface.params.get("flow_control")
472 .and_then(|v| config::parse_bool_pub(v))
473 .unwrap_or(false);
474 let beacon_interval = iface.params.get("id_interval")
475 .and_then(|v| v.parse().ok());
476 let beacon_data = iface.params.get("id_callsign")
477 .map(|v| v.as_bytes().to_vec());
478
479 interface_configs.push(InterfaceConfig {
480 variant: InterfaceVariant::Kiss(KissIfaceConfig {
481 name: iface.name.clone(),
482 port,
483 speed,
484 data_bits: databits,
485 parity,
486 stop_bits: stopbits,
487 preamble,
488 txtail,
489 persistence,
490 slottime,
491 flow_control,
492 beacon_interval,
493 beacon_data,
494 interface_id: iface_id,
495 }),
496 mode: iface_mode,
497 ifac: ifac_config,
498 discovery: discovery_config.clone(),
499 });
500 }
501 "RNodeInterface" => {
502 let port = match iface.params.get("port") {
503 Some(p) => p.clone(),
504 None => {
505 log::warn!("No port specified for RNodeInterface '{}'", iface.name);
506 continue;
507 }
508 };
509 let speed = iface.params.get("speed")
510 .and_then(|v| v.parse().ok())
511 .unwrap_or(115200);
512 let frequency = iface.params.get("frequency")
513 .and_then(|v| v.parse().ok())
514 .unwrap_or(868_000_000);
515 let bandwidth = iface.params.get("bandwidth")
516 .and_then(|v| v.parse().ok())
517 .unwrap_or(125_000);
518 let txpower = iface.params.get("txpower")
519 .and_then(|v| v.parse().ok())
520 .unwrap_or(7);
521 let spreading_factor = iface.params.get("spreadingfactor")
522 .or_else(|| iface.params.get("spreading_factor"))
523 .and_then(|v| v.parse().ok())
524 .unwrap_or(8);
525 let coding_rate = iface.params.get("codingrate")
526 .or_else(|| iface.params.get("coding_rate"))
527 .and_then(|v| v.parse().ok())
528 .unwrap_or(5);
529 let flow_control = iface.params.get("flow_control")
530 .and_then(|v| config::parse_bool_pub(v))
531 .unwrap_or(false);
532 let st_alock = iface.params.get("st_alock")
533 .and_then(|v| v.parse().ok());
534 let lt_alock = iface.params.get("lt_alock")
535 .and_then(|v| v.parse().ok());
536 let id_interval = iface.params.get("id_interval")
537 .and_then(|v| v.parse().ok());
538 let id_callsign = iface.params.get("id_callsign")
539 .map(|v| v.as_bytes().to_vec());
540
541 let sub = RNodeSubConfig {
542 name: iface.name.clone(),
543 frequency,
544 bandwidth,
545 txpower,
546 spreading_factor,
547 coding_rate,
548 flow_control,
549 st_alock,
550 lt_alock,
551 };
552
553 interface_configs.push(InterfaceConfig {
554 variant: InterfaceVariant::RNode(RNodeConfig {
555 name: iface.name.clone(),
556 port,
557 speed,
558 subinterfaces: vec![sub],
559 id_interval,
560 id_callsign,
561 base_interface_id: iface_id,
562 }),
563 mode: iface_mode,
564 ifac: ifac_config,
565 discovery: discovery_config.clone(),
566 });
567 }
568 "PipeInterface" => {
569 let command = match iface.params.get("command") {
570 Some(c) => c.clone(),
571 None => {
572 log::warn!("No command specified for PipeInterface '{}'", iface.name);
573 continue;
574 }
575 };
576 let respawn_delay = iface.params.get("respawn_delay")
577 .and_then(|v| v.parse::<u64>().ok())
578 .map(Duration::from_millis)
579 .unwrap_or(Duration::from_secs(5));
580
581 interface_configs.push(InterfaceConfig {
582 variant: InterfaceVariant::Pipe(PipeConfig {
583 name: iface.name.clone(),
584 command,
585 respawn_delay,
586 interface_id: iface_id,
587 }),
588 mode: iface_mode,
589 ifac: ifac_config,
590 discovery: discovery_config.clone(),
591 });
592 }
593 "BackboneInterface" => {
594 if let Some(target_host) = iface.params.get("remote")
595 .or_else(|| iface.params.get("target_host"))
596 {
597 let target_host = target_host.clone();
599 let target_port = iface.params.get("target_port")
600 .or_else(|| iface.params.get("port"))
601 .and_then(|v| v.parse().ok())
602 .unwrap_or(4242);
603 let transport_identity = iface.params.get("transport_identity").cloned();
604
605 interface_configs.push(InterfaceConfig {
606 variant: InterfaceVariant::BackboneClient(BackboneClientConfig {
607 name: iface.name.clone(),
608 target_host,
609 target_port,
610 interface_id: iface_id,
611 transport_identity,
612 ..BackboneClientConfig::default()
613 }),
614 mode: iface_mode,
615 ifac: ifac_config,
616 discovery: discovery_config.clone(),
617 });
618 } else {
619 let listen_ip = iface.params.get("listen_ip")
621 .or_else(|| iface.params.get("device"))
622 .cloned()
623 .unwrap_or_else(|| "0.0.0.0".into());
624 let listen_port = iface.params.get("listen_port")
625 .or_else(|| iface.params.get("port"))
626 .and_then(|v| v.parse().ok())
627 .unwrap_or(4242);
628
629 interface_configs.push(InterfaceConfig {
630 variant: InterfaceVariant::Backbone(BackboneConfig {
631 name: iface.name.clone(),
632 listen_ip,
633 listen_port,
634 interface_id: iface_id,
635 }),
636 mode: iface_mode,
637 ifac: ifac_config,
638 discovery: discovery_config.clone(),
639 });
640 }
641 }
642 "AutoInterface" => {
643 let group_id = iface
644 .params
645 .get("group_id")
646 .map(|s| s.as_bytes().to_vec())
647 .unwrap_or_else(|| crate::interface::auto::DEFAULT_GROUP_ID.to_vec());
648
649 let discovery_scope = iface
650 .params
651 .get("discovery_scope")
652 .map(|s| match s.to_lowercase().as_str() {
653 "link" => crate::interface::auto::SCOPE_LINK.to_string(),
654 "admin" => crate::interface::auto::SCOPE_ADMIN.to_string(),
655 "site" => crate::interface::auto::SCOPE_SITE.to_string(),
656 "organisation" | "organization" => crate::interface::auto::SCOPE_ORGANISATION.to_string(),
657 "global" => crate::interface::auto::SCOPE_GLOBAL.to_string(),
658 other => other.to_string(),
659 })
660 .unwrap_or_else(|| crate::interface::auto::SCOPE_LINK.to_string());
661
662 let discovery_port = iface
663 .params
664 .get("discovery_port")
665 .and_then(|v| v.parse().ok())
666 .unwrap_or(crate::interface::auto::DEFAULT_DISCOVERY_PORT);
667
668 let data_port = iface
669 .params
670 .get("data_port")
671 .and_then(|v| v.parse().ok())
672 .unwrap_or(crate::interface::auto::DEFAULT_DATA_PORT);
673
674 let multicast_address_type = iface
675 .params
676 .get("multicast_address_type")
677 .map(|s| match s.to_lowercase().as_str() {
678 "permanent" => crate::interface::auto::MULTICAST_PERMANENT_ADDRESS_TYPE.to_string(),
679 "temporary" => crate::interface::auto::MULTICAST_TEMPORARY_ADDRESS_TYPE.to_string(),
680 other => other.to_string(),
681 })
682 .unwrap_or_else(|| crate::interface::auto::MULTICAST_TEMPORARY_ADDRESS_TYPE.to_string());
683
684 let configured_bitrate = iface
685 .params
686 .get("configured_bitrate")
687 .or_else(|| iface.params.get("bitrate"))
688 .and_then(|v| v.parse().ok())
689 .unwrap_or(crate::interface::auto::BITRATE_GUESS);
690
691 let allowed_interfaces = iface
693 .params
694 .get("devices")
695 .or_else(|| iface.params.get("allowed_interfaces"))
696 .map(|s| s.split(',').map(|d| d.trim().to_string()).filter(|d| !d.is_empty()).collect())
697 .unwrap_or_default();
698
699 let ignored_interfaces = iface
700 .params
701 .get("ignored_devices")
702 .or_else(|| iface.params.get("ignored_interfaces"))
703 .map(|s| s.split(',').map(|d| d.trim().to_string()).filter(|d| !d.is_empty()).collect())
704 .unwrap_or_default();
705
706 interface_configs.push(InterfaceConfig {
707 variant: InterfaceVariant::Auto(AutoConfig {
708 name: iface.name.clone(),
709 group_id,
710 discovery_scope,
711 discovery_port,
712 data_port,
713 multicast_address_type,
714 allowed_interfaces,
715 ignored_interfaces,
716 configured_bitrate,
717 interface_id: iface_id,
718 }),
719 mode: iface_mode,
720 ifac: ifac_config,
721 discovery: discovery_config.clone(),
722 });
723 }
724 "I2PInterface" => {
725 let sam_host = iface
726 .params
727 .get("sam_host")
728 .cloned()
729 .unwrap_or_else(|| "127.0.0.1".into());
730 let sam_port = iface
731 .params
732 .get("sam_port")
733 .and_then(|v| v.parse().ok())
734 .unwrap_or(7656);
735 let connectable = iface
736 .params
737 .get("connectable")
738 .and_then(|v| config::parse_bool_pub(v))
739 .unwrap_or(false);
740 let peers: Vec<String> = iface
741 .params
742 .get("peers")
743 .map(|s| {
744 s.split(',')
745 .map(|p| p.trim().to_string())
746 .filter(|p| !p.is_empty())
747 .collect()
748 })
749 .unwrap_or_default();
750
751 interface_configs.push(InterfaceConfig {
752 variant: InterfaceVariant::I2p(I2pConfig {
753 name: iface.name.clone(),
754 interface_id: iface_id,
755 sam_host,
756 sam_port,
757 peers,
758 connectable,
759 storage_dir: paths.storage.clone(),
760 }),
761 mode: iface_mode,
762 ifac: ifac_config,
763 discovery: discovery_config.clone(),
764 });
765 }
766 _ => {
767 log::warn!(
768 "Unsupported interface type '{}' for '{}'",
769 iface.interface_type,
770 iface.name
771 );
772 }
773 }
774 }
775
776 let mut mgmt_allowed = Vec::new();
778 for hex_hash in &rns_config.reticulum.remote_management_allowed {
779 if hex_hash.len() == 32 {
780 if let Ok(bytes) = (0..hex_hash.len())
781 .step_by(2)
782 .map(|i| u8::from_str_radix(&hex_hash[i..i+2], 16))
783 .collect::<Result<Vec<u8>, _>>()
784 {
785 if bytes.len() == 16 {
786 let mut h = [0u8; 16];
787 h.copy_from_slice(&bytes);
788 mgmt_allowed.push(h);
789 }
790 } else {
791 log::warn!("Invalid hex in remote_management_allowed: {}", hex_hash);
792 }
793 } else {
794 log::warn!(
795 "Invalid entry in remote_management_allowed (expected 32 hex chars, got {}): {}",
796 hex_hash.len(), hex_hash,
797 );
798 }
799 }
800
801 let probe_addr = rns_config.reticulum.probe_addr.as_ref().and_then(|s| {
803 s.parse::<std::net::SocketAddr>().map_err(|e| {
804 log::warn!("Invalid probe_addr '{}': {}", s, e);
805 e
806 }).ok()
807 });
808
809 let node_config = NodeConfig {
810 transport_enabled: rns_config.reticulum.enable_transport,
811 identity: Some(identity),
812 interfaces: interface_configs,
813 share_instance: rns_config.reticulum.share_instance,
814 instance_name: rns_config.reticulum.instance_name.clone(),
815 shared_instance_port: rns_config.reticulum.shared_instance_port,
816 rpc_port: rns_config.reticulum.instance_control_port,
817 cache_dir: Some(paths.cache),
818 management: crate::management::ManagementConfig {
819 enable_remote_management: rns_config.reticulum.enable_remote_management,
820 remote_management_allowed: mgmt_allowed,
821 publish_blackhole: rns_config.reticulum.publish_blackhole,
822 },
823 probe_port: rns_config.reticulum.probe_port,
824 probe_addr,
825 device: rns_config.reticulum.device.clone(),
826 hooks: rns_config.hooks.clone(),
827 discover_interfaces: rns_config.reticulum.discover_interfaces,
828 discovery_required_value: rns_config.reticulum.required_discovery_value,
829 respond_to_probes: rns_config.reticulum.respond_to_probes,
830 };
831
832 Self::start(node_config, callbacks)
833 }
834
835 pub fn start(config: NodeConfig, callbacks: Box<dyn Callbacks>) -> io::Result<Self> {
837 let identity = config
838 .identity
839 .unwrap_or_else(|| Identity::new(&mut OsRng));
840
841 let transport_config = TransportConfig {
842 transport_enabled: config.transport_enabled,
843 identity_hash: Some(*identity.hash()),
844 };
845
846 let (tx, rx) = event::channel();
847 let mut driver = Driver::new(transport_config, rx, tx.clone(), callbacks);
848
849 if let Some(ref cache_dir) = config.cache_dir {
851 let announces_dir = cache_dir.join("announces");
852 let _ = std::fs::create_dir_all(&announces_dir);
853 driver.announce_cache = Some(crate::announce_cache::AnnounceCache::new(announces_dir));
854 }
855
856 if config.probe_addr.is_some() || config.device.is_some() {
858 driver.set_probe_config(config.probe_addr, config.device.clone());
859 }
860
861 let probe_server = if let Some(port) = config.probe_port {
863 let listen_addr: std::net::SocketAddr = ([0, 0, 0, 0], port).into();
864 match crate::holepunch::probe::start_probe_server(listen_addr) {
865 Ok(handle) => {
866 log::info!("Probe server started on 0.0.0.0:{}", port);
867 Some(handle)
868 }
869 Err(e) => {
870 log::error!("Failed to start probe server on port {}: {}", port, e);
871 None
872 }
873 }
874 } else {
875 None
876 };
877
878 driver.management_config = config.management.clone();
880
881 if let Some(prv_key) = identity.get_private_key() {
883 driver.transport_identity = Some(Identity::from_private_key(&prv_key));
884 }
885
886 #[cfg(feature = "rns-hooks")]
888 {
889 for hook_cfg in &config.hooks {
890 if !hook_cfg.enabled {
891 continue;
892 }
893 let point_idx = match config::parse_hook_point(&hook_cfg.attach_point) {
894 Some(idx) => idx,
895 None => {
896 log::warn!(
897 "Unknown hook point '{}' for hook '{}'",
898 hook_cfg.attach_point,
899 hook_cfg.name,
900 );
901 continue;
902 }
903 };
904 let mgr = match driver.hook_manager.as_ref() {
905 Some(m) => m,
906 None => {
907 log::warn!("Hook manager not available, skipping hook '{}'", hook_cfg.name);
908 continue;
909 }
910 };
911 match mgr.load_file(
912 hook_cfg.name.clone(),
913 std::path::Path::new(&hook_cfg.path),
914 hook_cfg.priority,
915 ) {
916 Ok(program) => {
917 driver.hook_slots[point_idx].attach(program);
918 log::info!(
919 "Loaded hook '{}' at point {} (priority {})",
920 hook_cfg.name,
921 hook_cfg.attach_point,
922 hook_cfg.priority,
923 );
924 }
925 Err(e) => {
926 log::error!(
927 "Failed to load hook '{}' from '{}': {}",
928 hook_cfg.name,
929 hook_cfg.path,
930 e,
931 );
932 }
933 }
934 }
935 }
936
937 driver.discover_interfaces = config.discover_interfaces;
939 if let Some(val) = config.discovery_required_value {
940 driver.discovery_required_value = val;
941 }
942
943 let next_dynamic_id = Arc::new(AtomicU64::new(10000));
945
946 let mut discoverable_interfaces = Vec::new();
948
949 for iface_config in config.interfaces {
951 let iface_mode = iface_config.mode;
952 let ifac_cfg = iface_config.ifac;
953
954 if let Some(ref disc) = iface_config.discovery {
956 discoverable_interfaces.push(crate::discovery::DiscoverableInterface {
957 config: disc.clone(),
958 transport_enabled: config.transport_enabled,
959 ifac_netname: ifac_cfg.as_ref().and_then(|ic| ic.netname.clone()),
960 ifac_netkey: ifac_cfg.as_ref().and_then(|ic| ic.netkey.clone()),
961 });
962 }
963
964 let mut ifac_state = ifac_cfg.as_ref().and_then(|ic| {
966 if ic.netname.is_some() || ic.netkey.is_some() {
967 Some(ifac::derive_ifac(
968 ic.netname.as_deref(),
969 ic.netkey.as_deref(),
970 ic.size,
971 ))
972 } else {
973 None
974 }
975 });
976
977 match iface_config.variant {
978 InterfaceVariant::TcpClient(tcp_config) => {
979 let id = tcp_config.interface_id;
980 let name = tcp_config.name.clone();
981 let info = InterfaceInfo {
982 id,
983 name,
984 mode: iface_mode,
985 out_capable: true,
986 in_capable: true,
987 bitrate: None,
988 announce_rate_target: None,
989 announce_rate_grace: 0,
990 announce_rate_penalty: 0.0,
991 announce_cap: rns_core::constants::ANNOUNCE_CAP,
992 is_local_client: false,
993 wants_tunnel: false,
994 tunnel_id: None,
995 mtu: 65535,
996 ingress_control: true,
997 ia_freq: 0.0,
998 started: time::now(),
999 };
1000
1001 let writer =
1002 crate::interface::tcp::start(tcp_config, tx.clone())?;
1003
1004 driver.engine.register_interface(info.clone());
1005 driver.interfaces.insert(
1006 id,
1007 InterfaceEntry {
1008 id,
1009 info,
1010 writer,
1011 online: false,
1012 dynamic: false,
1013 ifac: ifac_state,
1014 stats: InterfaceStats {
1015 started: time::now(),
1016 ..Default::default()
1017 },
1018 interface_type: "TCPClientInterface".to_string(),
1019 },
1020 );
1021 }
1022 InterfaceVariant::TcpServer(server_config) => {
1023 crate::interface::tcp_server::start(
1024 server_config,
1025 tx.clone(),
1026 next_dynamic_id.clone(),
1027 )?;
1028 }
1031 InterfaceVariant::Udp(udp_config) => {
1032 let id = udp_config.interface_id;
1033 let name = udp_config.name.clone();
1034 let out_capable = udp_config.forward_ip.is_some();
1035 let in_capable = udp_config.listen_ip.is_some();
1036
1037 let writer = crate::interface::udp::start(udp_config, tx.clone())?;
1038
1039 let info = InterfaceInfo {
1040 id,
1041 name,
1042 mode: iface_mode,
1043 out_capable,
1044 in_capable,
1045 bitrate: Some(10_000_000), announce_rate_target: None,
1047 announce_rate_grace: 0,
1048 announce_rate_penalty: 0.0,
1049 announce_cap: rns_core::constants::ANNOUNCE_CAP,
1050 is_local_client: false,
1051 wants_tunnel: false,
1052 tunnel_id: None,
1053 mtu: 1400,
1054 ingress_control: true,
1055 ia_freq: 0.0,
1056 started: time::now(),
1057 };
1058
1059 driver.engine.register_interface(info.clone());
1060
1061 if let Some(w) = writer {
1062 driver.interfaces.insert(
1063 id,
1064 InterfaceEntry {
1065 id,
1066 info,
1067 writer: w,
1068 online: in_capable || out_capable,
1069 dynamic: false,
1070 ifac: ifac_state,
1071 stats: InterfaceStats {
1072 started: time::now(),
1073 ..Default::default()
1074 },
1075 interface_type: "UDPInterface".to_string(),
1076 },
1077 );
1078 }
1079 }
1080 InterfaceVariant::LocalServer(local_config) => {
1081 crate::interface::local::start_server(
1082 local_config,
1083 tx.clone(),
1084 next_dynamic_id.clone(),
1085 )?;
1086 }
1087 InterfaceVariant::LocalClient(local_config) => {
1088 let id = local_config.interface_id;
1089 let name = local_config.name.clone();
1090 let info = InterfaceInfo {
1091 id,
1092 name,
1093 mode: iface_mode,
1094 out_capable: true,
1095 in_capable: true,
1096 bitrate: Some(1_000_000_000),
1097 announce_rate_target: None,
1098 announce_rate_grace: 0,
1099 announce_rate_penalty: 0.0,
1100 announce_cap: rns_core::constants::ANNOUNCE_CAP,
1101 is_local_client: false,
1102 wants_tunnel: false,
1103 tunnel_id: None,
1104 mtu: 65535,
1105 ingress_control: false,
1106 ia_freq: 0.0,
1107 started: time::now(),
1108 };
1109
1110 let writer =
1111 crate::interface::local::start_client(local_config, tx.clone())?;
1112
1113 driver.engine.register_interface(info.clone());
1114 driver.interfaces.insert(
1115 id,
1116 InterfaceEntry {
1117 id,
1118 info,
1119 writer,
1120 online: false,
1121 dynamic: false,
1122 ifac: ifac_state,
1123 stats: InterfaceStats {
1124 started: time::now(),
1125 ..Default::default()
1126 },
1127 interface_type: "LocalInterface".to_string(),
1128 },
1129 );
1130 }
1131 InterfaceVariant::Serial(serial_config) => {
1132 let id = serial_config.interface_id;
1133 let name = serial_config.name.clone();
1134 let bitrate = serial_config.speed;
1135 let info = InterfaceInfo {
1136 id,
1137 name,
1138 mode: iface_mode,
1139 out_capable: true,
1140 in_capable: true,
1141 bitrate: Some(bitrate as u64),
1142 announce_rate_target: None,
1143 announce_rate_grace: 0,
1144 announce_rate_penalty: 0.0,
1145 announce_cap: rns_core::constants::ANNOUNCE_CAP,
1146 is_local_client: false,
1147 wants_tunnel: false,
1148 tunnel_id: None,
1149 mtu: rns_core::constants::MTU as u32,
1150 ingress_control: false,
1151 ia_freq: 0.0,
1152 started: time::now(),
1153 };
1154
1155 let writer =
1156 crate::interface::serial_iface::start(serial_config, tx.clone())?;
1157
1158 driver.engine.register_interface(info.clone());
1159 driver.interfaces.insert(
1160 id,
1161 InterfaceEntry {
1162 id,
1163 info,
1164 writer,
1165 online: false,
1166 dynamic: false,
1167 ifac: ifac_state,
1168 stats: InterfaceStats {
1169 started: time::now(),
1170 ..Default::default()
1171 },
1172 interface_type: "SerialInterface".to_string(),
1173 },
1174 );
1175 }
1176 InterfaceVariant::Kiss(kiss_config) => {
1177 let id = kiss_config.interface_id;
1178 let name = kiss_config.name.clone();
1179 let info = InterfaceInfo {
1180 id,
1181 name,
1182 mode: iface_mode,
1183 out_capable: true,
1184 in_capable: true,
1185 bitrate: Some(1200), announce_rate_target: None,
1187 announce_rate_grace: 0,
1188 announce_rate_penalty: 0.0,
1189 announce_cap: rns_core::constants::ANNOUNCE_CAP,
1190 is_local_client: false,
1191 wants_tunnel: false,
1192 tunnel_id: None,
1193 mtu: rns_core::constants::MTU as u32,
1194 ingress_control: false,
1195 ia_freq: 0.0,
1196 started: time::now(),
1197 };
1198
1199 let writer =
1200 crate::interface::kiss_iface::start(kiss_config, tx.clone())?;
1201
1202 driver.engine.register_interface(info.clone());
1203 driver.interfaces.insert(
1204 id,
1205 InterfaceEntry {
1206 id,
1207 info,
1208 writer,
1209 online: false,
1210 dynamic: false,
1211 ifac: ifac_state,
1212 stats: InterfaceStats {
1213 started: time::now(),
1214 ..Default::default()
1215 },
1216 interface_type: "KISSInterface".to_string(),
1217 },
1218 );
1219 }
1220 InterfaceVariant::Pipe(pipe_config) => {
1221 let id = pipe_config.interface_id;
1222 let name = pipe_config.name.clone();
1223 let info = InterfaceInfo {
1224 id,
1225 name,
1226 mode: iface_mode,
1227 out_capable: true,
1228 in_capable: true,
1229 bitrate: Some(1_000_000), announce_rate_target: None,
1231 announce_rate_grace: 0,
1232 announce_rate_penalty: 0.0,
1233 announce_cap: rns_core::constants::ANNOUNCE_CAP,
1234 is_local_client: false,
1235 wants_tunnel: false,
1236 tunnel_id: None,
1237 mtu: rns_core::constants::MTU as u32,
1238 ingress_control: false,
1239 ia_freq: 0.0,
1240 started: time::now(),
1241 };
1242
1243 let writer =
1244 crate::interface::pipe::start(pipe_config, tx.clone())?;
1245
1246 driver.engine.register_interface(info.clone());
1247 driver.interfaces.insert(
1248 id,
1249 InterfaceEntry {
1250 id,
1251 info,
1252 writer,
1253 online: false,
1254 dynamic: false,
1255 ifac: ifac_state,
1256 stats: InterfaceStats {
1257 started: time::now(),
1258 ..Default::default()
1259 },
1260 interface_type: "PipeInterface".to_string(),
1261 },
1262 );
1263 }
1264 InterfaceVariant::RNode(rnode_config) => {
1265 let name = rnode_config.name.clone();
1266 let sub_writers =
1267 crate::interface::rnode::start(rnode_config, tx.clone())?;
1268
1269 let mut first = true;
1272 let mut sub_index = 0u32;
1273 for (sub_id, writer) in sub_writers {
1274 let sub_name = if sub_index == 0 {
1275 name.clone()
1276 } else {
1277 format!("{}/{}", name, sub_index)
1278 };
1279 sub_index += 1;
1280
1281 let info = InterfaceInfo {
1282 id: sub_id,
1283 name: sub_name,
1284 mode: iface_mode,
1285 out_capable: true,
1286 in_capable: true,
1287 bitrate: None, announce_rate_target: None,
1289 announce_rate_grace: 0,
1290 announce_rate_penalty: 0.0,
1291 announce_cap: rns_core::constants::ANNOUNCE_CAP,
1292 is_local_client: false,
1293 wants_tunnel: false,
1294 tunnel_id: None,
1295 mtu: rns_core::constants::MTU as u32,
1296 ingress_control: false,
1297 ia_freq: 0.0,
1298 started: time::now(),
1299 };
1300
1301 let sub_ifac = if first {
1302 first = false;
1303 ifac_state.take()
1304 } else if let Some(ref ic) = ifac_cfg {
1305 Some(ifac::derive_ifac(
1306 ic.netname.as_deref(),
1307 ic.netkey.as_deref(),
1308 ic.size,
1309 ))
1310 } else {
1311 None
1312 };
1313
1314 driver.engine.register_interface(info.clone());
1315 driver.interfaces.insert(
1316 sub_id,
1317 InterfaceEntry {
1318 id: sub_id,
1319 info,
1320 writer,
1321 online: false,
1322 dynamic: false,
1323 ifac: sub_ifac,
1324 stats: InterfaceStats {
1325 started: time::now(),
1326 ..Default::default()
1327 },
1328 interface_type: "RNodeInterface".to_string(),
1329 },
1330 );
1331 }
1332
1333 }
1334 InterfaceVariant::Backbone(backbone_config) => {
1335 crate::interface::backbone::start(
1336 backbone_config,
1337 tx.clone(),
1338 next_dynamic_id.clone(),
1339 )?;
1340 }
1343 InterfaceVariant::BackboneClient(config) => {
1344 let id = config.interface_id;
1345 let name = config.name.clone();
1346 let info = InterfaceInfo {
1347 id,
1348 name,
1349 mode: iface_mode,
1350 out_capable: true,
1351 in_capable: true,
1352 bitrate: Some(1_000_000_000),
1353 announce_rate_target: None,
1354 announce_rate_grace: 0,
1355 announce_rate_penalty: 0.0,
1356 announce_cap: rns_core::constants::ANNOUNCE_CAP,
1357 is_local_client: false,
1358 wants_tunnel: false,
1359 tunnel_id: None,
1360 mtu: 65535,
1361 ingress_control: true,
1362 ia_freq: 0.0,
1363 started: time::now(),
1364 };
1365
1366 let writer =
1367 crate::interface::backbone::start_client(config, tx.clone())?;
1368
1369 driver.engine.register_interface(info.clone());
1370 driver.interfaces.insert(
1371 id,
1372 InterfaceEntry {
1373 id,
1374 info,
1375 writer,
1376 online: false,
1377 dynamic: false,
1378 ifac: ifac_state,
1379 stats: InterfaceStats {
1380 started: time::now(),
1381 ..Default::default()
1382 },
1383 interface_type: "BackboneInterface".to_string(),
1384 },
1385 );
1386 }
1387 InterfaceVariant::Auto(auto_config) => {
1388 crate::interface::auto::start(
1389 auto_config,
1390 tx.clone(),
1391 next_dynamic_id.clone(),
1392 )?;
1393 }
1396 InterfaceVariant::I2p(i2p_config) => {
1397 crate::interface::i2p::start(
1398 i2p_config,
1399 tx.clone(),
1400 next_dynamic_id.clone(),
1401 )?;
1402 }
1405 }
1406 }
1407
1408 if !discoverable_interfaces.is_empty() {
1410 let transport_id = *identity.hash();
1411 let announcer = crate::discovery::InterfaceAnnouncer::new(
1412 transport_id,
1413 discoverable_interfaces,
1414 );
1415 log::info!("Interface discovery announcer initialized");
1416 driver.interface_announcer = Some(announcer);
1417 }
1418
1419 if let Some(ref cache_dir) = config.cache_dir {
1421 let disc_path = std::path::PathBuf::from(cache_dir)
1422 .parent()
1423 .unwrap_or(std::path::Path::new("."))
1424 .join("storage")
1425 .join("discovery")
1426 .join("interfaces");
1427 let _ = std::fs::create_dir_all(&disc_path);
1428 driver.discovered_interfaces = crate::discovery::DiscoveredInterfaceStorage::new(disc_path);
1429 }
1430
1431 if config.management.enable_remote_management {
1433 if let Some(prv_key) = identity.get_private_key() {
1434 let identity_hash = *identity.hash();
1435 let mgmt_dest = crate::management::management_dest_hash(&identity_hash);
1436
1437 let sig_prv = rns_crypto::ed25519::Ed25519PrivateKey::from_bytes(
1439 &prv_key[32..64].try_into().unwrap(),
1440 );
1441 let sig_pub_bytes: [u8; 32] = identity
1442 .get_public_key()
1443 .unwrap()[32..64]
1444 .try_into()
1445 .unwrap();
1446
1447 driver.engine.register_destination(
1449 mgmt_dest,
1450 rns_core::constants::DESTINATION_SINGLE,
1451 );
1452 driver.local_destinations.insert(
1453 mgmt_dest,
1454 rns_core::constants::DESTINATION_SINGLE,
1455 );
1456
1457 driver.link_manager.register_link_destination(
1459 mgmt_dest,
1460 sig_prv,
1461 sig_pub_bytes,
1462 crate::link_manager::ResourceStrategy::AcceptNone,
1463 );
1464
1465 driver.link_manager.register_management_path(
1467 crate::management::status_path_hash(),
1468 );
1469 driver.link_manager.register_management_path(
1470 crate::management::path_path_hash(),
1471 );
1472
1473 log::info!(
1474 "Remote management enabled on {:02x?}",
1475 &mgmt_dest[..4],
1476 );
1477
1478 if !config.management.remote_management_allowed.is_empty() {
1480 log::info!(
1481 "Remote management allowed for {} identities",
1482 config.management.remote_management_allowed.len(),
1483 );
1484 }
1485 }
1486 }
1487
1488 if config.management.publish_blackhole {
1489 if let Some(prv_key) = identity.get_private_key() {
1490 let identity_hash = *identity.hash();
1491 let bh_dest = crate::management::blackhole_dest_hash(&identity_hash);
1492
1493 let sig_prv = rns_crypto::ed25519::Ed25519PrivateKey::from_bytes(
1494 &prv_key[32..64].try_into().unwrap(),
1495 );
1496 let sig_pub_bytes: [u8; 32] = identity
1497 .get_public_key()
1498 .unwrap()[32..64]
1499 .try_into()
1500 .unwrap();
1501
1502 driver.engine.register_destination(
1503 bh_dest,
1504 rns_core::constants::DESTINATION_SINGLE,
1505 );
1506 driver.link_manager.register_link_destination(
1507 bh_dest,
1508 sig_prv,
1509 sig_pub_bytes,
1510 crate::link_manager::ResourceStrategy::AcceptNone,
1511 );
1512 driver.link_manager.register_management_path(
1513 crate::management::list_path_hash(),
1514 );
1515
1516 log::info!(
1517 "Blackhole list publishing enabled on {:02x?}",
1518 &bh_dest[..4],
1519 );
1520 }
1521 }
1522
1523 if config.respond_to_probes && config.transport_enabled {
1525 let identity_hash = *identity.hash();
1526 let probe_dest = crate::management::probe_dest_hash(&identity_hash);
1527
1528 driver.engine.register_destination(
1530 probe_dest,
1531 rns_core::constants::DESTINATION_SINGLE,
1532 );
1533 driver.local_destinations.insert(
1534 probe_dest,
1535 rns_core::constants::DESTINATION_SINGLE,
1536 );
1537
1538 let probe_identity = rns_crypto::identity::Identity::from_private_key(
1540 &identity.get_private_key().unwrap(),
1541 );
1542 driver.proof_strategies.insert(
1543 probe_dest,
1544 (
1545 rns_core::types::ProofStrategy::ProveAll,
1546 Some(probe_identity),
1547 ),
1548 );
1549
1550 driver.probe_responder_hash = Some(probe_dest);
1551
1552 log::info!(
1553 "Probe responder enabled on {:02x?}",
1554 &probe_dest[..4],
1555 );
1556 }
1557
1558 let tick_interval_ms = Arc::new(AtomicU64::new(1000));
1560 let timer_tx = tx.clone();
1561 let timer_interval = Arc::clone(&tick_interval_ms);
1562 thread::Builder::new()
1563 .name("rns-timer".into())
1564 .spawn(move || {
1565 loop {
1566 let ms = timer_interval.load(Ordering::Relaxed);
1567 thread::sleep(Duration::from_millis(ms));
1568 if timer_tx.send(Event::Tick).is_err() {
1569 break; }
1571 }
1572 })?;
1573
1574 if config.share_instance {
1576 let local_server_config = LocalServerConfig {
1577 instance_name: config.instance_name.clone(),
1578 port: config.shared_instance_port,
1579 interface_id: rns_core::transport::types::InterfaceId(0), };
1581 match crate::interface::local::start_server(
1582 local_server_config,
1583 tx.clone(),
1584 next_dynamic_id.clone(),
1585 ) {
1586 Ok(()) => {
1587 log::info!(
1588 "Local shared instance server started (instance={}, port={})",
1589 config.instance_name,
1590 config.shared_instance_port
1591 );
1592 }
1593 Err(e) => {
1594 log::error!("Failed to start local shared instance server: {}", e);
1595 }
1596 }
1597 }
1598
1599 let rpc_server = if config.share_instance {
1601 let auth_key = crate::rpc::derive_auth_key(
1602 &identity.get_private_key().unwrap_or([0u8; 64]),
1603 );
1604 let rpc_addr = crate::rpc::RpcAddr::Tcp("127.0.0.1".into(), config.rpc_port);
1605 match crate::rpc::RpcServer::start(&rpc_addr, auth_key, tx.clone()) {
1606 Ok(server) => {
1607 log::info!("RPC server started on 127.0.0.1:{}", config.rpc_port);
1608 Some(server)
1609 }
1610 Err(e) => {
1611 log::error!("Failed to start RPC server: {}", e);
1612 None
1613 }
1614 }
1615 } else {
1616 None
1617 };
1618
1619 let driver_handle = thread::Builder::new()
1621 .name("rns-driver".into())
1622 .spawn(move || {
1623 driver.run();
1624 })?;
1625
1626 Ok(RnsNode {
1627 tx,
1628 driver_handle: Some(driver_handle),
1629 rpc_server,
1630 tick_interval_ms,
1631 probe_server,
1632 })
1633 }
1634
1635 pub fn query(&self, request: QueryRequest) -> Result<QueryResponse, SendError> {
1637 let (resp_tx, resp_rx) = std::sync::mpsc::channel();
1638 self.tx
1639 .send(Event::Query(request, resp_tx))
1640 .map_err(|_| SendError)?;
1641 resp_rx.recv().map_err(|_| SendError)
1642 }
1643
1644 pub fn send_raw(
1646 &self,
1647 raw: Vec<u8>,
1648 dest_type: u8,
1649 attached_interface: Option<rns_core::transport::types::InterfaceId>,
1650 ) -> Result<(), SendError> {
1651 self.tx
1652 .send(Event::SendOutbound {
1653 raw,
1654 dest_type,
1655 attached_interface,
1656 })
1657 .map_err(|_| SendError)
1658 }
1659
1660 pub fn register_destination(
1662 &self,
1663 dest_hash: [u8; 16],
1664 dest_type: u8,
1665 ) -> Result<(), SendError> {
1666 self.tx
1667 .send(Event::RegisterDestination { dest_hash, dest_type })
1668 .map_err(|_| SendError)
1669 }
1670
1671 pub fn deregister_destination(&self, dest_hash: [u8; 16]) -> Result<(), SendError> {
1673 self.tx
1674 .send(Event::DeregisterDestination { dest_hash })
1675 .map_err(|_| SendError)
1676 }
1677
1678 pub fn deregister_link_destination(&self, dest_hash: [u8; 16]) -> Result<(), SendError> {
1680 self.tx
1681 .send(Event::DeregisterLinkDestination { dest_hash })
1682 .map_err(|_| SendError)
1683 }
1684
1685 pub fn register_link_destination(
1691 &self,
1692 dest_hash: [u8; 16],
1693 sig_prv_bytes: [u8; 32],
1694 sig_pub_bytes: [u8; 32],
1695 resource_strategy: u8,
1696 ) -> Result<(), SendError> {
1697 self.tx
1698 .send(Event::RegisterLinkDestination {
1699 dest_hash,
1700 sig_prv_bytes,
1701 sig_pub_bytes,
1702 resource_strategy,
1703 })
1704 .map_err(|_| SendError)
1705 }
1706
1707 pub fn register_request_handler<F>(
1709 &self,
1710 path: &str,
1711 allowed_list: Option<Vec<[u8; 16]>>,
1712 handler: F,
1713 ) -> Result<(), SendError>
1714 where
1715 F: Fn([u8; 16], &str, &[u8], Option<&([u8; 16], [u8; 64])>) -> Option<Vec<u8>> + Send + 'static,
1716 {
1717 self.tx
1718 .send(Event::RegisterRequestHandler {
1719 path: path.to_string(),
1720 allowed_list,
1721 handler: Box::new(handler),
1722 })
1723 .map_err(|_| SendError)
1724 }
1725
1726 pub fn create_link(
1730 &self,
1731 dest_hash: [u8; 16],
1732 dest_sig_pub_bytes: [u8; 32],
1733 ) -> Result<[u8; 16], SendError> {
1734 let (response_tx, response_rx) = std::sync::mpsc::channel();
1735 self.tx
1736 .send(Event::CreateLink {
1737 dest_hash,
1738 dest_sig_pub_bytes,
1739 response_tx,
1740 })
1741 .map_err(|_| SendError)?;
1742 response_rx.recv().map_err(|_| SendError)
1743 }
1744
1745 pub fn send_request(
1747 &self,
1748 link_id: [u8; 16],
1749 path: &str,
1750 data: &[u8],
1751 ) -> Result<(), SendError> {
1752 self.tx
1753 .send(Event::SendRequest {
1754 link_id,
1755 path: path.to_string(),
1756 data: data.to_vec(),
1757 })
1758 .map_err(|_| SendError)
1759 }
1760
1761 pub fn identify_on_link(
1763 &self,
1764 link_id: [u8; 16],
1765 identity_prv_key: [u8; 64],
1766 ) -> Result<(), SendError> {
1767 self.tx
1768 .send(Event::IdentifyOnLink {
1769 link_id,
1770 identity_prv_key,
1771 })
1772 .map_err(|_| SendError)
1773 }
1774
1775 pub fn teardown_link(&self, link_id: [u8; 16]) -> Result<(), SendError> {
1777 self.tx
1778 .send(Event::TeardownLink { link_id })
1779 .map_err(|_| SendError)
1780 }
1781
1782 pub fn send_resource(
1784 &self,
1785 link_id: [u8; 16],
1786 data: Vec<u8>,
1787 metadata: Option<Vec<u8>>,
1788 ) -> Result<(), SendError> {
1789 self.tx
1790 .send(Event::SendResource { link_id, data, metadata })
1791 .map_err(|_| SendError)
1792 }
1793
1794 pub fn set_resource_strategy(
1798 &self,
1799 link_id: [u8; 16],
1800 strategy: u8,
1801 ) -> Result<(), SendError> {
1802 self.tx
1803 .send(Event::SetResourceStrategy { link_id, strategy })
1804 .map_err(|_| SendError)
1805 }
1806
1807 pub fn accept_resource(
1809 &self,
1810 link_id: [u8; 16],
1811 resource_hash: Vec<u8>,
1812 accept: bool,
1813 ) -> Result<(), SendError> {
1814 self.tx
1815 .send(Event::AcceptResource { link_id, resource_hash, accept })
1816 .map_err(|_| SendError)
1817 }
1818
1819 pub fn send_channel_message(
1821 &self,
1822 link_id: [u8; 16],
1823 msgtype: u16,
1824 payload: Vec<u8>,
1825 ) -> Result<(), SendError> {
1826 self.tx
1827 .send(Event::SendChannelMessage { link_id, msgtype, payload })
1828 .map_err(|_| SendError)
1829 }
1830
1831 pub fn propose_direct_connect(&self, link_id: [u8; 16]) -> Result<(), SendError> {
1836 self.tx
1837 .send(Event::ProposeDirectConnect { link_id })
1838 .map_err(|_| SendError)
1839 }
1840
1841 pub fn set_direct_connect_policy(
1843 &self,
1844 policy: crate::holepunch::orchestrator::HolePunchPolicy,
1845 ) -> Result<(), SendError> {
1846 self.tx
1847 .send(Event::SetDirectConnectPolicy { policy })
1848 .map_err(|_| SendError)
1849 }
1850
1851 pub fn send_on_link(
1853 &self,
1854 link_id: [u8; 16],
1855 data: Vec<u8>,
1856 context: u8,
1857 ) -> Result<(), SendError> {
1858 self.tx
1859 .send(Event::SendOnLink { link_id, data, context })
1860 .map_err(|_| SendError)
1861 }
1862
1863 pub fn announce(
1868 &self,
1869 dest: &crate::destination::Destination,
1870 identity: &Identity,
1871 app_data: Option<&[u8]>,
1872 ) -> Result<(), SendError> {
1873 let name_hash = rns_core::destination::name_hash(
1874 &dest.app_name,
1875 &dest.aspects.iter().map(|s| s.as_str()).collect::<Vec<_>>(),
1876 );
1877
1878 let mut random_hash = [0u8; 10];
1879 OsRng.fill_bytes(&mut random_hash);
1880
1881 let (announce_data, _has_ratchet) = rns_core::announce::AnnounceData::pack(
1882 identity,
1883 &dest.hash.0,
1884 &name_hash,
1885 &random_hash,
1886 None, app_data,
1888 ).map_err(|_| SendError)?;
1889
1890 let context_flag = rns_core::constants::FLAG_UNSET;
1891
1892 let flags = rns_core::packet::PacketFlags {
1893 header_type: rns_core::constants::HEADER_1,
1894 context_flag,
1895 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
1896 destination_type: rns_core::constants::DESTINATION_SINGLE,
1897 packet_type: rns_core::constants::PACKET_TYPE_ANNOUNCE,
1898 };
1899
1900 let packet = rns_core::packet::RawPacket::pack(
1901 flags, 0, &dest.hash.0, None,
1902 rns_core::constants::CONTEXT_NONE, &announce_data,
1903 ).map_err(|_| SendError)?;
1904
1905 self.send_raw(
1906 packet.raw,
1907 dest.dest_type.to_wire_constant(),
1908 None,
1909 )
1910 }
1911
1912 pub fn send_packet(
1917 &self,
1918 dest: &crate::destination::Destination,
1919 data: &[u8],
1920 ) -> Result<rns_core::types::PacketHash, SendError> {
1921 use rns_core::types::DestinationType;
1922
1923 let payload = match dest.dest_type {
1924 DestinationType::Single => {
1925 let pub_key = dest.public_key.ok_or(SendError)?;
1926 let remote_id = rns_crypto::identity::Identity::from_public_key(&pub_key);
1927 remote_id.encrypt(data, &mut OsRng).map_err(|_| SendError)?
1928 }
1929 DestinationType::Plain => data.to_vec(),
1930 DestinationType::Group => {
1931 dest.encrypt(data).map_err(|_| SendError)?
1932 }
1933 };
1934
1935 let flags = rns_core::packet::PacketFlags {
1936 header_type: rns_core::constants::HEADER_1,
1937 context_flag: rns_core::constants::FLAG_UNSET,
1938 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
1939 destination_type: dest.dest_type.to_wire_constant(),
1940 packet_type: rns_core::constants::PACKET_TYPE_DATA,
1941 };
1942
1943 let packet = rns_core::packet::RawPacket::pack(
1944 flags, 0, &dest.hash.0, None,
1945 rns_core::constants::CONTEXT_NONE, &payload,
1946 ).map_err(|_| SendError)?;
1947
1948 let packet_hash = rns_core::types::PacketHash(packet.packet_hash);
1949
1950 self.tx
1951 .send(Event::SendOutbound {
1952 raw: packet.raw,
1953 dest_type: dest.dest_type.to_wire_constant(),
1954 attached_interface: None,
1955 })
1956 .map_err(|_| SendError)?;
1957
1958 Ok(packet_hash)
1959 }
1960
1961 pub fn register_destination_with_proof(
1966 &self,
1967 dest: &crate::destination::Destination,
1968 signing_key: Option<[u8; 64]>,
1969 ) -> Result<(), SendError> {
1970 self.register_destination(dest.hash.0, dest.dest_type.to_wire_constant())?;
1972
1973 if dest.proof_strategy != rns_core::types::ProofStrategy::ProveNone {
1975 self.tx
1976 .send(Event::RegisterProofStrategy {
1977 dest_hash: dest.hash.0,
1978 strategy: dest.proof_strategy,
1979 signing_key,
1980 })
1981 .map_err(|_| SendError)?;
1982 }
1983
1984 Ok(())
1985 }
1986
1987 pub fn request_path(&self, dest_hash: &rns_core::types::DestHash) -> Result<(), SendError> {
1989 self.tx
1990 .send(Event::RequestPath { dest_hash: dest_hash.0 })
1991 .map_err(|_| SendError)
1992 }
1993
1994 pub fn has_path(&self, dest_hash: &rns_core::types::DestHash) -> Result<bool, SendError> {
1996 match self.query(QueryRequest::HasPath { dest_hash: dest_hash.0 })? {
1997 QueryResponse::HasPath(v) => Ok(v),
1998 _ => Ok(false),
1999 }
2000 }
2001
2002 pub fn hops_to(&self, dest_hash: &rns_core::types::DestHash) -> Result<Option<u8>, SendError> {
2004 match self.query(QueryRequest::HopsTo { dest_hash: dest_hash.0 })? {
2005 QueryResponse::HopsTo(v) => Ok(v),
2006 _ => Ok(None),
2007 }
2008 }
2009
2010 pub fn recall_identity(
2012 &self,
2013 dest_hash: &rns_core::types::DestHash,
2014 ) -> Result<Option<crate::destination::AnnouncedIdentity>, SendError> {
2015 match self.query(QueryRequest::RecallIdentity { dest_hash: dest_hash.0 })? {
2016 QueryResponse::RecallIdentity(v) => Ok(v),
2017 _ => Ok(None),
2018 }
2019 }
2020
2021 pub fn load_hook(
2023 &self,
2024 name: String,
2025 wasm_bytes: Vec<u8>,
2026 attach_point: String,
2027 priority: i32,
2028 ) -> Result<Result<(), String>, SendError> {
2029 let (response_tx, response_rx) = std::sync::mpsc::channel();
2030 self.tx
2031 .send(Event::LoadHook {
2032 name,
2033 wasm_bytes,
2034 attach_point,
2035 priority,
2036 response_tx,
2037 })
2038 .map_err(|_| SendError)?;
2039 response_rx.recv().map_err(|_| SendError)
2040 }
2041
2042 pub fn unload_hook(
2044 &self,
2045 name: String,
2046 attach_point: String,
2047 ) -> Result<Result<(), String>, SendError> {
2048 let (response_tx, response_rx) = std::sync::mpsc::channel();
2049 self.tx
2050 .send(Event::UnloadHook {
2051 name,
2052 attach_point,
2053 response_tx,
2054 })
2055 .map_err(|_| SendError)?;
2056 response_rx.recv().map_err(|_| SendError)
2057 }
2058
2059 pub fn reload_hook(
2061 &self,
2062 name: String,
2063 attach_point: String,
2064 wasm_bytes: Vec<u8>,
2065 ) -> Result<Result<(), String>, SendError> {
2066 let (response_tx, response_rx) = std::sync::mpsc::channel();
2067 self.tx
2068 .send(Event::ReloadHook {
2069 name,
2070 attach_point,
2071 wasm_bytes,
2072 response_tx,
2073 })
2074 .map_err(|_| SendError)?;
2075 response_rx.recv().map_err(|_| SendError)
2076 }
2077
2078 pub fn list_hooks(&self) -> Result<Vec<crate::event::HookInfo>, SendError> {
2080 let (response_tx, response_rx) = std::sync::mpsc::channel();
2081 self.tx
2082 .send(Event::ListHooks { response_tx })
2083 .map_err(|_| SendError)?;
2084 response_rx.recv().map_err(|_| SendError)
2085 }
2086
2087 pub(crate) fn from_parts(
2090 tx: EventSender,
2091 driver_handle: thread::JoinHandle<()>,
2092 rpc_server: Option<crate::rpc::RpcServer>,
2093 tick_interval_ms: Arc<AtomicU64>,
2094 ) -> Self {
2095 RnsNode {
2096 tx,
2097 driver_handle: Some(driver_handle),
2098 rpc_server,
2099 tick_interval_ms,
2100 probe_server: None,
2101 }
2102 }
2103
2104 pub fn event_sender(&self) -> &EventSender {
2106 &self.tx
2107 }
2108
2109 pub fn set_tick_interval(&self, ms: u64) -> u64 {
2114 let clamped = ms.clamp(100, 10_000);
2115 if clamped != ms {
2116 log::warn!(
2117 "tick interval {}ms out of range, clamped to {}ms",
2118 ms,
2119 clamped
2120 );
2121 }
2122 self.tick_interval_ms.store(clamped, Ordering::Relaxed);
2123 clamped
2124 }
2125
2126 pub fn tick_interval(&self) -> u64 {
2128 self.tick_interval_ms.load(Ordering::Relaxed)
2129 }
2130
2131 pub fn shutdown(mut self) {
2133 if let Some(mut rpc) = self.rpc_server.take() {
2135 rpc.stop();
2136 }
2137 let _ = self.tx.send(Event::Shutdown);
2138 if let Some(handle) = self.driver_handle.take() {
2139 let _ = handle.join();
2140 }
2141 }
2142}
2143
2144#[cfg(test)]
2145mod tests {
2146 use super::*;
2147 use std::fs;
2148
2149 struct NoopCallbacks;
2150
2151 impl Callbacks for NoopCallbacks {
2152 fn on_announce(&mut self, _: crate::destination::AnnouncedIdentity) {}
2153 fn on_path_updated(&mut self, _: rns_core::types::DestHash, _: u8) {}
2154 fn on_local_delivery(&mut self, _: rns_core::types::DestHash, _: Vec<u8>, _: rns_core::types::PacketHash) {}
2155 }
2156
2157 #[test]
2158 fn start_and_shutdown() {
2159 let node = RnsNode::start(
2160 NodeConfig {
2161 transport_enabled: false,
2162 identity: None,
2163 interfaces: vec![],
2164 share_instance: false,
2165 instance_name: "default".into(),
2166 shared_instance_port: 37428,
2167 rpc_port: 0,
2168 cache_dir: None,
2169 management: Default::default(),
2170 probe_port: None,
2171 probe_addr: None,
2172 device: None,
2173 hooks: Vec::new(),
2174 discover_interfaces: false,
2175 discovery_required_value: None,
2176 respond_to_probes: false,
2177 },
2178 Box::new(NoopCallbacks),
2179 )
2180 .unwrap();
2181 node.shutdown();
2182 }
2183
2184 #[test]
2185 fn start_with_identity() {
2186 let identity = Identity::new(&mut OsRng);
2187 let hash = *identity.hash();
2188 let node = RnsNode::start(
2189 NodeConfig {
2190 transport_enabled: true,
2191 identity: Some(identity),
2192 interfaces: vec![],
2193 share_instance: false,
2194 instance_name: "default".into(),
2195 shared_instance_port: 37428,
2196 rpc_port: 0,
2197 cache_dir: None,
2198 management: Default::default(),
2199 probe_port: None,
2200 probe_addr: None,
2201 device: None,
2202 hooks: Vec::new(),
2203 discover_interfaces: false,
2204 discovery_required_value: None,
2205 respond_to_probes: false,
2206 },
2207 Box::new(NoopCallbacks),
2208 )
2209 .unwrap();
2210 let _ = hash;
2212 node.shutdown();
2213 }
2214
2215 #[test]
2216 fn start_generates_identity() {
2217 let node = RnsNode::start(
2218 NodeConfig {
2219 transport_enabled: false,
2220 identity: None,
2221 interfaces: vec![],
2222 share_instance: false,
2223 instance_name: "default".into(),
2224 shared_instance_port: 37428,
2225 rpc_port: 0,
2226 cache_dir: None,
2227 management: Default::default(),
2228 probe_port: None,
2229 probe_addr: None,
2230 device: None,
2231 hooks: Vec::new(),
2232 discover_interfaces: false,
2233 discovery_required_value: None,
2234 respond_to_probes: false,
2235 },
2236 Box::new(NoopCallbacks),
2237 )
2238 .unwrap();
2239 node.shutdown();
2241 }
2242
2243 #[test]
2244 fn from_config_creates_identity() {
2245 let dir = std::env::temp_dir().join(format!("rns-test-fc-{}", std::process::id()));
2246 let _ = fs::remove_dir_all(&dir);
2247 fs::create_dir_all(&dir).unwrap();
2248
2249 fs::write(
2251 dir.join("config"),
2252 "[reticulum]\nenable_transport = False\n",
2253 )
2254 .unwrap();
2255
2256 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
2257
2258 assert!(dir.join("storage/identities/identity").exists());
2260
2261 node.shutdown();
2262 let _ = fs::remove_dir_all(&dir);
2263 }
2264
2265 #[test]
2266 fn from_config_loads_identity() {
2267 let dir = std::env::temp_dir().join(format!("rns-test-fl-{}", std::process::id()));
2268 let _ = fs::remove_dir_all(&dir);
2269 fs::create_dir_all(dir.join("storage/identities")).unwrap();
2270
2271 let identity = Identity::new(&mut OsRng);
2273 let hash = *identity.hash();
2274 storage::save_identity(&identity, &dir.join("storage/identities/identity")).unwrap();
2275
2276 fs::write(
2277 dir.join("config"),
2278 "[reticulum]\nenable_transport = False\n",
2279 )
2280 .unwrap();
2281
2282 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
2283
2284 let loaded = storage::load_identity(&dir.join("storage/identities/identity")).unwrap();
2286 assert_eq!(*loaded.hash(), hash);
2287
2288 node.shutdown();
2289 let _ = fs::remove_dir_all(&dir);
2290 }
2291
2292 #[test]
2293 fn from_config_tcp_server() {
2294 let dir = std::env::temp_dir().join(format!("rns-test-fts-{}", std::process::id()));
2295 let _ = fs::remove_dir_all(&dir);
2296 fs::create_dir_all(&dir).unwrap();
2297
2298 let port = std::net::TcpListener::bind("127.0.0.1:0")
2300 .unwrap()
2301 .local_addr()
2302 .unwrap()
2303 .port();
2304
2305 let config = format!(
2306 r#"
2307[reticulum]
2308enable_transport = False
2309
2310[interfaces]
2311 [[Test TCP Server]]
2312 type = TCPServerInterface
2313 listen_ip = 127.0.0.1
2314 listen_port = {}
2315"#,
2316 port
2317 );
2318
2319 fs::write(dir.join("config"), config).unwrap();
2320
2321 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
2322
2323 thread::sleep(Duration::from_millis(100));
2325
2326 let _client = std::net::TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
2328
2329 node.shutdown();
2330 let _ = fs::remove_dir_all(&dir);
2331 }
2332
2333 #[test]
2334 fn test_parse_interface_mode() {
2335 use rns_core::constants::*;
2336
2337 assert_eq!(parse_interface_mode("full"), MODE_FULL);
2338 assert_eq!(parse_interface_mode("Full"), MODE_FULL);
2339 assert_eq!(parse_interface_mode("access_point"), MODE_ACCESS_POINT);
2340 assert_eq!(parse_interface_mode("accesspoint"), MODE_ACCESS_POINT);
2341 assert_eq!(parse_interface_mode("ap"), MODE_ACCESS_POINT);
2342 assert_eq!(parse_interface_mode("AP"), MODE_ACCESS_POINT);
2343 assert_eq!(parse_interface_mode("pointtopoint"), MODE_POINT_TO_POINT);
2344 assert_eq!(parse_interface_mode("ptp"), MODE_POINT_TO_POINT);
2345 assert_eq!(parse_interface_mode("roaming"), MODE_ROAMING);
2346 assert_eq!(parse_interface_mode("boundary"), MODE_BOUNDARY);
2347 assert_eq!(parse_interface_mode("gateway"), MODE_GATEWAY);
2348 assert_eq!(parse_interface_mode("gw"), MODE_GATEWAY);
2349 assert_eq!(parse_interface_mode("invalid"), MODE_FULL);
2351 }
2352
2353 #[test]
2354 fn to_node_config_serial() {
2355 let dir = std::env::temp_dir().join(format!("rns-test-serial-{}", std::process::id()));
2359 let _ = fs::remove_dir_all(&dir);
2360 fs::create_dir_all(&dir).unwrap();
2361
2362 let config = r#"
2363[reticulum]
2364enable_transport = False
2365
2366[interfaces]
2367 [[Test Serial Port]]
2368 type = SerialInterface
2369 port = /dev/nonexistent_rns_test_serial
2370 speed = 115200
2371 databits = 8
2372 parity = E
2373 stopbits = 1
2374 interface_mode = ptp
2375 networkname = testnet
2376"#;
2377 fs::write(dir.join("config"), config).unwrap();
2378
2379 let result = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks));
2380 match result {
2382 Ok(node) => {
2383 node.shutdown();
2384 panic!("Expected error from non-existent serial port");
2385 }
2386 Err(err) => {
2387 let msg = format!("{}", err);
2388 assert!(
2389 !msg.contains("Unsupported") && !msg.contains("parse"),
2390 "Error should be from serial open, got: {}",
2391 msg
2392 );
2393 }
2394 }
2395
2396 let _ = fs::remove_dir_all(&dir);
2397 }
2398
2399 #[test]
2400 fn to_node_config_kiss() {
2401 let dir = std::env::temp_dir().join(format!("rns-test-kiss-{}", std::process::id()));
2403 let _ = fs::remove_dir_all(&dir);
2404 fs::create_dir_all(&dir).unwrap();
2405
2406 let config = r#"
2407[reticulum]
2408enable_transport = False
2409
2410[interfaces]
2411 [[Test KISS TNC]]
2412 type = KISSInterface
2413 port = /dev/nonexistent_rns_test_kiss
2414 speed = 9600
2415 preamble = 500
2416 txtail = 30
2417 persistence = 128
2418 slottime = 40
2419 flow_control = True
2420 id_interval = 600
2421 id_callsign = TEST0
2422 interface_mode = full
2423 passphrase = secretkey
2424"#;
2425 fs::write(dir.join("config"), config).unwrap();
2426
2427 let result = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks));
2428 match result {
2430 Ok(node) => {
2431 node.shutdown();
2432 panic!("Expected error from non-existent serial port");
2433 }
2434 Err(err) => {
2435 let msg = format!("{}", err);
2436 assert!(
2437 !msg.contains("Unsupported") && !msg.contains("parse"),
2438 "Error should be from serial open, got: {}",
2439 msg
2440 );
2441 }
2442 }
2443
2444 let _ = fs::remove_dir_all(&dir);
2445 }
2446
2447 #[test]
2448 fn test_extract_ifac_config() {
2449 use std::collections::HashMap;
2450
2451 let params: HashMap<String, String> = HashMap::new();
2453 assert!(extract_ifac_config(¶ms, 16).is_none());
2454
2455 let mut params = HashMap::new();
2457 params.insert("networkname".into(), "testnet".into());
2458 let ifac = extract_ifac_config(¶ms, 16).unwrap();
2459 assert_eq!(ifac.netname.as_deref(), Some("testnet"));
2460 assert!(ifac.netkey.is_none());
2461 assert_eq!(ifac.size, 16);
2462
2463 let mut params = HashMap::new();
2465 params.insert("passphrase".into(), "secret".into());
2466 params.insert("ifac_size".into(), "64".into()); let ifac = extract_ifac_config(¶ms, 16).unwrap();
2468 assert!(ifac.netname.is_none());
2469 assert_eq!(ifac.netkey.as_deref(), Some("secret"));
2470 assert_eq!(ifac.size, 8);
2471
2472 let mut params = HashMap::new();
2474 params.insert("network_name".into(), "mynet".into());
2475 params.insert("pass_phrase".into(), "mykey".into());
2476 let ifac = extract_ifac_config(¶ms, 8).unwrap();
2477 assert_eq!(ifac.netname.as_deref(), Some("mynet"));
2478 assert_eq!(ifac.netkey.as_deref(), Some("mykey"));
2479 assert_eq!(ifac.size, 8);
2480 }
2481
2482 #[test]
2483 fn test_parse_parity() {
2484 assert_eq!(parse_parity("E"), Parity::Even);
2485 assert_eq!(parse_parity("even"), Parity::Even);
2486 assert_eq!(parse_parity("O"), Parity::Odd);
2487 assert_eq!(parse_parity("odd"), Parity::Odd);
2488 assert_eq!(parse_parity("N"), Parity::None);
2489 assert_eq!(parse_parity("none"), Parity::None);
2490 assert_eq!(parse_parity("unknown"), Parity::None);
2491 }
2492
2493 #[test]
2494 fn to_node_config_rnode() {
2495 let dir = std::env::temp_dir().join(format!("rns-test-rnode-{}", std::process::id()));
2498 let _ = fs::remove_dir_all(&dir);
2499 fs::create_dir_all(&dir).unwrap();
2500
2501 let config = r#"
2502[reticulum]
2503enable_transport = False
2504
2505[interfaces]
2506 [[Test RNode]]
2507 type = RNodeInterface
2508 port = /dev/nonexistent_rns_test_rnode
2509 frequency = 867200000
2510 bandwidth = 125000
2511 txpower = 7
2512 spreadingfactor = 8
2513 codingrate = 5
2514 flow_control = True
2515 st_alock = 5.0
2516 lt_alock = 2.5
2517 interface_mode = full
2518 networkname = testnet
2519"#;
2520 fs::write(dir.join("config"), config).unwrap();
2521
2522 let result = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks));
2523 match result {
2525 Ok(node) => {
2526 node.shutdown();
2527 panic!("Expected error from non-existent serial port");
2528 }
2529 Err(err) => {
2530 let msg = format!("{}", err);
2531 assert!(
2532 !msg.contains("Unsupported") && !msg.contains("parse"),
2533 "Error should be from serial open, got: {}",
2534 msg
2535 );
2536 }
2537 }
2538
2539 let _ = fs::remove_dir_all(&dir);
2540 }
2541
2542 #[test]
2543 fn to_node_config_pipe() {
2544 let dir = std::env::temp_dir().join(format!("rns-test-pipe-{}", std::process::id()));
2547 let _ = fs::remove_dir_all(&dir);
2548 fs::create_dir_all(&dir).unwrap();
2549
2550 let config = r#"
2551[reticulum]
2552enable_transport = False
2553
2554[interfaces]
2555 [[Test Pipe]]
2556 type = PipeInterface
2557 command = cat
2558 respawn_delay = 5000
2559 interface_mode = full
2560"#;
2561 fs::write(dir.join("config"), config).unwrap();
2562
2563 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
2564 node.shutdown();
2566
2567 let _ = fs::remove_dir_all(&dir);
2568 }
2569
2570 #[test]
2571 fn to_node_config_backbone() {
2572 let dir = std::env::temp_dir().join(format!("rns-test-backbone-{}", std::process::id()));
2574 let _ = fs::remove_dir_all(&dir);
2575 fs::create_dir_all(&dir).unwrap();
2576
2577 let port = std::net::TcpListener::bind("127.0.0.1:0")
2578 .unwrap()
2579 .local_addr()
2580 .unwrap()
2581 .port();
2582
2583 let config = format!(
2584 r#"
2585[reticulum]
2586enable_transport = False
2587
2588[interfaces]
2589 [[Test Backbone]]
2590 type = BackboneInterface
2591 listen_ip = 127.0.0.1
2592 listen_port = {}
2593 interface_mode = full
2594"#,
2595 port
2596 );
2597
2598 fs::write(dir.join("config"), config).unwrap();
2599
2600 let node = RnsNode::from_config(Some(&dir), Box::new(NoopCallbacks)).unwrap();
2601
2602 thread::sleep(Duration::from_millis(100));
2604
2605 {
2607 let _client = std::net::TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
2608 }
2610
2611 thread::sleep(Duration::from_millis(50));
2613
2614 node.shutdown();
2615 let _ = fs::remove_dir_all(&dir);
2616 }
2617
2618 #[test]
2619 fn rnode_config_defaults() {
2620 use crate::interface::rnode::{RNodeConfig, RNodeSubConfig};
2621
2622 let config = RNodeConfig::default();
2623 assert_eq!(config.speed, 115200);
2624 assert!(config.subinterfaces.is_empty());
2625 assert!(config.id_interval.is_none());
2626 assert!(config.id_callsign.is_none());
2627
2628 let sub = RNodeSubConfig {
2629 name: "test".into(),
2630 frequency: 868_000_000,
2631 bandwidth: 125_000,
2632 txpower: 7,
2633 spreading_factor: 8,
2634 coding_rate: 5,
2635 flow_control: false,
2636 st_alock: None,
2637 lt_alock: None,
2638 };
2639 assert_eq!(sub.frequency, 868_000_000);
2640 assert_eq!(sub.bandwidth, 125_000);
2641 assert!(!sub.flow_control);
2642 }
2643
2644 #[test]
2649 fn announce_builds_valid_packet() {
2650 let identity = Identity::new(&mut OsRng);
2651 let identity_hash = rns_core::types::IdentityHash(*identity.hash());
2652
2653 let node = RnsNode::start(
2654 NodeConfig {
2655 transport_enabled: false,
2656 identity: None,
2657 interfaces: vec![],
2658 share_instance: false,
2659 instance_name: "default".into(),
2660 shared_instance_port: 37428,
2661 rpc_port: 0,
2662 cache_dir: None,
2663 management: Default::default(),
2664 probe_port: None,
2665 probe_addr: None,
2666 device: None,
2667 hooks: Vec::new(),
2668 discover_interfaces: false,
2669 discovery_required_value: None,
2670 respond_to_probes: false,
2671 },
2672 Box::new(NoopCallbacks),
2673 ).unwrap();
2674
2675 let dest = crate::destination::Destination::single_in(
2676 "test", &["echo"], identity_hash,
2677 );
2678
2679 node.register_destination(dest.hash.0, dest.dest_type.to_wire_constant()).unwrap();
2681
2682 let result = node.announce(&dest, &identity, Some(b"hello"));
2684 assert!(result.is_ok());
2685
2686 node.shutdown();
2687 }
2688
2689 #[test]
2690 fn has_path_and_hops_to() {
2691 let node = RnsNode::start(
2692 NodeConfig {
2693 transport_enabled: false,
2694 identity: None,
2695 interfaces: vec![],
2696 share_instance: false,
2697 instance_name: "default".into(),
2698 shared_instance_port: 37428,
2699 rpc_port: 0,
2700 cache_dir: None,
2701 management: Default::default(),
2702 probe_port: None,
2703 probe_addr: None,
2704 device: None,
2705 hooks: Vec::new(),
2706 discover_interfaces: false,
2707 discovery_required_value: None,
2708 respond_to_probes: false,
2709 },
2710 Box::new(NoopCallbacks),
2711 ).unwrap();
2712
2713 let dh = rns_core::types::DestHash([0xAA; 16]);
2714
2715 assert_eq!(node.has_path(&dh).unwrap(), false);
2717 assert_eq!(node.hops_to(&dh).unwrap(), None);
2718
2719 node.shutdown();
2720 }
2721
2722 #[test]
2723 fn recall_identity_none_when_unknown() {
2724 let node = RnsNode::start(
2725 NodeConfig {
2726 transport_enabled: false,
2727 identity: None,
2728 interfaces: vec![],
2729 share_instance: false,
2730 instance_name: "default".into(),
2731 shared_instance_port: 37428,
2732 rpc_port: 0,
2733 cache_dir: None,
2734 management: Default::default(),
2735 probe_port: None,
2736 probe_addr: None,
2737 device: None,
2738 hooks: Vec::new(),
2739 discover_interfaces: false,
2740 discovery_required_value: None,
2741 respond_to_probes: false,
2742 },
2743 Box::new(NoopCallbacks),
2744 ).unwrap();
2745
2746 let dh = rns_core::types::DestHash([0xBB; 16]);
2747 assert!(node.recall_identity(&dh).unwrap().is_none());
2748
2749 node.shutdown();
2750 }
2751
2752 #[test]
2753 fn request_path_does_not_crash() {
2754 let node = RnsNode::start(
2755 NodeConfig {
2756 transport_enabled: false,
2757 identity: None,
2758 interfaces: vec![],
2759 share_instance: false,
2760 instance_name: "default".into(),
2761 shared_instance_port: 37428,
2762 rpc_port: 0,
2763 cache_dir: None,
2764 management: Default::default(),
2765 probe_port: None,
2766 probe_addr: None,
2767 device: None,
2768 hooks: Vec::new(),
2769 discover_interfaces: false,
2770 discovery_required_value: None,
2771 respond_to_probes: false,
2772 },
2773 Box::new(NoopCallbacks),
2774 ).unwrap();
2775
2776 let dh = rns_core::types::DestHash([0xCC; 16]);
2777 assert!(node.request_path(&dh).is_ok());
2778
2779 thread::sleep(Duration::from_millis(50));
2781
2782 node.shutdown();
2783 }
2784
2785 #[test]
2790 fn send_packet_plain() {
2791 let node = RnsNode::start(
2792 NodeConfig {
2793 transport_enabled: false,
2794 identity: None,
2795 interfaces: vec![],
2796 share_instance: false,
2797 instance_name: "default".into(),
2798 shared_instance_port: 37428,
2799 rpc_port: 0,
2800 cache_dir: None,
2801 management: Default::default(),
2802 probe_port: None,
2803 probe_addr: None,
2804 device: None,
2805 hooks: Vec::new(),
2806 discover_interfaces: false,
2807 discovery_required_value: None,
2808 respond_to_probes: false,
2809 },
2810 Box::new(NoopCallbacks),
2811 ).unwrap();
2812
2813 let dest = crate::destination::Destination::plain("test", &["echo"]);
2814 let result = node.send_packet(&dest, b"hello world");
2815 assert!(result.is_ok());
2816
2817 let packet_hash = result.unwrap();
2818 assert_ne!(packet_hash.0, [0u8; 32]);
2820
2821 thread::sleep(Duration::from_millis(50));
2823
2824 node.shutdown();
2825 }
2826
2827 #[test]
2828 fn send_packet_single_requires_public_key() {
2829 let node = RnsNode::start(
2830 NodeConfig {
2831 transport_enabled: false,
2832 identity: None,
2833 interfaces: vec![],
2834 share_instance: false,
2835 instance_name: "default".into(),
2836 shared_instance_port: 37428,
2837 rpc_port: 0,
2838 cache_dir: None,
2839 management: Default::default(),
2840 probe_port: None,
2841 probe_addr: None,
2842 device: None,
2843 hooks: Vec::new(),
2844 discover_interfaces: false,
2845 discovery_required_value: None,
2846 respond_to_probes: false,
2847 },
2848 Box::new(NoopCallbacks),
2849 ).unwrap();
2850
2851 let dest = crate::destination::Destination::single_in(
2853 "test", &["echo"],
2854 rns_core::types::IdentityHash([0x42; 16]),
2855 );
2856 let result = node.send_packet(&dest, b"hello");
2857 assert!(result.is_err(), "single_in has no public_key, should fail");
2858
2859 node.shutdown();
2860 }
2861
2862 #[test]
2863 fn send_packet_single_encrypts() {
2864 let node = RnsNode::start(
2865 NodeConfig {
2866 transport_enabled: false,
2867 identity: None,
2868 interfaces: vec![],
2869 share_instance: false,
2870 instance_name: "default".into(),
2871 shared_instance_port: 37428,
2872 rpc_port: 0,
2873 cache_dir: None,
2874 management: Default::default(),
2875 probe_port: None,
2876 probe_addr: None,
2877 device: None,
2878 hooks: Vec::new(),
2879 discover_interfaces: false,
2880 discovery_required_value: None,
2881 respond_to_probes: false,
2882 },
2883 Box::new(NoopCallbacks),
2884 ).unwrap();
2885
2886 let remote_identity = Identity::new(&mut OsRng);
2888 let recalled = crate::destination::AnnouncedIdentity {
2889 dest_hash: rns_core::types::DestHash([0xAA; 16]),
2890 identity_hash: rns_core::types::IdentityHash(*remote_identity.hash()),
2891 public_key: remote_identity.get_public_key().unwrap(),
2892 app_data: None,
2893 hops: 1,
2894 received_at: 0.0,
2895 };
2896 let dest = crate::destination::Destination::single_out("test", &["echo"], &recalled);
2897
2898 let result = node.send_packet(&dest, b"secret message");
2899 assert!(result.is_ok());
2900
2901 let packet_hash = result.unwrap();
2902 assert_ne!(packet_hash.0, [0u8; 32]);
2903
2904 thread::sleep(Duration::from_millis(50));
2905 node.shutdown();
2906 }
2907
2908 #[test]
2909 fn register_destination_with_proof_prove_all() {
2910 let node = RnsNode::start(
2911 NodeConfig {
2912 transport_enabled: false,
2913 identity: None,
2914 interfaces: vec![],
2915 share_instance: false,
2916 instance_name: "default".into(),
2917 shared_instance_port: 37428,
2918 rpc_port: 0,
2919 cache_dir: None,
2920 management: Default::default(),
2921 probe_port: None,
2922 probe_addr: None,
2923 device: None,
2924 hooks: Vec::new(),
2925 discover_interfaces: false,
2926 discovery_required_value: None,
2927 respond_to_probes: false,
2928 },
2929 Box::new(NoopCallbacks),
2930 ).unwrap();
2931
2932 let identity = Identity::new(&mut OsRng);
2933 let ih = rns_core::types::IdentityHash(*identity.hash());
2934 let dest = crate::destination::Destination::single_in("echo", &["request"], ih)
2935 .set_proof_strategy(rns_core::types::ProofStrategy::ProveAll);
2936 let prv_key = identity.get_private_key().unwrap();
2937
2938 let result = node.register_destination_with_proof(&dest, Some(prv_key));
2939 assert!(result.is_ok());
2940
2941 thread::sleep(Duration::from_millis(50));
2943
2944 node.shutdown();
2945 }
2946
2947 #[test]
2948 fn register_destination_with_proof_prove_none() {
2949 let node = RnsNode::start(
2950 NodeConfig {
2951 transport_enabled: false,
2952 identity: None,
2953 interfaces: vec![],
2954 share_instance: false,
2955 instance_name: "default".into(),
2956 shared_instance_port: 37428,
2957 rpc_port: 0,
2958 cache_dir: None,
2959 management: Default::default(),
2960 probe_port: None,
2961 probe_addr: None,
2962 device: None,
2963 hooks: Vec::new(),
2964 discover_interfaces: false,
2965 discovery_required_value: None,
2966 respond_to_probes: false,
2967 },
2968 Box::new(NoopCallbacks),
2969 ).unwrap();
2970
2971 let dest = crate::destination::Destination::plain("test", &["data"])
2973 .set_proof_strategy(rns_core::types::ProofStrategy::ProveNone);
2974
2975 let result = node.register_destination_with_proof(&dest, None);
2976 assert!(result.is_ok());
2977
2978 thread::sleep(Duration::from_millis(50));
2979 node.shutdown();
2980 }
2981}