1use std::sync::{Arc, Mutex};
4
5use futures::io::BufReader;
6use futures::{
7 AsyncRead, AsyncWrite, Future, FutureExt as _, Stream, StreamExt as _, select_biased,
8};
9use itertools::iproduct;
10use oneshot_fused_workaround as oneshot;
11use safelog::sensitive as sv;
12use std::collections::HashMap;
13use std::io::Error as IoError;
14use strum::IntoEnumIterator;
15use tor_cell::relaycell::msg as relaymsg;
16use tor_error::{ErrorKind, HasKind, debug_report};
17use tor_hsservice::{HsNickname, RendRequest, StreamRequest};
18use tor_log_ratelim::log_ratelim;
19use tor_proto::client::stream::DataStream;
20use tor_proto::stream::IncomingStreamRequest;
21use tor_rtcompat::{Runtime, SpawnExt as _};
22
23use crate::config::{
24 Encapsulation, ProxyAction, ProxyActionDiscriminants, ProxyConfig, TargetAddr,
25};
26
27#[derive(Debug)]
30pub struct OnionServiceReverseProxy {
31 state: Mutex<State>,
33}
34
35#[derive(Debug)]
37struct State {
38 config: ProxyConfig,
40 shutdown_tx: Option<oneshot::Sender<void::Void>>,
42 shutdown_rx: futures::future::Shared<oneshot::Receiver<void::Void>>,
44}
45
46#[derive(Clone, Debug, thiserror::Error)]
48#[non_exhaustive]
49pub enum HandleRequestsError {
50 #[error("Unable to spawn a task")]
52 Spawn(#[source] Arc<futures::task::SpawnError>),
53}
54
55impl HasKind for HandleRequestsError {
56 fn kind(&self) -> ErrorKind {
57 match self {
58 HandleRequestsError::Spawn(e) => e.kind(),
59 }
60 }
61}
62
63impl OnionServiceReverseProxy {
64 pub fn new(config: ProxyConfig) -> Arc<Self> {
66 let (shutdown_tx, shutdown_rx) = oneshot::channel();
67 Arc::new(Self {
68 state: Mutex::new(State {
69 config,
70 shutdown_tx: Some(shutdown_tx),
71 shutdown_rx: shutdown_rx.shared(),
72 }),
73 })
74 }
75
76 pub fn reconfigure(
81 &self,
82 config: ProxyConfig,
83 how: tor_config::Reconfigure,
84 ) -> Result<(), tor_config::ReconfigureError> {
85 if how == tor_config::Reconfigure::CheckAllOrNothing {
86 return Ok(());
88 }
89 let mut state = self.state.lock().expect("poisoned lock");
90 state.config = config;
91 Ok(())
96 }
97
98 pub fn shutdown(&self) {
100 let mut state = self.state.lock().expect("poisoned lock");
101 let _ = state.shutdown_tx.take();
102 }
103
104 pub async fn handle_requests<R, S>(
111 &self,
112 runtime: R,
113 nickname: HsNickname,
114 requests: S,
115 ) -> Result<(), HandleRequestsError>
116 where
117 R: Runtime,
118 S: Stream<Item = RendRequest> + Unpin,
119 {
120 let mut stream_requests = tor_hsservice::handle_rend_requests(requests).fuse();
121 let mut shutdown_rx = self
122 .state
123 .lock()
124 .expect("poisoned lock")
125 .shutdown_rx
126 .clone()
127 .fuse();
128 let nickname = Arc::new(nickname);
129
130 #[cfg(feature = "metrics")]
132 #[derive(Clone, Copy, Eq, PartialEq, Hash)]
133 enum CounterSelector {
134 Ret(Result<(), ()>),
136 Total,
138 }
139
140 #[cfg(feature = "metrics")]
141 let metrics_counters = {
142 use CounterSelector as CS;
143
144 let counters = iproduct!(
145 ProxyActionDiscriminants::iter(),
146 [
147 (CS::Total, "arti_hss_proxy_connections_total"),
148 (CS::Ret(Ok(())), "arti_hss_proxy_connections_ok_total"),
149 (CS::Ret(Err(())), "arti_hss_proxy_connections_failed_total"),
150 ],
151 )
152 .map(|(action, (outcome, name))| {
153 let k = (action, outcome);
154 let nickname = nickname.to_string();
155 let action: &str = action.into();
156 let v = metrics::counter!(name, "nickname" => nickname, "action" => action);
157 (k, v)
158 })
159 .collect::<HashMap<(ProxyActionDiscriminants, CounterSelector), _>>();
160
161 Arc::new(counters)
162 };
163
164 loop {
165 let stream_request = select_biased! {
166 _ = shutdown_rx => return Ok(()),
167 stream_request = stream_requests.next() => match stream_request {
168 None => return Ok(()),
169 Some(s) => s,
170 }
171 };
172
173 runtime.spawn({
174 let action = self.choose_action(stream_request.request());
175 let runtime = runtime.clone();
176 let nickname = nickname.clone();
177 let req = stream_request.request().clone();
178
179 #[cfg(feature = "metrics")]
180 let metrics_counters = metrics_counters.clone();
181
182 async move {
183 let outcome =
184 run_action(runtime, nickname.as_ref(), action.clone(), stream_request).await;
185
186 #[cfg(feature = "metrics")]
187 {
188 use CounterSelector as CS;
189
190 let action = ProxyActionDiscriminants::from(&action);
191 let outcome = outcome.as_ref().map(|_|()).map_err(|_|());
192 for outcome in [CS::Total, CS::Ret(outcome)] {
193 if let Some(counter) = metrics_counters.get(&(action, outcome)) {
194 counter.increment(1);
195 } else {
196 }
198 }
199 }
200
201 log_ratelim!(
202 "Performing action on {}", nickname;
203 outcome;
204 Err(_) => WARN, "Unable to take action {:?} for request {:?}", sv(action), sv(req)
205 );
206 }
207 })
208 .map_err(|e| HandleRequestsError::Spawn(Arc::new(e)))?;
209 }
210 }
211
212 fn choose_action(&self, stream_request: &IncomingStreamRequest) -> ProxyAction {
215 let port: u16 = match stream_request {
216 IncomingStreamRequest::Begin(begin) => {
217 begin.port()
220 }
221 other => {
222 tracing::warn!(
223 "Rejecting onion service request for invalid command {:?}. Internal error.",
224 other
225 );
226 return ProxyAction::DestroyCircuit;
227 }
228 };
229
230 self.state
231 .lock()
232 .expect("poisoned lock")
233 .config
234 .resolve_port_for_begin(port)
235 .cloned()
236 .unwrap_or(ProxyAction::DestroyCircuit)
238 }
239}
240
241async fn run_action<R: Runtime>(
243 runtime: R,
244 nickname: &HsNickname,
245 action: ProxyAction,
246 request: StreamRequest,
247) -> Result<(), RequestFailed> {
248 match action {
249 ProxyAction::DestroyCircuit => {
250 request
251 .shutdown_circuit()
252 .map_err(RequestFailed::CantDestroy)?;
253 }
254 ProxyAction::Forward(encap, target) => match (encap, target) {
255 (Encapsulation::Simple, ref addr @ TargetAddr::Inet(a)) => {
256 let rt_clone = runtime.clone();
257
258 let connect_options = Default::default();
260 let stream = runtime.connect(&a, &connect_options);
261
262 forward_connection(rt_clone, request, stream, nickname, addr).await?;
263 } },
269 ProxyAction::RejectStream => {
270 let end = relaymsg::End::new_with_reason(relaymsg::EndReason::DONE);
272
273 request
274 .reject(end)
275 .await
276 .map_err(RequestFailed::CantReject)?;
277 }
278 ProxyAction::IgnoreStream => drop(request),
279 };
280 Ok(())
281}
282
283#[derive(thiserror::Error, Debug, Clone)]
285enum RequestFailed {
286 #[error("Unable to destroy onion service circuit")]
288 CantDestroy(#[source] tor_error::Bug),
289
290 #[error("Unable to reject onion service request")]
292 CantReject(#[source] tor_hsservice::ClientError),
293
294 #[error("Unable to accept onion service connection")]
297 AcceptRemote(#[source] tor_hsservice::ClientError),
298
299 #[error("Unable to spawn task")]
301 Spawn(#[source] Arc<futures::task::SpawnError>),
302}
303
304impl HasKind for RequestFailed {
305 fn kind(&self) -> ErrorKind {
306 match self {
307 RequestFailed::CantDestroy(e) => e.kind(),
308 RequestFailed::CantReject(e) => e.kind(),
309 RequestFailed::AcceptRemote(e) => e.kind(),
310 RequestFailed::Spawn(e) => e.kind(),
311 }
312 }
313}
314
315const STREAM_BUF_LEN: usize = 4096;
324
325async fn forward_connection<R, FUT, TS>(
333 runtime: R,
334 request: StreamRequest,
335 target_stream_future: FUT,
336 nickname: &HsNickname,
337 addr: &TargetAddr,
338) -> Result<(), RequestFailed>
339where
340 R: Runtime,
341 FUT: Future<Output = Result<TS, IoError>>,
342 TS: AsyncRead + AsyncWrite + Send + 'static,
343{
344 let local_stream = target_stream_future.await.map_err(Arc::new);
345
346 log_ratelim!(
349 "Connecting to {} for onion service {}", sv(addr), nickname;
350 local_stream
351 );
352
353 let local_stream = match local_stream {
354 Ok(s) => s,
355 Err(_) => {
356 let end = relaymsg::End::new_with_reason(relaymsg::EndReason::DONE);
357 if let Err(e_rejecting) = request.reject(end).await {
358 debug_report!(
359 &e_rejecting,
360 "Unable to reject onion service request from client"
361 );
362 return Err(RequestFailed::CantReject(e_rejecting));
363 }
364 return Ok(());
367 }
368 };
369
370 let onion_service_stream: DataStream = {
371 let connected = relaymsg::Connected::new_empty();
372 request
373 .accept(connected)
374 .await
375 .map_err(RequestFailed::AcceptRemote)?
376 };
377
378 let onion_service_stream = BufReader::with_capacity(STREAM_BUF_LEN, onion_service_stream);
379 let local_stream = BufReader::with_capacity(STREAM_BUF_LEN, local_stream);
380
381 runtime
382 .spawn(
383 futures_copy::copy_buf_bidirectional(
384 onion_service_stream,
385 local_stream,
386 futures_copy::eof::Close,
387 futures_copy::eof::Close,
388 )
389 .map(|_| ()),
390 )
391 .map_err(|e| RequestFailed::Spawn(Arc::new(e)))?;
392
393 Ok(())
394}