use crate::PeerId;
use anyhow::Result;
use std::collections::HashMap;
use std::fs;
use std::io::Write;
use std::path::PathBuf;
pub struct KnownHosts {
path: PathBuf,
hosts: HashMap<String, Vec<PeerId>>, }
impl KnownHosts {
pub fn load() -> Result<Self> {
let path = dirs::home_dir()
.ok_or_else(|| anyhow::anyhow!("no home dir"))?
.join(".quicnet/known_hosts");
let hosts = if path.exists() {
Self::parse_file(&path)?
} else {
HashMap::new()
};
Ok(Self { path, hosts })
}
pub fn check(&self, host: &str, port: u16, peer_id: &PeerId) -> Trust {
let key = format!("{}:{}", host, port);
match self.hosts.get(&key) {
Some(known_ids) if known_ids.contains(peer_id) => Trust::Known,
Some(known_ids) if !known_ids.is_empty() => Trust::Different(known_ids[0]),
_ => Trust::Unknown,
}
}
pub fn add(&mut self, host: &str, port: u16, peer_id: PeerId) -> Result<()> {
let key = format!("{}:{}", host, port);
self.hosts
.entry(key.clone())
.or_default()
.push(peer_id);
let line = format!("{} {}\n", key, peer_id);
fs::create_dir_all(self.path.parent().unwrap())?;
fs::OpenOptions::new()
.create(true)
.append(true)
.open(&self.path)?
.write_all(line.as_bytes())?;
Ok(())
}
fn parse_file(path: &PathBuf) -> Result<HashMap<String, Vec<PeerId>>> {
let mut hosts: HashMap<String, Vec<PeerId>> = HashMap::new();
for line in fs::read_to_string(path)?.lines() {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() == 2
&& let Ok(peer_id) = parts[1].parse::<PeerId>() {
hosts
.entry(parts[0].to_string())
.or_default()
.push(peer_id);
}
}
Ok(hosts)
}
}
pub enum Trust {
Known,
Unknown,
Different(PeerId), }