main.rs
  1  use std::sync::Arc;
  2  
  3  use anyhow::Result;
  4  use futures::StreamExt;
  5  use hyper::body::Incoming;
  6  use hyper::server::conn::http1;
  7  use hyper::service::service_fn;
  8  use hyper::{Request, Response, StatusCode};
  9  use hyper_util::rt::TokioIo;
 10  use tokio_util::sync::CancellationToken;
 11  
 12  use arti_client::{TorClient, TorClientConfig};
 13  use safelog::sensitive;
 14  use tor_cell::relaycell::msg::Connected;
 15  use tor_hsservice::config::OnionServiceConfigBuilder;
 16  use tor_hsservice::StreamRequest;
 17  use tor_proto::stream::IncomingStreamRequest;
 18  
 19  struct WebHandler {
 20      shutdown: CancellationToken,
 21  }
 22  
 23  impl WebHandler {
 24      async fn serve(&self, request: Request<Incoming>) -> Result<Response<String>> {
 25          let path = request.uri().path();
 26  
 27          // TODO: Unauthenticated management. This route is accessible by anyone, and exists solely
 28          //  to demonstrate how to safely shutdown further incoming requests. You should probably
 29          //  move this elsewhere to ensure proper checks are in place!
 30          if path == "/shutdown" {
 31              self.shutdown.cancel();
 32          }
 33  
 34          Ok(Response::builder().status(StatusCode::OK).body(format!(
 35              "{} {}",
 36              request.method(),
 37              path
 38          ))?)
 39      }
 40  }
 41  
 42  #[tokio::main]
 43  async fn main() {
 44      // Make sure you read doc/OnionService.md to extract your Onion service hostname
 45  
 46      // Arti uses the `tracing` crate for logging. Install a handler for this, to print Arti's logs.
 47      // (You'll need to set RUST_LOG=info as an environment variable to actually see much; also try
 48      // =debug for more detailed logging.)
 49      tracing_subscriber::fmt::init();
 50  
 51      // Initialize web server data, if you need to
 52      let handler = Arc::new(WebHandler {
 53          shutdown: CancellationToken::new(),
 54      });
 55  
 56      // The client config includes things like where to store persistent Tor network state.
 57      // The defaults provided are the same as the Arti standalone application, and save data
 58      // to a conventional place depending on operating system (for example, ~/.local/share/arti
 59      // on Linux platforms)
 60      let config = TorClientConfig::default();
 61  
 62      // We now let the Arti client start and bootstrap a connection to the network.
 63      // (This takes a while to gather the necessary consensus state, etc.)
 64      let client = TorClient::create_bootstrapped(config).await.unwrap();
 65  
 66      let svc_cfg = OnionServiceConfigBuilder::default()
 67          .nickname("allium-ampeloprasum".parse().unwrap())
 68          .build()
 69          .unwrap();
 70      let (service, request_stream) = client.launch_onion_service(svc_cfg).unwrap();
 71  
 72      eprintln!("ready to serve connections");
 73  
 74      let stream_requests = tor_hsservice::handle_rend_requests(request_stream)
 75          .take_until(handler.shutdown.cancelled());
 76      tokio::pin!(stream_requests);
 77  
 78      while let Some(stream_request) = stream_requests.next().await {
 79          // incoming connection
 80          let handler = handler.clone();
 81  
 82          tokio::spawn(async move {
 83              let request = stream_request.request().clone();
 84              let result = handle_stream_request(stream_request, handler).await;
 85  
 86              match result {
 87                  Ok(()) => {}
 88                  Err(err) => {
 89                      eprintln!("error serving connection {:?}: {}", sensitive(request), err);
 90                  }
 91              }
 92          });
 93      }
 94  
 95      drop(service);
 96      eprintln!("onion service exited cleanly");
 97  }
 98  
 99  async fn handle_stream_request(
100      stream_request: StreamRequest,
101      handler: Arc<WebHandler>,
102  ) -> Result<()> {
103      match stream_request.request() {
104          IncomingStreamRequest::Begin(begin) if begin.port() == 80 => {
105              let onion_service_stream = stream_request.accept(Connected::new_empty()).await?;
106              let io = TokioIo::new(onion_service_stream);
107  
108              http1::Builder::new()
109                  .serve_connection(io, service_fn(|request| handler.serve(request)))
110                  .await?;
111          }
112          _ => {
113              stream_request.shutdown_circuit()?;
114          }
115      }
116  
117      Ok(())
118  }