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")]
77 pub bind_interface: Option<String>,
78
79 #[serde(default, skip_serializing_if = "Option::is_none")]
81 pub mtu: Option<u16>,
82
83 #[serde(default, skip_serializing_if = "Option::is_none")]
85 pub recv_buf_size: Option<usize>,
86
87 #[serde(default, skip_serializing_if = "Option::is_none")]
89 pub send_buf_size: Option<usize>,
90
91 #[serde(default, skip_serializing_if = "Option::is_none")]
94 pub advertise_on_nostr: Option<bool>,
95
96 #[serde(default, skip_serializing_if = "Option::is_none")]
102 pub public: Option<bool>,
103 #[serde(default, skip_serializing_if = "Option::is_none")]
112 pub external_addr: Option<String>,
113 #[serde(default, skip_serializing_if = "Option::is_none")]
121 pub outbound_only: Option<bool>,
122
123 #[serde(default, skip_serializing_if = "Option::is_none")]
132 pub accept_connections: Option<bool>,
133}
134
135impl UdpConfig {
136 pub fn bind_addr(&self) -> &str {
142 if self.outbound_only() {
143 if self
144 .bind_addr
145 .as_deref()
146 .and_then(|addr| addr.parse::<SocketAddr>().ok())
147 .is_some_and(|addr| addr.is_ipv6())
148 {
149 "[::]:0"
150 } else {
151 "0.0.0.0:0"
152 }
153 } else {
154 self.bind_addr.as_deref().unwrap_or(DEFAULT_UDP_BIND_ADDR)
155 }
156 }
157
158 pub fn mtu(&self) -> u16 {
160 self.mtu.unwrap_or(DEFAULT_UDP_MTU)
161 }
162
163 pub fn recv_buf_size(&self) -> usize {
165 self.recv_buf_size.unwrap_or(DEFAULT_UDP_RECV_BUF)
166 }
167
168 pub fn send_buf_size(&self) -> usize {
170 self.send_buf_size.unwrap_or(DEFAULT_UDP_SEND_BUF)
171 }
172
173 pub fn advertise_on_nostr(&self) -> bool {
176 if self.outbound_only() {
177 false
178 } else {
179 self.advertise_on_nostr.unwrap_or(false)
180 }
181 }
182
183 pub fn is_public(&self) -> bool {
185 self.public.unwrap_or(false)
186 }
187
188 pub fn external_advert_addr(&self) -> Option<SocketAddr> {
193 let raw = self.external_addr.as_deref()?;
194 let bind_port = parse_bind_port(self.bind_addr())?;
195 parse_external_advert_addr(raw, bind_port)
196 }
197
198 pub fn outbound_only(&self) -> bool {
200 self.outbound_only.unwrap_or(false)
201 }
202
203 pub fn accept_connections(&self) -> bool {
205 self.accept_connections.unwrap_or(true)
206 }
207}
208
209#[cfg(feature = "sim-transport")]
211const DEFAULT_SIM_MTU: u16 = 1280;
212
213#[cfg(feature = "sim-transport")]
215const DEFAULT_SIM_NETWORK: &str = "default";
216
217#[cfg(feature = "sim-transport")]
224#[derive(Debug, Clone, Default, Serialize, Deserialize)]
225#[serde(deny_unknown_fields)]
226pub struct SimTransportConfig {
227 #[serde(default, skip_serializing_if = "Option::is_none")]
229 pub network: Option<String>,
230
231 #[serde(default, skip_serializing_if = "Option::is_none")]
233 pub addr: Option<String>,
234
235 #[serde(default, skip_serializing_if = "Option::is_none")]
237 pub mtu: Option<u16>,
238
239 #[serde(default, skip_serializing_if = "Option::is_none")]
241 pub auto_connect: Option<bool>,
242
243 #[serde(default, skip_serializing_if = "Option::is_none")]
245 pub accept_connections: Option<bool>,
246}
247
248#[cfg(feature = "sim-transport")]
249impl SimTransportConfig {
250 pub fn network(&self) -> &str {
252 self.network.as_deref().unwrap_or(DEFAULT_SIM_NETWORK)
253 }
254
255 pub fn mtu(&self) -> u16 {
257 self.mtu.unwrap_or(DEFAULT_SIM_MTU)
258 }
259
260 pub fn auto_connect(&self) -> bool {
262 self.auto_connect.unwrap_or(false)
263 }
264
265 pub fn accept_connections(&self) -> bool {
267 self.accept_connections.unwrap_or(true)
268 }
269}
270
271#[derive(Debug, Clone, Serialize, Deserialize)]
290#[serde(untagged)]
291pub enum TransportInstances<T> {
292 Single(T),
294 Named(HashMap<String, T>),
296}
297
298impl<T> TransportInstances<T> {
299 pub fn len(&self) -> usize {
301 match self {
302 TransportInstances::Single(_) => 1,
303 TransportInstances::Named(map) => map.len(),
304 }
305 }
306
307 pub fn is_empty(&self) -> bool {
309 match self {
310 TransportInstances::Single(_) => false,
311 TransportInstances::Named(map) => map.is_empty(),
312 }
313 }
314
315 pub fn iter(&self) -> impl Iterator<Item = (Option<&str>, &T)> {
320 match self {
321 TransportInstances::Single(config) => vec![(None, config)].into_iter(),
322 TransportInstances::Named(map) => map
323 .iter()
324 .map(|(k, v)| (Some(k.as_str()), v))
325 .collect::<Vec<_>>()
326 .into_iter(),
327 }
328 }
329}
330
331impl<T> Default for TransportInstances<T> {
332 fn default() -> Self {
333 TransportInstances::Named(HashMap::new())
334 }
335}
336
337const DEFAULT_ETHERNET_ETHERTYPE: u16 = 0x2121;
339
340const DEFAULT_ETHERNET_RECV_BUF: usize = 2 * 1024 * 1024;
342
343const DEFAULT_ETHERNET_SEND_BUF: usize = 2 * 1024 * 1024;
345
346const DEFAULT_BEACON_INTERVAL_SECS: u64 = 30;
348
349const MIN_BEACON_INTERVAL_SECS: u64 = 10;
351
352#[derive(Debug, Clone, Default, Serialize, Deserialize)]
357#[serde(deny_unknown_fields)]
358pub struct EthernetConfig {
359 pub interface: String,
361
362 #[serde(default, skip_serializing_if = "Option::is_none")]
364 pub ethertype: Option<u16>,
365
366 #[serde(default, skip_serializing_if = "Option::is_none")]
369 pub mtu: Option<u16>,
370
371 #[serde(default, skip_serializing_if = "Option::is_none")]
373 pub recv_buf_size: Option<usize>,
374
375 #[serde(default, skip_serializing_if = "Option::is_none")]
377 pub send_buf_size: Option<usize>,
378
379 #[serde(default, skip_serializing_if = "Option::is_none")]
381 pub discovery: Option<bool>,
382
383 #[serde(default, skip_serializing_if = "Option::is_none")]
385 pub announce: Option<bool>,
386
387 #[serde(default, skip_serializing_if = "Option::is_none")]
389 pub auto_connect: Option<bool>,
390
391 #[serde(default, skip_serializing_if = "Option::is_none")]
393 pub accept_connections: Option<bool>,
394
395 #[serde(default, skip_serializing_if = "Option::is_none")]
401 pub discovery_scope: Option<String>,
402
403 #[serde(default, skip_serializing_if = "Option::is_none")]
405 pub beacon_interval_secs: Option<u64>,
406}
407
408impl EthernetConfig {
409 pub fn ethertype(&self) -> u16 {
411 self.ethertype.unwrap_or(DEFAULT_ETHERNET_ETHERTYPE)
412 }
413
414 pub fn recv_buf_size(&self) -> usize {
416 self.recv_buf_size.unwrap_or(DEFAULT_ETHERNET_RECV_BUF)
417 }
418
419 pub fn send_buf_size(&self) -> usize {
421 self.send_buf_size.unwrap_or(DEFAULT_ETHERNET_SEND_BUF)
422 }
423
424 pub fn discovery(&self) -> bool {
426 self.discovery.unwrap_or(true)
427 }
428
429 pub fn announce(&self) -> bool {
431 self.announce.unwrap_or(false)
432 }
433
434 pub fn auto_connect(&self) -> bool {
436 self.auto_connect.unwrap_or(false)
437 }
438
439 pub fn accept_connections(&self) -> bool {
441 self.accept_connections.unwrap_or(false)
442 }
443
444 pub fn discovery_scope(&self) -> Option<&str> {
446 self.discovery_scope.as_deref().filter(|s| !s.is_empty())
447 }
448
449 pub fn beacon_interval_secs(&self) -> u64 {
451 self.beacon_interval_secs
452 .unwrap_or(DEFAULT_BEACON_INTERVAL_SECS)
453 .max(MIN_BEACON_INTERVAL_SECS)
454 }
455}
456
457const DEFAULT_TCP_MTU: u16 = 1400;
463
464const DEFAULT_TCP_CONNECT_TIMEOUT_MS: u64 = 5000;
466
467const DEFAULT_TCP_FIRST_FRAME_TIMEOUT_MS: u64 = 3000;
470
471const DEFAULT_TCP_KEEPALIVE_SECS: u64 = 30;
473
474const DEFAULT_TCP_RECV_BUF: usize = 2 * 1024 * 1024;
476
477const DEFAULT_TCP_SEND_BUF: usize = 2 * 1024 * 1024;
479
480const DEFAULT_TCP_MAX_INBOUND: usize = 256;
482
483const DEFAULT_WEBSOCKET_PATH: &str = "/fips";
485
486const DEFAULT_WEBSOCKET_MTU: u16 = 1400;
488
489const DEFAULT_WEBSOCKET_MAX_FRAME_BYTES: usize = 66 * 1024;
491
492const DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS: u64 = 5_000;
493const DEFAULT_WEBSOCKET_KEY_HINT_TIMEOUT_MS: u64 = 3_000;
494const DEFAULT_WEBSOCKET_RECONNECT_INITIAL_MS: u64 = 1_000;
495const DEFAULT_WEBSOCKET_RECONNECT_MAX_MS: u64 = 30_000;
496const DEFAULT_WEBSOCKET_MAX_CONNECTIONS: usize = 256;
497const DEFAULT_WEBSOCKET_MAX_INBOUND: usize = 128;
498const DEFAULT_WEBSOCKET_MAX_SEND_QUEUE: usize = 256;
499const DEFAULT_WEBSOCKET_PING_INTERVAL_SECS: u64 = 20;
500const DEFAULT_WEBSOCKET_IDLE_TIMEOUT_SECS: u64 = 90;
501
502#[derive(Debug, Clone, Default, Serialize, Deserialize)]
504#[serde(deny_unknown_fields)]
505pub struct TcpConfig {
506 #[serde(default, skip_serializing_if = "Option::is_none")]
508 pub bind_addr: Option<String>,
509
510 #[serde(default, skip_serializing_if = "Option::is_none")]
514 pub mtu: Option<u16>,
515
516 #[serde(default, skip_serializing_if = "Option::is_none")]
518 pub connect_timeout_ms: Option<u64>,
519
520 #[serde(default, skip_serializing_if = "Option::is_none")]
524 pub first_frame_timeout_ms: Option<u64>,
525
526 #[serde(default, skip_serializing_if = "Option::is_none")]
528 pub nodelay: Option<bool>,
529
530 #[serde(default, skip_serializing_if = "Option::is_none")]
532 pub keepalive_secs: Option<u64>,
533
534 #[serde(default, skip_serializing_if = "Option::is_none")]
536 pub recv_buf_size: Option<usize>,
537
538 #[serde(default, skip_serializing_if = "Option::is_none")]
540 pub send_buf_size: Option<usize>,
541
542 #[serde(default, skip_serializing_if = "Option::is_none")]
544 pub max_inbound_connections: Option<usize>,
545
546 #[serde(default, skip_serializing_if = "Option::is_none")]
549 pub advertise_on_nostr: Option<bool>,
550
551 #[serde(default, skip_serializing_if = "Option::is_none")]
559 pub external_addr: Option<String>,
560}
561
562impl TcpConfig {
563 pub fn mtu(&self) -> u16 {
565 self.mtu.unwrap_or(DEFAULT_TCP_MTU)
566 }
567
568 pub fn connect_timeout_ms(&self) -> u64 {
570 self.connect_timeout_ms
571 .unwrap_or(DEFAULT_TCP_CONNECT_TIMEOUT_MS)
572 }
573
574 pub fn first_frame_timeout_ms(&self) -> u64 {
576 self.first_frame_timeout_ms
577 .unwrap_or(DEFAULT_TCP_FIRST_FRAME_TIMEOUT_MS)
578 }
579
580 pub fn nodelay(&self) -> bool {
582 self.nodelay.unwrap_or(true)
583 }
584
585 pub fn keepalive_secs(&self) -> u64 {
587 self.keepalive_secs.unwrap_or(DEFAULT_TCP_KEEPALIVE_SECS)
588 }
589
590 pub fn recv_buf_size(&self) -> usize {
592 self.recv_buf_size.unwrap_or(DEFAULT_TCP_RECV_BUF)
593 }
594
595 pub fn send_buf_size(&self) -> usize {
597 self.send_buf_size.unwrap_or(DEFAULT_TCP_SEND_BUF)
598 }
599
600 pub fn max_inbound_connections(&self) -> usize {
602 self.max_inbound_connections
603 .unwrap_or(DEFAULT_TCP_MAX_INBOUND)
604 }
605
606 pub fn advertise_on_nostr(&self) -> bool {
608 self.advertise_on_nostr.unwrap_or(false)
609 }
610
611 pub fn external_advert_addr(&self) -> Option<SocketAddr> {
616 let raw = self.external_addr.as_deref()?;
617 let bind_port = parse_bind_port(self.bind_addr.as_deref()?)?;
618 parse_external_advert_addr(raw, bind_port)
619 }
620}
621
622#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
632#[serde(deny_unknown_fields)]
633pub struct WebSocketConfig {
634 #[serde(default, skip_serializing_if = "Option::is_none")]
636 pub bind_addr: Option<String>,
637
638 #[serde(default, skip_serializing_if = "Option::is_none")]
640 pub public_url: Option<String>,
641
642 #[serde(default, skip_serializing_if = "Vec::is_empty")]
644 pub seed_urls: Vec<String>,
645
646 #[serde(default, skip_serializing_if = "Option::is_none")]
648 pub path: Option<String>,
649
650 #[serde(default, skip_serializing_if = "Option::is_none")]
652 pub mtu: Option<u16>,
653
654 #[serde(default, skip_serializing_if = "Option::is_none")]
656 pub max_frame_bytes: Option<usize>,
657
658 #[serde(default, skip_serializing_if = "Option::is_none")]
660 pub max_send_queue: Option<usize>,
661
662 #[serde(default, skip_serializing_if = "Option::is_none")]
664 pub max_connections: Option<usize>,
665
666 #[serde(default, skip_serializing_if = "Option::is_none")]
668 pub max_inbound_connections: Option<usize>,
669
670 #[serde(default, skip_serializing_if = "Option::is_none")]
672 pub connect_timeout_ms: Option<u64>,
673
674 #[serde(default, skip_serializing_if = "Option::is_none")]
676 pub key_hint_timeout_ms: Option<u64>,
677
678 #[serde(default, skip_serializing_if = "Option::is_none")]
680 pub reconnect_initial_ms: Option<u64>,
681
682 #[serde(default, skip_serializing_if = "Option::is_none")]
684 pub reconnect_max_ms: Option<u64>,
685
686 #[serde(default, skip_serializing_if = "Option::is_none")]
688 pub ping_interval_secs: Option<u64>,
689
690 #[serde(default, skip_serializing_if = "Option::is_none")]
692 pub idle_timeout_secs: Option<u64>,
693
694 #[serde(default, skip_serializing_if = "Option::is_none")]
698 pub accept_connections: Option<bool>,
699}
700
701impl WebSocketConfig {
702 pub fn path(&self) -> &str {
703 self.path.as_deref().unwrap_or(DEFAULT_WEBSOCKET_PATH)
704 }
705
706 pub fn mtu(&self) -> u16 {
707 self.mtu.unwrap_or(DEFAULT_WEBSOCKET_MTU)
708 }
709
710 pub fn max_frame_bytes(&self) -> usize {
711 self.max_frame_bytes
712 .unwrap_or(DEFAULT_WEBSOCKET_MAX_FRAME_BYTES)
713 }
714
715 pub fn max_send_queue(&self) -> usize {
716 self.max_send_queue
717 .unwrap_or(DEFAULT_WEBSOCKET_MAX_SEND_QUEUE)
718 .max(1)
719 }
720
721 pub fn max_connections(&self) -> usize {
722 self.max_connections
723 .unwrap_or(DEFAULT_WEBSOCKET_MAX_CONNECTIONS)
724 .max(1)
725 }
726
727 pub fn max_inbound_connections(&self) -> usize {
728 self.max_inbound_connections
729 .unwrap_or(DEFAULT_WEBSOCKET_MAX_INBOUND)
730 .max(1)
731 .min(self.max_connections())
732 }
733
734 pub fn connect_timeout_ms(&self) -> u64 {
735 self.connect_timeout_ms
736 .unwrap_or(DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS)
737 .max(1)
738 }
739
740 pub fn key_hint_timeout_ms(&self) -> u64 {
741 self.key_hint_timeout_ms
742 .unwrap_or(DEFAULT_WEBSOCKET_KEY_HINT_TIMEOUT_MS)
743 .max(1)
744 }
745
746 pub fn reconnect_initial_ms(&self) -> u64 {
747 self.reconnect_initial_ms
748 .unwrap_or(DEFAULT_WEBSOCKET_RECONNECT_INITIAL_MS)
749 .max(1)
750 }
751
752 pub fn reconnect_max_ms(&self) -> u64 {
753 self.reconnect_max_ms
754 .unwrap_or(DEFAULT_WEBSOCKET_RECONNECT_MAX_MS)
755 .max(self.reconnect_initial_ms())
756 }
757
758 pub fn ping_interval_secs(&self) -> u64 {
759 self.ping_interval_secs
760 .unwrap_or(DEFAULT_WEBSOCKET_PING_INTERVAL_SECS)
761 }
762
763 pub fn idle_timeout_secs(&self) -> u64 {
764 self.idle_timeout_secs
765 .unwrap_or(DEFAULT_WEBSOCKET_IDLE_TIMEOUT_SECS)
766 }
767
768 pub fn accept_connections(&self) -> bool {
769 self.accept_connections
770 .unwrap_or_else(|| self.bind_addr.is_some() || !self.seed_urls.is_empty())
771 }
772
773 pub fn validate(&self) -> Result<(), String> {
774 if let Some(bind_addr) = self.bind_addr.as_deref() {
775 bind_addr
776 .parse::<SocketAddr>()
777 .map_err(|error| format!("invalid bind_addr {bind_addr:?}: {error}"))?;
778 }
779 if !self.path().starts_with('/') || self.path().contains('?') || self.path().contains('#') {
780 return Err("path must be an absolute HTTP path without query or fragment".into());
781 }
782 if let Some(public_url) = self.public_url.as_deref() {
783 validate_websocket_url(public_url, false)?;
784 let uri = public_url
785 .parse::<tokio_tungstenite::tungstenite::http::Uri>()
786 .map_err(|error| format!("invalid public_url: {error}"))?;
787 if uri.path() != self.path() {
788 return Err(format!(
789 "public_url path {:?} does not match configured path {:?}",
790 uri.path(),
791 self.path()
792 ));
793 }
794 if self.bind_addr.is_none() {
795 return Err("public_url requires bind_addr".into());
796 }
797 }
798 let mut unique = std::collections::HashSet::new();
799 for seed_url in &self.seed_urls {
800 validate_websocket_url(seed_url, true)?;
801 if !unique.insert(seed_url) {
802 return Err(format!("duplicate seed URL {seed_url:?}"));
803 }
804 }
805 let minimum_frame = usize::from(self.mtu()).saturating_add(64);
806 if self.max_frame_bytes() < minimum_frame || self.max_frame_bytes() > 1024 * 1024 {
807 return Err(format!(
808 "max_frame_bytes must be between {minimum_frame} and 1048576"
809 ));
810 }
811 if self.max_send_queue() > 4096 {
812 return Err("max_send_queue must not exceed 4096".into());
813 }
814 if self.max_connections() > 4096 {
815 return Err("max_connections must not exceed 4096".into());
816 }
817 if self.max_inbound_connections() > self.max_connections() {
818 return Err("max_inbound_connections must not exceed max_connections".into());
819 }
820 if self.ping_interval_secs() > 0
821 && self.idle_timeout_secs() > 0
822 && self.idle_timeout_secs() <= self.ping_interval_secs()
823 {
824 return Err("idle_timeout_secs must exceed ping_interval_secs".into());
825 }
826 Ok(())
827 }
828}
829
830fn validate_websocket_url(raw: &str, allow_loopback_plaintext: bool) -> Result<(), String> {
831 let uri = raw
832 .parse::<tokio_tungstenite::tungstenite::http::Uri>()
833 .map_err(|error| format!("invalid WebSocket URL {raw:?}: {error}"))?;
834 let scheme = uri
835 .scheme_str()
836 .ok_or_else(|| format!("WebSocket URL {raw:?} is missing a scheme"))?;
837 let host = uri
838 .host()
839 .ok_or_else(|| format!("WebSocket URL {raw:?} is missing a host"))?;
840 if uri.authority().is_none() || uri.path().is_empty() {
841 return Err(format!("invalid WebSocket URL {raw:?}"));
842 }
843 match scheme {
844 "wss" => Ok(()),
845 "ws" if allow_loopback_plaintext && websocket_host_is_loopback(host) => Ok(()),
846 "ws" => Err(format!(
847 "plaintext WebSocket URL {raw:?} is allowed only for loopback seeds"
848 )),
849 _ => Err(format!("WebSocket URL {raw:?} must use wss://")),
850 }
851}
852
853fn websocket_host_is_loopback(host: &str) -> bool {
854 host.eq_ignore_ascii_case("localhost")
855 || host
856 .trim_matches(['[', ']'])
857 .parse::<IpAddr>()
858 .is_ok_and(|ip| ip.is_loopback())
859}
860
861mod aggregate;
862mod ble;
863#[cfg(test)]
864mod tests;
865mod tor;
866mod webrtc;
867
868pub use aggregate::TransportsConfig;
869pub use ble::BleConfig;
870pub use tor::{DirectoryServiceConfig, TorConfig};
871pub use webrtc::WebRtcConfig;