diff --git a/Cargo.lock b/Cargo.lock index 27c4475..125abea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,9 +7,23 @@ name = "Gamal" version = "0.1.0" dependencies = [ "arti-client", + "axum", + "bincode", "env_logger", + "futures", + "futures-util", + "hyper", + "hyper-util", "log", + "pin-project-lite", + "rusqlite", + "serde", + "sha3", "tokio", + "tor-cell", + "tor-hsservice", + "tor-proto", + "tower-service", ] [[package]] @@ -219,7 +233,6 @@ dependencies = [ "tor-netdoc", "tor-persist", "tor-proto", - "tor-ptmgr", "tor-rtcompat", "tracing", "void", @@ -357,6 +370,61 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "axum" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -390,6 +458,15 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -829,7 +906,7 @@ dependencies = [ "proc-macro2", "quote", "sha3", - "strum 0.27.1", + "strum", "syn 2.0.99", "void", ] @@ -847,7 +924,7 @@ dependencies = [ "proc-macro2", "quote", "sha3", - "strum 0.27.1", + "strum", "syn 2.0.99", "void", ] @@ -1526,6 +1603,29 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.10.1" @@ -1554,6 +1654,41 @@ dependencies = [ "serde", ] +[[package]] +name = "hyper" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "iana-time-zone" version = "0.1.61" @@ -1919,6 +2054,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" dependencies = [ + "cc", "pkg-config", "vcpkg", ] @@ -1960,6 +2096,12 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "memchr" version = "2.7.4" @@ -1987,6 +2129,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -2487,9 +2635,9 @@ dependencies = [ [[package]] name = "priority-queue" -version = "2.1.2" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090ded312ed32a928fb49cb91ab4db6523ae3767225e61fbf6ceaaec3664ed26" +checksum = "35d34aad26c181c38e0399695bb8cd445c65222e40f68bebfea809650eb3eda6" dependencies = [ "autocfg", "equivalent", @@ -2896,6 +3044,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_spanned" version = "0.6.8" @@ -2905,6 +3063,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_with" version = "3.12.0" @@ -3153,16 +3323,7 @@ version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ - "strum_macros 0.26.4", -] - -[[package]] -name = "strum" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" -dependencies = [ - "strum_macros 0.27.1", + "strum_macros", ] [[package]] @@ -3178,19 +3339,6 @@ dependencies = [ "syn 2.0.99", ] -[[package]] -name = "strum_macros" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.99", -] - [[package]] name = "subtle" version = "2.6.1" @@ -3219,6 +3367,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + [[package]] name = "synstructure" version = "0.13.1" @@ -3353,9 +3507,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.43.0" +version = "1.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" +checksum = "9975ea0f48b5aa3972bf2d888c238182458437cc2a19374b81b25cdf1023fb3a" dependencies = [ "backtrace", "bytes", @@ -3645,7 +3799,7 @@ dependencies = [ "serde", "serde-value", "serde_ignored", - "strum 0.26.3", + "strum", "thiserror 2.0.12", "toml", "tor-basic-utils", @@ -3741,7 +3895,7 @@ dependencies = [ "scopeguard", "serde", "signature", - "strum 0.26.3", + "strum", "thiserror 2.0.12", "time", "tor-async-utils", @@ -3774,7 +3928,7 @@ dependencies = [ "paste", "retry-error", "static_assertions", - "strum 0.26.3", + "strum", "thiserror 2.0.12", "tracing", "void", @@ -3815,7 +3969,7 @@ dependencies = [ "rand", "safelog", "serde", - "strum 0.26.3", + "strum", "thiserror 2.0.12", "tor-async-utils", "tor-basic-utils", @@ -3827,7 +3981,6 @@ dependencies = [ "tor-netdoc", "tor-persist", "tor-proto", - "tor-protover", "tor-relay-selection", "tor-rtcompat", "tor-units", @@ -3853,7 +4006,7 @@ dependencies = [ "retry-error", "safelog", "slotmap-careful", - "strum 0.26.3", + "strum", "thiserror 2.0.12", "tor-async-utils", "tor-basic-utils", @@ -3937,7 +4090,7 @@ dependencies = [ "safelog", "serde", "serde_with", - "strum 0.26.3", + "strum", "thiserror 2.0.12", "tor-async-utils", "tor-basic-utils", @@ -4040,7 +4193,7 @@ dependencies = [ "safelog", "serde", "serde_with", - "strum 0.26.3", + "strum", "thiserror 2.0.12", "tor-basic-utils", "tor-bytes", @@ -4146,7 +4299,7 @@ dependencies = [ "rand", "serde", "static_assertions", - "strum 0.26.3", + "strum", "thiserror 2.0.12", "time", "tor-basic-utils", @@ -4300,33 +4453,6 @@ dependencies = [ "thiserror 2.0.12", ] -[[package]] -name = "tor-ptmgr" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65a49254c95ab7c39bf626c595bb583894bae3437d5e33b75ebf40f53ae4f9b4" -dependencies = [ - "async-trait", - "cfg-if", - "derive_builder_fork_arti", - "fs-mistrust", - "futures", - "itertools", - "oneshot-fused-workaround", - "serde", - "thiserror 2.0.12", - "tor-async-utils", - "tor-basic-utils", - "tor-chanmgr", - "tor-config", - "tor-config-path", - "tor-error", - "tor-linkspec", - "tor-rtcompat", - "tor-socksproto", - "tracing", -] - [[package]] name = "tor-relay-selection" version = "0.28.0" @@ -4387,7 +4513,7 @@ dependencies = [ "pin-project", "priority-queue", "slotmap-careful", - "strum 0.26.3", + "strum", "thiserror 2.0.12", "tor-error", "tor-general-addr", @@ -4426,12 +4552,41 @@ dependencies = [ "tor-memquota", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + [[package]] name = "tracing" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", diff --git a/Cargo.toml b/Cargo.toml index 3ebb76b..594ab02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,22 @@ version = "0.1.0" edition = "2021" [dependencies] -arti-client = { version = "0.28.0", features = ["onion-service-client", "onion-service-service", "pt-client"] } +rusqlite = { version = "0.32.0", features = ["bundled"] } tokio = { version = "1", features = ["full"] } env_logger = "0.11.6" log = "0.4.20" +sha3 = "0.10.8" +serde = { version = "1", features = ["derive"] } +bincode = "1.3.3" +tor-cell = "0.28.0" +tor-hsservice = "0.28.0" +tor-proto = { version = "0.28.0", features = ["hs-service", "tokio"] } +arti-client = { version = "0.28.0", features = ["tokio", "onion-service-service"]} +futures = "0.3" +axum = "0.7.5" +futures-util = "0.3.30" +hyper = "1.4.1" +hyper-util = { version = "0.1.6", features = ["service"] } +pin-project-lite = "0.2.14" +tower-service = "0.3.2" +hypertor = "0.1" \ No newline at end of file diff --git a/src/dht.rs b/src/dht.rs new file mode 100644 index 0000000..e0f83b1 --- /dev/null +++ b/src/dht.rs @@ -0,0 +1,53 @@ +use std::{collections::HashMap, net::SocketAddr, sync::Arc}; + +use serde::{Deserialize, Serialize}; +use tokio::{net::UdpSocket, sync::{mpsc, Mutex}, task}; + +#[derive(Serialize, Deserialize, Debug)] +enum DHTMessage { + Put { key: String, value: String }, + Get { key: String }, + Response { value: Option }, +} + +async fn server() { + let addr = "127.0.0.1:8080"; // Change this per node + + let socket = UdpSocket::bind(addr).await.unwrap(); + + let storage = Arc::new(Mutex::new(HashMap::new())); + + let (tx, mut rx) = mpsc::channel::<(SocketAddr, DHTMessage)>(100); + + let storage_clone = storage.clone(); + + task::spawn(async move { + while let Some((peer, msg)) = rx.recv().await { + let response = match msg { + DHTMessage::Put { key, value } => { + storage_clone.lock().await.insert(key.clone(), value.clone()); + DHTMessage::Response { value: Some("OK".to_string()) } + } + DHTMessage::Get { key } => { + let value = storage_clone.lock().await.get(&key).cloned(); + DHTMessage::Response { value } + } + _ => DHTMessage::Response { value: None }, + + }; + + let response_bytes = bincode::serialize(&response).unwrap(); + socket.send_to(&response_bytes, peer).await.unwrap(); + } + }); + + /*let mut buf = [0; 1024]; + + loop { + let (size, peer) = socket.recv_from(&mut buf).await.unwrap(); + + if let Ok(msg) = bincode::deserialize::(&buf[..size]) { + tx.send((peer, msg)).await.unwrap(); + } + }*/ +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 9e90370..94ac719 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,18 @@ -use arti_client::{config::onion_service::OnionServiceConfigBuilder, TorClient, TorClientConfig}; -use log::info; +use env_logger::Builder; +use log::{info, LevelFilter}; + +//mod dht; +mod tor_axum; +mod tor; + #[tokio::main] async fn main() { - let config = TorClientConfig::default(); + Builder::new() + .filter(None, LevelFilter::Debug) + .init(); + + /* let config = TorClientConfig::default(); if let Ok(tor_client) = TorClient::create_bootstrapped(config).await { info!("Bootstrapped"); let onion_service_config = OnionServiceConfigBuilder::default().build().unwrap(); @@ -12,5 +21,6 @@ async fn main() { } } else { info!("Error with bootstrapping"); - } + } */ + tor::start().await; } diff --git a/src/tor.rs b/src/tor.rs new file mode 100644 index 0000000..5696151 --- /dev/null +++ b/src/tor.rs @@ -0,0 +1,32 @@ +use std::{error::Error, net::TcpListener}; + +use arti_client::{TorClient, TorClientConfig}; +use futures::{Stream, StreamExt}; +use log::info; +use tokio::io::split; +use tor_hsservice::config::OnionServiceConfigBuilder; +use tor_hsservice::handle_rend_requests; +use axum::{routing, Router}; + +use crate::tor_axum; + + +pub async fn start() -> Result<(), Box> { + let tor_client = TorClient::create_bootstrapped(TorClientConfig::default()).await?; + + let (onion_service, rend_requests) = tor_client.launch_onion_service( + OnionServiceConfigBuilder::default() + .nickname("gamal-service".to_owned().try_into().unwrap()) + .build()?, + )?; + + let stream_requests = handle_rend_requests(rend_requests); + + let app = Router::new().route("/", routing::get(|| async { "Hello, World!" })); + + println!("serving at: http://{}", onion_service.onion_name().unwrap()); + + tor_axum::serve(stream_requests, app).await; + + Ok(()) +} \ No newline at end of file diff --git a/src/tor_axum.rs b/src/tor_axum.rs new file mode 100644 index 0000000..dfb0fd4 --- /dev/null +++ b/src/tor_axum.rs @@ -0,0 +1,218 @@ +use std::{ + convert::Infallible, + future::{ + poll_fn, + Future, + IntoFuture, + }, + marker::PhantomData, + pin::Pin, + task::{ + Context, + Poll, + }, +}; + +use axum::{ + body::Body, + extract::Request, + response::Response, + Router, +}; +use futures_util::{ + future::{ + BoxFuture, + FutureExt, + }, + stream::{ + BoxStream, + Stream, + StreamExt, + }, +}; +use hyper::body::Incoming; +use hyper_util::{ + rt::{ + TokioExecutor, + TokioIo, + }, + server::conn::auto::Builder, + service::TowerToHyperService, +}; +use log::error; +use tor_cell::relaycell::msg::Connected; +use tor_hsservice::StreamRequest; +use tor_proto::stream::{ + DataStream, + IncomingStreamRequest, +}; +use tower_service::Service; + +/// Serve the service with the supplied stream requests. +/// +/// See the [crate documentation](`crate`) for an example. +pub fn serve( + stream_requests: impl Stream + Send + 'static, + make_service: M, +) -> Serve +where + M: for<'a> Service, Error = Infallible, Response = S>, + S: Service, +{ + Serve { + stream_requests: stream_requests.boxed(), + make_service, + _marker: PhantomData, + } +} + +/// Future returned by [`serve`]. +pub struct Serve { + stream_requests: BoxStream<'static, StreamRequest>, + make_service: M, + _marker: PhantomData, +} + +impl IntoFuture for Serve +where + M: for<'a> Service, Error = Infallible, Response = S> + Send + 'static, + for<'a> >>::Future: Send, + S: Service + Clone + Send + 'static, + S::Future: Send, +{ + type Output = (); + type IntoFuture = private::ServeFuture; + + fn into_future(mut self) -> Self::IntoFuture { + private::ServeFuture { + inner: async move { + while let Some(stream_request) = self.stream_requests.next().await { + let data_stream = match stream_request.request() { + IncomingStreamRequest::Begin(_) => { + match stream_request.accept(Connected::new_empty()).await { + Ok(data_stream) => data_stream, + Err(error) => { + error!("failed to accept incoming stream: {error}"); + continue; + } + } + } + _ => { + // we only accept BEGIN streams + continue; + } + }; + + poll_fn(|cx| self.make_service.poll_ready(cx)) + .await + .unwrap_or_else(|err| match err {}); + + let incoming_stream = IncomingStream { + data_stream: &data_stream, + }; + + let tower_service = self + .make_service + .call(incoming_stream) + .await + .unwrap_or_else(|err| match err {}); + + let transformed_service = MapRequestBodyService { + inner: tower_service, + }; + + let hyper_service = TowerToHyperService::new(transformed_service); + + tokio::spawn(async move { + match Builder::new(TokioExecutor::new()) + // upgrades needed for websockets + .serve_connection_with_upgrades( + TokioIo::new(data_stream), + hyper_service, + ) + .await + { + Ok(()) => {} + Err(_err) => { + // This error only appears when the client + // doesn't send a request and + // terminate the connection. + // + // If client sends one request then terminate + // connection whenever, it doesn't + // appear. + } + } + }); + } + } + .boxed(), + } + } +} + +mod private { + use super::*; + + pub struct ServeFuture { + pub inner: BoxFuture<'static, ()>, + } + + impl Future for ServeFuture { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.inner.poll_unpin(cx) + } + } +} + +#[derive(Clone)] +struct MapRequestBodyService { + inner: S, +} + +/// A Tower service that transforms the body of incoming requests from +/// `Incoming` to `Body` and delegates the transformed request to the inner +/// service. +impl Service> for MapRequestBodyService +where + S: Service>, +{ + type Response = S::Response; + type Error = S::Error; + type Future = S::Future; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready(cx) + } + + fn call(&mut self, req: Request) -> Self::Future { + let req = req.map(Body::new); + self.inner.call(req) + } +} + +/// An incoming stream. +/// +/// This is a single client connecting over the TOR network to your onion +/// service. +pub struct IncomingStream<'a> { + // in the future we can use this to return information about the circuit used etc. + #[allow(dead_code)] + data_stream: &'a DataStream, +} + +impl Service> for Router<()> { + type Response = Router; + type Error = Infallible; + type Future = std::future::Ready>; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, _req: IncomingStream<'_>) -> Self::Future { + std::future::ready(Ok(self.clone())) + } +} \ No newline at end of file