use crate::error::{Result, TransportError};
use iroh::{Endpoint, EndpointAddr, RelayConfig, RelayMode, SecretKey};
pub(crate) const BASE_ALPN: &[u8] = b"irosh/1";
pub fn derive_alpn(secret: Option<&str>) -> Vec<u8> {
match secret {
None => BASE_ALPN.to_vec(),
Some(s) => {
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(s.as_bytes());
let hash = hex::encode(&hasher.finalize()[..8]); format!("{}/{}", String::from_utf8_lossy(BASE_ALPN), hash).into_bytes()
}
}
}
#[derive(Debug, Clone)]
pub struct ServerEndpoint {
pub endpoint: Endpoint,
pub addr: EndpointAddr,
pub endpoint_id: String,
pub relay_urls: Vec<String>,
pub direct_addresses: Vec<String>,
}
pub async fn bind_server_endpoint(
secret_key: SecretKey,
alpns: Vec<Vec<u8>>,
relay_mode: RelayMode,
) -> Result<ServerEndpoint> {
let endpoint = Endpoint::builder()
.secret_key(secret_key)
.alpns(alpns)
.relay_mode(relay_mode)
.bind()
.await
.map_err(|source| TransportError::EndpointBind { source })?;
let _ = tokio::time::timeout(std::time::Duration::from_secs(10), endpoint.online()).await;
let endpoint_addr = endpoint.addr();
let node_id = endpoint.id();
let direct_addresses = endpoint_addr
.ip_addrs()
.map(|addr| addr.to_string())
.collect::<Vec<_>>();
let relay_urls = endpoint_addr
.relay_urls()
.map(|url| url.to_string())
.collect::<Vec<_>>();
Ok(ServerEndpoint {
endpoint_id: node_id.to_string(),
endpoint,
addr: endpoint_addr,
relay_urls,
direct_addresses,
})
}
pub async fn bind_client_endpoint(
secret_key: SecretKey,
alpns: Vec<Vec<u8>>,
relay_mode: RelayMode,
) -> Result<Endpoint> {
let endpoint = Endpoint::builder()
.secret_key(secret_key)
.alpns(alpns)
.relay_mode(relay_mode)
.bind()
.await
.map_err(|source| TransportError::EndpointBind { source })?;
let _ = tokio::time::timeout(std::time::Duration::from_secs(10), endpoint.online()).await;
Ok(endpoint)
}
pub fn parse_relay_mode(url: &str) -> Result<RelayMode> {
match url {
"disabled" => Ok(RelayMode::Disabled),
"default" => Ok(RelayMode::Default),
u if u.starts_with("http") => {
let url: iroh::RelayUrl = u
.parse()
.map_err(|_| TransportError::InvalidRelayUrl { url: u.to_string() })?;
Ok(RelayMode::Custom(RelayConfig::from(url).into()))
}
_ => Ok(RelayMode::Default),
}
}