use crate::su3::{ReseedRouterInfo, Su3};
use anyhow::anyhow;
use rand::{thread_rng, Rng};
use reqwest::{
header::{HeaderMap, HeaderValue, CONNECTION, USER_AGENT},
Client,
};
use std::collections::HashSet;
const LOG_TARGET: &str = "emissary::reseeder";
const NUM_RETRIES: usize = 5usize;
const RESEED_SERVERS: &[&str] = &[
"https://reseed.stormycloud.org/",
"https://i2p.ghativega.in/",
"https://reseed-pl.i2pd.xyz/",
"https://reseed-fr.i2pd.xyz/",
"https://www2.mk16.de/",
"https://reseed2.i2p.net/",
"https://banana.incognet.io/",
"https://reseed.diva.exchange/",
"https://reseed.i2pgit.org/",
"https://i2p.novg.net/",
"https://i2pseed.creativecowpat.net:8443/",
"https://reseed.onion.im/",
"https://reseed.memcpy.io/",
];
pub struct Reseeder;
impl Reseeder {
async fn reseed_inner(hosts: &[&str]) -> anyhow::Result<Vec<ReseedRouterInfo>> {
let client = Client::new();
let headers = HeaderMap::from_iter([
(USER_AGENT, HeaderValue::from_static("Wget/1.11.4")),
(CONNECTION, HeaderValue::from_static("close")),
]);
let mut already_tried = HashSet::<usize>::new();
for _ in 0..NUM_RETRIES {
let server = loop {
let server = thread_rng().gen_range(0..hosts.len());
if already_tried.insert(server) {
break server;
}
};
let response = match client
.get(format!("{}/i2pseeds.su3", hosts[server]))
.headers(headers.clone())
.send()
.await
{
Err(error) => {
tracing::debug!(
target: LOG_TARGET,
server = ?hosts[server],
?error,
"failed to reseed"
);
continue;
}
Ok(response) => response,
};
if !response.status().is_success() {
tracing::debug!(
target: LOG_TARGET,
status = ?response.status(),
"request to reseed server failed",
);
continue;
}
match response.bytes().await {
Ok(bytes) => match Su3::parse_reseed(&bytes, false) {
None => continue,
Some(routers) => return Ok(routers),
},
Err(error) => {
tracing::debug!(
target: LOG_TARGET,
server = ?hosts[server],
?error,
"failed to get response from reseeed server"
);
}
}
}
Err(anyhow!("failed to reseed server"))
}
pub async fn reseed(hosts: Option<Vec<String>>) -> anyhow::Result<Vec<ReseedRouterInfo>> {
match hosts {
None => Self::reseed_inner(RESEED_SERVERS).await,
Some(hosts) =>
Self::reseed_inner(&hosts.iter().map(AsRef::as_ref).collect::<Vec<_>>()).await,
}
}
}