1use std::collections::HashMap;
7use std::net::{IpAddr, SocketAddr};
8
9use serde::{Deserialize, Serialize};
10
11fn parse_external_advert_addr(raw: &str, bind_port: u16) -> Option<SocketAddr> {
19 if let Ok(sa) = raw.parse::<SocketAddr>() {
20 return Some(sa);
21 }
22 let ip: IpAddr = raw.parse().ok()?;
23 Some(SocketAddr::new(ip, bind_port))
24}
25
26fn parse_bind_port(raw: &str) -> Option<u16> {
29 raw.parse::<SocketAddr>().ok().map(|sa| sa.port())
30}
31
32const DEFAULT_UDP_BIND_ADDR: &str = "0.0.0.0:2121";
34
35const DEFAULT_UDP_MTU: u16 = 1280;
37
38const DEFAULT_UDP_RECV_BUF: usize = 16 * 1024 * 1024;
52
53const DEFAULT_UDP_SEND_BUF: usize = 8 * 1024 * 1024;
58
59#[derive(Debug, Clone, Default, Serialize, Deserialize)]
61#[serde(deny_unknown_fields)]
62pub struct UdpConfig {
63 #[serde(default, skip_serializing_if = "Option::is_none")]
68 pub bind_addr: Option<String>,
69
70 #[serde(default, skip_serializing_if = "Option::is_none")]
72 pub mtu: Option<u16>,
73
74 #[serde(default, skip_serializing_if = "Option::is_none")]
76 pub recv_buf_size: Option<usize>,
77
78 #[serde(default, skip_serializing_if = "Option::is_none")]
80 pub send_buf_size: Option<usize>,
81
82 #[serde(default, skip_serializing_if = "Option::is_none")]
85 pub advertise_on_nostr: Option<bool>,
86
87 #[serde(default, skip_serializing_if = "Option::is_none")]
93 pub public: Option<bool>,
94 #[serde(default, skip_serializing_if = "Option::is_none")]
103 pub external_addr: Option<String>,
104 #[serde(default, skip_serializing_if = "Option::is_none")]
112 pub outbound_only: Option<bool>,
113
114 #[serde(default, skip_serializing_if = "Option::is_none")]
123 pub accept_connections: Option<bool>,
124}
125
126impl UdpConfig {
127 pub fn bind_addr(&self) -> &str {
132 if self.outbound_only() {
133 "0.0.0.0:0"
134 } else {
135 self.bind_addr.as_deref().unwrap_or(DEFAULT_UDP_BIND_ADDR)
136 }
137 }
138
139 pub fn mtu(&self) -> u16 {
141 self.mtu.unwrap_or(DEFAULT_UDP_MTU)
142 }
143
144 pub fn recv_buf_size(&self) -> usize {
146 self.recv_buf_size.unwrap_or(DEFAULT_UDP_RECV_BUF)
147 }
148
149 pub fn send_buf_size(&self) -> usize {
151 self.send_buf_size.unwrap_or(DEFAULT_UDP_SEND_BUF)
152 }
153
154 pub fn advertise_on_nostr(&self) -> bool {
157 if self.outbound_only() {
158 false
159 } else {
160 self.advertise_on_nostr.unwrap_or(false)
161 }
162 }
163
164 pub fn is_public(&self) -> bool {
166 self.public.unwrap_or(false)
167 }
168
169 pub fn external_advert_addr(&self) -> Option<SocketAddr> {
174 let raw = self.external_addr.as_deref()?;
175 let bind_port = parse_bind_port(self.bind_addr())?;
176 parse_external_advert_addr(raw, bind_port)
177 }
178
179 pub fn outbound_only(&self) -> bool {
181 self.outbound_only.unwrap_or(false)
182 }
183
184 pub fn accept_connections(&self) -> bool {
186 self.accept_connections.unwrap_or(true)
187 }
188}
189
190#[cfg(feature = "sim-transport")]
192const DEFAULT_SIM_MTU: u16 = 1280;
193
194#[cfg(feature = "sim-transport")]
196const DEFAULT_SIM_NETWORK: &str = "default";
197
198#[cfg(feature = "sim-transport")]
205#[derive(Debug, Clone, Default, Serialize, Deserialize)]
206#[serde(deny_unknown_fields)]
207pub struct SimTransportConfig {
208 #[serde(default, skip_serializing_if = "Option::is_none")]
210 pub network: Option<String>,
211
212 #[serde(default, skip_serializing_if = "Option::is_none")]
214 pub addr: Option<String>,
215
216 #[serde(default, skip_serializing_if = "Option::is_none")]
218 pub mtu: Option<u16>,
219
220 #[serde(default, skip_serializing_if = "Option::is_none")]
222 pub auto_connect: Option<bool>,
223
224 #[serde(default, skip_serializing_if = "Option::is_none")]
226 pub accept_connections: Option<bool>,
227}
228
229#[cfg(feature = "sim-transport")]
230impl SimTransportConfig {
231 pub fn network(&self) -> &str {
233 self.network.as_deref().unwrap_or(DEFAULT_SIM_NETWORK)
234 }
235
236 pub fn mtu(&self) -> u16 {
238 self.mtu.unwrap_or(DEFAULT_SIM_MTU)
239 }
240
241 pub fn auto_connect(&self) -> bool {
243 self.auto_connect.unwrap_or(false)
244 }
245
246 pub fn accept_connections(&self) -> bool {
248 self.accept_connections.unwrap_or(true)
249 }
250}
251
252#[derive(Debug, Clone, Serialize, Deserialize)]
271#[serde(untagged)]
272pub enum TransportInstances<T> {
273 Single(T),
275 Named(HashMap<String, T>),
277}
278
279impl<T> TransportInstances<T> {
280 pub fn len(&self) -> usize {
282 match self {
283 TransportInstances::Single(_) => 1,
284 TransportInstances::Named(map) => map.len(),
285 }
286 }
287
288 pub fn is_empty(&self) -> bool {
290 match self {
291 TransportInstances::Single(_) => false,
292 TransportInstances::Named(map) => map.is_empty(),
293 }
294 }
295
296 pub fn iter(&self) -> impl Iterator<Item = (Option<&str>, &T)> {
301 match self {
302 TransportInstances::Single(config) => vec![(None, config)].into_iter(),
303 TransportInstances::Named(map) => map
304 .iter()
305 .map(|(k, v)| (Some(k.as_str()), v))
306 .collect::<Vec<_>>()
307 .into_iter(),
308 }
309 }
310}
311
312impl<T> Default for TransportInstances<T> {
313 fn default() -> Self {
314 TransportInstances::Named(HashMap::new())
315 }
316}
317
318const DEFAULT_ETHERNET_ETHERTYPE: u16 = 0x2121;
320
321const DEFAULT_ETHERNET_RECV_BUF: usize = 2 * 1024 * 1024;
323
324const DEFAULT_ETHERNET_SEND_BUF: usize = 2 * 1024 * 1024;
326
327const DEFAULT_BEACON_INTERVAL_SECS: u64 = 30;
329
330const MIN_BEACON_INTERVAL_SECS: u64 = 10;
332
333#[derive(Debug, Clone, Default, Serialize, Deserialize)]
338#[serde(deny_unknown_fields)]
339pub struct EthernetConfig {
340 pub interface: String,
342
343 #[serde(default, skip_serializing_if = "Option::is_none")]
345 pub ethertype: Option<u16>,
346
347 #[serde(default, skip_serializing_if = "Option::is_none")]
350 pub mtu: Option<u16>,
351
352 #[serde(default, skip_serializing_if = "Option::is_none")]
354 pub recv_buf_size: Option<usize>,
355
356 #[serde(default, skip_serializing_if = "Option::is_none")]
358 pub send_buf_size: Option<usize>,
359
360 #[serde(default, skip_serializing_if = "Option::is_none")]
362 pub discovery: Option<bool>,
363
364 #[serde(default, skip_serializing_if = "Option::is_none")]
366 pub announce: Option<bool>,
367
368 #[serde(default, skip_serializing_if = "Option::is_none")]
370 pub auto_connect: Option<bool>,
371
372 #[serde(default, skip_serializing_if = "Option::is_none")]
374 pub accept_connections: Option<bool>,
375
376 #[serde(default, skip_serializing_if = "Option::is_none")]
382 pub discovery_scope: Option<String>,
383
384 #[serde(default, skip_serializing_if = "Option::is_none")]
386 pub beacon_interval_secs: Option<u64>,
387}
388
389impl EthernetConfig {
390 pub fn ethertype(&self) -> u16 {
392 self.ethertype.unwrap_or(DEFAULT_ETHERNET_ETHERTYPE)
393 }
394
395 pub fn recv_buf_size(&self) -> usize {
397 self.recv_buf_size.unwrap_or(DEFAULT_ETHERNET_RECV_BUF)
398 }
399
400 pub fn send_buf_size(&self) -> usize {
402 self.send_buf_size.unwrap_or(DEFAULT_ETHERNET_SEND_BUF)
403 }
404
405 pub fn discovery(&self) -> bool {
407 self.discovery.unwrap_or(true)
408 }
409
410 pub fn announce(&self) -> bool {
412 self.announce.unwrap_or(false)
413 }
414
415 pub fn auto_connect(&self) -> bool {
417 self.auto_connect.unwrap_or(false)
418 }
419
420 pub fn accept_connections(&self) -> bool {
422 self.accept_connections.unwrap_or(false)
423 }
424
425 pub fn discovery_scope(&self) -> Option<&str> {
427 self.discovery_scope.as_deref().filter(|s| !s.is_empty())
428 }
429
430 pub fn beacon_interval_secs(&self) -> u64 {
432 self.beacon_interval_secs
433 .unwrap_or(DEFAULT_BEACON_INTERVAL_SECS)
434 .max(MIN_BEACON_INTERVAL_SECS)
435 }
436}
437
438const DEFAULT_TCP_MTU: u16 = 1400;
444
445const DEFAULT_TCP_CONNECT_TIMEOUT_MS: u64 = 5000;
447
448const DEFAULT_TCP_FIRST_FRAME_TIMEOUT_MS: u64 = 3000;
451
452const DEFAULT_TCP_KEEPALIVE_SECS: u64 = 30;
454
455const DEFAULT_TCP_RECV_BUF: usize = 2 * 1024 * 1024;
457
458const DEFAULT_TCP_SEND_BUF: usize = 2 * 1024 * 1024;
460
461const DEFAULT_TCP_MAX_INBOUND: usize = 256;
463
464const DEFAULT_WEBSOCKET_PATH: &str = "/fips";
466
467const DEFAULT_WEBSOCKET_MTU: u16 = 1400;
469
470const DEFAULT_WEBSOCKET_MAX_FRAME_BYTES: usize = 66 * 1024;
472
473const DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS: u64 = 5_000;
474const DEFAULT_WEBSOCKET_KEY_HINT_TIMEOUT_MS: u64 = 3_000;
475const DEFAULT_WEBSOCKET_RECONNECT_INITIAL_MS: u64 = 1_000;
476const DEFAULT_WEBSOCKET_RECONNECT_MAX_MS: u64 = 30_000;
477const DEFAULT_WEBSOCKET_MAX_CONNECTIONS: usize = 256;
478const DEFAULT_WEBSOCKET_MAX_INBOUND: usize = 128;
479const DEFAULT_WEBSOCKET_MAX_SEND_QUEUE: usize = 256;
480const DEFAULT_WEBSOCKET_PING_INTERVAL_SECS: u64 = 20;
481const DEFAULT_WEBSOCKET_IDLE_TIMEOUT_SECS: u64 = 90;
482
483#[derive(Debug, Clone, Default, Serialize, Deserialize)]
485#[serde(deny_unknown_fields)]
486pub struct TcpConfig {
487 #[serde(default, skip_serializing_if = "Option::is_none")]
489 pub bind_addr: Option<String>,
490
491 #[serde(default, skip_serializing_if = "Option::is_none")]
495 pub mtu: Option<u16>,
496
497 #[serde(default, skip_serializing_if = "Option::is_none")]
499 pub connect_timeout_ms: Option<u64>,
500
501 #[serde(default, skip_serializing_if = "Option::is_none")]
505 pub first_frame_timeout_ms: Option<u64>,
506
507 #[serde(default, skip_serializing_if = "Option::is_none")]
509 pub nodelay: Option<bool>,
510
511 #[serde(default, skip_serializing_if = "Option::is_none")]
513 pub keepalive_secs: Option<u64>,
514
515 #[serde(default, skip_serializing_if = "Option::is_none")]
517 pub recv_buf_size: Option<usize>,
518
519 #[serde(default, skip_serializing_if = "Option::is_none")]
521 pub send_buf_size: Option<usize>,
522
523 #[serde(default, skip_serializing_if = "Option::is_none")]
525 pub max_inbound_connections: Option<usize>,
526
527 #[serde(default, skip_serializing_if = "Option::is_none")]
530 pub advertise_on_nostr: Option<bool>,
531
532 #[serde(default, skip_serializing_if = "Option::is_none")]
540 pub external_addr: Option<String>,
541}
542
543impl TcpConfig {
544 pub fn mtu(&self) -> u16 {
546 self.mtu.unwrap_or(DEFAULT_TCP_MTU)
547 }
548
549 pub fn connect_timeout_ms(&self) -> u64 {
551 self.connect_timeout_ms
552 .unwrap_or(DEFAULT_TCP_CONNECT_TIMEOUT_MS)
553 }
554
555 pub fn first_frame_timeout_ms(&self) -> u64 {
557 self.first_frame_timeout_ms
558 .unwrap_or(DEFAULT_TCP_FIRST_FRAME_TIMEOUT_MS)
559 }
560
561 pub fn nodelay(&self) -> bool {
563 self.nodelay.unwrap_or(true)
564 }
565
566 pub fn keepalive_secs(&self) -> u64 {
568 self.keepalive_secs.unwrap_or(DEFAULT_TCP_KEEPALIVE_SECS)
569 }
570
571 pub fn recv_buf_size(&self) -> usize {
573 self.recv_buf_size.unwrap_or(DEFAULT_TCP_RECV_BUF)
574 }
575
576 pub fn send_buf_size(&self) -> usize {
578 self.send_buf_size.unwrap_or(DEFAULT_TCP_SEND_BUF)
579 }
580
581 pub fn max_inbound_connections(&self) -> usize {
583 self.max_inbound_connections
584 .unwrap_or(DEFAULT_TCP_MAX_INBOUND)
585 }
586
587 pub fn advertise_on_nostr(&self) -> bool {
589 self.advertise_on_nostr.unwrap_or(false)
590 }
591
592 pub fn external_advert_addr(&self) -> Option<SocketAddr> {
597 let raw = self.external_addr.as_deref()?;
598 let bind_port = parse_bind_port(self.bind_addr.as_deref()?)?;
599 parse_external_advert_addr(raw, bind_port)
600 }
601}
602
603#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
613#[serde(deny_unknown_fields)]
614pub struct WebSocketConfig {
615 #[serde(default, skip_serializing_if = "Option::is_none")]
617 pub bind_addr: Option<String>,
618
619 #[serde(default, skip_serializing_if = "Option::is_none")]
621 pub public_url: Option<String>,
622
623 #[serde(default, skip_serializing_if = "Vec::is_empty")]
625 pub seed_urls: Vec<String>,
626
627 #[serde(default, skip_serializing_if = "Option::is_none")]
629 pub path: Option<String>,
630
631 #[serde(default, skip_serializing_if = "Option::is_none")]
633 pub mtu: Option<u16>,
634
635 #[serde(default, skip_serializing_if = "Option::is_none")]
637 pub max_frame_bytes: Option<usize>,
638
639 #[serde(default, skip_serializing_if = "Option::is_none")]
641 pub max_send_queue: Option<usize>,
642
643 #[serde(default, skip_serializing_if = "Option::is_none")]
645 pub max_connections: Option<usize>,
646
647 #[serde(default, skip_serializing_if = "Option::is_none")]
649 pub max_inbound_connections: Option<usize>,
650
651 #[serde(default, skip_serializing_if = "Option::is_none")]
653 pub connect_timeout_ms: Option<u64>,
654
655 #[serde(default, skip_serializing_if = "Option::is_none")]
657 pub key_hint_timeout_ms: Option<u64>,
658
659 #[serde(default, skip_serializing_if = "Option::is_none")]
661 pub reconnect_initial_ms: Option<u64>,
662
663 #[serde(default, skip_serializing_if = "Option::is_none")]
665 pub reconnect_max_ms: Option<u64>,
666
667 #[serde(default, skip_serializing_if = "Option::is_none")]
669 pub ping_interval_secs: Option<u64>,
670
671 #[serde(default, skip_serializing_if = "Option::is_none")]
673 pub idle_timeout_secs: Option<u64>,
674
675 #[serde(default, skip_serializing_if = "Option::is_none")]
679 pub accept_connections: Option<bool>,
680}
681
682impl WebSocketConfig {
683 pub fn path(&self) -> &str {
684 self.path.as_deref().unwrap_or(DEFAULT_WEBSOCKET_PATH)
685 }
686
687 pub fn mtu(&self) -> u16 {
688 self.mtu.unwrap_or(DEFAULT_WEBSOCKET_MTU)
689 }
690
691 pub fn max_frame_bytes(&self) -> usize {
692 self.max_frame_bytes
693 .unwrap_or(DEFAULT_WEBSOCKET_MAX_FRAME_BYTES)
694 }
695
696 pub fn max_send_queue(&self) -> usize {
697 self.max_send_queue
698 .unwrap_or(DEFAULT_WEBSOCKET_MAX_SEND_QUEUE)
699 .max(1)
700 }
701
702 pub fn max_connections(&self) -> usize {
703 self.max_connections
704 .unwrap_or(DEFAULT_WEBSOCKET_MAX_CONNECTIONS)
705 .max(1)
706 }
707
708 pub fn max_inbound_connections(&self) -> usize {
709 self.max_inbound_connections
710 .unwrap_or(DEFAULT_WEBSOCKET_MAX_INBOUND)
711 .max(1)
712 .min(self.max_connections())
713 }
714
715 pub fn connect_timeout_ms(&self) -> u64 {
716 self.connect_timeout_ms
717 .unwrap_or(DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS)
718 .max(1)
719 }
720
721 pub fn key_hint_timeout_ms(&self) -> u64 {
722 self.key_hint_timeout_ms
723 .unwrap_or(DEFAULT_WEBSOCKET_KEY_HINT_TIMEOUT_MS)
724 .max(1)
725 }
726
727 pub fn reconnect_initial_ms(&self) -> u64 {
728 self.reconnect_initial_ms
729 .unwrap_or(DEFAULT_WEBSOCKET_RECONNECT_INITIAL_MS)
730 .max(1)
731 }
732
733 pub fn reconnect_max_ms(&self) -> u64 {
734 self.reconnect_max_ms
735 .unwrap_or(DEFAULT_WEBSOCKET_RECONNECT_MAX_MS)
736 .max(self.reconnect_initial_ms())
737 }
738
739 pub fn ping_interval_secs(&self) -> u64 {
740 self.ping_interval_secs
741 .unwrap_or(DEFAULT_WEBSOCKET_PING_INTERVAL_SECS)
742 }
743
744 pub fn idle_timeout_secs(&self) -> u64 {
745 self.idle_timeout_secs
746 .unwrap_or(DEFAULT_WEBSOCKET_IDLE_TIMEOUT_SECS)
747 }
748
749 pub fn accept_connections(&self) -> bool {
750 self.accept_connections
751 .unwrap_or_else(|| self.bind_addr.is_some() || !self.seed_urls.is_empty())
752 }
753
754 pub fn validate(&self) -> Result<(), String> {
755 if let Some(bind_addr) = self.bind_addr.as_deref() {
756 bind_addr
757 .parse::<SocketAddr>()
758 .map_err(|error| format!("invalid bind_addr {bind_addr:?}: {error}"))?;
759 }
760 if !self.path().starts_with('/') || self.path().contains('?') || self.path().contains('#') {
761 return Err("path must be an absolute HTTP path without query or fragment".into());
762 }
763 if let Some(public_url) = self.public_url.as_deref() {
764 validate_websocket_url(public_url, false)?;
765 let uri = public_url
766 .parse::<tokio_tungstenite::tungstenite::http::Uri>()
767 .map_err(|error| format!("invalid public_url: {error}"))?;
768 if uri.path() != self.path() {
769 return Err(format!(
770 "public_url path {:?} does not match configured path {:?}",
771 uri.path(),
772 self.path()
773 ));
774 }
775 if self.bind_addr.is_none() {
776 return Err("public_url requires bind_addr".into());
777 }
778 }
779 let mut unique = std::collections::HashSet::new();
780 for seed_url in &self.seed_urls {
781 validate_websocket_url(seed_url, true)?;
782 if !unique.insert(seed_url) {
783 return Err(format!("duplicate seed URL {seed_url:?}"));
784 }
785 }
786 let minimum_frame = usize::from(self.mtu()).saturating_add(64);
787 if self.max_frame_bytes() < minimum_frame || self.max_frame_bytes() > 1024 * 1024 {
788 return Err(format!(
789 "max_frame_bytes must be between {minimum_frame} and 1048576"
790 ));
791 }
792 if self.max_send_queue() > 4096 {
793 return Err("max_send_queue must not exceed 4096".into());
794 }
795 if self.max_connections() > 4096 {
796 return Err("max_connections must not exceed 4096".into());
797 }
798 if self.max_inbound_connections() > self.max_connections() {
799 return Err("max_inbound_connections must not exceed max_connections".into());
800 }
801 if self.ping_interval_secs() > 0
802 && self.idle_timeout_secs() > 0
803 && self.idle_timeout_secs() <= self.ping_interval_secs()
804 {
805 return Err("idle_timeout_secs must exceed ping_interval_secs".into());
806 }
807 Ok(())
808 }
809}
810
811fn validate_websocket_url(raw: &str, allow_loopback_plaintext: bool) -> Result<(), String> {
812 let uri = raw
813 .parse::<tokio_tungstenite::tungstenite::http::Uri>()
814 .map_err(|error| format!("invalid WebSocket URL {raw:?}: {error}"))?;
815 let scheme = uri
816 .scheme_str()
817 .ok_or_else(|| format!("WebSocket URL {raw:?} is missing a scheme"))?;
818 let host = uri
819 .host()
820 .ok_or_else(|| format!("WebSocket URL {raw:?} is missing a host"))?;
821 if uri.authority().is_none() || uri.path().is_empty() {
822 return Err(format!("invalid WebSocket URL {raw:?}"));
823 }
824 match scheme {
825 "wss" => Ok(()),
826 "ws" if allow_loopback_plaintext && websocket_host_is_loopback(host) => Ok(()),
827 "ws" => Err(format!(
828 "plaintext WebSocket URL {raw:?} is allowed only for loopback seeds"
829 )),
830 _ => Err(format!("WebSocket URL {raw:?} must use wss://")),
831 }
832}
833
834fn websocket_host_is_loopback(host: &str) -> bool {
835 host.eq_ignore_ascii_case("localhost")
836 || host
837 .trim_matches(['[', ']'])
838 .parse::<IpAddr>()
839 .is_ok_and(|ip| ip.is_loopback())
840}
841
842mod aggregate;
843mod ble;
844#[cfg(test)]
845mod tests;
846mod tor;
847mod webrtc;
848
849pub use aggregate::TransportsConfig;
850pub use ble::BleConfig;
851pub use tor::{DirectoryServiceConfig, TorConfig};
852pub use webrtc::WebRtcConfig;