Skip to main content

syncweb_core/net/
relay.rs

1//! Syncthing relay (BEP) client and the iroh custom transport that carries
2//! QUIC traffic through it.
3//!
4//! The wire protocol is the "bep-relay" protocol spoken by `strelaysrv` and
5//! Syncthing (upstream `lib/relay/protocol`). Its identities are X.509 client
6//! certificates: a device ID is the SHA-256 of the certificate in DER form.
7//! A device that wants to reach another asks the relay for a
8//! [`Message::SessionInvitation`], then both peers tunnel raw bytes through a
9//! plain TCP session the relay brokers between them.
10//!
11//! syncweb speaks this protocol as an [`iroh`] custom transport: the iroh
12//! endpoint registers a local [`CustomAddr`] derived from our relay identity
13//! (`BEP_TRANSPORT_ID` + 32-byte certificate hash), and QUIC datagrams are
14//! framed over relay sessions. Because iroh treats custom transports as a
15//! primary path (ahead of its own relay fallback), the Syncthing relay works
16//! as a drop-in TCP carrier between syncweb peers, even where UDP and the n0
17//! relay network are unreachable.
18
19use std::time::Duration;
20
21use serde::{Deserialize, Serialize};
22
23pub(crate) mod certs;
24pub mod manager;
25pub mod pool;
26pub mod protocol;
27mod transport;
28mod xdr;
29
30pub use manager::{RelayManager, RelayManagerOptions, TestRelayReport};
31pub use transport::{BEP_TRANSPORT_ID, SyncthingRelayTransport};
32
33/// Which relays to reach out to and how to time out their handshakes.
34#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
35#[non_exhaustive]
36pub struct RelayConfig {
37    #[serde(default)]
38    pub relay_urls: Vec<String>,
39    #[serde(with = "duration_seconds")]
40    pub timeout: Duration,
41    #[serde(default = "default_auto_fallback")]
42    pub auto_fallback: bool,
43}
44
45impl Default for RelayConfig {
46    fn default() -> Self {
47        Self {
48            relay_urls: Vec::new(),
49            timeout: Duration::from_secs(10),
50            auto_fallback: true,
51        }
52    }
53}
54
55const fn default_auto_fallback() -> bool {
56    true
57}
58
59mod duration_seconds {
60    use std::time::Duration;
61
62    use serde::{Deserialize, Deserializer, Serializer};
63
64    pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
65    where
66        S: Serializer,
67    {
68        serializer.serialize_u64(duration.as_secs())
69    }
70
71    pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
72    where
73        D: Deserializer<'de>,
74    {
75        let seconds = u64::deserialize(deserializer)?;
76        Ok(Duration::from_secs(seconds))
77    }
78}