1use std::str::FromStr;
24
25use iroh::endpoint::presets;
26use iroh::{Endpoint, EndpointAddr, RelayMode, RelayUrl, SecretKey};
27use tokio::time::timeout;
28
29use crate::auth::{
30 AuthConfig, AuthPolicy, AuthRole, DEFAULT_PRE_AUTH_TIMEOUT, NodeAuth, run_auth_phase,
31};
32use crate::session::{DEFAULT_IDLE_TIMEOUT, SessionError, SessionReport, SyncStore, run_session};
33use crate::wire::SYNC_ALPN;
34
35#[derive(Debug)]
37pub enum EndpointError {
38 RelayUrl(String),
40 Bind(String),
42 Connect(String),
44}
45
46impl std::fmt::Display for EndpointError {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 match self {
49 EndpointError::RelayUrl(m) => write!(f, "invalid relay url: {m}"),
50 EndpointError::Bind(m) => write!(f, "binding the sync endpoint failed: {m}"),
51 EndpointError::Connect(m) => write!(f, "sync connection setup failed: {m}"),
52 }
53 }
54}
55
56impl std::error::Error for EndpointError {}
57
58pub async fn build_endpoint(
62 secret_key: [u8; 32],
63 relay_url: &str,
64) -> Result<Endpoint, EndpointError> {
65 let relay_url =
66 RelayUrl::from_str(relay_url.trim()).map_err(|e| EndpointError::RelayUrl(e.to_string()))?;
67 Endpoint::builder(presets::Minimal)
68 .alpns(vec![SYNC_ALPN.to_vec()])
69 .relay_mode(RelayMode::custom([relay_url]))
70 .secret_key(SecretKey::from_bytes(&secret_key))
71 .bind()
72 .await
73 .map_err(|e| EndpointError::Bind(e.to_string()))
74}
75
76pub fn endpoint_addr(endpoint: &Endpoint) -> EndpointAddr {
79 endpoint.addr()
80}
81
82pub async fn connect_and_sync<S: SyncStore + NodeAuth>(
87 endpoint: &Endpoint,
88 peer: impl Into<EndpointAddr>,
89 store: &mut S,
90 policy: AuthPolicy,
91 now_ms: i64,
92) -> Result<SessionReport, SyncFailure> {
93 let local_node = *endpoint.id().as_bytes();
94 let conn = endpoint
95 .connect(peer, SYNC_ALPN)
96 .await
97 .map_err(|e| SyncFailure::Endpoint(EndpointError::Connect(e.to_string())))?;
98 let remote_node = *conn.remote_id().as_bytes();
99 let (mut send, mut recv) = conn
100 .open_bi()
101 .await
102 .map_err(|e| SyncFailure::Endpoint(EndpointError::Connect(e.to_string())))?;
103 run_auth_phase(&mut send, &mut recv, &*store, AuthConfig {
104 role: AuthRole::Dialer,
105 account_id: store.account_id(),
106 local_node,
107 remote_node,
108 policy,
109 now_ms,
110 pre_auth_timeout: DEFAULT_PRE_AUTH_TIMEOUT,
111 })
112 .await
113 .map_err(SyncFailure::Auth)?;
114 let report = run_session(store, send, recv).await.map_err(SyncFailure::Session)?;
115 conn.close(0u32.into(), b"done");
117 Ok(report)
118}
119
120pub async fn accept_and_sync<S: SyncStore + NodeAuth>(
128 endpoint: &Endpoint,
129 store: &mut S,
130 policy: AuthPolicy,
131 now_ms: i64,
132) -> Result<SessionReport, SyncFailure> {
133 let local_node = *endpoint.id().as_bytes();
134 let incoming = endpoint
135 .accept()
136 .await
137 .ok_or_else(|| SyncFailure::Endpoint(EndpointError::Connect("endpoint closed".into())))?;
138 let conn = timeout(DEFAULT_IDLE_TIMEOUT, incoming)
139 .await
140 .map_err(|_| SyncFailure::Endpoint(EndpointError::Connect("handshake timed out".into())))?
141 .map_err(|e| SyncFailure::Endpoint(EndpointError::Connect(e.to_string())))?;
142 let remote_node = *conn.remote_id().as_bytes();
143 let (mut send, mut recv) = timeout(DEFAULT_IDLE_TIMEOUT, conn.accept_bi())
144 .await
145 .map_err(|_| SyncFailure::Endpoint(EndpointError::Connect("peer opened no stream".into())))?
146 .map_err(|e| SyncFailure::Endpoint(EndpointError::Connect(e.to_string())))?;
147 run_auth_phase(&mut send, &mut recv, &*store, AuthConfig {
150 role: AuthRole::Acceptor,
151 account_id: store.account_id(),
152 local_node,
153 remote_node,
154 policy,
155 now_ms,
156 pre_auth_timeout: DEFAULT_PRE_AUTH_TIMEOUT,
157 })
158 .await
159 .map_err(SyncFailure::Auth)?;
160 let report = run_session(store, send, recv).await.map_err(SyncFailure::Session)?;
161 conn.close(0u32.into(), b"done");
162 Ok(report)
163}
164
165#[derive(Debug)]
168pub enum SyncFailure {
169 Endpoint(EndpointError),
170 Auth(crate::auth::AuthError),
172 Session(SessionError),
173}
174
175impl std::fmt::Display for SyncFailure {
176 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177 match self {
178 SyncFailure::Endpoint(e) => write!(f, "{e}"),
179 SyncFailure::Auth(e) => write!(f, "{e}"),
180 SyncFailure::Session(e) => write!(f, "{e}"),
181 }
182 }
183}
184
185impl std::error::Error for SyncFailure {}