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