use std::collections::HashMap; use std::path::Path; use anyhow::anyhow; use futures::TryStreamExt; use indexmap::IndexMap; use itertools::Either; use serde::{Deserialize, Deserializer, Serialize}; use sqlx::{Executor, Sqlite}; use torut::onion::TorSecretKeyV3; use crate::db::model::{InterfaceAddressMap, InterfaceAddresses}; use crate::id::Id; use crate::s9pk::manifest::PackageId; use crate::util::Port; use crate::Error; #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] pub struct Interfaces(pub IndexMap); // TODO impl Interfaces { pub async fn install( &self, secrets: &mut Ex, package_id: &PackageId, ) -> Result where for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>, { let mut interface_addresses = InterfaceAddressMap(IndexMap::new()); for (id, iface) in &self.0 { let mut addrs = InterfaceAddresses { tor_address: None, lan_address: None, }; if iface.tor_config.is_some() || iface.lan_config.is_some() { let key = TorSecretKeyV3::generate(); let key_vec = key.as_bytes().to_vec(); sqlx::query!( "INSERT INTO tor (package, interface, key) VALUES (?, ?, ?)", **package_id, **id, key_vec, ) .execute(&mut *secrets) .await?; let onion = key.public().get_onion_address(); if iface.tor_config.is_some() { addrs.tor_address = Some(onion.to_string()); } if iface.lan_config.is_some() { addrs.lan_address = Some(format!("{}.local", onion.get_address_without_dot_onion())); } } interface_addresses.0.insert(id.clone(), addrs); } Ok(interface_addresses) } pub async fn tor_keys( &self, secrets: &mut Ex, package_id: &PackageId, ) -> Result, Error> where for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>, { Ok(sqlx::query!( "SELECT interface, key FROM tor WHERE package = ?", **package_id ) .fetch_many(secrets) .map_err(Error::from) .try_filter_map(|qr| async move { Ok(if let Either::Right(r) = qr { let mut buf = [0; 64]; buf.clone_from_slice(r.key.get(0..64).ok_or_else(|| { Error::new( anyhow!("Invalid Tor Key Length"), crate::ErrorKind::Database, ) })?); Some((InterfaceId::from(Id::try_from(r.interface)?), buf.into())) } else { None }) }) .try_collect() .await?) } } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)] pub struct InterfaceId = String>(Id); impl> From> for InterfaceId { fn from(id: Id) -> Self { Self(id) } } impl> std::fmt::Display for InterfaceId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", &self.0) } } impl> std::ops::Deref for InterfaceId { type Target = S; fn deref(&self) -> &Self::Target { &*self.0 } } impl> AsRef for InterfaceId { fn as_ref(&self) -> &str { self.0.as_ref() } } impl<'de, S> Deserialize<'de> for InterfaceId where S: AsRef, Id: Deserialize<'de>, { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { Ok(InterfaceId(Deserialize::deserialize(deserializer)?)) } } impl> AsRef for InterfaceId { fn as_ref(&self) -> &Path { self.0.as_ref().as_ref() } } #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] pub struct Interface { pub name: String, pub description: String, pub tor_config: Option, pub lan_config: Option>, pub ui: bool, pub protocols: Vec, } #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] pub struct TorConfig { pub port_mapping: IndexMap, } #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] pub struct LanPortConfig { pub ssl: bool, pub mapping: u16, }