it builds

This commit is contained in:
Aiden McClelland
2025-08-18 18:12:03 -06:00
parent aaf2361909
commit 75a20ae5c5
9 changed files with 196 additions and 67 deletions

2
core/Cargo.lock generated
View File

@@ -7419,10 +7419,12 @@ dependencies = [
"tokio-tungstenite",
"tokio-util",
"toml",
"tor-cell",
"tor-hscrypto",
"tor-hsservice",
"tor-keymgr",
"tor-llcrypto",
"tor-proto",
"tor-rtcompat",
"tower-service",
"tracing",

View File

@@ -225,10 +225,12 @@ tokio-stream = { version = "0.1.14", features = ["io-util", "sync", "net"] }
tokio-tar = { git = "https://github.com/dr-bonez/tokio-tar.git" }
tokio-tungstenite = { version = "0.26.2", features = ["native-tls", "url"] }
tokio-util = { version = "0.7.9", features = ["io"] }
tor-cell = { version = "0.33" }
tor-hscrypto = { version = "0.33", features = ["full"] }
tor-hsservice = { version = "0.33" }
tor-keymgr = { version = "0.33", features = ["ephemeral-keystore"] }
tor-llcrypto = { version = "0.33", features = ["full"] }
tor-proto = { version = "0.33" }
tor-rtcompat = { version = "0.33", features = ["tokio", "rustls"] }
tower-service = "0.3.3"
tracing = "0.1.39"

View File

@@ -622,8 +622,8 @@ async fn watch_ip(
.chain(ip6_proxy.address_data().await?)
.collect_vec();
let lan_ip = [
ip4_proxy.gateway().await?.parse::<IpAddr>()?,
ip6_proxy.gateway().await?.parse::<IpAddr>()?,
dbg!(ip4_proxy.gateway().await?).parse::<IpAddr>()?,
dbg!(ip6_proxy.gateway().await?).parse::<IpAddr>()?,
]
.into_iter()
.collect();
@@ -852,7 +852,13 @@ impl NetworkInterfaceController {
) -> Result<(), Error> {
tracing::debug!("syncronizing {info:?} to db");
let dns = todo!();
// let dns = todo!();
let dns = info
.values()
.filter_map(|i| i.ip_info.as_ref())
.flat_map(|i| &i.dns_servers)
.copied()
.collect();
db.mutate(|db| {
let net = db.as_public_mut().as_server_info_mut().as_network_mut();

View File

@@ -12,8 +12,8 @@ use ts_rs::TS;
use crate::context::{CliContext, RpcContext};
use crate::db::model::public::NetworkInterfaceInfo;
use crate::net::forward::AvailablePorts;
use crate::net::host::HostApiKind;
use crate::net::gateway::InterfaceFilter;
use crate::net::host::HostApiKind;
use crate::net::vhost::AlpnInfo;
use crate::prelude::*;
use crate::util::serde::{display_serializable, HandlerExtSerde};
@@ -58,8 +58,10 @@ pub struct BindInfo {
#[ts(export)]
pub struct NetInfo {
#[ts(as = "BTreeSet::<GatewayId>")]
#[serde(default)]
pub private_disabled: OrdSet<GatewayId>,
#[ts(as = "BTreeSet::<GatewayId>")]
#[serde(default)]
pub public_enabled: OrdSet<GatewayId>,
pub assigned_port: Option<u16>,
pub assigned_ssl_port: Option<u16>,

View File

@@ -9,7 +9,7 @@ use arti_client::{TorClient, TorClientConfig};
use base64::Engine;
use clap::Parser;
use color_eyre::eyre::eyre;
use futures::FutureExt;
use futures::{FutureExt, Stream, StreamExt};
use helpers::NonDetachingJoinHandle;
use imbl_value::InternedString;
use itertools::Itertools;
@@ -18,15 +18,21 @@ use regex::Regex;
use rpc_toolkit::{from_fn_async, Context, Empty, HandlerExt, ParentHandler};
use safelog::DisplayRedacted;
use serde::{Deserialize, Serialize};
use tokio::io::AsyncWriteExt;
use tokio::net::TcpStream;
use tokio::sync::oneshot;
use tor_cell::relaycell::msg::Connected;
use tor_hscrypto::pk::{HsId, HsIdKeypair};
use tor_hsservice::status::State as ArtiOnionServiceState;
use tor_hsservice::{HsNickname, RunningOnionService};
use tor_hsservice::{HsNickname, RendRequest, RunningOnionService};
use tor_keymgr::config::ArtiKeystoreKind;
use tor_proto::stream::IncomingStreamRequest;
use tor_rtcompat::tokio::TokioRustlsRuntime;
use ts_rs::TS;
use crate::context::{CliContext, RpcContext};
use crate::prelude::*;
use crate::util::actor::background::BackgroundJobQueue;
use crate::util::serde::{
deserialize_from_str, display_serializable, serialize_display, Base64, HandlerExtSerde,
WithIoFormat, BASE64,
@@ -210,25 +216,6 @@ impl std::fmt::Debug for OnionStore {
}
}
enum ErrorLogSeverity {
Fatal { wipe_state: bool },
Unknown { wipe_state: bool },
}
lazy_static! {
static ref LOG_REGEXES: Vec<(Regex, ErrorLogSeverity)> = vec![(
Regex::new("This could indicate a route manipulation attack, network overload, bad local network connectivity, or a bug\\.").unwrap(),
ErrorLogSeverity::Unknown { wipe_state: true }
),(
Regex::new("died due to an invalid selected path").unwrap(),
ErrorLogSeverity::Fatal { wipe_state: false }
),(
Regex::new("Tor has not observed any network activity for the past").unwrap(),
ErrorLogSeverity::Unknown { wipe_state: false }
)];
static ref PROGRESS_REGEX: Regex = Regex::new("PROGRESS=([0-9]+)").unwrap();
}
pub fn tor_api<C: Context>() -> ParentHandler<C> {
ParentHandler::new()
.subcommand(
@@ -368,6 +355,11 @@ pub enum OnionServiceState {
Recovering,
Broken,
}
impl From<ArtiOnionServiceState> for OnionServiceState {
fn from(value: ArtiOnionServiceState) -> Self {
todo!()
}
}
pub async fn list_services(
ctx: RpcContext,
@@ -403,7 +395,9 @@ impl TorController {
let addr = key.onion_address();
match s.entry(addr) {
Entry::Occupied(e) => Ok(e.get().clone()),
Entry::Vacant(e) => Ok(e.insert(OnionService::launch(&self.client, key)?).clone()),
Entry::Vacant(e) => Ok(e
.insert(OnionService::launch(self.client.clone(), key)?)
.clone()),
}
})
}
@@ -457,31 +451,123 @@ impl TorController {
#[derive(Clone)]
pub struct OnionService(Arc<OnionServiceData>);
struct OnionServiceData {
service: Arc<RunningOnionService>,
service: Arc<SyncMutex<Option<Arc<RunningOnionService>>>>,
bindings: Arc<SyncRwLock<BTreeMap<u16, BTreeMap<SocketAddr, Weak<()>>>>>,
_thread: NonDetachingJoinHandle<()>,
}
impl OnionService {
fn launch(client: &TorClient<TokioRustlsRuntime>, key: TorSecretKey) -> Result<Self, Error> {
let (service, stream) = client.launch_onion_service_with_hsid(
OnionServiceConfigBuilder::default()
.nickname(
key.onion_address()
.to_string()
.trim_end_matches(".onion")
.parse::<HsNickname>()
.with_kind(ErrorKind::Tor)?,
)
.build()
.with_kind(ErrorKind::Tor)?,
key.0,
)?;
let bindings = Arc::new(SyncRwLock::new(BTreeMap::new()));
fn launch(client: TorClient<TokioRustlsRuntime>, key: TorSecretKey) -> Result<Self, Error> {
let service = Arc::new(SyncMutex::new(None));
let bindings = Arc::new(SyncRwLock::new(BTreeMap::<
u16,
BTreeMap<SocketAddr, Weak<()>>,
>::new()));
Ok(Self(Arc::new(OnionServiceData {
service: service.clone(),
bindings: bindings.clone(),
_thread: tokio::spawn(async move {
todo!();
let (bg, mut runner) = BackgroundJobQueue::new();
runner
.run_while(async {
loop {
if let Err(e) = async {
let (new_service, mut stream) = client
.launch_onion_service_with_hsid(
OnionServiceConfigBuilder::default()
.nickname(
key.onion_address()
.to_string()
.trim_end_matches(".onion")
.parse::<HsNickname>()
.with_kind(ErrorKind::Tor)?,
)
.build()
.with_kind(ErrorKind::Tor)?,
key.clone().0,
)
.with_kind(ErrorKind::Tor)?;
service.replace(Some(new_service));
while let Some(req) = stream.next().await {
bg.add_job({
let bg = bg.clone();
let bindings = bindings.clone();
async move {
if let Err(e) = async {
let mut stream =
req.accept().await.with_kind(ErrorKind::Tor)?;
while let Some(req) = stream.next().await {
let IncomingStreamRequest::Begin(begin) =
req.request()
else {
continue; // TODO: reject instead?
};
let Some(target) = bindings.peek(|b| {
b.get(&begin.port()).and_then(|a| {
a.iter()
.find(|(_, rc)| {
rc.strong_count() > 0
})
.map(|(addr, _)| *addr)
})
}) else {
continue; // TODO: reject instead?
};
bg.add_job(async move {
if let Err(e) = async {
let mut outgoing =
TcpStream::connect(target)
.await
.with_kind(
ErrorKind::Network,
)?;
let mut incoming = req
.accept(Connected::new_empty())
.await
.with_kind(ErrorKind::Tor)?;
if let Err(e) =
tokio::io::copy_bidirectional(
&mut outgoing,
&mut incoming,
)
.await
{
tracing::error!("{e}");
tracing::debug!("{e:?}");
}
incoming.flush().await?;
outgoing.flush().await?;
incoming.shutdown().await?;
outgoing.shutdown().await?;
Ok::<_, Error>(())
}
.await
{
tracing::error!("{e}");
tracing::debug!("{e:?}");
}
});
}
Ok::<_, Error>(())
}
.await
{
tracing::error!("{e}");
tracing::debug!("{e:?}");
}
}
});
}
Ok::<_, Error>(())
}
.await
{
tracing::error!("{e}");
tracing::debug!("{e:?}");
}
}
})
.await
})
.into(),
})))
@@ -491,14 +577,35 @@ impl OnionService {
&self,
bindings: impl IntoIterator<Item = (u16, SocketAddr)>,
) -> Rcs {
todo!()
self.0.bindings.mutate(|b| {
bindings
.into_iter()
.map(|(port, target)| {
let entry = b.entry(port).or_default().entry(target).or_default();
if let Some(rc) = entry.upgrade() {
rc
} else {
let rc = Arc::new(());
*entry = Arc::downgrade(&rc);
rc
}
})
.collect()
})
}
pub fn gc(&self) -> bool {
todo!()
self.0.bindings.mutate(|b| {
b.retain(|_, targets| {
targets.retain(|_, rc| rc.strong_count() > 0);
!targets.is_empty()
});
!b.is_empty()
})
}
pub async fn shutdown(self) -> Result<(), Error> {
todo!()
self.0._thread.abort();
Ok(())
}
}

View File

@@ -34,7 +34,6 @@ pub mod recipe;
├── manifest.json
├── icon.<ext>
├── LICENSE.md
├── instructions.md
├── dependencies
│ └── <id>
│ ├── metadata.json

View File

@@ -147,8 +147,6 @@ pub struct PackParams {
pub icon: Option<PathBuf>,
#[arg(long)]
pub license: Option<PathBuf>,
#[arg(long)]
pub instructions: Option<PathBuf>,
#[arg(long, conflicts_with = "no-assets")]
pub assets: Option<PathBuf>,
#[arg(long, conflicts_with = "assets")]
@@ -240,12 +238,6 @@ impl PackParams {
.await?
}
}
fn instructions(&self) -> PathBuf {
self.instructions
.as_ref()
.cloned()
.unwrap_or_else(|| self.path().join("instructions.md"))
}
fn assets(&self) -> PathBuf {
self.assets
.as_ref()
@@ -797,20 +789,10 @@ pub async fn list_ingredients(_: CliContext, params: PackParams) -> Result<Vec<P
Err(e) => {
warn!("failed to load manifest: {e}");
debug!("{e:?}");
return Ok(vec![
js_path,
params.icon().await?,
params.license().await?,
params.instructions(),
]);
return Ok(vec![js_path, params.icon().await?, params.license().await?]);
}
};
let mut ingredients = vec![
js_path,
params.icon().await?,
params.license().await?,
params.instructions(),
];
let mut ingredients = vec![js_path, params.icon().await?, params.license().await?];
for (_, dependency) in manifest.dependencies.0 {
if let Some(PathOrUrl::Path(p)) = dependency.s9pk {

View File

@@ -63,3 +63,29 @@ impl Future for BackgroundJobRunner {
}
}
}
impl BackgroundJobRunner {
pub fn run_while<Fut: Future + Send>(
&mut self,
fut: Fut,
) -> impl Future<Output = Fut::Output> + Send {
#[pin_project::pin_project]
struct RunWhile<'a, Fut> {
#[pin]
runner: &'a mut BackgroundJobRunner,
#[pin]
fut: Fut,
}
impl<'a, Fut: Future> Future for RunWhile<'a, Fut> {
type Output = Fut::Output;
fn poll(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
let this = self.project();
this.runner.poll(cx);
this.fut.poll(cx)
}
}
RunWhile { runner: self, fut }
}
}

View File

@@ -19,6 +19,9 @@ impl<T> SyncMutex<T> {
pub fn peek<F: FnOnce(&T) -> U, U>(&self, f: F) -> U {
f(&*self.0.lock().unwrap())
}
pub fn replace(&self, value: T) -> T {
std::mem::replace(&mut *self.0.lock().unwrap(), value)
}
}
#[derive(Debug, Default)]