use crate::tools::{config, tools};
use crate::transport::bootstrap_provider::bootstrap_provider::BootstrapProvider;
use gloo_net::http::Request;
use log::warn;
use send_wrapper::SendWrapper;
use std::collections::HashSet;
const DOH_PROVIDERS: &[&str] = &[
"https://cloudflare-dns.com/dns-query",
"https://dns.google/resolve",
];
#[derive(serde::Deserialize)]
struct DoHResponse {
#[serde(rename = "Answer")]
answer: Option<Vec<DoHRecord>>,
}
#[derive(serde::Deserialize)]
struct DoHRecord {
#[serde(rename = "type")]
record_type: u16,
data: String,
}
pub struct WasmBootstrapProvider {}
impl WasmBootstrapProvider {
pub fn new() -> Self {
Self {}
}
async fn query_doh(doh_provider: &str, domain: &str) -> Vec<String> {
let url = format!("{}?name={}&type=A", doh_provider, domain);
let fetch_future = async move {
let response = Request::get(&url)
.header("Accept", "application/dns-json")
.send()
.await
.map_err(|e| anyhow::anyhow!("DoH fetch error: {}", e))?;
let text = response
.text()
.await
.map_err(|e| anyhow::anyhow!("DoH read error: {}", e))?;
let doh_response: DoHResponse = serde_json::from_str(&text)
.map_err(|e| anyhow::anyhow!("DoH parse error: {}", e))?;
Ok::<DoHResponse, anyhow::Error>(doh_response)
};
match SendWrapper::new(fetch_future).await {
Ok(doh_response) => doh_response
.answer
.unwrap_or_default()
.into_iter()
.filter(|record| record.record_type == 1) .map(|record| format!("{}:443", record.data))
.collect(),
Err(e) => {
warn!("DoH query failed for {} via {}: {}", domain, doh_provider, e);
vec![]
}
}
}
}
#[async_trait::async_trait]
impl BootstrapProvider for WasmBootstrapProvider {
async fn get_bootstrap_addresses(&self) -> Vec<String> {
let bootstrap_config = SendWrapper::new(super::wasm_local_settings::local_settings_get("bootstrap"))
.await
.unwrap_or(None)
.unwrap_or_else(|| "production".to_string());
match bootstrap_config.as_str() {
"production" => {
let mut addresses = HashSet::new();
for domain in config::BOOTSTRAP_DOMAINS {
for doh_provider in DOH_PROVIDERS {
for address in Self::query_doh(doh_provider, domain).await {
addresses.insert(address);
}
}
}
let mut addresses: Vec<String> = addresses.into_iter().collect();
tools::shuffle(&mut addresses);
addresses
}
"local" => vec!["127.0.0.1:443".to_string()],
manual => {
let mut addresses: Vec<String> = manual.split(',').map(|s| s.trim().to_string()).collect();
tools::shuffle(&mut addresses);
addresses
}
}
}
}