use std::net::{Ipv4Addr, SocketAddrV4};
use std::path::Path;
use std::str::FromStr;
use decdn_common::config::{ResolvedDiscovery, load_file_config, resolve_discovery};
use decdn_common::identity::fresh_secret_key;
use decdn_common::redact::redact_userinfo;
use iroh::address_lookup::{DnsAddressLookup, MemoryLookup};
use iroh::endpoint::{QuicTransportConfig, VarInt, presets};
use iroh::{Endpoint, EndpointAddr, PublicKey, RelayMap, RelayMode, RelayUrl};
const CLIENT_STREAM_RECEIVE_WINDOW: u32 = 64 * 1024 * 1024;
const CLIENT_RECEIVE_WINDOW: u32 = 128 * 1024 * 1024;
const _: () = assert!(CLIENT_STREAM_RECEIVE_WINDOW <= CLIENT_RECEIVE_WINDOW);
fn client_transport_config() -> QuicTransportConfig {
QuicTransportConfig::builder()
.stream_receive_window(VarInt::from_u32(CLIENT_STREAM_RECEIVE_WINDOW))
.receive_window(VarInt::from_u32(CLIENT_RECEIVE_WINDOW))
.build()
}
pub fn resolve_relays(
flag: Option<&str>,
config_path: Option<&Path>,
) -> anyhow::Result<Vec<RelayUrl>> {
let raw: Vec<String> = if let Some(s) = flag {
vec![s.to_owned()]
} else if config_path.is_none_or(Path::exists) {
let net = load_file_config(config_path)?.network.unwrap_or_default();
net.relay_urls.unwrap_or_default()
} else {
Vec::new()
};
raw.iter()
.map(|s| {
RelayUrl::from_str(s)
.map_err(|e| anyhow::anyhow!("invalid relay url {:?}: {e}", redact_userinfo(s)))
})
.collect()
}
#[must_use]
#[doc(hidden)]
pub fn relay_mode(relays: &[RelayUrl]) -> RelayMode {
if relays.is_empty() {
RelayMode::Disabled
} else {
RelayMode::Custom(relays.iter().cloned().collect::<RelayMap>())
}
}
pub fn client_discovery(config_path: Option<&Path>) -> anyhow::Result<ResolvedDiscovery> {
if config_path.is_some_and(|p| !p.exists()) {
return Ok(ResolvedDiscovery::default());
}
resolve_discovery(&load_file_config(config_path)?)
}
pub async fn client_endpoint(
relays: &[RelayUrl],
discovery: &ResolvedDiscovery,
) -> anyhow::Result<Endpoint> {
let bind_addr = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0);
let mut builder = if discovery.is_empty() {
Endpoint::builder(presets::N0)
} else {
add_resolution_lookups(Endpoint::builder(presets::Minimal), discovery)?
};
builder = builder
.secret_key(fresh_secret_key())
.transport_config(client_transport_config());
builder = if !relays.is_empty() {
builder.relay_mode(relay_mode(relays))
} else if discovery.is_empty() {
builder } else {
builder.relay_mode(RelayMode::Default)
};
builder
.bind_addr(bind_addr)
.map_err(|e| anyhow::anyhow!("invalid bind addr {bind_addr}: {e}"))?
.bind()
.await
.map_err(|e| anyhow::anyhow!("endpoint bind failed: {e}"))
}
fn add_resolution_lookups(
mut builder: iroh::endpoint::Builder,
discovery: &ResolvedDiscovery,
) -> anyhow::Result<iroh::endpoint::Builder> {
if let Some(origin) = &discovery.dns_origin {
builder = builder.address_lookup(DnsAddressLookup::builder(origin.clone()));
}
if !discovery.peers.is_empty() {
let mut infos = Vec::with_capacity(discovery.peers.len());
for peer in &discovery.peers {
let id = peer.node_id.parse::<PublicKey>().map_err(|e| {
anyhow::anyhow!("invalid network.discovery peer id {}: {e}", peer.node_id)
})?;
let mut addr = EndpointAddr::new(id);
if let Some(relay) = &peer.relay_url {
let relay_url = relay.parse::<RelayUrl>().map_err(|e| {
anyhow::anyhow!(
"invalid relay_url {:?} for network.discovery peer {}: {e}",
redact_userinfo(relay),
peer.node_id
)
})?;
addr = addr.with_relay_url(relay_url);
}
for a in &peer.addrs {
let sock = a.parse::<std::net::SocketAddr>().map_err(|e| {
anyhow::anyhow!(
"invalid addr {a:?} for network.discovery peer {}: {e}",
peer.node_id
)
})?;
addr = addr.with_ip_addr(sock);
}
infos.push(addr);
}
builder = builder.address_lookup(MemoryLookup::from_endpoint_info(infos));
}
Ok(builder)
}
#[cfg(test)]
#[allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::indexing_slicing,
clippy::panic
)]
mod tests;