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