#![deny(missing_docs)]
#![deny(unsafe_code)]
#![doc = tx5_core::__doc_header!()]
use once_cell::sync::Lazy;
use trust_dns_resolver::config::*;
use trust_dns_resolver::error::*;
use trust_dns_resolver::*;
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum OnlineEvent {
Online,
Offline,
}
impl std::fmt::Display for OnlineEvent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
#[derive(Clone)]
pub struct OnlineReceiver(tokio::sync::watch::Receiver<OnlineEvent>);
impl Default for OnlineReceiver {
fn default() -> Self {
RCV.clone()
}
}
impl OnlineReceiver {
pub fn new() -> Self {
Self::default()
}
pub fn status(&mut self) -> OnlineEvent {
while self.0.has_changed().unwrap() {
self.0.borrow_and_update();
}
*self.0.borrow_and_update()
}
pub async fn recv(&mut self) -> OnlineEvent {
let _ = self.0.changed().await;
*self.0.borrow_and_update()
}
}
static RCV: Lazy<OnlineReceiver> = Lazy::new(|| {
let (snd, rcv) = tokio::sync::watch::channel(OnlineEvent::Offline);
tokio::task::spawn(async move {
loop {
let status = check_status().await;
snd.send_if_modified(move |s| {
if *s == status {
false
} else {
*s = status;
true
}
});
let s = rand::Rng::random_range(&mut rand::rng(), 4.0..8.0);
let s = std::time::Duration::from_secs_f64(s);
tokio::time::sleep(s).await;
}
});
OnlineReceiver(rcv)
});
async fn check_status() -> OnlineEvent {
let mut servers = Vec::new();
servers.append(&mut NameServerConfigGroup::cloudflare().into_inner());
servers.append(&mut NameServerConfigGroup::google().into_inner());
servers.append(&mut NameServerConfigGroup::quad9().into_inner());
rand::seq::SliceRandom::shuffle(&mut servers[..], &mut rand::rng());
let conf = ResolverConfig::from_parts(None, Vec::new(), servers);
let mut opts = ResolverOpts::default();
opts.server_ordering_strategy = ServerOrderingStrategy::UserProvidedOrder;
opts.cache_size = 0;
opts.use_hosts_file = false;
let r = TokioAsyncResolver::tokio(conf, opts);
const TLD: [&str; 5] = [".com.", ".net.", ".org.", ".edu.", ".gov."];
let tld =
rand::seq::IndexedRandom::choose(&TLD[..], &mut rand::rng()).unwrap();
let mut nonce = [0_u8; 8];
rand::Rng::fill(&mut rand::rng(), &mut nonce);
let mut name = String::new();
for c in nonce {
name.push_str(&format!("{c:02x}"));
}
name.push_str(tld);
r.clear_cache();
match r.lookup_ip(name).await {
Ok(_) => OnlineEvent::Online,
Err(err) => match err.kind() {
ResolveErrorKind::NoRecordsFound { .. } => OnlineEvent::Online,
_ => OnlineEvent::Offline,
},
}
}