1
//! Type and code for handling a "half-closed" stream.
2
//!
3
//! A half-closed stream is one that we've sent an END on, but where
4
//! we might still receive some cells.
5

            
6
use crate::circuit::sendme::{StreamRecvWindow, StreamSendWindow};
7
use crate::{Error, Result};
8
use tor_cell::relaycell::msg::RelayMsg;
9
use tor_error::internal;
10

            
11
/// Type to track state of half-closed streams.
12
///
13
/// A half-closed stream is one where we've sent an END cell, but where
14
/// the other side might still send us data.
15
///
16
/// We need to track these streams instead of forgetting about them entirely,
17
/// since otherwise we'd be vulnerable to a class of "DropMark" attacks;
18
/// see <https://gitlab.torproject.org/tpo/core/tor/-/issues/25573>.
19
pub(super) struct HalfStream {
20
    /// Send window for this stream. Used to detect whether we get too many
21
    /// SENDME cells.
22
    sendw: StreamSendWindow,
23
    /// Receive window for this stream. Used to detect whether we get too
24
    /// many data cells.
25
    recvw: StreamRecvWindow,
26
    /// If true, accept a connected cell on this stream.
27
    connected_ok: bool,
28
}
29

            
30
impl HalfStream {
31
    /// Create a new half-closed stream.
32
6
    pub(super) fn new(
33
6
        sendw: StreamSendWindow,
34
6
        recvw: StreamRecvWindow,
35
6
        connected_ok: bool,
36
6
    ) -> Self {
37
6
        HalfStream {
38
6
            sendw,
39
6
            recvw,
40
6
            connected_ok,
41
6
        }
42
6
    }
43

            
44
    /// Process an incoming message and adjust this HalfStream accordingly.
45
    /// Give an error if the protocol has been violated.
46
    ///
47
    /// The caller must handle END cells; it is an internal error to pass
48
    /// END cells to this method.
49
    /// no ends here.
50
27
    pub(super) fn handle_msg(&mut self, msg: &RelayMsg) -> Result<()> {
51
27
        match msg {
52
            RelayMsg::Sendme(_) => {
53
2
                self.sendw.put(Some(()))?;
54
1
                Ok(())
55
            }
56
            RelayMsg::Data(_) => {
57
21
                self.recvw.take()?;
58
20
                Ok(())
59
            }
60
            RelayMsg::Connected(_) => {
61
3
                if self.connected_ok {
62
1
                    self.connected_ok = false;
63
1
                    Ok(())
64
                } else {
65
2
                    Err(Error::CircProto(
66
2
                        "Bad CONNECTED cell on a closed stream!".into(),
67
2
                    ))
68
                }
69
            }
70
            RelayMsg::End(_) => Err(Error::from(internal!(
71
                "END cell in HalfStream::handle_msg()"
72
            ))),
73
1
            _ => Err(Error::CircProto(format!(
74
1
                "Bad {} cell on a closed stream!",
75
1
                msg.cmd()
76
1
            ))),
77
        }
78
27
    }
79
}
80

            
81
#[cfg(test)]
82
mod test {
83
    #![allow(clippy::unwrap_used)]
84
    use super::*;
85
    use crate::circuit::sendme::{StreamRecvWindow, StreamSendWindow};
86
    use tor_cell::relaycell::msg;
87

            
88
    #[test]
89
    fn halfstream_sendme() -> Result<()> {
90
        let mut sendw = StreamSendWindow::new(101);
91
        sendw.take(&())?; // Make sure that it will accept one sendme.
92

            
93
        let mut hs = HalfStream::new(sendw, StreamRecvWindow::new(20), true);
94

            
95
        // one sendme is fine
96
        let m = msg::Sendme::new_empty().into();
97
        assert!(hs.handle_msg(&m).is_ok());
98
        // but no more were expected!
99
        let e = hs.handle_msg(&m).err().unwrap();
100
        assert_eq!(
101
            format!("{}", e),
102
            "circuit protocol violation: Received a SENDME when none was expected"
103
        );
104
        Ok(())
105
    }
106

            
107
    fn hs_new() -> HalfStream {
108
        HalfStream::new(StreamSendWindow::new(20), StreamRecvWindow::new(20), true)
109
    }
110

            
111
    #[test]
112
    fn halfstream_data() {
113
        let mut hs = hs_new();
114

            
115
        // 20 data cells are okay.
116
        let m = msg::Data::new(&b"this offer is unrepeatable"[..])
117
            .unwrap()
118
            .into();
119
        for _ in 0_u8..20 {
120
            assert!(hs.handle_msg(&m).is_ok());
121
        }
122

            
123
        // But one more is a protocol violation.
124
        let e = hs.handle_msg(&m).err().unwrap();
125
        assert_eq!(
126
            format!("{}", e),
127
            "circuit protocol violation: Received a data cell in violation of a window"
128
        );
129
    }
130

            
131
    #[test]
132
    fn halfstream_connected() {
133
        let mut hs = hs_new();
134
        // We were told to accept a connected, so we'll accept one
135
        // and no more.
136
        let m = msg::Connected::new_empty().into();
137
        assert!(hs.handle_msg(&m).is_ok());
138
        assert!(hs.handle_msg(&m).is_err());
139

            
140
        // If we try that again with connected_ok == false, we won't
141
        // accept any.
142
        let mut hs = HalfStream::new(StreamSendWindow::new(20), StreamRecvWindow::new(20), false);
143
        let e = hs.handle_msg(&m).err().unwrap();
144
        assert_eq!(
145
            format!("{}", e),
146
            "circuit protocol violation: Bad CONNECTED cell on a closed stream!"
147
        );
148
    }
149

            
150
    #[test]
151
    fn halfstream_other() {
152
        let mut hs = hs_new();
153
        let m = msg::Extended2::new(Vec::new()).into();
154
        let e = hs.handle_msg(&m).err().unwrap();
155
        assert_eq!(
156
            format!("{}", e),
157
            "circuit protocol violation: Bad EXTENDED2 cell on a closed stream!"
158
        );
159
    }
160
}