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
}