1use anyhow::{Context, Result};
6use std::collections::HashMap;
7use std::net::SocketAddr;
8use std::sync::Arc;
9use tokio::net::TcpStream;
10use tokio::sync::Mutex;
11use tracing::{debug, trace};
12
13use super::circuit::StreamId;
14
15#[derive(Debug)]
17pub enum StreamState {
18 Connecting,
20 Active {
22 reader: Arc<Mutex<tokio::net::tcp::OwnedReadHalf>>,
28 writer: Arc<Mutex<tokio::net::tcp::OwnedWriteHalf>>,
30 bytes_sent: u64,
32 bytes_received: u64,
34 package_window: u16,
36 deliver_window: u16,
38 data_cells_received: u16,
40 },
41 Directory {
43 request_data: Vec<u8>,
45 package_window: u16,
47 deliver_window: u16,
49 data_cells_received: u16,
51 },
52 Closing,
54 Closed,
56}
57
58pub const STREAM_WINDOW_START: u16 = 500;
60pub const STREAM_WINDOW_INCREMENT: u16 = 50;
61
62pub const MAX_STREAMS_PER_CIRCUIT: usize = 64;
70
71#[derive(Debug)]
73pub struct Stream {
74 pub id: StreamId,
75 pub target: String,
76 pub state: StreamState,
77 pub created_at: crate::utils::clock::Instant,
78}
79
80impl Stream {
81 pub fn new(id: StreamId, target: String) -> Self {
83 Self {
84 id,
85 target,
86 state: StreamState::Connecting,
87 created_at: crate::utils::clock::Instant::now(),
88 }
89 }
90
91 pub fn set_active(&mut self, connection: TcpStream) {
93 let (reader, writer) = connection.into_split();
94 self.state = StreamState::Active {
95 reader: Arc::new(Mutex::new(reader)),
96 writer: Arc::new(Mutex::new(writer)),
97 bytes_sent: 0,
98 bytes_received: 0,
99 package_window: STREAM_WINDOW_START,
100 deliver_window: STREAM_WINDOW_START,
101 data_cells_received: 0,
102 };
103 }
104
105 pub fn set_directory(&mut self) {
107 self.state = StreamState::Directory {
108 request_data: Vec::new(),
109 package_window: STREAM_WINDOW_START,
110 deliver_window: STREAM_WINDOW_START,
111 data_cells_received: 0,
112 };
113 }
114
115 pub fn is_active(&self) -> bool {
117 matches!(self.state, StreamState::Active { .. })
118 }
119
120 pub fn is_directory(&self) -> bool {
122 matches!(self.state, StreamState::Directory { .. })
123 }
124
125 pub fn writer(&self) -> Option<Arc<Mutex<tokio::net::tcp::OwnedWriteHalf>>> {
127 match &self.state {
128 StreamState::Active { writer, .. } => Some(writer.clone()),
129 _ => None,
130 }
131 }
132
133 pub fn reader(&self) -> Option<Arc<Mutex<tokio::net::tcp::OwnedReadHalf>>> {
135 match &self.state {
136 StreamState::Active { reader, .. } => Some(reader.clone()),
137 _ => None,
138 }
139 }
140
141 pub fn record_sent(&mut self, bytes: u64) {
143 if let StreamState::Active { bytes_sent, .. } = &mut self.state {
144 *bytes_sent += bytes;
145 }
146 }
147
148 pub fn record_received(&mut self, bytes: u64) {
150 if let StreamState::Active { bytes_received, .. } = &mut self.state {
151 *bytes_received += bytes;
152 }
153 }
154
155 pub fn close(&mut self) {
157 self.state = StreamState::Closed;
158 }
159
160 pub fn record_data_received(&mut self) -> bool {
162 match &mut self.state {
163 StreamState::Active {
164 deliver_window,
165 data_cells_received,
166 ..
167 }
168 | StreamState::Directory {
169 deliver_window,
170 data_cells_received,
171 ..
172 } => {
173 *deliver_window = deliver_window.saturating_sub(1);
174 *data_cells_received += 1;
175
176 if *data_cells_received >= STREAM_WINDOW_INCREMENT {
178 *data_cells_received = 0;
179 *deliver_window += STREAM_WINDOW_INCREMENT;
180 return true;
181 }
182 }
183 _ => {}
184 }
185 false
186 }
187
188 pub fn process_sendme(&mut self) {
190 match &mut self.state {
191 StreamState::Active { package_window, .. }
192 | StreamState::Directory { package_window, .. } => {
193 *package_window += STREAM_WINDOW_INCREMENT;
194 trace!(
195 "Stream {} package window increased to {}",
196 self.id.as_u16(),
197 package_window
198 );
199 }
200 _ => {}
201 }
202 }
203
204 pub fn can_send_data(&self) -> bool {
206 match &self.state {
207 StreamState::Active { package_window, .. }
208 | StreamState::Directory { package_window, .. } => *package_window > 0,
209 _ => false,
210 }
211 }
212
213 pub fn consume_package_window(&mut self) {
215 match &mut self.state {
216 StreamState::Active { package_window, .. }
217 | StreamState::Directory { package_window, .. } => {
218 *package_window = package_window.saturating_sub(1);
219 }
220 _ => {}
221 }
222 }
223
224 pub fn append_request_data(&mut self, data: &[u8]) {
226 if let StreamState::Directory { request_data, .. } = &mut self.state {
227 request_data.extend_from_slice(data);
228 }
229 }
230
231 pub fn get_request_data(&self) -> Option<&[u8]> {
233 if let StreamState::Directory { request_data, .. } = &self.state {
234 Some(request_data)
235 } else {
236 None
237 }
238 }
239}
240
241#[derive(Debug)]
243pub struct StreamManager {
244 streams: HashMap<StreamId, Stream>,
245 next_stream_id: u16,
246}
247
248impl StreamManager {
249 pub fn new() -> Self {
251 Self {
252 streams: HashMap::new(),
253 next_stream_id: 1,
254 }
255 }
256
257 pub fn allocate_stream_id(&mut self) -> StreamId {
259 let id = StreamId::new(self.next_stream_id);
260 self.next_stream_id = self.next_stream_id.wrapping_add(1);
261 if self.next_stream_id == 0 {
262 self.next_stream_id = 1; }
264 id
265 }
266
267 pub fn create_stream(&mut self, id: StreamId, target: String) -> Result<()> {
269 if self.streams.contains_key(&id) {
270 return Err(anyhow::anyhow!("Stream ID already exists"));
271 }
272
273 if self.streams.len() >= MAX_STREAMS_PER_CIRCUIT {
277 return Err(anyhow::anyhow!(
278 "circuit already holds {} streams, the per-circuit limit",
279 MAX_STREAMS_PER_CIRCUIT
280 ));
281 }
282
283 let stream = Stream::new(id, target);
284 self.streams.insert(id, stream);
285 debug!("Created stream {} in circuit", id.as_u16());
286
287 Ok(())
288 }
289
290 pub fn get_mut(&mut self, id: StreamId) -> Option<&mut Stream> {
292 self.streams.get_mut(&id)
293 }
294
295 pub fn get(&self, id: StreamId) -> Option<&Stream> {
297 self.streams.get(&id)
298 }
299
300 pub fn remove(&mut self, id: StreamId) -> Option<Stream> {
302 self.streams.remove(&id)
303 }
304
305 pub fn active_streams(&self) -> Vec<StreamId> {
307 self.streams
308 .iter()
309 .filter(|(_, s)| s.is_active())
310 .map(|(id, _)| *id)
311 .collect()
312 }
313
314 pub fn count(&self) -> usize {
316 self.streams.len()
317 }
318}
319
320pub fn parse_begin_target(data: &[u8]) -> Result<String> {
323 let end = data.iter().position(|&b| b == 0).unwrap_or(data.len());
325 let target_str = std::str::from_utf8(&data[..end]).context("Invalid UTF-8 in BEGIN target")?;
326
327 if !target_str.contains(':') {
329 return Err(anyhow::anyhow!(
330 "Invalid BEGIN target format: {}",
331 target_str
332 ));
333 }
334
335 Ok(target_str.to_string())
336}
337
338pub const TARGET_CONNECT_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10);
348
349pub async fn connect_to_target(target: &str) -> Result<TcpStream> {
355 debug!("Connecting to target: {}", target);
356
357 let connect = async {
359 if let Ok(addr) = target.parse::<SocketAddr>() {
360 TcpStream::connect(addr).await
362 } else {
363 TcpStream::connect(target).await
365 }
366 };
367
368 let stream = tokio::time::timeout(TARGET_CONNECT_TIMEOUT, connect)
369 .await
370 .map_err(|_| {
371 anyhow::anyhow!(
372 "timed out after {}s connecting to {}",
373 TARGET_CONNECT_TIMEOUT.as_secs(),
374 target
375 )
376 })??;
377
378 debug!("Connected to {}", target);
379 Ok(stream)
380}
381
382pub fn build_relay_cell(circuit_id: u32, stream_id: u16, command: u8, data: &[u8]) -> Vec<u8> {
385 let mut cell = Vec::with_capacity(514);
386
387 cell.extend_from_slice(&circuit_id.to_be_bytes());
389
390 cell.push(3); cell.push(command); cell.extend_from_slice(&0u16.to_be_bytes()); cell.extend_from_slice(&stream_id.to_be_bytes()); cell.extend_from_slice(&[0u8; 4]); cell.extend_from_slice(&(data.len() as u16).to_be_bytes()); cell.extend_from_slice(data);
402
403 cell.resize(514, 0);
405
406 cell
407}
408
409pub mod relay_command {
411 pub const BEGIN: u8 = 1;
412 pub const DATA: u8 = 2;
413 pub const END: u8 = 3;
414 pub const CONNECTED: u8 = 4;
415 pub const SENDME: u8 = 5;
416 pub const EXTEND: u8 = 6;
417 pub const EXTENDED: u8 = 7;
418 pub const TRUNCATE: u8 = 8;
419 pub const TRUNCATED: u8 = 9;
420 pub const DROP: u8 = 10;
421 pub const RESOLVE: u8 = 11;
422 pub const RESOLVED: u8 = 12;
423 pub const BEGIN_DIR: u8 = 13;
424 pub const EXTEND2: u8 = 14;
425 pub const EXTENDED2: u8 = 15;
426}
427
428pub mod end_reason {
430 pub const MISC: u8 = 1;
431 pub const RESOLVE_FAILED: u8 = 2;
432 pub const CONNECT_REFUSED: u8 = 3;
433 pub const EXIT_POLICY: u8 = 4;
434 pub const DESTROY: u8 = 5;
435 pub const DONE: u8 = 6;
436 pub const TIMEOUT: u8 = 7;
437 pub const NO_ROUTE: u8 = 8;
438 pub const HIBERNATING: u8 = 9;
439 pub const INTERNAL: u8 = 10;
440 pub const RESOURCE_LIMIT: u8 = 11;
441 pub const CONNECT_RESET: u8 = 12;
442 pub const TOR_PROTOCOL: u8 = 13;
443}